Fossil SCM

Merge with trunk

ashish 2011-11-13 09:11 UTC ashish-ipv6 merge
Commit c30eaa88625c9585f6ab53ba928c2c3039918d98
105 files changed +5 -1 +1 -1 +38 +2 +208 +140 +1 +236 +476 +53 +3 +30 +30 +2 +2 -2 +41 -2 +10 -6 +1 +3 -1 +145 -23 +145 -23 +2 -4 +3 -3 +1 -1 +1 -1 +645 +9 +64 -7 +4 -3 +459 -9 +55 -38 +13 -2 +1 -1 +1 -3 +159 -35 +57 +2 +11 +69 +27 +36 +31 +1 +1 +20 +122 +57 +285 -107 +285 -107 +278 -76 +278 -76 +126 -4 +5 -1 +70 -6 +60 -7 +3 -2 +18 -4 +5 -3 +67 -7 +336 -262 +1 -1 +9 -5 +7 -19 +1 -1 +1 -1 +19 -1 +1 -1 +278 -68 +670 -233 +3 -3 +68 -36 +86 -25 +1 -1 +1 +4 -2 +44 -3 +8 +50 -32 +3 -7 +10 +1 -1 +19 -8 +1 -1 +26 -9 +1 -3 +1 -10 +3 -4 +1 -1 +5 -1 +6 +82 +22 +6 +9 +9 +5 +9 +18 +11 +86 -4 +116 -2 +212 +94 -3 +37 +5 -4
~ Makefile.in ~ VERSION ~ ajax/README ~ ajax/cgi-bin/fossil-json.cgi.example ~ ajax/i-test/rhino-shell.js ~ ajax/i-test/rhino-test.js ~ ajax/index.html ~ ajax/js/fossil-ajaj.js ~ ajax/js/json2.js ~ ajax/js/whajaj.js ~ ajax/wiki-editor.html ~ auto.def ~ auto.def ~ src/Makefile ~ src/add.c ~ src/blob.c ~ src/branch.c ~ src/browse.c ~ src/captcha.c ~ src/cgi.c ~ src/cgi.c ~ src/checkin.c ~ src/checkout.c ~ src/configure.c ~ src/content.c ~ src/cson_amalgamation.c ~ src/cson_amalgamation.h ~ src/db.c ~ src/descendants.c ~ src/diff.c ~ src/diffcmd.c ~ src/doc.c ~ src/finfo.c ~ src/http_transport.c ~ src/info.c ~ src/json.c ~ src/json_artifact.c ~ src/json_branch.c ~ src/json_detail.h ~ src/json_diff.c ~ src/json_login.c ~ src/json_query.c ~ src/json_report.c ~ src/json_tag.c ~ src/json_timeline.c ~ src/json_user.c ~ src/json_wiki.c ~ src/login.c ~ src/login.c ~ src/main.c ~ src/main.c ~ src/main.mk ~ src/makeheaders.c ~ src/makemake.tcl ~ src/manifest.c ~ src/md5.c ~ src/merge.c ~ src/merge3.c ~ src/mkindex.c ~ src/name.c ~ src/printf.c ~ src/rebuild.c ~ src/report.c ~ src/schema.c ~ src/search.c ~ src/setup.c ~ src/sha1.c ~ src/skins.c ~ src/sqlite3.c ~ src/sqlite3.h ~ src/stash.c ~ src/style.c ~ src/tar.c ~ src/th.h ~ src/th_lang.c ~ src/th_main.c ~ src/th_tcl.c ~ src/timeline.c ~ src/tkt.c ~ src/translate.c ~ src/undo.c ~ src/update.c ~ src/user.c ~ src/wiki.c ~ src/wikiformat.c ~ src/winhttp.c ~ src/xfer.c ~ src/zip.c ~ test/merge5.test ~ test/merge_renames.test ~ test/th1-tcl.test ~ test/th1-tcl1.txt ~ test/th1-tcl2.txt ~ test/th1-tcl3.txt ~ test/th1-tcl4.txt ~ test/th1-tcl5.txt ~ test/th1-tcl6.txt ~ test/th1-tcl7.txt ~ test/th1-tcl8.txt ~ win/Makefile.dmc ~ win/Makefile.mingw ~ win/Makefile.mingw.mistachkin ~ win/Makefile.msc ~ www/changes.wiki ~ www/checkin_names.wiki
+5 -1
--- Makefile.in
+++ Makefile.in
@@ -9,13 +9,17 @@
99
# the following to point from the build directory to the src/ folder.
1010
#
1111
SRCDIR = @srcdir@/src
1212
1313
#### The directory into which object code files should be written.
14
+# Having a "./" prefix in the value of this variable breaks our use of the
15
+# "makeheaders" tool when running make on the MinGW platform, apparently
16
+# due to some command line argument manipulation performed automatically
17
+# by the shell.
1418
#
1519
#
16
-OBJDIR = ./bld
20
+OBJDIR = bld
1721
1822
#### C Compiler and options for use in building executables that
1923
# will run on the platform that is doing the build. This is used
2024
# to compile code-generator programs as part of the build process.
2125
# See TCC below for the C compiler for building the finished binary.
2226
--- Makefile.in
+++ Makefile.in
@@ -9,13 +9,17 @@
9 # the following to point from the build directory to the src/ folder.
10 #
11 SRCDIR = @srcdir@/src
12
13 #### The directory into which object code files should be written.
 
 
 
 
14 #
15 #
16 OBJDIR = ./bld
17
18 #### C Compiler and options for use in building executables that
19 # will run on the platform that is doing the build. This is used
20 # to compile code-generator programs as part of the build process.
21 # See TCC below for the C compiler for building the finished binary.
22
--- Makefile.in
+++ Makefile.in
@@ -9,13 +9,17 @@
9 # the following to point from the build directory to the src/ folder.
10 #
11 SRCDIR = @srcdir@/src
12
13 #### The directory into which object code files should be written.
14 # Having a "./" prefix in the value of this variable breaks our use of the
15 # "makeheaders" tool when running make on the MinGW platform, apparently
16 # due to some command line argument manipulation performed automatically
17 # by the shell.
18 #
19 #
20 OBJDIR = bld
21
22 #### C Compiler and options for use in building executables that
23 # will run on the platform that is doing the build. This is used
24 # to compile code-generator programs as part of the build process.
25 # See TCC below for the C compiler for building the finished binary.
26
+1 -1
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1
-1.19
1
+1.20
22
33
ADDED ajax/README
44
ADDED ajax/cgi-bin/fossil-json.cgi.example
55
ADDED ajax/i-test/rhino-shell.js
66
ADDED ajax/i-test/rhino-test.js
77
ADDED ajax/index.html
88
ADDED ajax/js/fossil-ajaj.js
99
ADDED ajax/js/json2.js
1010
ADDED ajax/js/whajaj.js
1111
ADDED ajax/wiki-editor.html
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1 1.19
2
3 DDED ajax/README
4 DDED ajax/cgi-bin/fossil-json.cgi.example
5 DDED ajax/i-test/rhino-shell.js
6 DDED ajax/i-test/rhino-test.js
7 DDED ajax/index.html
8 DDED ajax/js/fossil-ajaj.js
9 DDED ajax/js/json2.js
10 DDED ajax/js/whajaj.js
11 DDED ajax/wiki-editor.html
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1 1.20
2
3 DDED ajax/README
4 DDED ajax/cgi-bin/fossil-json.cgi.example
5 DDED ajax/i-test/rhino-shell.js
6 DDED ajax/i-test/rhino-test.js
7 DDED ajax/index.html
8 DDED ajax/js/fossil-ajaj.js
9 DDED ajax/js/json2.js
10 DDED ajax/js/whajaj.js
11 DDED ajax/wiki-editor.html
+38
--- a/ajax/README
+++ b/ajax/README
@@ -0,0 +1,38 @@
1
+This is the README for how to set up the Fossil/JSON test web page
2
+under Apache on Unix systems. This is only intended only for
3
+Fossil/JSON developers/tinkerers:
4
+
5
+First, copy cgi-bin/fossil-json.cgi.example to
6
+cgi-bin/fossil-json.cgi. Edit it and correct the paths to the fossil
7
+binary and the repo you want to serve. Make it executable.
8
+
9
+MAKE SURE that the fossil repo you use is world-writable OR that your
10
+Web/CGI server is set up to run as the user ID of the owner of the
11
+fossil file. ALSO: the DIRECTORY CONTAINING the repo file must be
12
+writable by the CGI process.
13
+
14
+Next, set up an apache vhost entry. Mine looks like:
15
+
16
+<VirtualHost *:80>
17
+ ServerAlias fjson
18
+ ScriptAlias /cgi-bin/ /home/stephan/cvs/fossil/fossil-json/ajax/cgi-bin/
19
+ DocumentRoot /home/stephan/cvs/fossil/fossil-json/ajax
20
+</VirtualHost>
21
+
22
+Now add your preferred vhost name (fjson in the above example) to /etc/hosts:
23
+
24
+ 127.0.0.1 ...other aliases... fjson
25
+
26
+Restart your Apache.
27
+
28
+Now visit: http://fjson/
29
+
30
+that will show the test/demo page. If it doesn't, edit index.html and
31
+make sure that:
32
+
33
+ WhAjaj.Connector.options.ajax.url = ...;
34
+
35
+points to your CGI script. In theory you can also do this over fossil
36
+standalone server mode, but i haven't yet tested that particular test
37
+page in that mode.
38
+
--- a/ajax/README
+++ b/ajax/README
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/ajax/README
+++ b/ajax/README
@@ -0,0 +1,38 @@
1 This is the README for how to set up the Fossil/JSON test web page
2 under Apache on Unix systems. This is only intended only for
3 Fossil/JSON developers/tinkerers:
4
5 First, copy cgi-bin/fossil-json.cgi.example to
6 cgi-bin/fossil-json.cgi. Edit it and correct the paths to the fossil
7 binary and the repo you want to serve. Make it executable.
8
9 MAKE SURE that the fossil repo you use is world-writable OR that your
10 Web/CGI server is set up to run as the user ID of the owner of the
11 fossil file. ALSO: the DIRECTORY CONTAINING the repo file must be
12 writable by the CGI process.
13
14 Next, set up an apache vhost entry. Mine looks like:
15
16 <VirtualHost *:80>
17 ServerAlias fjson
18 ScriptAlias /cgi-bin/ /home/stephan/cvs/fossil/fossil-json/ajax/cgi-bin/
19 DocumentRoot /home/stephan/cvs/fossil/fossil-json/ajax
20 </VirtualHost>
21
22 Now add your preferred vhost name (fjson in the above example) to /etc/hosts:
23
24 127.0.0.1 ...other aliases... fjson
25
26 Restart your Apache.
27
28 Now visit: http://fjson/
29
30 that will show the test/demo page. If it doesn't, edit index.html and
31 make sure that:
32
33 WhAjaj.Connector.options.ajax.url = ...;
34
35 points to your CGI script. In theory you can also do this over fossil
36 standalone server mode, but i haven't yet tested that particular test
37 page in that mode.
38
--- a/ajax/cgi-bin/fossil-json.cgi.example
+++ b/ajax/cgi-bin/fossil-json.cgi.example
@@ -0,0 +1,2 @@
1
+#!/path/to/fossil/binary
2
+repository: /path/to/repo.fsl
--- a/ajax/cgi-bin/fossil-json.cgi.example
+++ b/ajax/cgi-bin/fossil-json.cgi.example
@@ -0,0 +1,2 @@
 
 
--- a/ajax/cgi-bin/fossil-json.cgi.example
+++ b/ajax/cgi-bin/fossil-json.cgi.example
@@ -0,0 +1,2 @@
1 #!/path/to/fossil/binary
2 repository: /path/to/repo.fsl
--- a/ajax/i-test/rhino-shell.js
+++ b/ajax/i-test/rhino-shell.js
@@ -0,0 +1,208 @@
1
+var FShell = {
2
+ serverUrl:
3
+ 'http://localhost:8080'
4
+ //'http://fjson/cgi-bin/fossil-json.cgi'
5
+ //'http://192.168.1.62:8080'
6
+ //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
7
+ ,
8
+ verbose:false,
9
+ prompt:"fossil shell > ",
10
+ wiki:{},
11
+ consol:java.lang.System.console(),
12
+ v:function(msg){
13
+ if(this.verbose){
14
+ print("VERBOSE: "+msg);
15
+ }
16
+ }
17
+};
18
+(function bootstrap() {
19
+ var srcdir = '../js/';
20
+ var includes = [srcdir+'json2.js',
21
+ srcdir+'whajaj.js',
22
+ srcdir+'fossil-ajaj.js'
23
+ ];
24
+ for( var i in includes ) {
25
+ load(includes[i]);
26
+ }
27
+ WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
28
+ FShell.fossil = new FossilAjaj({
29
+ asynchronous:false, /* rhino-based impl doesn't support async. */
30
+ timeout:10000,
31
+ url:FShell.serverUrl
32
+ });
33
+ print("Server: "+FShell.serverUrl);
34
+ var cb = FShell.fossil.ajaj.callbacks;
35
+ cb.beforeSend = function(req,opt){
36
+ if(!FShell.verbose) return;
37
+ print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
38
+ if(req) print("Request envelope="+WhAjaj.stringify(req));
39
+ };
40
+ cb.afterSend = function(req,opt){
41
+ //if(!FShell.verbose) return;
42
+ //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
43
+ //if(req) print("Request="+WhAjaj.stringify(req));
44
+ };
45
+ cb.onError = function(req,opt){
46
+ //if(!FShell.verbose) return;
47
+ print("ERROR: "+WhAjaj.stringify(opt));
48
+ };
49
+ cb.onResponse = function(resp,req){
50
+ if(!FShell.verbose) return;
51
+ if(resp && resp.resultCode){
52
+ print("Response contains error info: "+resp.resultCode+": "+resp.resultText);
53
+ }
54
+ print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
55
+ };
56
+ FShell.fossil.HAI({
57
+ onResponse:function(resp,opt){
58
+ assertResponseOK(resp);
59
+ }
60
+ });
61
+})();
62
+
63
+/**
64
+ Throws an exception of cond is a falsy value.
65
+*/
66
+function assert(cond, descr){
67
+ descr = descr || "Undescribed condition.";
68
+ if(!cond){
69
+ throw new Error("Assertion failed: "+descr);
70
+ }else{
71
+ //print("Assertion OK: "+descr);
72
+ }
73
+}
74
+
75
+/**
76
+ Convenience form of FShell.fossil.sendCommand(command,payload,ajajOpt).
77
+*/
78
+function send(command,payload, ajajOpt){
79
+ FShell.fossil.sendCommand(command,payload,ajajOpt);
80
+}
81
+
82
+/**
83
+ Asserts that resp is-a Object, resp.fossil is-a string, and
84
+ !resp.resultCode.
85
+*/
86
+function assertResponseOK(resp){
87
+ assert('object' === typeof resp,'Response is-a object.');
88
+ assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
89
+ assert( !resp.resultCode, 'resp.resultCode='+resp.resultCode);
90
+}
91
+/**
92
+ Asserts that resp is-a Object, resp.fossil is-a string, and
93
+ resp.resultCode is a truthy value. If expectCode is set then
94
+ it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
95
+*/
96
+function assertResponseError(resp,expectCode){
97
+ assert('object' === typeof resp,'Response is-a object.');
98
+ assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
99
+ assert( resp.resultCode, 'resp.resultCode='+resp.resultCode);
100
+ if(expectCode){
101
+ assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
102
+ }
103
+}
104
+
105
+FShell.readline = (typeof readline === 'function') ? (readline) : (function() {
106
+ importPackage(java.io);
107
+ importPackage(java.lang);
108
+ var stdin = new BufferedReader(new InputStreamReader(System['in']));
109
+ var self = this;
110
+ return function(prompt) {
111
+ if(prompt) print(prompt);
112
+ var x = stdin.readLine();
113
+ return null===x ? x : String(x) /*convert to JS string!*/;
114
+ };
115
+}());
116
+
117
+FShell.dispatchLine = function(line){
118
+ var av = line.split(' '); // FIXME: to shell-like tokenization. Too tired!
119
+ var cmd = av[0];
120
+ var key, h;
121
+ if('/' == cmd[0]) key = '/';
122
+ else key = this.commandAliases[cmd];
123
+ if(!key) key = cmd;
124
+ h = this.commandHandlers[key];
125
+ if(!h){
126
+ print("Command not known: "+cmd +" ("+key+")");
127
+ }else if(!WhAjaj.isFunction(h)){
128
+ print("Not a function: "+key);
129
+ }
130
+ else{
131
+ print("Sending ["+key+"] command... ");
132
+ try{h(av);}
133
+ catch(e){ print("EXCEPTION: "+e); }
134
+ }
135
+};
136
+
137
+FShell.onResponseDefault = function(callback){
138
+ return function(resp,req){
139
+ assertResponseOK(resp);
140
+ print("Payload: "+(resp.payload ? WhAjaj.stringify(resp.payload) : "none"));
141
+ if(WhAjaj.isFunction(callback)){
142
+ callback(resp,req);
143
+ }
144
+ };
145
+};
146
+FShell.commandHandlers = {
147
+ "?":function(args){
148
+ var k;
149
+ print("Available commands...\n");
150
+ var o = FShell.commandHandlers;
151
+ for(k in o){
152
+ if(! o.hasOwnProperty(k)) continue;
153
+ print("\t"+k);
154
+ }
155
+ },
156
+ "/":function(args){
157
+ FShell.fossil.sendCommand('/json'+args[0],undefined,{
158
+ beforeSend:function(req,opt){
159
+ print("Sending to: "+opt.url);
160
+ },
161
+ onResponse:FShell.onResponseDefault()
162
+ });
163
+ },
164
+ "eval":function(args){
165
+ eval(args.join(' '));
166
+ },
167
+ "login":function(args){
168
+ FShell.fossil.login(args[1], args[2], {
169
+ onResponse:FShell.onResponseDefault()
170
+ });
171
+ },
172
+ "whoami":function(args){
173
+ FShell.fossil.whoami({
174
+ onResponse:FShell.onResponseDefault()
175
+ });
176
+ },
177
+ "HAI":function(args){
178
+ FShell.fossil.HAI({
179
+ onResponse:FShell.onResponseDefault()
180
+ });
181
+ }
182
+
183
+};
184
+FShell.commandAliases = {
185
+ "li":"login",
186
+ "lo":"logout",
187
+ "who":"whoami",
188
+ "hi":"HAI",
189
+ "tci":"/timeline/ci?limit=3"
190
+};
191
+FShell.mainLoop = function(){
192
+ var line;
193
+ var check = /\S/;
194
+ //var isJavaNull = /java\.lang\.null/;
195
+ //print(typeof java.lang['null']);
196
+ while( null != (line=this.readline(this.prompt)) ){
197
+ if(null===line) break /*EOF*/;
198
+ else if( "" === line ) continue;
199
+ //print("Got line: "+line);
200
+ else if(!check.test(line)) continue;
201
+ print('typeof line = '+typeof line);
202
+ this.dispatchLine(line);
203
+ print("");
204
+ }
205
+ print("Bye!");
206
+};
207
+
208
+FShell.mainLoop();
--- a/ajax/i-test/rhino-shell.js
+++ b/ajax/i-test/rhino-shell.js
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/ajax/i-test/rhino-shell.js
+++ b/ajax/i-test/rhino-shell.js
@@ -0,0 +1,208 @@
1 var FShell = {
2 serverUrl:
3 'http://localhost:8080'
4 //'http://fjson/cgi-bin/fossil-json.cgi'
5 //'http://192.168.1.62:8080'
6 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
7 ,
8 verbose:false,
9 prompt:"fossil shell > ",
10 wiki:{},
11 consol:java.lang.System.console(),
12 v:function(msg){
13 if(this.verbose){
14 print("VERBOSE: "+msg);
15 }
16 }
17 };
18 (function bootstrap() {
19 var srcdir = '../js/';
20 var includes = [srcdir+'json2.js',
21 srcdir+'whajaj.js',
22 srcdir+'fossil-ajaj.js'
23 ];
24 for( var i in includes ) {
25 load(includes[i]);
26 }
27 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
28 FShell.fossil = new FossilAjaj({
29 asynchronous:false, /* rhino-based impl doesn't support async. */
30 timeout:10000,
31 url:FShell.serverUrl
32 });
33 print("Server: "+FShell.serverUrl);
34 var cb = FShell.fossil.ajaj.callbacks;
35 cb.beforeSend = function(req,opt){
36 if(!FShell.verbose) return;
37 print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
38 if(req) print("Request envelope="+WhAjaj.stringify(req));
39 };
40 cb.afterSend = function(req,opt){
41 //if(!FShell.verbose) return;
42 //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
43 //if(req) print("Request="+WhAjaj.stringify(req));
44 };
45 cb.onError = function(req,opt){
46 //if(!FShell.verbose) return;
47 print("ERROR: "+WhAjaj.stringify(opt));
48 };
49 cb.onResponse = function(resp,req){
50 if(!FShell.verbose) return;
51 if(resp && resp.resultCode){
52 print("Response contains error info: "+resp.resultCode+": "+resp.resultText);
53 }
54 print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
55 };
56 FShell.fossil.HAI({
57 onResponse:function(resp,opt){
58 assertResponseOK(resp);
59 }
60 });
61 })();
62
63 /**
64 Throws an exception of cond is a falsy value.
65 */
66 function assert(cond, descr){
67 descr = descr || "Undescribed condition.";
68 if(!cond){
69 throw new Error("Assertion failed: "+descr);
70 }else{
71 //print("Assertion OK: "+descr);
72 }
73 }
74
75 /**
76 Convenience form of FShell.fossil.sendCommand(command,payload,ajajOpt).
77 */
78 function send(command,payload, ajajOpt){
79 FShell.fossil.sendCommand(command,payload,ajajOpt);
80 }
81
82 /**
83 Asserts that resp is-a Object, resp.fossil is-a string, and
84 !resp.resultCode.
85 */
86 function assertResponseOK(resp){
87 assert('object' === typeof resp,'Response is-a object.');
88 assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
89 assert( !resp.resultCode, 'resp.resultCode='+resp.resultCode);
90 }
91 /**
92 Asserts that resp is-a Object, resp.fossil is-a string, and
93 resp.resultCode is a truthy value. If expectCode is set then
94 it also asserts that (resp.resultCode=='FOSSIL-'+expectCode).
95 */
96 function assertResponseError(resp,expectCode){
97 assert('object' === typeof resp,'Response is-a object.');
98 assert( 'string' === typeof resp.fossil, 'Response contains fossil property.');
99 assert( resp.resultCode, 'resp.resultCode='+resp.resultCode);
100 if(expectCode){
101 assert( 'FOSSIL-'+expectCode == resp.resultCode, 'Expecting result code '+expectCode );
102 }
103 }
104
105 FShell.readline = (typeof readline === 'function') ? (readline) : (function() {
106 importPackage(java.io);
107 importPackage(java.lang);
108 var stdin = new BufferedReader(new InputStreamReader(System['in']));
109 var self = this;
110 return function(prompt) {
111 if(prompt) print(prompt);
112 var x = stdin.readLine();
113 return null===x ? x : String(x) /*convert to JS string!*/;
114 };
115 }());
116
117 FShell.dispatchLine = function(line){
118 var av = line.split(' '); // FIXME: to shell-like tokenization. Too tired!
119 var cmd = av[0];
120 var key, h;
121 if('/' == cmd[0]) key = '/';
122 else key = this.commandAliases[cmd];
123 if(!key) key = cmd;
124 h = this.commandHandlers[key];
125 if(!h){
126 print("Command not known: "+cmd +" ("+key+")");
127 }else if(!WhAjaj.isFunction(h)){
128 print("Not a function: "+key);
129 }
130 else{
131 print("Sending ["+key+"] command... ");
132 try{h(av);}
133 catch(e){ print("EXCEPTION: "+e); }
134 }
135 };
136
137 FShell.onResponseDefault = function(callback){
138 return function(resp,req){
139 assertResponseOK(resp);
140 print("Payload: "+(resp.payload ? WhAjaj.stringify(resp.payload) : "none"));
141 if(WhAjaj.isFunction(callback)){
142 callback(resp,req);
143 }
144 };
145 };
146 FShell.commandHandlers = {
147 "?":function(args){
148 var k;
149 print("Available commands...\n");
150 var o = FShell.commandHandlers;
151 for(k in o){
152 if(! o.hasOwnProperty(k)) continue;
153 print("\t"+k);
154 }
155 },
156 "/":function(args){
157 FShell.fossil.sendCommand('/json'+args[0],undefined,{
158 beforeSend:function(req,opt){
159 print("Sending to: "+opt.url);
160 },
161 onResponse:FShell.onResponseDefault()
162 });
163 },
164 "eval":function(args){
165 eval(args.join(' '));
166 },
167 "login":function(args){
168 FShell.fossil.login(args[1], args[2], {
169 onResponse:FShell.onResponseDefault()
170 });
171 },
172 "whoami":function(args){
173 FShell.fossil.whoami({
174 onResponse:FShell.onResponseDefault()
175 });
176 },
177 "HAI":function(args){
178 FShell.fossil.HAI({
179 onResponse:FShell.onResponseDefault()
180 });
181 }
182
183 };
184 FShell.commandAliases = {
185 "li":"login",
186 "lo":"logout",
187 "who":"whoami",
188 "hi":"HAI",
189 "tci":"/timeline/ci?limit=3"
190 };
191 FShell.mainLoop = function(){
192 var line;
193 var check = /\S/;
194 //var isJavaNull = /java\.lang\.null/;
195 //print(typeof java.lang['null']);
196 while( null != (line=this.readline(this.prompt)) ){
197 if(null===line) break /*EOF*/;
198 else if( "" === line ) continue;
199 //print("Got line: "+line);
200 else if(!check.test(line)) continue;
201 print('typeof line = '+typeof line);
202 this.dispatchLine(line);
203 print("");
204 }
205 print("Bye!");
206 };
207
208 FShell.mainLoop();
--- a/ajax/i-test/rhino-test.js
+++ b/ajax/i-test/rhino-test.js
@@ -0,0 +1,140 @@
1
+//osb.flush(cription = 'Log out anonp://fjson/cgi-bin/fossil-json.cgi'
2
+ //'http://192.168.1.62:8080'
3
+ //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.fals'
4
+ ,
5
+ verbose:true,
6
+ fossilBinary:'fossil',
7
+ wiki:{}
8
+}var Ter = new java.io.OutputStreamWriter(outsartifactfossil-json.cgi'
9
+ //'http://192.168.1.62:8080'
10
+ //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
11
+ ,
12
+ verbose:true,
13
+ fossilBinary:'fossil',
14
+ wiki:{}
15
+};
16
+(function bootstrap() {
17
+ var srcdir = '../js/';
18
+ var includes = [srcdir+'json2.js',
19
+ srcdir+'whajaj.js',
20
+ srcdir+'fossil-ajaj.js'
21
+ ];
22
+ for( var i in includes ) {
23
+ load(includes[i]);
24
+ }
25
+ WhAjaj.Connector.prototype.sendImpl =ttp://192.168.1.62:8080'
26
+ //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
27
+ ,
28
+ verbose:true,
29
+ fossilBinary:'fossil',
30
+ wiki:{}
31
+};
32
+(function bootstrap() {
33
+ var srcdir = '../js/';
34
+ var includes = [srcdir+'json2.js',
35
+ srcdir+'whajaj.js',
36
+ srcdir+'fossil-ajaj.js'
37
+ ];
38
+ for( var i in includes ) {
39
+ load(includes[i]);
40
+ }
41
+ WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
42
+ TestApp.fossil = new FossilAjaj({
43
+ asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
44
+ timeout:0,
45
+ url:TestApp.serverUrl,
46
+ fossilBinary:TestApp.fossilBinary
47
+ });
48
+ var cb = TestApp.fossil.ajaj.callbacks;
49
+ cb.beforeSend = function(req,opt){
50
+ if(!TestApp.verbose) return;
51
+ print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
52
+ if(req) print("Request envelope="+WhAjaj.stringify(req));
53
+ };
54
+ cb.afterSend = function(req,opt){
55
+ //if(!TestApp.verbose) return;
56
+ //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
57
+ //if(req) print("Request="+WhAjaj.stringify(req));
58
+ };
59
+ cb.onError = function(req,opt){
60
+ if(!TestApp.verbose) return;
61
+ print("ERROR: "+WhAjaj.stringify(opt));
62
+ };
63
+ cb.onResponse = function(resp,req){
64
+ if(!TestApp.verbose) return;
65
+ print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
66
+ };
67
+
68
+})();
69
+
70
+/**
71
+ Throws an exception of cond is a falsy value.
72
+*/
73
+function assert(cond, descr){
74
+ descr = descr || "Undescribed condition.";
75
+ if(!cond){
76
+ print("Assertion FAILED: "+descr);
77
+ throw new Error("Assertion failed: "+descr);
78
+ // aarrgghh. Exceptions are of course swallowed by
79
+ // the AJAX layer, to keep from killing a browser's
80
+ // script environment.
81
+ }else{
82
+ if(TestAplls func() in a try/catch block and throws an exception if
83
+ func() does NOT throw.
84
+*/
85
+function assertThrows(func, descr){
86
+ descr = descr || "Undescribed condition failed.";
87
+ var ex;
88
+ try{
89
+ func();
90
+ }catch(e){
91
+ ex = e;
92
+ }
93
+ if(!ex){
94
+ throw new Error("Function did not throw (as expected): "+descr);
95
+ }else{
96
+ if(TestApp.ver
97
+}
98
+testIAmNobody.description = 'Ensure that current user is "nobody".';
99
+
100
+
101
+function testAnonymousLogin(){
102
+ //'http://fossi onRe/192.168.1.62:8080'
103
+ //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
104
+ ,
105
+ verbose:true,
106
+ fossilBinary:'fossil',
107
+ wiki:{}
108
+};
109
+(function bootstrap() {
110
+ var srcdir = '../js/';
111
+ var includes = [srcdir+'json2.js',
112
+ srcdir+'whajaj.js',
113
+ srcdir+'fossil-ajaj.js'
114
+ ];
115
+ for( var i in includes ) {
116
+ load(includes[i]);
117
+ }
118
+ WhAjaj.Connector.prototype.sendImpl =ttp://192.168.1.62:8080'
119
+ //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
120
+ ,
121
+ verbose:true,
122
+ fossilBinary:'fossil',
123
+ wiki:{}
124
+};
125
+(function bootstrap() {
126
+ var srcdir = '../js/';
127
+ var includes = [srcdir+'json2.j//.js'
128
+ srcdir+'whajaj.js',
129
+ ];
130
+ for( var i in includes ) {
131
+ load(includes[i]);
132
+ }
133
+ WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
134
+ TestApp.fossil = new FossilAjaj({
135
+ asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
136
+ timeout:0,
137
+ url:TestApp.serverUrl,
138
+ fossilBinary:TestApp.fossilBinary
139
+ });
140
+ var cb = TestApp.fossil.ajaj.callbac
--- a/ajax/i-test/rhino-test.js
+++ b/ajax/i-test/rhino-test.js
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/ajax/i-test/rhino-test.js
+++ b/ajax/i-test/rhino-test.js
@@ -0,0 +1,140 @@
1 //osb.flush(cription = 'Log out anonp://fjson/cgi-bin/fossil-json.cgi'
2 //'http://192.168.1.62:8080'
3 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.fals'
4 ,
5 verbose:true,
6 fossilBinary:'fossil',
7 wiki:{}
8 }var Ter = new java.io.OutputStreamWriter(outsartifactfossil-json.cgi'
9 //'http://192.168.1.62:8080'
10 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
11 ,
12 verbose:true,
13 fossilBinary:'fossil',
14 wiki:{}
15 };
16 (function bootstrap() {
17 var srcdir = '../js/';
18 var includes = [srcdir+'json2.js',
19 srcdir+'whajaj.js',
20 srcdir+'fossil-ajaj.js'
21 ];
22 for( var i in includes ) {
23 load(includes[i]);
24 }
25 WhAjaj.Connector.prototype.sendImpl =ttp://192.168.1.62:8080'
26 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
27 ,
28 verbose:true,
29 fossilBinary:'fossil',
30 wiki:{}
31 };
32 (function bootstrap() {
33 var srcdir = '../js/';
34 var includes = [srcdir+'json2.js',
35 srcdir+'whajaj.js',
36 srcdir+'fossil-ajaj.js'
37 ];
38 for( var i in includes ) {
39 load(includes[i]);
40 }
41 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
42 TestApp.fossil = new FossilAjaj({
43 asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
44 timeout:0,
45 url:TestApp.serverUrl,
46 fossilBinary:TestApp.fossilBinary
47 });
48 var cb = TestApp.fossil.ajaj.callbacks;
49 cb.beforeSend = function(req,opt){
50 if(!TestApp.verbose) return;
51 print("SENDING REQUEST: AJAJ options="+JSON.stringify(opt));
52 if(req) print("Request envelope="+WhAjaj.stringify(req));
53 };
54 cb.afterSend = function(req,opt){
55 //if(!TestApp.verbose) return;
56 //print("REQUEST RETURNED: opt="+JSON.stringify(opt));
57 //if(req) print("Request="+WhAjaj.stringify(req));
58 };
59 cb.onError = function(req,opt){
60 if(!TestApp.verbose) return;
61 print("ERROR: "+WhAjaj.stringify(opt));
62 };
63 cb.onResponse = function(resp,req){
64 if(!TestApp.verbose) return;
65 print("GOT RESPONSE: "+(('string'===typeof resp) ? resp : WhAjaj.stringify(resp)));
66 };
67
68 })();
69
70 /**
71 Throws an exception of cond is a falsy value.
72 */
73 function assert(cond, descr){
74 descr = descr || "Undescribed condition.";
75 if(!cond){
76 print("Assertion FAILED: "+descr);
77 throw new Error("Assertion failed: "+descr);
78 // aarrgghh. Exceptions are of course swallowed by
79 // the AJAX layer, to keep from killing a browser's
80 // script environment.
81 }else{
82 if(TestAplls func() in a try/catch block and throws an exception if
83 func() does NOT throw.
84 */
85 function assertThrows(func, descr){
86 descr = descr || "Undescribed condition failed.";
87 var ex;
88 try{
89 func();
90 }catch(e){
91 ex = e;
92 }
93 if(!ex){
94 throw new Error("Function did not throw (as expected): "+descr);
95 }else{
96 if(TestApp.ver
97 }
98 testIAmNobody.description = 'Ensure that current user is "nobody".';
99
100
101 function testAnonymousLogin(){
102 //'http://fossi onRe/192.168.1.62:8080'
103 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
104 ,
105 verbose:true,
106 fossilBinary:'fossil',
107 wiki:{}
108 };
109 (function bootstrap() {
110 var srcdir = '../js/';
111 var includes = [srcdir+'json2.js',
112 srcdir+'whajaj.js',
113 srcdir+'fossil-ajaj.js'
114 ];
115 for( var i in includes ) {
116 load(includes[i]);
117 }
118 WhAjaj.Connector.prototype.sendImpl =ttp://192.168.1.62:8080'
119 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi'
120 ,
121 verbose:true,
122 fossilBinary:'fossil',
123 wiki:{}
124 };
125 (function bootstrap() {
126 var srcdir = '../js/';
127 var includes = [srcdir+'json2.j//.js'
128 srcdir+'whajaj.js',
129 ];
130 for( var i in includes ) {
131 load(includes[i]);
132 }
133 WhAjaj.Connector.prototype.sendImpl = WhAjaj.Connector.sendImpls.rhino;
134 TestApp.fossil = new FossilAjaj({
135 asynchronous:false, /* rhino-based impl doesn't support async or timeout. */
136 timeout:0,
137 url:TestApp.serverUrl,
138 fossilBinary:TestApp.fossilBinary
139 });
140 var cb = TestApp.fossil.ajaj.callbac
--- a/ajax/index.html
+++ b/ajax/index.html
@@ -0,0 +1 @@
1
+<!get Fossil
--- a/ajax/index.html
+++ b/ajax/index.html
@@ -0,0 +1 @@
 
--- a/ajax/index.html
+++ b/ajax/index.html
@@ -0,0 +1 @@
1 <!get Fossil
--- a/ajax/js/fossil-ajaj.js
+++ b/ajax/js/fossil-ajaj.js
@@ -0,0 +1,236 @@
1
+/**
2
+ This file contains a WhAjaj extension for use with Fossil/JSON.
3
+
4
+ Author: Stephan Beal ([email protected])
5
+
6
+ License: Public Domain
7
+*/
8
+
9
+/**
10
+ Constructor for a new Fossil AJAJ client. ajajOpt may be an optional
11
+ object suitable for passing to the WhAja j.Connector() constructor.
12
+
13
+ On returning, this.ajaj is-a WhAjaj.C onnector instance which can
14
+ be used to send requests to the back- end (though the convenience
15
+ functions of this class are the prefe rred way to do it). Clients
16
+ are encouraged to use FossilAjaj.sendComm
17
+ of the underlying WhAjaj.Connector API, since this class' API
18
+ contains Fossil-specific request-calling handling (e.g. of authentication
19
+ info) whereas WhAjaj is more generic.
20
+*/
21
+function FossilAjaj(ajajOpt)
22
+{
23
+ this.ajaj = new WhAjaj.Connector(ajajOpt);
24
+ return this;
25
+}
26
+
27
+FossilAjaj.prototype.generateRequestId = function() {
28
+ return this.ajaj.generateRequestId();
29
+};
30
+
31
+/**
32
+ Proxy for this.ajaj.sendRequest().
33
+*/
34
+FossilAjaj.prototype.sendRequest = function(req,opt) {
35
+ return this.ajaj.sendRequest(req,opt);
36
+};
37
+
38
+/**
39
+ Sends a command to the fossil back-end. Command should be the
40
+ path part of the URL, e.g. /json/stat, payload is a request-specific
41
+ value type (may often be null/undefined). ajajOpt is an optional object
42
+ holding WhAjaj.sendReq uest()-compatible options.
43
+
44
+ This function constructs a Fossil/JSON request envelope based
45
+ on the given arguments and adds this.auth.authToken and a requestId
46
+ to it.
47
+*/
48
+FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
49
+ var req;
50
+ ajajOpt = ajajOpt || {};
51
+ if(payload || (this.auth && this.auth.authToken) || ajajOpt.jsonp) {
52
+ req = {
53
+ payload:payload,
54
+ requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
55
+ authToken:(this.auth ? this.auth.authToken : undefined),
56
+ jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
57
+ };
58
+ }
59
+ ajajOpt.method = req ? 'POST' : 'GET';
60
+ // just for debuggering: ajajOpt.method = 'POST'; if(!req) req={};
61
+ if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
62
+ this.ajaj.sendRequest(req,ajajOpt);
63
+};
64
+
65
+/**
66
+ Sends a login request to the back-end.
67
+
68
+ ajajOpt is
69
+ to sendCommand().
70
+
71
+ g. of authentication
72
+ info) whereas WhAjaj is more generic.
73
+*/
74
+function Foss ilAjaj(ajajOpt)
75
+{
76
+ this.ajaj = new WhAjaj.Connector(ajajOpt);
77
+ return this;
78
+}
79
+
80
+FossilAjaj.prototype.generateRequestId = function() {
81
+ return this.ajaj.generateRequestId();
82
+};
83
+
84
+/**
85
+ Proxy for this.ajaj.sendRequest().
86
+*/
87
+FossilAjaj.prototype.sendRequest = function(req,opt) {
88
+ return this.ajaj.sendRequest(req,opt);
89
+};
90
+
91
+/**
92
+ Sends a command to the fossil back-end. Command should be the
93
+ path part of the URL, e.g. /json/stat, payload is a request sing
94
+ to sendCommand().
95
+
96
+ ten be null/undefined). ajajOpt is an optional object
97
+ holding WhAjaj.sendRequest()-compatible options.
98
+
99
+ This function constructs a Fossil/JSON request envelope based
100
+ on the given arguments and adds this.auth.authToken and a requestId
101
+ to it.
102
+*/
103
+FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
104
+ var req;
105
+ ajajOpt = ajajOpt || {};
106
+ if(payload || (this.auth && this.auth.authToken) || ajajOpt.jsonp) {
107
+ req = {
108
+ payload:payload,
109
+ requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
110
+ authToken:(this.auth ? this.auth.authToken : undefined),
111
+ jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
112
+ };
113
+ }
114
+ ajajOpt.method = req ? 'POST' : 'GET';
115
+ // just for debuggering: ajajOpt.method = 'POST'; if(!req) req={};
116
+ if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
117
+ this.ajaj.sendRequest(req,ajajOpt);
118
+};
119
+
120
+/**
121
+ Sends a login request to the back-end.
122
+
123
+ ajajOpt is an optional configuration object suitable for passing
124
+ to sendCommand().
125
+
126
+ After the response returns, this.auth will be
127
+ set to the response payload.
128
+
129
+ If name === 'anonymous' (the default if none is passed in) then this
130
+ function ignores the pw argument and must make two requests - the first
131
+ one gets the captcha code and the second one submits it.
132
+ ajajOpt.onResponse() (if set) is only called for the actual login
133
+ response (the 2nd one), as opposed to being called for both requests.
134
+ However, this.ajaj.callbacks.onResponse() _is_ called for both (because
135
+ it happens at a lower level).
136
+
137
+ If this object has an onLogin() function it is called (with
138
+ no arguments) before the onResponse() handler of the login is called
139
+ (that is the 2nd request for anonymous logins) and any exceptions
140
+ it throws are ignored.
141
+
142
+*/
143
+FossilAj
144
+ to sendCommand().
145
+ sing
146
+ to sendCommand().
147
+
148
+ If this object has an onLogout() function it is called (with
149
+ no arguments) before the onResponse() handler is called.
150
+ IFF the response succeeds then this.auth is unset.
151
+*/
152
+FossilAjaj.prototype.logout = function(ajajOpt) {
153
+ var self = this;
154
+ ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
155
+ var oldOnResponse = ajajOpt.onResponse;
156
+ ajajOpt.onResponse = function(resp,req) {
157
+ var thisOpt = this;
158
+ self.auth = undefined;
159
+ if( WhAjaj.isFunction( self.onLogout ) ){
160
+ try{ self.onLogout(); }
161
+ catch(e){}
162
+ }
163
+ if( WhAjaj.isFunction(oldOnResponse) ) {
164
+ oldOnResponse.apply(thisOpt,[resp,req]);
165
+ }
166
+ };
167
+ this.sendCommand('/json/logout', undefined, ajajOpt );
168
+};
169
+
170
+/**
171
+ Sends a HAI request to the server. /json/HAI is an alias /json/version.
172
+
173
+ ajajOpt is an optional configuration
174
+ to sendCommand().
175
+*/
176
+FossilAjaj.prototype.HAI = function(ajajOpt) {
177
+ this.sendCommand('/json/HAI', undefined, ajajOpt);
178
+};
179
+
180
+
181
+/**
182
+ Sends a /json/whoami request. Updates this.auth to contain
183
+ the login info, removing them if the response does not contain
184
+ that data.
185
+*/
186
+FossilAjaj.prototype.whoami = function(ajajOpt) {
187
+ var self = this;
188
+ ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
189
+ var oldOnResponse = ajajOpt.onResponse;
190
+ ajajOpt.onResponse = function(resp,req) {
191
+ var thisOpt = this;
192
+ if( resp && resp.payload ){
193
+ if(!self.auth || (self.auth.authToken!==resp.payload.authToken)){
194
+ self.auth = resp.payload;
195
+ if
196
+*/
197
+jaj exAjaj.rhinoLocalBinarySendImpl = function(request,args){
198
+ var self = this;
199
+ request = request || {};
200
+ if(!args.fossilBinary){
201
+ throw new Error("fossilBinary is not set on AJAX options!");
202
+ }
203
+ var url = args.url.split('?')[0].split(/\/+/);
204
+ if(url.length>1){
205
+ // 3x shift(): protocol, host, 'json' part of path
206
+ request.command = (url.shift(),url.shift(),url.shift(), url.join('/'));
207
+ }
208
+ delete args.url;
209
+ //print("rhinoLocalBinarySendImpl SENDING: "+WhAjaj.stringify(request)); WhAjaj.stringify(request));
210
+ var json;
211
+ try{
212
+ var pargs = [args.fossilBinary, 'json', '--json-input', '-'];
213
+ var p = java.lang.Runtime.getRuntime().exec(pargs);
214
+ var outs = p.getOutputStream();
215
+ var osr = new java.io.OutputStreamWriter(outs
216
+ json = JSON.stringify(request);
217
+ osb.write(json,0, json.length);
218
+ osb.close();
219
+ var ins = p.getInputStream();
220
+ var isr = new java.io.InputStreamReader(ins);
221
+ var br = new java.io.BufferedReader(isr);
222
+ var line;
223
+ json = [];
224
+ while( null !== (line=br.readLine())){
225
+ json.push(line);
226
+ }
227
+ ins.close();
228
+ }catch(e){
229
+ args.errorMessage = e.toString();
230
+ WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
231
+ return undefined;
232
+ }
233
+ json = json.join('');
234
+ //print("READ IN JSON: "+json);
235
+ WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
236
+}/*rhinoLocalBinary*/
--- a/ajax/js/fossil-ajaj.js
+++ b/ajax/js/fossil-ajaj.js
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/ajax/js/fossil-ajaj.js
+++ b/ajax/js/fossil-ajaj.js
@@ -0,0 +1,236 @@
1 /**
2 This file contains a WhAjaj extension for use with Fossil/JSON.
3
4 Author: Stephan Beal ([email protected])
5
6 License: Public Domain
7 */
8
9 /**
10 Constructor for a new Fossil AJAJ client. ajajOpt may be an optional
11 object suitable for passing to the WhAja j.Connector() constructor.
12
13 On returning, this.ajaj is-a WhAjaj.C onnector instance which can
14 be used to send requests to the back- end (though the convenience
15 functions of this class are the prefe rred way to do it). Clients
16 are encouraged to use FossilAjaj.sendComm
17 of the underlying WhAjaj.Connector API, since this class' API
18 contains Fossil-specific request-calling handling (e.g. of authentication
19 info) whereas WhAjaj is more generic.
20 */
21 function FossilAjaj(ajajOpt)
22 {
23 this.ajaj = new WhAjaj.Connector(ajajOpt);
24 return this;
25 }
26
27 FossilAjaj.prototype.generateRequestId = function() {
28 return this.ajaj.generateRequestId();
29 };
30
31 /**
32 Proxy for this.ajaj.sendRequest().
33 */
34 FossilAjaj.prototype.sendRequest = function(req,opt) {
35 return this.ajaj.sendRequest(req,opt);
36 };
37
38 /**
39 Sends a command to the fossil back-end. Command should be the
40 path part of the URL, e.g. /json/stat, payload is a request-specific
41 value type (may often be null/undefined). ajajOpt is an optional object
42 holding WhAjaj.sendReq uest()-compatible options.
43
44 This function constructs a Fossil/JSON request envelope based
45 on the given arguments and adds this.auth.authToken and a requestId
46 to it.
47 */
48 FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
49 var req;
50 ajajOpt = ajajOpt || {};
51 if(payload || (this.auth && this.auth.authToken) || ajajOpt.jsonp) {
52 req = {
53 payload:payload,
54 requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
55 authToken:(this.auth ? this.auth.authToken : undefined),
56 jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
57 };
58 }
59 ajajOpt.method = req ? 'POST' : 'GET';
60 // just for debuggering: ajajOpt.method = 'POST'; if(!req) req={};
61 if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
62 this.ajaj.sendRequest(req,ajajOpt);
63 };
64
65 /**
66 Sends a login request to the back-end.
67
68 ajajOpt is
69 to sendCommand().
70
71 g. of authentication
72 info) whereas WhAjaj is more generic.
73 */
74 function Foss ilAjaj(ajajOpt)
75 {
76 this.ajaj = new WhAjaj.Connector(ajajOpt);
77 return this;
78 }
79
80 FossilAjaj.prototype.generateRequestId = function() {
81 return this.ajaj.generateRequestId();
82 };
83
84 /**
85 Proxy for this.ajaj.sendRequest().
86 */
87 FossilAjaj.prototype.sendRequest = function(req,opt) {
88 return this.ajaj.sendRequest(req,opt);
89 };
90
91 /**
92 Sends a command to the fossil back-end. Command should be the
93 path part of the URL, e.g. /json/stat, payload is a request sing
94 to sendCommand().
95
96 ten be null/undefined). ajajOpt is an optional object
97 holding WhAjaj.sendRequest()-compatible options.
98
99 This function constructs a Fossil/JSON request envelope based
100 on the given arguments and adds this.auth.authToken and a requestId
101 to it.
102 */
103 FossilAjaj.prototype.sendCommand = function(command, payload, ajajOpt) {
104 var req;
105 ajajOpt = ajajOpt || {};
106 if(payload || (this.auth && this.auth.authToken) || ajajOpt.jsonp) {
107 req = {
108 payload:payload,
109 requestId:('function' === typeof this.generateRequestId) ? this.generateRequestId() : undefined,
110 authToken:(this.auth ? this.auth.authToken : undefined),
111 jsonp:('string' === typeof ajajOpt.jsonp) ? ajajOpt.jsonp : undefined
112 };
113 }
114 ajajOpt.method = req ? 'POST' : 'GET';
115 // just for debuggering: ajajOpt.method = 'POST'; if(!req) req={};
116 if(command) ajajOpt.url = this.ajaj.derivedOption('url',ajajOpt) + command;
117 this.ajaj.sendRequest(req,ajajOpt);
118 };
119
120 /**
121 Sends a login request to the back-end.
122
123 ajajOpt is an optional configuration object suitable for passing
124 to sendCommand().
125
126 After the response returns, this.auth will be
127 set to the response payload.
128
129 If name === 'anonymous' (the default if none is passed in) then this
130 function ignores the pw argument and must make two requests - the first
131 one gets the captcha code and the second one submits it.
132 ajajOpt.onResponse() (if set) is only called for the actual login
133 response (the 2nd one), as opposed to being called for both requests.
134 However, this.ajaj.callbacks.onResponse() _is_ called for both (because
135 it happens at a lower level).
136
137 If this object has an onLogin() function it is called (with
138 no arguments) before the onResponse() handler of the login is called
139 (that is the 2nd request for anonymous logins) and any exceptions
140 it throws are ignored.
141
142 */
143 FossilAj
144 to sendCommand().
145 sing
146 to sendCommand().
147
148 If this object has an onLogout() function it is called (with
149 no arguments) before the onResponse() handler is called.
150 IFF the response succeeds then this.auth is unset.
151 */
152 FossilAjaj.prototype.logout = function(ajajOpt) {
153 var self = this;
154 ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
155 var oldOnResponse = ajajOpt.onResponse;
156 ajajOpt.onResponse = function(resp,req) {
157 var thisOpt = this;
158 self.auth = undefined;
159 if( WhAjaj.isFunction( self.onLogout ) ){
160 try{ self.onLogout(); }
161 catch(e){}
162 }
163 if( WhAjaj.isFunction(oldOnResponse) ) {
164 oldOnResponse.apply(thisOpt,[resp,req]);
165 }
166 };
167 this.sendCommand('/json/logout', undefined, ajajOpt );
168 };
169
170 /**
171 Sends a HAI request to the server. /json/HAI is an alias /json/version.
172
173 ajajOpt is an optional configuration
174 to sendCommand().
175 */
176 FossilAjaj.prototype.HAI = function(ajajOpt) {
177 this.sendCommand('/json/HAI', undefined, ajajOpt);
178 };
179
180
181 /**
182 Sends a /json/whoami request. Updates this.auth to contain
183 the login info, removing them if the response does not contain
184 that data.
185 */
186 FossilAjaj.prototype.whoami = function(ajajOpt) {
187 var self = this;
188 ajajOpt = this.ajaj.normalizeAjaxParameters( ajajOpt || {} );
189 var oldOnResponse = ajajOpt.onResponse;
190 ajajOpt.onResponse = function(resp,req) {
191 var thisOpt = this;
192 if( resp && resp.payload ){
193 if(!self.auth || (self.auth.authToken!==resp.payload.authToken)){
194 self.auth = resp.payload;
195 if
196 */
197 jaj exAjaj.rhinoLocalBinarySendImpl = function(request,args){
198 var self = this;
199 request = request || {};
200 if(!args.fossilBinary){
201 throw new Error("fossilBinary is not set on AJAX options!");
202 }
203 var url = args.url.split('?')[0].split(/\/+/);
204 if(url.length>1){
205 // 3x shift(): protocol, host, 'json' part of path
206 request.command = (url.shift(),url.shift(),url.shift(), url.join('/'));
207 }
208 delete args.url;
209 //print("rhinoLocalBinarySendImpl SENDING: "+WhAjaj.stringify(request)); WhAjaj.stringify(request));
210 var json;
211 try{
212 var pargs = [args.fossilBinary, 'json', '--json-input', '-'];
213 var p = java.lang.Runtime.getRuntime().exec(pargs);
214 var outs = p.getOutputStream();
215 var osr = new java.io.OutputStreamWriter(outs
216 json = JSON.stringify(request);
217 osb.write(json,0, json.length);
218 osb.close();
219 var ins = p.getInputStream();
220 var isr = new java.io.InputStreamReader(ins);
221 var br = new java.io.BufferedReader(isr);
222 var line;
223 json = [];
224 while( null !== (line=br.readLine())){
225 json.push(line);
226 }
227 ins.close();
228 }catch(e){
229 args.errorMessage = e.toString();
230 WhAjaj.Connector.sendHelper.onSendError.apply( self, [request, args] );
231 return undefined;
232 }
233 json = json.join('');
234 //print("READ IN JSON: "+json);
235 WhAjaj.Connector.sendHelper.onSendSuccess.apply( self, [request, json, args] );
236 }/*rhinoLocalBinary*/
--- a/ajax/js/json2.js
+++ b/ajax/js/json2.js
@@ -0,0 +1,476 @@
1
+/*
2
+ http://www.JSON.org/json2.js
3
+ 2009-06-29
4
+
5
+ Public Domain.
6
+
7
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8
+
9
+ See http://www.JSON.org/js.html
10
+
11
+ This file creates a global JSON object containing two methods: stringify
12
+ and parse.
13
+
14
+ JSON.stringify(value, replacer, space)
15
+ value any JavaScript value, usually an object or array.
16
+
17
+ replacer an optional parameter that determines how object
18
+ values are stringified for objects. It can be a
19
+ function or an array of strings.
20
+
21
+ space an optional parameter that specifies the indentation
22
+ of nested structures. If it is omitted, the text will
23
+ be packed without extra whitespace. If it is a number,
24
+ it will specify the number of spaces to indent at each
25
+ level. If it is a string (such as '\t' or '&nbsp;'),
26
+ it contains the characters used to indent at each level.
27
+
28
+ This method produces a JSON text from a JavaScript value.
29
+
30
+ When an object value is found, if the object contains a toJSON
31
+ method, its toJSON method will be called and the result will be
32
+ stringified. A toJSON method does not serialize: it returns the
33
+ value represented by the name/value pair that should be serialized,
34
+ or undefined if nothing should be serialized. The toJSON method
35
+ will be passed the key associated with the value, and this will be
36
+ bound to the object holding the key.
37
+
38
+ For example, this would serialize Dates as ISO strings.
39
+
40
+ Date.prototype.toJSON = function (key) {
41
+ function f(n) {
42
+ // Format integers to have at least two digits.
43
+ return n < 10 ? '0' + n : n;
44
+ }
45
+
46
+ return this.getUTCFullYear() + '-' +
47
+ f(this.getUTCMonth() + 1) + '-' +
48
+ f(this.getUTCDate()) + 'T' +
49
+ f(this.getUTCHours()) + ':' +
50
+ f(this.getUTCMinutes()) + ':' +
51
+ f(this.getUTCSeconds()) + 'Z';
52
+ };
53
+
54
+ You can provide an optional replacer method. It will be passed the
55
+ key and value of each member, with this bound to the containing
56
+ object. The value that is returned from your method will be
57
+ serialized. If your method returns undefined, then the member will
58
+ be excluded from the serialization.
59
+
60
+ If the replacer parameter is an array of strings, then it will be
61
+ used to select the members to be serialized. It filters the results
62
+ such that only members with keys listed in the replacer array are
63
+ stringified.
64
+
65
+ Values that do not have JSON representations, such as undefined or
66
+ functions, will not be serialized. Such values in objects will be
67
+ dropped; in arrays they will be replaced with null. You can use
68
+ a replacer function to replace those with JSON values.
69
+ JSON.stringify(undefined) returns undefined.
70
+
71
+ The optional space parameter produces a stringification of the
72
+ value that is filled with line breaks and indentation to make it
73
+ easier to read.
74
+
75
+ If the space parameter is a non-empty string, then that string will
76
+ be used for indentation. If the space parameter is a number, then
77
+ the indentation will be that many spaces.
78
+
79
+ Example:
80
+
81
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
82
+ // text is '["e",{"pluribus":"unum"}]'
83
+
84
+
85
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
86
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
87
+
88
+ text = JSON.stringify([new Date()], function (key, value) {
89
+ return this[key] instanceof Date ?
90
+ 'Date(' + this[key] + ')' : value;
91
+ });
92
+ // text is '["Date(---current time---)"]'
93
+
94
+
95
+ JSON.parse(text, reviver)
96
+ This method parses a JSON text to produce an object or array.
97
+ It can throw a SyntaxError exception.
98
+
99
+ The optional reviver parameter is a function that can filter and
100
+ transform the results. It receives each of the keys and values,
101
+ and its return value is used instead of the original value.
102
+ If it returns what it received, then the structure is not modified.
103
+ If it returns undefined then the member is deleted.
104
+
105
+ Example:
106
+
107
+ // Parse the text. Values that look like ISO date strings will
108
+ // be converted to Date objects.
109
+
110
+ myData = JSON.parse(text, function (key, value) {
111
+ var a;
112
+ if (typeof value === 'string') {
113
+ a =
114
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
115
+ if (a) {
116
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
117
+ +a[5], +a[6]));
118
+ }
119
+ }
120
+ return value;
121
+ });
122
+
123
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
124
+ var d;
125
+ if (typeof value === 'string' &&
126
+ value.slice(0, 5) === 'Date(' &&
127
+ value.slice(-1) === ')') {
128
+ d = new Date(value.slice(5, -1));
129
+ if (d) {
130
+ return d;
131
+ }
132
+ }
133
+ return value;
134
+ });
135
+
136
+
137
+ This is a reference implementation. You are free to copy, modify, or
138
+ redistribute.
139
+
140
+ This code should be minified before deployment.
141
+ See http://javascript.crockford.com/jsmin.html
142
+
143
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
144
+ NOT CONTROL.
145
+*/
146
+
147
+/*jslint evil: true */
148
+
149
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
150
+ call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
151
+ getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
152
+ lastIndex, length, parse, prototype, push, replace, slice, stringify,
153
+ test, toJSON, toString, valueOf
154
+*/
155
+
156
+// Create a JSON object only if one does not already exist. We create the
157
+// methods in a closure to avoid creating global variables.
158
+
159
+var JSON = JSON || {};
160
+
161
+(function () {
162
+
163
+ function f(n) {
164
+ // Format integers to have at least two digits.
165
+ return n < 10 ? '0' + n : n;
166
+ }
167
+
168
+ if (typeof Date.prototype.toJSON !== 'function') {
169
+
170
+ Date.prototype.toJSON = function (key) {
171
+
172
+ return isFinite(this.valueOf()) ?
173
+ this.getUTCFullYear() + '-' +
174
+ f(this.getUTCMonth() + 1) + '-' +
175
+ f(this.getUTCDate()) + 'T' +
176
+ f(this.getUTCHours()) + ':' +
177
+ f(this.getUTCMinutes()) + ':' +
178
+ f(this.getUTCSeconds()) + 'Z' : null;
179
+ };
180
+
181
+ String.prototype.toJSON =
182
+ Number.prototype.toJSON =
183
+ Boolean.prototype.toJSON = function (key) {
184
+ return this.valueOf();
185
+ };
186
+ }
187
+
188
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
189
+ escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
190
+ gap,
191
+ indent,
192
+ meta = { // table of character substitutions
193
+ '\b': '\\b',
194
+ '\t': '\\t',
195
+ '\n': '\\n',
196
+ '\f': '\\f',
197
+ '\r': '\\r',
198
+ '"' : '\\"',
199
+ '\\': '\\\\'
200
+ },
201
+ rep;
202
+
203
+
204
+ function quote(string) {
205
+
206
+// If the string contains no control characters, no quote characters, and no
207
+// backslash characters, then we can safely slap some quotes around it.
208
+// Otherwise we must also replace the offending characters with safe escape
209
+// sequences.
210
+
211
+ escapable.lastIndex = 0;
212
+ return escapable.test(string) ?
213
+ '"' + string.replace(escapable, function (a) {
214
+ var c = meta[a];
215
+ return typeof c === 'string' ? c :
216
+ '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
217
+ }) + '"' :
218
+ '"' + string + '"';
219
+ }
220
+
221
+
222
+ function str(key, holder) {
223
+
224
+// Produce a string from holder[key].
225
+
226
+ var i, // The loop counter.
227
+ k, // The member key.
228
+ v, // The member value.
229
+ length,
230
+ mind = gap,
231
+ partial,
232
+ value = holder[key];
233
+
234
+// If the value has a toJSON method, call it to obtain a replacement value.
235
+
236
+ if (value && typeof value === 'object' &&
237
+ typeof value.toJSON === 'function') {
238
+ value = value.toJSON(key);
239
+ }
240
+
241
+// If we were called with a replacer function, then call the replacer to
242
+// obtain a replacement value.
243
+
244
+ if (typeof rep === 'function') {
245
+ value = rep.call(holder, key, value);
246
+ }
247
+
248
+// What happens next depends on the value's type.
249
+
250
+ switch (typeof value) {
251
+ case 'string':
252
+ return quote(value);
253
+
254
+ case 'number':
255
+
256
+// JSON numbers must be finite. Encode non-finite numbers as null.
257
+
258
+ return isFinite(value) ? String(value) : 'null';
259
+
260
+ case 'boolean':
261
+ case 'null':
262
+
263
+// If the value is a boolean or null, convert it to a string. Note:
264
+// typeof null does not produce 'null'. The case is included here in
265
+// the remote chance that this gets fixed someday.
266
+
267
+ return String(value);
268
+
269
+// If the type is 'object', we might be dealing with an object or an array or
270
+// null.
271
+
272
+ case 'object':
273
+
274
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
275
+// so watch out for that case.
276
+
277
+ if (!value) {
278
+ return 'null';
279
+ }
280
+
281
+// Make an array to hold the partial results of stringifying this object value.
282
+
283
+ gap += indent;
284
+ partial = [];
285
+
286
+// Is the value an array?
287
+
288
+ if (Object.prototype.toString.apply(value) === '[object Array]') {
289
+
290
+// The value is an array. Stringify every element. Use null as a placeholder
291
+// for non-JSON values.
292
+
293
+ length = value.length;
294
+ for (i = 0; i < length; i += 1) {
295
+ partial[i] = str(i, value) || 'null';
296
+ }
297
+
298
+// Join all of the elements together, separated with commas, and wrap them in
299
+// brackets.
300
+
301
+ v = partial.length === 0 ? '[]' :
302
+ gap ? '[\n' + gap +
303
+ partial.join(',\n' + gap) + '\n' +
304
+ mind + ']' :
305
+ '[' + partial.join(',') + ']';
306
+ gap = mind;
307
+ return v;
308
+ }
309
+
310
+// If the replacer is an array, use it to select the members to be stringified.
311
+
312
+ if (rep && typeof rep === 'object') {
313
+ length = rep.length;
314
+ for (i = 0; i < length; i += 1) {
315
+ k = rep[i];
316
+ if (typeof k === 'string') {
317
+ v = str(k, value);
318
+ if (v) {
319
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
320
+ }
321
+ }
322
+ }
323
+ } else {
324
+
325
+// Otherwise, iterate through all of the keys in the object.
326
+
327
+ for (k in value) {
328
+ if (Object.hasOwnProperty.call(value, k)) {
329
+ v = str(k, value);
330
+ if (v) {
331
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+// Join all of the member texts together, separated with commas,
338
+// and wrap them in braces.
339
+
340
+ v = partial.length === 0 ? '{}' :
341
+ gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
342
+ mind + '}' : '{' + partial.join(',') + '}';
343
+ gap = mind;
344
+ return v;
345
+ }
346
+ }
347
+
348
+// If the JSON object does not yet have a stringify method, give it one.
349
+
350
+ if (typeof JSON.stringify !== 'function') {
351
+ JSON.stringify = function (value, replacer, space) {
352
+
353
+// The stringify method takes a value and an optional replacer, and an optional
354
+// space parameter, and returns a JSON text. The replacer can be a function
355
+// that can replace values, or an array of strings that will select the keys.
356
+// A default replacer method can be provided. Use of the space parameter can
357
+// produce text that is more easily readable.
358
+
359
+ var i;
360
+ gap = '';
361
+ indent = '';
362
+
363
+// If the space parameter is a number, make an indent string containing that
364
+// many spaces.
365
+
366
+ if (typeof space === 'number') {
367
+ for (i = 0; i < space; i += 1) {
368
+ indent += ' ';
369
+ }
370
+
371
+// If the space parameter is a string, it will be used as the indent string.
372
+
373
+ } else if (typeof space === 'string') {
374
+ indent = space;
375
+ }
376
+
377
+// If there is a replacer, it must be a function or an array.
378
+// Otherwise, throw an error.
379
+
380
+ rep = replacer;
381
+ if (replacer && typeof replacer !== 'function' &&
382
+ (typeof replacer !== 'object' ||
383
+ typeof replacer.length !== 'number')) {
384
+ throw new Error('JSON.stringify');
385
+ }
386
+
387
+// Make a fake root object containing our value under the key of ''.
388
+// Return the result of stringifying the value.
389
+
390
+ return str('', {'': value});
391
+ };
392
+ }
393
+
394
+
395
+// If the JSON object does not yet have a parse method, give it one.
396
+
397
+ if (typeof JSON.parse !== 'function') {
398
+ JSON.parse = function (text, reviver) {
399
+
400
+// The parse method takes a text and an optional reviver function, and returns
401
+// a JavaScript value if the text is a valid JSON text.
402
+
403
+ var j;
404
+
405
+ function walk(holder, key) {
406
+
407
+// The walk method is used to recursively walk the resulting structure so
408
+// that modifications can be made.
409
+
410
+ var k, v, value = holder[key];
411
+ if (value && typeof value === 'object') {
412
+ for (k in value) {
413
+ if (Object.hasOwnProperty.call(value, k)) {
414
+ v = walk(value, k);
415
+ if (v !== undefined) {
416
+ value[k] = v;
417
+ } else {
418
+ delete value[k];
419
+ }
420
+ }
421
+ }
422
+ }
423
+ return reviver.call(holder, key, value);
424
+ }
425
+
426
+
427
+// Parsing happens in four stages. In the first stage, we replace certain
428
+// Unicode characters with escape sequences. JavaScript handles many characters
429
+// incorrectly, either silently deleting them, or treating them as line endings.
430
+
431
+ cx.lastIndex = 0;
432
+ if (cx.test(text)) {
433
+ text = text.replace(cx, function (a) {
434
+ return '\\u' +
435
+ ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
436
+ });
437
+ }
438
+
439
+// In the second stage, we run the text against regular expressions that look
440
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
441
+// because they can cause invocation, and '=' because it can cause mutation.
442
+// But just to be safe, we want to reject all unexpected forms.
443
+
444
+// We split the second stage into 4 regexp operations in order to work around
445
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
446
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
447
+// replace all simple value tokens with ']' characters. Third, we delete all
448
+// open brackets that follow a colon or comma or that begin the text. Finally,
449
+// we look to see that the remaining characters are only whitespace or ']' or
450
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
451
+
452
+ if (/^[\],:{}\s]*$/.
453
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
454
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
455
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
456
+
457
+// In the third stage we use the eval function to compile the text into a
458
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
459
+// in JavaScript: it can begin a block or an object literal. We wrap the text
460
+// in parens to eliminate the ambiguity.
461
+
462
+ j = eval('(' + text + ')');
463
+
464
+// In the optional fourth stage, we recursively walk the new structure, passing
465
+// each name/value pair to a reviver function for possible transformation.
466
+
467
+ return typeof reviver === 'function' ?
468
+ walk({'': j}, '') : j;
469
+ }
470
+
471
+// If the text is not JSON parseable, then a SyntaxError is thrown.
472
+
473
+ throw new SyntaxError('JSON.parse');
474
+ };
475
+ }
476
+}());
--- a/ajax/js/json2.js
+++ b/ajax/js/json2.js
@@ -0,0 +1,476 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/ajax/js/json2.js
+++ b/ajax/js/json2.js
@@ -0,0 +1,476 @@
1 /*
2 http://www.JSON.org/json2.js
3 2009-06-29
4
5 Public Domain.
6
7 NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
8
9 See http://www.JSON.org/js.html
10
11 This file creates a global JSON object containing two methods: stringify
12 and parse.
13
14 JSON.stringify(value, replacer, space)
15 value any JavaScript value, usually an object or array.
16
17 replacer an optional parameter that determines how object
18 values are stringified for objects. It can be a
19 function or an array of strings.
20
21 space an optional parameter that specifies the indentation
22 of nested structures. If it is omitted, the text will
23 be packed without extra whitespace. If it is a number,
24 it will specify the number of spaces to indent at each
25 level. If it is a string (such as '\t' or '&nbsp;'),
26 it contains the characters used to indent at each level.
27
28 This method produces a JSON text from a JavaScript value.
29
30 When an object value is found, if the object contains a toJSON
31 method, its toJSON method will be called and the result will be
32 stringified. A toJSON method does not serialize: it returns the
33 value represented by the name/value pair that should be serialized,
34 or undefined if nothing should be serialized. The toJSON method
35 will be passed the key associated with the value, and this will be
36 bound to the object holding the key.
37
38 For example, this would serialize Dates as ISO strings.
39
40 Date.prototype.toJSON = function (key) {
41 function f(n) {
42 // Format integers to have at least two digits.
43 return n < 10 ? '0' + n : n;
44 }
45
46 return this.getUTCFullYear() + '-' +
47 f(this.getUTCMonth() + 1) + '-' +
48 f(this.getUTCDate()) + 'T' +
49 f(this.getUTCHours()) + ':' +
50 f(this.getUTCMinutes()) + ':' +
51 f(this.getUTCSeconds()) + 'Z';
52 };
53
54 You can provide an optional replacer method. It will be passed the
55 key and value of each member, with this bound to the containing
56 object. The value that is returned from your method will be
57 serialized. If your method returns undefined, then the member will
58 be excluded from the serialization.
59
60 If the replacer parameter is an array of strings, then it will be
61 used to select the members to be serialized. It filters the results
62 such that only members with keys listed in the replacer array are
63 stringified.
64
65 Values that do not have JSON representations, such as undefined or
66 functions, will not be serialized. Such values in objects will be
67 dropped; in arrays they will be replaced with null. You can use
68 a replacer function to replace those with JSON values.
69 JSON.stringify(undefined) returns undefined.
70
71 The optional space parameter produces a stringification of the
72 value that is filled with line breaks and indentation to make it
73 easier to read.
74
75 If the space parameter is a non-empty string, then that string will
76 be used for indentation. If the space parameter is a number, then
77 the indentation will be that many spaces.
78
79 Example:
80
81 text = JSON.stringify(['e', {pluribus: 'unum'}]);
82 // text is '["e",{"pluribus":"unum"}]'
83
84
85 text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
86 // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
87
88 text = JSON.stringify([new Date()], function (key, value) {
89 return this[key] instanceof Date ?
90 'Date(' + this[key] + ')' : value;
91 });
92 // text is '["Date(---current time---)"]'
93
94
95 JSON.parse(text, reviver)
96 This method parses a JSON text to produce an object or array.
97 It can throw a SyntaxError exception.
98
99 The optional reviver parameter is a function that can filter and
100 transform the results. It receives each of the keys and values,
101 and its return value is used instead of the original value.
102 If it returns what it received, then the structure is not modified.
103 If it returns undefined then the member is deleted.
104
105 Example:
106
107 // Parse the text. Values that look like ISO date strings will
108 // be converted to Date objects.
109
110 myData = JSON.parse(text, function (key, value) {
111 var a;
112 if (typeof value === 'string') {
113 a =
114 /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
115 if (a) {
116 return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
117 +a[5], +a[6]));
118 }
119 }
120 return value;
121 });
122
123 myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
124 var d;
125 if (typeof value === 'string' &&
126 value.slice(0, 5) === 'Date(' &&
127 value.slice(-1) === ')') {
128 d = new Date(value.slice(5, -1));
129 if (d) {
130 return d;
131 }
132 }
133 return value;
134 });
135
136
137 This is a reference implementation. You are free to copy, modify, or
138 redistribute.
139
140 This code should be minified before deployment.
141 See http://javascript.crockford.com/jsmin.html
142
143 USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
144 NOT CONTROL.
145 */
146
147 /*jslint evil: true */
148
149 /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
150 call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
151 getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
152 lastIndex, length, parse, prototype, push, replace, slice, stringify,
153 test, toJSON, toString, valueOf
154 */
155
156 // Create a JSON object only if one does not already exist. We create the
157 // methods in a closure to avoid creating global variables.
158
159 var JSON = JSON || {};
160
161 (function () {
162
163 function f(n) {
164 // Format integers to have at least two digits.
165 return n < 10 ? '0' + n : n;
166 }
167
168 if (typeof Date.prototype.toJSON !== 'function') {
169
170 Date.prototype.toJSON = function (key) {
171
172 return isFinite(this.valueOf()) ?
173 this.getUTCFullYear() + '-' +
174 f(this.getUTCMonth() + 1) + '-' +
175 f(this.getUTCDate()) + 'T' +
176 f(this.getUTCHours()) + ':' +
177 f(this.getUTCMinutes()) + ':' +
178 f(this.getUTCSeconds()) + 'Z' : null;
179 };
180
181 String.prototype.toJSON =
182 Number.prototype.toJSON =
183 Boolean.prototype.toJSON = function (key) {
184 return this.valueOf();
185 };
186 }
187
188 var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
189 escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
190 gap,
191 indent,
192 meta = { // table of character substitutions
193 '\b': '\\b',
194 '\t': '\\t',
195 '\n': '\\n',
196 '\f': '\\f',
197 '\r': '\\r',
198 '"' : '\\"',
199 '\\': '\\\\'
200 },
201 rep;
202
203
204 function quote(string) {
205
206 // If the string contains no control characters, no quote characters, and no
207 // backslash characters, then we can safely slap some quotes around it.
208 // Otherwise we must also replace the offending characters with safe escape
209 // sequences.
210
211 escapable.lastIndex = 0;
212 return escapable.test(string) ?
213 '"' + string.replace(escapable, function (a) {
214 var c = meta[a];
215 return typeof c === 'string' ? c :
216 '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
217 }) + '"' :
218 '"' + string + '"';
219 }
220
221
222 function str(key, holder) {
223
224 // Produce a string from holder[key].
225
226 var i, // The loop counter.
227 k, // The member key.
228 v, // The member value.
229 length,
230 mind = gap,
231 partial,
232 value = holder[key];
233
234 // If the value has a toJSON method, call it to obtain a replacement value.
235
236 if (value && typeof value === 'object' &&
237 typeof value.toJSON === 'function') {
238 value = value.toJSON(key);
239 }
240
241 // If we were called with a replacer function, then call the replacer to
242 // obtain a replacement value.
243
244 if (typeof rep === 'function') {
245 value = rep.call(holder, key, value);
246 }
247
248 // What happens next depends on the value's type.
249
250 switch (typeof value) {
251 case 'string':
252 return quote(value);
253
254 case 'number':
255
256 // JSON numbers must be finite. Encode non-finite numbers as null.
257
258 return isFinite(value) ? String(value) : 'null';
259
260 case 'boolean':
261 case 'null':
262
263 // If the value is a boolean or null, convert it to a string. Note:
264 // typeof null does not produce 'null'. The case is included here in
265 // the remote chance that this gets fixed someday.
266
267 return String(value);
268
269 // If the type is 'object', we might be dealing with an object or an array or
270 // null.
271
272 case 'object':
273
274 // Due to a specification blunder in ECMAScript, typeof null is 'object',
275 // so watch out for that case.
276
277 if (!value) {
278 return 'null';
279 }
280
281 // Make an array to hold the partial results of stringifying this object value.
282
283 gap += indent;
284 partial = [];
285
286 // Is the value an array?
287
288 if (Object.prototype.toString.apply(value) === '[object Array]') {
289
290 // The value is an array. Stringify every element. Use null as a placeholder
291 // for non-JSON values.
292
293 length = value.length;
294 for (i = 0; i < length; i += 1) {
295 partial[i] = str(i, value) || 'null';
296 }
297
298 // Join all of the elements together, separated with commas, and wrap them in
299 // brackets.
300
301 v = partial.length === 0 ? '[]' :
302 gap ? '[\n' + gap +
303 partial.join(',\n' + gap) + '\n' +
304 mind + ']' :
305 '[' + partial.join(',') + ']';
306 gap = mind;
307 return v;
308 }
309
310 // If the replacer is an array, use it to select the members to be stringified.
311
312 if (rep && typeof rep === 'object') {
313 length = rep.length;
314 for (i = 0; i < length; i += 1) {
315 k = rep[i];
316 if (typeof k === 'string') {
317 v = str(k, value);
318 if (v) {
319 partial.push(quote(k) + (gap ? ': ' : ':') + v);
320 }
321 }
322 }
323 } else {
324
325 // Otherwise, iterate through all of the keys in the object.
326
327 for (k in value) {
328 if (Object.hasOwnProperty.call(value, k)) {
329 v = str(k, value);
330 if (v) {
331 partial.push(quote(k) + (gap ? ': ' : ':') + v);
332 }
333 }
334 }
335 }
336
337 // Join all of the member texts together, separated with commas,
338 // and wrap them in braces.
339
340 v = partial.length === 0 ? '{}' :
341 gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
342 mind + '}' : '{' + partial.join(',') + '}';
343 gap = mind;
344 return v;
345 }
346 }
347
348 // If the JSON object does not yet have a stringify method, give it one.
349
350 if (typeof JSON.stringify !== 'function') {
351 JSON.stringify = function (value, replacer, space) {
352
353 // The stringify method takes a value and an optional replacer, and an optional
354 // space parameter, and returns a JSON text. The replacer can be a function
355 // that can replace values, or an array of strings that will select the keys.
356 // A default replacer method can be provided. Use of the space parameter can
357 // produce text that is more easily readable.
358
359 var i;
360 gap = '';
361 indent = '';
362
363 // If the space parameter is a number, make an indent string containing that
364 // many spaces.
365
366 if (typeof space === 'number') {
367 for (i = 0; i < space; i += 1) {
368 indent += ' ';
369 }
370
371 // If the space parameter is a string, it will be used as the indent string.
372
373 } else if (typeof space === 'string') {
374 indent = space;
375 }
376
377 // If there is a replacer, it must be a function or an array.
378 // Otherwise, throw an error.
379
380 rep = replacer;
381 if (replacer && typeof replacer !== 'function' &&
382 (typeof replacer !== 'object' ||
383 typeof replacer.length !== 'number')) {
384 throw new Error('JSON.stringify');
385 }
386
387 // Make a fake root object containing our value under the key of ''.
388 // Return the result of stringifying the value.
389
390 return str('', {'': value});
391 };
392 }
393
394
395 // If the JSON object does not yet have a parse method, give it one.
396
397 if (typeof JSON.parse !== 'function') {
398 JSON.parse = function (text, reviver) {
399
400 // The parse method takes a text and an optional reviver function, and returns
401 // a JavaScript value if the text is a valid JSON text.
402
403 var j;
404
405 function walk(holder, key) {
406
407 // The walk method is used to recursively walk the resulting structure so
408 // that modifications can be made.
409
410 var k, v, value = holder[key];
411 if (value && typeof value === 'object') {
412 for (k in value) {
413 if (Object.hasOwnProperty.call(value, k)) {
414 v = walk(value, k);
415 if (v !== undefined) {
416 value[k] = v;
417 } else {
418 delete value[k];
419 }
420 }
421 }
422 }
423 return reviver.call(holder, key, value);
424 }
425
426
427 // Parsing happens in four stages. In the first stage, we replace certain
428 // Unicode characters with escape sequences. JavaScript handles many characters
429 // incorrectly, either silently deleting them, or treating them as line endings.
430
431 cx.lastIndex = 0;
432 if (cx.test(text)) {
433 text = text.replace(cx, function (a) {
434 return '\\u' +
435 ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
436 });
437 }
438
439 // In the second stage, we run the text against regular expressions that look
440 // for non-JSON patterns. We are especially concerned with '()' and 'new'
441 // because they can cause invocation, and '=' because it can cause mutation.
442 // But just to be safe, we want to reject all unexpected forms.
443
444 // We split the second stage into 4 regexp operations in order to work around
445 // crippling inefficiencies in IE's and Safari's regexp engines. First we
446 // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
447 // replace all simple value tokens with ']' characters. Third, we delete all
448 // open brackets that follow a colon or comma or that begin the text. Finally,
449 // we look to see that the remaining characters are only whitespace or ']' or
450 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
451
452 if (/^[\],:{}\s]*$/.
453 test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
454 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
455 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
456
457 // In the third stage we use the eval function to compile the text into a
458 // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
459 // in JavaScript: it can begin a block or an object literal. We wrap the text
460 // in parens to eliminate the ambiguity.
461
462 j = eval('(' + text + ')');
463
464 // In the optional fourth stage, we recursively walk the new structure, passing
465 // each name/value pair to a reviver function for possible transformation.
466
467 return typeof reviver === 'function' ?
468 walk({'': j}, '') : j;
469 }
470
471 // If the text is not JSON parseable, then a SyntaxError is thrown.
472
473 throw new SyntaxError('JSON.parse');
474 };
475 }
476 }());
--- a/ajax/js/whajaj.js
+++ b/ajax/js/whajaj.js
@@ -0,0 +1,53 @@
1
+/**
2
+ This file provides a JS interface into the core functionality of
3
+ JSON-centric back-ends. It sends GET or JSON POST requests to
4
+ a back-end and expects JSON responses. The exact semantics of
5
+ the underlying back-end and overlying front-end are not its concern,
6
+ and it leaves the interpretation of the data up to the client/server
7
+ insofar as possible.
8
+
9
+ All functionality is part of a class named WhAjaj, and that class
10
+ acts as namespace for this framework.
11
+
12
+ Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
13
+
14
+ License: Public Domain
15
+
16
+ This framework is directly derived from code originally found in
17
+ http://code.google.com/
18
+ http://whiki.wanderinghorse.net, where it contained quite a bit
19
+ of application-specific logic. It was eventually (the 3rd time i
20
+ needed it) split off into its own library to simplify inclusion
21
+ into my many mini-projects.
22
+*/
23
+
24
+
25
+/**
26
+ The WhAjaj function is primarily a namespace, and not intended
27
+ to called or instantiated via the 'new' operator.
28
+*/
29
+function WhAjaj()
30
+{
31
+}
32
+
33
+/** Returns a millisecond Unix Epoch timestamp. */
34
+WhAjaj.msTimestamp = function()
35
+{
36
+ return (new Date()).getTime();
37
+};
38
+
39
+/** Returns a Unix Epoch timestamp (in seconds) in integer format.
40
+
41
+ Reminder to self: (1.1 %1.2) evaluate
42
+ in JS, and thus this implementation is less thundefinedation is less than optimal.
43
+*/
44
+WhAjaj.unixTimestamp = function()
45
+{
46
+ var ts = (new Date()).getTime();
47
+ return parseInt( ""+((ts / 1000) % ts) );
48
+};
49
+
50
+/**
51
+ Returns true if v is-a Array instance.
52
+*/
53
+WhAja
--- a/ajax/js/whajaj.js
+++ b/ajax/js/whajaj.js
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/ajax/js/whajaj.js
+++ b/ajax/js/whajaj.js
@@ -0,0 +1,53 @@
1 /**
2 This file provides a JS interface into the core functionality of
3 JSON-centric back-ends. It sends GET or JSON POST requests to
4 a back-end and expects JSON responses. The exact semantics of
5 the underlying back-end and overlying front-end are not its concern,
6 and it leaves the interpretation of the data up to the client/server
7 insofar as possible.
8
9 All functionality is part of a class named WhAjaj, and that class
10 acts as namespace for this framework.
11
12 Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)
13
14 License: Public Domain
15
16 This framework is directly derived from code originally found in
17 http://code.google.com/
18 http://whiki.wanderinghorse.net, where it contained quite a bit
19 of application-specific logic. It was eventually (the 3rd time i
20 needed it) split off into its own library to simplify inclusion
21 into my many mini-projects.
22 */
23
24
25 /**
26 The WhAjaj function is primarily a namespace, and not intended
27 to called or instantiated via the 'new' operator.
28 */
29 function WhAjaj()
30 {
31 }
32
33 /** Returns a millisecond Unix Epoch timestamp. */
34 WhAjaj.msTimestamp = function()
35 {
36 return (new Date()).getTime();
37 };
38
39 /** Returns a Unix Epoch timestamp (in seconds) in integer format.
40
41 Reminder to self: (1.1 %1.2) evaluate
42 in JS, and thus this implementation is less thundefinedation is less than optimal.
43 */
44 WhAjaj.unixTimestamp = function()
45 {
46 var ts = (new Date()).getTime();
47 return parseInt( ""+((ts / 1000) % ts) );
48 };
49
50 /**
51 Returns true if v is-a Array instance.
52 */
53 WhAja
--- a/ajax/wiki-editor.html
+++ b/ajax/wiki-editor.html
@@ -0,0 +1,3 @@
1
+<!DOCTYPE html PUBLIC "-//W3C
2
+ /DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+<html xmlns="http://www.w3.org/1999/xhtml" xml:l
--- a/ajax/wiki-editor.html
+++ b/ajax/wiki-editor.html
@@ -0,0 +1,3 @@
 
 
 
--- a/ajax/wiki-editor.html
+++ b/ajax/wiki-editor.html
@@ -0,0 +1,3 @@
1 <!DOCTYPE html PUBLIC "-//W3C
2 /DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" xml:l
+30
--- auto.def
+++ auto.def
@@ -4,15 +4,17 @@
44
55
options {
66
with-openssl:path|auto|none
77
=> {Look for openssl in the given path, or auto or none}
88
with-zlib:path => {Look for zlib in the given path}
9
+ with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
910
internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
1011
static=0 => {Link a static executable}
1112
lineedit=1 => {Disable line editing}
1213
fossil-debug=0 => {Build with fossil debugging enabled}
1314
ipv6=1 => {Disable IPv6 support}
15
+ json=0 => {Build with fossil JSON API enabled}
1416
}
1517
1618
# sqlite wants these types if possible
1719
cc-with {-includes {stdint.h inttypes.h}} {
1820
cc-check-types uint32_t uint16_t int16_t uint8_t
@@ -62,10 +64,14 @@
6264
}
6365
6466
if {[opt-bool fossil-debug]} {
6567
define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
6668
}
69
+
70
+if {[opt-bool json]} {
71
+ define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON
72
+}
6773
6874
if {[opt-bool static]} {
6975
# XXX: This will not work on all systems.
7076
define-append EXTRA_LDFLAGS -static
7177
}
@@ -88,10 +94,34 @@
8894
define-append EXTRA_LDFLAGS -L$zlibpath
8995
}
9096
if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} {
9197
user-error "zlib not found please install it or specify the location with --with-zlib"
9298
}
99
+
100
+set tclpath [opt-val with-tcl]
101
+if {$tclpath ne ""} {
102
+ if {$tclpath ne "1"} {
103
+ cc-with [list -cflags [list -I$tclpath/include -L$tclpath/lib]]
104
+ }
105
+ if {![cc-check-includes tcl.h]} {
106
+ user-error "Cannot find tcl.h"
107
+ }
108
+ foreach tlib {tcl8.6 tcl8.5 tcl notfound} {
109
+ if {$tlib=="notfound"} {
110
+ user-error "Cannot find a usable libtcl"
111
+ }
112
+ if {[cc-check-function-in-lib Tcl_CreateInterp $tlib]} {
113
+ define-append LIBS -l$tlib
114
+ break
115
+ }
116
+ }
117
+ define FOSSIL_ENABLE_TCL
118
+ if {$tclpath ne "1"} {
119
+ define-append EXTRA_CFLAGS -I$tclpath/include
120
+ define-append EXTRA_LDFLAGS -L$tclpath/lib
121
+ }
122
+}
93123
94124
# Helper for openssl checking
95125
proc check-for-openssl {msg {cflags {}}} {
96126
msg-checking "Checking for $msg..."
97127
set rc 0
98128
99129
ADDED src/Makefile
--- auto.def
+++ auto.def
@@ -4,15 +4,17 @@
4
5 options {
6 with-openssl:path|auto|none
7 => {Look for openssl in the given path, or auto or none}
8 with-zlib:path => {Look for zlib in the given path}
 
9 internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
10 static=0 => {Link a static executable}
11 lineedit=1 => {Disable line editing}
12 fossil-debug=0 => {Build with fossil debugging enabled}
13 ipv6=1 => {Disable IPv6 support}
 
14 }
15
16 # sqlite wants these types if possible
17 cc-with {-includes {stdint.h inttypes.h}} {
18 cc-check-types uint32_t uint16_t int16_t uint8_t
@@ -62,10 +64,14 @@
62 }
63
64 if {[opt-bool fossil-debug]} {
65 define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
66 }
 
 
 
 
67
68 if {[opt-bool static]} {
69 # XXX: This will not work on all systems.
70 define-append EXTRA_LDFLAGS -static
71 }
@@ -88,10 +94,34 @@
88 define-append EXTRA_LDFLAGS -L$zlibpath
89 }
90 if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} {
91 user-error "zlib not found please install it or specify the location with --with-zlib"
92 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
94 # Helper for openssl checking
95 proc check-for-openssl {msg {cflags {}}} {
96 msg-checking "Checking for $msg..."
97 set rc 0
98
99 DDED src/Makefile
--- auto.def
+++ auto.def
@@ -4,15 +4,17 @@
4
5 options {
6 with-openssl:path|auto|none
7 => {Look for openssl in the given path, or auto or none}
8 with-zlib:path => {Look for zlib in the given path}
9 with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
10 internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
11 static=0 => {Link a static executable}
12 lineedit=1 => {Disable line editing}
13 fossil-debug=0 => {Build with fossil debugging enabled}
14 ipv6=1 => {Disable IPv6 support}
15 json=0 => {Build with fossil JSON API enabled}
16 }
17
18 # sqlite wants these types if possible
19 cc-with {-includes {stdint.h inttypes.h}} {
20 cc-check-types uint32_t uint16_t int16_t uint8_t
@@ -62,10 +64,14 @@
64 }
65
66 if {[opt-bool fossil-debug]} {
67 define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
68 }
69
70 if {[opt-bool json]} {
71 define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON
72 }
73
74 if {[opt-bool static]} {
75 # XXX: This will not work on all systems.
76 define-append EXTRA_LDFLAGS -static
77 }
@@ -88,10 +94,34 @@
94 define-append EXTRA_LDFLAGS -L$zlibpath
95 }
96 if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} {
97 user-error "zlib not found please install it or specify the location with --with-zlib"
98 }
99
100 set tclpath [opt-val with-tcl]
101 if {$tclpath ne ""} {
102 if {$tclpath ne "1"} {
103 cc-with [list -cflags [list -I$tclpath/include -L$tclpath/lib]]
104 }
105 if {![cc-check-includes tcl.h]} {
106 user-error "Cannot find tcl.h"
107 }
108 foreach tlib {tcl8.6 tcl8.5 tcl notfound} {
109 if {$tlib=="notfound"} {
110 user-error "Cannot find a usable libtcl"
111 }
112 if {[cc-check-function-in-lib Tcl_CreateInterp $tlib]} {
113 define-append LIBS -l$tlib
114 break
115 }
116 }
117 define FOSSIL_ENABLE_TCL
118 if {$tclpath ne "1"} {
119 define-append EXTRA_CFLAGS -I$tclpath/include
120 define-append EXTRA_LDFLAGS -L$tclpath/lib
121 }
122 }
123
124 # Helper for openssl checking
125 proc check-for-openssl {msg {cflags {}}} {
126 msg-checking "Checking for $msg..."
127 set rc 0
128
129 DDED src/Makefile
+30
--- auto.def
+++ auto.def
@@ -4,15 +4,17 @@
44
55
options {
66
with-openssl:path|auto|none
77
=> {Look for openssl in the given path, or auto or none}
88
with-zlib:path => {Look for zlib in the given path}
9
+ with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
910
internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
1011
static=0 => {Link a static executable}
1112
lineedit=1 => {Disable line editing}
1213
fossil-debug=0 => {Build with fossil debugging enabled}
1314
ipv6=1 => {Disable IPv6 support}
15
+ json=0 => {Build with fossil JSON API enabled}
1416
}
1517
1618
# sqlite wants these types if possible
1719
cc-with {-includes {stdint.h inttypes.h}} {
1820
cc-check-types uint32_t uint16_t int16_t uint8_t
@@ -62,10 +64,14 @@
6264
}
6365
6466
if {[opt-bool fossil-debug]} {
6567
define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
6668
}
69
+
70
+if {[opt-bool json]} {
71
+ define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON
72
+}
6773
6874
if {[opt-bool static]} {
6975
# XXX: This will not work on all systems.
7076
define-append EXTRA_LDFLAGS -static
7177
}
@@ -88,10 +94,34 @@
8894
define-append EXTRA_LDFLAGS -L$zlibpath
8995
}
9096
if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} {
9197
user-error "zlib not found please install it or specify the location with --with-zlib"
9298
}
99
+
100
+set tclpath [opt-val with-tcl]
101
+if {$tclpath ne ""} {
102
+ if {$tclpath ne "1"} {
103
+ cc-with [list -cflags [list -I$tclpath/include -L$tclpath/lib]]
104
+ }
105
+ if {![cc-check-includes tcl.h]} {
106
+ user-error "Cannot find tcl.h"
107
+ }
108
+ foreach tlib {tcl8.6 tcl8.5 tcl notfound} {
109
+ if {$tlib=="notfound"} {
110
+ user-error "Cannot find a usable libtcl"
111
+ }
112
+ if {[cc-check-function-in-lib Tcl_CreateInterp $tlib]} {
113
+ define-append LIBS -l$tlib
114
+ break
115
+ }
116
+ }
117
+ define FOSSIL_ENABLE_TCL
118
+ if {$tclpath ne "1"} {
119
+ define-append EXTRA_CFLAGS -I$tclpath/include
120
+ define-append EXTRA_LDFLAGS -L$tclpath/lib
121
+ }
122
+}
93123
94124
# Helper for openssl checking
95125
proc check-for-openssl {msg {cflags {}}} {
96126
msg-checking "Checking for $msg..."
97127
set rc 0
98128
99129
ADDED src/Makefile
--- auto.def
+++ auto.def
@@ -4,15 +4,17 @@
4
5 options {
6 with-openssl:path|auto|none
7 => {Look for openssl in the given path, or auto or none}
8 with-zlib:path => {Look for zlib in the given path}
 
9 internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
10 static=0 => {Link a static executable}
11 lineedit=1 => {Disable line editing}
12 fossil-debug=0 => {Build with fossil debugging enabled}
13 ipv6=1 => {Disable IPv6 support}
 
14 }
15
16 # sqlite wants these types if possible
17 cc-with {-includes {stdint.h inttypes.h}} {
18 cc-check-types uint32_t uint16_t int16_t uint8_t
@@ -62,10 +64,14 @@
62 }
63
64 if {[opt-bool fossil-debug]} {
65 define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
66 }
 
 
 
 
67
68 if {[opt-bool static]} {
69 # XXX: This will not work on all systems.
70 define-append EXTRA_LDFLAGS -static
71 }
@@ -88,10 +94,34 @@
88 define-append EXTRA_LDFLAGS -L$zlibpath
89 }
90 if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} {
91 user-error "zlib not found please install it or specify the location with --with-zlib"
92 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
94 # Helper for openssl checking
95 proc check-for-openssl {msg {cflags {}}} {
96 msg-checking "Checking for $msg..."
97 set rc 0
98
99 DDED src/Makefile
--- auto.def
+++ auto.def
@@ -4,15 +4,17 @@
4
5 options {
6 with-openssl:path|auto|none
7 => {Look for openssl in the given path, or auto or none}
8 with-zlib:path => {Look for zlib in the given path}
9 with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
10 internal-sqlite=1 => {Don't use the internal sqlite, use the system one}
11 static=0 => {Link a static executable}
12 lineedit=1 => {Disable line editing}
13 fossil-debug=0 => {Build with fossil debugging enabled}
14 ipv6=1 => {Disable IPv6 support}
15 json=0 => {Build with fossil JSON API enabled}
16 }
17
18 # sqlite wants these types if possible
19 cc-with {-includes {stdint.h inttypes.h}} {
20 cc-check-types uint32_t uint16_t int16_t uint8_t
@@ -62,10 +64,14 @@
64 }
65
66 if {[opt-bool fossil-debug]} {
67 define-append EXTRA_CFLAGS -DFOSSIL_DEBUG
68 }
69
70 if {[opt-bool json]} {
71 define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON
72 }
73
74 if {[opt-bool static]} {
75 # XXX: This will not work on all systems.
76 define-append EXTRA_LDFLAGS -static
77 }
@@ -88,10 +94,34 @@
94 define-append EXTRA_LDFLAGS -L$zlibpath
95 }
96 if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} {
97 user-error "zlib not found please install it or specify the location with --with-zlib"
98 }
99
100 set tclpath [opt-val with-tcl]
101 if {$tclpath ne ""} {
102 if {$tclpath ne "1"} {
103 cc-with [list -cflags [list -I$tclpath/include -L$tclpath/lib]]
104 }
105 if {![cc-check-includes tcl.h]} {
106 user-error "Cannot find tcl.h"
107 }
108 foreach tlib {tcl8.6 tcl8.5 tcl notfound} {
109 if {$tlib=="notfound"} {
110 user-error "Cannot find a usable libtcl"
111 }
112 if {[cc-check-function-in-lib Tcl_CreateInterp $tlib]} {
113 define-append LIBS -l$tlib
114 break
115 }
116 }
117 define FOSSIL_ENABLE_TCL
118 if {$tclpath ne "1"} {
119 define-append EXTRA_CFLAGS -I$tclpath/include
120 define-append EXTRA_LDFLAGS -L$tclpath/lib
121 }
122 }
123
124 # Helper for openssl checking
125 proc check-for-openssl {msg {cflags {}}} {
126 msg-checking "Checking for $msg..."
127 set rc 0
128
129 DDED src/Makefile
--- a/src/Makefile
+++ b/src/Makefile
@@ -0,0 +1,2 @@
1
+all:
2
+ $(MAKE) -C ..
--- a/src/Makefile
+++ b/src/Makefile
@@ -0,0 +1,2 @@
 
 
--- a/src/Makefile
+++ b/src/Makefile
@@ -0,0 +1,2 @@
1 all:
2 $(MAKE) -C ..
+2 -2
--- src/add.c
+++ src/add.c
@@ -260,11 +260,11 @@
260260
db_end_transaction(0);
261261
}
262262
263263
/*
264264
** COMMAND: rm
265
-** COMMAND: delete
265
+** COMMAND: delete*
266266
**
267267
** Usage: %fossil rm FILE1 ?FILE2 ...?
268268
** or: %fossil delete FILE1 ?FILE2 ...?
269269
**
270270
** Remove one or more files or directories from the repository.
@@ -473,11 +473,11 @@
473473
);
474474
}
475475
476476
/*
477477
** COMMAND: mv
478
-** COMMAND: rename
478
+** COMMAND: rename*
479479
**
480480
** Usage: %fossil mv|rename OLDNAME NEWNAME
481481
** or: %fossil mv|rename OLDNAME... DIR
482482
**
483483
** Move or rename one or more files or directories within the repository tree.
484484
--- src/add.c
+++ src/add.c
@@ -260,11 +260,11 @@
260 db_end_transaction(0);
261 }
262
263 /*
264 ** COMMAND: rm
265 ** COMMAND: delete
266 **
267 ** Usage: %fossil rm FILE1 ?FILE2 ...?
268 ** or: %fossil delete FILE1 ?FILE2 ...?
269 **
270 ** Remove one or more files or directories from the repository.
@@ -473,11 +473,11 @@
473 );
474 }
475
476 /*
477 ** COMMAND: mv
478 ** COMMAND: rename
479 **
480 ** Usage: %fossil mv|rename OLDNAME NEWNAME
481 ** or: %fossil mv|rename OLDNAME... DIR
482 **
483 ** Move or rename one or more files or directories within the repository tree.
484
--- src/add.c
+++ src/add.c
@@ -260,11 +260,11 @@
260 db_end_transaction(0);
261 }
262
263 /*
264 ** COMMAND: rm
265 ** COMMAND: delete*
266 **
267 ** Usage: %fossil rm FILE1 ?FILE2 ...?
268 ** or: %fossil delete FILE1 ?FILE2 ...?
269 **
270 ** Remove one or more files or directories from the repository.
@@ -473,11 +473,11 @@
473 );
474 }
475
476 /*
477 ** COMMAND: mv
478 ** COMMAND: rename*
479 **
480 ** Usage: %fossil mv|rename OLDNAME NEWNAME
481 ** or: %fossil mv|rename OLDNAME... DIR
482 **
483 ** Move or rename one or more files or directories within the repository tree.
484
+41 -2
--- src/blob.c
+++ src/blob.c
@@ -766,12 +766,24 @@
766766
int blob_write_to_file(Blob *pBlob, const char *zFilename){
767767
FILE *out;
768768
int wrote;
769769
770770
if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){
771
- fossil_puts(blob_str(pBlob), 0);
772
- return blob_size(pBlob);
771
+ int n;
772
+#if defined(_WIN32)
773
+ if( _isatty(fileno(stdout)) ){
774
+ char *z;
775
+ z = fossil_utf8_to_console(blob_str(pBlob));
776
+ n = strlen(z);
777
+ fwrite(z, 1, n, stdout);
778
+ free(z);
779
+ return n;
780
+ }
781
+#endif
782
+ n = blob_size(pBlob);
783
+ fwrite(blob_buffer(pBlob), 1, n, stdout);
784
+ return n;
773785
}else{
774786
int i, nName;
775787
char *zName, zBuf[1000];
776788
777789
nName = strlen(zFilename);
@@ -1039,5 +1051,32 @@
10391051
return;
10401052
}
10411053
}
10421054
blob_append(pBlob, zIn, -1);
10431055
}
1056
+
1057
+/*
1058
+** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
1059
+** bytes from pIn, starting at position pIn->iCursor, and copies them
1060
+** to pDest (which must be valid memory at least nLen bytes long).
1061
+**
1062
+** Returns the number of bytes read/copied, which may be less than
1063
+** nLen (if end-of-blob is encountered).
1064
+**
1065
+** Updates pIn's cursor.
1066
+**
1067
+** Returns 0 if pIn contains no data.
1068
+*/
1069
+unsigned int blob_read(Blob *pIn, void * pDest, unsigned int nLen ){
1070
+ if( !pIn->aData || (pIn->iCursor >= pIn->nUsed) ){
1071
+ return 0;
1072
+ } else if( (pIn->iCursor + nLen) > (unsigned int)pIn->nUsed ){
1073
+ nLen = (unsigned int) (pIn->nUsed - pIn->iCursor);
1074
+ }
1075
+ assert( pIn->nUsed > pIn->iCursor );
1076
+ assert( (pIn->iCursor+nLen) <= pIn->nUsed );
1077
+ if( nLen ){
1078
+ memcpy( pDest, pIn->aData, nLen );
1079
+ pIn->iCursor += nLen;
1080
+ }
1081
+ return nLen;
1082
+}
10441083
--- src/blob.c
+++ src/blob.c
@@ -766,12 +766,24 @@
766 int blob_write_to_file(Blob *pBlob, const char *zFilename){
767 FILE *out;
768 int wrote;
769
770 if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){
771 fossil_puts(blob_str(pBlob), 0);
772 return blob_size(pBlob);
 
 
 
 
 
 
 
 
 
 
 
 
773 }else{
774 int i, nName;
775 char *zName, zBuf[1000];
776
777 nName = strlen(zFilename);
@@ -1039,5 +1051,32 @@
1039 return;
1040 }
1041 }
1042 blob_append(pBlob, zIn, -1);
1043 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1044
--- src/blob.c
+++ src/blob.c
@@ -766,12 +766,24 @@
766 int blob_write_to_file(Blob *pBlob, const char *zFilename){
767 FILE *out;
768 int wrote;
769
770 if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){
771 int n;
772 #if defined(_WIN32)
773 if( _isatty(fileno(stdout)) ){
774 char *z;
775 z = fossil_utf8_to_console(blob_str(pBlob));
776 n = strlen(z);
777 fwrite(z, 1, n, stdout);
778 free(z);
779 return n;
780 }
781 #endif
782 n = blob_size(pBlob);
783 fwrite(blob_buffer(pBlob), 1, n, stdout);
784 return n;
785 }else{
786 int i, nName;
787 char *zName, zBuf[1000];
788
789 nName = strlen(zFilename);
@@ -1039,5 +1051,32 @@
1051 return;
1052 }
1053 }
1054 blob_append(pBlob, zIn, -1);
1055 }
1056
1057 /*
1058 ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
1059 ** bytes from pIn, starting at position pIn->iCursor, and copies them
1060 ** to pDest (which must be valid memory at least nLen bytes long).
1061 **
1062 ** Returns the number of bytes read/copied, which may be less than
1063 ** nLen (if end-of-blob is encountered).
1064 **
1065 ** Updates pIn's cursor.
1066 **
1067 ** Returns 0 if pIn contains no data.
1068 */
1069 unsigned int blob_read(Blob *pIn, void * pDest, unsigned int nLen ){
1070 if( !pIn->aData || (pIn->iCursor >= pIn->nUsed) ){
1071 return 0;
1072 } else if( (pIn->iCursor + nLen) > (unsigned int)pIn->nUsed ){
1073 nLen = (unsigned int) (pIn->nUsed - pIn->iCursor);
1074 }
1075 assert( pIn->nUsed > pIn->iCursor );
1076 assert( (pIn->iCursor+nLen) <= pIn->nUsed );
1077 if( nLen ){
1078 memcpy( pDest, pIn->aData, nLen );
1079 pIn->iCursor += nLen;
1080 }
1081 return nLen;
1082 }
1083
+10 -6
--- src/branch.c
+++ src/branch.c
@@ -178,14 +178,18 @@
178178
/* Do an autosync push, if requested */
179179
if( !isPrivate ) autosync(AUTOSYNC_PUSH);
180180
}
181181
182182
/*
183
-** Prepare a query that will list all branches.
183
+** Prepare a query that will list branches.
184
+**
185
+** If (which<0) then the query pulls only closed branches. If
186
+** (which>0) then the query pulls all (closed and opened)
187
+** branches. Else the query pulls currently-opened branches.
184188
*/
185
-static void prepareBranchQuery(Stmt *pQuery, int showAll, int showClosed){
186
- if( showClosed ){
189
+void branch_prepare_list_query(Stmt *pQuery, int which ){
190
+ if( which < 0 ){
187191
db_prepare(pQuery,
188192
"SELECT value FROM tagxref"
189193
" WHERE tagid=%d AND value NOT NULL "
190194
"EXCEPT "
191195
"SELECT value FROM tagxref"
@@ -193,11 +197,11 @@
193197
" AND rid IN leaf"
194198
" AND NOT %z"
195199
" ORDER BY value COLLATE nocase /*sort*/",
196200
TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid")
197201
);
198
- }else if( showAll ){
202
+ }else if( which>0 ){
199203
db_prepare(pQuery,
200204
"SELECT DISTINCT value FROM tagxref"
201205
" WHERE tagid=%d AND value NOT NULL"
202206
" AND rid IN leaf"
203207
" ORDER BY value COLLATE nocase /*sort*/",
@@ -260,11 +264,11 @@
260264
if( g.localOpen ){
261265
vid = db_lget_int("checkout", 0);
262266
zCurrent = db_text(0, "SELECT value FROM tagxref"
263267
" WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
264268
}
265
- prepareBranchQuery(&q, showAll, showClosed);
269
+ branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0));
266270
while( db_step(&q)==SQLITE_ROW ){
267271
const char *zBr = db_column_text(&q, 0);
268272
int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
269273
fossil_print("%s%s\n", (isCur ? "* " : " "), zBr);
270274
}
@@ -327,11 +331,11 @@
327331
@ Closed branches are fixed and do not change (unless they are first
328332
@ reopened)</li>
329333
@ </ol>
330334
style_sidebox_end();
331335
332
- prepareBranchQuery(&q, showAll, showClosed);
336
+ branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0));
333337
cnt = 0;
334338
while( db_step(&q)==SQLITE_ROW ){
335339
const char *zBr = db_column_text(&q, 0);
336340
if( cnt==0 ){
337341
if( colorTest ){
338342
--- src/branch.c
+++ src/branch.c
@@ -178,14 +178,18 @@
178 /* Do an autosync push, if requested */
179 if( !isPrivate ) autosync(AUTOSYNC_PUSH);
180 }
181
182 /*
183 ** Prepare a query that will list all branches.
 
 
 
 
184 */
185 static void prepareBranchQuery(Stmt *pQuery, int showAll, int showClosed){
186 if( showClosed ){
187 db_prepare(pQuery,
188 "SELECT value FROM tagxref"
189 " WHERE tagid=%d AND value NOT NULL "
190 "EXCEPT "
191 "SELECT value FROM tagxref"
@@ -193,11 +197,11 @@
193 " AND rid IN leaf"
194 " AND NOT %z"
195 " ORDER BY value COLLATE nocase /*sort*/",
196 TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid")
197 );
198 }else if( showAll ){
199 db_prepare(pQuery,
200 "SELECT DISTINCT value FROM tagxref"
201 " WHERE tagid=%d AND value NOT NULL"
202 " AND rid IN leaf"
203 " ORDER BY value COLLATE nocase /*sort*/",
@@ -260,11 +264,11 @@
260 if( g.localOpen ){
261 vid = db_lget_int("checkout", 0);
262 zCurrent = db_text(0, "SELECT value FROM tagxref"
263 " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
264 }
265 prepareBranchQuery(&q, showAll, showClosed);
266 while( db_step(&q)==SQLITE_ROW ){
267 const char *zBr = db_column_text(&q, 0);
268 int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
269 fossil_print("%s%s\n", (isCur ? "* " : " "), zBr);
270 }
@@ -327,11 +331,11 @@
327 @ Closed branches are fixed and do not change (unless they are first
328 @ reopened)</li>
329 @ </ol>
330 style_sidebox_end();
331
332 prepareBranchQuery(&q, showAll, showClosed);
333 cnt = 0;
334 while( db_step(&q)==SQLITE_ROW ){
335 const char *zBr = db_column_text(&q, 0);
336 if( cnt==0 ){
337 if( colorTest ){
338
--- src/branch.c
+++ src/branch.c
@@ -178,14 +178,18 @@
178 /* Do an autosync push, if requested */
179 if( !isPrivate ) autosync(AUTOSYNC_PUSH);
180 }
181
182 /*
183 ** Prepare a query that will list branches.
184 **
185 ** If (which<0) then the query pulls only closed branches. If
186 ** (which>0) then the query pulls all (closed and opened)
187 ** branches. Else the query pulls currently-opened branches.
188 */
189 void branch_prepare_list_query(Stmt *pQuery, int which ){
190 if( which < 0 ){
191 db_prepare(pQuery,
192 "SELECT value FROM tagxref"
193 " WHERE tagid=%d AND value NOT NULL "
194 "EXCEPT "
195 "SELECT value FROM tagxref"
@@ -193,11 +197,11 @@
197 " AND rid IN leaf"
198 " AND NOT %z"
199 " ORDER BY value COLLATE nocase /*sort*/",
200 TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid")
201 );
202 }else if( which>0 ){
203 db_prepare(pQuery,
204 "SELECT DISTINCT value FROM tagxref"
205 " WHERE tagid=%d AND value NOT NULL"
206 " AND rid IN leaf"
207 " ORDER BY value COLLATE nocase /*sort*/",
@@ -260,11 +264,11 @@
264 if( g.localOpen ){
265 vid = db_lget_int("checkout", 0);
266 zCurrent = db_text(0, "SELECT value FROM tagxref"
267 " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH);
268 }
269 branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0));
270 while( db_step(&q)==SQLITE_ROW ){
271 const char *zBr = db_column_text(&q, 0);
272 int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
273 fossil_print("%s%s\n", (isCur ? "* " : " "), zBr);
274 }
@@ -327,11 +331,11 @@
331 @ Closed branches are fixed and do not change (unless they are first
332 @ reopened)</li>
333 @ </ol>
334 style_sidebox_end();
335
336 branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0));
337 cnt = 0;
338 while( db_step(&q)==SQLITE_ROW ){
339 const char *zBr = db_column_text(&q, 0);
340 if( cnt==0 ){
341 if( colorTest ){
342
--- src/browse.c
+++ src/browse.c
@@ -249,10 +249,11 @@
249249
/* Generate a multi-column table listing the contents of zD[]
250250
** directory.
251251
*/
252252
mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
253253
cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
254
+ if( mxLen<12 ) mxLen = 12;
254255
nCol = 100/mxLen;
255256
if( nCol<1 ) nCol = 1;
256257
if( nCol>5 ) nCol = 5;
257258
nRow = (cnt+nCol-1)/nCol;
258259
db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
259260
--- src/browse.c
+++ src/browse.c
@@ -249,10 +249,11 @@
249 /* Generate a multi-column table listing the contents of zD[]
250 ** directory.
251 */
252 mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
253 cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
 
254 nCol = 100/mxLen;
255 if( nCol<1 ) nCol = 1;
256 if( nCol>5 ) nCol = 5;
257 nRow = (cnt+nCol-1)/nCol;
258 db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
259
--- src/browse.c
+++ src/browse.c
@@ -249,10 +249,11 @@
249 /* Generate a multi-column table listing the contents of zD[]
250 ** directory.
251 */
252 mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
253 cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/");
254 if( mxLen<12 ) mxLen = 12;
255 nCol = 100/mxLen;
256 if( nCol<1 ) nCol = 1;
257 if( nCol>5 ) nCol = 5;
258 nRow = (cnt+nCol-1)/nCol;
259 db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/");
260
+3 -1
--- src/captcha.c
+++ src/captcha.c
@@ -412,12 +412,14 @@
412412
return x;
413413
}
414414
415415
/*
416416
** Translate a captcha seed value into the captcha password string.
417
+** The returned string is static and overwritten on each call to
418
+** this function.
417419
*/
418
-char *captcha_decode(unsigned int seed){
420
+char const *captcha_decode(unsigned int seed){
419421
const char *zSecret;
420422
const char *z;
421423
Blob b;
422424
static char zRes[20];
423425
424426
--- src/captcha.c
+++ src/captcha.c
@@ -412,12 +412,14 @@
412 return x;
413 }
414
415 /*
416 ** Translate a captcha seed value into the captcha password string.
 
 
417 */
418 char *captcha_decode(unsigned int seed){
419 const char *zSecret;
420 const char *z;
421 Blob b;
422 static char zRes[20];
423
424
--- src/captcha.c
+++ src/captcha.c
@@ -412,12 +412,14 @@
412 return x;
413 }
414
415 /*
416 ** Translate a captcha seed value into the captcha password string.
417 ** The returned string is static and overwritten on each call to
418 ** this function.
419 */
420 char const *captcha_decode(unsigned int seed){
421 const char *zSecret;
422 const char *z;
423 Blob b;
424 static char zRes[20];
425
426
+145 -23
--- src/cgi.c
+++ src/cgi.c
@@ -43,13 +43,10 @@
4343
#endif
4444
#include <time.h>
4545
#include <stdio.h>
4646
#include <stdlib.h>
4747
#include <unistd.h>
48
-#if defined (__POCC__)
49
-# undef INTERFACE
50
-#endif
5148
#include "cgi.h"
5249
5350
#if INTERFACE
5451
/*
5552
** Shortcuts for cgi_parameter. P("x") returns the value of query parameter
@@ -136,11 +133,11 @@
136133
}
137134
138135
/*
139136
** Return a pointer to the HTTP reply text.
140137
*/
141
-char *cgi_extract_content(int *pnAmt){
138
+char *cgi_extract_content(void){
142139
cgi_combine_header_and_body();
143140
return blob_buffer(&cgiContent[0]);
144141
}
145142
146143
/*
@@ -509,10 +506,13 @@
509506
zValue = "";
510507
}
511508
if( fossil_islower(zName[0]) ){
512509
cgi_set_parameter_nocopy(zName, zValue);
513510
}
511
+#ifdef FOSSIL_ENABLE_JSON
512
+ json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
513
+#endif /* FOSSIL_ENABLE_JSON */
514514
}
515515
}
516516
517517
/*
518518
** *pz is a string that consists of multiple lines of text. This
@@ -681,10 +681,88 @@
681681
}
682682
}
683683
}
684684
}
685685
}
686
+
687
+
688
+#ifdef FOSSIL_ENABLE_JSON
689
+/*
690
+** Internal helper for cson_data_source_FILE_n().
691
+*/
692
+typedef struct CgiPostReadState_ {
693
+ FILE * fh;
694
+ unsigned int len;
695
+ unsigned int pos;
696
+} CgiPostReadState;
697
+
698
+/*
699
+** cson_data_source_f() impl which reads only up to
700
+** a specified amount of data from its input FILE.
701
+** state MUST be a full populated (CgiPostReadState*).
702
+*/
703
+static int cson_data_source_FILE_n( void * state,
704
+ void * dest,
705
+ unsigned int * n ){
706
+ if( ! state || !dest || !n ) return cson_rc.ArgError;
707
+ else {
708
+ CgiPostReadState * st = (CgiPostReadState *)state;
709
+ if( st->pos >= st->len ){
710
+ *n = 0;
711
+ return 0;
712
+ } else if( !*n || ((st->pos + *n) > st->len) ){
713
+ return cson_rc.RangeError;
714
+ }else{
715
+ unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
716
+ if( ! rsz ){
717
+ *n = rsz;
718
+ return feof(st->fh) ? 0 : cson_rc.IOError;
719
+ }else{
720
+ *n = rsz;
721
+ st->pos += *n;
722
+ return 0;
723
+ }
724
+ }
725
+ }
726
+}
727
+
728
+/*
729
+** Reads a JSON object from the first contentLen bytes of zIn. On
730
+** g.json.post is updated to hold the content. On error a
731
+** FSL_JSON_E_INVALID_REQUEST response is output and fossil_exit() is
732
+** called (in HTTP mode exit code 0 is used).
733
+**
734
+** If contentLen is 0 then the whole file is read.
735
+*/
736
+void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
737
+ cson_value * jv = NULL;
738
+ int rc;
739
+ CgiPostReadState state;
740
+ state.fh = zIn;
741
+ state.len = contentLen;
742
+ state.pos = 0;
743
+ rc = cson_parse( &jv,
744
+ contentLen ? cson_data_source_FILE_n : cson_data_source_FILE,
745
+ contentLen ? (void *)&state : (void *)zIn, NULL, NULL );
746
+ if(rc){
747
+ goto invalidRequest;
748
+ }else{
749
+ json_gc_add( "POST.JSON", jv );
750
+ g.json.post.v = jv;
751
+ g.json.post.o = cson_value_get_object( jv );
752
+ if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */
753
+ goto invalidRequest;
754
+ }
755
+ }
756
+ return;
757
+ invalidRequest:
758
+ cgi_set_content_type(json_guess_content_type());
759
+ json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 );
760
+ fossil_exit( g.isHTTP ? 0 : 1);
761
+}
762
+#endif /* FOSSIL_ENABLE_JSON */
763
+
686764
687765
/*
688766
** Initialize the query parameter database. Information is pulled from
689767
** the QUERY_STRING environment variable (if it exists), from standard
690768
** input if there is POST data, and from HTTP_COOKIE.
@@ -691,19 +769,32 @@
691769
*/
692770
void cgi_init(void){
693771
char *z;
694772
const char *zType;
695773
int len;
774
+#ifdef FOSSIL_ENABLE_JSON
775
+ json_main_bootstrap();
776
+#endif
777
+ g.isHTTP = 1;
696778
cgi_destination(CGI_BODY);
779
+
780
+ z = (char*)P("HTTP_COOKIE");
781
+ if( z ){
782
+ z = mprintf("%s",z);
783
+ add_param_list(z, ';');
784
+ }
785
+
697786
z = (char*)P("QUERY_STRING");
698787
if( z ){
699788
z = mprintf("%s",z);
700789
add_param_list(z, '&');
701790
}
702791
703792
z = (char*)P("REMOTE_ADDR");
704
- if( z ) g.zIpAddr = mprintf("%s", z);
793
+ if( z ){
794
+ g.zIpAddr = mprintf("%s", z);
795
+ }
705796
706797
len = atoi(PD("CONTENT_LENGTH", "0"));
707798
g.zContentType = zType = P("CONTENT_TYPE");
708799
if( len>0 && zType ){
709800
blob_zero(&g.cgiIn);
@@ -723,17 +814,36 @@
723814
}else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
724815
blob_read_from_channel(&g.cgiIn, g.httpIn, len);
725816
}else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
726817
blob_read_from_channel(&g.cgiIn, g.httpIn, len);
727818
}
819
+#ifdef FOSSIL_ENABLE_JSON
820
+ else if( fossil_strcmp(zType, "application/json")
821
+ || fossil_strcmp(zType,"text/plain")/*assume this MIGHT be JSON*/
822
+ || fossil_strcmp(zType,"application/javascript")){
823
+ g.json.isJsonMode = 1;
824
+ cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
825
+ /* FIXMEs:
826
+
827
+ - See if fossil really needs g.cgiIn to be set for this purpose
828
+ (i don't think it does). If it does then fill g.cgiIn and
829
+ refactor to parse the JSON from there.
830
+
831
+ - After parsing POST JSON, copy the "first layer" of keys/values
832
+ to cgi_setenv(), honoring the upper-case distinction used
833
+ in add_param_list(). However...
834
+
835
+ - If we do that then we might get a disconnect in precedence of
836
+ GET/POST arguments. i prefer for GET entries to take precedence
837
+ over like-named POST entries, but in order for that to happen we
838
+ need to process QUERY_STRING _after_ reading the POST data.
839
+ */
840
+ cgi_set_content_type(json_guess_content_type());
841
+ }
842
+#endif /* FOSSIL_ENABLE_JSON */
728843
}
729844
730
- z = (char*)P("HTTP_COOKIE");
731
- if( z ){
732
- z = mprintf("%s",z);
733
- add_param_list(z, ';');
734
- }
735845
}
736846
737847
/*
738848
** This is the comparison function used to sort the aParamQP[] array of
739849
** query parameters and cookies.
@@ -943,20 +1053,33 @@
9431053
** Panic and die while processing a webpage.
9441054
*/
9451055
NORETURN void cgi_panic(const char *zFormat, ...){
9461056
va_list ap;
9471057
cgi_reset_content();
948
- cgi_set_status(500, "Internal Server Error");
949
- cgi_printf(
950
- "<html><body><h1>Internal Server Error</h1>\n"
951
- "<plaintext>"
952
- );
953
- va_start(ap, zFormat);
954
- vxprintf(pContent,zFormat,ap);
955
- va_end(ap);
956
- cgi_reply();
957
- fossil_exit(1);
1058
+#ifdef FOSSIL_ENABLE_JSON
1059
+ if( g.json.isJsonMode ){
1060
+ char * zMsg;
1061
+ va_start(ap, zFormat);
1062
+ zMsg = vmprintf(zFormat,ap);
1063
+ va_end(ap);
1064
+ json_err( FSL_JSON_E_PANIC, zMsg, 1 );
1065
+ free(zMsg);
1066
+ fossil_exit( g.isHTTP ? 0 : 1 );
1067
+ }else
1068
+#endif /* FOSSIL_ENABLE_JSON */
1069
+ {
1070
+ cgi_set_status(500, "Internal Server Error");
1071
+ cgi_printf(
1072
+ "<html><body><h1>Internal Server Error</h1>\n"
1073
+ "<plaintext>"
1074
+ );
1075
+ va_start(ap, zFormat);
1076
+ vxprintf(pContent,zFormat,ap);
1077
+ va_end(ap);
1078
+ cgi_reply();
1079
+ fossil_exit(1);
1080
+ }
9581081
}
9591082
9601083
/*
9611084
** Remove the first space-delimited token from a string and return
9621085
** a pointer to it. Add a NULL to the string to terminate the token.
@@ -993,11 +1116,10 @@
9931116
char *z, *zToken;
9941117
int i;
9951118
struct sockaddr_storage remoteName;
9961119
socklen_t size = sizeof(remoteName);
9971120
char zLine[2000]; /* A single line of input. */
998
-
9991121
g.fullHttpReply = 1;
10001122
if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
10011123
malformed_request();
10021124
}
10031125
zToken = extract_token(zLine, &z);
@@ -1058,16 +1180,16 @@
10581180
while(*p && *p != ',') p++;
10591181
*p = '\0';
10601182
zIpAddr = mprintf( "%s", zVal );
10611183
}
10621184
#if 0
1063
- else if( fossil_strcmp(zFieldName,"referer:")==0 ){
1185
+ }else if( fossil_strcmp(zFieldName,"referer:")==0 ){
10641186
cgi_setenv("HTTP_REFERER", zVal);
1187
+#endif
10651188
}else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
10661189
cgi_setenv("HTTP_USER_AGENT", zVal);
10671190
}
1068
-#endif
10691191
}
10701192
10711193
if( zIpAddr==0 &&
10721194
getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName,
10731195
&size)>=0
10741196
--- src/cgi.c
+++ src/cgi.c
@@ -43,13 +43,10 @@
43 #endif
44 #include <time.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #if defined (__POCC__)
49 # undef INTERFACE
50 #endif
51 #include "cgi.h"
52
53 #if INTERFACE
54 /*
55 ** Shortcuts for cgi_parameter. P("x") returns the value of query parameter
@@ -136,11 +133,11 @@
136 }
137
138 /*
139 ** Return a pointer to the HTTP reply text.
140 */
141 char *cgi_extract_content(int *pnAmt){
142 cgi_combine_header_and_body();
143 return blob_buffer(&cgiContent[0]);
144 }
145
146 /*
@@ -509,10 +506,13 @@
509 zValue = "";
510 }
511 if( fossil_islower(zName[0]) ){
512 cgi_set_parameter_nocopy(zName, zValue);
513 }
 
 
 
514 }
515 }
516
517 /*
518 ** *pz is a string that consists of multiple lines of text. This
@@ -681,10 +681,88 @@
681 }
682 }
683 }
684 }
685 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
687 /*
688 ** Initialize the query parameter database. Information is pulled from
689 ** the QUERY_STRING environment variable (if it exists), from standard
690 ** input if there is POST data, and from HTTP_COOKIE.
@@ -691,19 +769,32 @@
691 */
692 void cgi_init(void){
693 char *z;
694 const char *zType;
695 int len;
 
 
 
 
696 cgi_destination(CGI_BODY);
 
 
 
 
 
 
 
697 z = (char*)P("QUERY_STRING");
698 if( z ){
699 z = mprintf("%s",z);
700 add_param_list(z, '&');
701 }
702
703 z = (char*)P("REMOTE_ADDR");
704 if( z ) g.zIpAddr = mprintf("%s", z);
 
 
705
706 len = atoi(PD("CONTENT_LENGTH", "0"));
707 g.zContentType = zType = P("CONTENT_TYPE");
708 if( len>0 && zType ){
709 blob_zero(&g.cgiIn);
@@ -723,17 +814,36 @@
723 }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
724 blob_read_from_channel(&g.cgiIn, g.httpIn, len);
725 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
726 blob_read_from_channel(&g.cgiIn, g.httpIn, len);
727 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
728 }
729
730 z = (char*)P("HTTP_COOKIE");
731 if( z ){
732 z = mprintf("%s",z);
733 add_param_list(z, ';');
734 }
735 }
736
737 /*
738 ** This is the comparison function used to sort the aParamQP[] array of
739 ** query parameters and cookies.
@@ -943,20 +1053,33 @@
943 ** Panic and die while processing a webpage.
944 */
945 NORETURN void cgi_panic(const char *zFormat, ...){
946 va_list ap;
947 cgi_reset_content();
948 cgi_set_status(500, "Internal Server Error");
949 cgi_printf(
950 "<html><body><h1>Internal Server Error</h1>\n"
951 "<plaintext>"
952 );
953 va_start(ap, zFormat);
954 vxprintf(pContent,zFormat,ap);
955 va_end(ap);
956 cgi_reply();
957 fossil_exit(1);
 
 
 
 
 
 
 
 
 
 
 
 
 
958 }
959
960 /*
961 ** Remove the first space-delimited token from a string and return
962 ** a pointer to it. Add a NULL to the string to terminate the token.
@@ -993,11 +1116,10 @@
993 char *z, *zToken;
994 int i;
995 struct sockaddr_storage remoteName;
996 socklen_t size = sizeof(remoteName);
997 char zLine[2000]; /* A single line of input. */
998
999 g.fullHttpReply = 1;
1000 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1001 malformed_request();
1002 }
1003 zToken = extract_token(zLine, &z);
@@ -1058,16 +1180,16 @@
1058 while(*p && *p != ',') p++;
1059 *p = '\0';
1060 zIpAddr = mprintf( "%s", zVal );
1061 }
1062 #if 0
1063 else if( fossil_strcmp(zFieldName,"referer:")==0 ){
1064 cgi_setenv("HTTP_REFERER", zVal);
 
1065 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1066 cgi_setenv("HTTP_USER_AGENT", zVal);
1067 }
1068 #endif
1069 }
1070
1071 if( zIpAddr==0 &&
1072 getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName,
1073 &size)>=0
1074
--- src/cgi.c
+++ src/cgi.c
@@ -43,13 +43,10 @@
43 #endif
44 #include <time.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
 
 
 
48 #include "cgi.h"
49
50 #if INTERFACE
51 /*
52 ** Shortcuts for cgi_parameter. P("x") returns the value of query parameter
@@ -136,11 +133,11 @@
133 }
134
135 /*
136 ** Return a pointer to the HTTP reply text.
137 */
138 char *cgi_extract_content(void){
139 cgi_combine_header_and_body();
140 return blob_buffer(&cgiContent[0]);
141 }
142
143 /*
@@ -509,10 +506,13 @@
506 zValue = "";
507 }
508 if( fossil_islower(zName[0]) ){
509 cgi_set_parameter_nocopy(zName, zValue);
510 }
511 #ifdef FOSSIL_ENABLE_JSON
512 json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
513 #endif /* FOSSIL_ENABLE_JSON */
514 }
515 }
516
517 /*
518 ** *pz is a string that consists of multiple lines of text. This
@@ -681,10 +681,88 @@
681 }
682 }
683 }
684 }
685 }
686
687
688 #ifdef FOSSIL_ENABLE_JSON
689 /*
690 ** Internal helper for cson_data_source_FILE_n().
691 */
692 typedef struct CgiPostReadState_ {
693 FILE * fh;
694 unsigned int len;
695 unsigned int pos;
696 } CgiPostReadState;
697
698 /*
699 ** cson_data_source_f() impl which reads only up to
700 ** a specified amount of data from its input FILE.
701 ** state MUST be a full populated (CgiPostReadState*).
702 */
703 static int cson_data_source_FILE_n( void * state,
704 void * dest,
705 unsigned int * n ){
706 if( ! state || !dest || !n ) return cson_rc.ArgError;
707 else {
708 CgiPostReadState * st = (CgiPostReadState *)state;
709 if( st->pos >= st->len ){
710 *n = 0;
711 return 0;
712 } else if( !*n || ((st->pos + *n) > st->len) ){
713 return cson_rc.RangeError;
714 }else{
715 unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
716 if( ! rsz ){
717 *n = rsz;
718 return feof(st->fh) ? 0 : cson_rc.IOError;
719 }else{
720 *n = rsz;
721 st->pos += *n;
722 return 0;
723 }
724 }
725 }
726 }
727
728 /*
729 ** Reads a JSON object from the first contentLen bytes of zIn. On
730 ** g.json.post is updated to hold the content. On error a
731 ** FSL_JSON_E_INVALID_REQUEST response is output and fossil_exit() is
732 ** called (in HTTP mode exit code 0 is used).
733 **
734 ** If contentLen is 0 then the whole file is read.
735 */
736 void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
737 cson_value * jv = NULL;
738 int rc;
739 CgiPostReadState state;
740 state.fh = zIn;
741 state.len = contentLen;
742 state.pos = 0;
743 rc = cson_parse( &jv,
744 contentLen ? cson_data_source_FILE_n : cson_data_source_FILE,
745 contentLen ? (void *)&state : (void *)zIn, NULL, NULL );
746 if(rc){
747 goto invalidRequest;
748 }else{
749 json_gc_add( "POST.JSON", jv );
750 g.json.post.v = jv;
751 g.json.post.o = cson_value_get_object( jv );
752 if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */
753 goto invalidRequest;
754 }
755 }
756 return;
757 invalidRequest:
758 cgi_set_content_type(json_guess_content_type());
759 json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 );
760 fossil_exit( g.isHTTP ? 0 : 1);
761 }
762 #endif /* FOSSIL_ENABLE_JSON */
763
764
765 /*
766 ** Initialize the query parameter database. Information is pulled from
767 ** the QUERY_STRING environment variable (if it exists), from standard
768 ** input if there is POST data, and from HTTP_COOKIE.
@@ -691,19 +769,32 @@
769 */
770 void cgi_init(void){
771 char *z;
772 const char *zType;
773 int len;
774 #ifdef FOSSIL_ENABLE_JSON
775 json_main_bootstrap();
776 #endif
777 g.isHTTP = 1;
778 cgi_destination(CGI_BODY);
779
780 z = (char*)P("HTTP_COOKIE");
781 if( z ){
782 z = mprintf("%s",z);
783 add_param_list(z, ';');
784 }
785
786 z = (char*)P("QUERY_STRING");
787 if( z ){
788 z = mprintf("%s",z);
789 add_param_list(z, '&');
790 }
791
792 z = (char*)P("REMOTE_ADDR");
793 if( z ){
794 g.zIpAddr = mprintf("%s", z);
795 }
796
797 len = atoi(PD("CONTENT_LENGTH", "0"));
798 g.zContentType = zType = P("CONTENT_TYPE");
799 if( len>0 && zType ){
800 blob_zero(&g.cgiIn);
@@ -723,17 +814,36 @@
814 }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
815 blob_read_from_channel(&g.cgiIn, g.httpIn, len);
816 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
817 blob_read_from_channel(&g.cgiIn, g.httpIn, len);
818 }
819 #ifdef FOSSIL_ENABLE_JSON
820 else if( fossil_strcmp(zType, "application/json")
821 || fossil_strcmp(zType,"text/plain")/*assume this MIGHT be JSON*/
822 || fossil_strcmp(zType,"application/javascript")){
823 g.json.isJsonMode = 1;
824 cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
825 /* FIXMEs:
826
827 - See if fossil really needs g.cgiIn to be set for this purpose
828 (i don't think it does). If it does then fill g.cgiIn and
829 refactor to parse the JSON from there.
830
831 - After parsing POST JSON, copy the "first layer" of keys/values
832 to cgi_setenv(), honoring the upper-case distinction used
833 in add_param_list(). However...
834
835 - If we do that then we might get a disconnect in precedence of
836 GET/POST arguments. i prefer for GET entries to take precedence
837 over like-named POST entries, but in order for that to happen we
838 need to process QUERY_STRING _after_ reading the POST data.
839 */
840 cgi_set_content_type(json_guess_content_type());
841 }
842 #endif /* FOSSIL_ENABLE_JSON */
843 }
844
 
 
 
 
 
845 }
846
847 /*
848 ** This is the comparison function used to sort the aParamQP[] array of
849 ** query parameters and cookies.
@@ -943,20 +1053,33 @@
1053 ** Panic and die while processing a webpage.
1054 */
1055 NORETURN void cgi_panic(const char *zFormat, ...){
1056 va_list ap;
1057 cgi_reset_content();
1058 #ifdef FOSSIL_ENABLE_JSON
1059 if( g.json.isJsonMode ){
1060 char * zMsg;
1061 va_start(ap, zFormat);
1062 zMsg = vmprintf(zFormat,ap);
1063 va_end(ap);
1064 json_err( FSL_JSON_E_PANIC, zMsg, 1 );
1065 free(zMsg);
1066 fossil_exit( g.isHTTP ? 0 : 1 );
1067 }else
1068 #endif /* FOSSIL_ENABLE_JSON */
1069 {
1070 cgi_set_status(500, "Internal Server Error");
1071 cgi_printf(
1072 "<html><body><h1>Internal Server Error</h1>\n"
1073 "<plaintext>"
1074 );
1075 va_start(ap, zFormat);
1076 vxprintf(pContent,zFormat,ap);
1077 va_end(ap);
1078 cgi_reply();
1079 fossil_exit(1);
1080 }
1081 }
1082
1083 /*
1084 ** Remove the first space-delimited token from a string and return
1085 ** a pointer to it. Add a NULL to the string to terminate the token.
@@ -993,11 +1116,10 @@
1116 char *z, *zToken;
1117 int i;
1118 struct sockaddr_storage remoteName;
1119 socklen_t size = sizeof(remoteName);
1120 char zLine[2000]; /* A single line of input. */
 
1121 g.fullHttpReply = 1;
1122 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1123 malformed_request();
1124 }
1125 zToken = extract_token(zLine, &z);
@@ -1058,16 +1180,16 @@
1180 while(*p && *p != ',') p++;
1181 *p = '\0';
1182 zIpAddr = mprintf( "%s", zVal );
1183 }
1184 #if 0
1185 }else if( fossil_strcmp(zFieldName,"referer:")==0 ){
1186 cgi_setenv("HTTP_REFERER", zVal);
1187 #endif
1188 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1189 cgi_setenv("HTTP_USER_AGENT", zVal);
1190 }
 
1191 }
1192
1193 if( zIpAddr==0 &&
1194 getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName,
1195 &size)>=0
1196
+145 -23
--- src/cgi.c
+++ src/cgi.c
@@ -43,13 +43,10 @@
4343
#endif
4444
#include <time.h>
4545
#include <stdio.h>
4646
#include <stdlib.h>
4747
#include <unistd.h>
48
-#if defined (__POCC__)
49
-# undef INTERFACE
50
-#endif
5148
#include "cgi.h"
5249
5350
#if INTERFACE
5451
/*
5552
** Shortcuts for cgi_parameter. P("x") returns the value of query parameter
@@ -136,11 +133,11 @@
136133
}
137134
138135
/*
139136
** Return a pointer to the HTTP reply text.
140137
*/
141
-char *cgi_extract_content(int *pnAmt){
138
+char *cgi_extract_content(void){
142139
cgi_combine_header_and_body();
143140
return blob_buffer(&cgiContent[0]);
144141
}
145142
146143
/*
@@ -509,10 +506,13 @@
509506
zValue = "";
510507
}
511508
if( fossil_islower(zName[0]) ){
512509
cgi_set_parameter_nocopy(zName, zValue);
513510
}
511
+#ifdef FOSSIL_ENABLE_JSON
512
+ json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
513
+#endif /* FOSSIL_ENABLE_JSON */
514514
}
515515
}
516516
517517
/*
518518
** *pz is a string that consists of multiple lines of text. This
@@ -681,10 +681,88 @@
681681
}
682682
}
683683
}
684684
}
685685
}
686
+
687
+
688
+#ifdef FOSSIL_ENABLE_JSON
689
+/*
690
+** Internal helper for cson_data_source_FILE_n().
691
+*/
692
+typedef struct CgiPostReadState_ {
693
+ FILE * fh;
694
+ unsigned int len;
695
+ unsigned int pos;
696
+} CgiPostReadState;
697
+
698
+/*
699
+** cson_data_source_f() impl which reads only up to
700
+** a specified amount of data from its input FILE.
701
+** state MUST be a full populated (CgiPostReadState*).
702
+*/
703
+static int cson_data_source_FILE_n( void * state,
704
+ void * dest,
705
+ unsigned int * n ){
706
+ if( ! state || !dest || !n ) return cson_rc.ArgError;
707
+ else {
708
+ CgiPostReadState * st = (CgiPostReadState *)state;
709
+ if( st->pos >= st->len ){
710
+ *n = 0;
711
+ return 0;
712
+ } else if( !*n || ((st->pos + *n) > st->len) ){
713
+ return cson_rc.RangeError;
714
+ }else{
715
+ unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
716
+ if( ! rsz ){
717
+ *n = rsz;
718
+ return feof(st->fh) ? 0 : cson_rc.IOError;
719
+ }else{
720
+ *n = rsz;
721
+ st->pos += *n;
722
+ return 0;
723
+ }
724
+ }
725
+ }
726
+}
727
+
728
+/*
729
+** Reads a JSON object from the first contentLen bytes of zIn. On
730
+** g.json.post is updated to hold the content. On error a
731
+** FSL_JSON_E_INVALID_REQUEST response is output and fossil_exit() is
732
+** called (in HTTP mode exit code 0 is used).
733
+**
734
+** If contentLen is 0 then the whole file is read.
735
+*/
736
+void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
737
+ cson_value * jv = NULL;
738
+ int rc;
739
+ CgiPostReadState state;
740
+ state.fh = zIn;
741
+ state.len = contentLen;
742
+ state.pos = 0;
743
+ rc = cson_parse( &jv,
744
+ contentLen ? cson_data_source_FILE_n : cson_data_source_FILE,
745
+ contentLen ? (void *)&state : (void *)zIn, NULL, NULL );
746
+ if(rc){
747
+ goto invalidRequest;
748
+ }else{
749
+ json_gc_add( "POST.JSON", jv );
750
+ g.json.post.v = jv;
751
+ g.json.post.o = cson_value_get_object( jv );
752
+ if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */
753
+ goto invalidRequest;
754
+ }
755
+ }
756
+ return;
757
+ invalidRequest:
758
+ cgi_set_content_type(json_guess_content_type());
759
+ json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 );
760
+ fossil_exit( g.isHTTP ? 0 : 1);
761
+}
762
+#endif /* FOSSIL_ENABLE_JSON */
763
+
686764
687765
/*
688766
** Initialize the query parameter database. Information is pulled from
689767
** the QUERY_STRING environment variable (if it exists), from standard
690768
** input if there is POST data, and from HTTP_COOKIE.
@@ -691,19 +769,32 @@
691769
*/
692770
void cgi_init(void){
693771
char *z;
694772
const char *zType;
695773
int len;
774
+#ifdef FOSSIL_ENABLE_JSON
775
+ json_main_bootstrap();
776
+#endif
777
+ g.isHTTP = 1;
696778
cgi_destination(CGI_BODY);
779
+
780
+ z = (char*)P("HTTP_COOKIE");
781
+ if( z ){
782
+ z = mprintf("%s",z);
783
+ add_param_list(z, ';');
784
+ }
785
+
697786
z = (char*)P("QUERY_STRING");
698787
if( z ){
699788
z = mprintf("%s",z);
700789
add_param_list(z, '&');
701790
}
702791
703792
z = (char*)P("REMOTE_ADDR");
704
- if( z ) g.zIpAddr = mprintf("%s", z);
793
+ if( z ){
794
+ g.zIpAddr = mprintf("%s", z);
795
+ }
705796
706797
len = atoi(PD("CONTENT_LENGTH", "0"));
707798
g.zContentType = zType = P("CONTENT_TYPE");
708799
if( len>0 && zType ){
709800
blob_zero(&g.cgiIn);
@@ -723,17 +814,36 @@
723814
}else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
724815
blob_read_from_channel(&g.cgiIn, g.httpIn, len);
725816
}else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
726817
blob_read_from_channel(&g.cgiIn, g.httpIn, len);
727818
}
819
+#ifdef FOSSIL_ENABLE_JSON
820
+ else if( fossil_strcmp(zType, "application/json")
821
+ || fossil_strcmp(zType,"text/plain")/*assume this MIGHT be JSON*/
822
+ || fossil_strcmp(zType,"application/javascript")){
823
+ g.json.isJsonMode = 1;
824
+ cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
825
+ /* FIXMEs:
826
+
827
+ - See if fossil really needs g.cgiIn to be set for this purpose
828
+ (i don't think it does). If it does then fill g.cgiIn and
829
+ refactor to parse the JSON from there.
830
+
831
+ - After parsing POST JSON, copy the "first layer" of keys/values
832
+ to cgi_setenv(), honoring the upper-case distinction used
833
+ in add_param_list(). However...
834
+
835
+ - If we do that then we might get a disconnect in precedence of
836
+ GET/POST arguments. i prefer for GET entries to take precedence
837
+ over like-named POST entries, but in order for that to happen we
838
+ need to process QUERY_STRING _after_ reading the POST data.
839
+ */
840
+ cgi_set_content_type(json_guess_content_type());
841
+ }
842
+#endif /* FOSSIL_ENABLE_JSON */
728843
}
729844
730
- z = (char*)P("HTTP_COOKIE");
731
- if( z ){
732
- z = mprintf("%s",z);
733
- add_param_list(z, ';');
734
- }
735845
}
736846
737847
/*
738848
** This is the comparison function used to sort the aParamQP[] array of
739849
** query parameters and cookies.
@@ -943,20 +1053,33 @@
9431053
** Panic and die while processing a webpage.
9441054
*/
9451055
NORETURN void cgi_panic(const char *zFormat, ...){
9461056
va_list ap;
9471057
cgi_reset_content();
948
- cgi_set_status(500, "Internal Server Error");
949
- cgi_printf(
950
- "<html><body><h1>Internal Server Error</h1>\n"
951
- "<plaintext>"
952
- );
953
- va_start(ap, zFormat);
954
- vxprintf(pContent,zFormat,ap);
955
- va_end(ap);
956
- cgi_reply();
957
- fossil_exit(1);
1058
+#ifdef FOSSIL_ENABLE_JSON
1059
+ if( g.json.isJsonMode ){
1060
+ char * zMsg;
1061
+ va_start(ap, zFormat);
1062
+ zMsg = vmprintf(zFormat,ap);
1063
+ va_end(ap);
1064
+ json_err( FSL_JSON_E_PANIC, zMsg, 1 );
1065
+ free(zMsg);
1066
+ fossil_exit( g.isHTTP ? 0 : 1 );
1067
+ }else
1068
+#endif /* FOSSIL_ENABLE_JSON */
1069
+ {
1070
+ cgi_set_status(500, "Internal Server Error");
1071
+ cgi_printf(
1072
+ "<html><body><h1>Internal Server Error</h1>\n"
1073
+ "<plaintext>"
1074
+ );
1075
+ va_start(ap, zFormat);
1076
+ vxprintf(pContent,zFormat,ap);
1077
+ va_end(ap);
1078
+ cgi_reply();
1079
+ fossil_exit(1);
1080
+ }
9581081
}
9591082
9601083
/*
9611084
** Remove the first space-delimited token from a string and return
9621085
** a pointer to it. Add a NULL to the string to terminate the token.
@@ -993,11 +1116,10 @@
9931116
char *z, *zToken;
9941117
int i;
9951118
struct sockaddr_storage remoteName;
9961119
socklen_t size = sizeof(remoteName);
9971120
char zLine[2000]; /* A single line of input. */
998
-
9991121
g.fullHttpReply = 1;
10001122
if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
10011123
malformed_request();
10021124
}
10031125
zToken = extract_token(zLine, &z);
@@ -1058,16 +1180,16 @@
10581180
while(*p && *p != ',') p++;
10591181
*p = '\0';
10601182
zIpAddr = mprintf( "%s", zVal );
10611183
}
10621184
#if 0
1063
- else if( fossil_strcmp(zFieldName,"referer:")==0 ){
1185
+ }else if( fossil_strcmp(zFieldName,"referer:")==0 ){
10641186
cgi_setenv("HTTP_REFERER", zVal);
1187
+#endif
10651188
}else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
10661189
cgi_setenv("HTTP_USER_AGENT", zVal);
10671190
}
1068
-#endif
10691191
}
10701192
10711193
if( zIpAddr==0 &&
10721194
getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName,
10731195
&size)>=0
10741196
--- src/cgi.c
+++ src/cgi.c
@@ -43,13 +43,10 @@
43 #endif
44 #include <time.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #if defined (__POCC__)
49 # undef INTERFACE
50 #endif
51 #include "cgi.h"
52
53 #if INTERFACE
54 /*
55 ** Shortcuts for cgi_parameter. P("x") returns the value of query parameter
@@ -136,11 +133,11 @@
136 }
137
138 /*
139 ** Return a pointer to the HTTP reply text.
140 */
141 char *cgi_extract_content(int *pnAmt){
142 cgi_combine_header_and_body();
143 return blob_buffer(&cgiContent[0]);
144 }
145
146 /*
@@ -509,10 +506,13 @@
509 zValue = "";
510 }
511 if( fossil_islower(zName[0]) ){
512 cgi_set_parameter_nocopy(zName, zValue);
513 }
 
 
 
514 }
515 }
516
517 /*
518 ** *pz is a string that consists of multiple lines of text. This
@@ -681,10 +681,88 @@
681 }
682 }
683 }
684 }
685 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
687 /*
688 ** Initialize the query parameter database. Information is pulled from
689 ** the QUERY_STRING environment variable (if it exists), from standard
690 ** input if there is POST data, and from HTTP_COOKIE.
@@ -691,19 +769,32 @@
691 */
692 void cgi_init(void){
693 char *z;
694 const char *zType;
695 int len;
 
 
 
 
696 cgi_destination(CGI_BODY);
 
 
 
 
 
 
 
697 z = (char*)P("QUERY_STRING");
698 if( z ){
699 z = mprintf("%s",z);
700 add_param_list(z, '&');
701 }
702
703 z = (char*)P("REMOTE_ADDR");
704 if( z ) g.zIpAddr = mprintf("%s", z);
 
 
705
706 len = atoi(PD("CONTENT_LENGTH", "0"));
707 g.zContentType = zType = P("CONTENT_TYPE");
708 if( len>0 && zType ){
709 blob_zero(&g.cgiIn);
@@ -723,17 +814,36 @@
723 }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
724 blob_read_from_channel(&g.cgiIn, g.httpIn, len);
725 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
726 blob_read_from_channel(&g.cgiIn, g.httpIn, len);
727 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
728 }
729
730 z = (char*)P("HTTP_COOKIE");
731 if( z ){
732 z = mprintf("%s",z);
733 add_param_list(z, ';');
734 }
735 }
736
737 /*
738 ** This is the comparison function used to sort the aParamQP[] array of
739 ** query parameters and cookies.
@@ -943,20 +1053,33 @@
943 ** Panic and die while processing a webpage.
944 */
945 NORETURN void cgi_panic(const char *zFormat, ...){
946 va_list ap;
947 cgi_reset_content();
948 cgi_set_status(500, "Internal Server Error");
949 cgi_printf(
950 "<html><body><h1>Internal Server Error</h1>\n"
951 "<plaintext>"
952 );
953 va_start(ap, zFormat);
954 vxprintf(pContent,zFormat,ap);
955 va_end(ap);
956 cgi_reply();
957 fossil_exit(1);
 
 
 
 
 
 
 
 
 
 
 
 
 
958 }
959
960 /*
961 ** Remove the first space-delimited token from a string and return
962 ** a pointer to it. Add a NULL to the string to terminate the token.
@@ -993,11 +1116,10 @@
993 char *z, *zToken;
994 int i;
995 struct sockaddr_storage remoteName;
996 socklen_t size = sizeof(remoteName);
997 char zLine[2000]; /* A single line of input. */
998
999 g.fullHttpReply = 1;
1000 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1001 malformed_request();
1002 }
1003 zToken = extract_token(zLine, &z);
@@ -1058,16 +1180,16 @@
1058 while(*p && *p != ',') p++;
1059 *p = '\0';
1060 zIpAddr = mprintf( "%s", zVal );
1061 }
1062 #if 0
1063 else if( fossil_strcmp(zFieldName,"referer:")==0 ){
1064 cgi_setenv("HTTP_REFERER", zVal);
 
1065 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1066 cgi_setenv("HTTP_USER_AGENT", zVal);
1067 }
1068 #endif
1069 }
1070
1071 if( zIpAddr==0 &&
1072 getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName,
1073 &size)>=0
1074
--- src/cgi.c
+++ src/cgi.c
@@ -43,13 +43,10 @@
43 #endif
44 #include <time.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
 
 
 
48 #include "cgi.h"
49
50 #if INTERFACE
51 /*
52 ** Shortcuts for cgi_parameter. P("x") returns the value of query parameter
@@ -136,11 +133,11 @@
133 }
134
135 /*
136 ** Return a pointer to the HTTP reply text.
137 */
138 char *cgi_extract_content(void){
139 cgi_combine_header_and_body();
140 return blob_buffer(&cgiContent[0]);
141 }
142
143 /*
@@ -509,10 +506,13 @@
506 zValue = "";
507 }
508 if( fossil_islower(zName[0]) ){
509 cgi_set_parameter_nocopy(zName, zValue);
510 }
511 #ifdef FOSSIL_ENABLE_JSON
512 json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
513 #endif /* FOSSIL_ENABLE_JSON */
514 }
515 }
516
517 /*
518 ** *pz is a string that consists of multiple lines of text. This
@@ -681,10 +681,88 @@
681 }
682 }
683 }
684 }
685 }
686
687
688 #ifdef FOSSIL_ENABLE_JSON
689 /*
690 ** Internal helper for cson_data_source_FILE_n().
691 */
692 typedef struct CgiPostReadState_ {
693 FILE * fh;
694 unsigned int len;
695 unsigned int pos;
696 } CgiPostReadState;
697
698 /*
699 ** cson_data_source_f() impl which reads only up to
700 ** a specified amount of data from its input FILE.
701 ** state MUST be a full populated (CgiPostReadState*).
702 */
703 static int cson_data_source_FILE_n( void * state,
704 void * dest,
705 unsigned int * n ){
706 if( ! state || !dest || !n ) return cson_rc.ArgError;
707 else {
708 CgiPostReadState * st = (CgiPostReadState *)state;
709 if( st->pos >= st->len ){
710 *n = 0;
711 return 0;
712 } else if( !*n || ((st->pos + *n) > st->len) ){
713 return cson_rc.RangeError;
714 }else{
715 unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
716 if( ! rsz ){
717 *n = rsz;
718 return feof(st->fh) ? 0 : cson_rc.IOError;
719 }else{
720 *n = rsz;
721 st->pos += *n;
722 return 0;
723 }
724 }
725 }
726 }
727
728 /*
729 ** Reads a JSON object from the first contentLen bytes of zIn. On
730 ** g.json.post is updated to hold the content. On error a
731 ** FSL_JSON_E_INVALID_REQUEST response is output and fossil_exit() is
732 ** called (in HTTP mode exit code 0 is used).
733 **
734 ** If contentLen is 0 then the whole file is read.
735 */
736 void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
737 cson_value * jv = NULL;
738 int rc;
739 CgiPostReadState state;
740 state.fh = zIn;
741 state.len = contentLen;
742 state.pos = 0;
743 rc = cson_parse( &jv,
744 contentLen ? cson_data_source_FILE_n : cson_data_source_FILE,
745 contentLen ? (void *)&state : (void *)zIn, NULL, NULL );
746 if(rc){
747 goto invalidRequest;
748 }else{
749 json_gc_add( "POST.JSON", jv );
750 g.json.post.v = jv;
751 g.json.post.o = cson_value_get_object( jv );
752 if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */
753 goto invalidRequest;
754 }
755 }
756 return;
757 invalidRequest:
758 cgi_set_content_type(json_guess_content_type());
759 json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 );
760 fossil_exit( g.isHTTP ? 0 : 1);
761 }
762 #endif /* FOSSIL_ENABLE_JSON */
763
764
765 /*
766 ** Initialize the query parameter database. Information is pulled from
767 ** the QUERY_STRING environment variable (if it exists), from standard
768 ** input if there is POST data, and from HTTP_COOKIE.
@@ -691,19 +769,32 @@
769 */
770 void cgi_init(void){
771 char *z;
772 const char *zType;
773 int len;
774 #ifdef FOSSIL_ENABLE_JSON
775 json_main_bootstrap();
776 #endif
777 g.isHTTP = 1;
778 cgi_destination(CGI_BODY);
779
780 z = (char*)P("HTTP_COOKIE");
781 if( z ){
782 z = mprintf("%s",z);
783 add_param_list(z, ';');
784 }
785
786 z = (char*)P("QUERY_STRING");
787 if( z ){
788 z = mprintf("%s",z);
789 add_param_list(z, '&');
790 }
791
792 z = (char*)P("REMOTE_ADDR");
793 if( z ){
794 g.zIpAddr = mprintf("%s", z);
795 }
796
797 len = atoi(PD("CONTENT_LENGTH", "0"));
798 g.zContentType = zType = P("CONTENT_TYPE");
799 if( len>0 && zType ){
800 blob_zero(&g.cgiIn);
@@ -723,17 +814,36 @@
814 }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
815 blob_read_from_channel(&g.cgiIn, g.httpIn, len);
816 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
817 blob_read_from_channel(&g.cgiIn, g.httpIn, len);
818 }
819 #ifdef FOSSIL_ENABLE_JSON
820 else if( fossil_strcmp(zType, "application/json")
821 || fossil_strcmp(zType,"text/plain")/*assume this MIGHT be JSON*/
822 || fossil_strcmp(zType,"application/javascript")){
823 g.json.isJsonMode = 1;
824 cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
825 /* FIXMEs:
826
827 - See if fossil really needs g.cgiIn to be set for this purpose
828 (i don't think it does). If it does then fill g.cgiIn and
829 refactor to parse the JSON from there.
830
831 - After parsing POST JSON, copy the "first layer" of keys/values
832 to cgi_setenv(), honoring the upper-case distinction used
833 in add_param_list(). However...
834
835 - If we do that then we might get a disconnect in precedence of
836 GET/POST arguments. i prefer for GET entries to take precedence
837 over like-named POST entries, but in order for that to happen we
838 need to process QUERY_STRING _after_ reading the POST data.
839 */
840 cgi_set_content_type(json_guess_content_type());
841 }
842 #endif /* FOSSIL_ENABLE_JSON */
843 }
844
 
 
 
 
 
845 }
846
847 /*
848 ** This is the comparison function used to sort the aParamQP[] array of
849 ** query parameters and cookies.
@@ -943,20 +1053,33 @@
1053 ** Panic and die while processing a webpage.
1054 */
1055 NORETURN void cgi_panic(const char *zFormat, ...){
1056 va_list ap;
1057 cgi_reset_content();
1058 #ifdef FOSSIL_ENABLE_JSON
1059 if( g.json.isJsonMode ){
1060 char * zMsg;
1061 va_start(ap, zFormat);
1062 zMsg = vmprintf(zFormat,ap);
1063 va_end(ap);
1064 json_err( FSL_JSON_E_PANIC, zMsg, 1 );
1065 free(zMsg);
1066 fossil_exit( g.isHTTP ? 0 : 1 );
1067 }else
1068 #endif /* FOSSIL_ENABLE_JSON */
1069 {
1070 cgi_set_status(500, "Internal Server Error");
1071 cgi_printf(
1072 "<html><body><h1>Internal Server Error</h1>\n"
1073 "<plaintext>"
1074 );
1075 va_start(ap, zFormat);
1076 vxprintf(pContent,zFormat,ap);
1077 va_end(ap);
1078 cgi_reply();
1079 fossil_exit(1);
1080 }
1081 }
1082
1083 /*
1084 ** Remove the first space-delimited token from a string and return
1085 ** a pointer to it. Add a NULL to the string to terminate the token.
@@ -993,11 +1116,10 @@
1116 char *z, *zToken;
1117 int i;
1118 struct sockaddr_storage remoteName;
1119 socklen_t size = sizeof(remoteName);
1120 char zLine[2000]; /* A single line of input. */
 
1121 g.fullHttpReply = 1;
1122 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1123 malformed_request();
1124 }
1125 zToken = extract_token(zLine, &z);
@@ -1058,16 +1180,16 @@
1180 while(*p && *p != ',') p++;
1181 *p = '\0';
1182 zIpAddr = mprintf( "%s", zVal );
1183 }
1184 #if 0
1185 }else if( fossil_strcmp(zFieldName,"referer:")==0 ){
1186 cgi_setenv("HTTP_REFERER", zVal);
1187 #endif
1188 }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
1189 cgi_setenv("HTTP_USER_AGENT", zVal);
1190 }
 
1191 }
1192
1193 if( zIpAddr==0 &&
1194 getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName,
1195 &size)>=0
1196
+2 -4
--- src/checkin.c
+++ src/checkin.c
@@ -182,15 +182,15 @@
182182
int vid;
183183
db_must_be_within_tree();
184184
/* 012345678901234 */
185185
fossil_print("repository: %s\n", db_lget("repository",""));
186186
fossil_print("local-root: %s\n", g.zLocalRoot);
187
- fossil_print("server-code: %s\n", db_get("server-code", ""));
188187
vid = db_lget_int("checkout", 0);
189188
if( vid ){
190189
show_common_info(vid, "checkout:", 1, 1);
191190
}
191
+ db_record_repository_filename(0);
192192
changes_cmd();
193193
}
194194
195195
/*
196196
** COMMAND: ls
@@ -282,18 +282,16 @@
282282
Stmt q;
283283
int n;
284284
const char *zIgnoreFlag = find_option("ignore",0,1);
285285
int allFlag = find_option("dotfiles",0,0)!=0;
286286
int cwdRelative = 0;
287
- int outputManifest;
288287
Glob *pIgnore;
289288
Blob rewrittenPathname;
290289
const char *zPathname, *zDisplayName;
291290
292291
db_must_be_within_tree();
293292
cwdRelative = determine_cwd_relative_option();
294
- outputManifest = db_get_boolean("manifest",0);
295293
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
296294
n = strlen(g.zLocalRoot);
297295
blob_init(&path, g.zLocalRoot, n-1);
298296
if( zIgnoreFlag==0 ){
299297
zIgnoreFlag = db_get("ignore-glob", 0);
@@ -827,11 +825,11 @@
827825
blob_reset(&fname);
828826
}
829827
}
830828
831829
/*
832
-** COMMAND: ci
830
+** COMMAND: ci*
833831
** COMMAND: commit
834832
**
835833
** Usage: %fossil commit ?OPTIONS? ?FILE...?
836834
**
837835
** Create a new version containing all of the changes in the current
838836
--- src/checkin.c
+++ src/checkin.c
@@ -182,15 +182,15 @@
182 int vid;
183 db_must_be_within_tree();
184 /* 012345678901234 */
185 fossil_print("repository: %s\n", db_lget("repository",""));
186 fossil_print("local-root: %s\n", g.zLocalRoot);
187 fossil_print("server-code: %s\n", db_get("server-code", ""));
188 vid = db_lget_int("checkout", 0);
189 if( vid ){
190 show_common_info(vid, "checkout:", 1, 1);
191 }
 
192 changes_cmd();
193 }
194
195 /*
196 ** COMMAND: ls
@@ -282,18 +282,16 @@
282 Stmt q;
283 int n;
284 const char *zIgnoreFlag = find_option("ignore",0,1);
285 int allFlag = find_option("dotfiles",0,0)!=0;
286 int cwdRelative = 0;
287 int outputManifest;
288 Glob *pIgnore;
289 Blob rewrittenPathname;
290 const char *zPathname, *zDisplayName;
291
292 db_must_be_within_tree();
293 cwdRelative = determine_cwd_relative_option();
294 outputManifest = db_get_boolean("manifest",0);
295 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
296 n = strlen(g.zLocalRoot);
297 blob_init(&path, g.zLocalRoot, n-1);
298 if( zIgnoreFlag==0 ){
299 zIgnoreFlag = db_get("ignore-glob", 0);
@@ -827,11 +825,11 @@
827 blob_reset(&fname);
828 }
829 }
830
831 /*
832 ** COMMAND: ci
833 ** COMMAND: commit
834 **
835 ** Usage: %fossil commit ?OPTIONS? ?FILE...?
836 **
837 ** Create a new version containing all of the changes in the current
838
--- src/checkin.c
+++ src/checkin.c
@@ -182,15 +182,15 @@
182 int vid;
183 db_must_be_within_tree();
184 /* 012345678901234 */
185 fossil_print("repository: %s\n", db_lget("repository",""));
186 fossil_print("local-root: %s\n", g.zLocalRoot);
 
187 vid = db_lget_int("checkout", 0);
188 if( vid ){
189 show_common_info(vid, "checkout:", 1, 1);
190 }
191 db_record_repository_filename(0);
192 changes_cmd();
193 }
194
195 /*
196 ** COMMAND: ls
@@ -282,18 +282,16 @@
282 Stmt q;
283 int n;
284 const char *zIgnoreFlag = find_option("ignore",0,1);
285 int allFlag = find_option("dotfiles",0,0)!=0;
286 int cwdRelative = 0;
 
287 Glob *pIgnore;
288 Blob rewrittenPathname;
289 const char *zPathname, *zDisplayName;
290
291 db_must_be_within_tree();
292 cwdRelative = determine_cwd_relative_option();
 
293 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)");
294 n = strlen(g.zLocalRoot);
295 blob_init(&path, g.zLocalRoot, n-1);
296 if( zIgnoreFlag==0 ){
297 zIgnoreFlag = db_get("ignore-glob", 0);
@@ -827,11 +825,11 @@
825 blob_reset(&fname);
826 }
827 }
828
829 /*
830 ** COMMAND: ci*
831 ** COMMAND: commit
832 **
833 ** Usage: %fossil commit ?OPTIONS? ?FILE...?
834 **
835 ** Create a new version containing all of the changes in the current
836
+3 -3
--- src/checkout.c
+++ src/checkout.c
@@ -161,12 +161,12 @@
161161
}
162162
163163
}
164164
165165
/*
166
-** COMMAND: checkout
167
-** COMMAND: co
166
+** COMMAND: checkout*
167
+** COMMAND: co*
168168
**
169169
** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
170170
** or: %fossil co ?VERSION | --latest? ?OPTIONS?
171171
**
172172
** Check out a version specified on the command-line. This command
@@ -272,11 +272,11 @@
272272
}
273273
}
274274
}
275275
276276
/*
277
-** COMMAND: close
277
+** COMMAND: close*
278278
**
279279
** Usage: %fossil close ?OPTIONS?
280280
**
281281
** The opposite of "open". Close the current database connection.
282282
** Require a -f or --force flag if there are unsaved changed in the
283283
--- src/checkout.c
+++ src/checkout.c
@@ -161,12 +161,12 @@
161 }
162
163 }
164
165 /*
166 ** COMMAND: checkout
167 ** COMMAND: co
168 **
169 ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
170 ** or: %fossil co ?VERSION | --latest? ?OPTIONS?
171 **
172 ** Check out a version specified on the command-line. This command
@@ -272,11 +272,11 @@
272 }
273 }
274 }
275
276 /*
277 ** COMMAND: close
278 **
279 ** Usage: %fossil close ?OPTIONS?
280 **
281 ** The opposite of "open". Close the current database connection.
282 ** Require a -f or --force flag if there are unsaved changed in the
283
--- src/checkout.c
+++ src/checkout.c
@@ -161,12 +161,12 @@
161 }
162
163 }
164
165 /*
166 ** COMMAND: checkout*
167 ** COMMAND: co*
168 **
169 ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
170 ** or: %fossil co ?VERSION | --latest? ?OPTIONS?
171 **
172 ** Check out a version specified on the command-line. This command
@@ -272,11 +272,11 @@
272 }
273 }
274 }
275
276 /*
277 ** COMMAND: close*
278 **
279 ** Usage: %fossil close ?OPTIONS?
280 **
281 ** The opposite of "open". Close the current database connection.
282 ** Require a -f or --force flag if there are unsaved changed in the
283
+1 -1
--- src/configure.c
+++ src/configure.c
@@ -724,11 +724,11 @@
724724
blob_reset(&out);
725725
}
726726
727727
728728
/*
729
-** COMMAND: configuration
729
+** COMMAND: configuration*
730730
**
731731
** Usage: %fossil configuration METHOD ... ?OPTIONS?
732732
**
733733
** Where METHOD is one of: export import merge pull push reset. All methods
734734
** accept the -R or --repository option to specific a repository.
735735
--- src/configure.c
+++ src/configure.c
@@ -724,11 +724,11 @@
724 blob_reset(&out);
725 }
726
727
728 /*
729 ** COMMAND: configuration
730 **
731 ** Usage: %fossil configuration METHOD ... ?OPTIONS?
732 **
733 ** Where METHOD is one of: export import merge pull push reset. All methods
734 ** accept the -R or --repository option to specific a repository.
735
--- src/configure.c
+++ src/configure.c
@@ -724,11 +724,11 @@
724 blob_reset(&out);
725 }
726
727
728 /*
729 ** COMMAND: configuration*
730 **
731 ** Usage: %fossil configuration METHOD ... ?OPTIONS?
732 **
733 ** Where METHOD is one of: export import merge pull push reset. All methods
734 ** accept the -R or --repository option to specific a repository.
735
+1 -1
--- src/content.c
+++ src/content.c
@@ -303,11 +303,11 @@
303303
}
304304
return rc;
305305
}
306306
307307
/*
308
-** COMMAND: artifact
308
+** COMMAND: artifact*
309309
**
310310
** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS?
311311
**
312312
** Extract an artifact by its SHA1 hash and write the results on
313313
** standard output, or if the optional 4th argument is given, in
314314
315315
ADDED src/cson_amalgamation.c
316316
ADDED src/cson_amalgamation.h
--- src/content.c
+++ src/content.c
@@ -303,11 +303,11 @@
303 }
304 return rc;
305 }
306
307 /*
308 ** COMMAND: artifact
309 **
310 ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS?
311 **
312 ** Extract an artifact by its SHA1 hash and write the results on
313 ** standard output, or if the optional 4th argument is given, in
314
315 DDED src/cson_amalgamation.c
316 DDED src/cson_amalgamation.h
--- src/content.c
+++ src/content.c
@@ -303,11 +303,11 @@
303 }
304 return rc;
305 }
306
307 /*
308 ** COMMAND: artifact*
309 **
310 ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS?
311 **
312 ** Extract an artifact by its SHA1 hash and write the results on
313 ** standard output, or if the optional 4th argument is given, in
314
315 DDED src/cson_amalgamation.c
316 DDED src/cson_amalgamation.h
--- a/src/cson_amalgamation.c
+++ b/src/cson_amalgamation.c
@@ -0,0 +1,645 @@
1
+ use
2
+ the the defaul
3
+o 2008-05-20 * C2
4
+ MODE_KEY = 3, break;
5
+/* LICENSE
6
+
7
+CopyCENSE
8
+
9
+Copy
10
+
11
+
12
+
13
+
14
+
15
+pack.c */
16
+#V)->value)m/* FIXME: if sizeof(void*) > then store
17
+ the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
18
+ use
19
+ the defaul
20
+o 2008-05-20 * C2
21
+ MODE_KEY = 3, break;
22
+/* LICENSE
23
+
24
+CopyCENSE
25
+
26
+Copy
27
+
28
+
29
+
30
+
31
+
32
+pack.c */
33
+if( use
34
+ the clone_shared(ul
35
+o 2008-05-20 2008-05-20 * sharedshared use
36
+ ('0'<=*arg) && ('9'>=*arg)){ use
37
+ the def use
38
+ the defaul
39
+o 2008-05-20 * C2
40
+ MODE_KEY = 3, break;
41
+/* LICENSE
42
+
43
+CopyCENSE
44
+
45
+Copy
46
+
47
+ use
48
+ }
49
+
50
+ use
51
+ the defaul
52
+o 2008-05-20 * C2
53
+ MODE_KEY = 3, break;
54
+/* LICENSE
55
+
56
+CopyCENSE
57
+
58
+Copy
59
+
60
+
61
+
62
+
63
+
64
+pack.c */
65
+? cson_guess_arg_type(pos: use
66
+ the defaul
67
+0 != (rc= use
68
+ the defaul
69
+o 2008-05-20 * C2
70
+ MODE_KEY = 3, break;
71
+/* LICENSE
72
+
73
+CopyCENSE
74
+
75
+Copy
76
+
77
+
78
+
79
+
80
+
81
+pack.c */
82
+ defaul
83
+o 2008-05-20 * C2
84
+ MODE_KEY = 3, break;
85
+/* LICENSE
86
+
87
+CopyCENSE
88
+
89
+Copy
90
+
91
+
92
+
93
+
94
+
95
+pack.c */
96
+#V)->value)m/* FIXME: if sizeof(void*) > then store
97
+ the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
98
+ use
99
+ the defaul
100
+o 2008-05-20 * C2
101
+ MODE_KEY = 3, break;
102
+/* LICENSE
103
+
104
+CopyCENSE
105
+
106
+Copy
107
+
108
+
109
+
110
+
111
+
112
+pack.c */
113
+if( use
114
+ the clone_shared(ul
115
+o 2008-05-20 2008-05-20 * sharedshared use
116
+ ('0'<=*arg) && ('9'>=*arg)){ use
117
+ the def use
118
+ the defaul
119
+o 2008-05-20 * C2
120
+ MODE_KEY = 3, break;
121
+/* LICENSE
122
+
123
+CopyCENSE
124
+
125
+Copy
126
+
127
+ use
128
+ }
129
+
130
+ use
131
+ the defaul
132
+o 2008-05-20 * C2
133
+ MODE_KEY = 3, break;
134
+/* LICENSE
135
+
136
+CopyCENSE
137
+
138
+Copy
139
+
140
+
141
+
142
+
143
+
144
+pack.c */
145
+? cson_guess_arg_type(pos: use
146
+ the defaul
147
+0 != (rc= use
148
+ the defaul
149
+o 2008-05-20 * C2
150
+ MODE_KEY = 3, break;
151
+/* LICENSE
152
+
153
+CopyCENSE
154
+
155
+Copy
156
+
157
+
158
+
159
+
160
+
161
+pack.c */
162
+0_guess_arg_type(pos: use
163
+ t use
164
+ MODE_KEY = 3, use
165
+ ;
166
+ p->c 0 == rc ) use
167
+ }ARRAY_END:( ch}/*
168
+ refcount the keys! We first need a setter which takes
169
+ 8@JnB,b:a cson_string or cson_value key type.
170
+K@OzU,D:cson_value * K@I20,G@FK0,H@cX0,3:keyK@Xhl,J@_4E,S@YGA,E:( kvp->value )H@NM0,X@YHA,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,M@Yfi,C:CSON_STR(keyH@bp0,1B@YIj,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,G@8RG,TX@YOR,5:constb@YpW,z@YrX,G@cDG,1I_@Ysq,_@Nql,J@SPy,4NC@_BU,28lZAF; defined(_WIN32)
171
+# pop ) ownership of it to the
172
+ caller. It must eventually be destroyed, by the caller or its
173
+ owning or transfering
174
+
175
+void/**
176
+ This special-case impl is needed because the underlying
177
+ (generic) list operations do not know how to populate
178
+ new entries
179
+ */ use
180
+ the =*arg)){ use
181
+ the def use
182
+ the defaul
183
+o 2008-05-20 * C2
184
+ MODE_KEY = 3, break;
185
+/* LICENSE
186
+
187
+CopyCENSE
188
+
189
+Copy
190
+
191
+ use
192
+ }
193
+
194
+ use
195
+ the defaul
196
+o 2008-05-20 * C2
197
+ MODE_KEY = 3, break;
198
+/* LICENSE
199
+
200
+CopyCENSE
201
+
202
+Copy
203
+
204
+
205
+
206
+
207
+
208
+pack.c */
209
+? cson_guess_arg_type(pos: use
210
+ the defaul
211
+0 != (rc= use
212
+ the defaul
213
+o 2008-05-20 * C2
214
+ MODE_KEY = 3, break;
215
+/* LICENSE
216
+
217
+CopyCENSE
218
+
219
+Copy
220
+
221
+
222
+
223
+
224
+
225
+pack.c */
226
+0_guess_arg_type(pos: use
227
+ t use
228
+ MODE_KEY = 3, use
229
+ ;
230
+ p->c 0 == rc ) use
231
+ }Aary use
232
+ the the defaul
233
+o 2008-05-20 * C2
234
+ MODE_KEY = 3, break;
235
+/* LICENSE
236
+
237
+CopyCENSE
238
+
239
+Copy
240
+
241
+
242
+
243
+
244
+
245
+pack.c */
246
+#V)->value)m/* FIXME: if sizeof(void*) > then store
247
+ the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
248
+ use
249
+ the defaul
250
+o 2008-05-20 * C2
251
+ MODE_KEY = 3, break;
252
+/* LICENSE
253
+
254
+CopyCENSE
255
+
256
+Copy
257
+
258
+
259
+
260
+
261
+
262
+pack.c */
263
+if( use
264
+ the clone_shared(ul
265
+o 2008-05-20 2008-05-20 * sharedshared use
266
+ ('0'<=*arg) && ('9'>=*arg)){ use
267
+ the def use
268
+ the defaul
269
+o 2008-05-20 * C2
270
+ MODE_KEY = 3, break;
271
+/* LICENSE
272
+
273
+CopyCENSE
274
+
275
+Copy
276
+
277
+ use
278
+ }
279
+
280
+ use
281
+ the defaul
282
+o 2008-05-20 * C2
283
+ MODE_KEY = 3, break;
284
+/* LICENSE
285
+
286
+CopyCENSE
287
+
288
+Copy
289
+
290
+
291
+
292
+
293
+
294
+pack.c */
295
+? cson_guess_arg_type(pos: use
296
+ the defaul
297
+0 != (rc= use
298
+ the defaul
299
+o 2008-05-20 * C2
300
+ MODE_KEY = 3, break;
301
+/* LICENSE
302
+
303
+CopyCENSE
304
+
305
+Copy
306
+
307
+
308
+
309
+
310
+
311
+pack.c */
312
+ defaul
313
+o 2008-05-20 * C2
314
+ MODE_KEY = 3, break;
315
+/* LICENSE
316
+
317
+CopyCENSE
318
+
319
+Copy
320
+
321
+
322
+
323
+
324
+
325
+pack.c */
326
+#V)->value)m/* FIXME: if sizeof(void*) > then store
327
+ the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
328
+ use
329
+ the defaul
330
+o 2008-05-20 * C2
331
+ MODE_KEY = 3, break;
332
+/* LICENSE
333
+
334
+CopyCENSE
335
+
336
+Copy
337
+
338
+
339
+
340
+
341
+
342
+pack.c */
343
+if( use
344
+ the clone_shared(ul
345
+o 2008-05-20 2008-05-20 * sharedshared use
346
+ ('0'<=*arg) && ('9'>=*arg)){ use
347
+ the def use
348
+ the defaul
349
+o 2008-05-20 * C2
350
+ MODE_KEY = 3, break;
351
+/* LICENSE
352
+
353
+CopyCENSE
354
+
355
+Copy
356
+
357
+ use
358
+ }
359
+
360
+ use
361
+ the defaul
362
+o 2008-05-20 * C2
363
+ MODE_KEY = 3, break;
364
+/* LICENSE
365
+
366
+CopyCENSE
367
+
368
+Copy
369
+
370
+
371
+
372
+
373
+
374
+pack.c */
375
+? cson_guess_arg_type(pos: use
376
+ the defaul
377
+0 != (rc= use
378
+ the defaul
379
+o 2008-05-20 * C2
380
+ MODE_KEY = 3, break;
381
+/* LICENSE
382
+
383
+CopyCENSE
384
+
385
+Copy
386
+
387
+
388
+
389
+
390
+
391
+pack.c */
392
+0_guess_arg_type(pos: use
393
+ t use
394
+ MODE_KEY = 3, use
395
+ ;
396
+ p->c 0 == rc ) use
397
+ }ARRAY_END:( ch}/*
398
+ refcount the keys! We first need a setter which takes
399
+ 8@JnB,b:a cson_string or cson_value key type.
400
+K@OzU,D:cson_value * K@I20,G@FK0,H@cX0,3:keyK@Xhl,J@_4E,S@YGA,E:( kvp->value )H@NM0,X@YHA,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,M@Yfi,C:CSON_STR(keyH@bp0,1B@YIj,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,G@8RG,TX@YOR,5:constb@YpW,z@YrX,G@cDG,1I_@Ysq,_@Nql,J@SPy,4NC@_BU,28lZAF; defined(_WIN32)
401
+# pop ) ownership of it to the
402
+ caller. It must eventually be destroyed, by the caller or its
403
+ owning or transfering
404
+
405
+void/**
406
+ This special-case impl is needed because the underlying
407
+ (generic) list operations do not know how to populate
408
+ new entries
409
+ */ use
410
+ the =*arg)){ use
411
+ the def use
412
+ the defaul
413
+o 2008-05-20 * C2
414
+ MODE_KEY = 3, break;
415
+/* LICENSE
416
+
417
+CopyCENSE
418
+
419
+Copy
420
+
421
+ use
422
+ }
423
+
424
+ use
425
+ the defaul
426
+o 2008-05-20 * C2
427
+ MODE_KEY = 3, break;
428
+/* LICENSE
429
+
430
+CopyCENSE
431
+
432
+Copy
433
+
434
+
435
+
436
+
437
+
438
+pack.c */
439
+? cson_guess_arg_type(pos: use
440
+ the defaul
441
+0 != (rc= use
442
+ the defaul
443
+o 2008-05-20 * C2
444
+ MODE_KEY = 3, break;
445
+/* LICENSE
446
+
447
+CopyCENSE
448
+
449
+Copy
450
+
451
+
452
+
453
+
454
+
455
+pack.c */
456
+0_guess_arg_type(pos: use
457
+ t use
458
+ MODE_KEY = 3, use
459
+ ;
460
+ p->c 0 == rc ) use
461
+ }Aarye
462
+ }
463
+
464
+ use
465
+ the defaul
466
+o 2008-05-20 * C2
467
+ MODE_KEY = 3, break;
468
+/* LICENSE
469
+
470
+CopyCENSE
471
+
472
+Copy
473
+
474
+
475
+
476
+
477
+
478
+pack.c */
479
+? cson_guess_arg_type(pos: use
480
+ the defaul
481
+0 != (rc= use
482
+ the defaul
483
+o 2008-05-20 * C2
484
+ MODE_KEY = 3, break;
485
+/* LICENSE
486
+
487
+CopyCENSE
488
+
489
+Copy
490
+
491
+
492
+
493
+
494
+
495
+pack.c */
496
+ defaul
497
+o 2008-05-20 * C2
498
+ MODE_KEY = 3, break;
499
+/* LICENSE
500
+
501
+CopyCENSE
502
+
503
+Copy
504
+
505
+
506
+
507
+
508
+
509
+pack.c */
510
+#V)->value)m/* FIXME: if sizeof(void*) > then store
511
+ the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
512
+ use
513
+ the defaul
514
+o 2008-05-20 * C2
515
+ MODE_KEY = 3, break;
516
+/* LICENSE
517
+
518
+CopyCENSE
519
+
520
+Copy
521
+
522
+
523
+
524
+
525
+
526
+pack.c */
527
+if( use
528
+ the clone_shared(ul
529
+o 2008-05-20 2008-05-20 * sharedshared use
530
+ ('0'<=*arg) && ('9'>=*arg)){ use
531
+ the def use
532
+ the defaul
533
+o 2008-05-20 * C2
534
+ MODE_KEY = 3, break;
535
+/* LICENSE
536
+
537
+CopyCENSE
538
+
539
+Copy
540
+
541
+ use
542
+ }
543
+
544
+ use
545
+ the defaul
546
+o 2008-05-20 * C2
547
+ MODE_KEY = 3, break;
548
+/* LICENSE
549
+
550
+CopyCENSE
551
+
552
+Copy
553
+
554
+
555
+
556
+
557
+
558
+pack.c */
559
+? cson_guess_arg_type(pos: use
560
+ the defaul
561
+0 != (rc= use
562
+ the defaul
563
+o 2008-05-20 * C2
564
+ MODE_KEY = 3, break;
565
+/* LICENSE
566
+
567
+CopyCENSE
568
+
569
+Copy
570
+
571
+
572
+
573
+
574
+
575
+pack.c */
576
+0_guess_arg_type(pos: use
577
+ t use
578
+ MODE_KEY = 3, use
579
+ ;
580
+ p->c 0 == rc ) use
581
+ }ARRAY_END:( ch}/*
582
+ refcount the keys! We first need a setter which takes
583
+ 8@JnB,b:a cson_string or cson_value key type.
584
+K@OzU,D:cson_value * K@I20,G@FK0,H@cX0,3:keyK@Xhl,J@_4E,S@YGA,E:( kvp->value )H@NM0,X@YHA,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,M@Yfi,C:CSON_STR(keyH@bp0,1B@YIj,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,G@8RG,TX@YOR,5:constb@YpW,z@YrX,G@cDG,1I_@Ysq,_@Nql,J@SPy,4NC@_BU,28lZAF; defined(_WIN32)
585
+# pop ) ownership of it to the
586
+ caller. It must eventually be destroyed, by the caller or its
587
+ owning or transfering
588
+
589
+void/**
590
+ This special-case impl is needed because the underlying
591
+ (generic) list operations do not know how to populate
592
+ new entries
593
+ */ use
594
+ the =*arg)){ use
595
+ the def use
596
+ the defaul
597
+o 2008-05-20 * C2
598
+ MODE_KEY = 3, break;
599
+/* LICENSE
600
+
601
+CopyCENSE
602
+
603
+Copy
604
+
605
+ use
606
+ }
607
+
608
+ use
609
+ the defaul
610
+o 2008-05-20 * C2
611
+ MODE_KEY = 3, break;
612
+/* LICENSE
613
+
614
+CopyCENSE
615
+
616
+Copy
617
+
618
+
619
+
620
+
621
+
622
+pack.c */
623
+? cson_guess_arg_type(pos: use
624
+ the defaul
625
+0 != (rc= use
626
+ the defaul
627
+o 2008-05-20 * C2
628
+ MODE_KEY = 3, break;
629
+/* LICENSE
630
+
631
+CopyCENSE
632
+
633
+Copy
634
+
635
+
636
+
637
+
638
+
639
+pack.c */
640
+0_guess_arg_type(pos: use
641
+ t use
642
+ MODE_KEY = 3, use
643
+ ;
644
+ p->c 0 == rc ) use
645
+ }Aary
--- a/src/cson_amalgamation.c
+++ b/src/cson_amalgamation.c
@@ -0,0 +1,645 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/cson_amalgamation.c
+++ b/src/cson_amalgamation.c
@@ -0,0 +1,645 @@
1 use
2 the the defaul
3 o 2008-05-20 * C2
4 MODE_KEY = 3, break;
5 /* LICENSE
6
7 CopyCENSE
8
9 Copy
10
11
12
13
14
15 pack.c */
16 #V)->value)m/* FIXME: if sizeof(void*) > then store
17 the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
18 use
19 the defaul
20 o 2008-05-20 * C2
21 MODE_KEY = 3, break;
22 /* LICENSE
23
24 CopyCENSE
25
26 Copy
27
28
29
30
31
32 pack.c */
33 if( use
34 the clone_shared(ul
35 o 2008-05-20 2008-05-20 * sharedshared use
36 ('0'<=*arg) && ('9'>=*arg)){ use
37 the def use
38 the defaul
39 o 2008-05-20 * C2
40 MODE_KEY = 3, break;
41 /* LICENSE
42
43 CopyCENSE
44
45 Copy
46
47 use
48 }
49
50 use
51 the defaul
52 o 2008-05-20 * C2
53 MODE_KEY = 3, break;
54 /* LICENSE
55
56 CopyCENSE
57
58 Copy
59
60
61
62
63
64 pack.c */
65 ? cson_guess_arg_type(pos: use
66 the defaul
67 0 != (rc= use
68 the defaul
69 o 2008-05-20 * C2
70 MODE_KEY = 3, break;
71 /* LICENSE
72
73 CopyCENSE
74
75 Copy
76
77
78
79
80
81 pack.c */
82 defaul
83 o 2008-05-20 * C2
84 MODE_KEY = 3, break;
85 /* LICENSE
86
87 CopyCENSE
88
89 Copy
90
91
92
93
94
95 pack.c */
96 #V)->value)m/* FIXME: if sizeof(void*) > then store
97 the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
98 use
99 the defaul
100 o 2008-05-20 * C2
101 MODE_KEY = 3, break;
102 /* LICENSE
103
104 CopyCENSE
105
106 Copy
107
108
109
110
111
112 pack.c */
113 if( use
114 the clone_shared(ul
115 o 2008-05-20 2008-05-20 * sharedshared use
116 ('0'<=*arg) && ('9'>=*arg)){ use
117 the def use
118 the defaul
119 o 2008-05-20 * C2
120 MODE_KEY = 3, break;
121 /* LICENSE
122
123 CopyCENSE
124
125 Copy
126
127 use
128 }
129
130 use
131 the defaul
132 o 2008-05-20 * C2
133 MODE_KEY = 3, break;
134 /* LICENSE
135
136 CopyCENSE
137
138 Copy
139
140
141
142
143
144 pack.c */
145 ? cson_guess_arg_type(pos: use
146 the defaul
147 0 != (rc= use
148 the defaul
149 o 2008-05-20 * C2
150 MODE_KEY = 3, break;
151 /* LICENSE
152
153 CopyCENSE
154
155 Copy
156
157
158
159
160
161 pack.c */
162 0_guess_arg_type(pos: use
163 t use
164 MODE_KEY = 3, use
165 ;
166 p->c 0 == rc ) use
167 }ARRAY_END:( ch}/*
168 refcount the keys! We first need a setter which takes
169 8@JnB,b:a cson_string or cson_value key type.
170 K@OzU,D:cson_value * K@I20,G@FK0,H@cX0,3:keyK@Xhl,J@_4E,S@YGA,E:( kvp->value )H@NM0,X@YHA,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,M@Yfi,C:CSON_STR(keyH@bp0,1B@YIj,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,G@8RG,TX@YOR,5:constb@YpW,z@YrX,G@cDG,1I_@Ysq,_@Nql,J@SPy,4NC@_BU,28lZAF; defined(_WIN32)
171 # pop ) ownership of it to the
172 caller. It must eventually be destroyed, by the caller or its
173 owning or transfering
174
175 void/**
176 This special-case impl is needed because the underlying
177 (generic) list operations do not know how to populate
178 new entries
179 */ use
180 the =*arg)){ use
181 the def use
182 the defaul
183 o 2008-05-20 * C2
184 MODE_KEY = 3, break;
185 /* LICENSE
186
187 CopyCENSE
188
189 Copy
190
191 use
192 }
193
194 use
195 the defaul
196 o 2008-05-20 * C2
197 MODE_KEY = 3, break;
198 /* LICENSE
199
200 CopyCENSE
201
202 Copy
203
204
205
206
207
208 pack.c */
209 ? cson_guess_arg_type(pos: use
210 the defaul
211 0 != (rc= use
212 the defaul
213 o 2008-05-20 * C2
214 MODE_KEY = 3, break;
215 /* LICENSE
216
217 CopyCENSE
218
219 Copy
220
221
222
223
224
225 pack.c */
226 0_guess_arg_type(pos: use
227 t use
228 MODE_KEY = 3, use
229 ;
230 p->c 0 == rc ) use
231 }Aary use
232 the the defaul
233 o 2008-05-20 * C2
234 MODE_KEY = 3, break;
235 /* LICENSE
236
237 CopyCENSE
238
239 Copy
240
241
242
243
244
245 pack.c */
246 #V)->value)m/* FIXME: if sizeof(void*) > then store
247 the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
248 use
249 the defaul
250 o 2008-05-20 * C2
251 MODE_KEY = 3, break;
252 /* LICENSE
253
254 CopyCENSE
255
256 Copy
257
258
259
260
261
262 pack.c */
263 if( use
264 the clone_shared(ul
265 o 2008-05-20 2008-05-20 * sharedshared use
266 ('0'<=*arg) && ('9'>=*arg)){ use
267 the def use
268 the defaul
269 o 2008-05-20 * C2
270 MODE_KEY = 3, break;
271 /* LICENSE
272
273 CopyCENSE
274
275 Copy
276
277 use
278 }
279
280 use
281 the defaul
282 o 2008-05-20 * C2
283 MODE_KEY = 3, break;
284 /* LICENSE
285
286 CopyCENSE
287
288 Copy
289
290
291
292
293
294 pack.c */
295 ? cson_guess_arg_type(pos: use
296 the defaul
297 0 != (rc= use
298 the defaul
299 o 2008-05-20 * C2
300 MODE_KEY = 3, break;
301 /* LICENSE
302
303 CopyCENSE
304
305 Copy
306
307
308
309
310
311 pack.c */
312 defaul
313 o 2008-05-20 * C2
314 MODE_KEY = 3, break;
315 /* LICENSE
316
317 CopyCENSE
318
319 Copy
320
321
322
323
324
325 pack.c */
326 #V)->value)m/* FIXME: if sizeof(void*) > then store
327 the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
328 use
329 the defaul
330 o 2008-05-20 * C2
331 MODE_KEY = 3, break;
332 /* LICENSE
333
334 CopyCENSE
335
336 Copy
337
338
339
340
341
342 pack.c */
343 if( use
344 the clone_shared(ul
345 o 2008-05-20 2008-05-20 * sharedshared use
346 ('0'<=*arg) && ('9'>=*arg)){ use
347 the def use
348 the defaul
349 o 2008-05-20 * C2
350 MODE_KEY = 3, break;
351 /* LICENSE
352
353 CopyCENSE
354
355 Copy
356
357 use
358 }
359
360 use
361 the defaul
362 o 2008-05-20 * C2
363 MODE_KEY = 3, break;
364 /* LICENSE
365
366 CopyCENSE
367
368 Copy
369
370
371
372
373
374 pack.c */
375 ? cson_guess_arg_type(pos: use
376 the defaul
377 0 != (rc= use
378 the defaul
379 o 2008-05-20 * C2
380 MODE_KEY = 3, break;
381 /* LICENSE
382
383 CopyCENSE
384
385 Copy
386
387
388
389
390
391 pack.c */
392 0_guess_arg_type(pos: use
393 t use
394 MODE_KEY = 3, use
395 ;
396 p->c 0 == rc ) use
397 }ARRAY_END:( ch}/*
398 refcount the keys! We first need a setter which takes
399 8@JnB,b:a cson_string or cson_value key type.
400 K@OzU,D:cson_value * K@I20,G@FK0,H@cX0,3:keyK@Xhl,J@_4E,S@YGA,E:( kvp->value )H@NM0,X@YHA,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,M@Yfi,C:CSON_STR(keyH@bp0,1B@YIj,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,G@8RG,TX@YOR,5:constb@YpW,z@YrX,G@cDG,1I_@Ysq,_@Nql,J@SPy,4NC@_BU,28lZAF; defined(_WIN32)
401 # pop ) ownership of it to the
402 caller. It must eventually be destroyed, by the caller or its
403 owning or transfering
404
405 void/**
406 This special-case impl is needed because the underlying
407 (generic) list operations do not know how to populate
408 new entries
409 */ use
410 the =*arg)){ use
411 the def use
412 the defaul
413 o 2008-05-20 * C2
414 MODE_KEY = 3, break;
415 /* LICENSE
416
417 CopyCENSE
418
419 Copy
420
421 use
422 }
423
424 use
425 the defaul
426 o 2008-05-20 * C2
427 MODE_KEY = 3, break;
428 /* LICENSE
429
430 CopyCENSE
431
432 Copy
433
434
435
436
437
438 pack.c */
439 ? cson_guess_arg_type(pos: use
440 the defaul
441 0 != (rc= use
442 the defaul
443 o 2008-05-20 * C2
444 MODE_KEY = 3, break;
445 /* LICENSE
446
447 CopyCENSE
448
449 Copy
450
451
452
453
454
455 pack.c */
456 0_guess_arg_type(pos: use
457 t use
458 MODE_KEY = 3, use
459 ;
460 p->c 0 == rc ) use
461 }Aarye
462 }
463
464 use
465 the defaul
466 o 2008-05-20 * C2
467 MODE_KEY = 3, break;
468 /* LICENSE
469
470 CopyCENSE
471
472 Copy
473
474
475
476
477
478 pack.c */
479 ? cson_guess_arg_type(pos: use
480 the defaul
481 0 != (rc= use
482 the defaul
483 o 2008-05-20 * C2
484 MODE_KEY = 3, break;
485 /* LICENSE
486
487 CopyCENSE
488
489 Copy
490
491
492
493
494
495 pack.c */
496 defaul
497 o 2008-05-20 * C2
498 MODE_KEY = 3, break;
499 /* LICENSE
500
501 CopyCENSE
502
503 Copy
504
505
506
507
508
509 pack.c */
510 #V)->value)m/* FIXME: if sizeof(void*) > then store
511 the int value directly in the v). The curbytes(!!!) on 64-bit builds.in
512 use
513 the defaul
514 o 2008-05-20 * C2
515 MODE_KEY = 3, break;
516 /* LICENSE
517
518 CopyCENSE
519
520 Copy
521
522
523
524
525
526 pack.c */
527 if( use
528 the clone_shared(ul
529 o 2008-05-20 2008-05-20 * sharedshared use
530 ('0'<=*arg) && ('9'>=*arg)){ use
531 the def use
532 the defaul
533 o 2008-05-20 * C2
534 MODE_KEY = 3, break;
535 /* LICENSE
536
537 CopyCENSE
538
539 Copy
540
541 use
542 }
543
544 use
545 the defaul
546 o 2008-05-20 * C2
547 MODE_KEY = 3, break;
548 /* LICENSE
549
550 CopyCENSE
551
552 Copy
553
554
555
556
557
558 pack.c */
559 ? cson_guess_arg_type(pos: use
560 the defaul
561 0 != (rc= use
562 the defaul
563 o 2008-05-20 * C2
564 MODE_KEY = 3, break;
565 /* LICENSE
566
567 CopyCENSE
568
569 Copy
570
571
572
573
574
575 pack.c */
576 0_guess_arg_type(pos: use
577 t use
578 MODE_KEY = 3, use
579 ;
580 p->c 0 == rc ) use
581 }ARRAY_END:( ch}/*
582 refcount the keys! We first need a setter which takes
583 8@JnB,b:a cson_string or cson_value key type.
584 K@OzU,D:cson_value * K@I20,G@FK0,H@cX0,3:keyK@Xhl,J@_4E,S@YGA,E:( kvp->value )H@NM0,X@YHA,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,M@Yfi,C:CSON_STR(keyH@bp0,1B@YIj,G@cDG,G@PEl,W@O4l,_@YNG,L@blG,G@8RG,TX@YOR,5:constb@YpW,z@YrX,G@cDG,1I_@Ysq,_@Nql,J@SPy,4NC@_BU,28lZAF; defined(_WIN32)
585 # pop ) ownership of it to the
586 caller. It must eventually be destroyed, by the caller or its
587 owning or transfering
588
589 void/**
590 This special-case impl is needed because the underlying
591 (generic) list operations do not know how to populate
592 new entries
593 */ use
594 the =*arg)){ use
595 the def use
596 the defaul
597 o 2008-05-20 * C2
598 MODE_KEY = 3, break;
599 /* LICENSE
600
601 CopyCENSE
602
603 Copy
604
605 use
606 }
607
608 use
609 the defaul
610 o 2008-05-20 * C2
611 MODE_KEY = 3, break;
612 /* LICENSE
613
614 CopyCENSE
615
616 Copy
617
618
619
620
621
622 pack.c */
623 ? cson_guess_arg_type(pos: use
624 the defaul
625 0 != (rc= use
626 the defaul
627 o 2008-05-20 * C2
628 MODE_KEY = 3, break;
629 /* LICENSE
630
631 CopyCENSE
632
633 Copy
634
635
636
637
638
639 pack.c */
640 0_guess_arg_type(pos: use
641 t use
642 MODE_KEY = 3, use
643 ;
644 p->c 0 == rc ) use
645 }Aary
--- a/src/cson_amalgamation.h
+++ b/src/cson_amalgamation.h
@@ -0,0 +1,9 @@
1
+#ifdef FOSSIL_ENABLEconst
2
+ #ifdef returned
3
+ #ifde.value * recursively if it is
4
+ and multiple times within a
5
+ full cost. We
6
+ have no what visited
7
+ we
8
+ count
9
+ for#ifdef FOSSIL_ENABLEFOSSIL_ENABLE
--- a/src/cson_amalgamation.h
+++ b/src/cson_amalgamation.h
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
--- a/src/cson_amalgamation.h
+++ b/src/cson_amalgamation.h
@@ -0,0 +1,9 @@
1 #ifdef FOSSIL_ENABLEconst
2 #ifdef returned
3 #ifde.value * recursively if it is
4 and multiple times within a
5 full cost. We
6 have no what visited
7 we
8 count
9 for#ifdef FOSSIL_ENABLEFOSSIL_ENABLE
+64 -7
--- src/db.c
+++ src/db.c
@@ -48,40 +48,59 @@
4848
Blob sql; /* The SQL for this statement */
4949
sqlite3_stmt *pStmt; /* The results of sqlite3_prepare() */
5050
Stmt *pNext, *pPrev; /* List of all unfinalized statements */
5151
int nStep; /* Number of sqlite3_step() calls */
5252
};
53
+
54
+/*
55
+** Copy this to initialize a Stmt object to a clean/empty state. This
56
+** is useful to help avoid assertions when performing cleanup in some
57
+** error handling cases.
58
+*/
59
+#define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0}
5360
#endif /* INTERFACE */
61
+const struct Stmt empty_Stmt = empty_Stmt_m;
5462
5563
/*
5664
** Call this routine when a database error occurs.
5765
*/
5866
static void db_err(const char *zFormat, ...){
5967
va_list ap;
6068
char *z;
69
+ int rc = 1;
6170
static const char zRebuildMsg[] =
6271
"If you have recently updated your fossil executable, you might\n"
6372
"need to run \"fossil all rebuild\" to bring the repository\n"
6473
"schemas up to date.\n";
6574
va_start(ap, zFormat);
6675
z = vmprintf(zFormat, ap);
6776
va_end(ap);
77
+#ifdef FOSSIL_ENABLE_JSON
78
+ if( g.json.isJsonMode ){
79
+ json_err( 0, z, 1 );
80
+ if( g.isHTTP ){
81
+ rc = 0 /* avoid HTTP 500 */;
82
+ }
83
+ }
84
+ else
85
+#endif /* FOSSIL_ENABLE_JSON */
6886
if( g.xferPanic ){
6987
cgi_reset_content();
7088
@ error Database\serror:\s%F(z)
71
- cgi_reply();
89
+ cgi_reply();
7290
}
73
- if( g.cgiOutput ){
91
+ else if( g.cgiOutput ){
7492
g.cgiOutput = 0;
7593
cgi_printf("<h1>Database Error</h1>\n"
7694
"<pre>%h</pre><p>%s</p>", z, zRebuildMsg);
7795
cgi_reply();
7896
}else{
7997
fprintf(stderr, "%s: %s\n\n%s", fossil_nameofexe(), z, zRebuildMsg);
8098
}
99
+ free(z);
81100
db_force_rollback();
82
- fossil_exit(1);
101
+ fossil_exit(rc);
83102
}
84103
85104
static int nBegin = 0; /* Nesting depth of BEGIN */
86105
static int doRollback = 0; /* True to force a rollback */
87106
static int nCommitHook = 0; /* Number of commit hooks */
@@ -561,11 +580,11 @@
561580
** Execute a query. Return the first column of the first row
562581
** of the result set as a string. Space to hold the string is
563582
** obtained from malloc(). If the result set is empty, return
564583
** zDefault instead.
565584
*/
566
-char *db_text(char *zDefault, const char *zSql, ...){
585
+char *db_text(char const *zDefault, const char *zSql, ...){
567586
va_list ap;
568587
Stmt s;
569588
char *z;
570589
va_start(ap, zSql);
571590
db_vprepare(&s, 0, zSql, ap);
@@ -863,15 +882,24 @@
863882
db_err("unable to find the name of a repository database");
864883
}
865884
}
866885
if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){
867886
if( file_access(zDbName, 0) ){
887
+#ifdef FOSSIL_ENABLE_JSON
888
+ g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
889
+#endif
868890
fossil_panic("repository does not exist or"
869891
" is in an unreadable directory: %s", zDbName);
870892
}else if( file_access(zDbName, R_OK) ){
893
+#ifdef FOSSIL_ENABLE_JSON
894
+ g.json.resultCode = FSL_JSON_E_DENIED;
895
+#endif
871896
fossil_panic("read permission denied for repository %s", zDbName);
872897
}else{
898
+#ifdef FOSSIL_ENABLE_JSON
899
+ g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
900
+#endif
873901
fossil_panic("not a valid repository: %s", zDbName);
874902
}
875903
}
876904
db_open_or_attach(zDbName, "repository");
877905
g.repositoryOpen = 1;
@@ -902,11 +930,11 @@
902930
}
903931
if( zRep==0 ){
904932
if( db_open_local()==0 ){
905933
goto rep_not_found;
906934
}
907
- zRep = db_lget("repository", 0);
935
+ zRep = db_lget("repository", 0)/*leak here*/;
908936
if( zRep==0 ){
909937
goto rep_not_found;
910938
}
911939
}
912940
db_open_repository(zRep);
@@ -914,10 +942,13 @@
914942
if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
915943
return;
916944
}
917945
rep_not_found:
918946
if( (bFlags & OPEN_OK_NOT_FOUND)==0 ){
947
+#ifdef FOSSIL_ENABLE_JSON
948
+ g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
949
+#endif
919950
fossil_fatal("use --repository or -R to specify the repository database");
920951
}
921952
}
922953
923954
/*
@@ -944,10 +975,13 @@
944975
** Verify that the repository schema is correct. If it is not correct,
945976
** issue a fatal error and die.
946977
*/
947978
void db_verify_schema(void){
948979
if( db_schema_is_outofdate() ){
980
+#ifdef FOSSIL_ENABLE_JSON
981
+ g.json.resultCode = FSL_JSON_E_DB_NEEDS_REBUILD;
982
+#endif
949983
fossil_warning("incorrect repository schema version");
950984
fossil_warning("your repository has schema version \"%s\" "
951985
"but this binary expects version \"%s\"",
952986
db_get("aux-schema",0), AUX_SCHEMA);
953987
fossil_fatal("run \"fossil rebuild\" to fix this problem");
@@ -1164,11 +1198,11 @@
11641198
manifest_crosslink(rid, &manifest);
11651199
}
11661200
}
11671201
11681202
/*
1169
-** COMMAND: new
1203
+** COMMAND: new*
11701204
** COMMAND: init
11711205
**
11721206
** Usage: %fossil new ?OPTIONS? FILENAME
11731207
** Or: %fossil init ?OPTIONS? FILENAME
11741208
**
@@ -1621,10 +1655,17 @@
16211655
** of the following form:
16221656
**
16231657
** repo:%s
16241658
**
16251659
** The value field is set to 1.
1660
+**
1661
+** If running from a local checkout, also record the root of the checkout
1662
+** as follows:
1663
+**
1664
+** ckout:%s
1665
+**
1666
+** Where %s is the checkout root. The value is the repository file.
16261667
*/
16271668
void db_record_repository_filename(const char *zName){
16281669
Blob full;
16291670
if( zName==0 ){
16301671
if( !g.localOpen ) return;
@@ -1635,10 +1676,17 @@
16351676
db_multi_exec(
16361677
"INSERT OR IGNORE INTO global_config(name,value)"
16371678
"VALUES('repo:%q',1)",
16381679
blob_str(&full)
16391680
);
1681
+ if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){
1682
+ db_multi_exec(
1683
+ "REPLACE INTO global_config(name, value)"
1684
+ "VALUES('ckout:%q','%q');",
1685
+ g.zLocalRoot, blob_str(&full)
1686
+ );
1687
+ }
16401688
db_swap_connections();
16411689
blob_reset(&full);
16421690
}
16431691
16441692
/*
@@ -1792,18 +1840,21 @@
17921840
{ "repo-cksum", 0, 0, 0, "on" },
17931841
{ "self-register", 0, 0, 0, "off" },
17941842
{ "ssl-ca-location",0, 40, 0, "" },
17951843
{ "ssl-identity", 0, 40, 0, "" },
17961844
{ "ssh-command", 0, 32, 0, "" },
1845
+#ifdef FOSSIL_ENABLE_TCL
1846
+ { "tcl", 0, 0, 0, "off" },
1847
+#endif
17971848
{ "web-browser", 0, 32, 0, "" },
17981849
{ "white-foreground", 0, 0, 0, "off" },
17991850
{ 0,0,0,0,0 }
18001851
};
18011852
18021853
/*
18031854
** COMMAND: settings
1804
-** COMMAND: unset
1855
+** COMMAND: unset*
18051856
**
18061857
** %fossil settings ?PROPERTY? ?VALUE? ?-global?
18071858
** %fossil unset PROPERTY ?-global?
18081859
**
18091860
** The "settings" command with no arguments lists all properties and their
@@ -1945,10 +1996,16 @@
19451996
** authenticate this client, in addition to the normal
19461997
** password authentication.
19471998
**
19481999
** ssh-command Command used to talk to a remote machine with
19492000
** the "ssh://" protocol.
2001
+**
2002
+** tcl If enabled, Tcl integration commands will be added to
2003
+** the TH1 interpreter, allowing Tcl expressions and
2004
+** scripts to be evaluated from TH1. Additionally, the
2005
+** Tcl interpreter will be able to evaluate TH1 expressions
2006
+** and scripts. Default: off.
19502007
**
19512008
** web-browser A shell command used to launch your preferred
19522009
** web browser when given a URL as an argument.
19532010
** Defaults to "start" on windows, "open" on Mac,
19542011
** and "firefox" on Unix.
19552012
--- src/db.c
+++ src/db.c
@@ -48,40 +48,59 @@
48 Blob sql; /* The SQL for this statement */
49 sqlite3_stmt *pStmt; /* The results of sqlite3_prepare() */
50 Stmt *pNext, *pPrev; /* List of all unfinalized statements */
51 int nStep; /* Number of sqlite3_step() calls */
52 };
 
 
 
 
 
 
 
53 #endif /* INTERFACE */
 
54
55 /*
56 ** Call this routine when a database error occurs.
57 */
58 static void db_err(const char *zFormat, ...){
59 va_list ap;
60 char *z;
 
61 static const char zRebuildMsg[] =
62 "If you have recently updated your fossil executable, you might\n"
63 "need to run \"fossil all rebuild\" to bring the repository\n"
64 "schemas up to date.\n";
65 va_start(ap, zFormat);
66 z = vmprintf(zFormat, ap);
67 va_end(ap);
 
 
 
 
 
 
 
 
 
68 if( g.xferPanic ){
69 cgi_reset_content();
70 @ error Database\serror:\s%F(z)
71 cgi_reply();
72 }
73 if( g.cgiOutput ){
74 g.cgiOutput = 0;
75 cgi_printf("<h1>Database Error</h1>\n"
76 "<pre>%h</pre><p>%s</p>", z, zRebuildMsg);
77 cgi_reply();
78 }else{
79 fprintf(stderr, "%s: %s\n\n%s", fossil_nameofexe(), z, zRebuildMsg);
80 }
 
81 db_force_rollback();
82 fossil_exit(1);
83 }
84
85 static int nBegin = 0; /* Nesting depth of BEGIN */
86 static int doRollback = 0; /* True to force a rollback */
87 static int nCommitHook = 0; /* Number of commit hooks */
@@ -561,11 +580,11 @@
561 ** Execute a query. Return the first column of the first row
562 ** of the result set as a string. Space to hold the string is
563 ** obtained from malloc(). If the result set is empty, return
564 ** zDefault instead.
565 */
566 char *db_text(char *zDefault, const char *zSql, ...){
567 va_list ap;
568 Stmt s;
569 char *z;
570 va_start(ap, zSql);
571 db_vprepare(&s, 0, zSql, ap);
@@ -863,15 +882,24 @@
863 db_err("unable to find the name of a repository database");
864 }
865 }
866 if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){
867 if( file_access(zDbName, 0) ){
 
 
 
868 fossil_panic("repository does not exist or"
869 " is in an unreadable directory: %s", zDbName);
870 }else if( file_access(zDbName, R_OK) ){
 
 
 
871 fossil_panic("read permission denied for repository %s", zDbName);
872 }else{
 
 
 
873 fossil_panic("not a valid repository: %s", zDbName);
874 }
875 }
876 db_open_or_attach(zDbName, "repository");
877 g.repositoryOpen = 1;
@@ -902,11 +930,11 @@
902 }
903 if( zRep==0 ){
904 if( db_open_local()==0 ){
905 goto rep_not_found;
906 }
907 zRep = db_lget("repository", 0);
908 if( zRep==0 ){
909 goto rep_not_found;
910 }
911 }
912 db_open_repository(zRep);
@@ -914,10 +942,13 @@
914 if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
915 return;
916 }
917 rep_not_found:
918 if( (bFlags & OPEN_OK_NOT_FOUND)==0 ){
 
 
 
919 fossil_fatal("use --repository or -R to specify the repository database");
920 }
921 }
922
923 /*
@@ -944,10 +975,13 @@
944 ** Verify that the repository schema is correct. If it is not correct,
945 ** issue a fatal error and die.
946 */
947 void db_verify_schema(void){
948 if( db_schema_is_outofdate() ){
 
 
 
949 fossil_warning("incorrect repository schema version");
950 fossil_warning("your repository has schema version \"%s\" "
951 "but this binary expects version \"%s\"",
952 db_get("aux-schema",0), AUX_SCHEMA);
953 fossil_fatal("run \"fossil rebuild\" to fix this problem");
@@ -1164,11 +1198,11 @@
1164 manifest_crosslink(rid, &manifest);
1165 }
1166 }
1167
1168 /*
1169 ** COMMAND: new
1170 ** COMMAND: init
1171 **
1172 ** Usage: %fossil new ?OPTIONS? FILENAME
1173 ** Or: %fossil init ?OPTIONS? FILENAME
1174 **
@@ -1621,10 +1655,17 @@
1621 ** of the following form:
1622 **
1623 ** repo:%s
1624 **
1625 ** The value field is set to 1.
 
 
 
 
 
 
 
1626 */
1627 void db_record_repository_filename(const char *zName){
1628 Blob full;
1629 if( zName==0 ){
1630 if( !g.localOpen ) return;
@@ -1635,10 +1676,17 @@
1635 db_multi_exec(
1636 "INSERT OR IGNORE INTO global_config(name,value)"
1637 "VALUES('repo:%q',1)",
1638 blob_str(&full)
1639 );
 
 
 
 
 
 
 
1640 db_swap_connections();
1641 blob_reset(&full);
1642 }
1643
1644 /*
@@ -1792,18 +1840,21 @@
1792 { "repo-cksum", 0, 0, 0, "on" },
1793 { "self-register", 0, 0, 0, "off" },
1794 { "ssl-ca-location",0, 40, 0, "" },
1795 { "ssl-identity", 0, 40, 0, "" },
1796 { "ssh-command", 0, 32, 0, "" },
 
 
 
1797 { "web-browser", 0, 32, 0, "" },
1798 { "white-foreground", 0, 0, 0, "off" },
1799 { 0,0,0,0,0 }
1800 };
1801
1802 /*
1803 ** COMMAND: settings
1804 ** COMMAND: unset
1805 **
1806 ** %fossil settings ?PROPERTY? ?VALUE? ?-global?
1807 ** %fossil unset PROPERTY ?-global?
1808 **
1809 ** The "settings" command with no arguments lists all properties and their
@@ -1945,10 +1996,16 @@
1945 ** authenticate this client, in addition to the normal
1946 ** password authentication.
1947 **
1948 ** ssh-command Command used to talk to a remote machine with
1949 ** the "ssh://" protocol.
 
 
 
 
 
 
1950 **
1951 ** web-browser A shell command used to launch your preferred
1952 ** web browser when given a URL as an argument.
1953 ** Defaults to "start" on windows, "open" on Mac,
1954 ** and "firefox" on Unix.
1955
--- src/db.c
+++ src/db.c
@@ -48,40 +48,59 @@
48 Blob sql; /* The SQL for this statement */
49 sqlite3_stmt *pStmt; /* The results of sqlite3_prepare() */
50 Stmt *pNext, *pPrev; /* List of all unfinalized statements */
51 int nStep; /* Number of sqlite3_step() calls */
52 };
53
54 /*
55 ** Copy this to initialize a Stmt object to a clean/empty state. This
56 ** is useful to help avoid assertions when performing cleanup in some
57 ** error handling cases.
58 */
59 #define empty_Stmt_m {BLOB_INITIALIZER,NULL, NULL, NULL, 0}
60 #endif /* INTERFACE */
61 const struct Stmt empty_Stmt = empty_Stmt_m;
62
63 /*
64 ** Call this routine when a database error occurs.
65 */
66 static void db_err(const char *zFormat, ...){
67 va_list ap;
68 char *z;
69 int rc = 1;
70 static const char zRebuildMsg[] =
71 "If you have recently updated your fossil executable, you might\n"
72 "need to run \"fossil all rebuild\" to bring the repository\n"
73 "schemas up to date.\n";
74 va_start(ap, zFormat);
75 z = vmprintf(zFormat, ap);
76 va_end(ap);
77 #ifdef FOSSIL_ENABLE_JSON
78 if( g.json.isJsonMode ){
79 json_err( 0, z, 1 );
80 if( g.isHTTP ){
81 rc = 0 /* avoid HTTP 500 */;
82 }
83 }
84 else
85 #endif /* FOSSIL_ENABLE_JSON */
86 if( g.xferPanic ){
87 cgi_reset_content();
88 @ error Database\serror:\s%F(z)
89 cgi_reply();
90 }
91 else if( g.cgiOutput ){
92 g.cgiOutput = 0;
93 cgi_printf("<h1>Database Error</h1>\n"
94 "<pre>%h</pre><p>%s</p>", z, zRebuildMsg);
95 cgi_reply();
96 }else{
97 fprintf(stderr, "%s: %s\n\n%s", fossil_nameofexe(), z, zRebuildMsg);
98 }
99 free(z);
100 db_force_rollback();
101 fossil_exit(rc);
102 }
103
104 static int nBegin = 0; /* Nesting depth of BEGIN */
105 static int doRollback = 0; /* True to force a rollback */
106 static int nCommitHook = 0; /* Number of commit hooks */
@@ -561,11 +580,11 @@
580 ** Execute a query. Return the first column of the first row
581 ** of the result set as a string. Space to hold the string is
582 ** obtained from malloc(). If the result set is empty, return
583 ** zDefault instead.
584 */
585 char *db_text(char const *zDefault, const char *zSql, ...){
586 va_list ap;
587 Stmt s;
588 char *z;
589 va_start(ap, zSql);
590 db_vprepare(&s, 0, zSql, ap);
@@ -863,15 +882,24 @@
882 db_err("unable to find the name of a repository database");
883 }
884 }
885 if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){
886 if( file_access(zDbName, 0) ){
887 #ifdef FOSSIL_ENABLE_JSON
888 g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
889 #endif
890 fossil_panic("repository does not exist or"
891 " is in an unreadable directory: %s", zDbName);
892 }else if( file_access(zDbName, R_OK) ){
893 #ifdef FOSSIL_ENABLE_JSON
894 g.json.resultCode = FSL_JSON_E_DENIED;
895 #endif
896 fossil_panic("read permission denied for repository %s", zDbName);
897 }else{
898 #ifdef FOSSIL_ENABLE_JSON
899 g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
900 #endif
901 fossil_panic("not a valid repository: %s", zDbName);
902 }
903 }
904 db_open_or_attach(zDbName, "repository");
905 g.repositoryOpen = 1;
@@ -902,11 +930,11 @@
930 }
931 if( zRep==0 ){
932 if( db_open_local()==0 ){
933 goto rep_not_found;
934 }
935 zRep = db_lget("repository", 0)/*leak here*/;
936 if( zRep==0 ){
937 goto rep_not_found;
938 }
939 }
940 db_open_repository(zRep);
@@ -914,10 +942,13 @@
942 if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
943 return;
944 }
945 rep_not_found:
946 if( (bFlags & OPEN_OK_NOT_FOUND)==0 ){
947 #ifdef FOSSIL_ENABLE_JSON
948 g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
949 #endif
950 fossil_fatal("use --repository or -R to specify the repository database");
951 }
952 }
953
954 /*
@@ -944,10 +975,13 @@
975 ** Verify that the repository schema is correct. If it is not correct,
976 ** issue a fatal error and die.
977 */
978 void db_verify_schema(void){
979 if( db_schema_is_outofdate() ){
980 #ifdef FOSSIL_ENABLE_JSON
981 g.json.resultCode = FSL_JSON_E_DB_NEEDS_REBUILD;
982 #endif
983 fossil_warning("incorrect repository schema version");
984 fossil_warning("your repository has schema version \"%s\" "
985 "but this binary expects version \"%s\"",
986 db_get("aux-schema",0), AUX_SCHEMA);
987 fossil_fatal("run \"fossil rebuild\" to fix this problem");
@@ -1164,11 +1198,11 @@
1198 manifest_crosslink(rid, &manifest);
1199 }
1200 }
1201
1202 /*
1203 ** COMMAND: new*
1204 ** COMMAND: init
1205 **
1206 ** Usage: %fossil new ?OPTIONS? FILENAME
1207 ** Or: %fossil init ?OPTIONS? FILENAME
1208 **
@@ -1621,10 +1655,17 @@
1655 ** of the following form:
1656 **
1657 ** repo:%s
1658 **
1659 ** The value field is set to 1.
1660 **
1661 ** If running from a local checkout, also record the root of the checkout
1662 ** as follows:
1663 **
1664 ** ckout:%s
1665 **
1666 ** Where %s is the checkout root. The value is the repository file.
1667 */
1668 void db_record_repository_filename(const char *zName){
1669 Blob full;
1670 if( zName==0 ){
1671 if( !g.localOpen ) return;
@@ -1635,10 +1676,17 @@
1676 db_multi_exec(
1677 "INSERT OR IGNORE INTO global_config(name,value)"
1678 "VALUES('repo:%q',1)",
1679 blob_str(&full)
1680 );
1681 if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){
1682 db_multi_exec(
1683 "REPLACE INTO global_config(name, value)"
1684 "VALUES('ckout:%q','%q');",
1685 g.zLocalRoot, blob_str(&full)
1686 );
1687 }
1688 db_swap_connections();
1689 blob_reset(&full);
1690 }
1691
1692 /*
@@ -1792,18 +1840,21 @@
1840 { "repo-cksum", 0, 0, 0, "on" },
1841 { "self-register", 0, 0, 0, "off" },
1842 { "ssl-ca-location",0, 40, 0, "" },
1843 { "ssl-identity", 0, 40, 0, "" },
1844 { "ssh-command", 0, 32, 0, "" },
1845 #ifdef FOSSIL_ENABLE_TCL
1846 { "tcl", 0, 0, 0, "off" },
1847 #endif
1848 { "web-browser", 0, 32, 0, "" },
1849 { "white-foreground", 0, 0, 0, "off" },
1850 { 0,0,0,0,0 }
1851 };
1852
1853 /*
1854 ** COMMAND: settings
1855 ** COMMAND: unset*
1856 **
1857 ** %fossil settings ?PROPERTY? ?VALUE? ?-global?
1858 ** %fossil unset PROPERTY ?-global?
1859 **
1860 ** The "settings" command with no arguments lists all properties and their
@@ -1945,10 +1996,16 @@
1996 ** authenticate this client, in addition to the normal
1997 ** password authentication.
1998 **
1999 ** ssh-command Command used to talk to a remote machine with
2000 ** the "ssh://" protocol.
2001 **
2002 ** tcl If enabled, Tcl integration commands will be added to
2003 ** the TH1 interpreter, allowing Tcl expressions and
2004 ** scripts to be evaluated from TH1. Additionally, the
2005 ** Tcl interpreter will be able to evaluate TH1 expressions
2006 ** and scripts. Default: off.
2007 **
2008 ** web-browser A shell command used to launch your preferred
2009 ** web browser when given a URL as an argument.
2010 ** Defaults to "start" on windows, "open" on Mac,
2011 ** and "firefox" on Unix.
2012
--- src/descendants.c
+++ src/descendants.c
@@ -202,11 +202,12 @@
202202
void compute_direct_ancestors(int rid, int N){
203203
Stmt ins;
204204
Stmt q;
205205
int gen = 0;
206206
db_multi_exec(
207
- "CREATE TEMP TABLE ancestor(rid INTEGER, generation INTEGER PRIMARY KEY);"
207
+ "CREATE TEMP TABLE IF NOT EXISTS ancestor(rid INTEGER, generation INTEGER PRIMARY KEY);"
208
+ "DELETE FROM ancestor;"
208209
"INSERT INTO ancestor VALUES(%d, 0);", rid
209210
);
210211
db_prepare(&ins, "INSERT INTO ancestor VALUES(:rid, :gen)");
211212
db_prepare(&q,
212213
"SELECT pid FROM plink"
@@ -262,11 +263,11 @@
262263
db_finalize(&ins);
263264
db_finalize(&q);
264265
}
265266
266267
/*
267
-** COMMAND: descendants
268
+** COMMAND: descendants*
268269
**
269270
** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS?
270271
**
271272
** Find all leaf descendants of the baseline specified or if the argument
272273
** is omitted, of the baseline currently checked out.
@@ -297,11 +298,11 @@
297298
print_timeline(&q, 20, 0);
298299
db_finalize(&q);
299300
}
300301
301302
/*
302
-** COMMAND: leaves
303
+** COMMAND: leaves*
303304
**
304305
** Usage: %fossil leaves ?OPTIONS?
305306
**
306307
** Find leaves of all branches. By default show only open leaves.
307308
** The --all flag causes all leaves (closed and open) to be shown.
308309
--- src/descendants.c
+++ src/descendants.c
@@ -202,11 +202,12 @@
202 void compute_direct_ancestors(int rid, int N){
203 Stmt ins;
204 Stmt q;
205 int gen = 0;
206 db_multi_exec(
207 "CREATE TEMP TABLE ancestor(rid INTEGER, generation INTEGER PRIMARY KEY);"
 
208 "INSERT INTO ancestor VALUES(%d, 0);", rid
209 );
210 db_prepare(&ins, "INSERT INTO ancestor VALUES(:rid, :gen)");
211 db_prepare(&q,
212 "SELECT pid FROM plink"
@@ -262,11 +263,11 @@
262 db_finalize(&ins);
263 db_finalize(&q);
264 }
265
266 /*
267 ** COMMAND: descendants
268 **
269 ** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS?
270 **
271 ** Find all leaf descendants of the baseline specified or if the argument
272 ** is omitted, of the baseline currently checked out.
@@ -297,11 +298,11 @@
297 print_timeline(&q, 20, 0);
298 db_finalize(&q);
299 }
300
301 /*
302 ** COMMAND: leaves
303 **
304 ** Usage: %fossil leaves ?OPTIONS?
305 **
306 ** Find leaves of all branches. By default show only open leaves.
307 ** The --all flag causes all leaves (closed and open) to be shown.
308
--- src/descendants.c
+++ src/descendants.c
@@ -202,11 +202,12 @@
202 void compute_direct_ancestors(int rid, int N){
203 Stmt ins;
204 Stmt q;
205 int gen = 0;
206 db_multi_exec(
207 "CREATE TEMP TABLE IF NOT EXISTS ancestor(rid INTEGER, generation INTEGER PRIMARY KEY);"
208 "DELETE FROM ancestor;"
209 "INSERT INTO ancestor VALUES(%d, 0);", rid
210 );
211 db_prepare(&ins, "INSERT INTO ancestor VALUES(:rid, :gen)");
212 db_prepare(&q,
213 "SELECT pid FROM plink"
@@ -262,11 +263,11 @@
263 db_finalize(&ins);
264 db_finalize(&q);
265 }
266
267 /*
268 ** COMMAND: descendants*
269 **
270 ** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS?
271 **
272 ** Find all leaf descendants of the baseline specified or if the argument
273 ** is omitted, of the baseline currently checked out.
@@ -297,11 +298,11 @@
298 print_timeline(&q, 20, 0);
299 db_finalize(&q);
300 }
301
302 /*
303 ** COMMAND: leaves*
304 **
305 ** Usage: %fossil leaves ?OPTIONS?
306 **
307 ** Find leaves of all branches. By default show only open leaves.
308 ** The --all flag causes all leaves (closed and open) to be shown.
309
+459 -9
--- src/diff.c
+++ src/diff.c
@@ -21,10 +21,22 @@
2121
#include "config.h"
2222
#include "diff.h"
2323
#include <assert.h>
2424
2525
26
+#if INTERFACE
27
+/*
28
+** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions:
29
+*/
30
+#define DIFF_CONTEXT_MASK 0x0000fff /* Lines of context. Default if 0 */
31
+#define DIFF_WIDTH_MASK 0x00ff000 /* side-by-side column width */
32
+#define DIFF_IGNORE_EOLWS 0x0100000 /* Ignore end-of-line whitespace */
33
+#define DIFF_SIDEBYSIDE 0x0200000 /* Generate a side-by-side diff */
34
+#define DIFF_NEWFILE 0x0400000 /* Missing files are as empty files */
35
+
36
+#endif /* INTERFACE */
37
+
2638
/*
2739
** Maximum length of a line in a text file. (8192)
2840
*/
2941
#define LENGTH_MASK_SZ 13
3042
#define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
@@ -285,10 +297,196 @@
285297
for(j=0; j<m; j++){
286298
appendDiffLine(pOut, " ", &B[b+j]);
287299
}
288300
}
289301
}
302
+
303
+/*
304
+** Write a 6-digit line number into the buffer z[]. z[] is guaranteed to
305
+** have space for at least 7 characters.
306
+*/
307
+static void sbsWriteLineno(char *z, int ln){
308
+ sqlite3_snprintf(7, z, "%6d", ln+1);
309
+ z[6] = ' ';
310
+}
311
+
312
+/*
313
+** Write up to width characters of pLine into z[]. Translate tabs into
314
+** spaces. If trunc is true, then append \n\000 after the last character
315
+** written.
316
+*/
317
+static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){
318
+ int n = pLine->h & LENGTH_MASK;
319
+ int i, j;
320
+ const char *zIn = pLine->z;
321
+ for(i=j=0; i<n && j<width; i++){
322
+ char c = zIn[i];
323
+ if( c=='\t' ){
324
+ z[j++] = ' ';
325
+ while( (j&7)!=0 && j<width ) z[j++] = ' ';
326
+ }else if( c=='\r' || c=='\f' ){
327
+ z[j++] = ' ';
328
+ }else{
329
+ z[j++] = c;
330
+ }
331
+ }
332
+ if( trunc ){
333
+ z[j++] = '\n';
334
+ z[j] = 0;
335
+ }
336
+ return j;
337
+}
338
+
339
+
340
+/*
341
+** Given a diff context in which the aEdit[] array has been filled
342
+** in, compute a side-by-side diff into pOut.
343
+*/
344
+static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){
345
+ DLine *A; /* Left side of the diff */
346
+ DLine *B; /* Right side of the diff */
347
+ int a = 0; /* Index of next line in A[] */
348
+ int b = 0; /* Index of next line in B[] */
349
+ int *R; /* Array of COPY/DELETE/INSERT triples */
350
+ int r; /* Index into R[] */
351
+ int nr; /* Number of COPY/DELETE/INSERT triples to process */
352
+ int mxr; /* Maximum value for r */
353
+ int na, nb; /* Number of lines shown from A and B */
354
+ int i, j; /* Loop counters */
355
+ int m, ma, mb;/* Number of lines to output */
356
+ int skip; /* Number of lines to skip */
357
+ int mxLine; /* Length of a line of text */
358
+ char *zLine; /* A line of text being formatted */
359
+ int len; /* Length of an output line */
360
+
361
+ mxLine = width*2 + 2*7 + 3 + 1;
362
+ zLine = fossil_malloc( mxLine + 1 );
363
+ if( zLine==0 ) return;
364
+ zLine[mxLine] = 0;
365
+ A = p->aFrom;
366
+ B = p->aTo;
367
+ R = p->aEdit;
368
+ mxr = p->nEdit;
369
+ while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
370
+ for(r=0; r<mxr; r += 3*nr){
371
+ /* Figure out how many triples to show in a single block */
372
+ for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
373
+ /* printf("r=%d nr=%d\n", r, nr); */
374
+
375
+ /* For the current block comprising nr triples, figure out
376
+ ** how many lines of A and B are to be displayed
377
+ */
378
+ if( R[r]>nContext ){
379
+ na = nb = nContext;
380
+ skip = R[r] - nContext;
381
+ }else{
382
+ na = nb = R[r];
383
+ skip = 0;
384
+ }
385
+ for(i=0; i<nr; i++){
386
+ na += R[r+i*3+1];
387
+ nb += R[r+i*3+2];
388
+ }
389
+ if( R[r+nr*3]>nContext ){
390
+ na += nContext;
391
+ nb += nContext;
392
+ }else{
393
+ na += R[r+nr*3];
394
+ nb += R[r+nr*3];
395
+ }
396
+ for(i=1; i<nr; i++){
397
+ na += R[r+i*3];
398
+ nb += R[r+i*3];
399
+ }
400
+ /*
401
+ * If the patch changes an empty file or results in an empty file,
402
+ * the block header must use 0,0 as position indicator and not 1,0.
403
+ * Otherwise, patch would be confused and may reject the diff.
404
+ */
405
+ if( r>0 ) blob_appendf(pOut,"%.*c\n", width*2+16, '.');
406
+
407
+ /* Show the initial common area */
408
+ a += skip;
409
+ b += skip;
410
+ m = R[r] - skip;
411
+ for(j=0; j<m; j++){
412
+ memset(zLine, ' ', mxLine);
413
+ sbsWriteLineno(zLine, a+j);
414
+ sbsWriteText(&zLine[7], &A[a+j], width, 0);
415
+ sbsWriteLineno(&zLine[width+10], b+j);
416
+ len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
417
+ blob_append(pOut, zLine, len+width+17);
418
+ }
419
+ a += m;
420
+ b += m;
421
+
422
+ /* Show the differences */
423
+ for(i=0; i<nr; i++){
424
+ ma = R[r+i*3+1];
425
+ mb = R[r+i*3+2];
426
+ m = ma<mb ? ma : mb;
427
+ for(j=0; j<m; j++){
428
+ memset(zLine, ' ', mxLine);
429
+ sbsWriteLineno(zLine, a+j);
430
+ sbsWriteText(&zLine[7], &A[a+j], width, 0);
431
+ zLine[width+8] = '|';
432
+ sbsWriteLineno(&zLine[width+10], b+j);
433
+ len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
434
+ blob_append(pOut, zLine, len+width+17);
435
+ }
436
+ a += m;
437
+ b += m;
438
+ ma -= m;
439
+ mb -= m;
440
+ for(j=0; j<ma; j++){
441
+ memset(zLine, ' ', width+7);
442
+ sbsWriteLineno(zLine, a+j);
443
+ sbsWriteText(&zLine[7], &A[a+j], width, 0);
444
+ zLine[width+8] = '<';
445
+ zLine[width+9] = '\n';
446
+ zLine[width+10] = 0;
447
+ blob_append(pOut, zLine, width+10);
448
+ }
449
+ a += ma;
450
+ for(j=0; j<mb; j++){
451
+ memset(zLine, ' ', mxLine);
452
+ zLine[width+8] = '>';
453
+ sbsWriteLineno(&zLine[width+10], b+j);
454
+ len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
455
+ blob_append(pOut, zLine, len+width+17);
456
+ }
457
+ b += mb;
458
+ if( i<nr-1 ){
459
+ m = R[r+i*3+3];
460
+ for(j=0; j<m; j++){
461
+ memset(zLine, ' ', mxLine);
462
+ sbsWriteLineno(zLine, a+j);
463
+ sbsWriteText(&zLine[7], &A[a+j], width, 0);
464
+ sbsWriteLineno(&zLine[width+10], b+j);
465
+ len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
466
+ blob_append(pOut, zLine, len+width+17);
467
+ }
468
+ b += m;
469
+ a += m;
470
+ }
471
+ }
472
+
473
+ /* Show the final common area */
474
+ assert( nr==i );
475
+ m = R[r+nr*3];
476
+ if( m>nContext ) m = nContext;
477
+ for(j=0; j<m; j++){
478
+ memset(zLine, ' ', mxLine);
479
+ sbsWriteLineno(zLine, a+j);
480
+ sbsWriteText(&zLine[7], &A[a+j], width, 0);
481
+ sbsWriteLineno(&zLine[width+10], b+j);
482
+ len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
483
+ blob_append(pOut, zLine, len+width+17);
484
+ }
485
+ }
486
+ free(zLine);
487
+}
290488
291489
/*
292490
** Compute the optimal longest common subsequence (LCS) using an
293491
** exhaustive search. This version of the LCS is only used for
294492
** shorter input strings since runtime is O(N*N) where N is the
@@ -520,10 +718,30 @@
520718
p->aEdit[p->nEdit++] = 0;
521719
p->aEdit[p->nEdit++] = 0;
522720
p->aEdit[p->nEdit++] = 0;
523721
}
524722
}
723
+
724
+/*
725
+** Extract the number of lines of context from diffFlags. Supply an
726
+** appropriate default if no context width is specified.
727
+*/
728
+int diff_context_lines(int diffFlags){
729
+ int n = diffFlags & DIFF_CONTEXT_MASK;
730
+ if( n==0 ) n = 5;
731
+ return n;
732
+}
733
+
734
+/*
735
+** Extract the width of columns for side-by-side diff. Supply an
736
+** appropriate default if no width is given.
737
+*/
738
+int diff_width(int diffFlags){
739
+ int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
740
+ if( w==0 ) w = 80;
741
+ return w;
742
+}
525743
526744
/*
527745
** Generate a report of the differences between files pA and pB.
528746
** If pOut is not NULL then a unified diff is appended there. It
529747
** is assumed that pOut has already been initialized. If pOut is
@@ -538,16 +756,20 @@
538756
** text "cannot compute difference between binary files".
539757
*/
540758
int *text_diff(
541759
Blob *pA_Blob, /* FROM file */
542760
Blob *pB_Blob, /* TO file */
543
- Blob *pOut, /* Write unified diff here if not NULL */
544
- int nContext, /* Amount of context to unified diff */
545
- int ignoreEolWs /* Ignore whitespace at the end of lines */
761
+ Blob *pOut, /* Write diff here if not NULL */
762
+ int diffFlags /* DIFF_* flags defined above */
546763
){
764
+ int ignoreEolWs; /* Ignore whitespace at the end of lines */
765
+ int nContext; /* Amount of context to display */
547766
DContext c;
548
-
767
+
768
+ nContext = diff_context_lines(diffFlags);
769
+ ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
770
+
549771
/* Prepare the input files */
550772
memset(&c, 0, sizeof(c));
551773
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
552774
&c.nFrom, ignoreEolWs);
553775
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
@@ -563,12 +785,17 @@
563785
564786
/* Compute the difference */
565787
diff_all(&c);
566788
567789
if( pOut ){
568
- /* Compute a context diff if requested */
569
- contextDiff(&c, pOut, nContext);
790
+ /* Compute a context or side-by-side diff into pOut */
791
+ if( diffFlags & DIFF_SIDEBYSIDE ){
792
+ int width = diff_width(diffFlags);
793
+ sbsDiff(&c, pOut, nContext, width);
794
+ }else{
795
+ contextDiff(&c, pOut, nContext);
796
+ }
570797
free(c.aFrom);
571798
free(c.aTo);
572799
free(c.aEdit);
573800
return 0;
574801
}else{
@@ -578,10 +805,194 @@
578805
free(c.aFrom);
579806
free(c.aTo);
580807
return c.aEdit;
581808
}
582809
}
810
+
811
+/*
812
+** Copy a line with a limit. Used for side-by-side diffs to enforce a maximum
813
+** line length limit.
814
+*/
815
+static char *copylimline(char *out, DLine *dl, int lim){
816
+ int len;
817
+ len = dl->h & LENGTH_MASK;
818
+ if( lim && len > lim ){
819
+ memcpy(out, dl->z, lim-3);
820
+ memcpy(&out[lim-3], "...", 4);
821
+ }else{
822
+ memcpy(out, dl->z, len);
823
+ out[len] = '\0';
824
+ }
825
+ return out;
826
+}
827
+
828
+/*
829
+** Output table body of a side-by-side diff. Prior to the call, the caller
830
+** should have output:
831
+** <table class="sbsdiff">
832
+** <tr><th colspan="2" class="diffhdr">Old title</th><th/>
833
+** <th colspan="2" class="diffhdr">New title</th></tr>
834
+**
835
+** And after the call, it should output:
836
+** </table>
837
+**
838
+** Some good reference diffs in the fossil repository for testing:
839
+** /vdiff?from=080d27a&to=4b0f813&detail=1
840
+** /vdiff?from=636804745b&to=c1d78e0556&detail=1
841
+** /vdiff?from=c0b6c28d29&to=25169506b7&detail=1
842
+** /vdiff?from=e3d022dffa&to=48bcfbd47b&detail=1
843
+*/
844
+int html_sbsdiff(
845
+ Blob *pA_Blob, /* FROM file */
846
+ Blob *pB_Blob, /* TO file */
847
+ int nContext, /* Amount of context to unified diff */
848
+ int ignoreEolWs /* Ignore whitespace at the end of lines */
849
+){
850
+ DContext c;
851
+ int i;
852
+ int iFrom, iTo;
853
+ char *linebuf;
854
+ int collim=0; /* Currently not settable; allows a column limit for diffs */
855
+ int allowExp=0; /* Currently not settable; (dis)allow expansion of rows */
856
+
857
+ /* Prepare the input files */
858
+ memset(&c, 0, sizeof(c));
859
+ c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
860
+ &c.nFrom, ignoreEolWs);
861
+ c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
862
+ &c.nTo, ignoreEolWs);
863
+ if( c.aFrom==0 || c.aTo==0 ){
864
+ free(c.aFrom);
865
+ free(c.aTo);
866
+ /* Note: This would be generated within a table. */
867
+ @ <p class="generalError" style="white-space: nowrap">cannot compute
868
+ @ difference between binary files</p>
869
+ return 0;
870
+ }
871
+
872
+ collim = collim < 4 ? 0 : collim;
873
+
874
+ /* Compute the difference */
875
+ diff_all(&c);
876
+
877
+ linebuf = fossil_malloc(LENGTH_MASK+1);
878
+ if( !linebuf ){
879
+ free(c.aFrom);
880
+ free(c.aTo);
881
+ free(c.aEdit);
882
+ return 0;
883
+ }
884
+
885
+ iFrom=iTo=0;
886
+ i=0;
887
+ while( i<c.nEdit ){
888
+ int j;
889
+ /* Copied lines */
890
+ for( j=0; j<c.aEdit[i]; j++){
891
+ /* Hide lines which are copied and are further away from block boundaries
892
+ ** than nContext lines. For each block with hidden lines, show a row
893
+ ** notifying the user about the hidden rows.
894
+ */
895
+ if( j<nContext || j>c.aEdit[i]-nContext-1 ){
896
+ @ <tr>
897
+ }else if( j==nContext && j<c.aEdit[i]-nContext-1 ){
898
+ @ <tr>
899
+ @ <td class="meta" colspan="5" style="white-space: nowrap;">
900
+ @ %d(c.aEdit[i]-2*nContext) hidden lines</td>
901
+ @ </tr>
902
+ if( !allowExp )
903
+ continue;
904
+ @ <tr style="display:none;">
905
+ }else{
906
+ if( !allowExp )
907
+ continue;
908
+ @ <tr style="display:none;">
909
+ }
910
+
911
+ copylimline(linebuf, &c.aFrom[iFrom+j], collim);
912
+ @ <td class="lineno">%d(iFrom+j+1)</td>
913
+ @ <td class="srcline">%h(linebuf)</td>
914
+
915
+ @ <td> </td>
916
+
917
+ copylimline(linebuf, &c.aTo[iTo+j], collim);
918
+ @ <td class="lineno">%d(iTo+j+1)</td>
919
+ @ <td class="srcline">%h(linebuf)</td>
920
+
921
+ @ </tr>
922
+ }
923
+ iFrom+=c.aEdit[i];
924
+ iTo+=c.aEdit[i];
925
+
926
+ if( c.aEdit[i+1]!=0 && c.aEdit[i+2]!=0 ){
927
+ int lim;
928
+ lim = c.aEdit[i+1] > c.aEdit[i+2] ? c.aEdit[i+1] : c.aEdit[i+2];
929
+
930
+ /* Assume changed lines */
931
+ for( j=0; j<lim; j++ ){
932
+ @ <tr>
933
+
934
+ if( j<c.aEdit[i+1] ){
935
+ copylimline(linebuf, &c.aFrom[iFrom+j], collim);
936
+ @ <td class="changed lineno">%d(iFrom+j+1)</td>
937
+ @ <td class="changed srcline">%h(linebuf)</td>
938
+ }else{
939
+ @ <td colspan="2" class="changedvoid"/>
940
+ }
941
+
942
+ @ <td class="changed">|</td>
943
+
944
+ if( j<c.aEdit[i+2] ){
945
+ copylimline(linebuf, &c.aTo[iTo+j], collim);
946
+ @ <td class="changed lineno">%d(iTo+j+1)</td>
947
+ @ <td class="changed srcline">%h(linebuf)</td>
948
+ }else{
949
+ @ <td colspan="2" class="changedvoid"/>
950
+ }
951
+
952
+ @ </tr>
953
+ }
954
+ iFrom+=c.aEdit[i+1];
955
+ iTo+=c.aEdit[i+2];
956
+ }else{
957
+
958
+ /* Process deleted lines */
959
+ for( j=0; j<c.aEdit[i+1]; j++ ){
960
+ @ <tr>
961
+
962
+ copylimline(linebuf, &c.aFrom[iFrom+j], collim);
963
+ @ <td class="removed lineno">%d(iFrom+j+1)</td>
964
+ @ <td class="removed srcline">%h(linebuf)</td>
965
+ @ <td>&lt;</td>
966
+ @ <td colspan="2" class="removedvoid"/>
967
+ @ </tr>
968
+ }
969
+ iFrom+=c.aEdit[i+1];
970
+
971
+ /* Process inserted lines */
972
+ for( j=0; j<c.aEdit[i+2]; j++ ){
973
+ @ <tr>
974
+ @ <td colspan="2" class="addedvoid"/>
975
+ @ <td>&gt;</td>
976
+ copylimline(linebuf, &c.aTo[iTo+j], collim);
977
+ @ <td class="added lineno">%d(iTo+j+1)</td>
978
+ @ <td class="added srcline">%h(linebuf)</td>
979
+ @ </tr>
980
+ }
981
+ iTo+=c.aEdit[i+2];
982
+ }
983
+
984
+ i+=3;
985
+ }
986
+
987
+ free(linebuf);
988
+ free(c.aFrom);
989
+ free(c.aTo);
990
+ free(c.aEdit);
991
+ return 1;
992
+}
993
+
583994
584995
/*
585996
** COMMAND: test-rawdiff
586997
*/
587998
void test_rawdiff_cmd(void){
@@ -592,29 +1003,58 @@
5921003
if( g.argc<4 ) usage("FILE1 FILE2 ...");
5931004
blob_read_from_file(&a, g.argv[2]);
5941005
for(i=3; i<g.argc; i++){
5951006
if( i>3 ) fossil_print("-------------------------------\n");
5961007
blob_read_from_file(&b, g.argv[i]);
597
- R = text_diff(&a, &b, 0, 0, 0);
1008
+ R = text_diff(&a, &b, 0, 0);
5981009
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
5991010
fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
6001011
}
6011012
/* free(R); */
6021013
blob_reset(&b);
6031014
}
6041015
}
1016
+
1017
+/*
1018
+** Process diff-related command-line options and return an appropriate
1019
+** "diffFlags" integer.
1020
+**
1021
+** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
1022
+** --context|-c N N lines of context. DIFF_CONTEXT_MASK
1023
+** --width|-W N N character lines. DIFF_WIDTH_MASK
1024
+*/
1025
+int diff_options(void){
1026
+ int diffFlags = 0;
1027
+ const char *z;
1028
+ int f;
1029
+ if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
1030
+ if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>0 ){
1031
+ if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
1032
+ diffFlags |= f;
1033
+ }
1034
+ if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
1035
+ f *= DIFF_CONTEXT_MASK+1;
1036
+ if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1037
+ diffFlags |= f;
1038
+ }
1039
+ return diffFlags;
1040
+}
6051041
6061042
/*
6071043
** COMMAND: test-udiff
1044
+**
1045
+** Print the difference between two files. The usual diff options apply.
6081046
*/
6091047
void test_udiff_cmd(void){
6101048
Blob a, b, out;
1049
+ int diffFlag = diff_options();
1050
+
6111051
if( g.argc!=4 ) usage("FILE1 FILE2");
6121052
blob_read_from_file(&a, g.argv[2]);
6131053
blob_read_from_file(&b, g.argv[3]);
6141054
blob_zero(&out);
615
- text_diff(&a, &b, &out, 3, 0);
1055
+ text_diff(&a, &b, &out, diffFlag);
6161056
blob_write_to_file(&out, "-");
6171057
}
6181058
6191059
/**************************************************************************
6201060
** The basic difference engine is above. What follows is the annotation
@@ -884,10 +1324,11 @@
8841324
*/
8851325
void annotate_cmd(void){
8861326
int fnid; /* Filename ID */
8871327
int fid; /* File instance ID */
8881328
int mid; /* Manifest where file was checked in */
1329
+ int cid; /* Checkout ID */
8891330
Blob treename; /* FILENAME translated to canonical form */
8901331
char *zFilename; /* Cannonical filename */
8911332
Annotator ann; /* The annotation of the file */
8921333
int i; /* Loop counter */
8931334
const char *zLimit; /* The value to the --limit option */
@@ -913,11 +1354,20 @@
9131354
}
9141355
fid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFilename);
9151356
if( fid==0 ){
9161357
fossil_fatal("not part of current checkout: %s", zFilename);
9171358
}
918
- mid = db_int(0, "SELECT mid FROM mlink WHERE fid=%d AND fnid=%d", fid, fnid);
1359
+ cid = db_lget_int("checkout", 0);
1360
+ if (cid == 0){
1361
+ fossil_fatal("Not in a checkout");
1362
+ }
1363
+ if( iLimit<=0 ) iLimit = 1000000000;
1364
+ compute_direct_ancestors(cid, iLimit);
1365
+ mid = db_int(0, "SELECT mlink.mid FROM mlink, ancestor "
1366
+ " WHERE mlink.fid=%d AND mlink.fnid=%d AND mlink.mid=ancestor.rid"
1367
+ " ORDER BY ancestor.generation ASC LIMIT 1",
1368
+ fid, fnid);
9191369
if( mid==0 ){
9201370
fossil_panic("unable to find manifest");
9211371
}
9221372
if( fileVers ) annFlags |= ANN_FILE_VERS;
9231373
annotate_file(&ann, fnid, mid, 0, iLimit, annFlags);
9241374
--- src/diff.c
+++ src/diff.c
@@ -21,10 +21,22 @@
21 #include "config.h"
22 #include "diff.h"
23 #include <assert.h>
24
25
 
 
 
 
 
 
 
 
 
 
 
 
26 /*
27 ** Maximum length of a line in a text file. (8192)
28 */
29 #define LENGTH_MASK_SZ 13
30 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
@@ -285,10 +297,196 @@
285 for(j=0; j<m; j++){
286 appendDiffLine(pOut, " ", &B[b+j]);
287 }
288 }
289 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
291 /*
292 ** Compute the optimal longest common subsequence (LCS) using an
293 ** exhaustive search. This version of the LCS is only used for
294 ** shorter input strings since runtime is O(N*N) where N is the
@@ -520,10 +718,30 @@
520 p->aEdit[p->nEdit++] = 0;
521 p->aEdit[p->nEdit++] = 0;
522 p->aEdit[p->nEdit++] = 0;
523 }
524 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
526 /*
527 ** Generate a report of the differences between files pA and pB.
528 ** If pOut is not NULL then a unified diff is appended there. It
529 ** is assumed that pOut has already been initialized. If pOut is
@@ -538,16 +756,20 @@
538 ** text "cannot compute difference between binary files".
539 */
540 int *text_diff(
541 Blob *pA_Blob, /* FROM file */
542 Blob *pB_Blob, /* TO file */
543 Blob *pOut, /* Write unified diff here if not NULL */
544 int nContext, /* Amount of context to unified diff */
545 int ignoreEolWs /* Ignore whitespace at the end of lines */
546 ){
 
 
547 DContext c;
548
 
 
 
549 /* Prepare the input files */
550 memset(&c, 0, sizeof(c));
551 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
552 &c.nFrom, ignoreEolWs);
553 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
@@ -563,12 +785,17 @@
563
564 /* Compute the difference */
565 diff_all(&c);
566
567 if( pOut ){
568 /* Compute a context diff if requested */
569 contextDiff(&c, pOut, nContext);
 
 
 
 
 
570 free(c.aFrom);
571 free(c.aTo);
572 free(c.aEdit);
573 return 0;
574 }else{
@@ -578,10 +805,194 @@
578 free(c.aFrom);
579 free(c.aTo);
580 return c.aEdit;
581 }
582 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
583
584 /*
585 ** COMMAND: test-rawdiff
586 */
587 void test_rawdiff_cmd(void){
@@ -592,29 +1003,58 @@
592 if( g.argc<4 ) usage("FILE1 FILE2 ...");
593 blob_read_from_file(&a, g.argv[2]);
594 for(i=3; i<g.argc; i++){
595 if( i>3 ) fossil_print("-------------------------------\n");
596 blob_read_from_file(&b, g.argv[i]);
597 R = text_diff(&a, &b, 0, 0, 0);
598 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
599 fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
600 }
601 /* free(R); */
602 blob_reset(&b);
603 }
604 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
606 /*
607 ** COMMAND: test-udiff
 
 
608 */
609 void test_udiff_cmd(void){
610 Blob a, b, out;
 
 
611 if( g.argc!=4 ) usage("FILE1 FILE2");
612 blob_read_from_file(&a, g.argv[2]);
613 blob_read_from_file(&b, g.argv[3]);
614 blob_zero(&out);
615 text_diff(&a, &b, &out, 3, 0);
616 blob_write_to_file(&out, "-");
617 }
618
619 /**************************************************************************
620 ** The basic difference engine is above. What follows is the annotation
@@ -884,10 +1324,11 @@
884 */
885 void annotate_cmd(void){
886 int fnid; /* Filename ID */
887 int fid; /* File instance ID */
888 int mid; /* Manifest where file was checked in */
 
889 Blob treename; /* FILENAME translated to canonical form */
890 char *zFilename; /* Cannonical filename */
891 Annotator ann; /* The annotation of the file */
892 int i; /* Loop counter */
893 const char *zLimit; /* The value to the --limit option */
@@ -913,11 +1354,20 @@
913 }
914 fid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFilename);
915 if( fid==0 ){
916 fossil_fatal("not part of current checkout: %s", zFilename);
917 }
918 mid = db_int(0, "SELECT mid FROM mlink WHERE fid=%d AND fnid=%d", fid, fnid);
 
 
 
 
 
 
 
 
 
919 if( mid==0 ){
920 fossil_panic("unable to find manifest");
921 }
922 if( fileVers ) annFlags |= ANN_FILE_VERS;
923 annotate_file(&ann, fnid, mid, 0, iLimit, annFlags);
924
--- src/diff.c
+++ src/diff.c
@@ -21,10 +21,22 @@
21 #include "config.h"
22 #include "diff.h"
23 #include <assert.h>
24
25
26 #if INTERFACE
27 /*
28 ** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions:
29 */
30 #define DIFF_CONTEXT_MASK 0x0000fff /* Lines of context. Default if 0 */
31 #define DIFF_WIDTH_MASK 0x00ff000 /* side-by-side column width */
32 #define DIFF_IGNORE_EOLWS 0x0100000 /* Ignore end-of-line whitespace */
33 #define DIFF_SIDEBYSIDE 0x0200000 /* Generate a side-by-side diff */
34 #define DIFF_NEWFILE 0x0400000 /* Missing files are as empty files */
35
36 #endif /* INTERFACE */
37
38 /*
39 ** Maximum length of a line in a text file. (8192)
40 */
41 #define LENGTH_MASK_SZ 13
42 #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1)
@@ -285,10 +297,196 @@
297 for(j=0; j<m; j++){
298 appendDiffLine(pOut, " ", &B[b+j]);
299 }
300 }
301 }
302
303 /*
304 ** Write a 6-digit line number into the buffer z[]. z[] is guaranteed to
305 ** have space for at least 7 characters.
306 */
307 static void sbsWriteLineno(char *z, int ln){
308 sqlite3_snprintf(7, z, "%6d", ln+1);
309 z[6] = ' ';
310 }
311
312 /*
313 ** Write up to width characters of pLine into z[]. Translate tabs into
314 ** spaces. If trunc is true, then append \n\000 after the last character
315 ** written.
316 */
317 static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){
318 int n = pLine->h & LENGTH_MASK;
319 int i, j;
320 const char *zIn = pLine->z;
321 for(i=j=0; i<n && j<width; i++){
322 char c = zIn[i];
323 if( c=='\t' ){
324 z[j++] = ' ';
325 while( (j&7)!=0 && j<width ) z[j++] = ' ';
326 }else if( c=='\r' || c=='\f' ){
327 z[j++] = ' ';
328 }else{
329 z[j++] = c;
330 }
331 }
332 if( trunc ){
333 z[j++] = '\n';
334 z[j] = 0;
335 }
336 return j;
337 }
338
339
340 /*
341 ** Given a diff context in which the aEdit[] array has been filled
342 ** in, compute a side-by-side diff into pOut.
343 */
344 static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){
345 DLine *A; /* Left side of the diff */
346 DLine *B; /* Right side of the diff */
347 int a = 0; /* Index of next line in A[] */
348 int b = 0; /* Index of next line in B[] */
349 int *R; /* Array of COPY/DELETE/INSERT triples */
350 int r; /* Index into R[] */
351 int nr; /* Number of COPY/DELETE/INSERT triples to process */
352 int mxr; /* Maximum value for r */
353 int na, nb; /* Number of lines shown from A and B */
354 int i, j; /* Loop counters */
355 int m, ma, mb;/* Number of lines to output */
356 int skip; /* Number of lines to skip */
357 int mxLine; /* Length of a line of text */
358 char *zLine; /* A line of text being formatted */
359 int len; /* Length of an output line */
360
361 mxLine = width*2 + 2*7 + 3 + 1;
362 zLine = fossil_malloc( mxLine + 1 );
363 if( zLine==0 ) return;
364 zLine[mxLine] = 0;
365 A = p->aFrom;
366 B = p->aTo;
367 R = p->aEdit;
368 mxr = p->nEdit;
369 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
370 for(r=0; r<mxr; r += 3*nr){
371 /* Figure out how many triples to show in a single block */
372 for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
373 /* printf("r=%d nr=%d\n", r, nr); */
374
375 /* For the current block comprising nr triples, figure out
376 ** how many lines of A and B are to be displayed
377 */
378 if( R[r]>nContext ){
379 na = nb = nContext;
380 skip = R[r] - nContext;
381 }else{
382 na = nb = R[r];
383 skip = 0;
384 }
385 for(i=0; i<nr; i++){
386 na += R[r+i*3+1];
387 nb += R[r+i*3+2];
388 }
389 if( R[r+nr*3]>nContext ){
390 na += nContext;
391 nb += nContext;
392 }else{
393 na += R[r+nr*3];
394 nb += R[r+nr*3];
395 }
396 for(i=1; i<nr; i++){
397 na += R[r+i*3];
398 nb += R[r+i*3];
399 }
400 /*
401 * If the patch changes an empty file or results in an empty file,
402 * the block header must use 0,0 as position indicator and not 1,0.
403 * Otherwise, patch would be confused and may reject the diff.
404 */
405 if( r>0 ) blob_appendf(pOut,"%.*c\n", width*2+16, '.');
406
407 /* Show the initial common area */
408 a += skip;
409 b += skip;
410 m = R[r] - skip;
411 for(j=0; j<m; j++){
412 memset(zLine, ' ', mxLine);
413 sbsWriteLineno(zLine, a+j);
414 sbsWriteText(&zLine[7], &A[a+j], width, 0);
415 sbsWriteLineno(&zLine[width+10], b+j);
416 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
417 blob_append(pOut, zLine, len+width+17);
418 }
419 a += m;
420 b += m;
421
422 /* Show the differences */
423 for(i=0; i<nr; i++){
424 ma = R[r+i*3+1];
425 mb = R[r+i*3+2];
426 m = ma<mb ? ma : mb;
427 for(j=0; j<m; j++){
428 memset(zLine, ' ', mxLine);
429 sbsWriteLineno(zLine, a+j);
430 sbsWriteText(&zLine[7], &A[a+j], width, 0);
431 zLine[width+8] = '|';
432 sbsWriteLineno(&zLine[width+10], b+j);
433 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
434 blob_append(pOut, zLine, len+width+17);
435 }
436 a += m;
437 b += m;
438 ma -= m;
439 mb -= m;
440 for(j=0; j<ma; j++){
441 memset(zLine, ' ', width+7);
442 sbsWriteLineno(zLine, a+j);
443 sbsWriteText(&zLine[7], &A[a+j], width, 0);
444 zLine[width+8] = '<';
445 zLine[width+9] = '\n';
446 zLine[width+10] = 0;
447 blob_append(pOut, zLine, width+10);
448 }
449 a += ma;
450 for(j=0; j<mb; j++){
451 memset(zLine, ' ', mxLine);
452 zLine[width+8] = '>';
453 sbsWriteLineno(&zLine[width+10], b+j);
454 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
455 blob_append(pOut, zLine, len+width+17);
456 }
457 b += mb;
458 if( i<nr-1 ){
459 m = R[r+i*3+3];
460 for(j=0; j<m; j++){
461 memset(zLine, ' ', mxLine);
462 sbsWriteLineno(zLine, a+j);
463 sbsWriteText(&zLine[7], &A[a+j], width, 0);
464 sbsWriteLineno(&zLine[width+10], b+j);
465 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
466 blob_append(pOut, zLine, len+width+17);
467 }
468 b += m;
469 a += m;
470 }
471 }
472
473 /* Show the final common area */
474 assert( nr==i );
475 m = R[r+nr*3];
476 if( m>nContext ) m = nContext;
477 for(j=0; j<m; j++){
478 memset(zLine, ' ', mxLine);
479 sbsWriteLineno(zLine, a+j);
480 sbsWriteText(&zLine[7], &A[a+j], width, 0);
481 sbsWriteLineno(&zLine[width+10], b+j);
482 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1);
483 blob_append(pOut, zLine, len+width+17);
484 }
485 }
486 free(zLine);
487 }
488
489 /*
490 ** Compute the optimal longest common subsequence (LCS) using an
491 ** exhaustive search. This version of the LCS is only used for
492 ** shorter input strings since runtime is O(N*N) where N is the
@@ -520,10 +718,30 @@
718 p->aEdit[p->nEdit++] = 0;
719 p->aEdit[p->nEdit++] = 0;
720 p->aEdit[p->nEdit++] = 0;
721 }
722 }
723
724 /*
725 ** Extract the number of lines of context from diffFlags. Supply an
726 ** appropriate default if no context width is specified.
727 */
728 int diff_context_lines(int diffFlags){
729 int n = diffFlags & DIFF_CONTEXT_MASK;
730 if( n==0 ) n = 5;
731 return n;
732 }
733
734 /*
735 ** Extract the width of columns for side-by-side diff. Supply an
736 ** appropriate default if no width is given.
737 */
738 int diff_width(int diffFlags){
739 int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
740 if( w==0 ) w = 80;
741 return w;
742 }
743
744 /*
745 ** Generate a report of the differences between files pA and pB.
746 ** If pOut is not NULL then a unified diff is appended there. It
747 ** is assumed that pOut has already been initialized. If pOut is
@@ -538,16 +756,20 @@
756 ** text "cannot compute difference between binary files".
757 */
758 int *text_diff(
759 Blob *pA_Blob, /* FROM file */
760 Blob *pB_Blob, /* TO file */
761 Blob *pOut, /* Write diff here if not NULL */
762 int diffFlags /* DIFF_* flags defined above */
 
763 ){
764 int ignoreEolWs; /* Ignore whitespace at the end of lines */
765 int nContext; /* Amount of context to display */
766 DContext c;
767
768 nContext = diff_context_lines(diffFlags);
769 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0;
770
771 /* Prepare the input files */
772 memset(&c, 0, sizeof(c));
773 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
774 &c.nFrom, ignoreEolWs);
775 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
@@ -563,12 +785,17 @@
785
786 /* Compute the difference */
787 diff_all(&c);
788
789 if( pOut ){
790 /* Compute a context or side-by-side diff into pOut */
791 if( diffFlags & DIFF_SIDEBYSIDE ){
792 int width = diff_width(diffFlags);
793 sbsDiff(&c, pOut, nContext, width);
794 }else{
795 contextDiff(&c, pOut, nContext);
796 }
797 free(c.aFrom);
798 free(c.aTo);
799 free(c.aEdit);
800 return 0;
801 }else{
@@ -578,10 +805,194 @@
805 free(c.aFrom);
806 free(c.aTo);
807 return c.aEdit;
808 }
809 }
810
811 /*
812 ** Copy a line with a limit. Used for side-by-side diffs to enforce a maximum
813 ** line length limit.
814 */
815 static char *copylimline(char *out, DLine *dl, int lim){
816 int len;
817 len = dl->h & LENGTH_MASK;
818 if( lim && len > lim ){
819 memcpy(out, dl->z, lim-3);
820 memcpy(&out[lim-3], "...", 4);
821 }else{
822 memcpy(out, dl->z, len);
823 out[len] = '\0';
824 }
825 return out;
826 }
827
828 /*
829 ** Output table body of a side-by-side diff. Prior to the call, the caller
830 ** should have output:
831 ** <table class="sbsdiff">
832 ** <tr><th colspan="2" class="diffhdr">Old title</th><th/>
833 ** <th colspan="2" class="diffhdr">New title</th></tr>
834 **
835 ** And after the call, it should output:
836 ** </table>
837 **
838 ** Some good reference diffs in the fossil repository for testing:
839 ** /vdiff?from=080d27a&to=4b0f813&detail=1
840 ** /vdiff?from=636804745b&to=c1d78e0556&detail=1
841 ** /vdiff?from=c0b6c28d29&to=25169506b7&detail=1
842 ** /vdiff?from=e3d022dffa&to=48bcfbd47b&detail=1
843 */
844 int html_sbsdiff(
845 Blob *pA_Blob, /* FROM file */
846 Blob *pB_Blob, /* TO file */
847 int nContext, /* Amount of context to unified diff */
848 int ignoreEolWs /* Ignore whitespace at the end of lines */
849 ){
850 DContext c;
851 int i;
852 int iFrom, iTo;
853 char *linebuf;
854 int collim=0; /* Currently not settable; allows a column limit for diffs */
855 int allowExp=0; /* Currently not settable; (dis)allow expansion of rows */
856
857 /* Prepare the input files */
858 memset(&c, 0, sizeof(c));
859 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
860 &c.nFrom, ignoreEolWs);
861 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
862 &c.nTo, ignoreEolWs);
863 if( c.aFrom==0 || c.aTo==0 ){
864 free(c.aFrom);
865 free(c.aTo);
866 /* Note: This would be generated within a table. */
867 @ <p class="generalError" style="white-space: nowrap">cannot compute
868 @ difference between binary files</p>
869 return 0;
870 }
871
872 collim = collim < 4 ? 0 : collim;
873
874 /* Compute the difference */
875 diff_all(&c);
876
877 linebuf = fossil_malloc(LENGTH_MASK+1);
878 if( !linebuf ){
879 free(c.aFrom);
880 free(c.aTo);
881 free(c.aEdit);
882 return 0;
883 }
884
885 iFrom=iTo=0;
886 i=0;
887 while( i<c.nEdit ){
888 int j;
889 /* Copied lines */
890 for( j=0; j<c.aEdit[i]; j++){
891 /* Hide lines which are copied and are further away from block boundaries
892 ** than nContext lines. For each block with hidden lines, show a row
893 ** notifying the user about the hidden rows.
894 */
895 if( j<nContext || j>c.aEdit[i]-nContext-1 ){
896 @ <tr>
897 }else if( j==nContext && j<c.aEdit[i]-nContext-1 ){
898 @ <tr>
899 @ <td class="meta" colspan="5" style="white-space: nowrap;">
900 @ %d(c.aEdit[i]-2*nContext) hidden lines</td>
901 @ </tr>
902 if( !allowExp )
903 continue;
904 @ <tr style="display:none;">
905 }else{
906 if( !allowExp )
907 continue;
908 @ <tr style="display:none;">
909 }
910
911 copylimline(linebuf, &c.aFrom[iFrom+j], collim);
912 @ <td class="lineno">%d(iFrom+j+1)</td>
913 @ <td class="srcline">%h(linebuf)</td>
914
915 @ <td> </td>
916
917 copylimline(linebuf, &c.aTo[iTo+j], collim);
918 @ <td class="lineno">%d(iTo+j+1)</td>
919 @ <td class="srcline">%h(linebuf)</td>
920
921 @ </tr>
922 }
923 iFrom+=c.aEdit[i];
924 iTo+=c.aEdit[i];
925
926 if( c.aEdit[i+1]!=0 && c.aEdit[i+2]!=0 ){
927 int lim;
928 lim = c.aEdit[i+1] > c.aEdit[i+2] ? c.aEdit[i+1] : c.aEdit[i+2];
929
930 /* Assume changed lines */
931 for( j=0; j<lim; j++ ){
932 @ <tr>
933
934 if( j<c.aEdit[i+1] ){
935 copylimline(linebuf, &c.aFrom[iFrom+j], collim);
936 @ <td class="changed lineno">%d(iFrom+j+1)</td>
937 @ <td class="changed srcline">%h(linebuf)</td>
938 }else{
939 @ <td colspan="2" class="changedvoid"/>
940 }
941
942 @ <td class="changed">|</td>
943
944 if( j<c.aEdit[i+2] ){
945 copylimline(linebuf, &c.aTo[iTo+j], collim);
946 @ <td class="changed lineno">%d(iTo+j+1)</td>
947 @ <td class="changed srcline">%h(linebuf)</td>
948 }else{
949 @ <td colspan="2" class="changedvoid"/>
950 }
951
952 @ </tr>
953 }
954 iFrom+=c.aEdit[i+1];
955 iTo+=c.aEdit[i+2];
956 }else{
957
958 /* Process deleted lines */
959 for( j=0; j<c.aEdit[i+1]; j++ ){
960 @ <tr>
961
962 copylimline(linebuf, &c.aFrom[iFrom+j], collim);
963 @ <td class="removed lineno">%d(iFrom+j+1)</td>
964 @ <td class="removed srcline">%h(linebuf)</td>
965 @ <td>&lt;</td>
966 @ <td colspan="2" class="removedvoid"/>
967 @ </tr>
968 }
969 iFrom+=c.aEdit[i+1];
970
971 /* Process inserted lines */
972 for( j=0; j<c.aEdit[i+2]; j++ ){
973 @ <tr>
974 @ <td colspan="2" class="addedvoid"/>
975 @ <td>&gt;</td>
976 copylimline(linebuf, &c.aTo[iTo+j], collim);
977 @ <td class="added lineno">%d(iTo+j+1)</td>
978 @ <td class="added srcline">%h(linebuf)</td>
979 @ </tr>
980 }
981 iTo+=c.aEdit[i+2];
982 }
983
984 i+=3;
985 }
986
987 free(linebuf);
988 free(c.aFrom);
989 free(c.aTo);
990 free(c.aEdit);
991 return 1;
992 }
993
994
995 /*
996 ** COMMAND: test-rawdiff
997 */
998 void test_rawdiff_cmd(void){
@@ -592,29 +1003,58 @@
1003 if( g.argc<4 ) usage("FILE1 FILE2 ...");
1004 blob_read_from_file(&a, g.argv[2]);
1005 for(i=3; i<g.argc; i++){
1006 if( i>3 ) fossil_print("-------------------------------\n");
1007 blob_read_from_file(&b, g.argv[i]);
1008 R = text_diff(&a, &b, 0, 0);
1009 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
1010 fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]);
1011 }
1012 /* free(R); */
1013 blob_reset(&b);
1014 }
1015 }
1016
1017 /*
1018 ** Process diff-related command-line options and return an appropriate
1019 ** "diffFlags" integer.
1020 **
1021 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE
1022 ** --context|-c N N lines of context. DIFF_CONTEXT_MASK
1023 ** --width|-W N N character lines. DIFF_WIDTH_MASK
1024 */
1025 int diff_options(void){
1026 int diffFlags = 0;
1027 const char *z;
1028 int f;
1029 if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE;
1030 if( (z = find_option("context","c",1))!=0 && (f = atoi(z))>0 ){
1031 if( f > DIFF_CONTEXT_MASK ) f = DIFF_CONTEXT_MASK;
1032 diffFlags |= f;
1033 }
1034 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
1035 f *= DIFF_CONTEXT_MASK+1;
1036 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK;
1037 diffFlags |= f;
1038 }
1039 return diffFlags;
1040 }
1041
1042 /*
1043 ** COMMAND: test-udiff
1044 **
1045 ** Print the difference between two files. The usual diff options apply.
1046 */
1047 void test_udiff_cmd(void){
1048 Blob a, b, out;
1049 int diffFlag = diff_options();
1050
1051 if( g.argc!=4 ) usage("FILE1 FILE2");
1052 blob_read_from_file(&a, g.argv[2]);
1053 blob_read_from_file(&b, g.argv[3]);
1054 blob_zero(&out);
1055 text_diff(&a, &b, &out, diffFlag);
1056 blob_write_to_file(&out, "-");
1057 }
1058
1059 /**************************************************************************
1060 ** The basic difference engine is above. What follows is the annotation
@@ -884,10 +1324,11 @@
1324 */
1325 void annotate_cmd(void){
1326 int fnid; /* Filename ID */
1327 int fid; /* File instance ID */
1328 int mid; /* Manifest where file was checked in */
1329 int cid; /* Checkout ID */
1330 Blob treename; /* FILENAME translated to canonical form */
1331 char *zFilename; /* Cannonical filename */
1332 Annotator ann; /* The annotation of the file */
1333 int i; /* Loop counter */
1334 const char *zLimit; /* The value to the --limit option */
@@ -913,11 +1354,20 @@
1354 }
1355 fid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFilename);
1356 if( fid==0 ){
1357 fossil_fatal("not part of current checkout: %s", zFilename);
1358 }
1359 cid = db_lget_int("checkout", 0);
1360 if (cid == 0){
1361 fossil_fatal("Not in a checkout");
1362 }
1363 if( iLimit<=0 ) iLimit = 1000000000;
1364 compute_direct_ancestors(cid, iLimit);
1365 mid = db_int(0, "SELECT mlink.mid FROM mlink, ancestor "
1366 " WHERE mlink.fid=%d AND mlink.fnid=%d AND mlink.mid=ancestor.rid"
1367 " ORDER BY ancestor.generation ASC LIMIT 1",
1368 fid, fnid);
1369 if( mid==0 ){
1370 fossil_panic("unable to find manifest");
1371 }
1372 if( fileVers ) annFlags |= ANN_FILE_VERS;
1373 annotate_file(&ann, fnid, mid, 0, iLimit, annFlags);
1374
+55 -38
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -19,16 +19,10 @@
1919
*/
2020
#include "config.h"
2121
#include "diffcmd.h"
2222
#include <assert.h>
2323
24
-/*
25
-** Diff option flags
26
-*/
27
-#define DIFF_NEWFILE 0x01 /* Treat non-existing fails as empty files */
28
-#define DIFF_NOEOLWS 0x02 /* Ignore whitespace at the end of lines */
29
-
3024
/*
3125
** Output the results of a diff. Output goes to stdout for command-line
3226
** or to the CGI/HTTP result buffer for web pages.
3327
*/
3428
static void diff_printf(const char *zFormat, ...){
@@ -41,15 +35,38 @@
4135
}
4236
va_end(ap);
4337
}
4438
4539
/*
46
-** Print the "Index:" message that patch wants to see at the top of a diff.
40
+** Print the "Index:" message that patches wants to see at the top of a diff.
41
+*/
42
+void diff_print_index(const char *zFile, int diffFlags){
43
+ if( (diffFlags & DIFF_SIDEBYSIDE)==0 ){
44
+ char *z = mprintf("Index: %s\n%.66c\n", zFile, '=');
45
+ diff_printf("%s", z);
46
+ fossil_free(z);
47
+ }
48
+}
49
+
50
+/*
51
+** Print the +++/--- filename lines for a diff operation.
4752
*/
48
-void diff_print_index(const char *zFile){
49
- diff_printf("Index: %s\n======================================="
50
- "============================\n", zFile);
53
+void diff_print_filenames(const char *zLeft, const char *zRight, int diffFlags){
54
+ char *z = 0;
55
+ if( diffFlags & DIFF_SIDEBYSIDE ){
56
+ int w = diff_width(diffFlags);
57
+ int n1 = strlen(zLeft);
58
+ int x;
59
+ if( n1>w*2 ) n1 = w*2;
60
+ x = w*2+17 - (n1+2);
61
+ z = mprintf("%.*c %.*s %.*c\n",
62
+ x/2, '=', n1, zLeft, (x+1)/2, '=');
63
+ }else{
64
+ z = mprintf("--- %s\n+++ %s\n", zLeft, zRight);
65
+ }
66
+ diff_printf("%s", z);
67
+ fossil_free(z);
5168
}
5269
5370
/*
5471
** Show the difference between two files, one in memory and one on disk.
5572
**
@@ -62,11 +79,11 @@
6279
void diff_file(
6380
Blob *pFile1, /* In memory content to compare from */
6481
const char *zFile2, /* On disk content to compare to */
6582
const char *zName, /* Display name of the file */
6683
const char *zDiffCmd, /* Command for comparison */
67
- int ignoreEolWs /* Ignore whitespace at end of line */
84
+ int diffFlags /* Flags to control the diff */
6885
){
6986
if( zDiffCmd==0 ){
7087
Blob out; /* Diff output text */
7188
Blob file2; /* Content of zFile2 */
7289
const char *zName2; /* Name of zFile2 for display */
@@ -84,13 +101,13 @@
84101
zName2 = zName;
85102
}
86103
87104
/* Compute and output the differences */
88105
blob_zero(&out);
89
- text_diff(pFile1, &file2, &out, 5, ignoreEolWs);
106
+ text_diff(pFile1, &file2, &out, diffFlags);
90107
if( blob_size(&out) ){
91
- diff_printf("--- %s\n+++ %s\n", zName, zName2);
108
+ diff_print_filenames(zName, zName2, diffFlags);
92109
diff_printf("%s\n", blob_str(&out));
93110
}
94111
95112
/* Release memory resources */
96113
blob_reset(&file2);
@@ -138,18 +155,18 @@
138155
void diff_file_mem(
139156
Blob *pFile1, /* In memory content to compare from */
140157
Blob *pFile2, /* In memory content to compare to */
141158
const char *zName, /* Display name of the file */
142159
const char *zDiffCmd, /* Command for comparison */
143
- int ignoreEolWs /* Ignore whitespace at end of lines */
160
+ int diffFlags /* Diff flags */
144161
){
145162
if( zDiffCmd==0 ){
146163
Blob out; /* Diff output text */
147164
148165
blob_zero(&out);
149
- text_diff(pFile1, pFile2, &out, 5, ignoreEolWs);
150
- diff_printf("--- %s\n+++ %s\n", zName, zName);
166
+ text_diff(pFile1, pFile2, &out, diffFlags);
167
+ diff_print_filenames(zName, zName, diffFlags);
151168
diff_printf("%s\n", blob_str(&out));
152169
153170
/* Release memory resources */
154171
blob_reset(&out);
155172
}else{
@@ -185,11 +202,11 @@
185202
** against the same file on disk.
186203
*/
187204
static void diff_one_against_disk(
188205
const char *zFrom, /* Name of file */
189206
const char *zDiffCmd, /* Use this "diff" command */
190
- int ignoreEolWs, /* Ignore whitespace changes at end of lines */
207
+ int diffFlags, /* Diff control flags */
191208
const char *zFileTreeName
192209
){
193210
Blob fname;
194211
Blob content;
195212
int isLink;
@@ -196,11 +213,11 @@
196213
file_tree_name(zFileTreeName, &fname, 1);
197214
historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
198215
if( !isLink != !file_wd_islink(zFrom) ){
199216
diff_printf("cannot compute difference between symlink and regular file\n");
200217
}else{
201
- diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, ignoreEolWs);
218
+ diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags);
202219
}
203220
blob_reset(&content);
204221
blob_reset(&fname);
205222
}
206223
@@ -215,14 +232,12 @@
215232
int diffFlags /* Flags controlling diff output */
216233
){
217234
int vid;
218235
Blob sql;
219236
Stmt q;
220
- int ignoreEolWs; /* Ignore end-of-line whitespace */
221237
int asNewFile; /* Treat non-existant files as empty files */
222238
223
- ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0;
224239
asNewFile = (diffFlags & DIFF_NEWFILE)!=0;
225240
vid = db_lget_int("checkout", 0);
226241
vfile_check_signature(vid, 1, 0);
227242
blob_zero(&sql);
228243
db_begin_transaction();
@@ -289,22 +304,22 @@
289304
if( !asNewFile ){ showDiff = 0; }
290305
}
291306
if( showDiff ){
292307
Blob content;
293308
if( !isLink != !file_wd_islink(zFullName) ){
294
- diff_print_index(zPathname);
295
- diff_printf("--- %s\n+++ %s\n", zPathname, zPathname);
309
+ diff_print_index(zPathname, diffFlags);
310
+ diff_print_filenames(zPathname, zPathname, diffFlags);
296311
diff_printf("cannot compute difference between symlink and regular file\n");
297312
continue;
298313
}
299314
if( srcid>0 ){
300315
content_get(srcid, &content);
301316
}else{
302317
blob_zero(&content);
303318
}
304
- diff_print_index(zPathname);
305
- diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs);
319
+ diff_print_index(zPathname, diffFlags);
320
+ diff_file(&content, zFullName, zPathname, zDiffCmd, diffFlags);
306321
blob_reset(&content);
307322
}
308323
free(zToFree);
309324
}
310325
db_finalize(&q);
@@ -317,11 +332,11 @@
317332
*/
318333
static void diff_one_two_versions(
319334
const char *zFrom,
320335
const char *zTo,
321336
const char *zDiffCmd,
322
- int ignoreEolWs,
337
+ int diffFlags,
323338
const char *zFileTreeName
324339
){
325340
char *zName;
326341
Blob fname;
327342
Blob v1, v2;
@@ -329,14 +344,14 @@
329344
file_tree_name(zFileTreeName, &fname, 1);
330345
zName = blob_str(&fname);
331346
historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
332347
historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
333348
if( isLink1 != isLink2 ){
334
- diff_printf("--- %s\n+++ %s\n", zName, zName);
349
+ diff_print_filenames(zName, zName, diffFlags);
335350
diff_printf("cannot compute difference between symlink and regular file\n");
336351
}else{
337
- diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
352
+ diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags);
338353
}
339354
blob_reset(&v1);
340355
blob_reset(&v2);
341356
blob_reset(&fname);
342357
}
@@ -347,16 +362,16 @@
347362
*/
348363
static void diff_manifest_entry(
349364
struct ManifestFile *pFrom,
350365
struct ManifestFile *pTo,
351366
const char *zDiffCmd,
352
- int ignoreEolWs
367
+ int diffFlags
353368
){
354369
Blob f1, f2;
355370
int rid;
356371
const char *zName = pFrom ? pFrom->zName : pTo->zName;
357
- diff_print_index(zName);
372
+ diff_print_index(zName, diffFlags);
358373
if( pFrom ){
359374
rid = uuid_to_rid(pFrom->zUuid, 0);
360375
content_get(rid, &f1);
361376
}else{
362377
blob_zero(&f1);
@@ -365,11 +380,11 @@
365380
rid = uuid_to_rid(pTo->zUuid, 0);
366381
content_get(rid, &f2);
367382
}else{
368383
blob_zero(&f2);
369384
}
370
- diff_file_mem(&f1, &f2, zName, zDiffCmd, ignoreEolWs);
385
+ diff_file_mem(&f1, &f2, zName, zDiffCmd, diffFlags);
371386
blob_reset(&f1);
372387
blob_reset(&f2);
373388
}
374389
375390
/*
@@ -381,11 +396,10 @@
381396
const char *zDiffCmd,
382397
int diffFlags
383398
){
384399
Manifest *pFrom, *pTo;
385400
ManifestFile *pFromFile, *pToFile;
386
- int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
387401
int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
388402
389403
pFrom = manifest_get_by_name(zFrom, 0);
390404
manifest_file_rewind(pFrom);
391405
pFromFile = manifest_file_next(pFrom,0);
@@ -403,26 +417,26 @@
403417
cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
404418
}
405419
if( cmp<0 ){
406420
diff_printf("DELETED %s\n", pFromFile->zName);
407421
if( asNewFlag ){
408
- diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs);
422
+ diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags);
409423
}
410424
pFromFile = manifest_file_next(pFrom,0);
411425
}else if( cmp>0 ){
412426
diff_printf("ADDED %s\n", pToFile->zName);
413427
if( asNewFlag ){
414
- diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs);
428
+ diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags);
415429
}
416430
pToFile = manifest_file_next(pTo,0);
417431
}else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
418432
/* No changes */
419433
pFromFile = manifest_file_next(pFrom,0);
420434
pToFile = manifest_file_next(pTo,0);
421435
}else{
422436
/* diff_printf("CHANGED %s\n", pFromFile->zName); */
423
- diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs);
437
+ diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags);
424438
pFromFile = manifest_file_next(pFrom,0);
425439
pToFile = manifest_file_next(pTo,0);
426440
}
427441
}
428442
manifest_destroy(pFrom);
@@ -456,14 +470,17 @@
456470
**
457471
** The "-N" or "--new-file" option causes the complete text of added or
458472
** deleted files to be displayed.
459473
**
460474
** Options:
475
+** --context|-c N Use N lines of context
461476
** --from|-r VERSION select VERSION as source for the diff
462477
** --new-file|-N output complete text of added or deleted files
463478
** -i use internal diff logic
464479
** --to VERSION select VERSION as target for the diff
480
+** --side-by-side|-y side-by-side diff
481
+** --width|-W N Width of lines in side-by-side diff
465482
*/
466483
void diff_cmd(void){
467484
int isGDiff; /* True for gdiff. False for normal diff */
468485
int isInternDiff; /* True for internal diff */
469486
int hasNFlag; /* True if -N or --new-file flag is used */
@@ -475,23 +492,23 @@
475492
476493
isGDiff = g.argv[1][0]=='g';
477494
isInternDiff = find_option("internal","i",0)!=0;
478495
zFrom = find_option("from", "r", 1);
479496
zTo = find_option("to", 0, 1);
497
+ diffFlags = diff_options();
480498
hasNFlag = find_option("new-file","N",0)!=0;
481
-
482
-
483499
if( hasNFlag ) diffFlags |= DIFF_NEWFILE;
500
+
484501
if( zTo==0 ){
485502
db_must_be_within_tree();
486503
verify_all_options();
487504
if( !isInternDiff ){
488505
zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
489506
}
490507
if( g.argc>=3 ){
491508
for(f=2; f<g.argc; ++f){
492
- diff_one_against_disk(zFrom, zDiffCmd, 0, g.argv[f]);
509
+ diff_one_against_disk(zFrom, zDiffCmd, diffFlags, g.argv[f]);
493510
}
494511
}else{
495512
diff_all_against_disk(zFrom, zDiffCmd, diffFlags);
496513
}
497514
}else if( zFrom==0 ){
@@ -502,11 +519,11 @@
502519
if( !isInternDiff ){
503520
zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
504521
}
505522
if( g.argc>=3 ){
506523
for(f=2; f<g.argc; ++f){
507
- diff_one_two_versions(zFrom, zTo, zDiffCmd, 0, g.argv[f]);
524
+ diff_one_two_versions(zFrom, zTo, zDiffCmd, diffFlags, g.argv[f]);
508525
}
509526
}else{
510527
diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags);
511528
}
512529
}
513530
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -19,16 +19,10 @@
19 */
20 #include "config.h"
21 #include "diffcmd.h"
22 #include <assert.h>
23
24 /*
25 ** Diff option flags
26 */
27 #define DIFF_NEWFILE 0x01 /* Treat non-existing fails as empty files */
28 #define DIFF_NOEOLWS 0x02 /* Ignore whitespace at the end of lines */
29
30 /*
31 ** Output the results of a diff. Output goes to stdout for command-line
32 ** or to the CGI/HTTP result buffer for web pages.
33 */
34 static void diff_printf(const char *zFormat, ...){
@@ -41,15 +35,38 @@
41 }
42 va_end(ap);
43 }
44
45 /*
46 ** Print the "Index:" message that patch wants to see at the top of a diff.
 
 
 
 
 
 
 
 
 
 
 
47 */
48 void diff_print_index(const char *zFile){
49 diff_printf("Index: %s\n======================================="
50 "============================\n", zFile);
 
 
 
 
 
 
 
 
 
 
 
 
51 }
52
53 /*
54 ** Show the difference between two files, one in memory and one on disk.
55 **
@@ -62,11 +79,11 @@
62 void diff_file(
63 Blob *pFile1, /* In memory content to compare from */
64 const char *zFile2, /* On disk content to compare to */
65 const char *zName, /* Display name of the file */
66 const char *zDiffCmd, /* Command for comparison */
67 int ignoreEolWs /* Ignore whitespace at end of line */
68 ){
69 if( zDiffCmd==0 ){
70 Blob out; /* Diff output text */
71 Blob file2; /* Content of zFile2 */
72 const char *zName2; /* Name of zFile2 for display */
@@ -84,13 +101,13 @@
84 zName2 = zName;
85 }
86
87 /* Compute and output the differences */
88 blob_zero(&out);
89 text_diff(pFile1, &file2, &out, 5, ignoreEolWs);
90 if( blob_size(&out) ){
91 diff_printf("--- %s\n+++ %s\n", zName, zName2);
92 diff_printf("%s\n", blob_str(&out));
93 }
94
95 /* Release memory resources */
96 blob_reset(&file2);
@@ -138,18 +155,18 @@
138 void diff_file_mem(
139 Blob *pFile1, /* In memory content to compare from */
140 Blob *pFile2, /* In memory content to compare to */
141 const char *zName, /* Display name of the file */
142 const char *zDiffCmd, /* Command for comparison */
143 int ignoreEolWs /* Ignore whitespace at end of lines */
144 ){
145 if( zDiffCmd==0 ){
146 Blob out; /* Diff output text */
147
148 blob_zero(&out);
149 text_diff(pFile1, pFile2, &out, 5, ignoreEolWs);
150 diff_printf("--- %s\n+++ %s\n", zName, zName);
151 diff_printf("%s\n", blob_str(&out));
152
153 /* Release memory resources */
154 blob_reset(&out);
155 }else{
@@ -185,11 +202,11 @@
185 ** against the same file on disk.
186 */
187 static void diff_one_against_disk(
188 const char *zFrom, /* Name of file */
189 const char *zDiffCmd, /* Use this "diff" command */
190 int ignoreEolWs, /* Ignore whitespace changes at end of lines */
191 const char *zFileTreeName
192 ){
193 Blob fname;
194 Blob content;
195 int isLink;
@@ -196,11 +213,11 @@
196 file_tree_name(zFileTreeName, &fname, 1);
197 historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
198 if( !isLink != !file_wd_islink(zFrom) ){
199 diff_printf("cannot compute difference between symlink and regular file\n");
200 }else{
201 diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, ignoreEolWs);
202 }
203 blob_reset(&content);
204 blob_reset(&fname);
205 }
206
@@ -215,14 +232,12 @@
215 int diffFlags /* Flags controlling diff output */
216 ){
217 int vid;
218 Blob sql;
219 Stmt q;
220 int ignoreEolWs; /* Ignore end-of-line whitespace */
221 int asNewFile; /* Treat non-existant files as empty files */
222
223 ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0;
224 asNewFile = (diffFlags & DIFF_NEWFILE)!=0;
225 vid = db_lget_int("checkout", 0);
226 vfile_check_signature(vid, 1, 0);
227 blob_zero(&sql);
228 db_begin_transaction();
@@ -289,22 +304,22 @@
289 if( !asNewFile ){ showDiff = 0; }
290 }
291 if( showDiff ){
292 Blob content;
293 if( !isLink != !file_wd_islink(zFullName) ){
294 diff_print_index(zPathname);
295 diff_printf("--- %s\n+++ %s\n", zPathname, zPathname);
296 diff_printf("cannot compute difference between symlink and regular file\n");
297 continue;
298 }
299 if( srcid>0 ){
300 content_get(srcid, &content);
301 }else{
302 blob_zero(&content);
303 }
304 diff_print_index(zPathname);
305 diff_file(&content, zFullName, zPathname, zDiffCmd, ignoreEolWs);
306 blob_reset(&content);
307 }
308 free(zToFree);
309 }
310 db_finalize(&q);
@@ -317,11 +332,11 @@
317 */
318 static void diff_one_two_versions(
319 const char *zFrom,
320 const char *zTo,
321 const char *zDiffCmd,
322 int ignoreEolWs,
323 const char *zFileTreeName
324 ){
325 char *zName;
326 Blob fname;
327 Blob v1, v2;
@@ -329,14 +344,14 @@
329 file_tree_name(zFileTreeName, &fname, 1);
330 zName = blob_str(&fname);
331 historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
332 historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
333 if( isLink1 != isLink2 ){
334 diff_printf("--- %s\n+++ %s\n", zName, zName);
335 diff_printf("cannot compute difference between symlink and regular file\n");
336 }else{
337 diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs);
338 }
339 blob_reset(&v1);
340 blob_reset(&v2);
341 blob_reset(&fname);
342 }
@@ -347,16 +362,16 @@
347 */
348 static void diff_manifest_entry(
349 struct ManifestFile *pFrom,
350 struct ManifestFile *pTo,
351 const char *zDiffCmd,
352 int ignoreEolWs
353 ){
354 Blob f1, f2;
355 int rid;
356 const char *zName = pFrom ? pFrom->zName : pTo->zName;
357 diff_print_index(zName);
358 if( pFrom ){
359 rid = uuid_to_rid(pFrom->zUuid, 0);
360 content_get(rid, &f1);
361 }else{
362 blob_zero(&f1);
@@ -365,11 +380,11 @@
365 rid = uuid_to_rid(pTo->zUuid, 0);
366 content_get(rid, &f2);
367 }else{
368 blob_zero(&f2);
369 }
370 diff_file_mem(&f1, &f2, zName, zDiffCmd, ignoreEolWs);
371 blob_reset(&f1);
372 blob_reset(&f2);
373 }
374
375 /*
@@ -381,11 +396,10 @@
381 const char *zDiffCmd,
382 int diffFlags
383 ){
384 Manifest *pFrom, *pTo;
385 ManifestFile *pFromFile, *pToFile;
386 int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0;
387 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
388
389 pFrom = manifest_get_by_name(zFrom, 0);
390 manifest_file_rewind(pFrom);
391 pFromFile = manifest_file_next(pFrom,0);
@@ -403,26 +417,26 @@
403 cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
404 }
405 if( cmp<0 ){
406 diff_printf("DELETED %s\n", pFromFile->zName);
407 if( asNewFlag ){
408 diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs);
409 }
410 pFromFile = manifest_file_next(pFrom,0);
411 }else if( cmp>0 ){
412 diff_printf("ADDED %s\n", pToFile->zName);
413 if( asNewFlag ){
414 diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs);
415 }
416 pToFile = manifest_file_next(pTo,0);
417 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
418 /* No changes */
419 pFromFile = manifest_file_next(pFrom,0);
420 pToFile = manifest_file_next(pTo,0);
421 }else{
422 /* diff_printf("CHANGED %s\n", pFromFile->zName); */
423 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs);
424 pFromFile = manifest_file_next(pFrom,0);
425 pToFile = manifest_file_next(pTo,0);
426 }
427 }
428 manifest_destroy(pFrom);
@@ -456,14 +470,17 @@
456 **
457 ** The "-N" or "--new-file" option causes the complete text of added or
458 ** deleted files to be displayed.
459 **
460 ** Options:
 
461 ** --from|-r VERSION select VERSION as source for the diff
462 ** --new-file|-N output complete text of added or deleted files
463 ** -i use internal diff logic
464 ** --to VERSION select VERSION as target for the diff
 
 
465 */
466 void diff_cmd(void){
467 int isGDiff; /* True for gdiff. False for normal diff */
468 int isInternDiff; /* True for internal diff */
469 int hasNFlag; /* True if -N or --new-file flag is used */
@@ -475,23 +492,23 @@
475
476 isGDiff = g.argv[1][0]=='g';
477 isInternDiff = find_option("internal","i",0)!=0;
478 zFrom = find_option("from", "r", 1);
479 zTo = find_option("to", 0, 1);
 
480 hasNFlag = find_option("new-file","N",0)!=0;
481
482
483 if( hasNFlag ) diffFlags |= DIFF_NEWFILE;
 
484 if( zTo==0 ){
485 db_must_be_within_tree();
486 verify_all_options();
487 if( !isInternDiff ){
488 zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
489 }
490 if( g.argc>=3 ){
491 for(f=2; f<g.argc; ++f){
492 diff_one_against_disk(zFrom, zDiffCmd, 0, g.argv[f]);
493 }
494 }else{
495 diff_all_against_disk(zFrom, zDiffCmd, diffFlags);
496 }
497 }else if( zFrom==0 ){
@@ -502,11 +519,11 @@
502 if( !isInternDiff ){
503 zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
504 }
505 if( g.argc>=3 ){
506 for(f=2; f<g.argc; ++f){
507 diff_one_two_versions(zFrom, zTo, zDiffCmd, 0, g.argv[f]);
508 }
509 }else{
510 diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags);
511 }
512 }
513
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -19,16 +19,10 @@
19 */
20 #include "config.h"
21 #include "diffcmd.h"
22 #include <assert.h>
23
 
 
 
 
 
 
24 /*
25 ** Output the results of a diff. Output goes to stdout for command-line
26 ** or to the CGI/HTTP result buffer for web pages.
27 */
28 static void diff_printf(const char *zFormat, ...){
@@ -41,15 +35,38 @@
35 }
36 va_end(ap);
37 }
38
39 /*
40 ** Print the "Index:" message that patches wants to see at the top of a diff.
41 */
42 void diff_print_index(const char *zFile, int diffFlags){
43 if( (diffFlags & DIFF_SIDEBYSIDE)==0 ){
44 char *z = mprintf("Index: %s\n%.66c\n", zFile, '=');
45 diff_printf("%s", z);
46 fossil_free(z);
47 }
48 }
49
50 /*
51 ** Print the +++/--- filename lines for a diff operation.
52 */
53 void diff_print_filenames(const char *zLeft, const char *zRight, int diffFlags){
54 char *z = 0;
55 if( diffFlags & DIFF_SIDEBYSIDE ){
56 int w = diff_width(diffFlags);
57 int n1 = strlen(zLeft);
58 int x;
59 if( n1>w*2 ) n1 = w*2;
60 x = w*2+17 - (n1+2);
61 z = mprintf("%.*c %.*s %.*c\n",
62 x/2, '=', n1, zLeft, (x+1)/2, '=');
63 }else{
64 z = mprintf("--- %s\n+++ %s\n", zLeft, zRight);
65 }
66 diff_printf("%s", z);
67 fossil_free(z);
68 }
69
70 /*
71 ** Show the difference between two files, one in memory and one on disk.
72 **
@@ -62,11 +79,11 @@
79 void diff_file(
80 Blob *pFile1, /* In memory content to compare from */
81 const char *zFile2, /* On disk content to compare to */
82 const char *zName, /* Display name of the file */
83 const char *zDiffCmd, /* Command for comparison */
84 int diffFlags /* Flags to control the diff */
85 ){
86 if( zDiffCmd==0 ){
87 Blob out; /* Diff output text */
88 Blob file2; /* Content of zFile2 */
89 const char *zName2; /* Name of zFile2 for display */
@@ -84,13 +101,13 @@
101 zName2 = zName;
102 }
103
104 /* Compute and output the differences */
105 blob_zero(&out);
106 text_diff(pFile1, &file2, &out, diffFlags);
107 if( blob_size(&out) ){
108 diff_print_filenames(zName, zName2, diffFlags);
109 diff_printf("%s\n", blob_str(&out));
110 }
111
112 /* Release memory resources */
113 blob_reset(&file2);
@@ -138,18 +155,18 @@
155 void diff_file_mem(
156 Blob *pFile1, /* In memory content to compare from */
157 Blob *pFile2, /* In memory content to compare to */
158 const char *zName, /* Display name of the file */
159 const char *zDiffCmd, /* Command for comparison */
160 int diffFlags /* Diff flags */
161 ){
162 if( zDiffCmd==0 ){
163 Blob out; /* Diff output text */
164
165 blob_zero(&out);
166 text_diff(pFile1, pFile2, &out, diffFlags);
167 diff_print_filenames(zName, zName, diffFlags);
168 diff_printf("%s\n", blob_str(&out));
169
170 /* Release memory resources */
171 blob_reset(&out);
172 }else{
@@ -185,11 +202,11 @@
202 ** against the same file on disk.
203 */
204 static void diff_one_against_disk(
205 const char *zFrom, /* Name of file */
206 const char *zDiffCmd, /* Use this "diff" command */
207 int diffFlags, /* Diff control flags */
208 const char *zFileTreeName
209 ){
210 Blob fname;
211 Blob content;
212 int isLink;
@@ -196,11 +213,11 @@
213 file_tree_name(zFileTreeName, &fname, 1);
214 historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0);
215 if( !isLink != !file_wd_islink(zFrom) ){
216 diff_printf("cannot compute difference between symlink and regular file\n");
217 }else{
218 diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags);
219 }
220 blob_reset(&content);
221 blob_reset(&fname);
222 }
223
@@ -215,14 +232,12 @@
232 int diffFlags /* Flags controlling diff output */
233 ){
234 int vid;
235 Blob sql;
236 Stmt q;
 
237 int asNewFile; /* Treat non-existant files as empty files */
238
 
239 asNewFile = (diffFlags & DIFF_NEWFILE)!=0;
240 vid = db_lget_int("checkout", 0);
241 vfile_check_signature(vid, 1, 0);
242 blob_zero(&sql);
243 db_begin_transaction();
@@ -289,22 +304,22 @@
304 if( !asNewFile ){ showDiff = 0; }
305 }
306 if( showDiff ){
307 Blob content;
308 if( !isLink != !file_wd_islink(zFullName) ){
309 diff_print_index(zPathname, diffFlags);
310 diff_print_filenames(zPathname, zPathname, diffFlags);
311 diff_printf("cannot compute difference between symlink and regular file\n");
312 continue;
313 }
314 if( srcid>0 ){
315 content_get(srcid, &content);
316 }else{
317 blob_zero(&content);
318 }
319 diff_print_index(zPathname, diffFlags);
320 diff_file(&content, zFullName, zPathname, zDiffCmd, diffFlags);
321 blob_reset(&content);
322 }
323 free(zToFree);
324 }
325 db_finalize(&q);
@@ -317,11 +332,11 @@
332 */
333 static void diff_one_two_versions(
334 const char *zFrom,
335 const char *zTo,
336 const char *zDiffCmd,
337 int diffFlags,
338 const char *zFileTreeName
339 ){
340 char *zName;
341 Blob fname;
342 Blob v1, v2;
@@ -329,14 +344,14 @@
344 file_tree_name(zFileTreeName, &fname, 1);
345 zName = blob_str(&fname);
346 historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0);
347 historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0);
348 if( isLink1 != isLink2 ){
349 diff_print_filenames(zName, zName, diffFlags);
350 diff_printf("cannot compute difference between symlink and regular file\n");
351 }else{
352 diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags);
353 }
354 blob_reset(&v1);
355 blob_reset(&v2);
356 blob_reset(&fname);
357 }
@@ -347,16 +362,16 @@
362 */
363 static void diff_manifest_entry(
364 struct ManifestFile *pFrom,
365 struct ManifestFile *pTo,
366 const char *zDiffCmd,
367 int diffFlags
368 ){
369 Blob f1, f2;
370 int rid;
371 const char *zName = pFrom ? pFrom->zName : pTo->zName;
372 diff_print_index(zName, diffFlags);
373 if( pFrom ){
374 rid = uuid_to_rid(pFrom->zUuid, 0);
375 content_get(rid, &f1);
376 }else{
377 blob_zero(&f1);
@@ -365,11 +380,11 @@
380 rid = uuid_to_rid(pTo->zUuid, 0);
381 content_get(rid, &f2);
382 }else{
383 blob_zero(&f2);
384 }
385 diff_file_mem(&f1, &f2, zName, zDiffCmd, diffFlags);
386 blob_reset(&f1);
387 blob_reset(&f2);
388 }
389
390 /*
@@ -381,11 +396,10 @@
396 const char *zDiffCmd,
397 int diffFlags
398 ){
399 Manifest *pFrom, *pTo;
400 ManifestFile *pFromFile, *pToFile;
 
401 int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0;
402
403 pFrom = manifest_get_by_name(zFrom, 0);
404 manifest_file_rewind(pFrom);
405 pFromFile = manifest_file_next(pFrom,0);
@@ -403,26 +417,26 @@
417 cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
418 }
419 if( cmp<0 ){
420 diff_printf("DELETED %s\n", pFromFile->zName);
421 if( asNewFlag ){
422 diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags);
423 }
424 pFromFile = manifest_file_next(pFrom,0);
425 }else if( cmp>0 ){
426 diff_printf("ADDED %s\n", pToFile->zName);
427 if( asNewFlag ){
428 diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags);
429 }
430 pToFile = manifest_file_next(pTo,0);
431 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
432 /* No changes */
433 pFromFile = manifest_file_next(pFrom,0);
434 pToFile = manifest_file_next(pTo,0);
435 }else{
436 /* diff_printf("CHANGED %s\n", pFromFile->zName); */
437 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags);
438 pFromFile = manifest_file_next(pFrom,0);
439 pToFile = manifest_file_next(pTo,0);
440 }
441 }
442 manifest_destroy(pFrom);
@@ -456,14 +470,17 @@
470 **
471 ** The "-N" or "--new-file" option causes the complete text of added or
472 ** deleted files to be displayed.
473 **
474 ** Options:
475 ** --context|-c N Use N lines of context
476 ** --from|-r VERSION select VERSION as source for the diff
477 ** --new-file|-N output complete text of added or deleted files
478 ** -i use internal diff logic
479 ** --to VERSION select VERSION as target for the diff
480 ** --side-by-side|-y side-by-side diff
481 ** --width|-W N Width of lines in side-by-side diff
482 */
483 void diff_cmd(void){
484 int isGDiff; /* True for gdiff. False for normal diff */
485 int isInternDiff; /* True for internal diff */
486 int hasNFlag; /* True if -N or --new-file flag is used */
@@ -475,23 +492,23 @@
492
493 isGDiff = g.argv[1][0]=='g';
494 isInternDiff = find_option("internal","i",0)!=0;
495 zFrom = find_option("from", "r", 1);
496 zTo = find_option("to", 0, 1);
497 diffFlags = diff_options();
498 hasNFlag = find_option("new-file","N",0)!=0;
 
 
499 if( hasNFlag ) diffFlags |= DIFF_NEWFILE;
500
501 if( zTo==0 ){
502 db_must_be_within_tree();
503 verify_all_options();
504 if( !isInternDiff ){
505 zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
506 }
507 if( g.argc>=3 ){
508 for(f=2; f<g.argc; ++f){
509 diff_one_against_disk(zFrom, zDiffCmd, diffFlags, g.argv[f]);
510 }
511 }else{
512 diff_all_against_disk(zFrom, zDiffCmd, diffFlags);
513 }
514 }else if( zFrom==0 ){
@@ -502,11 +519,11 @@
519 if( !isInternDiff ){
520 zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
521 }
522 if( g.argc>=3 ){
523 for(f=2; f<g.argc; ++f){
524 diff_one_two_versions(zFrom, zTo, zDiffCmd, diffFlags, g.argv[f]);
525 }
526 }else{
527 diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags);
528 }
529 }
530
+13 -2
--- src/doc.c
+++ src/doc.c
@@ -367,18 +367,27 @@
367367
login_check_credentials();
368368
if( !g.perm.Read ){ login_needed(); return; }
369369
zName = PD("name", "tip/index.wiki");
370370
for(i=0; zName[i] && zName[i]!='/'; i++){}
371371
if( zName[i]==0 || i>UUID_SIZE ){
372
+ zName = "index.html";
372373
goto doc_not_found;
373374
}
374375
memcpy(zBaseline, zName, i);
375376
zBaseline[i] = 0;
376377
zName += i;
377378
while( zName[0]=='/' ){ zName++; }
378379
if( !file_is_simple_pathname(zName) ){
379
- goto doc_not_found;
380
+ int n = strlen(zName);
381
+ if( n>0 && zName[n-1]=='/' ){
382
+ zName = mprintf("%sindex.html", zName);
383
+ if( !file_is_simple_pathname(zName) ){
384
+ goto doc_not_found;
385
+ }
386
+ }else{
387
+ goto doc_not_found;
388
+ }
380389
}
381390
if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){
382391
sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip");
383392
}
384393
if( fossil_strcmp(zBaseline,"ckout")==0 ){
@@ -408,10 +417,12 @@
408417
" fname TEXT, -- filename\n"
409418
" rid INTEGER, -- artifact ID\n"
410419
" UNIQUE(vid,fname,rid)\n"
411420
")"
412421
);
422
+
423
+
413424
414425
/* Check to see if the documentation file artifact ID is contained
415426
** in the baseline cache */
416427
rid = db_int(0, "SELECT rid FROM vcache"
417428
" WHERE vid=%d AND fname=%Q", vid, zName);
@@ -499,11 +510,11 @@
499510
500511
doc_not_found:
501512
/* Jump here when unable to locate the document */
502513
db_end_transaction(0);
503514
style_header("Document Not Found");
504
- @ <p>No such document: %h(PD("name","tip/index.wiki"))</p>
515
+ @ <p>No such document: %h(zName)</p>
505516
style_footer();
506517
return;
507518
}
508519
509520
/*
510521
--- src/doc.c
+++ src/doc.c
@@ -367,18 +367,27 @@
367 login_check_credentials();
368 if( !g.perm.Read ){ login_needed(); return; }
369 zName = PD("name", "tip/index.wiki");
370 for(i=0; zName[i] && zName[i]!='/'; i++){}
371 if( zName[i]==0 || i>UUID_SIZE ){
 
372 goto doc_not_found;
373 }
374 memcpy(zBaseline, zName, i);
375 zBaseline[i] = 0;
376 zName += i;
377 while( zName[0]=='/' ){ zName++; }
378 if( !file_is_simple_pathname(zName) ){
379 goto doc_not_found;
 
 
 
 
 
 
 
 
380 }
381 if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){
382 sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip");
383 }
384 if( fossil_strcmp(zBaseline,"ckout")==0 ){
@@ -408,10 +417,12 @@
408 " fname TEXT, -- filename\n"
409 " rid INTEGER, -- artifact ID\n"
410 " UNIQUE(vid,fname,rid)\n"
411 ")"
412 );
 
 
413
414 /* Check to see if the documentation file artifact ID is contained
415 ** in the baseline cache */
416 rid = db_int(0, "SELECT rid FROM vcache"
417 " WHERE vid=%d AND fname=%Q", vid, zName);
@@ -499,11 +510,11 @@
499
500 doc_not_found:
501 /* Jump here when unable to locate the document */
502 db_end_transaction(0);
503 style_header("Document Not Found");
504 @ <p>No such document: %h(PD("name","tip/index.wiki"))</p>
505 style_footer();
506 return;
507 }
508
509 /*
510
--- src/doc.c
+++ src/doc.c
@@ -367,18 +367,27 @@
367 login_check_credentials();
368 if( !g.perm.Read ){ login_needed(); return; }
369 zName = PD("name", "tip/index.wiki");
370 for(i=0; zName[i] && zName[i]!='/'; i++){}
371 if( zName[i]==0 || i>UUID_SIZE ){
372 zName = "index.html";
373 goto doc_not_found;
374 }
375 memcpy(zBaseline, zName, i);
376 zBaseline[i] = 0;
377 zName += i;
378 while( zName[0]=='/' ){ zName++; }
379 if( !file_is_simple_pathname(zName) ){
380 int n = strlen(zName);
381 if( n>0 && zName[n-1]=='/' ){
382 zName = mprintf("%sindex.html", zName);
383 if( !file_is_simple_pathname(zName) ){
384 goto doc_not_found;
385 }
386 }else{
387 goto doc_not_found;
388 }
389 }
390 if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){
391 sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip");
392 }
393 if( fossil_strcmp(zBaseline,"ckout")==0 ){
@@ -408,10 +417,12 @@
417 " fname TEXT, -- filename\n"
418 " rid INTEGER, -- artifact ID\n"
419 " UNIQUE(vid,fname,rid)\n"
420 ")"
421 );
422
423
424
425 /* Check to see if the documentation file artifact ID is contained
426 ** in the baseline cache */
427 rid = db_int(0, "SELECT rid FROM vcache"
428 " WHERE vid=%d AND fname=%Q", vid, zName);
@@ -499,11 +510,11 @@
510
511 doc_not_found:
512 /* Jump here when unable to locate the document */
513 db_end_transaction(0);
514 style_header("Document Not Found");
515 @ <p>No such document: %h(zName)</p>
516 style_footer();
517 return;
518 }
519
520 /*
521
+1 -1
--- src/finfo.c
+++ src/finfo.c
@@ -287,11 +287,11 @@
287287
char zShortCkin[20];
288288
if( zBr==0 ) zBr = "trunk";
289289
if( uBg ){
290290
zBgClr = hash_color(zUser);
291291
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
292
- zBgClr = strcmp(zBr,"trunk")==0 ? "white" : hash_color(zBr);
292
+ zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
293293
}
294294
gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr, 0);
295295
if( memcmp(zDate, zPrevDate, 10) ){
296296
sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
297297
@ <tr><td>
298298
--- src/finfo.c
+++ src/finfo.c
@@ -287,11 +287,11 @@
287 char zShortCkin[20];
288 if( zBr==0 ) zBr = "trunk";
289 if( uBg ){
290 zBgClr = hash_color(zUser);
291 }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
292 zBgClr = strcmp(zBr,"trunk")==0 ? "white" : hash_color(zBr);
293 }
294 gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr, 0);
295 if( memcmp(zDate, zPrevDate, 10) ){
296 sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
297 @ <tr><td>
298
--- src/finfo.c
+++ src/finfo.c
@@ -287,11 +287,11 @@
287 char zShortCkin[20];
288 if( zBr==0 ) zBr = "trunk";
289 if( uBg ){
290 zBgClr = hash_color(zUser);
291 }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
292 zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
293 }
294 gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr, 0);
295 if( memcmp(zDate, zPrevDate, 10) ){
296 sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
297 @ <tr><td>
298
--- src/http_transport.c
+++ src/http_transport.c
@@ -265,14 +265,12 @@
265265
void transport_send(Blob *toSend){
266266
char *z = blob_buffer(toSend);
267267
int n = blob_size(toSend);
268268
transport.nSent += n;
269269
if( g.urlIsSsh ){
270
- int sent;
271
- sent = fwrite(z, 1, n, sshOut);
270
+ fwrite(z, 1, n, sshOut);
272271
fflush(sshOut);
273
- /* printf("sent %d of %d bytes\n", sent, n); fflush(stdout); */
274272
}else if( g.urlIsHttps ){
275273
#ifdef FOSSIL_ENABLE_SSL
276274
int sent;
277275
while( n>0 ){
278276
sent = ssl_send(0, z, n);
279277
--- src/http_transport.c
+++ src/http_transport.c
@@ -265,14 +265,12 @@
265 void transport_send(Blob *toSend){
266 char *z = blob_buffer(toSend);
267 int n = blob_size(toSend);
268 transport.nSent += n;
269 if( g.urlIsSsh ){
270 int sent;
271 sent = fwrite(z, 1, n, sshOut);
272 fflush(sshOut);
273 /* printf("sent %d of %d bytes\n", sent, n); fflush(stdout); */
274 }else if( g.urlIsHttps ){
275 #ifdef FOSSIL_ENABLE_SSL
276 int sent;
277 while( n>0 ){
278 sent = ssl_send(0, z, n);
279
--- src/http_transport.c
+++ src/http_transport.c
@@ -265,14 +265,12 @@
265 void transport_send(Blob *toSend){
266 char *z = blob_buffer(toSend);
267 int n = blob_size(toSend);
268 transport.nSent += n;
269 if( g.urlIsSsh ){
270 fwrite(z, 1, n, sshOut);
 
271 fflush(sshOut);
 
272 }else if( g.urlIsHttps ){
273 #ifdef FOSSIL_ENABLE_SSL
274 int sent;
275 while( n>0 ){
276 sent = ssl_send(0, z, n);
277
+159 -35
--- src/info.c
+++ src/info.c
@@ -148,11 +148,10 @@
148148
db_open_config(0);
149149
db_record_repository_filename(g.argv[2]);
150150
db_open_repository(g.argv[2]);
151151
fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
152152
fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
153
- fossil_print("server-code: %s\n", db_get("server-code", "<none>"));
154153
return;
155154
}
156155
db_find_and_open_repository(0,0);
157156
if( g.argc==2 ){
158157
int vid;
@@ -167,11 +166,10 @@
167166
if( g.zHome ){
168167
fossil_print("user-home: %s\n", g.zHome);
169168
}
170169
#endif
171170
fossil_print("project-code: %s\n", db_get("project-code", ""));
172
- fossil_print("server-code: %s\n", db_get("server-code", ""));
173171
vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
174172
if( vid ){
175173
show_common_info(vid, "checkout:", 1, 1);
176174
}
177175
}else{
@@ -270,16 +268,46 @@
270268
content_get(toid, &to);
271269
}else{
272270
blob_zero(&to);
273271
}
274272
blob_zero(&out);
275
- text_diff(&from, &to, &out, 5, 1);
273
+ text_diff(&from, &to, &out, DIFF_IGNORE_EOLWS | 5);
276274
@ %h(blob_str(&out))
277275
blob_reset(&from);
278276
blob_reset(&to);
279277
blob_reset(&out);
280278
}
279
+
280
+
281
+/*
282
+** Write the difference between two RIDs to the output
283
+*/
284
+static void generate_sbsdiff(const char *zFrom, const char *zTo){
285
+ int fromid;
286
+ int toid;
287
+ Blob from, to;
288
+ if( zFrom ){
289
+ fromid = uuid_to_rid(zFrom, 0);
290
+ content_get(fromid, &from);
291
+ }else{
292
+ blob_zero(&from);
293
+ }
294
+ if( zTo ){
295
+ toid = uuid_to_rid(zTo, 0);
296
+ content_get(toid, &to);
297
+ }else{
298
+ blob_zero(&to);
299
+ }
300
+ @ <table class="sbsdiff">
301
+ @ <tr><th colspan="2" class="diffhdr">Old (%S(zFrom))</th><th/>
302
+ @ <th colspan="2" class="diffhdr">New (%S(zTo))</th></tr>
303
+ html_sbsdiff(&from, &to, 5, 1);
304
+ @ </table>
305
+ blob_reset(&from);
306
+ blob_reset(&to);
307
+}
308
+
281309
282310
/*
283311
** Write a line of web-page output that shows changes that have occurred
284312
** to a file between two check-ins.
285313
*/
@@ -287,10 +315,11 @@
287315
const char *zName, /* Name of the file that has changed */
288316
const char *zOld, /* blob.uuid before change. NULL for added files */
289317
const char *zNew, /* blob.uuid after change. NULL for deletes */
290318
const char *zOldName, /* Prior name. NULL if no name change. */
291319
int showDiff, /* Show edit diffs if true */
320
+ int sideBySide, /* Show diffs side-by-side */
292321
int mperm /* executable or symlink permission for zNew */
293322
){
294323
if( !g.perm.History ){
295324
if( zNew==0 ){
296325
@ <p>Deleted %h(zName)</p>
@@ -303,13 +332,17 @@
303332
@ for %h(zName)</p>
304333
}else{
305334
@ <p>Changes to %h(zName)</p>
306335
}
307336
if( showDiff ){
308
- @ <blockquote><pre>
309
- append_diff(zOld, zNew);
310
- @ </pre></blockquote>
337
+ if( sideBySide ){
338
+ generate_sbsdiff(zOld, zNew);
339
+ }else{
340
+ @ <blockquote><pre>
341
+ append_diff(zOld, zNew);
342
+ @ </pre></blockquote>
343
+ }
311344
}
312345
}else{
313346
if( zOld && zNew ){
314347
if( fossil_strcmp(zOld, zNew)!=0 ){
315348
@ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@@ -329,13 +362,17 @@
329362
}else{
330363
@ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
331364
@ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a>
332365
}
333366
if( showDiff ){
334
- @ <blockquote><pre>
335
- append_diff(zOld, zNew);
336
- @ </pre></blockquote>
367
+ if( sideBySide ){
368
+ generate_sbsdiff(zOld, zNew);
369
+ }else{
370
+ @ <blockquote><pre>
371
+ append_diff(zOld, zNew);
372
+ @ </pre></blockquote>
373
+ }
337374
}else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
338375
@ &nbsp;&nbsp;
339376
@ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a>
340377
}
341378
@ </p>
@@ -361,10 +398,11 @@
361398
void ci_page(void){
362399
Stmt q;
363400
int rid;
364401
int isLeaf;
365402
int showDiff;
403
+ int sideBySide;
366404
const char *zName; /* Name of the checkin to be displayed */
367405
const char *zUuid; /* UUID of zName */
368406
const char *zParent; /* UUID of the parent checkin (if any) */
369407
370408
login_check_credentials();
@@ -390,10 +428,11 @@
390428
" FROM blob, event"
391429
" WHERE blob.rid=%d"
392430
" AND event.objid=%d",
393431
rid, rid
394432
);
433
+ sideBySide = atoi(PD("sbs","1"));
395434
if( db_step(&q)==SQLITE_ROW ){
396435
const char *zUuid = db_column_text(&q, 0);
397436
char *zTitle = mprintf("Check-in [%.10s]", zUuid);
398437
char *zEUser, *zEComment;
399438
const char *zUser;
@@ -467,11 +506,11 @@
467506
}
468507
if( !isLeaf ){
469508
@ | <a href="%s(g.zTop)/timeline?d=%S(zUuid)">descendants</a>
470509
}
471510
if( zParent && !isLeaf ){
472
- @ | <a href="%s(g.zTop)/timeline?d=%S(zUuid)&amp;p=%S(zUuid)">both</a>
511
+ @ | <a href="%s(g.zTop)/timeline?dp=%S(zUuid)">both</a>
473512
}
474513
db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
475514
" WHERE rid=%d AND tagtype>0 "
476515
" AND tag.tagid=tagxref.tagid "
477516
" AND +tag.tagname GLOB 'sym-*'", rid);
@@ -506,27 +545,49 @@
506545
}
507546
db_finalize(&q);
508547
showTags(rid, "");
509548
if( zParent ){
510549
@ <div class="section">Changes</div>
550
+ @ <div class="sectionmenu">
511551
showDiff = g.zPath[0]!='c';
512552
if( db_get_boolean("show-version-diffs", 0)==0 ){
513553
showDiff = !showDiff;
514554
if( showDiff ){
515
- @ <a href="%s(g.zTop)/vinfo/%T(zName)">[hide&nbsp;diffs]</a>
555
+ @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)">
556
+ @ hide&nbsp;diffs</a>
557
+ if( sideBySide ){
558
+ @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=0">
559
+ @ unified&nbsp;diffs</a>
560
+ }else{
561
+ @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=1">
562
+ @ side-by-side&nbsp;diffs</a>
563
+ }
516564
}else{
517
- @ <a href="%s(g.zTop)/ci/%T(zName)">[show&nbsp;diffs]</a>
565
+ @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=0">
566
+ @ show&nbsp;unified&nbsp;diffs</a>
567
+ @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=1">
568
+ @ show&nbsp;side-by-side&nbsp;diffs</a>
518569
}
519570
}else{
520571
if( showDiff ){
521
- @ <a href="%s(g.zTop)/ci/%T(zName)">[hide&nbsp;diffs]</a>
572
+ @ <a class="button" href="%s(g.zTop)/ci/%T(zName)">hide&nbsp;diffs</a>
573
+ if( sideBySide ){
574
+ @ <a class="button" href="%s(g.zTop)/info/%T(zName)?sbs=0">
575
+ @ unified&nbsp;diffs</a>
576
+ }else{
577
+ @ <a class="button" href="%s(g.zTop)/info/%T(zName)?sbs=1">
578
+ @ side-by-side&nbsp;diffs</a>
579
+ }
522580
}else{
523
- @ <a href="%s(g.zTop)/vinfo/%T(zName)">[show&nbsp;diffs]</a>
581
+ @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)?sbs=0">
582
+ @ show&nbsp;unified&nbsp;diffs</a>
583
+ @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)?sbs=1">
584
+ @ show&nbsp;side-by-side&nbsp;diffs</a>
524585
}
525586
}
526
- @ &nbsp;&nbsp;
527
- @ <a href="%s(g.zTop)/vpatch?from=%S(zParent)&to=%S(zUuid)">[patch]</a><br/>
587
+ @ <a class="button" href="%s(g.zTop)/vpatch?from=%S(zParent)&to=%S(zUuid)">
588
+ @ patch</a></div>
528589
db_prepare(&q,
529590
"SELECT name,"
530591
" mperm,"
531592
" (SELECT uuid FROM blob WHERE rid=mlink.pid),"
532593
" (SELECT uuid FROM blob WHERE rid=mlink.fid),"
@@ -540,11 +601,12 @@
540601
const char *zName = db_column_text(&q,0);
541602
int mperm = db_column_int(&q, 1);
542603
const char *zOld = db_column_text(&q,2);
543604
const char *zNew = db_column_text(&q,3);
544605
const char *zOldName = db_column_text(&q, 4);
545
- append_file_change_line(zName, zOld, zNew, zOldName, showDiff, mperm);
606
+ append_file_change_line(zName, zOld, zNew, zOldName, showDiff,
607
+ sideBySide, mperm);
546608
}
547609
db_finalize(&q);
548610
}
549611
style_footer();
550612
}
@@ -690,17 +752,18 @@
690752
}
691753
692754
693755
/*
694756
** WEBPAGE: vdiff
695
-** URL: /vdiff?from=UUID&amp;to=UUID&amp;detail=BOOLEAN
757
+** URL: /vdiff?from=UUID&amp;to=UUID&amp;detail=BOOLEAN;sbs=BOOLEAN
696758
**
697759
** Show all differences between two checkins.
698760
*/
699761
void vdiff_page(void){
700762
int ridFrom, ridTo;
701763
int showDetail = 0;
764
+ int sideBySide = 0;
702765
Manifest *pFrom, *pTo;
703766
ManifestFile *pFileFrom, *pFileTo;
704767
705768
login_check_credentials();
706769
if( !g.perm.Read ){ login_needed(); return; }
@@ -709,10 +772,20 @@
709772
pFrom = vdiff_parse_manifest("from", &ridFrom);
710773
if( pFrom==0 ) return;
711774
pTo = vdiff_parse_manifest("to", &ridTo);
712775
if( pTo==0 ) return;
713776
showDetail = atoi(PD("detail","0"));
777
+ sideBySide = atoi(PD("sbs","1"));
778
+ if( !sideBySide ){
779
+ style_submenu_element("Side-by-side Diff", "sbsdiff",
780
+ "%s/vdiff?from=%T&to=%T&detail=%d&sbs=1",
781
+ g.zTop, P("from"), P("to"), showDetail);
782
+ }else{
783
+ style_submenu_element("Unified Diff", "udiff",
784
+ "%s/vdiff?from=%T&to=%T&detail=%d&sbs=0",
785
+ g.zTop, P("from"), P("to"), showDetail);
786
+ }
714787
style_header("Check-in Differences");
715788
@ <h2>Difference From:</h2><blockquote>
716789
checkin_description(ridFrom);
717790
@ </blockquote><h2>To:</h2><blockquote>
718791
checkin_description(ridTo);
@@ -731,25 +804,25 @@
731804
}else{
732805
cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
733806
}
734807
if( cmp<0 ){
735808
append_file_change_line(pFileFrom->zName,
736
- pFileFrom->zUuid, 0, 0, 0, 0);
809
+ pFileFrom->zUuid, 0, 0, 0, 0, 0);
737810
pFileFrom = manifest_file_next(pFrom, 0);
738811
}else if( cmp>0 ){
739812
append_file_change_line(pFileTo->zName,
740
- 0, pFileTo->zUuid, 0, 0,
813
+ 0, pFileTo->zUuid, 0, 0, 0,
741814
manifest_file_mperm(pFileTo));
742815
pFileTo = manifest_file_next(pTo, 0);
743816
}else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
744817
/* No changes */
745818
pFileFrom = manifest_file_next(pFrom, 0);
746819
pFileTo = manifest_file_next(pTo, 0);
747820
}else{
748821
append_file_change_line(pFileFrom->zName,
749822
pFileFrom->zUuid,
750
- pFileTo->zUuid, 0, showDetail,
823
+ pFileTo->zUuid, 0, showDetail, sideBySide,
751824
manifest_file_mperm(pFileTo));
752825
pFileFrom = manifest_file_next(pFrom, 0);
753826
pFileTo = manifest_file_next(pTo, 0);
754827
}
755828
}
@@ -983,28 +1056,30 @@
9831056
}
9841057
9851058
9861059
/*
9871060
** WEBPAGE: fdiff
988
-** URL: fdiff?v1=UUID&v2=UUID&patch
1061
+** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN
9891062
**
990
-** Two arguments, v1 and v2, identify the files to be diffed. Show the
991
-** difference between the two artifacts. Generate plaintext if "patch"
992
-** is present.
1063
+** Two arguments, v1 and v2, identify the files to be diffed. Show the
1064
+** difference between the two artifacts. Show diff side by side unless sbs
1065
+** is 0. Generate plaintext if "patch" is present.
9931066
*/
9941067
void diff_page(void){
9951068
int v1, v2;
9961069
int isPatch;
1070
+ int sideBySide;
9971071
Blob c1, c2, diff, *pOut;
9981072
char *zV1;
9991073
char *zV2;
10001074
10011075
login_check_credentials();
10021076
if( !g.perm.Read ){ login_needed(); return; }
10031077
v1 = name_to_rid_www("v1");
10041078
v2 = name_to_rid_www("v2");
10051079
if( v1==0 || v2==0 ) fossil_redirect_home();
1080
+ sideBySide = atoi(PD("sbs","1"));
10061081
zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
10071082
zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
10081083
isPatch = P("patch")!=0;
10091084
if( isPatch ){
10101085
pOut = cgi_output_blob();
@@ -1011,28 +1086,44 @@
10111086
cgi_set_content_type("text/plain");
10121087
}else{
10131088
blob_zero(&diff);
10141089
pOut = &diff;
10151090
}
1016
- content_get(v1, &c1);
1017
- content_get(v2, &c2);
1018
- text_diff(&c1, &c2, pOut, 4, 1);
1019
- blob_reset(&c1);
1020
- blob_reset(&c2);
1091
+ if( !sideBySide || isPatch ){
1092
+ content_get(v1, &c1);
1093
+ content_get(v2, &c2);
1094
+ text_diff(&c1, &c2, pOut, 4 | 0);
1095
+ blob_reset(&c1);
1096
+ blob_reset(&c2);
1097
+ }
10211098
if( !isPatch ){
10221099
style_header("Diff");
10231100
style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
10241101
g.zTop, P("v1"), P("v2"));
1102
+ if( !sideBySide ){
1103
+ style_submenu_element("Side-by-side Diff", "sbsdiff",
1104
+ "%s/fdiff?v1=%T&v2=%T&sbs=1",
1105
+ g.zTop, P("v1"), P("v2"));
1106
+ }else{
1107
+ style_submenu_element("Unified Diff", "udiff",
1108
+ "%s/fdiff?v1=%T&v2=%T&sbs=0",
1109
+ g.zTop, P("v1"), P("v2"));
1110
+ }
1111
+
10251112
@ <h2>Differences From
10261113
@ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2>
10271114
object_description(v1, 0, 0);
10281115
@ <h2>To Artifact <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2>
10291116
object_description(v2, 0, 0);
10301117
@ <hr />
1031
- @ <blockquote><pre>
1032
- @ %h(blob_str(&diff))
1033
- @ </pre></blockquote>
1118
+ if( sideBySide ){
1119
+ generate_sbsdiff(zV1, zV2);
1120
+ }else{
1121
+ @ <blockquote><pre>
1122
+ @ %h(blob_str(&diff))
1123
+ @ </pre></blockquote>
1124
+ }
10341125
blob_reset(&diff);
10351126
style_footer();
10361127
}
10371128
}
10381129
@@ -1584,10 +1675,42 @@
15841675
@ value="%h(stdClrFound?"":zDefaultColor)" />
15851676
@ </td>
15861677
@ </tr>
15871678
@ </table>
15881679
}
1680
+
1681
+/*
1682
+** Do a comment comparison.
1683
+**
1684
+** + Leading and trailing whitespace are ignored.
1685
+** + \r\n characters compare equal to \n
1686
+**
1687
+** Return true if equal and false if not equal.
1688
+*/
1689
+static int comment_compare(const char *zA, const char *zB){
1690
+ if( zA==0 ) zA = "";
1691
+ if( zB==0 ) zB = "";
1692
+ while( fossil_isspace(zA[0]) ) zA++;
1693
+ while( fossil_isspace(zB[0]) ) zB++;
1694
+ while( zA[0] && zB[0] ){
1695
+ if( zA[0]==zB[0] ){ zA++; zB++; continue; }
1696
+ if( zA[0]=='\r' && zA[1]=='\n' && zB[0]=='\n' ){
1697
+ zA += 2;
1698
+ zB++;
1699
+ continue;
1700
+ }
1701
+ if( zB[0]=='\r' && zB[1]=='\n' && zA[0]=='\n' ){
1702
+ zB += 2;
1703
+ zA++;
1704
+ continue;
1705
+ }
1706
+ return 0;
1707
+ }
1708
+ while( fossil_isspace(zB[0]) ) zB++;
1709
+ while( fossil_isspace(zA[0]) ) zA++;
1710
+ return zA[0]==0 && zB[0]==0;
1711
+}
15891712
15901713
/*
15911714
** WEBPAGE: ci_edit
15921715
** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
15931716
**
@@ -1661,11 +1784,12 @@
16611784
blob_zero(&ctrl);
16621785
zNow = date_in_standard_format("now");
16631786
blob_appendf(&ctrl, "D %s\n", zNow);
16641787
db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
16651788
if( zNewColor[0]
1666
- && (fPropagateColor!=fNewPropagateColor || fossil_strcmp(zColor,zNewColor)!=0)
1789
+ && (fPropagateColor!=fNewPropagateColor
1790
+ || fossil_strcmp(zColor,zNewColor)!=0)
16671791
){
16681792
char *zPrefix = "+";
16691793
if( fNewPropagateColor ){
16701794
zPrefix = "*";
16711795
}
@@ -1673,11 +1797,11 @@
16731797
zPrefix, zNewColor);
16741798
}
16751799
if( zNewColor[0]==0 && zColor[0]!=0 ){
16761800
db_multi_exec("REPLACE INTO newtags VALUES('bgcolor','-',NULL)");
16771801
}
1678
- if( fossil_strcmp(zComment,zNewComment)!=0 ){
1802
+ if( comment_compare(zComment,zNewComment)==0 ){
16791803
db_multi_exec("REPLACE INTO newtags VALUES('comment','+',%Q)",
16801804
zNewComment);
16811805
}
16821806
if( fossil_strcmp(zDate,zNewDate)!=0 ){
16831807
db_multi_exec("REPLACE INTO newtags VALUES('date','+',%Q)",
16841808
16851809
ADDED src/json.c
16861810
ADDED src/json_artifact.c
16871811
ADDED src/json_branch.c
16881812
ADDED src/json_detail.h
16891813
ADDED src/json_diff.c
16901814
ADDED src/json_login.c
16911815
ADDED src/json_query.c
16921816
ADDED src/json_report.c
16931817
ADDED src/json_tag.c
16941818
ADDED src/json_timeline.c
16951819
ADDED src/json_user.c
16961820
ADDED src/json_wiki.c
--- src/info.c
+++ src/info.c
@@ -148,11 +148,10 @@
148 db_open_config(0);
149 db_record_repository_filename(g.argv[2]);
150 db_open_repository(g.argv[2]);
151 fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
152 fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
153 fossil_print("server-code: %s\n", db_get("server-code", "<none>"));
154 return;
155 }
156 db_find_and_open_repository(0,0);
157 if( g.argc==2 ){
158 int vid;
@@ -167,11 +166,10 @@
167 if( g.zHome ){
168 fossil_print("user-home: %s\n", g.zHome);
169 }
170 #endif
171 fossil_print("project-code: %s\n", db_get("project-code", ""));
172 fossil_print("server-code: %s\n", db_get("server-code", ""));
173 vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
174 if( vid ){
175 show_common_info(vid, "checkout:", 1, 1);
176 }
177 }else{
@@ -270,16 +268,46 @@
270 content_get(toid, &to);
271 }else{
272 blob_zero(&to);
273 }
274 blob_zero(&out);
275 text_diff(&from, &to, &out, 5, 1);
276 @ %h(blob_str(&out))
277 blob_reset(&from);
278 blob_reset(&to);
279 blob_reset(&out);
280 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
282 /*
283 ** Write a line of web-page output that shows changes that have occurred
284 ** to a file between two check-ins.
285 */
@@ -287,10 +315,11 @@
287 const char *zName, /* Name of the file that has changed */
288 const char *zOld, /* blob.uuid before change. NULL for added files */
289 const char *zNew, /* blob.uuid after change. NULL for deletes */
290 const char *zOldName, /* Prior name. NULL if no name change. */
291 int showDiff, /* Show edit diffs if true */
 
292 int mperm /* executable or symlink permission for zNew */
293 ){
294 if( !g.perm.History ){
295 if( zNew==0 ){
296 @ <p>Deleted %h(zName)</p>
@@ -303,13 +332,17 @@
303 @ for %h(zName)</p>
304 }else{
305 @ <p>Changes to %h(zName)</p>
306 }
307 if( showDiff ){
308 @ <blockquote><pre>
309 append_diff(zOld, zNew);
310 @ </pre></blockquote>
 
 
 
 
311 }
312 }else{
313 if( zOld && zNew ){
314 if( fossil_strcmp(zOld, zNew)!=0 ){
315 @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@@ -329,13 +362,17 @@
329 }else{
330 @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
331 @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a>
332 }
333 if( showDiff ){
334 @ <blockquote><pre>
335 append_diff(zOld, zNew);
336 @ </pre></blockquote>
 
 
 
 
337 }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
338 @ &nbsp;&nbsp;
339 @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a>
340 }
341 @ </p>
@@ -361,10 +398,11 @@
361 void ci_page(void){
362 Stmt q;
363 int rid;
364 int isLeaf;
365 int showDiff;
 
366 const char *zName; /* Name of the checkin to be displayed */
367 const char *zUuid; /* UUID of zName */
368 const char *zParent; /* UUID of the parent checkin (if any) */
369
370 login_check_credentials();
@@ -390,10 +428,11 @@
390 " FROM blob, event"
391 " WHERE blob.rid=%d"
392 " AND event.objid=%d",
393 rid, rid
394 );
 
395 if( db_step(&q)==SQLITE_ROW ){
396 const char *zUuid = db_column_text(&q, 0);
397 char *zTitle = mprintf("Check-in [%.10s]", zUuid);
398 char *zEUser, *zEComment;
399 const char *zUser;
@@ -467,11 +506,11 @@
467 }
468 if( !isLeaf ){
469 @ | <a href="%s(g.zTop)/timeline?d=%S(zUuid)">descendants</a>
470 }
471 if( zParent && !isLeaf ){
472 @ | <a href="%s(g.zTop)/timeline?d=%S(zUuid)&amp;p=%S(zUuid)">both</a>
473 }
474 db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
475 " WHERE rid=%d AND tagtype>0 "
476 " AND tag.tagid=tagxref.tagid "
477 " AND +tag.tagname GLOB 'sym-*'", rid);
@@ -506,27 +545,49 @@
506 }
507 db_finalize(&q);
508 showTags(rid, "");
509 if( zParent ){
510 @ <div class="section">Changes</div>
 
511 showDiff = g.zPath[0]!='c';
512 if( db_get_boolean("show-version-diffs", 0)==0 ){
513 showDiff = !showDiff;
514 if( showDiff ){
515 @ <a href="%s(g.zTop)/vinfo/%T(zName)">[hide&nbsp;diffs]</a>
 
 
 
 
 
 
 
 
516 }else{
517 @ <a href="%s(g.zTop)/ci/%T(zName)">[show&nbsp;diffs]</a>
 
 
 
518 }
519 }else{
520 if( showDiff ){
521 @ <a href="%s(g.zTop)/ci/%T(zName)">[hide&nbsp;diffs]</a>
 
 
 
 
 
 
 
522 }else{
523 @ <a href="%s(g.zTop)/vinfo/%T(zName)">[show&nbsp;diffs]</a>
 
 
 
524 }
525 }
526 @ &nbsp;&nbsp;
527 @ <a href="%s(g.zTop)/vpatch?from=%S(zParent)&to=%S(zUuid)">[patch]</a><br/>
528 db_prepare(&q,
529 "SELECT name,"
530 " mperm,"
531 " (SELECT uuid FROM blob WHERE rid=mlink.pid),"
532 " (SELECT uuid FROM blob WHERE rid=mlink.fid),"
@@ -540,11 +601,12 @@
540 const char *zName = db_column_text(&q,0);
541 int mperm = db_column_int(&q, 1);
542 const char *zOld = db_column_text(&q,2);
543 const char *zNew = db_column_text(&q,3);
544 const char *zOldName = db_column_text(&q, 4);
545 append_file_change_line(zName, zOld, zNew, zOldName, showDiff, mperm);
 
546 }
547 db_finalize(&q);
548 }
549 style_footer();
550 }
@@ -690,17 +752,18 @@
690 }
691
692
693 /*
694 ** WEBPAGE: vdiff
695 ** URL: /vdiff?from=UUID&amp;to=UUID&amp;detail=BOOLEAN
696 **
697 ** Show all differences between two checkins.
698 */
699 void vdiff_page(void){
700 int ridFrom, ridTo;
701 int showDetail = 0;
 
702 Manifest *pFrom, *pTo;
703 ManifestFile *pFileFrom, *pFileTo;
704
705 login_check_credentials();
706 if( !g.perm.Read ){ login_needed(); return; }
@@ -709,10 +772,20 @@
709 pFrom = vdiff_parse_manifest("from", &ridFrom);
710 if( pFrom==0 ) return;
711 pTo = vdiff_parse_manifest("to", &ridTo);
712 if( pTo==0 ) return;
713 showDetail = atoi(PD("detail","0"));
 
 
 
 
 
 
 
 
 
 
714 style_header("Check-in Differences");
715 @ <h2>Difference From:</h2><blockquote>
716 checkin_description(ridFrom);
717 @ </blockquote><h2>To:</h2><blockquote>
718 checkin_description(ridTo);
@@ -731,25 +804,25 @@
731 }else{
732 cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
733 }
734 if( cmp<0 ){
735 append_file_change_line(pFileFrom->zName,
736 pFileFrom->zUuid, 0, 0, 0, 0);
737 pFileFrom = manifest_file_next(pFrom, 0);
738 }else if( cmp>0 ){
739 append_file_change_line(pFileTo->zName,
740 0, pFileTo->zUuid, 0, 0,
741 manifest_file_mperm(pFileTo));
742 pFileTo = manifest_file_next(pTo, 0);
743 }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
744 /* No changes */
745 pFileFrom = manifest_file_next(pFrom, 0);
746 pFileTo = manifest_file_next(pTo, 0);
747 }else{
748 append_file_change_line(pFileFrom->zName,
749 pFileFrom->zUuid,
750 pFileTo->zUuid, 0, showDetail,
751 manifest_file_mperm(pFileTo));
752 pFileFrom = manifest_file_next(pFrom, 0);
753 pFileTo = manifest_file_next(pTo, 0);
754 }
755 }
@@ -983,28 +1056,30 @@
983 }
984
985
986 /*
987 ** WEBPAGE: fdiff
988 ** URL: fdiff?v1=UUID&v2=UUID&patch
989 **
990 ** Two arguments, v1 and v2, identify the files to be diffed. Show the
991 ** difference between the two artifacts. Generate plaintext if "patch"
992 ** is present.
993 */
994 void diff_page(void){
995 int v1, v2;
996 int isPatch;
 
997 Blob c1, c2, diff, *pOut;
998 char *zV1;
999 char *zV2;
1000
1001 login_check_credentials();
1002 if( !g.perm.Read ){ login_needed(); return; }
1003 v1 = name_to_rid_www("v1");
1004 v2 = name_to_rid_www("v2");
1005 if( v1==0 || v2==0 ) fossil_redirect_home();
 
1006 zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
1007 zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
1008 isPatch = P("patch")!=0;
1009 if( isPatch ){
1010 pOut = cgi_output_blob();
@@ -1011,28 +1086,44 @@
1011 cgi_set_content_type("text/plain");
1012 }else{
1013 blob_zero(&diff);
1014 pOut = &diff;
1015 }
1016 content_get(v1, &c1);
1017 content_get(v2, &c2);
1018 text_diff(&c1, &c2, pOut, 4, 1);
1019 blob_reset(&c1);
1020 blob_reset(&c2);
 
 
1021 if( !isPatch ){
1022 style_header("Diff");
1023 style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
1024 g.zTop, P("v1"), P("v2"));
 
 
 
 
 
 
 
 
 
 
1025 @ <h2>Differences From
1026 @ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2>
1027 object_description(v1, 0, 0);
1028 @ <h2>To Artifact <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2>
1029 object_description(v2, 0, 0);
1030 @ <hr />
1031 @ <blockquote><pre>
1032 @ %h(blob_str(&diff))
1033 @ </pre></blockquote>
 
 
 
 
1034 blob_reset(&diff);
1035 style_footer();
1036 }
1037 }
1038
@@ -1584,10 +1675,42 @@
1584 @ value="%h(stdClrFound?"":zDefaultColor)" />
1585 @ </td>
1586 @ </tr>
1587 @ </table>
1588 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1589
1590 /*
1591 ** WEBPAGE: ci_edit
1592 ** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
1593 **
@@ -1661,11 +1784,12 @@
1661 blob_zero(&ctrl);
1662 zNow = date_in_standard_format("now");
1663 blob_appendf(&ctrl, "D %s\n", zNow);
1664 db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
1665 if( zNewColor[0]
1666 && (fPropagateColor!=fNewPropagateColor || fossil_strcmp(zColor,zNewColor)!=0)
 
1667 ){
1668 char *zPrefix = "+";
1669 if( fNewPropagateColor ){
1670 zPrefix = "*";
1671 }
@@ -1673,11 +1797,11 @@
1673 zPrefix, zNewColor);
1674 }
1675 if( zNewColor[0]==0 && zColor[0]!=0 ){
1676 db_multi_exec("REPLACE INTO newtags VALUES('bgcolor','-',NULL)");
1677 }
1678 if( fossil_strcmp(zComment,zNewComment)!=0 ){
1679 db_multi_exec("REPLACE INTO newtags VALUES('comment','+',%Q)",
1680 zNewComment);
1681 }
1682 if( fossil_strcmp(zDate,zNewDate)!=0 ){
1683 db_multi_exec("REPLACE INTO newtags VALUES('date','+',%Q)",
1684
1685 DDED src/json.c
1686 DDED src/json_artifact.c
1687 DDED src/json_branch.c
1688 DDED src/json_detail.h
1689 DDED src/json_diff.c
1690 DDED src/json_login.c
1691 DDED src/json_query.c
1692 DDED src/json_report.c
1693 DDED src/json_tag.c
1694 DDED src/json_timeline.c
1695 DDED src/json_user.c
1696 DDED src/json_wiki.c
--- src/info.c
+++ src/info.c
@@ -148,11 +148,10 @@
148 db_open_config(0);
149 db_record_repository_filename(g.argv[2]);
150 db_open_repository(g.argv[2]);
151 fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
152 fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
 
153 return;
154 }
155 db_find_and_open_repository(0,0);
156 if( g.argc==2 ){
157 int vid;
@@ -167,11 +166,10 @@
166 if( g.zHome ){
167 fossil_print("user-home: %s\n", g.zHome);
168 }
169 #endif
170 fossil_print("project-code: %s\n", db_get("project-code", ""));
 
171 vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
172 if( vid ){
173 show_common_info(vid, "checkout:", 1, 1);
174 }
175 }else{
@@ -270,16 +268,46 @@
268 content_get(toid, &to);
269 }else{
270 blob_zero(&to);
271 }
272 blob_zero(&out);
273 text_diff(&from, &to, &out, DIFF_IGNORE_EOLWS | 5);
274 @ %h(blob_str(&out))
275 blob_reset(&from);
276 blob_reset(&to);
277 blob_reset(&out);
278 }
279
280
281 /*
282 ** Write the difference between two RIDs to the output
283 */
284 static void generate_sbsdiff(const char *zFrom, const char *zTo){
285 int fromid;
286 int toid;
287 Blob from, to;
288 if( zFrom ){
289 fromid = uuid_to_rid(zFrom, 0);
290 content_get(fromid, &from);
291 }else{
292 blob_zero(&from);
293 }
294 if( zTo ){
295 toid = uuid_to_rid(zTo, 0);
296 content_get(toid, &to);
297 }else{
298 blob_zero(&to);
299 }
300 @ <table class="sbsdiff">
301 @ <tr><th colspan="2" class="diffhdr">Old (%S(zFrom))</th><th/>
302 @ <th colspan="2" class="diffhdr">New (%S(zTo))</th></tr>
303 html_sbsdiff(&from, &to, 5, 1);
304 @ </table>
305 blob_reset(&from);
306 blob_reset(&to);
307 }
308
309
310 /*
311 ** Write a line of web-page output that shows changes that have occurred
312 ** to a file between two check-ins.
313 */
@@ -287,10 +315,11 @@
315 const char *zName, /* Name of the file that has changed */
316 const char *zOld, /* blob.uuid before change. NULL for added files */
317 const char *zNew, /* blob.uuid after change. NULL for deletes */
318 const char *zOldName, /* Prior name. NULL if no name change. */
319 int showDiff, /* Show edit diffs if true */
320 int sideBySide, /* Show diffs side-by-side */
321 int mperm /* executable or symlink permission for zNew */
322 ){
323 if( !g.perm.History ){
324 if( zNew==0 ){
325 @ <p>Deleted %h(zName)</p>
@@ -303,13 +332,17 @@
332 @ for %h(zName)</p>
333 }else{
334 @ <p>Changes to %h(zName)</p>
335 }
336 if( showDiff ){
337 if( sideBySide ){
338 generate_sbsdiff(zOld, zNew);
339 }else{
340 @ <blockquote><pre>
341 append_diff(zOld, zNew);
342 @ </pre></blockquote>
343 }
344 }
345 }else{
346 if( zOld && zNew ){
347 if( fossil_strcmp(zOld, zNew)!=0 ){
348 @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
@@ -329,13 +362,17 @@
362 }else{
363 @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a>
364 @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a>
365 }
366 if( showDiff ){
367 if( sideBySide ){
368 generate_sbsdiff(zOld, zNew);
369 }else{
370 @ <blockquote><pre>
371 append_diff(zOld, zNew);
372 @ </pre></blockquote>
373 }
374 }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
375 @ &nbsp;&nbsp;
376 @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a>
377 }
378 @ </p>
@@ -361,10 +398,11 @@
398 void ci_page(void){
399 Stmt q;
400 int rid;
401 int isLeaf;
402 int showDiff;
403 int sideBySide;
404 const char *zName; /* Name of the checkin to be displayed */
405 const char *zUuid; /* UUID of zName */
406 const char *zParent; /* UUID of the parent checkin (if any) */
407
408 login_check_credentials();
@@ -390,10 +428,11 @@
428 " FROM blob, event"
429 " WHERE blob.rid=%d"
430 " AND event.objid=%d",
431 rid, rid
432 );
433 sideBySide = atoi(PD("sbs","1"));
434 if( db_step(&q)==SQLITE_ROW ){
435 const char *zUuid = db_column_text(&q, 0);
436 char *zTitle = mprintf("Check-in [%.10s]", zUuid);
437 char *zEUser, *zEComment;
438 const char *zUser;
@@ -467,11 +506,11 @@
506 }
507 if( !isLeaf ){
508 @ | <a href="%s(g.zTop)/timeline?d=%S(zUuid)">descendants</a>
509 }
510 if( zParent && !isLeaf ){
511 @ | <a href="%s(g.zTop)/timeline?dp=%S(zUuid)">both</a>
512 }
513 db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag "
514 " WHERE rid=%d AND tagtype>0 "
515 " AND tag.tagid=tagxref.tagid "
516 " AND +tag.tagname GLOB 'sym-*'", rid);
@@ -506,27 +545,49 @@
545 }
546 db_finalize(&q);
547 showTags(rid, "");
548 if( zParent ){
549 @ <div class="section">Changes</div>
550 @ <div class="sectionmenu">
551 showDiff = g.zPath[0]!='c';
552 if( db_get_boolean("show-version-diffs", 0)==0 ){
553 showDiff = !showDiff;
554 if( showDiff ){
555 @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)">
556 @ hide&nbsp;diffs</a>
557 if( sideBySide ){
558 @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=0">
559 @ unified&nbsp;diffs</a>
560 }else{
561 @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=1">
562 @ side-by-side&nbsp;diffs</a>
563 }
564 }else{
565 @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=0">
566 @ show&nbsp;unified&nbsp;diffs</a>
567 @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=1">
568 @ show&nbsp;side-by-side&nbsp;diffs</a>
569 }
570 }else{
571 if( showDiff ){
572 @ <a class="button" href="%s(g.zTop)/ci/%T(zName)">hide&nbsp;diffs</a>
573 if( sideBySide ){
574 @ <a class="button" href="%s(g.zTop)/info/%T(zName)?sbs=0">
575 @ unified&nbsp;diffs</a>
576 }else{
577 @ <a class="button" href="%s(g.zTop)/info/%T(zName)?sbs=1">
578 @ side-by-side&nbsp;diffs</a>
579 }
580 }else{
581 @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)?sbs=0">
582 @ show&nbsp;unified&nbsp;diffs</a>
583 @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)?sbs=1">
584 @ show&nbsp;side-by-side&nbsp;diffs</a>
585 }
586 }
587 @ <a class="button" href="%s(g.zTop)/vpatch?from=%S(zParent)&to=%S(zUuid)">
588 @ patch</a></div>
589 db_prepare(&q,
590 "SELECT name,"
591 " mperm,"
592 " (SELECT uuid FROM blob WHERE rid=mlink.pid),"
593 " (SELECT uuid FROM blob WHERE rid=mlink.fid),"
@@ -540,11 +601,12 @@
601 const char *zName = db_column_text(&q,0);
602 int mperm = db_column_int(&q, 1);
603 const char *zOld = db_column_text(&q,2);
604 const char *zNew = db_column_text(&q,3);
605 const char *zOldName = db_column_text(&q, 4);
606 append_file_change_line(zName, zOld, zNew, zOldName, showDiff,
607 sideBySide, mperm);
608 }
609 db_finalize(&q);
610 }
611 style_footer();
612 }
@@ -690,17 +752,18 @@
752 }
753
754
755 /*
756 ** WEBPAGE: vdiff
757 ** URL: /vdiff?from=UUID&amp;to=UUID&amp;detail=BOOLEAN;sbs=BOOLEAN
758 **
759 ** Show all differences between two checkins.
760 */
761 void vdiff_page(void){
762 int ridFrom, ridTo;
763 int showDetail = 0;
764 int sideBySide = 0;
765 Manifest *pFrom, *pTo;
766 ManifestFile *pFileFrom, *pFileTo;
767
768 login_check_credentials();
769 if( !g.perm.Read ){ login_needed(); return; }
@@ -709,10 +772,20 @@
772 pFrom = vdiff_parse_manifest("from", &ridFrom);
773 if( pFrom==0 ) return;
774 pTo = vdiff_parse_manifest("to", &ridTo);
775 if( pTo==0 ) return;
776 showDetail = atoi(PD("detail","0"));
777 sideBySide = atoi(PD("sbs","1"));
778 if( !sideBySide ){
779 style_submenu_element("Side-by-side Diff", "sbsdiff",
780 "%s/vdiff?from=%T&to=%T&detail=%d&sbs=1",
781 g.zTop, P("from"), P("to"), showDetail);
782 }else{
783 style_submenu_element("Unified Diff", "udiff",
784 "%s/vdiff?from=%T&to=%T&detail=%d&sbs=0",
785 g.zTop, P("from"), P("to"), showDetail);
786 }
787 style_header("Check-in Differences");
788 @ <h2>Difference From:</h2><blockquote>
789 checkin_description(ridFrom);
790 @ </blockquote><h2>To:</h2><blockquote>
791 checkin_description(ridTo);
@@ -731,25 +804,25 @@
804 }else{
805 cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName);
806 }
807 if( cmp<0 ){
808 append_file_change_line(pFileFrom->zName,
809 pFileFrom->zUuid, 0, 0, 0, 0, 0);
810 pFileFrom = manifest_file_next(pFrom, 0);
811 }else if( cmp>0 ){
812 append_file_change_line(pFileTo->zName,
813 0, pFileTo->zUuid, 0, 0, 0,
814 manifest_file_mperm(pFileTo));
815 pFileTo = manifest_file_next(pTo, 0);
816 }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){
817 /* No changes */
818 pFileFrom = manifest_file_next(pFrom, 0);
819 pFileTo = manifest_file_next(pTo, 0);
820 }else{
821 append_file_change_line(pFileFrom->zName,
822 pFileFrom->zUuid,
823 pFileTo->zUuid, 0, showDetail, sideBySide,
824 manifest_file_mperm(pFileTo));
825 pFileFrom = manifest_file_next(pFrom, 0);
826 pFileTo = manifest_file_next(pTo, 0);
827 }
828 }
@@ -983,28 +1056,30 @@
1056 }
1057
1058
1059 /*
1060 ** WEBPAGE: fdiff
1061 ** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN
1062 **
1063 ** Two arguments, v1 and v2, identify the files to be diffed. Show the
1064 ** difference between the two artifacts. Show diff side by side unless sbs
1065 ** is 0. Generate plaintext if "patch" is present.
1066 */
1067 void diff_page(void){
1068 int v1, v2;
1069 int isPatch;
1070 int sideBySide;
1071 Blob c1, c2, diff, *pOut;
1072 char *zV1;
1073 char *zV2;
1074
1075 login_check_credentials();
1076 if( !g.perm.Read ){ login_needed(); return; }
1077 v1 = name_to_rid_www("v1");
1078 v2 = name_to_rid_www("v2");
1079 if( v1==0 || v2==0 ) fossil_redirect_home();
1080 sideBySide = atoi(PD("sbs","1"));
1081 zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
1082 zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
1083 isPatch = P("patch")!=0;
1084 if( isPatch ){
1085 pOut = cgi_output_blob();
@@ -1011,28 +1086,44 @@
1086 cgi_set_content_type("text/plain");
1087 }else{
1088 blob_zero(&diff);
1089 pOut = &diff;
1090 }
1091 if( !sideBySide || isPatch ){
1092 content_get(v1, &c1);
1093 content_get(v2, &c2);
1094 text_diff(&c1, &c2, pOut, 4 | 0);
1095 blob_reset(&c1);
1096 blob_reset(&c2);
1097 }
1098 if( !isPatch ){
1099 style_header("Diff");
1100 style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch",
1101 g.zTop, P("v1"), P("v2"));
1102 if( !sideBySide ){
1103 style_submenu_element("Side-by-side Diff", "sbsdiff",
1104 "%s/fdiff?v1=%T&v2=%T&sbs=1",
1105 g.zTop, P("v1"), P("v2"));
1106 }else{
1107 style_submenu_element("Unified Diff", "udiff",
1108 "%s/fdiff?v1=%T&v2=%T&sbs=0",
1109 g.zTop, P("v1"), P("v2"));
1110 }
1111
1112 @ <h2>Differences From
1113 @ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2>
1114 object_description(v1, 0, 0);
1115 @ <h2>To Artifact <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2>
1116 object_description(v2, 0, 0);
1117 @ <hr />
1118 if( sideBySide ){
1119 generate_sbsdiff(zV1, zV2);
1120 }else{
1121 @ <blockquote><pre>
1122 @ %h(blob_str(&diff))
1123 @ </pre></blockquote>
1124 }
1125 blob_reset(&diff);
1126 style_footer();
1127 }
1128 }
1129
@@ -1584,10 +1675,42 @@
1675 @ value="%h(stdClrFound?"":zDefaultColor)" />
1676 @ </td>
1677 @ </tr>
1678 @ </table>
1679 }
1680
1681 /*
1682 ** Do a comment comparison.
1683 **
1684 ** + Leading and trailing whitespace are ignored.
1685 ** + \r\n characters compare equal to \n
1686 **
1687 ** Return true if equal and false if not equal.
1688 */
1689 static int comment_compare(const char *zA, const char *zB){
1690 if( zA==0 ) zA = "";
1691 if( zB==0 ) zB = "";
1692 while( fossil_isspace(zA[0]) ) zA++;
1693 while( fossil_isspace(zB[0]) ) zB++;
1694 while( zA[0] && zB[0] ){
1695 if( zA[0]==zB[0] ){ zA++; zB++; continue; }
1696 if( zA[0]=='\r' && zA[1]=='\n' && zB[0]=='\n' ){
1697 zA += 2;
1698 zB++;
1699 continue;
1700 }
1701 if( zB[0]=='\r' && zB[1]=='\n' && zA[0]=='\n' ){
1702 zB += 2;
1703 zA++;
1704 continue;
1705 }
1706 return 0;
1707 }
1708 while( fossil_isspace(zB[0]) ) zB++;
1709 while( fossil_isspace(zA[0]) ) zA++;
1710 return zA[0]==0 && zB[0]==0;
1711 }
1712
1713 /*
1714 ** WEBPAGE: ci_edit
1715 ** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER
1716 **
@@ -1661,11 +1784,12 @@
1784 blob_zero(&ctrl);
1785 zNow = date_in_standard_format("now");
1786 blob_appendf(&ctrl, "D %s\n", zNow);
1787 db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)");
1788 if( zNewColor[0]
1789 && (fPropagateColor!=fNewPropagateColor
1790 || fossil_strcmp(zColor,zNewColor)!=0)
1791 ){
1792 char *zPrefix = "+";
1793 if( fNewPropagateColor ){
1794 zPrefix = "*";
1795 }
@@ -1673,11 +1797,11 @@
1797 zPrefix, zNewColor);
1798 }
1799 if( zNewColor[0]==0 && zColor[0]!=0 ){
1800 db_multi_exec("REPLACE INTO newtags VALUES('bgcolor','-',NULL)");
1801 }
1802 if( comment_compare(zComment,zNewComment)==0 ){
1803 db_multi_exec("REPLACE INTO newtags VALUES('comment','+',%Q)",
1804 zNewComment);
1805 }
1806 if( fossil_strcmp(zDate,zNewDate)!=0 ){
1807 db_multi_exec("REPLACE INTO newtags VALUES('date','+',%Q)",
1808
1809 DDED src/json.c
1810 DDED src/json_artifact.c
1811 DDED src/json_branch.c
1812 DDED src/json_detail.h
1813 DDED src/json_diff.c
1814 DDED src/json_login.c
1815 DDED src/json_query.c
1816 DDED src/json_report.c
1817 DDED src/json_tag.c
1818 DDED src/json_timeline.c
1819 DDED src/json_user.c
1820 DDED src/json_wiki.c
+57
--- a/src/json.c
+++ b/src/json.c
@@ -0,0 +1,57 @@
1
+ or succis to mFor notes regardinghas_timer(fetch}
2
+ if/* Timer code taken from sqlite3's shell.c, modified slightly.
3
+ FIXME: move the timer into the fossil core API so that we can
4
+ start the timer early on in the app init phase. Right now we're
5
+ just timing the json ops themselves.
6
+*/
7
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL)
8
+#include <sys/time.h>
9
+#include <sys/resource.h>
10
+
11
+/* Saved resourcthe beginning of an operation */
12
+static struct rusage sBegin;
13
+
14
+/*
15
+** Begin timing an operationbeginTimer(void){
16
+ getrusage(R USAGE_SELF, &sBegin);
17
+}
18
+
19
+/* Return the difference of two time_structs in milliseconds */
20
+static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
21
+ return ((pEnd->tv_usec - pStart->tv_usec)*0.001 +
22
+ (double)((pEnd->tv_sec - pStart->tv_sec)*1000.0));
23
+}
24
+
25
+/*
26
+** Print the timing results.
27
+*/
28
+statiDiff(&sBegin.ru_utiu_stime, &sEnd.ru_stime));
29
+#endif
30
+ return timeDiff(&sBegin.ru_utime, &sEnd.ru_utimc double endTimer(void){
31
+ struct rusage sEnd;
32
+ getrusage(RUSAGE_SELF, &sEnd);
33
+#if 0
34
+ printf("CPU Time: user %f sys %f\n",
35
+ timeDiff(&sBegincmr notes regardinghas_timer(fetch}
36
+ if/* Timer code taken from sqlite3's shell.c, modifinghas_timer(fetch}
37
+ if/* Timer!cmd || !*cmdcmroot);root);
38
+ }
39
+0, 0);cm1> 0 /*HTTP1 payload /*
40
+** Internal helpers to manipulate a byte array as a bitset. The B
41
+** argument must be-a array at least (BIT/8+1) bytes long.
42
+** The BIT argument is the bit number to query/set/clear/toggle.
43
+*/
44
+#define BITSET_BYTEFOR(B,BIT) ((B)[ BIT / 8 ])
45
+#define BITSET_SET(B,BIT) ((BITSET_BYTEFOR(B,BIT) |= (0x01 << (BIT%8))),0x01)
46
+#define BITSET_UNSET(B,BIT) ((BITSET_BYTEFOR(B,BIT) &= ~(0x01 << (BIT%8))),0x00)
47
+#define BITSET_GET(B,BIT) ((BITSET_BYTEFOR(B,BIT) & (0x01 << (BIT%8))) ? 0x01 : 0x00)
48
+#define BITSET_TOGGLE(B,BIT) (BITSET_GET(B,BIT) ? (BITSET_UNSET(B,BIT)) : (BITSET_SET(B,BIT)))*/*/
49
+
50
+#undef BITSET_BYTEFOR
51
+#undef BITSET_SET
52
+#undef BITSET_UNSET
53
+#undef BITS*/
54
+tmp =string(MANIFEST, else it
55
+**char const *e json ops themselves.
56
+*/
57
+#if !defined(_WIN32) && !defined(WIN32) & or s
--- a/src/json.c
+++ b/src/json.c
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/json.c
+++ b/src/json.c
@@ -0,0 +1,57 @@
1 or succis to mFor notes regardinghas_timer(fetch}
2 if/* Timer code taken from sqlite3's shell.c, modified slightly.
3 FIXME: move the timer into the fossil core API so that we can
4 start the timer early on in the app init phase. Right now we're
5 just timing the json ops themselves.
6 */
7 #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL)
8 #include <sys/time.h>
9 #include <sys/resource.h>
10
11 /* Saved resourcthe beginning of an operation */
12 static struct rusage sBegin;
13
14 /*
15 ** Begin timing an operationbeginTimer(void){
16 getrusage(R USAGE_SELF, &sBegin);
17 }
18
19 /* Return the difference of two time_structs in milliseconds */
20 static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
21 return ((pEnd->tv_usec - pStart->tv_usec)*0.001 +
22 (double)((pEnd->tv_sec - pStart->tv_sec)*1000.0));
23 }
24
25 /*
26 ** Print the timing results.
27 */
28 statiDiff(&sBegin.ru_utiu_stime, &sEnd.ru_stime));
29 #endif
30 return timeDiff(&sBegin.ru_utime, &sEnd.ru_utimc double endTimer(void){
31 struct rusage sEnd;
32 getrusage(RUSAGE_SELF, &sEnd);
33 #if 0
34 printf("CPU Time: user %f sys %f\n",
35 timeDiff(&sBegincmr notes regardinghas_timer(fetch}
36 if/* Timer code taken from sqlite3's shell.c, modifinghas_timer(fetch}
37 if/* Timer!cmd || !*cmdcmroot);root);
38 }
39 0, 0);cm1> 0 /*HTTP1 payload /*
40 ** Internal helpers to manipulate a byte array as a bitset. The B
41 ** argument must be-a array at least (BIT/8+1) bytes long.
42 ** The BIT argument is the bit number to query/set/clear/toggle.
43 */
44 #define BITSET_BYTEFOR(B,BIT) ((B)[ BIT / 8 ])
45 #define BITSET_SET(B,BIT) ((BITSET_BYTEFOR(B,BIT) |= (0x01 << (BIT%8))),0x01)
46 #define BITSET_UNSET(B,BIT) ((BITSET_BYTEFOR(B,BIT) &= ~(0x01 << (BIT%8))),0x00)
47 #define BITSET_GET(B,BIT) ((BITSET_BYTEFOR(B,BIT) & (0x01 << (BIT%8))) ? 0x01 : 0x00)
48 #define BITSET_TOGGLE(B,BIT) (BITSET_GET(B,BIT) ? (BITSET_UNSET(B,BIT)) : (BITSET_SET(B,BIT)))*/*/
49
50 #undef BITSET_BYTEFOR
51 #undef BITSET_SET
52 #undef BITSET_UNSET
53 #undef BITS*/
54 tmp =string(MANIFEST, else it
55 **char const *e json ops themselves.
56 */
57 #if !defined(_WIN32) && !defined(WIN32) & or s
--- a/src/json_artifact.c
+++ b/src/json_artifact.c
@@ -0,0 +1,2 @@
1
+(2 g.perm.History && ! h' or 'o' access." )else{addContent =;
2
+ /* todo: support format=(raw|html|none) like /wiki/get does. */addContent ? -1 :
--- a/src/json_artifact.c
+++ b/src/json_artifact.c
@@ -0,0 +1,2 @@
 
 
--- a/src/json_artifact.c
+++ b/src/json_artifact.c
@@ -0,0 +1,2 @@
1 (2 g.perm.History && ! h' or 'o' access." )else{addContent =;
2 /* todo: support format=(raw|html|none) like /wiki/get does. */addContent ? -1 :
--- a/src/json_branch.c
+++ b/src/json_branch.c
@@ -0,0 +1,11 @@
1
+#ifdef FOSSIL_ENABLE_JSON
2
+/*
3
+** Copyright (c) 2011 D. Richard Hipp
4
+**
5
+** This program is free software; you can redistribute it and/or
6
+** modify it \nPLQwhich = 0which = -1which = 1which = 0whichxfer_run_common_script(panicpanic("%s\n==0unable to install new manifest"#ifdef FOSSIL_ENABLE_JSON
7
+/*
8
+** Copyright (c) 2011 D. Richard Hipp
9
+**
10
+** This program is free software; you can redistribute it and/or
11
+** modify it \nPLQwhich = 0which = -1which = 1which = 0whichxfer_run_common_script(panicpanic("%s\n444AUTO3
--- a/src/json_branch.c
+++ b/src/json_branch.c
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
--- a/src/json_branch.c
+++ b/src/json_branch.c
@@ -0,0 +1,11 @@
1 #ifdef FOSSIL_ENABLE_JSON
2 /*
3 ** Copyright (c) 2011 D. Richard Hipp
4 **
5 ** This program is free software; you can redistribute it and/or
6 ** modify it \nPLQwhich = 0which = -1which = 1which = 0whichxfer_run_common_script(panicpanic("%s\n==0unable to install new manifest"#ifdef FOSSIL_ENABLE_JSON
7 /*
8 ** Copyright (c) 2011 D. Richard Hipp
9 **
10 ** This program is free software; you can redistribute it and/or
11 ** modify it \nPLQwhich = 0which = -1which = 1which = 0whichxfer_run_common_script(panicpanic("%s\n444AUTO3
--- a/src/json_detail.h
+++ b/src/json_detail.h
@@ -0,0 +1,69 @@
1
+#ifdef FOSSIL_ENABLE_JSON
2
+#if !defined(FOSSIL_JSON_DETAIL_H_INCLUDED)
3
+#define FOSSIL_JSON_DETAIL_(c)ifdef FOSSIL_ENABLE_JSON
4
+#if !defined(FOSSIL_JSON_DETAIL_H_INCLUDED)
5
+#define FOSSIL_JSON_DETAIL_H_INCLUDED
6
+/*
7
+** Copyright (c) 2011 D. Richard Hipp
8
+**
9
+** This program is free software; you can redistribute it and/or
10
+** modify it under the terms of the Simplified BSD License (also
11
+** known as the "2-Clause License" or "FreeBSD License".)
12
+**
13
+** This program is distributed in the hope that it will be useful,
14
+** but without any warranty; without even the implied warranty of
15
+** merchantability or fitness for a particular purpose.
16
+**
17
+** Author contact in
18
+#define FOSSIL_JSON_API_VERSION "20120713"
19
+
20
+/*
21
+** Impl details for the JSON API which need to be shared
22
+** across multiple C files.
23
+*/
24
+
25
+/*
26
+** The "official" list of Fossil/JSON error codes. Their values might
27
+** very well change during initial development but after their first
28
+** public release they must stay stable.
29
+**
30
+** Values must be in the range 1000..9999 for error codes and 1..999
31
+** for warning codes.
32
+**
33
+** Numbers evenly dividable by 100 are "categories", and error codes
34
+** for a given
35
+um FossilJsonCodes ,
36
+FSL_JSON_W_STRING_TO_ARRAY_FAILED /*+4*/,
37
+FSL_JSON_W_TAG_NOT_FOUND /*+5*/,
38
+
39
+FSL_JSON_W_END = 1000,
40
+FSL_JSON_E_GENERIC = 1000,
41
+FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 10#ifdef FOSSIL_ENABLE_JSON
42
+#if !defined(FOSSIL_JSON_DETAIL_H_INCLUDED)
43
+#define FOSSIL_JSON_DETAIL_H_INCLUDED
44
+/*
45
+** Copyright (c) 2011 D. Richard Hipp
46
+**
47
+** This program is free software; you can redistribute it and/or
48
+** modify it under the terms of the Simplified BSD License (also
49
+** known as the "2-Clause License" or "FreeBSD License".)
50
+**
51
+** This program is distributed in the hope that it will be useful,
52
+** but without any warranty; without even the implied warranty of
53
+** merchantability or fitness for a particular purpose.
54
+**
55
+** Author contact information:
56
+** [email protected]
57
+** http://wnclude "cson_amalgamation.h"
58
+
59
+/**
60
+ FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest
61
+ "significant" change to the JSON API (a change in an interface or
62
+ new funcierer then
63
+#endif/*FOSSIL_JSON_DETAIL_(c)ifdef FImplements the /json/wiki fwiki();
64
+
65
+/*
66
+** Implements /json/timeline/wiki and /json/wiki/timelinetimeline_wiki();
67
+
68
+/*
69
+** Implements /json/timeline family of functionstimeline(
--- a/src/json_detail.h
+++ b/src/json_detail.h
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/json_detail.h
+++ b/src/json_detail.h
@@ -0,0 +1,69 @@
1 #ifdef FOSSIL_ENABLE_JSON
2 #if !defined(FOSSIL_JSON_DETAIL_H_INCLUDED)
3 #define FOSSIL_JSON_DETAIL_(c)ifdef FOSSIL_ENABLE_JSON
4 #if !defined(FOSSIL_JSON_DETAIL_H_INCLUDED)
5 #define FOSSIL_JSON_DETAIL_H_INCLUDED
6 /*
7 ** Copyright (c) 2011 D. Richard Hipp
8 **
9 ** This program is free software; you can redistribute it and/or
10 ** modify it under the terms of the Simplified BSD License (also
11 ** known as the "2-Clause License" or "FreeBSD License".)
12 **
13 ** This program is distributed in the hope that it will be useful,
14 ** but without any warranty; without even the implied warranty of
15 ** merchantability or fitness for a particular purpose.
16 **
17 ** Author contact in
18 #define FOSSIL_JSON_API_VERSION "20120713"
19
20 /*
21 ** Impl details for the JSON API which need to be shared
22 ** across multiple C files.
23 */
24
25 /*
26 ** The "official" list of Fossil/JSON error codes. Their values might
27 ** very well change during initial development but after their first
28 ** public release they must stay stable.
29 **
30 ** Values must be in the range 1000..9999 for error codes and 1..999
31 ** for warning codes.
32 **
33 ** Numbers evenly dividable by 100 are "categories", and error codes
34 ** for a given
35 um FossilJsonCodes ,
36 FSL_JSON_W_STRING_TO_ARRAY_FAILED /*+4*/,
37 FSL_JSON_W_TAG_NOT_FOUND /*+5*/,
38
39 FSL_JSON_W_END = 1000,
40 FSL_JSON_E_GENERIC = 1000,
41 FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 10#ifdef FOSSIL_ENABLE_JSON
42 #if !defined(FOSSIL_JSON_DETAIL_H_INCLUDED)
43 #define FOSSIL_JSON_DETAIL_H_INCLUDED
44 /*
45 ** Copyright (c) 2011 D. Richard Hipp
46 **
47 ** This program is free software; you can redistribute it and/or
48 ** modify it under the terms of the Simplified BSD License (also
49 ** known as the "2-Clause License" or "FreeBSD License".)
50 **
51 ** This program is distributed in the hope that it will be useful,
52 ** but without any warranty; without even the implied warranty of
53 ** merchantability or fitness for a particular purpose.
54 **
55 ** Author contact information:
56 ** [email protected]
57 ** http://wnclude "cson_amalgamation.h"
58
59 /**
60 FOSSIL_JSON_API_VERSION holds the date (YYYYMMDD) of the latest
61 "significant" change to the JSON API (a change in an interface or
62 new funcierer then
63 #endif/*FOSSIL_JSON_DETAIL_(c)ifdef FImplements the /json/wiki fwiki();
64
65 /*
66 ** Implements /json/timeline/wiki and /json/wiki/timelinetimeline_wiki();
67
68 /*
69 ** Implements /json/timeline family of functionstimeline(
--- a/src/json_diff.c
+++ b/src/json_diff.c
@@ -0,0 +1,27 @@
1
+;
2
+ DiffConfig (c)g;
3
+ Blob from = empty_blob, to = empty_blob, out = empty_blob;
4
+ cson_value * rc = NULL;
5
+ int flags = (fSbsDIFF_CONTEXT_MASK & nContext)
6
+ |bs ? DIFF_SIDEBYSIDE : 0)
7
+ | (fHtml ? DIFF_HTML : 0);
8
+ fromid = name_to_typed_rid(zFrom, "*");
9
+ if(fromid<=0){
10
+ json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
11
+ "Could not resolve 'from' ID.");
12
+ return NULL;
13
+ }
14
+ toid = name_to_typed_rid(zTo, "*");
15
+ if(toid<=0){
16
+ json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
17
+ "Could not resolve 'to' ID.");
18
+ return NULL;
19
+ }
20
+ content_get(fromid, &from_diff(&from, &to, &out, &DCf0, flags&from, &to, &out, &DCfg);
21
+ blob_reset(&from);
22
+ blob_reset(&to);
23
+ outLen = blob_size(&out);
24
+ if(outLen>=0){
25
+ rc = cson_value_new_string(blob_buffer(&out),
26
+ flags,
27
+ flagsv = char const * zType = "ci"
--- a/src/json_diff.c
+++ b/src/json_diff.c
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/json_diff.c
+++ b/src/json_diff.c
@@ -0,0 +1,27 @@
1 ;
2 DiffConfig (c)g;
3 Blob from = empty_blob, to = empty_blob, out = empty_blob;
4 cson_value * rc = NULL;
5 int flags = (fSbsDIFF_CONTEXT_MASK & nContext)
6 |bs ? DIFF_SIDEBYSIDE : 0)
7 | (fHtml ? DIFF_HTML : 0);
8 fromid = name_to_typed_rid(zFrom, "*");
9 if(fromid<=0){
10 json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
11 "Could not resolve 'from' ID.");
12 return NULL;
13 }
14 toid = name_to_typed_rid(zTo, "*");
15 if(toid<=0){
16 json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
17 "Could not resolve 'to' ID.");
18 return NULL;
19 }
20 content_get(fromid, &from_diff(&from, &to, &out, &DCf0, flags&from, &to, &out, &DCfg);
21 blob_reset(&from);
22 blob_reset(&to);
23 outLen = blob_size(&out);
24 if(outLen>=0){
25 rc = cson_value_new_string(blob_buffer(&out),
26 flags,
27 flagsv = char const * zType = "ci"
--- a/src/json_login.c
+++ b/src/json_login.c
@@ -0,0 +1,36 @@
1
+);
2
+ }
3
+ payloadjson_new_string(cap)son_object_set(po, "authToken"ookie is
4
+ set because anon does not get a db entry like normal users
5
+ do. Anonymous cookies currently have a hard-coded lifetime in
6
+ login_set_anon_cookie() (currently 6 hours), which we "should
7
+ arguably" change to use the time configured for non-anonymous
8
+ users (see login_set_user_cookie() for details).
9
+ *NULL, /
10
+ rrn payload;
11
+ }
12
+}
13
+
14
+/*
15
+** Impl of );
16
+ }p"p"/"pasted by this.
17
+ */
18
+ cprintf(th forms work. The
19
+ "p"/"pasted by this.
20
+ */
21
+ char const * name = cson_value_get_cstr(json_req_payload_get("name"));
22
+ char const * pw = NULL;
23
+ char const * anonSeed = NULL;
24
+ cson_value * payload = NULL;
25
+ int uid = 0;
26
+ /* reminder to self: Fossil internally (for the sake of /wiki)
27
+ interprets paths in the form /foo/bar/baz such that P("name") ==
28
+ "bar/baz". T AILED_NOSEED
29
+ d
30
+ thus we do some rather elaborate name=... checking.
31
+ */
32
+ pw = cson_value_get_cstr(json_req_payload_get("password"));
33
+ if( !pw ){
34
+ pw = PD("p",NULL);
35
+ if( !pw ){
36
+ pw = PD("pass
--- a/src/json_login.c
+++ b/src/json_login.c
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/json_login.c
+++ b/src/json_login.c
@@ -0,0 +1,36 @@
1 );
2 }
3 payloadjson_new_string(cap)son_object_set(po, "authToken"ookie is
4 set because anon does not get a db entry like normal users
5 do. Anonymous cookies currently have a hard-coded lifetime in
6 login_set_anon_cookie() (currently 6 hours), which we "should
7 arguably" change to use the time configured for non-anonymous
8 users (see login_set_user_cookie() for details).
9 *NULL, /
10 rrn payload;
11 }
12 }
13
14 /*
15 ** Impl of );
16 }p"p"/"pasted by this.
17 */
18 cprintf(th forms work. The
19 "p"/"pasted by this.
20 */
21 char const * name = cson_value_get_cstr(json_req_payload_get("name"));
22 char const * pw = NULL;
23 char const * anonSeed = NULL;
24 cson_value * payload = NULL;
25 int uid = 0;
26 /* reminder to self: Fossil internally (for the sake of /wiki)
27 interprets paths in the form /foo/bar/baz such that P("name") ==
28 "bar/baz". T AILED_NOSEED
29 d
30 thus we do some rather elaborate name=... checking.
31 */
32 pw = cson_value_get_cstr(json_req_payload_get("password"));
33 if( !pw ){
34 pw = PD("p",NULL);
35 if( !pw ){
36 pw = PD("pass
--- a/src/json_query.c
+++ b/src/json_query.c
@@ -0,0 +1,31 @@
1
+#ifdef FOSSIL_E(c)LE_JSON
2
+/*
3
+** Copyright (c) 2011 D. Richard Hipp
4
+**
5
+** This program is free software; you can redistribute it and/or
6
+** modify it under the terms of the Simplified BSD License (also
7
+** known as the "2-Clause License" or "FreeBSD License".)
8
+**
9
+** This program is distributed in the hope that it will be useful,
10
+** but without any warranty; without even the implied warranty of
11
+** merchantability or fitness for a particular purpose.
12
+**
13
+** Author contact information:
14
+** [email protected]
15
+** http://www.hwaci.com/drh/
16
+**
17
+*/
18
+
19
+#include "config.h"
20
+#include "json_query.h"
21
+
22
+#if INTERFACE
23
+#include "json_detail.h"
24
+#endif
25
+
26
+
27
+/*
28
+** Implementation of the /json/query page.
29
+**
30
+** Requires admin privileges. Intended primarily to assist me in
31
+** coming up with JSON output str4433
--- a/src/json_query.c
+++ b/src/json_query.c
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/json_query.c
+++ b/src/json_query.c
@@ -0,0 +1,31 @@
1 #ifdef FOSSIL_E(c)LE_JSON
2 /*
3 ** Copyright (c) 2011 D. Richard Hipp
4 **
5 ** This program is free software; you can redistribute it and/or
6 ** modify it under the terms of the Simplified BSD License (also
7 ** known as the "2-Clause License" or "FreeBSD License".)
8 **
9 ** This program is distributed in the hope that it will be useful,
10 ** but without any warranty; without even the implied warranty of
11 ** merchantability or fitness for a particular purpose.
12 **
13 ** Author contact information:
14 ** [email protected]
15 ** http://www.hwaci.com/drh/
16 **
17 */
18
19 #include "config.h"
20 #include "json_query.h"
21
22 #if INTERFACE
23 #include "json_detail.h"
24 #endif
25
26
27 /*
28 ** Implementation of the /json/query page.
29 **
30 ** Requires admin privileges. Intended primarily to assist me in
31 ** coming up with JSON output str4433
--- a/src/json_report.c
+++ b/src/json_report.c
@@ -0,0 +1 @@
1
+3333mtime,
--- a/src/json_report.c
+++ b/src/json_report.c
@@ -0,0 +1 @@
 
--- a/src/json_report.c
+++ b/src/json_report.c
@@ -0,0 +1 @@
1 3333mtime,
--- a/src/json_tag.c
+++ b/src/json_tag.c
@@ -0,0 +1 @@
1
+int constmtime
--- a/src/json_tag.c
+++ b/src/json_tag.c
@@ -0,0 +1 @@
 
--- a/src/json_tag.c
+++ b/src/json_tag.c
@@ -0,0 +1 @@
1 int constmtime
--- a/src/json_timeline.c
+++ b/src/json_timeline.c
@@ -0,0 +1,20 @@
1
+ * CopzSqlcson_( * CopzSqlcson_(c) json_timeli);yV);
2
+r check = );
3
+/*
4
+** Mapping of /json/timeline/XXX commands/paths to callbacks.
5
+*/
6
+static con forms are only enabled in CLI mode, to avoid
7
+ that we end up with HTTP clients using 3 different names
8
+ for the same requests.
9
+*/
10
+{"branch", json_timeline_branch, 0},
11
+{"checkin", json_timelinecci, -1},
12
+{"tticket, -1},
13
+{"ticketticket, 0},
14
+{"w-1Read ){
15
+ /* IMO this falls more under the category of g.perm.History, but
16
+ i'm following timpl hereo) json_timeli
17
+ goto end;
18
+);
19
+
20
+ end:char warnStringToArray
--- a/src/json_timeline.c
+++ b/src/json_timeline.c
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/json_timeline.c
+++ b/src/json_timeline.c
@@ -0,0 +1,20 @@
1 * CopzSqlcson_( * CopzSqlcson_(c) json_timeli);yV);
2 r check = );
3 /*
4 ** Mapping of /json/timeline/XXX commands/paths to callbacks.
5 */
6 static con forms are only enabled in CLI mode, to avoid
7 that we end up with HTTP clients using 3 different names
8 for the same requests.
9 */
10 {"branch", json_timeline_branch, 0},
11 {"checkin", json_timelinecci, -1},
12 {"tticket, -1},
13 {"ticketticket, 0},
14 {"w-1Read ){
15 /* IMO this falls more under the category of g.perm.History, but
16 i'm following timpl hereo) json_timeli
17 goto end;
18 );
19
20 end:char warnStringToArray
--- a/src/json_user.c
+++ b/src/json_user.c
@@ -0,0 +1,122 @@
1
+ !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
2
+ "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
3
+** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
4
+ pUser = json_getenv_cstr("name")
5
+ /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
6
+if we pass the name as part of the path, which is why we check
7
+ with json_command_path() before trying to get("name").
8
+ */;
9
+ }) st(LK,NULL,NULL); password");
10
+ PROP("in#if 0create();
11
+
12
+#endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
13
+ } char * zPWHash = NULL++gotFieldsif(g.isHTTP){
14
+ }o user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
15
+ pUser = json_getenv_cstr("name")
16
+ /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
17
+if we pass the name as part of the path, which is why we check
18
+ with json_command_path() before trying to get("name").
19
+ */;
20
+ }) st(LK,NULL,NULL); password");
21
+ PROP(){
22
+ = FSL_JSON_E_DENIEDption_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
23
+ } char * zPWHash = NULL++gotFields !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
24
+ "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
25
+** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
26
+ pUser = json_getenv_cstr("name")
27
+ /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
28
+if we pass the name as part of the path, which is why we check
29
+ with json_command_path() before trying to get("name").
30
+ */;
31
+ }) st(LK,NULL,NULL); password");
32
+ PROP("in#if 0create();
33
+
34
+#endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
35
+ } char * zPWHash = NULL++gotFields_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
36
+** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
37
+ pUser = json_getenv_cstr("name")
38
+ !&& zNameNew && (zName != zNameNew)
39
+ && (0!=strcmp(zNameNew,zName))){
40
+ _err(FSL_JSON_E_D login=%Q", zNameNew){name");g.perm.Setup)(c) json_set_err(FSL_JSON_E_DEset_err(FSL_JSON_E_DENIED,
41
+ "Requires 'an_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
42
+ } char * zPWHash = NULL++gotFields !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
43
+ "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
44
+** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
45
+ pUser = json_getenv_cstr("name")
46
+ /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
47
+if we pass the name as part of the path, which is why we check
48
+ with json_command_path() before trying to get("name").
49
+ */;
50
+ }) st(LK,NULL,NULL); password");
51
+ PROP("in#if 0create();
52
+
53
+#endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
54
+ } char * zPWHash = NULL++gotFields_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
55
+** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
56
+ pUser = json_getenv_cstr("name")
57
+ !&& zNameNew && (zName != zNameNew)
58
+ && (0!=strcmp(zNameNew,zName))){
59
+ _err(FSL_JSON_E_D login=%Q", zNameNew){name");g.perm.Setup)(c) json_set_
60
+**
61
+** TODOs:
62
+**
63
+** - Admin non-Setup users cannot change the information for Setup
64
+** users_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
65
+** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
66
+ pUser = json_getenv_cstr("name")
67
+ /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
68
+if we pass the name as part of the path, which is why we check
69
+ with json_command_path() before trying to get("name").
70
+ */;
71
+ }) st(LK,NULL,NULL); password");
72
+ PROP("in#if 0create();
73
+
74
+#endifcreate", json_page_nyi, 0},e_find_option), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.SetuUSER
75
+ if( zPW ){uid=%d", uid);blob_reset(&sql*
76
+** TODOs:
77
+**
78
+** - Return something useful in the payload (at least the id of the
79
+**m.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
80
+ } char * zPWHash = NULL++gotFieldsif(g.isHTTP){
81
+ }o user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
82
+ pUser = json_getenv_cstr("name")
83
+ /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
84
+if we pass the name as part of the path, which is why we check
85
+ w e))){
86
+ _err(FSL_JSON_E_D login=%Q", zNameNew){name");g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
87
+ "Requirset_err(FSL_JSON_E_DENIED,
88
+ "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
89
+** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
90
+ pUser = json_getenv_cstr("name")
91
+ /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
92
+if we pass the name as part of the path, which is why we check
93
+ with json_command_path() before trying to get("name").
94
+ */;
95
+ }) st(LK,NULL,NULL); password");
96
+ PROP("in#if 0create();
97
+
98
+#endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.SetuUSER
99
+ if( zPW ){uid=%d", uid);blob_reset(&sql*
100
+** TODOs:
101
+**
102
+** - Return something useful in the payload (at least the id of the
103
+**m.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_/*
104
+ TODO: do not allow an admin user to modify a setup user
105
+ unless the admin is also a setup user. setup.c uses
106
+ that logic. There is a corner case for a NEW Setup user
107
+ which the admin is just!g.perm.Setup)(c) jsoup)(c) json_set_err(t_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(_option_cstr2("namto j_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
108
+ } char * zPWHash = NULL++gotFields !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
109
+ "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
110
+** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
111
+ pUser = json_getenv_cstr("name")
112
+ /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
113
+if we pass the name as part of the path, which is why we check
114
+ with json_command_path() before trying to get("name").
115
+ */;
116
+ }) st(LK,NULL,NULL); password");
117
+ PROP("in#if 0create();
118
+
119
+#endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
120
+ } char * zPWHash = NULL++gotFields_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
121
+** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
122
+ pUser = json_getenv
--- a/src/json_user.c
+++ b/src/json_user.c
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/json_user.c
+++ b/src/json_user.c
@@ -0,0 +1,122 @@
1 !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
2 "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
3 ** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
4 pUser = json_getenv_cstr("name")
5 /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
6 if we pass the name as part of the path, which is why we check
7 with json_command_path() before trying to get("name").
8 */;
9 }) st(LK,NULL,NULL); password");
10 PROP("in#if 0create();
11
12 #endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
13 } char * zPWHash = NULL++gotFieldsif(g.isHTTP){
14 }o user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
15 pUser = json_getenv_cstr("name")
16 /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
17 if we pass the name as part of the path, which is why we check
18 with json_command_path() before trying to get("name").
19 */;
20 }) st(LK,NULL,NULL); password");
21 PROP(){
22 = FSL_JSON_E_DENIEDption_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
23 } char * zPWHash = NULL++gotFields !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
24 "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
25 ** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
26 pUser = json_getenv_cstr("name")
27 /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
28 if we pass the name as part of the path, which is why we check
29 with json_command_path() before trying to get("name").
30 */;
31 }) st(LK,NULL,NULL); password");
32 PROP("in#if 0create();
33
34 #endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
35 } char * zPWHash = NULL++gotFields_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
36 ** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
37 pUser = json_getenv_cstr("name")
38 !&& zNameNew && (zName != zNameNew)
39 && (0!=strcmp(zNameNew,zName))){
40 _err(FSL_JSON_E_D login=%Q", zNameNew){name");g.perm.Setup)(c) json_set_err(FSL_JSON_E_DEset_err(FSL_JSON_E_DENIED,
41 "Requires 'an_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
42 } char * zPWHash = NULL++gotFields !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
43 "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
44 ** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
45 pUser = json_getenv_cstr("name")
46 /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
47 if we pass the name as part of the path, which is why we check
48 with json_command_path() before trying to get("name").
49 */;
50 }) st(LK,NULL,NULL); password");
51 PROP("in#if 0create();
52
53 #endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
54 } char * zPWHash = NULL++gotFields_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
55 ** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
56 pUser = json_getenv_cstr("name")
57 !&& zNameNew && (zName != zNameNew)
58 && (0!=strcmp(zNameNew,zName))){
59 _err(FSL_JSON_E_D login=%Q", zNameNew){name");g.perm.Setup)(c) json_set_
60 **
61 ** TODOs:
62 **
63 ** - Admin non-Setup users cannot change the information for Setup
64 ** users_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
65 ** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
66 pUser = json_getenv_cstr("name")
67 /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
68 if we pass the name as part of the path, which is why we check
69 with json_command_path() before trying to get("name").
70 */;
71 }) st(LK,NULL,NULL); password");
72 PROP("in#if 0create();
73
74 #endifcreate", json_page_nyi, 0},e_find_option), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.SetuUSER
75 if( zPW ){uid=%d", uid);blob_reset(&sql*
76 ** TODOs:
77 **
78 ** - Return something useful in the payload (at least the id of the
79 **m.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
80 } char * zPWHash = NULL++gotFieldsif(g.isHTTP){
81 }o user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
82 pUser = json_getenv_cstr("name")
83 /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
84 if we pass the name as part of the path, which is why we check
85 w e))){
86 _err(FSL_JSON_E_D login=%Q", zNameNew){name");g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
87 "Requirset_err(FSL_JSON_E_DENIED,
88 "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
89 ** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
90 pUser = json_getenv_cstr("name")
91 /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
92 if we pass the name as part of the path, which is why we check
93 with json_command_path() before trying to get("name").
94 */;
95 }) st(LK,NULL,NULL); password");
96 PROP("in#if 0create();
97
98 #endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.SetuUSER
99 if( zPW ){uid=%d", uid);blob_reset(&sql*
100 ** TODOs:
101 **
102 ** - Return something useful in the payload (at least the id of the
103 **m.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_/*
104 TODO: do not allow an admin user to modify a setup user
105 unless the admin is also a setup user. setup.c uses
106 that logic. There is a corner case for a NEW Setup user
107 which the admin is just!g.perm.Setup)(c) jsoup)(c) json_set_err(t_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(_option_cstr2("namto j_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
108 } char * zPWHash = NULL++gotFields !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
109 "Requires 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
110 ** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
111 pUser = json_getenv_cstr("name")
112 /* ACHTUNG: fossil apparently internally sets name=user/get/XYZ
113 if we pass the name as part of the path, which is why we check
114 with json_command_path() before trying to get("name").
115 */;
116 }) st(LK,NULL,NULL); password");
117 PROP("in#if 0create();
118
119 #endifcreate", json_page_nyi, 0},e_find_option_cstr2("_find_option_cstr2(" !g.perm.Setup)(c) jsos 'a' or son_find_option_cstr2("namto json_load_user_by_name(), but expec !g.perm.Setup)(c) !g.perm.Setup)(c) _find_option_cstr2(" !g.perm.Setup)(c) json_set_}_find_option_cstr2("){_find_option_cstr2("namto json_load_user_by_name(), but expects a !g.perm.Setup)(c) json_set_err(FSL_JSON_E_DENIED,
120 } char * zPWHash = NULL++gotFields_find_option_cstr2("namto json_load_user_by_name(), but expects a user ID. Returns
121 ** NULL if noL if no user founson_value * tmpVcommand_arg( g.isHTTP && (!pUser || !*pUser) ){
122 pUser = json_getenv
--- a/src/json_wiki.c
+++ b/src/json_wiki.c
@@ -0,0 +1,57 @@
1
+laLoads the given wiki page an representation
2
+**la is positive tru is HTML-ized
3
+** negative then no
4
+** is not returned
5
+**
6
+**Manifest *pWiki = 0;
7
+ char * zUuid = NULL;
8
+ Stmt q;
9
+ db_prepare(&q,
10
+,(SQLITE_ROW != db_step(&q)) ){
11
+ id = db_column_int(&q,0);
12
+ zUuiconst * zBody = NULLnegative then no
13
+*laLoads the given wik = -1;
14
+ Stmt q;!=0 ){
15
+ }
16
+
17
+ {
18
+ unsigned int lenlen = strlen(zBody);
19
+e page is saved
20
+ again before the next line finishes, payV could be the results
21
+ nameVcontentVemptyContentpayV = NULL;
22
+ doParse
23
+** is using fossil's
24
+**else it is not parseddoParseed
25
+** negative then no
26
+** is not returned
27
+**
28
+**Manifest *pWiki = 0;
29
+ char * zUuidlaLoads the given wiki page an representation
30
+**la is positive tru is HTML-ized
31
+** negative then no
32
+** is not returned
33
+**
34
+**Manifest *pWiki = 0;
35
+ char * zUuid = NULL;
36
+ Stmt q;
37
+ db_prepare(&q,
38
+,(SQLITE_ROW != db_step(&q)) ){
39
+ id = db_column_int(&q,0);
40
+ zUuiconst * zBody = NULLnegative then no
41
+*laLoads the given wik = -1;
42
+ Stmt q;!=0 ){
43
+ }
44
+
45
+ {
46
+ unsigned int lenlen = strlen(zBody);
47
+e page is saved
48
+ again before the next line finishes, payV could be the results
49
+ nameVcontentVemptyContentpayV = NULL;
50
+if( doParse ){
51
+ len = strlen(zBody);
52
+}else{
53
+doParsedoParsedoParse){
54
+ zFormat = "raw";
55
+ }
56
+ if( 'r' != *zFormat ){
57
+ zFormat = "html"'h'==*z
--- a/src/json_wiki.c
+++ b/src/json_wiki.c
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/json_wiki.c
+++ b/src/json_wiki.c
@@ -0,0 +1,57 @@
1 laLoads the given wiki page an representation
2 **la is positive tru is HTML-ized
3 ** negative then no
4 ** is not returned
5 **
6 **Manifest *pWiki = 0;
7 char * zUuid = NULL;
8 Stmt q;
9 db_prepare(&q,
10 ,(SQLITE_ROW != db_step(&q)) ){
11 id = db_column_int(&q,0);
12 zUuiconst * zBody = NULLnegative then no
13 *laLoads the given wik = -1;
14 Stmt q;!=0 ){
15 }
16
17 {
18 unsigned int lenlen = strlen(zBody);
19 e page is saved
20 again before the next line finishes, payV could be the results
21 nameVcontentVemptyContentpayV = NULL;
22 doParse
23 ** is using fossil's
24 **else it is not parseddoParseed
25 ** negative then no
26 ** is not returned
27 **
28 **Manifest *pWiki = 0;
29 char * zUuidlaLoads the given wiki page an representation
30 **la is positive tru is HTML-ized
31 ** negative then no
32 ** is not returned
33 **
34 **Manifest *pWiki = 0;
35 char * zUuid = NULL;
36 Stmt q;
37 db_prepare(&q,
38 ,(SQLITE_ROW != db_step(&q)) ){
39 id = db_column_int(&q,0);
40 zUuiconst * zBody = NULLnegative then no
41 *laLoads the given wik = -1;
42 Stmt q;!=0 ){
43 }
44
45 {
46 unsigned int lenlen = strlen(zBody);
47 e page is saved
48 again before the next line finishes, payV could be the results
49 nameVcontentVemptyContentpayV = NULL;
50 if( doParse ){
51 len = strlen(zBody);
52 }else{
53 doParsedoParsedoParse){
54 zFormat = "raw";
55 }
56 if( 'r' != *zFormat ){
57 zFormat = "html"'h'==*z
+285 -107
--- src/login.c
+++ src/login.c
@@ -84,11 +84,11 @@
8484
**
8585
** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX
8686
** where the Xs are the first 16 characters of the login-group-code or
8787
** of the project-code if we are not a member of any login-group.
8888
*/
89
-static char *login_cookie_name(void){
89
+char *login_cookie_name(void){
9090
static char *zCookieName = 0;
9191
if( zCookieName==0 ){
9292
zCookieName = db_text(0,
9393
"SELECT 'fossil-' || substr(value,1,16)"
9494
" FROM config"
@@ -117,15 +117,20 @@
117117
** But some clients are behind firewalls that shift the IP address
118118
** with each HTTP request. To allow such (broken) clients to log in,
119119
** extract just a prefix of the IP address.
120120
*/
121121
static char *ipPrefix(const char *zIP){
122
- int i, j;
122
+ int i, j;
123
+ static int ip_prefix_terms = -1;
124
+ if( ip_prefix_terms<0 ){
125
+ ip_prefix_terms = db_get_int("ip-prefix-terms",2);
126
+ }
127
+ if( ip_prefix_terms==0 ) return mprintf("0");
123128
for(i=j=0; zIP[i]; i++){
124129
if( zIP[i]=='.' ){
125130
j++;
126
- if( j==2 ) break;
131
+ if( j==ip_prefix_terms ) break;
127132
}
128133
}
129134
return mprintf("%.*s", i, zIP);
130135
}
131136
@@ -141,24 +146,26 @@
141146
142147
143148
/*
144149
** Check to see if the anonymous login is valid. If it is valid, return
145150
** the userid of the anonymous user.
151
+**
152
+** The zCS parameter is the "captcha seed" used for a specific
153
+** anonymous login request.
146154
*/
147
-static int isValidAnonymousLogin(
155
+int login_is_valid_anonymous(
148156
const char *zUsername, /* The username. Must be "anonymous" */
149
- const char *zPassword /* The supplied password */
157
+ const char *zPassword, /* The supplied password */
158
+ const char *zCS /* The captcha seed value */
150159
){
151
- const char *zCS; /* The captcha seed value */
152160
const char *zPw; /* The correct password shown in the captcha */
153161
int uid; /* The user ID of anonymous */
154162
155163
if( zUsername==0 ) return 0;
156
- if( zPassword==0 ) return 0;
157
- if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
158
- zCS = P("cs"); /* The "cs" parameter is the "captcha seed" */
159
- if( zCS==0 ) return 0;
164
+ else if( zPassword==0 ) return 0;
165
+ else if( zCS==0 ) return 0;
166
+ else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
160167
zPw = captcha_decode((unsigned int)atoi(zCS));
161168
if( fossil_stricmp(zPw, zPassword)!=0 ) return 0;
162169
uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
163170
" AND length(pw)>0 AND length(cap)>0");
164171
return uid;
@@ -192,10 +199,225 @@
192199
"INSERT INTO accesslog(uname,ipaddr,success,mtime)"
193200
"VALUES(%Q,%Q,%d,julianday('now'));",
194201
zUsername, zIpAddr, bSuccess
195202
);
196203
}
204
+
205
+/*
206
+** Searches for the user ID matching the given name and password.
207
+** On success it returns a positive value. On error it returns 0.
208
+** On serious (DB-level) error it will probably exit.
209
+**
210
+** zPassword may be either the plain-text form or the encrypted
211
+** form of the user's password.
212
+*/
213
+int login_search_uid(char const *zUsername, char const *zPasswd){
214
+ char * zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
215
+ int const uid =
216
+ db_int(0,
217
+ "SELECT uid FROM user"
218
+ " WHERE login=%Q"
219
+ " AND length(cap)>0 AND length(pw)>0"
220
+ " AND login NOT IN ('anonymous','nobody','developer','reader')"
221
+ " AND (pw=%Q OR pw=%Q)",
222
+ zUsername, zPasswd, zSha1Pw
223
+ );
224
+ free(zSha1Pw);
225
+ return uid;
226
+}
227
+
228
+/*
229
+** Generates a login cookie value for a non-anonymous user.
230
+**
231
+** The zHash parameter must be a random value which must be
232
+** subsequently stored in user.cookie for later validation.
233
+**
234
+** The returned memory should be free()d after use.
235
+*/
236
+char * login_gen_user_cookie_value(char const *zUsername, char const * zHash){
237
+ char * zProjCode = db_get("project-code",NULL);
238
+ char *zCode = abbreviated_project_code(zProjCode);
239
+ free(zProjCode);
240
+ assert((zUsername && *zUsername) && "Invalid user data.");
241
+ return mprintf("%s/%z/%s", zHash, zCode, zUsername);
242
+}
243
+
244
+/*
245
+** Generates a login cookie for NON-ANONYMOUS users. Note that this
246
+** function "could" figure out the uid by itself but it currently
247
+** doesn't because the code which calls this already has the uid.
248
+**
249
+** This function also updates the user.cookie, user.ipaddr,
250
+** and user.cexpire fields for the given user.
251
+**
252
+** If zDest is not NULL then the generated cookie is copied to
253
+** *zDdest and ownership is transfered to the caller (who should
254
+** eventually pass it to free()).
255
+*/
256
+void login_set_user_cookie(
257
+ char const * zUsername, /* User's name */
258
+ int uid, /* User's ID */
259
+ char ** zDest /* Optional: store generated cookie value. */
260
+){
261
+ const char *zCookieName = login_cookie_name();
262
+ const char *zExpire = db_get("cookie-expire","8766");
263
+ int expires = atoi(zExpire)*3600;
264
+ char *zHash;
265
+ char *zCookie;
266
+ char const * zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
267
+ char * zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
268
+ assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data.");
269
+ zHash = db_text(0, "SELECT hex(randomblob(25))");
270
+ zCookie = login_gen_user_cookie_value(zUsername, zHash);
271
+ cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
272
+ record_login_attempt(zUsername, zIpAddr, 1);
273
+ db_multi_exec(
274
+ "UPDATE user SET cookie=%Q, ipaddr=%Q, "
275
+ " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
276
+ zHash, zRemoteAddr, expires, uid
277
+ );
278
+ free(zRemoteAddr);
279
+ free(zHash);
280
+ if( zDest ){
281
+ *zDest = zCookie;
282
+ }else{
283
+ free(zCookie);
284
+ }
285
+}
286
+
287
+/* Sets a cookie for an anonymous user login, which looks like this:
288
+**
289
+** HASH/TIME/anonymous
290
+**
291
+** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
292
+** is the abbreviated IP address and SECRET is captcha-secret.
293
+**
294
+** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR
295
+** is used.
296
+**
297
+** If zCookieDest is not NULL then the generated cookie is assigned to
298
+** *zCookieDest and the caller must eventually free() it.
299
+*/
300
+void login_set_anon_cookie(char const * zIpAddr, char ** zCookieDest ){
301
+ char const *zNow; /* Current time (julian day number) */
302
+ char *zCookie; /* The login cookie */
303
+ char const *zCookieName; /* Name of the login cookie */
304
+ Blob b; /* Blob used during cookie construction */
305
+ char * zRemoteAddr; /* Abbreviated IP address */
306
+ if(!zIpAddr){
307
+ zIpAddr = PD("REMOTE_ADDR","nil");
308
+ }
309
+ zRemoteAddr = ipPrefix(zIpAddr);
310
+ zCookieName = login_cookie_name();
311
+ zNow = db_text("0", "SELECT julianday('now')");
312
+ assert( zCookieName && zRemoteAddr && zIpAddr && zNow );
313
+ blob_init(&b, zNow, -1);
314
+ blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
315
+ sha1sum_blob(&b, &b);
316
+ zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
317
+ blob_reset(&b);
318
+ cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
319
+ if( zCookieDest ){
320
+ *zCookieDest = zCookie;
321
+ }else{
322
+ free(zCookie);
323
+ }
324
+
325
+}
326
+
327
+/*
328
+** "Unsets" the login cookie (insofar as cookies can be unset) and
329
+** clears the current user's (g.userUid) login information from the
330
+** user table. Sets: user.cookie, user.ipaddr, user.cexpire.
331
+**
332
+** We could/should arguably clear out g.userUid and g.perm here, but
333
+** we don't currently do not.
334
+**
335
+** This is a no-op if g.userUid is 0.
336
+*/
337
+void login_clear_login_data(){
338
+ if(!g.userUid){
339
+ return;
340
+ }else{
341
+ char const * cookie = login_cookie_name();
342
+ /* To logout, change the cookie value to an empty string */
343
+ cgi_set_cookie(cookie, "",
344
+ login_cookie_path(), -86400);
345
+ db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, "
346
+ " cexpire=0 WHERE uid=%d"
347
+ " AND login NOT IN ('anonymous','nobody',"
348
+ " 'developer','reader')", g.userUid);
349
+ cgi_replace_parameter(cookie, NULL)
350
+ /* At the time of this writing, cgi_replace_parameter() was
351
+ ** "NULL-value-safe", and i'm hoping the NULL doesn't cause any
352
+ ** downstream problems here. We could alternately use "" here.
353
+ */
354
+ ;
355
+ }
356
+}
357
+
358
+/*
359
+** Look at the HTTP_USER_AGENT parameter and try to determine if the user agent
360
+** is a manually operated browser or a bot. When in doubt, assume a bot. Return
361
+** true if we believe the agent is a real person.
362
+*/
363
+static int isHuman(const char *zAgent){
364
+ int i;
365
+ if( zAgent==0 ) return 0;
366
+ for(i=0; zAgent[i]; i++){
367
+ if( zAgent[i]=='b' && memcmp(&zAgent[i],"bot",3)==0 ) return 0;
368
+ if( zAgent[i]=='s' && memcmp(&zAgent[i],"spider",6)==0 ) return 0;
369
+ }
370
+ if( memcmp(zAgent, "Mozilla/", 8)==0 ){
371
+ return atoi(&zAgent[8])>=4;
372
+ }
373
+ if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
374
+ if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
375
+ if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1;
376
+ return 0;
377
+}
378
+
379
+/*
380
+** COMMAND: test-ishuman
381
+**
382
+** Read lines of text from standard input. Interpret each line of text
383
+** as a User-Agent string from an HTTP header. Label each line as HUMAN
384
+** or ROBOT.
385
+*/
386
+void test_ishuman(void){
387
+ char zLine[3000];
388
+ while( fgets(zLine, sizeof(zLine), stdin) ){
389
+ fossil_print("%s %s", isHuman(zLine) ? "HUMAN" : "ROBOT", zLine);
390
+ }
391
+}
392
+
393
+/*
394
+** SQL function for constant time comparison of two values.
395
+** Sets result to 0 if two values are equal.
396
+*/
397
+static void constant_time_cmp_function(
398
+ sqlite3_context *context,
399
+ int argc,
400
+ sqlite3_value **argv
401
+){
402
+ const unsigned char *buf1, *buf2;
403
+ int len, i;
404
+ unsigned char rc = 0;
405
+
406
+ assert( argc==2 );
407
+ len = sqlite3_value_bytes(argv[0]);
408
+ if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
409
+ rc = 1;
410
+ }else{
411
+ buf1 = sqlite3_value_text(argv[0]);
412
+ buf2 = sqlite3_value_text(argv[1]);
413
+ for( i=0; i<len; i++ ){
414
+ rc = rc | (buf1[i] ^ buf2[i]);
415
+ }
416
+ }
417
+ sqlite3_result_int(context, rc);
418
+}
197419
198420
/*
199421
** WEBPAGE: login
200422
** WEBPAGE: logout
201423
** WEBPAGE: my
@@ -214,23 +436,24 @@
214436
int anonFlag;
215437
char *zErrMsg = "";
216438
int uid; /* User id loged in user */
217439
char *zSha1Pw;
218440
const char *zIpAddr; /* IP address of requestor */
219
- char *zRemoteAddr; /* Abbreviated IP address of requestor */
220441
221442
login_check_credentials();
443
+ sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
444
+ constant_time_cmp_function, 0, 0);
222445
zUsername = P("u");
223446
zPasswd = P("p");
224447
anonFlag = P("anon")!=0;
225448
if( P("out")!=0 ){
226
- /* To logout, change the cookie value to an empty string */
227
- const char *zCookieName = login_cookie_name();
228
- cgi_set_cookie(zCookieName, "", login_cookie_path(), -86400);
449
+ login_clear_login_data();
229450
redirect_to_g();
230451
}
231
- if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
452
+ if( g.perm.Password && zPasswd
453
+ && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0
454
+ ){
232455
/* The user requests a password change */
233456
zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
234457
if( db_int(1, "SELECT 0 FROM user"
235458
" WHERE uid=%d"
236459
" AND (constant_time_cmp(pw,%Q)=0"
@@ -273,50 +496,20 @@
273496
return;
274497
}
275498
}
276499
}
277500
zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
278
- zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
279
- uid = isValidAnonymousLogin(zUsername, zPasswd);
501
+ uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs"));
280502
if( uid>0 ){
281
- /* Successful login as anonymous. Set a cookie that looks like
282
- ** this:
283
- **
284
- ** HASH/TIME/anonymous
285
- **
286
- ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
287
- ** is the abbreviated IP address and SECRET is captcha-secret.
288
- */
289
- char *zNow; /* Current time (julian day number) */
290
- char *zCookie; /* The login cookie */
291
- const char *zCookieName; /* Name of the login cookie */
292
- Blob b; /* Blob used during cookie construction */
293
-
294
- zCookieName = login_cookie_name();
295
- zNow = db_text("0", "SELECT julianday('now')");
296
- blob_init(&b, zNow, -1);
297
- blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
298
- sha1sum_blob(&b, &b);
299
- zCookie = sqlite3_mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
300
- blob_reset(&b);
301
- free(zNow);
302
- cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
503
+ login_set_anon_cookie(zIpAddr, NULL);
303504
record_login_attempt("anonymous", zIpAddr, 1);
304505
redirect_to_g();
305506
}
306507
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
307508
/* Attempting to log in as a user other than anonymous.
308509
*/
309
- zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
310
- uid = db_int(0,
311
- "SELECT uid FROM user"
312
- " WHERE login=%Q"
313
- " AND length(cap)>0 AND length(pw)>0"
314
- " AND login NOT IN ('anonymous','nobody','developer','reader')"
315
- " AND (constant_time_cmp(pw,%Q)=0 OR constant_time_cmp(pw,%Q)=0)",
316
- zUsername, zSha1Pw, zPasswd
317
- );
510
+ uid = login_search_uid(zUsername, zPasswd);
318511
if( uid<=0 ){
319512
sleep(1);
320513
zErrMsg =
321514
@ <p><span class="loginError">
322515
@ You entered an unknown user or an incorrect password.
@@ -329,26 +522,11 @@
329522
** HASH/PROJECT/LOGIN
330523
**
331524
** where HASH is a random hex number, PROJECT is either project
332525
** code prefix, and LOGIN is the user name.
333526
*/
334
- char *zCookie;
335
- const char *zCookieName = login_cookie_name();
336
- const char *zExpire = db_get("cookie-expire","8766");
337
- int expires = atoi(zExpire)*3600;
338
- char *zCode = abbreviated_project_code(db_get("project-code",""));
339
- char *zHash;
340
-
341
- zHash = db_text(0, "SELECT hex(randomblob(25))");
342
- zCookie = mprintf("%s/%s/%s", zHash, zCode, zUsername);
343
- cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
344
- record_login_attempt(zUsername, zIpAddr, 1);
345
- db_multi_exec(
346
- "UPDATE user SET cookie=%Q, ipaddr=%Q, "
347
- " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
348
- zHash, zRemoteAddr, expires, uid
349
- );
527
+ login_set_user_cookie(zUsername, uid, NULL);
350528
redirect_to_g();
351529
}
352530
}
353531
style_header("Login/Logout");
354532
@ %s(zErrMsg)
@@ -454,37 +632,10 @@
454632
@ </form>
455633
}
456634
style_footer();
457635
}
458636
459
-/*
460
-** SQL function for constant time comparison of two values.
461
-** Sets result to 0 if two values are equal.
462
-*/
463
-static void constant_time_cmp_function(
464
- sqlite3_context *context,
465
- int argc,
466
- sqlite3_value **argv
467
-){
468
- const unsigned char *buf1, *buf2;
469
- int len, i;
470
- unsigned char rc = 0;
471
-
472
- assert( argc==2 );
473
- len = sqlite3_value_bytes(argv[0]);
474
- if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
475
- rc = 1;
476
- }else{
477
- buf1 = sqlite3_value_text(argv[0]);
478
- buf2 = sqlite3_value_text(argv[1]);
479
- for( i=0; i<len; i++ ){
480
- rc = rc | (buf1[i] ^ buf2[i]);
481
- }
482
- }
483
- sqlite3_result_int(context, rc);
484
-}
485
-
486637
/*
487638
** Attempt to find login credentials for user zLogin on a peer repository
488639
** with project code zCode. Transfer those credentials to the local
489640
** repository.
490641
**
@@ -542,12 +693,16 @@
542693
fossil_free(zOtherRepo);
543694
return nXfer;
544695
}
545696
546697
/*
547
-** Lookup the uid for a user with zLogin and zCookie and zRemoteAddr.
548
-** Return 0 if not found.
698
+** Lookup the uid for a non-built-in user with zLogin and zCookie and
699
+** zRemoteAddr. Return 0 if not found.
700
+**
701
+** Note that this only searches for logged-in entries with matching
702
+** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr)
703
+** entries.
549704
*/
550705
static int login_find_user(
551706
const char *zLogin, /* User name */
552707
const char *zCookie, /* Login cookie value */
553708
const char *zRemoteAddr /* Abbreviated IP address for valid login */
@@ -569,16 +724,14 @@
569724
);
570725
return uid;
571726
}
572727
573728
/*
574
-** This routine examines the login cookie to see if it exists and
575
-** and is valid. If the login cookie checks out, it then sets
576
-** global variables appropriately. Global variables set include
577
-** g.userUid and g.zLogin and of the g.perm.Read family of permission
578
-** booleans.
579
-**
729
+** This routine examines the login cookie to see if it exists and and
730
+** is valid. If the login cookie checks out, it then sets global
731
+** variables appropriately. Global variables set include g.userUid
732
+** and g.zLogin and the g.perm family of permission booleans.
580733
*/
581734
void login_check_credentials(void){
582735
int uid = 0; /* User id */
583736
const char *zCookie; /* Text of the login cookie */
584737
const char *zIpAddr; /* Raw IP address of the requestor */
@@ -667,11 +820,11 @@
667820
}
668821
sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
669822
}
670823
671824
/* If no user found and the REMOTE_USER environment variable is set,
672
- ** the accept the value of REMOTE_USER as the user.
825
+ ** then accept the value of REMOTE_USER as the user.
673826
*/
674827
if( uid==0 ){
675828
const char *zRemoteUser = P("REMOTE_USER");
676829
if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
677830
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -717,12 +870,16 @@
717870
if( fossil_strcmp(g.zLogin,"nobody")==0 ){
718871
g.zLogin = 0;
719872
}
720873
721874
/* Set the capabilities */
722
- login_set_capabilities(zCap, 0);
875
+ login_replace_capabilities(zCap, 0);
723876
login_set_anon_nobody_capabilities();
877
+ if( zCap[0] && !g.perm.History && db_get_boolean("auto-enable-hyperlinks",1)
878
+ && isHuman(P("HTTP_USER_AGENT")) ){
879
+ g.perm.History = 1;
880
+ }
724881
}
725882
726883
/*
727884
** Memory of settings
728885
*/
@@ -746,22 +903,25 @@
746903
login_anon_once = 0;
747904
}
748905
}
749906
750907
/*
751
-** Flags passed into the 2nd argument of login_set_capabilities().
908
+** Flags passed into the 2nd argument of login_set/replace_capabilities().
752909
*/
753910
#if INTERFACE
754911
#define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
755912
#define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
756913
#endif
757914
758915
/*
759
-** Set the global capability flags based on a capability string.
916
+** Adds all capability flags in zCap to g.perm.
760917
*/
761918
void login_set_capabilities(const char *zCap, unsigned flags){
762919
int i;
920
+ if(NULL==zCap){
921
+ return;
922
+ }
763923
for(i=0; zCap[i]; i++){
764924
switch( zCap[i] ){
765925
case 's': g.perm.Setup = 1; /* Fall thru into Admin */
766926
case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
767927
g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -815,10 +975,18 @@
815975
break;
816976
}
817977
}
818978
}
819979
}
980
+
981
+/*
982
+** Zeroes out g.perm and calls login_set_capabilities(zCap,flags).
983
+*/
984
+void login_replace_capabilities(const char *zCap, unsigned flags){
985
+ memset(&g.perm, 0, sizeof(g.perm));
986
+ login_set_capabilities(zCap, flags);
987
+}
820988
821989
/*
822990
** If the current login lacks any of the capabilities listed in
823991
** the input, then return 0. If all capabilities are present, then
824992
** return 1.
@@ -893,14 +1061,24 @@
8931061
/*
8941062
** Call this routine when the credential check fails. It causes
8951063
** a redirect to the "login" page.
8961064
*/
8971065
void login_needed(void){
898
- const char *zUrl = PD("REQUEST_URI", "index");
899
- cgi_redirect(mprintf("login?g=%T", zUrl));
900
- /* NOTREACHED */
901
- assert(0);
1066
+#ifdef FOSSIL_ENABLE_JSON
1067
+ if(g.json.isJsonMode){
1068
+ json_err( FSL_JSON_E_DENIED, NULL, 1 );
1069
+ fossil_exit(0);
1070
+ /* NOTREACHED */
1071
+ assert(0);
1072
+ }else
1073
+#endif /* FOSSIL_ENABLE_JSON */
1074
+ {
1075
+ const char *zUrl = PD("REQUEST_URI", "index");
1076
+ cgi_redirect(mprintf("login?g=%T", zUrl));
1077
+ /* NOTREACHED */
1078
+ assert(0);
1079
+ }
9021080
}
9031081
9041082
/*
9051083
** Call this routine if the user lacks okHistory permission. If
9061084
** the anonymous user has okHistory permission, then paint a mesage
9071085
--- src/login.c
+++ src/login.c
@@ -84,11 +84,11 @@
84 **
85 ** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX
86 ** where the Xs are the first 16 characters of the login-group-code or
87 ** of the project-code if we are not a member of any login-group.
88 */
89 static char *login_cookie_name(void){
90 static char *zCookieName = 0;
91 if( zCookieName==0 ){
92 zCookieName = db_text(0,
93 "SELECT 'fossil-' || substr(value,1,16)"
94 " FROM config"
@@ -117,15 +117,20 @@
117 ** But some clients are behind firewalls that shift the IP address
118 ** with each HTTP request. To allow such (broken) clients to log in,
119 ** extract just a prefix of the IP address.
120 */
121 static char *ipPrefix(const char *zIP){
122 int i, j;
 
 
 
 
 
123 for(i=j=0; zIP[i]; i++){
124 if( zIP[i]=='.' ){
125 j++;
126 if( j==2 ) break;
127 }
128 }
129 return mprintf("%.*s", i, zIP);
130 }
131
@@ -141,24 +146,26 @@
141
142
143 /*
144 ** Check to see if the anonymous login is valid. If it is valid, return
145 ** the userid of the anonymous user.
 
 
 
146 */
147 static int isValidAnonymousLogin(
148 const char *zUsername, /* The username. Must be "anonymous" */
149 const char *zPassword /* The supplied password */
 
150 ){
151 const char *zCS; /* The captcha seed value */
152 const char *zPw; /* The correct password shown in the captcha */
153 int uid; /* The user ID of anonymous */
154
155 if( zUsername==0 ) return 0;
156 if( zPassword==0 ) return 0;
157 if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
158 zCS = P("cs"); /* The "cs" parameter is the "captcha seed" */
159 if( zCS==0 ) return 0;
160 zPw = captcha_decode((unsigned int)atoi(zCS));
161 if( fossil_stricmp(zPw, zPassword)!=0 ) return 0;
162 uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
163 " AND length(pw)>0 AND length(cap)>0");
164 return uid;
@@ -192,10 +199,225 @@
192 "INSERT INTO accesslog(uname,ipaddr,success,mtime)"
193 "VALUES(%Q,%Q,%d,julianday('now'));",
194 zUsername, zIpAddr, bSuccess
195 );
196 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
198 /*
199 ** WEBPAGE: login
200 ** WEBPAGE: logout
201 ** WEBPAGE: my
@@ -214,23 +436,24 @@
214 int anonFlag;
215 char *zErrMsg = "";
216 int uid; /* User id loged in user */
217 char *zSha1Pw;
218 const char *zIpAddr; /* IP address of requestor */
219 char *zRemoteAddr; /* Abbreviated IP address of requestor */
220
221 login_check_credentials();
 
 
222 zUsername = P("u");
223 zPasswd = P("p");
224 anonFlag = P("anon")!=0;
225 if( P("out")!=0 ){
226 /* To logout, change the cookie value to an empty string */
227 const char *zCookieName = login_cookie_name();
228 cgi_set_cookie(zCookieName, "", login_cookie_path(), -86400);
229 redirect_to_g();
230 }
231 if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
 
 
232 /* The user requests a password change */
233 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
234 if( db_int(1, "SELECT 0 FROM user"
235 " WHERE uid=%d"
236 " AND (constant_time_cmp(pw,%Q)=0"
@@ -273,50 +496,20 @@
273 return;
274 }
275 }
276 }
277 zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
278 zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
279 uid = isValidAnonymousLogin(zUsername, zPasswd);
280 if( uid>0 ){
281 /* Successful login as anonymous. Set a cookie that looks like
282 ** this:
283 **
284 ** HASH/TIME/anonymous
285 **
286 ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
287 ** is the abbreviated IP address and SECRET is captcha-secret.
288 */
289 char *zNow; /* Current time (julian day number) */
290 char *zCookie; /* The login cookie */
291 const char *zCookieName; /* Name of the login cookie */
292 Blob b; /* Blob used during cookie construction */
293
294 zCookieName = login_cookie_name();
295 zNow = db_text("0", "SELECT julianday('now')");
296 blob_init(&b, zNow, -1);
297 blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
298 sha1sum_blob(&b, &b);
299 zCookie = sqlite3_mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
300 blob_reset(&b);
301 free(zNow);
302 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
303 record_login_attempt("anonymous", zIpAddr, 1);
304 redirect_to_g();
305 }
306 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
307 /* Attempting to log in as a user other than anonymous.
308 */
309 zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
310 uid = db_int(0,
311 "SELECT uid FROM user"
312 " WHERE login=%Q"
313 " AND length(cap)>0 AND length(pw)>0"
314 " AND login NOT IN ('anonymous','nobody','developer','reader')"
315 " AND (constant_time_cmp(pw,%Q)=0 OR constant_time_cmp(pw,%Q)=0)",
316 zUsername, zSha1Pw, zPasswd
317 );
318 if( uid<=0 ){
319 sleep(1);
320 zErrMsg =
321 @ <p><span class="loginError">
322 @ You entered an unknown user or an incorrect password.
@@ -329,26 +522,11 @@
329 ** HASH/PROJECT/LOGIN
330 **
331 ** where HASH is a random hex number, PROJECT is either project
332 ** code prefix, and LOGIN is the user name.
333 */
334 char *zCookie;
335 const char *zCookieName = login_cookie_name();
336 const char *zExpire = db_get("cookie-expire","8766");
337 int expires = atoi(zExpire)*3600;
338 char *zCode = abbreviated_project_code(db_get("project-code",""));
339 char *zHash;
340
341 zHash = db_text(0, "SELECT hex(randomblob(25))");
342 zCookie = mprintf("%s/%s/%s", zHash, zCode, zUsername);
343 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
344 record_login_attempt(zUsername, zIpAddr, 1);
345 db_multi_exec(
346 "UPDATE user SET cookie=%Q, ipaddr=%Q, "
347 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
348 zHash, zRemoteAddr, expires, uid
349 );
350 redirect_to_g();
351 }
352 }
353 style_header("Login/Logout");
354 @ %s(zErrMsg)
@@ -454,37 +632,10 @@
454 @ </form>
455 }
456 style_footer();
457 }
458
459 /*
460 ** SQL function for constant time comparison of two values.
461 ** Sets result to 0 if two values are equal.
462 */
463 static void constant_time_cmp_function(
464 sqlite3_context *context,
465 int argc,
466 sqlite3_value **argv
467 ){
468 const unsigned char *buf1, *buf2;
469 int len, i;
470 unsigned char rc = 0;
471
472 assert( argc==2 );
473 len = sqlite3_value_bytes(argv[0]);
474 if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
475 rc = 1;
476 }else{
477 buf1 = sqlite3_value_text(argv[0]);
478 buf2 = sqlite3_value_text(argv[1]);
479 for( i=0; i<len; i++ ){
480 rc = rc | (buf1[i] ^ buf2[i]);
481 }
482 }
483 sqlite3_result_int(context, rc);
484 }
485
486 /*
487 ** Attempt to find login credentials for user zLogin on a peer repository
488 ** with project code zCode. Transfer those credentials to the local
489 ** repository.
490 **
@@ -542,12 +693,16 @@
542 fossil_free(zOtherRepo);
543 return nXfer;
544 }
545
546 /*
547 ** Lookup the uid for a user with zLogin and zCookie and zRemoteAddr.
548 ** Return 0 if not found.
 
 
 
 
549 */
550 static int login_find_user(
551 const char *zLogin, /* User name */
552 const char *zCookie, /* Login cookie value */
553 const char *zRemoteAddr /* Abbreviated IP address for valid login */
@@ -569,16 +724,14 @@
569 );
570 return uid;
571 }
572
573 /*
574 ** This routine examines the login cookie to see if it exists and
575 ** and is valid. If the login cookie checks out, it then sets
576 ** global variables appropriately. Global variables set include
577 ** g.userUid and g.zLogin and of the g.perm.Read family of permission
578 ** booleans.
579 **
580 */
581 void login_check_credentials(void){
582 int uid = 0; /* User id */
583 const char *zCookie; /* Text of the login cookie */
584 const char *zIpAddr; /* Raw IP address of the requestor */
@@ -667,11 +820,11 @@
667 }
668 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
669 }
670
671 /* If no user found and the REMOTE_USER environment variable is set,
672 ** the accept the value of REMOTE_USER as the user.
673 */
674 if( uid==0 ){
675 const char *zRemoteUser = P("REMOTE_USER");
676 if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
677 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -717,12 +870,16 @@
717 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
718 g.zLogin = 0;
719 }
720
721 /* Set the capabilities */
722 login_set_capabilities(zCap, 0);
723 login_set_anon_nobody_capabilities();
 
 
 
 
724 }
725
726 /*
727 ** Memory of settings
728 */
@@ -746,22 +903,25 @@
746 login_anon_once = 0;
747 }
748 }
749
750 /*
751 ** Flags passed into the 2nd argument of login_set_capabilities().
752 */
753 #if INTERFACE
754 #define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
755 #define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
756 #endif
757
758 /*
759 ** Set the global capability flags based on a capability string.
760 */
761 void login_set_capabilities(const char *zCap, unsigned flags){
762 int i;
 
 
 
763 for(i=0; zCap[i]; i++){
764 switch( zCap[i] ){
765 case 's': g.perm.Setup = 1; /* Fall thru into Admin */
766 case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
767 g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -815,10 +975,18 @@
815 break;
816 }
817 }
818 }
819 }
 
 
 
 
 
 
 
 
820
821 /*
822 ** If the current login lacks any of the capabilities listed in
823 ** the input, then return 0. If all capabilities are present, then
824 ** return 1.
@@ -893,14 +1061,24 @@
893 /*
894 ** Call this routine when the credential check fails. It causes
895 ** a redirect to the "login" page.
896 */
897 void login_needed(void){
898 const char *zUrl = PD("REQUEST_URI", "index");
899 cgi_redirect(mprintf("login?g=%T", zUrl));
900 /* NOTREACHED */
901 assert(0);
 
 
 
 
 
 
 
 
 
 
902 }
903
904 /*
905 ** Call this routine if the user lacks okHistory permission. If
906 ** the anonymous user has okHistory permission, then paint a mesage
907
--- src/login.c
+++ src/login.c
@@ -84,11 +84,11 @@
84 **
85 ** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX
86 ** where the Xs are the first 16 characters of the login-group-code or
87 ** of the project-code if we are not a member of any login-group.
88 */
89 char *login_cookie_name(void){
90 static char *zCookieName = 0;
91 if( zCookieName==0 ){
92 zCookieName = db_text(0,
93 "SELECT 'fossil-' || substr(value,1,16)"
94 " FROM config"
@@ -117,15 +117,20 @@
117 ** But some clients are behind firewalls that shift the IP address
118 ** with each HTTP request. To allow such (broken) clients to log in,
119 ** extract just a prefix of the IP address.
120 */
121 static char *ipPrefix(const char *zIP){
122 int i, j;
123 static int ip_prefix_terms = -1;
124 if( ip_prefix_terms<0 ){
125 ip_prefix_terms = db_get_int("ip-prefix-terms",2);
126 }
127 if( ip_prefix_terms==0 ) return mprintf("0");
128 for(i=j=0; zIP[i]; i++){
129 if( zIP[i]=='.' ){
130 j++;
131 if( j==ip_prefix_terms ) break;
132 }
133 }
134 return mprintf("%.*s", i, zIP);
135 }
136
@@ -141,24 +146,26 @@
146
147
148 /*
149 ** Check to see if the anonymous login is valid. If it is valid, return
150 ** the userid of the anonymous user.
151 **
152 ** The zCS parameter is the "captcha seed" used for a specific
153 ** anonymous login request.
154 */
155 int login_is_valid_anonymous(
156 const char *zUsername, /* The username. Must be "anonymous" */
157 const char *zPassword, /* The supplied password */
158 const char *zCS /* The captcha seed value */
159 ){
 
160 const char *zPw; /* The correct password shown in the captcha */
161 int uid; /* The user ID of anonymous */
162
163 if( zUsername==0 ) return 0;
164 else if( zPassword==0 ) return 0;
165 else if( zCS==0 ) return 0;
166 else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
 
167 zPw = captcha_decode((unsigned int)atoi(zCS));
168 if( fossil_stricmp(zPw, zPassword)!=0 ) return 0;
169 uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
170 " AND length(pw)>0 AND length(cap)>0");
171 return uid;
@@ -192,10 +199,225 @@
199 "INSERT INTO accesslog(uname,ipaddr,success,mtime)"
200 "VALUES(%Q,%Q,%d,julianday('now'));",
201 zUsername, zIpAddr, bSuccess
202 );
203 }
204
205 /*
206 ** Searches for the user ID matching the given name and password.
207 ** On success it returns a positive value. On error it returns 0.
208 ** On serious (DB-level) error it will probably exit.
209 **
210 ** zPassword may be either the plain-text form or the encrypted
211 ** form of the user's password.
212 */
213 int login_search_uid(char const *zUsername, char const *zPasswd){
214 char * zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
215 int const uid =
216 db_int(0,
217 "SELECT uid FROM user"
218 " WHERE login=%Q"
219 " AND length(cap)>0 AND length(pw)>0"
220 " AND login NOT IN ('anonymous','nobody','developer','reader')"
221 " AND (pw=%Q OR pw=%Q)",
222 zUsername, zPasswd, zSha1Pw
223 );
224 free(zSha1Pw);
225 return uid;
226 }
227
228 /*
229 ** Generates a login cookie value for a non-anonymous user.
230 **
231 ** The zHash parameter must be a random value which must be
232 ** subsequently stored in user.cookie for later validation.
233 **
234 ** The returned memory should be free()d after use.
235 */
236 char * login_gen_user_cookie_value(char const *zUsername, char const * zHash){
237 char * zProjCode = db_get("project-code",NULL);
238 char *zCode = abbreviated_project_code(zProjCode);
239 free(zProjCode);
240 assert((zUsername && *zUsername) && "Invalid user data.");
241 return mprintf("%s/%z/%s", zHash, zCode, zUsername);
242 }
243
244 /*
245 ** Generates a login cookie for NON-ANONYMOUS users. Note that this
246 ** function "could" figure out the uid by itself but it currently
247 ** doesn't because the code which calls this already has the uid.
248 **
249 ** This function also updates the user.cookie, user.ipaddr,
250 ** and user.cexpire fields for the given user.
251 **
252 ** If zDest is not NULL then the generated cookie is copied to
253 ** *zDdest and ownership is transfered to the caller (who should
254 ** eventually pass it to free()).
255 */
256 void login_set_user_cookie(
257 char const * zUsername, /* User's name */
258 int uid, /* User's ID */
259 char ** zDest /* Optional: store generated cookie value. */
260 ){
261 const char *zCookieName = login_cookie_name();
262 const char *zExpire = db_get("cookie-expire","8766");
263 int expires = atoi(zExpire)*3600;
264 char *zHash;
265 char *zCookie;
266 char const * zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
267 char * zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
268 assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data.");
269 zHash = db_text(0, "SELECT hex(randomblob(25))");
270 zCookie = login_gen_user_cookie_value(zUsername, zHash);
271 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
272 record_login_attempt(zUsername, zIpAddr, 1);
273 db_multi_exec(
274 "UPDATE user SET cookie=%Q, ipaddr=%Q, "
275 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
276 zHash, zRemoteAddr, expires, uid
277 );
278 free(zRemoteAddr);
279 free(zHash);
280 if( zDest ){
281 *zDest = zCookie;
282 }else{
283 free(zCookie);
284 }
285 }
286
287 /* Sets a cookie for an anonymous user login, which looks like this:
288 **
289 ** HASH/TIME/anonymous
290 **
291 ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
292 ** is the abbreviated IP address and SECRET is captcha-secret.
293 **
294 ** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR
295 ** is used.
296 **
297 ** If zCookieDest is not NULL then the generated cookie is assigned to
298 ** *zCookieDest and the caller must eventually free() it.
299 */
300 void login_set_anon_cookie(char const * zIpAddr, char ** zCookieDest ){
301 char const *zNow; /* Current time (julian day number) */
302 char *zCookie; /* The login cookie */
303 char const *zCookieName; /* Name of the login cookie */
304 Blob b; /* Blob used during cookie construction */
305 char * zRemoteAddr; /* Abbreviated IP address */
306 if(!zIpAddr){
307 zIpAddr = PD("REMOTE_ADDR","nil");
308 }
309 zRemoteAddr = ipPrefix(zIpAddr);
310 zCookieName = login_cookie_name();
311 zNow = db_text("0", "SELECT julianday('now')");
312 assert( zCookieName && zRemoteAddr && zIpAddr && zNow );
313 blob_init(&b, zNow, -1);
314 blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
315 sha1sum_blob(&b, &b);
316 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
317 blob_reset(&b);
318 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
319 if( zCookieDest ){
320 *zCookieDest = zCookie;
321 }else{
322 free(zCookie);
323 }
324
325 }
326
327 /*
328 ** "Unsets" the login cookie (insofar as cookies can be unset) and
329 ** clears the current user's (g.userUid) login information from the
330 ** user table. Sets: user.cookie, user.ipaddr, user.cexpire.
331 **
332 ** We could/should arguably clear out g.userUid and g.perm here, but
333 ** we don't currently do not.
334 **
335 ** This is a no-op if g.userUid is 0.
336 */
337 void login_clear_login_data(){
338 if(!g.userUid){
339 return;
340 }else{
341 char const * cookie = login_cookie_name();
342 /* To logout, change the cookie value to an empty string */
343 cgi_set_cookie(cookie, "",
344 login_cookie_path(), -86400);
345 db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, "
346 " cexpire=0 WHERE uid=%d"
347 " AND login NOT IN ('anonymous','nobody',"
348 " 'developer','reader')", g.userUid);
349 cgi_replace_parameter(cookie, NULL)
350 /* At the time of this writing, cgi_replace_parameter() was
351 ** "NULL-value-safe", and i'm hoping the NULL doesn't cause any
352 ** downstream problems here. We could alternately use "" here.
353 */
354 ;
355 }
356 }
357
358 /*
359 ** Look at the HTTP_USER_AGENT parameter and try to determine if the user agent
360 ** is a manually operated browser or a bot. When in doubt, assume a bot. Return
361 ** true if we believe the agent is a real person.
362 */
363 static int isHuman(const char *zAgent){
364 int i;
365 if( zAgent==0 ) return 0;
366 for(i=0; zAgent[i]; i++){
367 if( zAgent[i]=='b' && memcmp(&zAgent[i],"bot",3)==0 ) return 0;
368 if( zAgent[i]=='s' && memcmp(&zAgent[i],"spider",6)==0 ) return 0;
369 }
370 if( memcmp(zAgent, "Mozilla/", 8)==0 ){
371 return atoi(&zAgent[8])>=4;
372 }
373 if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
374 if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
375 if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1;
376 return 0;
377 }
378
379 /*
380 ** COMMAND: test-ishuman
381 **
382 ** Read lines of text from standard input. Interpret each line of text
383 ** as a User-Agent string from an HTTP header. Label each line as HUMAN
384 ** or ROBOT.
385 */
386 void test_ishuman(void){
387 char zLine[3000];
388 while( fgets(zLine, sizeof(zLine), stdin) ){
389 fossil_print("%s %s", isHuman(zLine) ? "HUMAN" : "ROBOT", zLine);
390 }
391 }
392
393 /*
394 ** SQL function for constant time comparison of two values.
395 ** Sets result to 0 if two values are equal.
396 */
397 static void constant_time_cmp_function(
398 sqlite3_context *context,
399 int argc,
400 sqlite3_value **argv
401 ){
402 const unsigned char *buf1, *buf2;
403 int len, i;
404 unsigned char rc = 0;
405
406 assert( argc==2 );
407 len = sqlite3_value_bytes(argv[0]);
408 if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
409 rc = 1;
410 }else{
411 buf1 = sqlite3_value_text(argv[0]);
412 buf2 = sqlite3_value_text(argv[1]);
413 for( i=0; i<len; i++ ){
414 rc = rc | (buf1[i] ^ buf2[i]);
415 }
416 }
417 sqlite3_result_int(context, rc);
418 }
419
420 /*
421 ** WEBPAGE: login
422 ** WEBPAGE: logout
423 ** WEBPAGE: my
@@ -214,23 +436,24 @@
436 int anonFlag;
437 char *zErrMsg = "";
438 int uid; /* User id loged in user */
439 char *zSha1Pw;
440 const char *zIpAddr; /* IP address of requestor */
 
441
442 login_check_credentials();
443 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
444 constant_time_cmp_function, 0, 0);
445 zUsername = P("u");
446 zPasswd = P("p");
447 anonFlag = P("anon")!=0;
448 if( P("out")!=0 ){
449 login_clear_login_data();
 
 
450 redirect_to_g();
451 }
452 if( g.perm.Password && zPasswd
453 && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0
454 ){
455 /* The user requests a password change */
456 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
457 if( db_int(1, "SELECT 0 FROM user"
458 " WHERE uid=%d"
459 " AND (constant_time_cmp(pw,%Q)=0"
@@ -273,50 +496,20 @@
496 return;
497 }
498 }
499 }
500 zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
501 uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs"));
 
502 if( uid>0 ){
503 login_set_anon_cookie(zIpAddr, NULL);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504 record_login_attempt("anonymous", zIpAddr, 1);
505 redirect_to_g();
506 }
507 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
508 /* Attempting to log in as a user other than anonymous.
509 */
510 uid = login_search_uid(zUsername, zPasswd);
 
 
 
 
 
 
 
 
511 if( uid<=0 ){
512 sleep(1);
513 zErrMsg =
514 @ <p><span class="loginError">
515 @ You entered an unknown user or an incorrect password.
@@ -329,26 +522,11 @@
522 ** HASH/PROJECT/LOGIN
523 **
524 ** where HASH is a random hex number, PROJECT is either project
525 ** code prefix, and LOGIN is the user name.
526 */
527 login_set_user_cookie(zUsername, uid, NULL);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528 redirect_to_g();
529 }
530 }
531 style_header("Login/Logout");
532 @ %s(zErrMsg)
@@ -454,37 +632,10 @@
632 @ </form>
633 }
634 style_footer();
635 }
636
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637 /*
638 ** Attempt to find login credentials for user zLogin on a peer repository
639 ** with project code zCode. Transfer those credentials to the local
640 ** repository.
641 **
@@ -542,12 +693,16 @@
693 fossil_free(zOtherRepo);
694 return nXfer;
695 }
696
697 /*
698 ** Lookup the uid for a non-built-in user with zLogin and zCookie and
699 ** zRemoteAddr. Return 0 if not found.
700 **
701 ** Note that this only searches for logged-in entries with matching
702 ** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr)
703 ** entries.
704 */
705 static int login_find_user(
706 const char *zLogin, /* User name */
707 const char *zCookie, /* Login cookie value */
708 const char *zRemoteAddr /* Abbreviated IP address for valid login */
@@ -569,16 +724,14 @@
724 );
725 return uid;
726 }
727
728 /*
729 ** This routine examines the login cookie to see if it exists and and
730 ** is valid. If the login cookie checks out, it then sets global
731 ** variables appropriately. Global variables set include g.userUid
732 ** and g.zLogin and the g.perm family of permission booleans.
 
 
733 */
734 void login_check_credentials(void){
735 int uid = 0; /* User id */
736 const char *zCookie; /* Text of the login cookie */
737 const char *zIpAddr; /* Raw IP address of the requestor */
@@ -667,11 +820,11 @@
820 }
821 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
822 }
823
824 /* If no user found and the REMOTE_USER environment variable is set,
825 ** then accept the value of REMOTE_USER as the user.
826 */
827 if( uid==0 ){
828 const char *zRemoteUser = P("REMOTE_USER");
829 if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
830 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -717,12 +870,16 @@
870 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
871 g.zLogin = 0;
872 }
873
874 /* Set the capabilities */
875 login_replace_capabilities(zCap, 0);
876 login_set_anon_nobody_capabilities();
877 if( zCap[0] && !g.perm.History && db_get_boolean("auto-enable-hyperlinks",1)
878 && isHuman(P("HTTP_USER_AGENT")) ){
879 g.perm.History = 1;
880 }
881 }
882
883 /*
884 ** Memory of settings
885 */
@@ -746,22 +903,25 @@
903 login_anon_once = 0;
904 }
905 }
906
907 /*
908 ** Flags passed into the 2nd argument of login_set/replace_capabilities().
909 */
910 #if INTERFACE
911 #define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
912 #define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
913 #endif
914
915 /*
916 ** Adds all capability flags in zCap to g.perm.
917 */
918 void login_set_capabilities(const char *zCap, unsigned flags){
919 int i;
920 if(NULL==zCap){
921 return;
922 }
923 for(i=0; zCap[i]; i++){
924 switch( zCap[i] ){
925 case 's': g.perm.Setup = 1; /* Fall thru into Admin */
926 case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
927 g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -815,10 +975,18 @@
975 break;
976 }
977 }
978 }
979 }
980
981 /*
982 ** Zeroes out g.perm and calls login_set_capabilities(zCap,flags).
983 */
984 void login_replace_capabilities(const char *zCap, unsigned flags){
985 memset(&g.perm, 0, sizeof(g.perm));
986 login_set_capabilities(zCap, flags);
987 }
988
989 /*
990 ** If the current login lacks any of the capabilities listed in
991 ** the input, then return 0. If all capabilities are present, then
992 ** return 1.
@@ -893,14 +1061,24 @@
1061 /*
1062 ** Call this routine when the credential check fails. It causes
1063 ** a redirect to the "login" page.
1064 */
1065 void login_needed(void){
1066 #ifdef FOSSIL_ENABLE_JSON
1067 if(g.json.isJsonMode){
1068 json_err( FSL_JSON_E_DENIED, NULL, 1 );
1069 fossil_exit(0);
1070 /* NOTREACHED */
1071 assert(0);
1072 }else
1073 #endif /* FOSSIL_ENABLE_JSON */
1074 {
1075 const char *zUrl = PD("REQUEST_URI", "index");
1076 cgi_redirect(mprintf("login?g=%T", zUrl));
1077 /* NOTREACHED */
1078 assert(0);
1079 }
1080 }
1081
1082 /*
1083 ** Call this routine if the user lacks okHistory permission. If
1084 ** the anonymous user has okHistory permission, then paint a mesage
1085
+285 -107
--- src/login.c
+++ src/login.c
@@ -84,11 +84,11 @@
8484
**
8585
** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX
8686
** where the Xs are the first 16 characters of the login-group-code or
8787
** of the project-code if we are not a member of any login-group.
8888
*/
89
-static char *login_cookie_name(void){
89
+char *login_cookie_name(void){
9090
static char *zCookieName = 0;
9191
if( zCookieName==0 ){
9292
zCookieName = db_text(0,
9393
"SELECT 'fossil-' || substr(value,1,16)"
9494
" FROM config"
@@ -117,15 +117,20 @@
117117
** But some clients are behind firewalls that shift the IP address
118118
** with each HTTP request. To allow such (broken) clients to log in,
119119
** extract just a prefix of the IP address.
120120
*/
121121
static char *ipPrefix(const char *zIP){
122
- int i, j;
122
+ int i, j;
123
+ static int ip_prefix_terms = -1;
124
+ if( ip_prefix_terms<0 ){
125
+ ip_prefix_terms = db_get_int("ip-prefix-terms",2);
126
+ }
127
+ if( ip_prefix_terms==0 ) return mprintf("0");
123128
for(i=j=0; zIP[i]; i++){
124129
if( zIP[i]=='.' ){
125130
j++;
126
- if( j==2 ) break;
131
+ if( j==ip_prefix_terms ) break;
127132
}
128133
}
129134
return mprintf("%.*s", i, zIP);
130135
}
131136
@@ -141,24 +146,26 @@
141146
142147
143148
/*
144149
** Check to see if the anonymous login is valid. If it is valid, return
145150
** the userid of the anonymous user.
151
+**
152
+** The zCS parameter is the "captcha seed" used for a specific
153
+** anonymous login request.
146154
*/
147
-static int isValidAnonymousLogin(
155
+int login_is_valid_anonymous(
148156
const char *zUsername, /* The username. Must be "anonymous" */
149
- const char *zPassword /* The supplied password */
157
+ const char *zPassword, /* The supplied password */
158
+ const char *zCS /* The captcha seed value */
150159
){
151
- const char *zCS; /* The captcha seed value */
152160
const char *zPw; /* The correct password shown in the captcha */
153161
int uid; /* The user ID of anonymous */
154162
155163
if( zUsername==0 ) return 0;
156
- if( zPassword==0 ) return 0;
157
- if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
158
- zCS = P("cs"); /* The "cs" parameter is the "captcha seed" */
159
- if( zCS==0 ) return 0;
164
+ else if( zPassword==0 ) return 0;
165
+ else if( zCS==0 ) return 0;
166
+ else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
160167
zPw = captcha_decode((unsigned int)atoi(zCS));
161168
if( fossil_stricmp(zPw, zPassword)!=0 ) return 0;
162169
uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
163170
" AND length(pw)>0 AND length(cap)>0");
164171
return uid;
@@ -192,10 +199,225 @@
192199
"INSERT INTO accesslog(uname,ipaddr,success,mtime)"
193200
"VALUES(%Q,%Q,%d,julianday('now'));",
194201
zUsername, zIpAddr, bSuccess
195202
);
196203
}
204
+
205
+/*
206
+** Searches for the user ID matching the given name and password.
207
+** On success it returns a positive value. On error it returns 0.
208
+** On serious (DB-level) error it will probably exit.
209
+**
210
+** zPassword may be either the plain-text form or the encrypted
211
+** form of the user's password.
212
+*/
213
+int login_search_uid(char const *zUsername, char const *zPasswd){
214
+ char * zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
215
+ int const uid =
216
+ db_int(0,
217
+ "SELECT uid FROM user"
218
+ " WHERE login=%Q"
219
+ " AND length(cap)>0 AND length(pw)>0"
220
+ " AND login NOT IN ('anonymous','nobody','developer','reader')"
221
+ " AND (pw=%Q OR pw=%Q)",
222
+ zUsername, zPasswd, zSha1Pw
223
+ );
224
+ free(zSha1Pw);
225
+ return uid;
226
+}
227
+
228
+/*
229
+** Generates a login cookie value for a non-anonymous user.
230
+**
231
+** The zHash parameter must be a random value which must be
232
+** subsequently stored in user.cookie for later validation.
233
+**
234
+** The returned memory should be free()d after use.
235
+*/
236
+char * login_gen_user_cookie_value(char const *zUsername, char const * zHash){
237
+ char * zProjCode = db_get("project-code",NULL);
238
+ char *zCode = abbreviated_project_code(zProjCode);
239
+ free(zProjCode);
240
+ assert((zUsername && *zUsername) && "Invalid user data.");
241
+ return mprintf("%s/%z/%s", zHash, zCode, zUsername);
242
+}
243
+
244
+/*
245
+** Generates a login cookie for NON-ANONYMOUS users. Note that this
246
+** function "could" figure out the uid by itself but it currently
247
+** doesn't because the code which calls this already has the uid.
248
+**
249
+** This function also updates the user.cookie, user.ipaddr,
250
+** and user.cexpire fields for the given user.
251
+**
252
+** If zDest is not NULL then the generated cookie is copied to
253
+** *zDdest and ownership is transfered to the caller (who should
254
+** eventually pass it to free()).
255
+*/
256
+void login_set_user_cookie(
257
+ char const * zUsername, /* User's name */
258
+ int uid, /* User's ID */
259
+ char ** zDest /* Optional: store generated cookie value. */
260
+){
261
+ const char *zCookieName = login_cookie_name();
262
+ const char *zExpire = db_get("cookie-expire","8766");
263
+ int expires = atoi(zExpire)*3600;
264
+ char *zHash;
265
+ char *zCookie;
266
+ char const * zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
267
+ char * zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
268
+ assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data.");
269
+ zHash = db_text(0, "SELECT hex(randomblob(25))");
270
+ zCookie = login_gen_user_cookie_value(zUsername, zHash);
271
+ cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
272
+ record_login_attempt(zUsername, zIpAddr, 1);
273
+ db_multi_exec(
274
+ "UPDATE user SET cookie=%Q, ipaddr=%Q, "
275
+ " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
276
+ zHash, zRemoteAddr, expires, uid
277
+ );
278
+ free(zRemoteAddr);
279
+ free(zHash);
280
+ if( zDest ){
281
+ *zDest = zCookie;
282
+ }else{
283
+ free(zCookie);
284
+ }
285
+}
286
+
287
+/* Sets a cookie for an anonymous user login, which looks like this:
288
+**
289
+** HASH/TIME/anonymous
290
+**
291
+** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
292
+** is the abbreviated IP address and SECRET is captcha-secret.
293
+**
294
+** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR
295
+** is used.
296
+**
297
+** If zCookieDest is not NULL then the generated cookie is assigned to
298
+** *zCookieDest and the caller must eventually free() it.
299
+*/
300
+void login_set_anon_cookie(char const * zIpAddr, char ** zCookieDest ){
301
+ char const *zNow; /* Current time (julian day number) */
302
+ char *zCookie; /* The login cookie */
303
+ char const *zCookieName; /* Name of the login cookie */
304
+ Blob b; /* Blob used during cookie construction */
305
+ char * zRemoteAddr; /* Abbreviated IP address */
306
+ if(!zIpAddr){
307
+ zIpAddr = PD("REMOTE_ADDR","nil");
308
+ }
309
+ zRemoteAddr = ipPrefix(zIpAddr);
310
+ zCookieName = login_cookie_name();
311
+ zNow = db_text("0", "SELECT julianday('now')");
312
+ assert( zCookieName && zRemoteAddr && zIpAddr && zNow );
313
+ blob_init(&b, zNow, -1);
314
+ blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
315
+ sha1sum_blob(&b, &b);
316
+ zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
317
+ blob_reset(&b);
318
+ cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
319
+ if( zCookieDest ){
320
+ *zCookieDest = zCookie;
321
+ }else{
322
+ free(zCookie);
323
+ }
324
+
325
+}
326
+
327
+/*
328
+** "Unsets" the login cookie (insofar as cookies can be unset) and
329
+** clears the current user's (g.userUid) login information from the
330
+** user table. Sets: user.cookie, user.ipaddr, user.cexpire.
331
+**
332
+** We could/should arguably clear out g.userUid and g.perm here, but
333
+** we don't currently do not.
334
+**
335
+** This is a no-op if g.userUid is 0.
336
+*/
337
+void login_clear_login_data(){
338
+ if(!g.userUid){
339
+ return;
340
+ }else{
341
+ char const * cookie = login_cookie_name();
342
+ /* To logout, change the cookie value to an empty string */
343
+ cgi_set_cookie(cookie, "",
344
+ login_cookie_path(), -86400);
345
+ db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, "
346
+ " cexpire=0 WHERE uid=%d"
347
+ " AND login NOT IN ('anonymous','nobody',"
348
+ " 'developer','reader')", g.userUid);
349
+ cgi_replace_parameter(cookie, NULL)
350
+ /* At the time of this writing, cgi_replace_parameter() was
351
+ ** "NULL-value-safe", and i'm hoping the NULL doesn't cause any
352
+ ** downstream problems here. We could alternately use "" here.
353
+ */
354
+ ;
355
+ }
356
+}
357
+
358
+/*
359
+** Look at the HTTP_USER_AGENT parameter and try to determine if the user agent
360
+** is a manually operated browser or a bot. When in doubt, assume a bot. Return
361
+** true if we believe the agent is a real person.
362
+*/
363
+static int isHuman(const char *zAgent){
364
+ int i;
365
+ if( zAgent==0 ) return 0;
366
+ for(i=0; zAgent[i]; i++){
367
+ if( zAgent[i]=='b' && memcmp(&zAgent[i],"bot",3)==0 ) return 0;
368
+ if( zAgent[i]=='s' && memcmp(&zAgent[i],"spider",6)==0 ) return 0;
369
+ }
370
+ if( memcmp(zAgent, "Mozilla/", 8)==0 ){
371
+ return atoi(&zAgent[8])>=4;
372
+ }
373
+ if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
374
+ if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
375
+ if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1;
376
+ return 0;
377
+}
378
+
379
+/*
380
+** COMMAND: test-ishuman
381
+**
382
+** Read lines of text from standard input. Interpret each line of text
383
+** as a User-Agent string from an HTTP header. Label each line as HUMAN
384
+** or ROBOT.
385
+*/
386
+void test_ishuman(void){
387
+ char zLine[3000];
388
+ while( fgets(zLine, sizeof(zLine), stdin) ){
389
+ fossil_print("%s %s", isHuman(zLine) ? "HUMAN" : "ROBOT", zLine);
390
+ }
391
+}
392
+
393
+/*
394
+** SQL function for constant time comparison of two values.
395
+** Sets result to 0 if two values are equal.
396
+*/
397
+static void constant_time_cmp_function(
398
+ sqlite3_context *context,
399
+ int argc,
400
+ sqlite3_value **argv
401
+){
402
+ const unsigned char *buf1, *buf2;
403
+ int len, i;
404
+ unsigned char rc = 0;
405
+
406
+ assert( argc==2 );
407
+ len = sqlite3_value_bytes(argv[0]);
408
+ if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
409
+ rc = 1;
410
+ }else{
411
+ buf1 = sqlite3_value_text(argv[0]);
412
+ buf2 = sqlite3_value_text(argv[1]);
413
+ for( i=0; i<len; i++ ){
414
+ rc = rc | (buf1[i] ^ buf2[i]);
415
+ }
416
+ }
417
+ sqlite3_result_int(context, rc);
418
+}
197419
198420
/*
199421
** WEBPAGE: login
200422
** WEBPAGE: logout
201423
** WEBPAGE: my
@@ -214,23 +436,24 @@
214436
int anonFlag;
215437
char *zErrMsg = "";
216438
int uid; /* User id loged in user */
217439
char *zSha1Pw;
218440
const char *zIpAddr; /* IP address of requestor */
219
- char *zRemoteAddr; /* Abbreviated IP address of requestor */
220441
221442
login_check_credentials();
443
+ sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
444
+ constant_time_cmp_function, 0, 0);
222445
zUsername = P("u");
223446
zPasswd = P("p");
224447
anonFlag = P("anon")!=0;
225448
if( P("out")!=0 ){
226
- /* To logout, change the cookie value to an empty string */
227
- const char *zCookieName = login_cookie_name();
228
- cgi_set_cookie(zCookieName, "", login_cookie_path(), -86400);
449
+ login_clear_login_data();
229450
redirect_to_g();
230451
}
231
- if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
452
+ if( g.perm.Password && zPasswd
453
+ && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0
454
+ ){
232455
/* The user requests a password change */
233456
zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
234457
if( db_int(1, "SELECT 0 FROM user"
235458
" WHERE uid=%d"
236459
" AND (constant_time_cmp(pw,%Q)=0"
@@ -273,50 +496,20 @@
273496
return;
274497
}
275498
}
276499
}
277500
zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
278
- zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
279
- uid = isValidAnonymousLogin(zUsername, zPasswd);
501
+ uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs"));
280502
if( uid>0 ){
281
- /* Successful login as anonymous. Set a cookie that looks like
282
- ** this:
283
- **
284
- ** HASH/TIME/anonymous
285
- **
286
- ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
287
- ** is the abbreviated IP address and SECRET is captcha-secret.
288
- */
289
- char *zNow; /* Current time (julian day number) */
290
- char *zCookie; /* The login cookie */
291
- const char *zCookieName; /* Name of the login cookie */
292
- Blob b; /* Blob used during cookie construction */
293
-
294
- zCookieName = login_cookie_name();
295
- zNow = db_text("0", "SELECT julianday('now')");
296
- blob_init(&b, zNow, -1);
297
- blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
298
- sha1sum_blob(&b, &b);
299
- zCookie = sqlite3_mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
300
- blob_reset(&b);
301
- free(zNow);
302
- cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
503
+ login_set_anon_cookie(zIpAddr, NULL);
303504
record_login_attempt("anonymous", zIpAddr, 1);
304505
redirect_to_g();
305506
}
306507
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
307508
/* Attempting to log in as a user other than anonymous.
308509
*/
309
- zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
310
- uid = db_int(0,
311
- "SELECT uid FROM user"
312
- " WHERE login=%Q"
313
- " AND length(cap)>0 AND length(pw)>0"
314
- " AND login NOT IN ('anonymous','nobody','developer','reader')"
315
- " AND (constant_time_cmp(pw,%Q)=0 OR constant_time_cmp(pw,%Q)=0)",
316
- zUsername, zSha1Pw, zPasswd
317
- );
510
+ uid = login_search_uid(zUsername, zPasswd);
318511
if( uid<=0 ){
319512
sleep(1);
320513
zErrMsg =
321514
@ <p><span class="loginError">
322515
@ You entered an unknown user or an incorrect password.
@@ -329,26 +522,11 @@
329522
** HASH/PROJECT/LOGIN
330523
**
331524
** where HASH is a random hex number, PROJECT is either project
332525
** code prefix, and LOGIN is the user name.
333526
*/
334
- char *zCookie;
335
- const char *zCookieName = login_cookie_name();
336
- const char *zExpire = db_get("cookie-expire","8766");
337
- int expires = atoi(zExpire)*3600;
338
- char *zCode = abbreviated_project_code(db_get("project-code",""));
339
- char *zHash;
340
-
341
- zHash = db_text(0, "SELECT hex(randomblob(25))");
342
- zCookie = mprintf("%s/%s/%s", zHash, zCode, zUsername);
343
- cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
344
- record_login_attempt(zUsername, zIpAddr, 1);
345
- db_multi_exec(
346
- "UPDATE user SET cookie=%Q, ipaddr=%Q, "
347
- " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
348
- zHash, zRemoteAddr, expires, uid
349
- );
527
+ login_set_user_cookie(zUsername, uid, NULL);
350528
redirect_to_g();
351529
}
352530
}
353531
style_header("Login/Logout");
354532
@ %s(zErrMsg)
@@ -454,37 +632,10 @@
454632
@ </form>
455633
}
456634
style_footer();
457635
}
458636
459
-/*
460
-** SQL function for constant time comparison of two values.
461
-** Sets result to 0 if two values are equal.
462
-*/
463
-static void constant_time_cmp_function(
464
- sqlite3_context *context,
465
- int argc,
466
- sqlite3_value **argv
467
-){
468
- const unsigned char *buf1, *buf2;
469
- int len, i;
470
- unsigned char rc = 0;
471
-
472
- assert( argc==2 );
473
- len = sqlite3_value_bytes(argv[0]);
474
- if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
475
- rc = 1;
476
- }else{
477
- buf1 = sqlite3_value_text(argv[0]);
478
- buf2 = sqlite3_value_text(argv[1]);
479
- for( i=0; i<len; i++ ){
480
- rc = rc | (buf1[i] ^ buf2[i]);
481
- }
482
- }
483
- sqlite3_result_int(context, rc);
484
-}
485
-
486637
/*
487638
** Attempt to find login credentials for user zLogin on a peer repository
488639
** with project code zCode. Transfer those credentials to the local
489640
** repository.
490641
**
@@ -542,12 +693,16 @@
542693
fossil_free(zOtherRepo);
543694
return nXfer;
544695
}
545696
546697
/*
547
-** Lookup the uid for a user with zLogin and zCookie and zRemoteAddr.
548
-** Return 0 if not found.
698
+** Lookup the uid for a non-built-in user with zLogin and zCookie and
699
+** zRemoteAddr. Return 0 if not found.
700
+**
701
+** Note that this only searches for logged-in entries with matching
702
+** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr)
703
+** entries.
549704
*/
550705
static int login_find_user(
551706
const char *zLogin, /* User name */
552707
const char *zCookie, /* Login cookie value */
553708
const char *zRemoteAddr /* Abbreviated IP address for valid login */
@@ -569,16 +724,14 @@
569724
);
570725
return uid;
571726
}
572727
573728
/*
574
-** This routine examines the login cookie to see if it exists and
575
-** and is valid. If the login cookie checks out, it then sets
576
-** global variables appropriately. Global variables set include
577
-** g.userUid and g.zLogin and of the g.perm.Read family of permission
578
-** booleans.
579
-**
729
+** This routine examines the login cookie to see if it exists and and
730
+** is valid. If the login cookie checks out, it then sets global
731
+** variables appropriately. Global variables set include g.userUid
732
+** and g.zLogin and the g.perm family of permission booleans.
580733
*/
581734
void login_check_credentials(void){
582735
int uid = 0; /* User id */
583736
const char *zCookie; /* Text of the login cookie */
584737
const char *zIpAddr; /* Raw IP address of the requestor */
@@ -667,11 +820,11 @@
667820
}
668821
sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
669822
}
670823
671824
/* If no user found and the REMOTE_USER environment variable is set,
672
- ** the accept the value of REMOTE_USER as the user.
825
+ ** then accept the value of REMOTE_USER as the user.
673826
*/
674827
if( uid==0 ){
675828
const char *zRemoteUser = P("REMOTE_USER");
676829
if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
677830
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -717,12 +870,16 @@
717870
if( fossil_strcmp(g.zLogin,"nobody")==0 ){
718871
g.zLogin = 0;
719872
}
720873
721874
/* Set the capabilities */
722
- login_set_capabilities(zCap, 0);
875
+ login_replace_capabilities(zCap, 0);
723876
login_set_anon_nobody_capabilities();
877
+ if( zCap[0] && !g.perm.History && db_get_boolean("auto-enable-hyperlinks",1)
878
+ && isHuman(P("HTTP_USER_AGENT")) ){
879
+ g.perm.History = 1;
880
+ }
724881
}
725882
726883
/*
727884
** Memory of settings
728885
*/
@@ -746,22 +903,25 @@
746903
login_anon_once = 0;
747904
}
748905
}
749906
750907
/*
751
-** Flags passed into the 2nd argument of login_set_capabilities().
908
+** Flags passed into the 2nd argument of login_set/replace_capabilities().
752909
*/
753910
#if INTERFACE
754911
#define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
755912
#define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
756913
#endif
757914
758915
/*
759
-** Set the global capability flags based on a capability string.
916
+** Adds all capability flags in zCap to g.perm.
760917
*/
761918
void login_set_capabilities(const char *zCap, unsigned flags){
762919
int i;
920
+ if(NULL==zCap){
921
+ return;
922
+ }
763923
for(i=0; zCap[i]; i++){
764924
switch( zCap[i] ){
765925
case 's': g.perm.Setup = 1; /* Fall thru into Admin */
766926
case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
767927
g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -815,10 +975,18 @@
815975
break;
816976
}
817977
}
818978
}
819979
}
980
+
981
+/*
982
+** Zeroes out g.perm and calls login_set_capabilities(zCap,flags).
983
+*/
984
+void login_replace_capabilities(const char *zCap, unsigned flags){
985
+ memset(&g.perm, 0, sizeof(g.perm));
986
+ login_set_capabilities(zCap, flags);
987
+}
820988
821989
/*
822990
** If the current login lacks any of the capabilities listed in
823991
** the input, then return 0. If all capabilities are present, then
824992
** return 1.
@@ -893,14 +1061,24 @@
8931061
/*
8941062
** Call this routine when the credential check fails. It causes
8951063
** a redirect to the "login" page.
8961064
*/
8971065
void login_needed(void){
898
- const char *zUrl = PD("REQUEST_URI", "index");
899
- cgi_redirect(mprintf("login?g=%T", zUrl));
900
- /* NOTREACHED */
901
- assert(0);
1066
+#ifdef FOSSIL_ENABLE_JSON
1067
+ if(g.json.isJsonMode){
1068
+ json_err( FSL_JSON_E_DENIED, NULL, 1 );
1069
+ fossil_exit(0);
1070
+ /* NOTREACHED */
1071
+ assert(0);
1072
+ }else
1073
+#endif /* FOSSIL_ENABLE_JSON */
1074
+ {
1075
+ const char *zUrl = PD("REQUEST_URI", "index");
1076
+ cgi_redirect(mprintf("login?g=%T", zUrl));
1077
+ /* NOTREACHED */
1078
+ assert(0);
1079
+ }
9021080
}
9031081
9041082
/*
9051083
** Call this routine if the user lacks okHistory permission. If
9061084
** the anonymous user has okHistory permission, then paint a mesage
9071085
--- src/login.c
+++ src/login.c
@@ -84,11 +84,11 @@
84 **
85 ** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX
86 ** where the Xs are the first 16 characters of the login-group-code or
87 ** of the project-code if we are not a member of any login-group.
88 */
89 static char *login_cookie_name(void){
90 static char *zCookieName = 0;
91 if( zCookieName==0 ){
92 zCookieName = db_text(0,
93 "SELECT 'fossil-' || substr(value,1,16)"
94 " FROM config"
@@ -117,15 +117,20 @@
117 ** But some clients are behind firewalls that shift the IP address
118 ** with each HTTP request. To allow such (broken) clients to log in,
119 ** extract just a prefix of the IP address.
120 */
121 static char *ipPrefix(const char *zIP){
122 int i, j;
 
 
 
 
 
123 for(i=j=0; zIP[i]; i++){
124 if( zIP[i]=='.' ){
125 j++;
126 if( j==2 ) break;
127 }
128 }
129 return mprintf("%.*s", i, zIP);
130 }
131
@@ -141,24 +146,26 @@
141
142
143 /*
144 ** Check to see if the anonymous login is valid. If it is valid, return
145 ** the userid of the anonymous user.
 
 
 
146 */
147 static int isValidAnonymousLogin(
148 const char *zUsername, /* The username. Must be "anonymous" */
149 const char *zPassword /* The supplied password */
 
150 ){
151 const char *zCS; /* The captcha seed value */
152 const char *zPw; /* The correct password shown in the captcha */
153 int uid; /* The user ID of anonymous */
154
155 if( zUsername==0 ) return 0;
156 if( zPassword==0 ) return 0;
157 if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
158 zCS = P("cs"); /* The "cs" parameter is the "captcha seed" */
159 if( zCS==0 ) return 0;
160 zPw = captcha_decode((unsigned int)atoi(zCS));
161 if( fossil_stricmp(zPw, zPassword)!=0 ) return 0;
162 uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
163 " AND length(pw)>0 AND length(cap)>0");
164 return uid;
@@ -192,10 +199,225 @@
192 "INSERT INTO accesslog(uname,ipaddr,success,mtime)"
193 "VALUES(%Q,%Q,%d,julianday('now'));",
194 zUsername, zIpAddr, bSuccess
195 );
196 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
198 /*
199 ** WEBPAGE: login
200 ** WEBPAGE: logout
201 ** WEBPAGE: my
@@ -214,23 +436,24 @@
214 int anonFlag;
215 char *zErrMsg = "";
216 int uid; /* User id loged in user */
217 char *zSha1Pw;
218 const char *zIpAddr; /* IP address of requestor */
219 char *zRemoteAddr; /* Abbreviated IP address of requestor */
220
221 login_check_credentials();
 
 
222 zUsername = P("u");
223 zPasswd = P("p");
224 anonFlag = P("anon")!=0;
225 if( P("out")!=0 ){
226 /* To logout, change the cookie value to an empty string */
227 const char *zCookieName = login_cookie_name();
228 cgi_set_cookie(zCookieName, "", login_cookie_path(), -86400);
229 redirect_to_g();
230 }
231 if( g.perm.Password && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
 
 
232 /* The user requests a password change */
233 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
234 if( db_int(1, "SELECT 0 FROM user"
235 " WHERE uid=%d"
236 " AND (constant_time_cmp(pw,%Q)=0"
@@ -273,50 +496,20 @@
273 return;
274 }
275 }
276 }
277 zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
278 zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
279 uid = isValidAnonymousLogin(zUsername, zPasswd);
280 if( uid>0 ){
281 /* Successful login as anonymous. Set a cookie that looks like
282 ** this:
283 **
284 ** HASH/TIME/anonymous
285 **
286 ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
287 ** is the abbreviated IP address and SECRET is captcha-secret.
288 */
289 char *zNow; /* Current time (julian day number) */
290 char *zCookie; /* The login cookie */
291 const char *zCookieName; /* Name of the login cookie */
292 Blob b; /* Blob used during cookie construction */
293
294 zCookieName = login_cookie_name();
295 zNow = db_text("0", "SELECT julianday('now')");
296 blob_init(&b, zNow, -1);
297 blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
298 sha1sum_blob(&b, &b);
299 zCookie = sqlite3_mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
300 blob_reset(&b);
301 free(zNow);
302 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
303 record_login_attempt("anonymous", zIpAddr, 1);
304 redirect_to_g();
305 }
306 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
307 /* Attempting to log in as a user other than anonymous.
308 */
309 zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
310 uid = db_int(0,
311 "SELECT uid FROM user"
312 " WHERE login=%Q"
313 " AND length(cap)>0 AND length(pw)>0"
314 " AND login NOT IN ('anonymous','nobody','developer','reader')"
315 " AND (constant_time_cmp(pw,%Q)=0 OR constant_time_cmp(pw,%Q)=0)",
316 zUsername, zSha1Pw, zPasswd
317 );
318 if( uid<=0 ){
319 sleep(1);
320 zErrMsg =
321 @ <p><span class="loginError">
322 @ You entered an unknown user or an incorrect password.
@@ -329,26 +522,11 @@
329 ** HASH/PROJECT/LOGIN
330 **
331 ** where HASH is a random hex number, PROJECT is either project
332 ** code prefix, and LOGIN is the user name.
333 */
334 char *zCookie;
335 const char *zCookieName = login_cookie_name();
336 const char *zExpire = db_get("cookie-expire","8766");
337 int expires = atoi(zExpire)*3600;
338 char *zCode = abbreviated_project_code(db_get("project-code",""));
339 char *zHash;
340
341 zHash = db_text(0, "SELECT hex(randomblob(25))");
342 zCookie = mprintf("%s/%s/%s", zHash, zCode, zUsername);
343 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
344 record_login_attempt(zUsername, zIpAddr, 1);
345 db_multi_exec(
346 "UPDATE user SET cookie=%Q, ipaddr=%Q, "
347 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
348 zHash, zRemoteAddr, expires, uid
349 );
350 redirect_to_g();
351 }
352 }
353 style_header("Login/Logout");
354 @ %s(zErrMsg)
@@ -454,37 +632,10 @@
454 @ </form>
455 }
456 style_footer();
457 }
458
459 /*
460 ** SQL function for constant time comparison of two values.
461 ** Sets result to 0 if two values are equal.
462 */
463 static void constant_time_cmp_function(
464 sqlite3_context *context,
465 int argc,
466 sqlite3_value **argv
467 ){
468 const unsigned char *buf1, *buf2;
469 int len, i;
470 unsigned char rc = 0;
471
472 assert( argc==2 );
473 len = sqlite3_value_bytes(argv[0]);
474 if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
475 rc = 1;
476 }else{
477 buf1 = sqlite3_value_text(argv[0]);
478 buf2 = sqlite3_value_text(argv[1]);
479 for( i=0; i<len; i++ ){
480 rc = rc | (buf1[i] ^ buf2[i]);
481 }
482 }
483 sqlite3_result_int(context, rc);
484 }
485
486 /*
487 ** Attempt to find login credentials for user zLogin on a peer repository
488 ** with project code zCode. Transfer those credentials to the local
489 ** repository.
490 **
@@ -542,12 +693,16 @@
542 fossil_free(zOtherRepo);
543 return nXfer;
544 }
545
546 /*
547 ** Lookup the uid for a user with zLogin and zCookie and zRemoteAddr.
548 ** Return 0 if not found.
 
 
 
 
549 */
550 static int login_find_user(
551 const char *zLogin, /* User name */
552 const char *zCookie, /* Login cookie value */
553 const char *zRemoteAddr /* Abbreviated IP address for valid login */
@@ -569,16 +724,14 @@
569 );
570 return uid;
571 }
572
573 /*
574 ** This routine examines the login cookie to see if it exists and
575 ** and is valid. If the login cookie checks out, it then sets
576 ** global variables appropriately. Global variables set include
577 ** g.userUid and g.zLogin and of the g.perm.Read family of permission
578 ** booleans.
579 **
580 */
581 void login_check_credentials(void){
582 int uid = 0; /* User id */
583 const char *zCookie; /* Text of the login cookie */
584 const char *zIpAddr; /* Raw IP address of the requestor */
@@ -667,11 +820,11 @@
667 }
668 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
669 }
670
671 /* If no user found and the REMOTE_USER environment variable is set,
672 ** the accept the value of REMOTE_USER as the user.
673 */
674 if( uid==0 ){
675 const char *zRemoteUser = P("REMOTE_USER");
676 if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
677 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -717,12 +870,16 @@
717 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
718 g.zLogin = 0;
719 }
720
721 /* Set the capabilities */
722 login_set_capabilities(zCap, 0);
723 login_set_anon_nobody_capabilities();
 
 
 
 
724 }
725
726 /*
727 ** Memory of settings
728 */
@@ -746,22 +903,25 @@
746 login_anon_once = 0;
747 }
748 }
749
750 /*
751 ** Flags passed into the 2nd argument of login_set_capabilities().
752 */
753 #if INTERFACE
754 #define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
755 #define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
756 #endif
757
758 /*
759 ** Set the global capability flags based on a capability string.
760 */
761 void login_set_capabilities(const char *zCap, unsigned flags){
762 int i;
 
 
 
763 for(i=0; zCap[i]; i++){
764 switch( zCap[i] ){
765 case 's': g.perm.Setup = 1; /* Fall thru into Admin */
766 case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
767 g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -815,10 +975,18 @@
815 break;
816 }
817 }
818 }
819 }
 
 
 
 
 
 
 
 
820
821 /*
822 ** If the current login lacks any of the capabilities listed in
823 ** the input, then return 0. If all capabilities are present, then
824 ** return 1.
@@ -893,14 +1061,24 @@
893 /*
894 ** Call this routine when the credential check fails. It causes
895 ** a redirect to the "login" page.
896 */
897 void login_needed(void){
898 const char *zUrl = PD("REQUEST_URI", "index");
899 cgi_redirect(mprintf("login?g=%T", zUrl));
900 /* NOTREACHED */
901 assert(0);
 
 
 
 
 
 
 
 
 
 
902 }
903
904 /*
905 ** Call this routine if the user lacks okHistory permission. If
906 ** the anonymous user has okHistory permission, then paint a mesage
907
--- src/login.c
+++ src/login.c
@@ -84,11 +84,11 @@
84 **
85 ** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX
86 ** where the Xs are the first 16 characters of the login-group-code or
87 ** of the project-code if we are not a member of any login-group.
88 */
89 char *login_cookie_name(void){
90 static char *zCookieName = 0;
91 if( zCookieName==0 ){
92 zCookieName = db_text(0,
93 "SELECT 'fossil-' || substr(value,1,16)"
94 " FROM config"
@@ -117,15 +117,20 @@
117 ** But some clients are behind firewalls that shift the IP address
118 ** with each HTTP request. To allow such (broken) clients to log in,
119 ** extract just a prefix of the IP address.
120 */
121 static char *ipPrefix(const char *zIP){
122 int i, j;
123 static int ip_prefix_terms = -1;
124 if( ip_prefix_terms<0 ){
125 ip_prefix_terms = db_get_int("ip-prefix-terms",2);
126 }
127 if( ip_prefix_terms==0 ) return mprintf("0");
128 for(i=j=0; zIP[i]; i++){
129 if( zIP[i]=='.' ){
130 j++;
131 if( j==ip_prefix_terms ) break;
132 }
133 }
134 return mprintf("%.*s", i, zIP);
135 }
136
@@ -141,24 +146,26 @@
146
147
148 /*
149 ** Check to see if the anonymous login is valid. If it is valid, return
150 ** the userid of the anonymous user.
151 **
152 ** The zCS parameter is the "captcha seed" used for a specific
153 ** anonymous login request.
154 */
155 int login_is_valid_anonymous(
156 const char *zUsername, /* The username. Must be "anonymous" */
157 const char *zPassword, /* The supplied password */
158 const char *zCS /* The captcha seed value */
159 ){
 
160 const char *zPw; /* The correct password shown in the captcha */
161 int uid; /* The user ID of anonymous */
162
163 if( zUsername==0 ) return 0;
164 else if( zPassword==0 ) return 0;
165 else if( zCS==0 ) return 0;
166 else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
 
167 zPw = captcha_decode((unsigned int)atoi(zCS));
168 if( fossil_stricmp(zPw, zPassword)!=0 ) return 0;
169 uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
170 " AND length(pw)>0 AND length(cap)>0");
171 return uid;
@@ -192,10 +199,225 @@
199 "INSERT INTO accesslog(uname,ipaddr,success,mtime)"
200 "VALUES(%Q,%Q,%d,julianday('now'));",
201 zUsername, zIpAddr, bSuccess
202 );
203 }
204
205 /*
206 ** Searches for the user ID matching the given name and password.
207 ** On success it returns a positive value. On error it returns 0.
208 ** On serious (DB-level) error it will probably exit.
209 **
210 ** zPassword may be either the plain-text form or the encrypted
211 ** form of the user's password.
212 */
213 int login_search_uid(char const *zUsername, char const *zPasswd){
214 char * zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
215 int const uid =
216 db_int(0,
217 "SELECT uid FROM user"
218 " WHERE login=%Q"
219 " AND length(cap)>0 AND length(pw)>0"
220 " AND login NOT IN ('anonymous','nobody','developer','reader')"
221 " AND (pw=%Q OR pw=%Q)",
222 zUsername, zPasswd, zSha1Pw
223 );
224 free(zSha1Pw);
225 return uid;
226 }
227
228 /*
229 ** Generates a login cookie value for a non-anonymous user.
230 **
231 ** The zHash parameter must be a random value which must be
232 ** subsequently stored in user.cookie for later validation.
233 **
234 ** The returned memory should be free()d after use.
235 */
236 char * login_gen_user_cookie_value(char const *zUsername, char const * zHash){
237 char * zProjCode = db_get("project-code",NULL);
238 char *zCode = abbreviated_project_code(zProjCode);
239 free(zProjCode);
240 assert((zUsername && *zUsername) && "Invalid user data.");
241 return mprintf("%s/%z/%s", zHash, zCode, zUsername);
242 }
243
244 /*
245 ** Generates a login cookie for NON-ANONYMOUS users. Note that this
246 ** function "could" figure out the uid by itself but it currently
247 ** doesn't because the code which calls this already has the uid.
248 **
249 ** This function also updates the user.cookie, user.ipaddr,
250 ** and user.cexpire fields for the given user.
251 **
252 ** If zDest is not NULL then the generated cookie is copied to
253 ** *zDdest and ownership is transfered to the caller (who should
254 ** eventually pass it to free()).
255 */
256 void login_set_user_cookie(
257 char const * zUsername, /* User's name */
258 int uid, /* User's ID */
259 char ** zDest /* Optional: store generated cookie value. */
260 ){
261 const char *zCookieName = login_cookie_name();
262 const char *zExpire = db_get("cookie-expire","8766");
263 int expires = atoi(zExpire)*3600;
264 char *zHash;
265 char *zCookie;
266 char const * zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
267 char * zRemoteAddr = ipPrefix(zIpAddr); /* Abbreviated IP address */
268 assert((zUsername && *zUsername) && (uid > 0) && "Invalid user data.");
269 zHash = db_text(0, "SELECT hex(randomblob(25))");
270 zCookie = login_gen_user_cookie_value(zUsername, zHash);
271 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
272 record_login_attempt(zUsername, zIpAddr, 1);
273 db_multi_exec(
274 "UPDATE user SET cookie=%Q, ipaddr=%Q, "
275 " cexpire=julianday('now')+%d/86400.0 WHERE uid=%d",
276 zHash, zRemoteAddr, expires, uid
277 );
278 free(zRemoteAddr);
279 free(zHash);
280 if( zDest ){
281 *zDest = zCookie;
282 }else{
283 free(zCookie);
284 }
285 }
286
287 /* Sets a cookie for an anonymous user login, which looks like this:
288 **
289 ** HASH/TIME/anonymous
290 **
291 ** Where HASH is the sha1sum of TIME/IPADDR/SECRET, in which IPADDR
292 ** is the abbreviated IP address and SECRET is captcha-secret.
293 **
294 ** If either zIpAddr or zRemoteAddr are NULL then REMOTE_ADDR
295 ** is used.
296 **
297 ** If zCookieDest is not NULL then the generated cookie is assigned to
298 ** *zCookieDest and the caller must eventually free() it.
299 */
300 void login_set_anon_cookie(char const * zIpAddr, char ** zCookieDest ){
301 char const *zNow; /* Current time (julian day number) */
302 char *zCookie; /* The login cookie */
303 char const *zCookieName; /* Name of the login cookie */
304 Blob b; /* Blob used during cookie construction */
305 char * zRemoteAddr; /* Abbreviated IP address */
306 if(!zIpAddr){
307 zIpAddr = PD("REMOTE_ADDR","nil");
308 }
309 zRemoteAddr = ipPrefix(zIpAddr);
310 zCookieName = login_cookie_name();
311 zNow = db_text("0", "SELECT julianday('now')");
312 assert( zCookieName && zRemoteAddr && zIpAddr && zNow );
313 blob_init(&b, zNow, -1);
314 blob_appendf(&b, "/%s/%s", zRemoteAddr, db_get("captcha-secret",""));
315 sha1sum_blob(&b, &b);
316 zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
317 blob_reset(&b);
318 cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), 6*3600);
319 if( zCookieDest ){
320 *zCookieDest = zCookie;
321 }else{
322 free(zCookie);
323 }
324
325 }
326
327 /*
328 ** "Unsets" the login cookie (insofar as cookies can be unset) and
329 ** clears the current user's (g.userUid) login information from the
330 ** user table. Sets: user.cookie, user.ipaddr, user.cexpire.
331 **
332 ** We could/should arguably clear out g.userUid and g.perm here, but
333 ** we don't currently do not.
334 **
335 ** This is a no-op if g.userUid is 0.
336 */
337 void login_clear_login_data(){
338 if(!g.userUid){
339 return;
340 }else{
341 char const * cookie = login_cookie_name();
342 /* To logout, change the cookie value to an empty string */
343 cgi_set_cookie(cookie, "",
344 login_cookie_path(), -86400);
345 db_multi_exec("UPDATE user SET cookie=NULL, ipaddr=NULL, "
346 " cexpire=0 WHERE uid=%d"
347 " AND login NOT IN ('anonymous','nobody',"
348 " 'developer','reader')", g.userUid);
349 cgi_replace_parameter(cookie, NULL)
350 /* At the time of this writing, cgi_replace_parameter() was
351 ** "NULL-value-safe", and i'm hoping the NULL doesn't cause any
352 ** downstream problems here. We could alternately use "" here.
353 */
354 ;
355 }
356 }
357
358 /*
359 ** Look at the HTTP_USER_AGENT parameter and try to determine if the user agent
360 ** is a manually operated browser or a bot. When in doubt, assume a bot. Return
361 ** true if we believe the agent is a real person.
362 */
363 static int isHuman(const char *zAgent){
364 int i;
365 if( zAgent==0 ) return 0;
366 for(i=0; zAgent[i]; i++){
367 if( zAgent[i]=='b' && memcmp(&zAgent[i],"bot",3)==0 ) return 0;
368 if( zAgent[i]=='s' && memcmp(&zAgent[i],"spider",6)==0 ) return 0;
369 }
370 if( memcmp(zAgent, "Mozilla/", 8)==0 ){
371 return atoi(&zAgent[8])>=4;
372 }
373 if( memcmp(zAgent, "Opera/", 6)==0 ) return 1;
374 if( memcmp(zAgent, "Safari/", 7)==0 ) return 1;
375 if( memcmp(zAgent, "Lynx/", 5)==0 ) return 1;
376 return 0;
377 }
378
379 /*
380 ** COMMAND: test-ishuman
381 **
382 ** Read lines of text from standard input. Interpret each line of text
383 ** as a User-Agent string from an HTTP header. Label each line as HUMAN
384 ** or ROBOT.
385 */
386 void test_ishuman(void){
387 char zLine[3000];
388 while( fgets(zLine, sizeof(zLine), stdin) ){
389 fossil_print("%s %s", isHuman(zLine) ? "HUMAN" : "ROBOT", zLine);
390 }
391 }
392
393 /*
394 ** SQL function for constant time comparison of two values.
395 ** Sets result to 0 if two values are equal.
396 */
397 static void constant_time_cmp_function(
398 sqlite3_context *context,
399 int argc,
400 sqlite3_value **argv
401 ){
402 const unsigned char *buf1, *buf2;
403 int len, i;
404 unsigned char rc = 0;
405
406 assert( argc==2 );
407 len = sqlite3_value_bytes(argv[0]);
408 if( len==0 || len!=sqlite3_value_bytes(argv[1]) ){
409 rc = 1;
410 }else{
411 buf1 = sqlite3_value_text(argv[0]);
412 buf2 = sqlite3_value_text(argv[1]);
413 for( i=0; i<len; i++ ){
414 rc = rc | (buf1[i] ^ buf2[i]);
415 }
416 }
417 sqlite3_result_int(context, rc);
418 }
419
420 /*
421 ** WEBPAGE: login
422 ** WEBPAGE: logout
423 ** WEBPAGE: my
@@ -214,23 +436,24 @@
436 int anonFlag;
437 char *zErrMsg = "";
438 int uid; /* User id loged in user */
439 char *zSha1Pw;
440 const char *zIpAddr; /* IP address of requestor */
 
441
442 login_check_credentials();
443 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
444 constant_time_cmp_function, 0, 0);
445 zUsername = P("u");
446 zPasswd = P("p");
447 anonFlag = P("anon")!=0;
448 if( P("out")!=0 ){
449 login_clear_login_data();
 
 
450 redirect_to_g();
451 }
452 if( g.perm.Password && zPasswd
453 && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0
454 ){
455 /* The user requests a password change */
456 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
457 if( db_int(1, "SELECT 0 FROM user"
458 " WHERE uid=%d"
459 " AND (constant_time_cmp(pw,%Q)=0"
@@ -273,50 +496,20 @@
496 return;
497 }
498 }
499 }
500 zIpAddr = PD("REMOTE_ADDR","nil"); /* Complete IP address for logging */
501 uid = login_is_valid_anonymous(zUsername, zPasswd, P("cs"));
 
502 if( uid>0 ){
503 login_set_anon_cookie(zIpAddr, NULL);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504 record_login_attempt("anonymous", zIpAddr, 1);
505 redirect_to_g();
506 }
507 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
508 /* Attempting to log in as a user other than anonymous.
509 */
510 uid = login_search_uid(zUsername, zPasswd);
 
 
 
 
 
 
 
 
511 if( uid<=0 ){
512 sleep(1);
513 zErrMsg =
514 @ <p><span class="loginError">
515 @ You entered an unknown user or an incorrect password.
@@ -329,26 +522,11 @@
522 ** HASH/PROJECT/LOGIN
523 **
524 ** where HASH is a random hex number, PROJECT is either project
525 ** code prefix, and LOGIN is the user name.
526 */
527 login_set_user_cookie(zUsername, uid, NULL);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528 redirect_to_g();
529 }
530 }
531 style_header("Login/Logout");
532 @ %s(zErrMsg)
@@ -454,37 +632,10 @@
632 @ </form>
633 }
634 style_footer();
635 }
636
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637 /*
638 ** Attempt to find login credentials for user zLogin on a peer repository
639 ** with project code zCode. Transfer those credentials to the local
640 ** repository.
641 **
@@ -542,12 +693,16 @@
693 fossil_free(zOtherRepo);
694 return nXfer;
695 }
696
697 /*
698 ** Lookup the uid for a non-built-in user with zLogin and zCookie and
699 ** zRemoteAddr. Return 0 if not found.
700 **
701 ** Note that this only searches for logged-in entries with matching
702 ** zCookie (db: user.cookie) and zRemoteAddr (db: user.ipaddr)
703 ** entries.
704 */
705 static int login_find_user(
706 const char *zLogin, /* User name */
707 const char *zCookie, /* Login cookie value */
708 const char *zRemoteAddr /* Abbreviated IP address for valid login */
@@ -569,16 +724,14 @@
724 );
725 return uid;
726 }
727
728 /*
729 ** This routine examines the login cookie to see if it exists and and
730 ** is valid. If the login cookie checks out, it then sets global
731 ** variables appropriately. Global variables set include g.userUid
732 ** and g.zLogin and the g.perm family of permission booleans.
 
 
733 */
734 void login_check_credentials(void){
735 int uid = 0; /* User id */
736 const char *zCookie; /* Text of the login cookie */
737 const char *zIpAddr; /* Raw IP address of the requestor */
@@ -667,11 +820,11 @@
820 }
821 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash);
822 }
823
824 /* If no user found and the REMOTE_USER environment variable is set,
825 ** then accept the value of REMOTE_USER as the user.
826 */
827 if( uid==0 ){
828 const char *zRemoteUser = P("REMOTE_USER");
829 if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
830 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
@@ -717,12 +870,16 @@
870 if( fossil_strcmp(g.zLogin,"nobody")==0 ){
871 g.zLogin = 0;
872 }
873
874 /* Set the capabilities */
875 login_replace_capabilities(zCap, 0);
876 login_set_anon_nobody_capabilities();
877 if( zCap[0] && !g.perm.History && db_get_boolean("auto-enable-hyperlinks",1)
878 && isHuman(P("HTTP_USER_AGENT")) ){
879 g.perm.History = 1;
880 }
881 }
882
883 /*
884 ** Memory of settings
885 */
@@ -746,22 +903,25 @@
903 login_anon_once = 0;
904 }
905 }
906
907 /*
908 ** Flags passed into the 2nd argument of login_set/replace_capabilities().
909 */
910 #if INTERFACE
911 #define LOGIN_IGNORE_U 0x01 /* Ignore "u" */
912 #define LOGIN_IGNORE_V 0x01 /* Ignore "v" */
913 #endif
914
915 /*
916 ** Adds all capability flags in zCap to g.perm.
917 */
918 void login_set_capabilities(const char *zCap, unsigned flags){
919 int i;
920 if(NULL==zCap){
921 return;
922 }
923 for(i=0; zCap[i]; i++){
924 switch( zCap[i] ){
925 case 's': g.perm.Setup = 1; /* Fall thru into Admin */
926 case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip =
927 g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki =
@@ -815,10 +975,18 @@
975 break;
976 }
977 }
978 }
979 }
980
981 /*
982 ** Zeroes out g.perm and calls login_set_capabilities(zCap,flags).
983 */
984 void login_replace_capabilities(const char *zCap, unsigned flags){
985 memset(&g.perm, 0, sizeof(g.perm));
986 login_set_capabilities(zCap, flags);
987 }
988
989 /*
990 ** If the current login lacks any of the capabilities listed in
991 ** the input, then return 0. If all capabilities are present, then
992 ** return 1.
@@ -893,14 +1061,24 @@
1061 /*
1062 ** Call this routine when the credential check fails. It causes
1063 ** a redirect to the "login" page.
1064 */
1065 void login_needed(void){
1066 #ifdef FOSSIL_ENABLE_JSON
1067 if(g.json.isJsonMode){
1068 json_err( FSL_JSON_E_DENIED, NULL, 1 );
1069 fossil_exit(0);
1070 /* NOTREACHED */
1071 assert(0);
1072 }else
1073 #endif /* FOSSIL_ENABLE_JSON */
1074 {
1075 const char *zUrl = PD("REQUEST_URI", "index");
1076 cgi_redirect(mprintf("login?g=%T", zUrl));
1077 /* NOTREACHED */
1078 assert(0);
1079 }
1080 }
1081
1082 /*
1083 ** Call this routine if the user lacks okHistory permission. If
1084 ** the anonymous user has okHistory permission, then paint a mesage
1085
+278 -76
--- src/main.c
+++ src/main.c
@@ -23,13 +23,20 @@
2323
#include <string.h>
2424
#include <time.h>
2525
#include <fcntl.h>
2626
#include <sys/types.h>
2727
#include <sys/stat.h>
28
-
28
+#include <stdlib.h> /* atexit() */
2929
3030
#if INTERFACE
31
+#ifdef FOSSIL_ENABLE_JSON
32
+# include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */
33
+# include "json_detail.h"
34
+#endif
35
+#ifdef FOSSIL_ENABLE_TCL
36
+#include "tcl.h"
37
+#endif
3138
3239
/*
3340
** Number of elements in an array
3441
*/
3542
#define count(X) (sizeof(X)/sizeof(X[0]))
@@ -69,10 +76,23 @@
6976
char TktFmt; /* t: create new ticket report formats */
7077
char RdAddr; /* e: read email addresses or other private data */
7178
char Zip; /* z: download zipped artifact via /zip URL */
7279
char Private; /* x: can send and receive private content */
7380
};
81
+
82
+#ifdef FOSSIL_ENABLE_TCL
83
+/*
84
+** All Tcl related context information is in this structure. This structure
85
+** definition has been copied from and should be kept in sync with the one in
86
+** "th_tcl.c".
87
+*/
88
+struct TclContext {
89
+ int argc;
90
+ char **argv;
91
+ Tcl_Interp *interp;
92
+};
93
+#endif
7494
7595
/*
7696
** All global variables are in this structure.
7797
*/
7898
struct Global {
@@ -117,10 +137,11 @@
117137
int xlinkClusterOnly; /* Set when cloning. Only process clusters */
118138
int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
119139
int *aCommitFile; /* Array of files to be committed */
120140
int markPrivate; /* All new artifacts are private if true */
121141
int clockSkewSeen; /* True if clocks on client and server out of sync */
142
+ int isHTTP; /* True if running in server/CGI modes, else assume CLI. */
122143
123144
int urlIsFile; /* True if a "file:" url */
124145
int urlIsHttps; /* True if a "https:" url */
125146
int urlIsSsh; /* True if an "ssh:" url */
126147
char *urlName; /* Hostname for http: or filename for file: */
@@ -133,11 +154,11 @@
133154
char *urlPasswd; /* Password for http: */
134155
char *urlCanonical; /* Canonical representation of the URL */
135156
char *urlProxyAuth; /* Proxy-Authorizer: string */
136157
char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */
137158
int dontKeepUrl; /* Do not persist the URL */
138
-
159
+
139160
const char *zLogin; /* Login name. "" if not logged in. */
140161
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */
141162
int useLocalauth; /* No login required if from 127.0.0.1 */
142163
int noPswd; /* Logged in without password (on 127.0.0.1) */
143164
int userUid; /* Integer user id */
@@ -147,10 +168,15 @@
147168
char *zIpAddr; /* The remote IP address */
148169
char *zNonce; /* The nonce used for login */
149170
150171
/* permissions used by the server */
151172
struct FossilUserPerms perm;
173
+
174
+#ifdef FOSSIL_ENABLE_TCL
175
+ /* all Tcl related context necessary for integration */
176
+ struct TclContext tcl;
177
+#endif
152178
153179
/* For defense against Cross-site Request Forgery attacks */
154180
char zCsrfToken[12]; /* Value of the anti-CSRF token */
155181
int okCsrf; /* Anti-CSRF token is present and valid */
156182
@@ -168,10 +194,66 @@
168194
const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */
169195
const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
170196
int anAuxCols[MX_AUX]; /* Number of columns for option() values */
171197
172198
int allowSymlinks; /* Cached "allow-symlinks" option */
199
+
200
+#ifdef FOSSIL_ENABLE_JSON
201
+ struct FossilJsonBits {
202
+ int isJsonMode; /* True if running in JSON mode, else
203
+ false. This changes how errors are
204
+ reported. In JSON mode we try to
205
+ always output JSON-form error
206
+ responses and always exit() with
207
+ code 0 to avoid an HTTP 500 error.
208
+ */
209
+ int resultCode; /* used for passing back specific codes from /json callbacks. */
210
+ int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
211
+ cson_output_opt outOpt; /* formatting options for JSON mode. */
212
+ cson_value * authToken; /* authentication token */
213
+ char const * jsonp; /* Name of JSONP function wrapper. */
214
+ unsigned char dispatchDepth /* Tells JSON command dispatching
215
+ which argument we are currently
216
+ working on. For this purpose, arg#0
217
+ is the "json" path/CLI arg.
218
+ */;
219
+ struct { /* "garbage collector" */
220
+ cson_value * v;
221
+ cson_array * a;
222
+ } gc;
223
+ struct { /* JSON POST data. */
224
+ cson_value * v;
225
+ cson_array * a;
226
+ int offset; /* Tells us which PATH_INFO/CLI args
227
+ part holds the "json" command, so
228
+ that we can account for sub-repos
229
+ and path prefixes. This is handled
230
+ differently for CLI and CGI modes.
231
+ */
232
+ char const * commandStr /*"command" request param.*/;
233
+ } cmd;
234
+ struct { /* JSON POST data. */
235
+ cson_value * v;
236
+ cson_object * o;
237
+ } post;
238
+ struct { /* GET/COOKIE params in JSON mode.
239
+ FIXME (stephan): verify that this is
240
+ still used and remove if it is not.
241
+ */
242
+ cson_value * v;
243
+ cson_object * o;
244
+ } param;
245
+ struct {
246
+ cson_value * v;
247
+ cson_object * o;
248
+ } reqPayload; /* request payload object (if any) */
249
+ struct { /* response warnings */
250
+ cson_value * v;
251
+ cson_array * a;
252
+ } warnings;
253
+ } json;
254
+#endif /* FOSSIL_ENABLE_JSON */
173255
};
174256
175257
/*
176258
** Macro for debugging:
177259
*/
@@ -232,10 +314,25 @@
232314
*pIndex = m;
233315
return 0;
234316
}
235317
return 1+(cnt>1);
236318
}
319
+
320
+/*
321
+** atexit() handler which frees up "some" of the resources
322
+** used by fossil.
323
+*/
324
+void fossil_atexit(void) {
325
+#ifdef FOSSIL_ENABLE_JSON
326
+ cson_value_free(g.json.gc.v);
327
+ memset(&g.json, 0, sizeof(g.json));
328
+#endif
329
+ free(g.zErrMsg);
330
+ if(g.db){
331
+ db_close(0);
332
+ }
333
+}
237334
238335
/*
239336
** Search g.argv for arguments "--args FILENAME". If found, then
240337
** (1) remove the two arguments from g.argv
241338
** (2) Read the file FILENAME
@@ -315,27 +412,52 @@
315412
int main(int argc, char **argv){
316413
const char *zCmdName = "unknown";
317414
int idx;
318415
int rc;
319416
int i;
417
+
418
+#ifdef FOSSIL_ENABLE_TCL
419
+ g.tcl.argc = argc;
420
+ g.tcl.argv = argv;
421
+ g.tcl.interp = 0;
422
+#endif
320423
321424
sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
425
+ memset(&g, 0, sizeof(g));
322426
g.now = time(0);
323427
g.argc = argc;
324428
g.argv = argv;
429
+#ifdef FOSSIL_ENABLE_JSON
430
+#if defined(NDEBUG)
431
+ g.json.errorDetailParanoia = 2 /* FIXME: make configurable
432
+ One problem we have here is that this
433
+ code is needed before the db is opened,
434
+ so we can't sql for it.*/;
435
+#else
436
+ g.json.errorDetailParanoia = 0;
437
+#endif
438
+ g.json.outOpt = cson_output_opt_empty;
439
+ g.json.outOpt.addNewline = 1;
440
+ g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
441
+#endif /* FOSSIL_ENABLE_JSON */
325442
expand_args_option();
326443
argc = g.argc;
327444
argv = g.argv;
328445
for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
329446
if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
330447
zCmdName = "cgi";
448
+ g.isHTTP = 1;
331449
}else if( argc<2 ){
332
- fossil_fatal("Usage: %s COMMAND ...\n"
333
- "\"%s help\" for a list of available commands\n"
334
- "\"%s help COMMAND\" for specific details\n",
335
- argv[0], argv[0], argv[0]);
450
+ fossil_print(
451
+ "Usage: %s COMMAND ...\n"
452
+ " or: %s help -- for a list of common commands\n"
453
+ " or: %s help COMMMAND -- for help with the named command\n"
454
+ " or: %s commands -- for a list of all commands\n",
455
+ argv[0], argv[0], argv[0], argv[0]);
456
+ fossil_exit(1);
336457
}else{
458
+ g.isHTTP = 0;
337459
g.fQuiet = find_option("quiet", 0, 0)!=0;
338460
g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
339461
g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
340462
g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
341463
if( g.fSqlTrace ) g.fSqlStats = 1;
@@ -370,15 +492,17 @@
370492
for(i=0; i<count(aCommand); i++){
371493
if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){
372494
blob_appendf(&couldbe, " %s", aCommand[i].zName);
373495
}
374496
}
375
- fossil_fatal("%s: ambiguous command prefix: %s\n"
497
+ fossil_print("%s: ambiguous command prefix: %s\n"
376498
"%s: could be any of:%s\n"
377499
"%s: use \"help\" for more information\n",
378500
argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]);
501
+ fossil_exit(1);
379502
}
503
+ atexit( fossil_atexit );
380504
aCommand[idx].xFunc();
381505
fossil_exit(0);
382506
/*NOT_REACHED*/
383507
return 0;
384508
}
@@ -414,43 +538,70 @@
414538
** routines never return.
415539
*/
416540
NORETURN void fossil_panic(const char *zFormat, ...){
417541
char *z;
418542
va_list ap;
543
+ int rc = 1;
419544
static int once = 1;
420545
mainInFatalError = 1;
421546
va_start(ap, zFormat);
422547
z = vmprintf(zFormat, ap);
423548
va_end(ap);
424
- if( g.cgiOutput && once ){
425
- once = 0;
426
- cgi_printf("<p class=\"generalError\">%h</p>", z);
427
- cgi_reply();
428
- }else{
429
- char *zOut = mprintf("%s: %s\n", fossil_nameofexe(), z);
430
- fossil_puts(zOut, 1);
431
- }
549
+#ifdef FOSSIL_ENABLE_JSON
550
+ if( g.json.isJsonMode ){
551
+ json_err( 0, z, 1 );
552
+ if( g.isHTTP ){
553
+ rc = 0 /* avoid HTTP 500 */;
554
+ }
555
+ }
556
+ else
557
+#endif
558
+ {
559
+ if( g.cgiOutput && once ){
560
+ once = 0;
561
+ cgi_printf("<p class=\"generalError\">%h</p>", z);
562
+ cgi_reply();
563
+ }else{
564
+ char *zOut = mprintf("%s: %s\n", fossil_nameofexe(), z);
565
+ fossil_puts(zOut, 1);
566
+ }
567
+ }
568
+ free(z);
432569
db_force_rollback();
433
- fossil_exit(1);
570
+ fossil_exit(rc);
434571
}
572
+
435573
NORETURN void fossil_fatal(const char *zFormat, ...){
436574
char *z;
575
+ int rc = 1;
437576
va_list ap;
438577
mainInFatalError = 1;
439578
va_start(ap, zFormat);
440579
z = vmprintf(zFormat, ap);
441580
va_end(ap);
442
- if( g.cgiOutput ){
443
- g.cgiOutput = 0;
444
- cgi_printf("<p class=\"generalError\">%h</p>", z);
445
- cgi_reply();
446
- }else{
447
- char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
448
- fossil_puts(zOut, 1);
449
- }
581
+#ifdef FOSSIL_ENABLE_JSON
582
+ if( g.json.isJsonMode ){
583
+ json_err( g.json.resultCode, z, 1 );
584
+ if( g.isHTTP ){
585
+ rc = 0 /* avoid HTTP 500 */;
586
+ }
587
+ }
588
+ else
589
+#endif
590
+ {
591
+ if( g.cgiOutput ){
592
+ g.cgiOutput = 0;
593
+ cgi_printf("<p class=\"generalError\">%h</p>", z);
594
+ cgi_reply();
595
+ }else{
596
+ char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
597
+ fossil_puts(zOut, 1);
598
+ }
599
+ }
600
+ free(z);
450601
db_force_rollback();
451
- fossil_exit(1);
602
+ fossil_exit(rc);
452603
}
453604
454605
/* This routine works like fossil_fatal() except that if called
455606
** recursively, the recursive call is a no-op.
456607
**
@@ -461,25 +612,37 @@
461612
** be prepared for this routine to return.
462613
*/
463614
void fossil_fatal_recursive(const char *zFormat, ...){
464615
char *z;
465616
va_list ap;
617
+ int rc = 1;
466618
if( mainInFatalError ) return;
467619
mainInFatalError = 1;
468620
va_start(ap, zFormat);
469621
z = vmprintf(zFormat, ap);
470622
va_end(ap);
471
- if( g.cgiOutput ){
472
- g.cgiOutput = 0;
473
- cgi_printf("<p class=\"generalError\">%h</p>", z);
474
- cgi_reply();
475
- }else{
476
- char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
477
- fossil_puts(zOut, 1);
623
+#ifdef FOSSIL_ENABLE_JSON
624
+ if( g.json.isJsonMode ){
625
+ json_err( g.json.resultCode, z, 1 );
626
+ if( g.isHTTP ){
627
+ rc = 0 /* avoid HTTP 500 */;
628
+ }
629
+ } else
630
+#endif
631
+ {
632
+ if( g.cgiOutput ){
633
+ g.cgiOutput = 0;
634
+ cgi_printf("<p class=\"generalError\">%h</p>", z);
635
+ cgi_reply();
636
+ }else{
637
+ char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
638
+ fossil_puts(zOut, 1);
639
+ free(zOut);
640
+ }
478641
}
479642
db_force_rollback();
480
- fossil_exit(1);
643
+ fossil_exit(rc);
481644
}
482645
483646
484647
/* Print a warning message */
485648
void fossil_warning(const char *zFormat, ...){
@@ -486,17 +649,25 @@
486649
char *z;
487650
va_list ap;
488651
va_start(ap, zFormat);
489652
z = vmprintf(zFormat, ap);
490653
va_end(ap);
491
- if( g.cgiOutput ){
492
- cgi_printf("<p class=\"generalError\">%h</p>", z);
493
- }else{
494
- char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
495
- fossil_puts(zOut, 1);
496
- free(zOut);
654
+#ifdef FOSSIL_ENABLE_JSON
655
+ if(g.json.isJsonMode){
656
+ json_warn( FSL_JSON_W_UNKNOWN, z );
657
+ }else
658
+#endif
659
+ {
660
+ if( g.cgiOutput ){
661
+ cgi_printf("<p class=\"generalError\">%h</p>", z);
662
+ }else{
663
+ char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
664
+ fossil_puts(zOut, 1);
665
+ free(zOut);
666
+ }
497667
}
668
+ free(z);
498669
}
499670
500671
/*
501672
** Malloc and free routines that cannot fail
502673
*/
@@ -695,40 +866,22 @@
695866
}
696867
697868
/*
698869
** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
699870
*/
700
-static void cmd_cmd_list(const char *zPrefix){
871
+static void command_list(const char *zPrefix, int cmdMask){
701872
int i, nCmd;
702873
int nPrefix = zPrefix ? strlen(zPrefix) : 0;
703874
const char *aCmd[count(aCommand)];
704875
for(i=nCmd=0; i<count(aCommand); i++){
705876
const char *z = aCommand[i].zName;
706
- if( memcmp(z,"test",4)==0 ) continue;
877
+ if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue;
707878
if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
708879
aCmd[nCmd++] = aCommand[i].zName;
709880
}
710881
multi_column_list(aCmd, nCmd);
711882
}
712
-
713
-/*
714
-** COMMAND: test-commands
715
-**
716
-** Usage: %fossil test-commands
717
-**
718
-** List all commands used for testing and debugging.
719
-*/
720
-void cmd_test_cmd_list(void){
721
- int i, nCmd;
722
- const char *aCmd[count(aCommand)];
723
- for(i=nCmd=0; i<count(aCommand); i++){
724
- if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue;
725
- aCmd[nCmd++] = aCommand[i].zName;
726
- }
727
- multi_column_list(aCmd, nCmd);
728
-}
729
-
730883
731884
/*
732885
** COMMAND: test-list-webpage
733886
**
734887
** List all web pages
@@ -739,11 +892,10 @@
739892
for(i=nCmd=0; i<count(aWebpage); i++){
740893
aCmd[nCmd++] = aWebpage[i].zName;
741894
}
742895
multi_column_list(aCmd, nCmd);
743896
}
744
-
745897
746898
/*
747899
** COMMAND: version
748900
**
749901
** Usage: %fossil version
@@ -758,32 +910,54 @@
758910
759911
/*
760912
** COMMAND: help
761913
**
762914
** Usage: %fossil help COMMAND
915
+** or: %fossil COMMAND -help
916
+**
917
+** Display information on how to use COMMAND. To display a list of
918
+** available commands one of:
763919
**
764
-** Display information on how to use COMMAND
920
+** %fossil help Show common commands
921
+** %fossil help --all Show both command and auxiliary commands
922
+** %fossil help --test Show test commands only
923
+** %fossil help --aux Show auxiliary commands only
765924
*/
766925
void help_cmd(void){
767926
int rc, idx;
768927
const char *z;
769928
if( g.argc<3 ){
770
- fossil_print("Usage: %s help COMMAND.\nAvailable COMMANDs:\n",
771
- fossil_nameofexe());
772
- cmd_cmd_list(0);
929
+ z = fossil_nameofexe();
930
+ fossil_print(
931
+ "Usage: %s help COMMAND\n"
932
+ "Common COMMANDs: (use \"%s help --all\" for a complete list)\n",
933
+ z, z);
934
+ command_list(0, CMDFLAG_1ST_TIER);
773935
version_cmd();
774936
return;
937
+ }
938
+ if( find_option("all",0,0) ){
939
+ command_list(0, CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER);
940
+ return;
941
+ }
942
+ if( find_option("aux",0,0) ){
943
+ command_list(0, CMDFLAG_2ND_TIER);
944
+ return;
945
+ }
946
+ if( find_option("test",0,0) ){
947
+ command_list(0, CMDFLAG_TEST);
948
+ return;
775949
}
776950
rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
777951
if( rc==1 ){
778952
fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]);
779
- cmd_cmd_list(0);
953
+ command_list(0, 0xff);
780954
fossil_exit(1);
781955
}else if( rc==2 ){
782956
fossil_print("ambiguous command prefix: %s\nMatching commands:\n",
783957
g.argv[2]);
784
- cmd_cmd_list(g.argv[2]);
958
+ command_list(g.argv[2], 0xff);
785959
fossil_exit(1);
786960
}
787961
z = aCmdHelp[idx];
788962
if( z==0 ){
789963
fossil_fatal("no help available for the %s command",
@@ -1033,10 +1207,16 @@
10331207
10341208
if( szFile<1024 ){
10351209
if( zNotFound ){
10361210
cgi_redirect(zNotFound);
10371211
}else{
1212
+#ifdef FOSSIL_ENABLE_JSON
1213
+ if(g.json.isJsonMode){
1214
+ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1215
+ return;
1216
+ }
1217
+#endif
10381218
@ <h1>Not Found</h1>
10391219
cgi_set_status(404, "not found");
10401220
cgi_reply();
10411221
}
10421222
return;
@@ -1064,11 +1244,17 @@
10641244
zPathInfo = "/xfer";
10651245
}
10661246
set_base_url();
10671247
if( zPathInfo==0 || zPathInfo[0]==0
10681248
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
1069
- fossil_redirect_home();
1249
+#ifdef FOSSIL_ENABLE_JSON
1250
+ if(g.json.isJsonMode){
1251
+ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1252
+ fossil_exit(0);
1253
+ }
1254
+#endif
1255
+ fossil_redirect_home() /*does not return*/;
10701256
}else{
10711257
zPath = mprintf("%s", zPathInfo);
10721258
}
10731259
10741260
/* Make g.zPath point to the first element of the path. Make
@@ -1125,10 +1311,12 @@
11251311
break;
11261312
}
11271313
if( g.zExtra ){
11281314
/* CGI parameters get this treatment elsewhere, but places like getfile
11291315
** will use g.zExtra directly.
1316
+ ** Reminder: the login mechanism uses 'name' differently, and may
1317
+ ** eventually have a problem/collision with this.
11301318
*/
11311319
dehttpize(g.zExtra);
11321320
cgi_set_parameter_nocopy("name", g.zExtra);
11331321
}
11341322
@@ -1135,17 +1323,31 @@
11351323
/* Locate the method specified by the path and execute the function
11361324
** that implements that method.
11371325
*/
11381326
if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) &&
11391327
name_search("not_found", aWebpage, count(aWebpage), &idx) ){
1140
- cgi_set_status(404,"Not Found");
1141
- @ <h1>Not Found</h1>
1142
- @ <p>Page not found: %h(g.zPath)</p>
1328
+#ifdef FOSSIL_ENABLE_JSON
1329
+ if(g.json.isJsonMode){
1330
+ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
1331
+ }else
1332
+#endif
1333
+ {
1334
+ cgi_set_status(404,"Not Found");
1335
+ @ <h1>Not Found</h1>
1336
+ @ <p>Page not found: %h(g.zPath)</p>
1337
+ }
11431338
}else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){
1144
- @ <h1>Server Configuration Error</h1>
1145
- @ <p>The database schema on the server is out-of-date. Please ask
1146
- @ the administrator to run <b>fossil rebuild</b>.</p>
1339
+#ifdef FOSSIL_ENABLE_JSON
1340
+ if(g.json.isJsonMode){
1341
+ json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
1342
+ }else
1343
+#endif
1344
+ {
1345
+ @ <h1>Server Configuration Error</h1>
1346
+ @ <p>The database schema on the server is out-of-date. Please ask
1347
+ @ the administrator to run <b>fossil rebuild</b>.</p>
1348
+ }
11471349
}else{
11481350
aWebpage[idx].xFunc();
11491351
}
11501352
11511353
/* Return the result.
@@ -1152,11 +1354,11 @@
11521354
*/
11531355
cgi_reply();
11541356
}
11551357
11561358
/*
1157
-** COMMAND: cgi
1359
+** COMMAND: cgi*
11581360
**
11591361
** Usage: %fossil ?cgi? SCRIPT
11601362
**
11611363
** The SCRIPT argument is the name of a file that is the CGI script
11621364
** that is being run. The command name, "cgi", may be omitted if
@@ -1339,11 +1541,11 @@
13391541
**
13401542
** fossil http REPOSITORY INFILE OUTFILE IPADDR
13411543
**
13421544
** The argv==6 form is used by the win32 server only.
13431545
**
1344
-** COMMAND: http
1546
+** COMMAND: http*
13451547
**
13461548
** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https]
13471549
**
13481550
** Handle a single HTTP request appearing on stdin. The resulting webpage
13491551
** is delivered on stdout. This method is used to launch an HTTP request
@@ -1440,11 +1642,11 @@
14401642
}
14411643
#endif
14421644
#endif
14431645
14441646
/*
1445
-** COMMAND: server
1647
+** COMMAND: server*
14461648
** COMMAND: ui
14471649
**
14481650
** Usage: %fossil server ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
14491651
** Or: %fossil ui ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
14501652
**
14511653
--- src/main.c
+++ src/main.c
@@ -23,13 +23,20 @@
23 #include <string.h>
24 #include <time.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28
29
30 #if INTERFACE
 
 
 
 
 
 
 
31
32 /*
33 ** Number of elements in an array
34 */
35 #define count(X) (sizeof(X)/sizeof(X[0]))
@@ -69,10 +76,23 @@
69 char TktFmt; /* t: create new ticket report formats */
70 char RdAddr; /* e: read email addresses or other private data */
71 char Zip; /* z: download zipped artifact via /zip URL */
72 char Private; /* x: can send and receive private content */
73 };
 
 
 
 
 
 
 
 
 
 
 
 
 
74
75 /*
76 ** All global variables are in this structure.
77 */
78 struct Global {
@@ -117,10 +137,11 @@
117 int xlinkClusterOnly; /* Set when cloning. Only process clusters */
118 int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
119 int *aCommitFile; /* Array of files to be committed */
120 int markPrivate; /* All new artifacts are private if true */
121 int clockSkewSeen; /* True if clocks on client and server out of sync */
 
122
123 int urlIsFile; /* True if a "file:" url */
124 int urlIsHttps; /* True if a "https:" url */
125 int urlIsSsh; /* True if an "ssh:" url */
126 char *urlName; /* Hostname for http: or filename for file: */
@@ -133,11 +154,11 @@
133 char *urlPasswd; /* Password for http: */
134 char *urlCanonical; /* Canonical representation of the URL */
135 char *urlProxyAuth; /* Proxy-Authorizer: string */
136 char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */
137 int dontKeepUrl; /* Do not persist the URL */
138
139 const char *zLogin; /* Login name. "" if not logged in. */
140 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */
141 int useLocalauth; /* No login required if from 127.0.0.1 */
142 int noPswd; /* Logged in without password (on 127.0.0.1) */
143 int userUid; /* Integer user id */
@@ -147,10 +168,15 @@
147 char *zIpAddr; /* The remote IP address */
148 char *zNonce; /* The nonce used for login */
149
150 /* permissions used by the server */
151 struct FossilUserPerms perm;
 
 
 
 
 
152
153 /* For defense against Cross-site Request Forgery attacks */
154 char zCsrfToken[12]; /* Value of the anti-CSRF token */
155 int okCsrf; /* Anti-CSRF token is present and valid */
156
@@ -168,10 +194,66 @@
168 const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */
169 const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
170 int anAuxCols[MX_AUX]; /* Number of columns for option() values */
171
172 int allowSymlinks; /* Cached "allow-symlinks" option */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173 };
174
175 /*
176 ** Macro for debugging:
177 */
@@ -232,10 +314,25 @@
232 *pIndex = m;
233 return 0;
234 }
235 return 1+(cnt>1);
236 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
238 /*
239 ** Search g.argv for arguments "--args FILENAME". If found, then
240 ** (1) remove the two arguments from g.argv
241 ** (2) Read the file FILENAME
@@ -315,27 +412,52 @@
315 int main(int argc, char **argv){
316 const char *zCmdName = "unknown";
317 int idx;
318 int rc;
319 int i;
 
 
 
 
 
 
320
321 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
 
322 g.now = time(0);
323 g.argc = argc;
324 g.argv = argv;
 
 
 
 
 
 
 
 
 
 
 
 
 
325 expand_args_option();
326 argc = g.argc;
327 argv = g.argv;
328 for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
329 if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
330 zCmdName = "cgi";
 
331 }else if( argc<2 ){
332 fossil_fatal("Usage: %s COMMAND ...\n"
333 "\"%s help\" for a list of available commands\n"
334 "\"%s help COMMAND\" for specific details\n",
335 argv[0], argv[0], argv[0]);
 
 
 
336 }else{
 
337 g.fQuiet = find_option("quiet", 0, 0)!=0;
338 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
339 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
340 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
341 if( g.fSqlTrace ) g.fSqlStats = 1;
@@ -370,15 +492,17 @@
370 for(i=0; i<count(aCommand); i++){
371 if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){
372 blob_appendf(&couldbe, " %s", aCommand[i].zName);
373 }
374 }
375 fossil_fatal("%s: ambiguous command prefix: %s\n"
376 "%s: could be any of:%s\n"
377 "%s: use \"help\" for more information\n",
378 argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]);
 
379 }
 
380 aCommand[idx].xFunc();
381 fossil_exit(0);
382 /*NOT_REACHED*/
383 return 0;
384 }
@@ -414,43 +538,70 @@
414 ** routines never return.
415 */
416 NORETURN void fossil_panic(const char *zFormat, ...){
417 char *z;
418 va_list ap;
 
419 static int once = 1;
420 mainInFatalError = 1;
421 va_start(ap, zFormat);
422 z = vmprintf(zFormat, ap);
423 va_end(ap);
424 if( g.cgiOutput && once ){
425 once = 0;
426 cgi_printf("<p class=\"generalError\">%h</p>", z);
427 cgi_reply();
428 }else{
429 char *zOut = mprintf("%s: %s\n", fossil_nameofexe(), z);
430 fossil_puts(zOut, 1);
431 }
 
 
 
 
 
 
 
 
 
 
 
 
432 db_force_rollback();
433 fossil_exit(1);
434 }
 
435 NORETURN void fossil_fatal(const char *zFormat, ...){
436 char *z;
 
437 va_list ap;
438 mainInFatalError = 1;
439 va_start(ap, zFormat);
440 z = vmprintf(zFormat, ap);
441 va_end(ap);
442 if( g.cgiOutput ){
443 g.cgiOutput = 0;
444 cgi_printf("<p class=\"generalError\">%h</p>", z);
445 cgi_reply();
446 }else{
447 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
448 fossil_puts(zOut, 1);
449 }
 
 
 
 
 
 
 
 
 
 
 
 
450 db_force_rollback();
451 fossil_exit(1);
452 }
453
454 /* This routine works like fossil_fatal() except that if called
455 ** recursively, the recursive call is a no-op.
456 **
@@ -461,25 +612,37 @@
461 ** be prepared for this routine to return.
462 */
463 void fossil_fatal_recursive(const char *zFormat, ...){
464 char *z;
465 va_list ap;
 
466 if( mainInFatalError ) return;
467 mainInFatalError = 1;
468 va_start(ap, zFormat);
469 z = vmprintf(zFormat, ap);
470 va_end(ap);
471 if( g.cgiOutput ){
472 g.cgiOutput = 0;
473 cgi_printf("<p class=\"generalError\">%h</p>", z);
474 cgi_reply();
475 }else{
476 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
477 fossil_puts(zOut, 1);
 
 
 
 
 
 
 
 
 
 
 
478 }
479 db_force_rollback();
480 fossil_exit(1);
481 }
482
483
484 /* Print a warning message */
485 void fossil_warning(const char *zFormat, ...){
@@ -486,17 +649,25 @@
486 char *z;
487 va_list ap;
488 va_start(ap, zFormat);
489 z = vmprintf(zFormat, ap);
490 va_end(ap);
491 if( g.cgiOutput ){
492 cgi_printf("<p class=\"generalError\">%h</p>", z);
493 }else{
494 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
495 fossil_puts(zOut, 1);
496 free(zOut);
 
 
 
 
 
 
 
497 }
 
498 }
499
500 /*
501 ** Malloc and free routines that cannot fail
502 */
@@ -695,40 +866,22 @@
695 }
696
697 /*
698 ** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
699 */
700 static void cmd_cmd_list(const char *zPrefix){
701 int i, nCmd;
702 int nPrefix = zPrefix ? strlen(zPrefix) : 0;
703 const char *aCmd[count(aCommand)];
704 for(i=nCmd=0; i<count(aCommand); i++){
705 const char *z = aCommand[i].zName;
706 if( memcmp(z,"test",4)==0 ) continue;
707 if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
708 aCmd[nCmd++] = aCommand[i].zName;
709 }
710 multi_column_list(aCmd, nCmd);
711 }
712
713 /*
714 ** COMMAND: test-commands
715 **
716 ** Usage: %fossil test-commands
717 **
718 ** List all commands used for testing and debugging.
719 */
720 void cmd_test_cmd_list(void){
721 int i, nCmd;
722 const char *aCmd[count(aCommand)];
723 for(i=nCmd=0; i<count(aCommand); i++){
724 if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue;
725 aCmd[nCmd++] = aCommand[i].zName;
726 }
727 multi_column_list(aCmd, nCmd);
728 }
729
730
731 /*
732 ** COMMAND: test-list-webpage
733 **
734 ** List all web pages
@@ -739,11 +892,10 @@
739 for(i=nCmd=0; i<count(aWebpage); i++){
740 aCmd[nCmd++] = aWebpage[i].zName;
741 }
742 multi_column_list(aCmd, nCmd);
743 }
744
745
746 /*
747 ** COMMAND: version
748 **
749 ** Usage: %fossil version
@@ -758,32 +910,54 @@
758
759 /*
760 ** COMMAND: help
761 **
762 ** Usage: %fossil help COMMAND
 
 
 
 
763 **
764 ** Display information on how to use COMMAND
 
 
 
765 */
766 void help_cmd(void){
767 int rc, idx;
768 const char *z;
769 if( g.argc<3 ){
770 fossil_print("Usage: %s help COMMAND.\nAvailable COMMANDs:\n",
771 fossil_nameofexe());
772 cmd_cmd_list(0);
 
 
 
773 version_cmd();
774 return;
 
 
 
 
 
 
 
 
 
 
 
 
775 }
776 rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
777 if( rc==1 ){
778 fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]);
779 cmd_cmd_list(0);
780 fossil_exit(1);
781 }else if( rc==2 ){
782 fossil_print("ambiguous command prefix: %s\nMatching commands:\n",
783 g.argv[2]);
784 cmd_cmd_list(g.argv[2]);
785 fossil_exit(1);
786 }
787 z = aCmdHelp[idx];
788 if( z==0 ){
789 fossil_fatal("no help available for the %s command",
@@ -1033,10 +1207,16 @@
1033
1034 if( szFile<1024 ){
1035 if( zNotFound ){
1036 cgi_redirect(zNotFound);
1037 }else{
 
 
 
 
 
 
1038 @ <h1>Not Found</h1>
1039 cgi_set_status(404, "not found");
1040 cgi_reply();
1041 }
1042 return;
@@ -1064,11 +1244,17 @@
1064 zPathInfo = "/xfer";
1065 }
1066 set_base_url();
1067 if( zPathInfo==0 || zPathInfo[0]==0
1068 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
1069 fossil_redirect_home();
 
 
 
 
 
 
1070 }else{
1071 zPath = mprintf("%s", zPathInfo);
1072 }
1073
1074 /* Make g.zPath point to the first element of the path. Make
@@ -1125,10 +1311,12 @@
1125 break;
1126 }
1127 if( g.zExtra ){
1128 /* CGI parameters get this treatment elsewhere, but places like getfile
1129 ** will use g.zExtra directly.
 
 
1130 */
1131 dehttpize(g.zExtra);
1132 cgi_set_parameter_nocopy("name", g.zExtra);
1133 }
1134
@@ -1135,17 +1323,31 @@
1135 /* Locate the method specified by the path and execute the function
1136 ** that implements that method.
1137 */
1138 if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) &&
1139 name_search("not_found", aWebpage, count(aWebpage), &idx) ){
1140 cgi_set_status(404,"Not Found");
1141 @ <h1>Not Found</h1>
1142 @ <p>Page not found: %h(g.zPath)</p>
 
 
 
 
 
 
 
1143 }else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){
1144 @ <h1>Server Configuration Error</h1>
1145 @ <p>The database schema on the server is out-of-date. Please ask
1146 @ the administrator to run <b>fossil rebuild</b>.</p>
 
 
 
 
 
 
 
1147 }else{
1148 aWebpage[idx].xFunc();
1149 }
1150
1151 /* Return the result.
@@ -1152,11 +1354,11 @@
1152 */
1153 cgi_reply();
1154 }
1155
1156 /*
1157 ** COMMAND: cgi
1158 **
1159 ** Usage: %fossil ?cgi? SCRIPT
1160 **
1161 ** The SCRIPT argument is the name of a file that is the CGI script
1162 ** that is being run. The command name, "cgi", may be omitted if
@@ -1339,11 +1541,11 @@
1339 **
1340 ** fossil http REPOSITORY INFILE OUTFILE IPADDR
1341 **
1342 ** The argv==6 form is used by the win32 server only.
1343 **
1344 ** COMMAND: http
1345 **
1346 ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https]
1347 **
1348 ** Handle a single HTTP request appearing on stdin. The resulting webpage
1349 ** is delivered on stdout. This method is used to launch an HTTP request
@@ -1440,11 +1642,11 @@
1440 }
1441 #endif
1442 #endif
1443
1444 /*
1445 ** COMMAND: server
1446 ** COMMAND: ui
1447 **
1448 ** Usage: %fossil server ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
1449 ** Or: %fossil ui ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
1450 **
1451
--- src/main.c
+++ src/main.c
@@ -23,13 +23,20 @@
23 #include <string.h>
24 #include <time.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdlib.h> /* atexit() */
29
30 #if INTERFACE
31 #ifdef FOSSIL_ENABLE_JSON
32 # include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */
33 # include "json_detail.h"
34 #endif
35 #ifdef FOSSIL_ENABLE_TCL
36 #include "tcl.h"
37 #endif
38
39 /*
40 ** Number of elements in an array
41 */
42 #define count(X) (sizeof(X)/sizeof(X[0]))
@@ -69,10 +76,23 @@
76 char TktFmt; /* t: create new ticket report formats */
77 char RdAddr; /* e: read email addresses or other private data */
78 char Zip; /* z: download zipped artifact via /zip URL */
79 char Private; /* x: can send and receive private content */
80 };
81
82 #ifdef FOSSIL_ENABLE_TCL
83 /*
84 ** All Tcl related context information is in this structure. This structure
85 ** definition has been copied from and should be kept in sync with the one in
86 ** "th_tcl.c".
87 */
88 struct TclContext {
89 int argc;
90 char **argv;
91 Tcl_Interp *interp;
92 };
93 #endif
94
95 /*
96 ** All global variables are in this structure.
97 */
98 struct Global {
@@ -117,10 +137,11 @@
137 int xlinkClusterOnly; /* Set when cloning. Only process clusters */
138 int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
139 int *aCommitFile; /* Array of files to be committed */
140 int markPrivate; /* All new artifacts are private if true */
141 int clockSkewSeen; /* True if clocks on client and server out of sync */
142 int isHTTP; /* True if running in server/CGI modes, else assume CLI. */
143
144 int urlIsFile; /* True if a "file:" url */
145 int urlIsHttps; /* True if a "https:" url */
146 int urlIsSsh; /* True if an "ssh:" url */
147 char *urlName; /* Hostname for http: or filename for file: */
@@ -133,11 +154,11 @@
154 char *urlPasswd; /* Password for http: */
155 char *urlCanonical; /* Canonical representation of the URL */
156 char *urlProxyAuth; /* Proxy-Authorizer: string */
157 char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */
158 int dontKeepUrl; /* Do not persist the URL */
159
160 const char *zLogin; /* Login name. "" if not logged in. */
161 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */
162 int useLocalauth; /* No login required if from 127.0.0.1 */
163 int noPswd; /* Logged in without password (on 127.0.0.1) */
164 int userUid; /* Integer user id */
@@ -147,10 +168,15 @@
168 char *zIpAddr; /* The remote IP address */
169 char *zNonce; /* The nonce used for login */
170
171 /* permissions used by the server */
172 struct FossilUserPerms perm;
173
174 #ifdef FOSSIL_ENABLE_TCL
175 /* all Tcl related context necessary for integration */
176 struct TclContext tcl;
177 #endif
178
179 /* For defense against Cross-site Request Forgery attacks */
180 char zCsrfToken[12]; /* Value of the anti-CSRF token */
181 int okCsrf; /* Anti-CSRF token is present and valid */
182
@@ -168,10 +194,66 @@
194 const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */
195 const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
196 int anAuxCols[MX_AUX]; /* Number of columns for option() values */
197
198 int allowSymlinks; /* Cached "allow-symlinks" option */
199
200 #ifdef FOSSIL_ENABLE_JSON
201 struct FossilJsonBits {
202 int isJsonMode; /* True if running in JSON mode, else
203 false. This changes how errors are
204 reported. In JSON mode we try to
205 always output JSON-form error
206 responses and always exit() with
207 code 0 to avoid an HTTP 500 error.
208 */
209 int resultCode; /* used for passing back specific codes from /json callbacks. */
210 int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
211 cson_output_opt outOpt; /* formatting options for JSON mode. */
212 cson_value * authToken; /* authentication token */
213 char const * jsonp; /* Name of JSONP function wrapper. */
214 unsigned char dispatchDepth /* Tells JSON command dispatching
215 which argument we are currently
216 working on. For this purpose, arg#0
217 is the "json" path/CLI arg.
218 */;
219 struct { /* "garbage collector" */
220 cson_value * v;
221 cson_array * a;
222 } gc;
223 struct { /* JSON POST data. */
224 cson_value * v;
225 cson_array * a;
226 int offset; /* Tells us which PATH_INFO/CLI args
227 part holds the "json" command, so
228 that we can account for sub-repos
229 and path prefixes. This is handled
230 differently for CLI and CGI modes.
231 */
232 char const * commandStr /*"command" request param.*/;
233 } cmd;
234 struct { /* JSON POST data. */
235 cson_value * v;
236 cson_object * o;
237 } post;
238 struct { /* GET/COOKIE params in JSON mode.
239 FIXME (stephan): verify that this is
240 still used and remove if it is not.
241 */
242 cson_value * v;
243 cson_object * o;
244 } param;
245 struct {
246 cson_value * v;
247 cson_object * o;
248 } reqPayload; /* request payload object (if any) */
249 struct { /* response warnings */
250 cson_value * v;
251 cson_array * a;
252 } warnings;
253 } json;
254 #endif /* FOSSIL_ENABLE_JSON */
255 };
256
257 /*
258 ** Macro for debugging:
259 */
@@ -232,10 +314,25 @@
314 *pIndex = m;
315 return 0;
316 }
317 return 1+(cnt>1);
318 }
319
320 /*
321 ** atexit() handler which frees up "some" of the resources
322 ** used by fossil.
323 */
324 void fossil_atexit(void) {
325 #ifdef FOSSIL_ENABLE_JSON
326 cson_value_free(g.json.gc.v);
327 memset(&g.json, 0, sizeof(g.json));
328 #endif
329 free(g.zErrMsg);
330 if(g.db){
331 db_close(0);
332 }
333 }
334
335 /*
336 ** Search g.argv for arguments "--args FILENAME". If found, then
337 ** (1) remove the two arguments from g.argv
338 ** (2) Read the file FILENAME
@@ -315,27 +412,52 @@
412 int main(int argc, char **argv){
413 const char *zCmdName = "unknown";
414 int idx;
415 int rc;
416 int i;
417
418 #ifdef FOSSIL_ENABLE_TCL
419 g.tcl.argc = argc;
420 g.tcl.argv = argv;
421 g.tcl.interp = 0;
422 #endif
423
424 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
425 memset(&g, 0, sizeof(g));
426 g.now = time(0);
427 g.argc = argc;
428 g.argv = argv;
429 #ifdef FOSSIL_ENABLE_JSON
430 #if defined(NDEBUG)
431 g.json.errorDetailParanoia = 2 /* FIXME: make configurable
432 One problem we have here is that this
433 code is needed before the db is opened,
434 so we can't sql for it.*/;
435 #else
436 g.json.errorDetailParanoia = 0;
437 #endif
438 g.json.outOpt = cson_output_opt_empty;
439 g.json.outOpt.addNewline = 1;
440 g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
441 #endif /* FOSSIL_ENABLE_JSON */
442 expand_args_option();
443 argc = g.argc;
444 argv = g.argv;
445 for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
446 if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
447 zCmdName = "cgi";
448 g.isHTTP = 1;
449 }else if( argc<2 ){
450 fossil_print(
451 "Usage: %s COMMAND ...\n"
452 " or: %s help -- for a list of common commands\n"
453 " or: %s help COMMMAND -- for help with the named command\n"
454 " or: %s commands -- for a list of all commands\n",
455 argv[0], argv[0], argv[0], argv[0]);
456 fossil_exit(1);
457 }else{
458 g.isHTTP = 0;
459 g.fQuiet = find_option("quiet", 0, 0)!=0;
460 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
461 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
462 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
463 if( g.fSqlTrace ) g.fSqlStats = 1;
@@ -370,15 +492,17 @@
492 for(i=0; i<count(aCommand); i++){
493 if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){
494 blob_appendf(&couldbe, " %s", aCommand[i].zName);
495 }
496 }
497 fossil_print("%s: ambiguous command prefix: %s\n"
498 "%s: could be any of:%s\n"
499 "%s: use \"help\" for more information\n",
500 argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]);
501 fossil_exit(1);
502 }
503 atexit( fossil_atexit );
504 aCommand[idx].xFunc();
505 fossil_exit(0);
506 /*NOT_REACHED*/
507 return 0;
508 }
@@ -414,43 +538,70 @@
538 ** routines never return.
539 */
540 NORETURN void fossil_panic(const char *zFormat, ...){
541 char *z;
542 va_list ap;
543 int rc = 1;
544 static int once = 1;
545 mainInFatalError = 1;
546 va_start(ap, zFormat);
547 z = vmprintf(zFormat, ap);
548 va_end(ap);
549 #ifdef FOSSIL_ENABLE_JSON
550 if( g.json.isJsonMode ){
551 json_err( 0, z, 1 );
552 if( g.isHTTP ){
553 rc = 0 /* avoid HTTP 500 */;
554 }
555 }
556 else
557 #endif
558 {
559 if( g.cgiOutput && once ){
560 once = 0;
561 cgi_printf("<p class=\"generalError\">%h</p>", z);
562 cgi_reply();
563 }else{
564 char *zOut = mprintf("%s: %s\n", fossil_nameofexe(), z);
565 fossil_puts(zOut, 1);
566 }
567 }
568 free(z);
569 db_force_rollback();
570 fossil_exit(rc);
571 }
572
573 NORETURN void fossil_fatal(const char *zFormat, ...){
574 char *z;
575 int rc = 1;
576 va_list ap;
577 mainInFatalError = 1;
578 va_start(ap, zFormat);
579 z = vmprintf(zFormat, ap);
580 va_end(ap);
581 #ifdef FOSSIL_ENABLE_JSON
582 if( g.json.isJsonMode ){
583 json_err( g.json.resultCode, z, 1 );
584 if( g.isHTTP ){
585 rc = 0 /* avoid HTTP 500 */;
586 }
587 }
588 else
589 #endif
590 {
591 if( g.cgiOutput ){
592 g.cgiOutput = 0;
593 cgi_printf("<p class=\"generalError\">%h</p>", z);
594 cgi_reply();
595 }else{
596 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
597 fossil_puts(zOut, 1);
598 }
599 }
600 free(z);
601 db_force_rollback();
602 fossil_exit(rc);
603 }
604
605 /* This routine works like fossil_fatal() except that if called
606 ** recursively, the recursive call is a no-op.
607 **
@@ -461,25 +612,37 @@
612 ** be prepared for this routine to return.
613 */
614 void fossil_fatal_recursive(const char *zFormat, ...){
615 char *z;
616 va_list ap;
617 int rc = 1;
618 if( mainInFatalError ) return;
619 mainInFatalError = 1;
620 va_start(ap, zFormat);
621 z = vmprintf(zFormat, ap);
622 va_end(ap);
623 #ifdef FOSSIL_ENABLE_JSON
624 if( g.json.isJsonMode ){
625 json_err( g.json.resultCode, z, 1 );
626 if( g.isHTTP ){
627 rc = 0 /* avoid HTTP 500 */;
628 }
629 } else
630 #endif
631 {
632 if( g.cgiOutput ){
633 g.cgiOutput = 0;
634 cgi_printf("<p class=\"generalError\">%h</p>", z);
635 cgi_reply();
636 }else{
637 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
638 fossil_puts(zOut, 1);
639 free(zOut);
640 }
641 }
642 db_force_rollback();
643 fossil_exit(rc);
644 }
645
646
647 /* Print a warning message */
648 void fossil_warning(const char *zFormat, ...){
@@ -486,17 +649,25 @@
649 char *z;
650 va_list ap;
651 va_start(ap, zFormat);
652 z = vmprintf(zFormat, ap);
653 va_end(ap);
654 #ifdef FOSSIL_ENABLE_JSON
655 if(g.json.isJsonMode){
656 json_warn( FSL_JSON_W_UNKNOWN, z );
657 }else
658 #endif
659 {
660 if( g.cgiOutput ){
661 cgi_printf("<p class=\"generalError\">%h</p>", z);
662 }else{
663 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
664 fossil_puts(zOut, 1);
665 free(zOut);
666 }
667 }
668 free(z);
669 }
670
671 /*
672 ** Malloc and free routines that cannot fail
673 */
@@ -695,40 +866,22 @@
866 }
867
868 /*
869 ** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
870 */
871 static void command_list(const char *zPrefix, int cmdMask){
872 int i, nCmd;
873 int nPrefix = zPrefix ? strlen(zPrefix) : 0;
874 const char *aCmd[count(aCommand)];
875 for(i=nCmd=0; i<count(aCommand); i++){
876 const char *z = aCommand[i].zName;
877 if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue;
878 if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
879 aCmd[nCmd++] = aCommand[i].zName;
880 }
881 multi_column_list(aCmd, nCmd);
882 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883
884 /*
885 ** COMMAND: test-list-webpage
886 **
887 ** List all web pages
@@ -739,11 +892,10 @@
892 for(i=nCmd=0; i<count(aWebpage); i++){
893 aCmd[nCmd++] = aWebpage[i].zName;
894 }
895 multi_column_list(aCmd, nCmd);
896 }
 
897
898 /*
899 ** COMMAND: version
900 **
901 ** Usage: %fossil version
@@ -758,32 +910,54 @@
910
911 /*
912 ** COMMAND: help
913 **
914 ** Usage: %fossil help COMMAND
915 ** or: %fossil COMMAND -help
916 **
917 ** Display information on how to use COMMAND. To display a list of
918 ** available commands one of:
919 **
920 ** %fossil help Show common commands
921 ** %fossil help --all Show both command and auxiliary commands
922 ** %fossil help --test Show test commands only
923 ** %fossil help --aux Show auxiliary commands only
924 */
925 void help_cmd(void){
926 int rc, idx;
927 const char *z;
928 if( g.argc<3 ){
929 z = fossil_nameofexe();
930 fossil_print(
931 "Usage: %s help COMMAND\n"
932 "Common COMMANDs: (use \"%s help --all\" for a complete list)\n",
933 z, z);
934 command_list(0, CMDFLAG_1ST_TIER);
935 version_cmd();
936 return;
937 }
938 if( find_option("all",0,0) ){
939 command_list(0, CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER);
940 return;
941 }
942 if( find_option("aux",0,0) ){
943 command_list(0, CMDFLAG_2ND_TIER);
944 return;
945 }
946 if( find_option("test",0,0) ){
947 command_list(0, CMDFLAG_TEST);
948 return;
949 }
950 rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
951 if( rc==1 ){
952 fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]);
953 command_list(0, 0xff);
954 fossil_exit(1);
955 }else if( rc==2 ){
956 fossil_print("ambiguous command prefix: %s\nMatching commands:\n",
957 g.argv[2]);
958 command_list(g.argv[2], 0xff);
959 fossil_exit(1);
960 }
961 z = aCmdHelp[idx];
962 if( z==0 ){
963 fossil_fatal("no help available for the %s command",
@@ -1033,10 +1207,16 @@
1207
1208 if( szFile<1024 ){
1209 if( zNotFound ){
1210 cgi_redirect(zNotFound);
1211 }else{
1212 #ifdef FOSSIL_ENABLE_JSON
1213 if(g.json.isJsonMode){
1214 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1215 return;
1216 }
1217 #endif
1218 @ <h1>Not Found</h1>
1219 cgi_set_status(404, "not found");
1220 cgi_reply();
1221 }
1222 return;
@@ -1064,11 +1244,17 @@
1244 zPathInfo = "/xfer";
1245 }
1246 set_base_url();
1247 if( zPathInfo==0 || zPathInfo[0]==0
1248 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
1249 #ifdef FOSSIL_ENABLE_JSON
1250 if(g.json.isJsonMode){
1251 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1252 fossil_exit(0);
1253 }
1254 #endif
1255 fossil_redirect_home() /*does not return*/;
1256 }else{
1257 zPath = mprintf("%s", zPathInfo);
1258 }
1259
1260 /* Make g.zPath point to the first element of the path. Make
@@ -1125,10 +1311,12 @@
1311 break;
1312 }
1313 if( g.zExtra ){
1314 /* CGI parameters get this treatment elsewhere, but places like getfile
1315 ** will use g.zExtra directly.
1316 ** Reminder: the login mechanism uses 'name' differently, and may
1317 ** eventually have a problem/collision with this.
1318 */
1319 dehttpize(g.zExtra);
1320 cgi_set_parameter_nocopy("name", g.zExtra);
1321 }
1322
@@ -1135,17 +1323,31 @@
1323 /* Locate the method specified by the path and execute the function
1324 ** that implements that method.
1325 */
1326 if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) &&
1327 name_search("not_found", aWebpage, count(aWebpage), &idx) ){
1328 #ifdef FOSSIL_ENABLE_JSON
1329 if(g.json.isJsonMode){
1330 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
1331 }else
1332 #endif
1333 {
1334 cgi_set_status(404,"Not Found");
1335 @ <h1>Not Found</h1>
1336 @ <p>Page not found: %h(g.zPath)</p>
1337 }
1338 }else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){
1339 #ifdef FOSSIL_ENABLE_JSON
1340 if(g.json.isJsonMode){
1341 json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
1342 }else
1343 #endif
1344 {
1345 @ <h1>Server Configuration Error</h1>
1346 @ <p>The database schema on the server is out-of-date. Please ask
1347 @ the administrator to run <b>fossil rebuild</b>.</p>
1348 }
1349 }else{
1350 aWebpage[idx].xFunc();
1351 }
1352
1353 /* Return the result.
@@ -1152,11 +1354,11 @@
1354 */
1355 cgi_reply();
1356 }
1357
1358 /*
1359 ** COMMAND: cgi*
1360 **
1361 ** Usage: %fossil ?cgi? SCRIPT
1362 **
1363 ** The SCRIPT argument is the name of a file that is the CGI script
1364 ** that is being run. The command name, "cgi", may be omitted if
@@ -1339,11 +1541,11 @@
1541 **
1542 ** fossil http REPOSITORY INFILE OUTFILE IPADDR
1543 **
1544 ** The argv==6 form is used by the win32 server only.
1545 **
1546 ** COMMAND: http*
1547 **
1548 ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https]
1549 **
1550 ** Handle a single HTTP request appearing on stdin. The resulting webpage
1551 ** is delivered on stdout. This method is used to launch an HTTP request
@@ -1440,11 +1642,11 @@
1642 }
1643 #endif
1644 #endif
1645
1646 /*
1647 ** COMMAND: server*
1648 ** COMMAND: ui
1649 **
1650 ** Usage: %fossil server ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
1651 ** Or: %fossil ui ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
1652 **
1653
+278 -76
--- src/main.c
+++ src/main.c
@@ -23,13 +23,20 @@
2323
#include <string.h>
2424
#include <time.h>
2525
#include <fcntl.h>
2626
#include <sys/types.h>
2727
#include <sys/stat.h>
28
-
28
+#include <stdlib.h> /* atexit() */
2929
3030
#if INTERFACE
31
+#ifdef FOSSIL_ENABLE_JSON
32
+# include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */
33
+# include "json_detail.h"
34
+#endif
35
+#ifdef FOSSIL_ENABLE_TCL
36
+#include "tcl.h"
37
+#endif
3138
3239
/*
3340
** Number of elements in an array
3441
*/
3542
#define count(X) (sizeof(X)/sizeof(X[0]))
@@ -69,10 +76,23 @@
6976
char TktFmt; /* t: create new ticket report formats */
7077
char RdAddr; /* e: read email addresses or other private data */
7178
char Zip; /* z: download zipped artifact via /zip URL */
7279
char Private; /* x: can send and receive private content */
7380
};
81
+
82
+#ifdef FOSSIL_ENABLE_TCL
83
+/*
84
+** All Tcl related context information is in this structure. This structure
85
+** definition has been copied from and should be kept in sync with the one in
86
+** "th_tcl.c".
87
+*/
88
+struct TclContext {
89
+ int argc;
90
+ char **argv;
91
+ Tcl_Interp *interp;
92
+};
93
+#endif
7494
7595
/*
7696
** All global variables are in this structure.
7797
*/
7898
struct Global {
@@ -117,10 +137,11 @@
117137
int xlinkClusterOnly; /* Set when cloning. Only process clusters */
118138
int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
119139
int *aCommitFile; /* Array of files to be committed */
120140
int markPrivate; /* All new artifacts are private if true */
121141
int clockSkewSeen; /* True if clocks on client and server out of sync */
142
+ int isHTTP; /* True if running in server/CGI modes, else assume CLI. */
122143
123144
int urlIsFile; /* True if a "file:" url */
124145
int urlIsHttps; /* True if a "https:" url */
125146
int urlIsSsh; /* True if an "ssh:" url */
126147
char *urlName; /* Hostname for http: or filename for file: */
@@ -133,11 +154,11 @@
133154
char *urlPasswd; /* Password for http: */
134155
char *urlCanonical; /* Canonical representation of the URL */
135156
char *urlProxyAuth; /* Proxy-Authorizer: string */
136157
char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */
137158
int dontKeepUrl; /* Do not persist the URL */
138
-
159
+
139160
const char *zLogin; /* Login name. "" if not logged in. */
140161
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */
141162
int useLocalauth; /* No login required if from 127.0.0.1 */
142163
int noPswd; /* Logged in without password (on 127.0.0.1) */
143164
int userUid; /* Integer user id */
@@ -147,10 +168,15 @@
147168
char *zIpAddr; /* The remote IP address */
148169
char *zNonce; /* The nonce used for login */
149170
150171
/* permissions used by the server */
151172
struct FossilUserPerms perm;
173
+
174
+#ifdef FOSSIL_ENABLE_TCL
175
+ /* all Tcl related context necessary for integration */
176
+ struct TclContext tcl;
177
+#endif
152178
153179
/* For defense against Cross-site Request Forgery attacks */
154180
char zCsrfToken[12]; /* Value of the anti-CSRF token */
155181
int okCsrf; /* Anti-CSRF token is present and valid */
156182
@@ -168,10 +194,66 @@
168194
const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */
169195
const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
170196
int anAuxCols[MX_AUX]; /* Number of columns for option() values */
171197
172198
int allowSymlinks; /* Cached "allow-symlinks" option */
199
+
200
+#ifdef FOSSIL_ENABLE_JSON
201
+ struct FossilJsonBits {
202
+ int isJsonMode; /* True if running in JSON mode, else
203
+ false. This changes how errors are
204
+ reported. In JSON mode we try to
205
+ always output JSON-form error
206
+ responses and always exit() with
207
+ code 0 to avoid an HTTP 500 error.
208
+ */
209
+ int resultCode; /* used for passing back specific codes from /json callbacks. */
210
+ int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
211
+ cson_output_opt outOpt; /* formatting options for JSON mode. */
212
+ cson_value * authToken; /* authentication token */
213
+ char const * jsonp; /* Name of JSONP function wrapper. */
214
+ unsigned char dispatchDepth /* Tells JSON command dispatching
215
+ which argument we are currently
216
+ working on. For this purpose, arg#0
217
+ is the "json" path/CLI arg.
218
+ */;
219
+ struct { /* "garbage collector" */
220
+ cson_value * v;
221
+ cson_array * a;
222
+ } gc;
223
+ struct { /* JSON POST data. */
224
+ cson_value * v;
225
+ cson_array * a;
226
+ int offset; /* Tells us which PATH_INFO/CLI args
227
+ part holds the "json" command, so
228
+ that we can account for sub-repos
229
+ and path prefixes. This is handled
230
+ differently for CLI and CGI modes.
231
+ */
232
+ char const * commandStr /*"command" request param.*/;
233
+ } cmd;
234
+ struct { /* JSON POST data. */
235
+ cson_value * v;
236
+ cson_object * o;
237
+ } post;
238
+ struct { /* GET/COOKIE params in JSON mode.
239
+ FIXME (stephan): verify that this is
240
+ still used and remove if it is not.
241
+ */
242
+ cson_value * v;
243
+ cson_object * o;
244
+ } param;
245
+ struct {
246
+ cson_value * v;
247
+ cson_object * o;
248
+ } reqPayload; /* request payload object (if any) */
249
+ struct { /* response warnings */
250
+ cson_value * v;
251
+ cson_array * a;
252
+ } warnings;
253
+ } json;
254
+#endif /* FOSSIL_ENABLE_JSON */
173255
};
174256
175257
/*
176258
** Macro for debugging:
177259
*/
@@ -232,10 +314,25 @@
232314
*pIndex = m;
233315
return 0;
234316
}
235317
return 1+(cnt>1);
236318
}
319
+
320
+/*
321
+** atexit() handler which frees up "some" of the resources
322
+** used by fossil.
323
+*/
324
+void fossil_atexit(void) {
325
+#ifdef FOSSIL_ENABLE_JSON
326
+ cson_value_free(g.json.gc.v);
327
+ memset(&g.json, 0, sizeof(g.json));
328
+#endif
329
+ free(g.zErrMsg);
330
+ if(g.db){
331
+ db_close(0);
332
+ }
333
+}
237334
238335
/*
239336
** Search g.argv for arguments "--args FILENAME". If found, then
240337
** (1) remove the two arguments from g.argv
241338
** (2) Read the file FILENAME
@@ -315,27 +412,52 @@
315412
int main(int argc, char **argv){
316413
const char *zCmdName = "unknown";
317414
int idx;
318415
int rc;
319416
int i;
417
+
418
+#ifdef FOSSIL_ENABLE_TCL
419
+ g.tcl.argc = argc;
420
+ g.tcl.argv = argv;
421
+ g.tcl.interp = 0;
422
+#endif
320423
321424
sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
425
+ memset(&g, 0, sizeof(g));
322426
g.now = time(0);
323427
g.argc = argc;
324428
g.argv = argv;
429
+#ifdef FOSSIL_ENABLE_JSON
430
+#if defined(NDEBUG)
431
+ g.json.errorDetailParanoia = 2 /* FIXME: make configurable
432
+ One problem we have here is that this
433
+ code is needed before the db is opened,
434
+ so we can't sql for it.*/;
435
+#else
436
+ g.json.errorDetailParanoia = 0;
437
+#endif
438
+ g.json.outOpt = cson_output_opt_empty;
439
+ g.json.outOpt.addNewline = 1;
440
+ g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
441
+#endif /* FOSSIL_ENABLE_JSON */
325442
expand_args_option();
326443
argc = g.argc;
327444
argv = g.argv;
328445
for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
329446
if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
330447
zCmdName = "cgi";
448
+ g.isHTTP = 1;
331449
}else if( argc<2 ){
332
- fossil_fatal("Usage: %s COMMAND ...\n"
333
- "\"%s help\" for a list of available commands\n"
334
- "\"%s help COMMAND\" for specific details\n",
335
- argv[0], argv[0], argv[0]);
450
+ fossil_print(
451
+ "Usage: %s COMMAND ...\n"
452
+ " or: %s help -- for a list of common commands\n"
453
+ " or: %s help COMMMAND -- for help with the named command\n"
454
+ " or: %s commands -- for a list of all commands\n",
455
+ argv[0], argv[0], argv[0], argv[0]);
456
+ fossil_exit(1);
336457
}else{
458
+ g.isHTTP = 0;
337459
g.fQuiet = find_option("quiet", 0, 0)!=0;
338460
g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
339461
g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
340462
g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
341463
if( g.fSqlTrace ) g.fSqlStats = 1;
@@ -370,15 +492,17 @@
370492
for(i=0; i<count(aCommand); i++){
371493
if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){
372494
blob_appendf(&couldbe, " %s", aCommand[i].zName);
373495
}
374496
}
375
- fossil_fatal("%s: ambiguous command prefix: %s\n"
497
+ fossil_print("%s: ambiguous command prefix: %s\n"
376498
"%s: could be any of:%s\n"
377499
"%s: use \"help\" for more information\n",
378500
argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]);
501
+ fossil_exit(1);
379502
}
503
+ atexit( fossil_atexit );
380504
aCommand[idx].xFunc();
381505
fossil_exit(0);
382506
/*NOT_REACHED*/
383507
return 0;
384508
}
@@ -414,43 +538,70 @@
414538
** routines never return.
415539
*/
416540
NORETURN void fossil_panic(const char *zFormat, ...){
417541
char *z;
418542
va_list ap;
543
+ int rc = 1;
419544
static int once = 1;
420545
mainInFatalError = 1;
421546
va_start(ap, zFormat);
422547
z = vmprintf(zFormat, ap);
423548
va_end(ap);
424
- if( g.cgiOutput && once ){
425
- once = 0;
426
- cgi_printf("<p class=\"generalError\">%h</p>", z);
427
- cgi_reply();
428
- }else{
429
- char *zOut = mprintf("%s: %s\n", fossil_nameofexe(), z);
430
- fossil_puts(zOut, 1);
431
- }
549
+#ifdef FOSSIL_ENABLE_JSON
550
+ if( g.json.isJsonMode ){
551
+ json_err( 0, z, 1 );
552
+ if( g.isHTTP ){
553
+ rc = 0 /* avoid HTTP 500 */;
554
+ }
555
+ }
556
+ else
557
+#endif
558
+ {
559
+ if( g.cgiOutput && once ){
560
+ once = 0;
561
+ cgi_printf("<p class=\"generalError\">%h</p>", z);
562
+ cgi_reply();
563
+ }else{
564
+ char *zOut = mprintf("%s: %s\n", fossil_nameofexe(), z);
565
+ fossil_puts(zOut, 1);
566
+ }
567
+ }
568
+ free(z);
432569
db_force_rollback();
433
- fossil_exit(1);
570
+ fossil_exit(rc);
434571
}
572
+
435573
NORETURN void fossil_fatal(const char *zFormat, ...){
436574
char *z;
575
+ int rc = 1;
437576
va_list ap;
438577
mainInFatalError = 1;
439578
va_start(ap, zFormat);
440579
z = vmprintf(zFormat, ap);
441580
va_end(ap);
442
- if( g.cgiOutput ){
443
- g.cgiOutput = 0;
444
- cgi_printf("<p class=\"generalError\">%h</p>", z);
445
- cgi_reply();
446
- }else{
447
- char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
448
- fossil_puts(zOut, 1);
449
- }
581
+#ifdef FOSSIL_ENABLE_JSON
582
+ if( g.json.isJsonMode ){
583
+ json_err( g.json.resultCode, z, 1 );
584
+ if( g.isHTTP ){
585
+ rc = 0 /* avoid HTTP 500 */;
586
+ }
587
+ }
588
+ else
589
+#endif
590
+ {
591
+ if( g.cgiOutput ){
592
+ g.cgiOutput = 0;
593
+ cgi_printf("<p class=\"generalError\">%h</p>", z);
594
+ cgi_reply();
595
+ }else{
596
+ char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
597
+ fossil_puts(zOut, 1);
598
+ }
599
+ }
600
+ free(z);
450601
db_force_rollback();
451
- fossil_exit(1);
602
+ fossil_exit(rc);
452603
}
453604
454605
/* This routine works like fossil_fatal() except that if called
455606
** recursively, the recursive call is a no-op.
456607
**
@@ -461,25 +612,37 @@
461612
** be prepared for this routine to return.
462613
*/
463614
void fossil_fatal_recursive(const char *zFormat, ...){
464615
char *z;
465616
va_list ap;
617
+ int rc = 1;
466618
if( mainInFatalError ) return;
467619
mainInFatalError = 1;
468620
va_start(ap, zFormat);
469621
z = vmprintf(zFormat, ap);
470622
va_end(ap);
471
- if( g.cgiOutput ){
472
- g.cgiOutput = 0;
473
- cgi_printf("<p class=\"generalError\">%h</p>", z);
474
- cgi_reply();
475
- }else{
476
- char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
477
- fossil_puts(zOut, 1);
623
+#ifdef FOSSIL_ENABLE_JSON
624
+ if( g.json.isJsonMode ){
625
+ json_err( g.json.resultCode, z, 1 );
626
+ if( g.isHTTP ){
627
+ rc = 0 /* avoid HTTP 500 */;
628
+ }
629
+ } else
630
+#endif
631
+ {
632
+ if( g.cgiOutput ){
633
+ g.cgiOutput = 0;
634
+ cgi_printf("<p class=\"generalError\">%h</p>", z);
635
+ cgi_reply();
636
+ }else{
637
+ char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
638
+ fossil_puts(zOut, 1);
639
+ free(zOut);
640
+ }
478641
}
479642
db_force_rollback();
480
- fossil_exit(1);
643
+ fossil_exit(rc);
481644
}
482645
483646
484647
/* Print a warning message */
485648
void fossil_warning(const char *zFormat, ...){
@@ -486,17 +649,25 @@
486649
char *z;
487650
va_list ap;
488651
va_start(ap, zFormat);
489652
z = vmprintf(zFormat, ap);
490653
va_end(ap);
491
- if( g.cgiOutput ){
492
- cgi_printf("<p class=\"generalError\">%h</p>", z);
493
- }else{
494
- char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
495
- fossil_puts(zOut, 1);
496
- free(zOut);
654
+#ifdef FOSSIL_ENABLE_JSON
655
+ if(g.json.isJsonMode){
656
+ json_warn( FSL_JSON_W_UNKNOWN, z );
657
+ }else
658
+#endif
659
+ {
660
+ if( g.cgiOutput ){
661
+ cgi_printf("<p class=\"generalError\">%h</p>", z);
662
+ }else{
663
+ char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
664
+ fossil_puts(zOut, 1);
665
+ free(zOut);
666
+ }
497667
}
668
+ free(z);
498669
}
499670
500671
/*
501672
** Malloc and free routines that cannot fail
502673
*/
@@ -695,40 +866,22 @@
695866
}
696867
697868
/*
698869
** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
699870
*/
700
-static void cmd_cmd_list(const char *zPrefix){
871
+static void command_list(const char *zPrefix, int cmdMask){
701872
int i, nCmd;
702873
int nPrefix = zPrefix ? strlen(zPrefix) : 0;
703874
const char *aCmd[count(aCommand)];
704875
for(i=nCmd=0; i<count(aCommand); i++){
705876
const char *z = aCommand[i].zName;
706
- if( memcmp(z,"test",4)==0 ) continue;
877
+ if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue;
707878
if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
708879
aCmd[nCmd++] = aCommand[i].zName;
709880
}
710881
multi_column_list(aCmd, nCmd);
711882
}
712
-
713
-/*
714
-** COMMAND: test-commands
715
-**
716
-** Usage: %fossil test-commands
717
-**
718
-** List all commands used for testing and debugging.
719
-*/
720
-void cmd_test_cmd_list(void){
721
- int i, nCmd;
722
- const char *aCmd[count(aCommand)];
723
- for(i=nCmd=0; i<count(aCommand); i++){
724
- if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue;
725
- aCmd[nCmd++] = aCommand[i].zName;
726
- }
727
- multi_column_list(aCmd, nCmd);
728
-}
729
-
730883
731884
/*
732885
** COMMAND: test-list-webpage
733886
**
734887
** List all web pages
@@ -739,11 +892,10 @@
739892
for(i=nCmd=0; i<count(aWebpage); i++){
740893
aCmd[nCmd++] = aWebpage[i].zName;
741894
}
742895
multi_column_list(aCmd, nCmd);
743896
}
744
-
745897
746898
/*
747899
** COMMAND: version
748900
**
749901
** Usage: %fossil version
@@ -758,32 +910,54 @@
758910
759911
/*
760912
** COMMAND: help
761913
**
762914
** Usage: %fossil help COMMAND
915
+** or: %fossil COMMAND -help
916
+**
917
+** Display information on how to use COMMAND. To display a list of
918
+** available commands one of:
763919
**
764
-** Display information on how to use COMMAND
920
+** %fossil help Show common commands
921
+** %fossil help --all Show both command and auxiliary commands
922
+** %fossil help --test Show test commands only
923
+** %fossil help --aux Show auxiliary commands only
765924
*/
766925
void help_cmd(void){
767926
int rc, idx;
768927
const char *z;
769928
if( g.argc<3 ){
770
- fossil_print("Usage: %s help COMMAND.\nAvailable COMMANDs:\n",
771
- fossil_nameofexe());
772
- cmd_cmd_list(0);
929
+ z = fossil_nameofexe();
930
+ fossil_print(
931
+ "Usage: %s help COMMAND\n"
932
+ "Common COMMANDs: (use \"%s help --all\" for a complete list)\n",
933
+ z, z);
934
+ command_list(0, CMDFLAG_1ST_TIER);
773935
version_cmd();
774936
return;
937
+ }
938
+ if( find_option("all",0,0) ){
939
+ command_list(0, CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER);
940
+ return;
941
+ }
942
+ if( find_option("aux",0,0) ){
943
+ command_list(0, CMDFLAG_2ND_TIER);
944
+ return;
945
+ }
946
+ if( find_option("test",0,0) ){
947
+ command_list(0, CMDFLAG_TEST);
948
+ return;
775949
}
776950
rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
777951
if( rc==1 ){
778952
fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]);
779
- cmd_cmd_list(0);
953
+ command_list(0, 0xff);
780954
fossil_exit(1);
781955
}else if( rc==2 ){
782956
fossil_print("ambiguous command prefix: %s\nMatching commands:\n",
783957
g.argv[2]);
784
- cmd_cmd_list(g.argv[2]);
958
+ command_list(g.argv[2], 0xff);
785959
fossil_exit(1);
786960
}
787961
z = aCmdHelp[idx];
788962
if( z==0 ){
789963
fossil_fatal("no help available for the %s command",
@@ -1033,10 +1207,16 @@
10331207
10341208
if( szFile<1024 ){
10351209
if( zNotFound ){
10361210
cgi_redirect(zNotFound);
10371211
}else{
1212
+#ifdef FOSSIL_ENABLE_JSON
1213
+ if(g.json.isJsonMode){
1214
+ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1215
+ return;
1216
+ }
1217
+#endif
10381218
@ <h1>Not Found</h1>
10391219
cgi_set_status(404, "not found");
10401220
cgi_reply();
10411221
}
10421222
return;
@@ -1064,11 +1244,17 @@
10641244
zPathInfo = "/xfer";
10651245
}
10661246
set_base_url();
10671247
if( zPathInfo==0 || zPathInfo[0]==0
10681248
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
1069
- fossil_redirect_home();
1249
+#ifdef FOSSIL_ENABLE_JSON
1250
+ if(g.json.isJsonMode){
1251
+ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1252
+ fossil_exit(0);
1253
+ }
1254
+#endif
1255
+ fossil_redirect_home() /*does not return*/;
10701256
}else{
10711257
zPath = mprintf("%s", zPathInfo);
10721258
}
10731259
10741260
/* Make g.zPath point to the first element of the path. Make
@@ -1125,10 +1311,12 @@
11251311
break;
11261312
}
11271313
if( g.zExtra ){
11281314
/* CGI parameters get this treatment elsewhere, but places like getfile
11291315
** will use g.zExtra directly.
1316
+ ** Reminder: the login mechanism uses 'name' differently, and may
1317
+ ** eventually have a problem/collision with this.
11301318
*/
11311319
dehttpize(g.zExtra);
11321320
cgi_set_parameter_nocopy("name", g.zExtra);
11331321
}
11341322
@@ -1135,17 +1323,31 @@
11351323
/* Locate the method specified by the path and execute the function
11361324
** that implements that method.
11371325
*/
11381326
if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) &&
11391327
name_search("not_found", aWebpage, count(aWebpage), &idx) ){
1140
- cgi_set_status(404,"Not Found");
1141
- @ <h1>Not Found</h1>
1142
- @ <p>Page not found: %h(g.zPath)</p>
1328
+#ifdef FOSSIL_ENABLE_JSON
1329
+ if(g.json.isJsonMode){
1330
+ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
1331
+ }else
1332
+#endif
1333
+ {
1334
+ cgi_set_status(404,"Not Found");
1335
+ @ <h1>Not Found</h1>
1336
+ @ <p>Page not found: %h(g.zPath)</p>
1337
+ }
11431338
}else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){
1144
- @ <h1>Server Configuration Error</h1>
1145
- @ <p>The database schema on the server is out-of-date. Please ask
1146
- @ the administrator to run <b>fossil rebuild</b>.</p>
1339
+#ifdef FOSSIL_ENABLE_JSON
1340
+ if(g.json.isJsonMode){
1341
+ json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
1342
+ }else
1343
+#endif
1344
+ {
1345
+ @ <h1>Server Configuration Error</h1>
1346
+ @ <p>The database schema on the server is out-of-date. Please ask
1347
+ @ the administrator to run <b>fossil rebuild</b>.</p>
1348
+ }
11471349
}else{
11481350
aWebpage[idx].xFunc();
11491351
}
11501352
11511353
/* Return the result.
@@ -1152,11 +1354,11 @@
11521354
*/
11531355
cgi_reply();
11541356
}
11551357
11561358
/*
1157
-** COMMAND: cgi
1359
+** COMMAND: cgi*
11581360
**
11591361
** Usage: %fossil ?cgi? SCRIPT
11601362
**
11611363
** The SCRIPT argument is the name of a file that is the CGI script
11621364
** that is being run. The command name, "cgi", may be omitted if
@@ -1339,11 +1541,11 @@
13391541
**
13401542
** fossil http REPOSITORY INFILE OUTFILE IPADDR
13411543
**
13421544
** The argv==6 form is used by the win32 server only.
13431545
**
1344
-** COMMAND: http
1546
+** COMMAND: http*
13451547
**
13461548
** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https]
13471549
**
13481550
** Handle a single HTTP request appearing on stdin. The resulting webpage
13491551
** is delivered on stdout. This method is used to launch an HTTP request
@@ -1440,11 +1642,11 @@
14401642
}
14411643
#endif
14421644
#endif
14431645
14441646
/*
1445
-** COMMAND: server
1647
+** COMMAND: server*
14461648
** COMMAND: ui
14471649
**
14481650
** Usage: %fossil server ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
14491651
** Or: %fossil ui ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
14501652
**
14511653
--- src/main.c
+++ src/main.c
@@ -23,13 +23,20 @@
23 #include <string.h>
24 #include <time.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28
29
30 #if INTERFACE
 
 
 
 
 
 
 
31
32 /*
33 ** Number of elements in an array
34 */
35 #define count(X) (sizeof(X)/sizeof(X[0]))
@@ -69,10 +76,23 @@
69 char TktFmt; /* t: create new ticket report formats */
70 char RdAddr; /* e: read email addresses or other private data */
71 char Zip; /* z: download zipped artifact via /zip URL */
72 char Private; /* x: can send and receive private content */
73 };
 
 
 
 
 
 
 
 
 
 
 
 
 
74
75 /*
76 ** All global variables are in this structure.
77 */
78 struct Global {
@@ -117,10 +137,11 @@
117 int xlinkClusterOnly; /* Set when cloning. Only process clusters */
118 int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
119 int *aCommitFile; /* Array of files to be committed */
120 int markPrivate; /* All new artifacts are private if true */
121 int clockSkewSeen; /* True if clocks on client and server out of sync */
 
122
123 int urlIsFile; /* True if a "file:" url */
124 int urlIsHttps; /* True if a "https:" url */
125 int urlIsSsh; /* True if an "ssh:" url */
126 char *urlName; /* Hostname for http: or filename for file: */
@@ -133,11 +154,11 @@
133 char *urlPasswd; /* Password for http: */
134 char *urlCanonical; /* Canonical representation of the URL */
135 char *urlProxyAuth; /* Proxy-Authorizer: string */
136 char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */
137 int dontKeepUrl; /* Do not persist the URL */
138
139 const char *zLogin; /* Login name. "" if not logged in. */
140 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */
141 int useLocalauth; /* No login required if from 127.0.0.1 */
142 int noPswd; /* Logged in without password (on 127.0.0.1) */
143 int userUid; /* Integer user id */
@@ -147,10 +168,15 @@
147 char *zIpAddr; /* The remote IP address */
148 char *zNonce; /* The nonce used for login */
149
150 /* permissions used by the server */
151 struct FossilUserPerms perm;
 
 
 
 
 
152
153 /* For defense against Cross-site Request Forgery attacks */
154 char zCsrfToken[12]; /* Value of the anti-CSRF token */
155 int okCsrf; /* Anti-CSRF token is present and valid */
156
@@ -168,10 +194,66 @@
168 const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */
169 const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
170 int anAuxCols[MX_AUX]; /* Number of columns for option() values */
171
172 int allowSymlinks; /* Cached "allow-symlinks" option */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173 };
174
175 /*
176 ** Macro for debugging:
177 */
@@ -232,10 +314,25 @@
232 *pIndex = m;
233 return 0;
234 }
235 return 1+(cnt>1);
236 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
238 /*
239 ** Search g.argv for arguments "--args FILENAME". If found, then
240 ** (1) remove the two arguments from g.argv
241 ** (2) Read the file FILENAME
@@ -315,27 +412,52 @@
315 int main(int argc, char **argv){
316 const char *zCmdName = "unknown";
317 int idx;
318 int rc;
319 int i;
 
 
 
 
 
 
320
321 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
 
322 g.now = time(0);
323 g.argc = argc;
324 g.argv = argv;
 
 
 
 
 
 
 
 
 
 
 
 
 
325 expand_args_option();
326 argc = g.argc;
327 argv = g.argv;
328 for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
329 if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
330 zCmdName = "cgi";
 
331 }else if( argc<2 ){
332 fossil_fatal("Usage: %s COMMAND ...\n"
333 "\"%s help\" for a list of available commands\n"
334 "\"%s help COMMAND\" for specific details\n",
335 argv[0], argv[0], argv[0]);
 
 
 
336 }else{
 
337 g.fQuiet = find_option("quiet", 0, 0)!=0;
338 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
339 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
340 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
341 if( g.fSqlTrace ) g.fSqlStats = 1;
@@ -370,15 +492,17 @@
370 for(i=0; i<count(aCommand); i++){
371 if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){
372 blob_appendf(&couldbe, " %s", aCommand[i].zName);
373 }
374 }
375 fossil_fatal("%s: ambiguous command prefix: %s\n"
376 "%s: could be any of:%s\n"
377 "%s: use \"help\" for more information\n",
378 argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]);
 
379 }
 
380 aCommand[idx].xFunc();
381 fossil_exit(0);
382 /*NOT_REACHED*/
383 return 0;
384 }
@@ -414,43 +538,70 @@
414 ** routines never return.
415 */
416 NORETURN void fossil_panic(const char *zFormat, ...){
417 char *z;
418 va_list ap;
 
419 static int once = 1;
420 mainInFatalError = 1;
421 va_start(ap, zFormat);
422 z = vmprintf(zFormat, ap);
423 va_end(ap);
424 if( g.cgiOutput && once ){
425 once = 0;
426 cgi_printf("<p class=\"generalError\">%h</p>", z);
427 cgi_reply();
428 }else{
429 char *zOut = mprintf("%s: %s\n", fossil_nameofexe(), z);
430 fossil_puts(zOut, 1);
431 }
 
 
 
 
 
 
 
 
 
 
 
 
432 db_force_rollback();
433 fossil_exit(1);
434 }
 
435 NORETURN void fossil_fatal(const char *zFormat, ...){
436 char *z;
 
437 va_list ap;
438 mainInFatalError = 1;
439 va_start(ap, zFormat);
440 z = vmprintf(zFormat, ap);
441 va_end(ap);
442 if( g.cgiOutput ){
443 g.cgiOutput = 0;
444 cgi_printf("<p class=\"generalError\">%h</p>", z);
445 cgi_reply();
446 }else{
447 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
448 fossil_puts(zOut, 1);
449 }
 
 
 
 
 
 
 
 
 
 
 
 
450 db_force_rollback();
451 fossil_exit(1);
452 }
453
454 /* This routine works like fossil_fatal() except that if called
455 ** recursively, the recursive call is a no-op.
456 **
@@ -461,25 +612,37 @@
461 ** be prepared for this routine to return.
462 */
463 void fossil_fatal_recursive(const char *zFormat, ...){
464 char *z;
465 va_list ap;
 
466 if( mainInFatalError ) return;
467 mainInFatalError = 1;
468 va_start(ap, zFormat);
469 z = vmprintf(zFormat, ap);
470 va_end(ap);
471 if( g.cgiOutput ){
472 g.cgiOutput = 0;
473 cgi_printf("<p class=\"generalError\">%h</p>", z);
474 cgi_reply();
475 }else{
476 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
477 fossil_puts(zOut, 1);
 
 
 
 
 
 
 
 
 
 
 
478 }
479 db_force_rollback();
480 fossil_exit(1);
481 }
482
483
484 /* Print a warning message */
485 void fossil_warning(const char *zFormat, ...){
@@ -486,17 +649,25 @@
486 char *z;
487 va_list ap;
488 va_start(ap, zFormat);
489 z = vmprintf(zFormat, ap);
490 va_end(ap);
491 if( g.cgiOutput ){
492 cgi_printf("<p class=\"generalError\">%h</p>", z);
493 }else{
494 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
495 fossil_puts(zOut, 1);
496 free(zOut);
 
 
 
 
 
 
 
497 }
 
498 }
499
500 /*
501 ** Malloc and free routines that cannot fail
502 */
@@ -695,40 +866,22 @@
695 }
696
697 /*
698 ** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
699 */
700 static void cmd_cmd_list(const char *zPrefix){
701 int i, nCmd;
702 int nPrefix = zPrefix ? strlen(zPrefix) : 0;
703 const char *aCmd[count(aCommand)];
704 for(i=nCmd=0; i<count(aCommand); i++){
705 const char *z = aCommand[i].zName;
706 if( memcmp(z,"test",4)==0 ) continue;
707 if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
708 aCmd[nCmd++] = aCommand[i].zName;
709 }
710 multi_column_list(aCmd, nCmd);
711 }
712
713 /*
714 ** COMMAND: test-commands
715 **
716 ** Usage: %fossil test-commands
717 **
718 ** List all commands used for testing and debugging.
719 */
720 void cmd_test_cmd_list(void){
721 int i, nCmd;
722 const char *aCmd[count(aCommand)];
723 for(i=nCmd=0; i<count(aCommand); i++){
724 if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue;
725 aCmd[nCmd++] = aCommand[i].zName;
726 }
727 multi_column_list(aCmd, nCmd);
728 }
729
730
731 /*
732 ** COMMAND: test-list-webpage
733 **
734 ** List all web pages
@@ -739,11 +892,10 @@
739 for(i=nCmd=0; i<count(aWebpage); i++){
740 aCmd[nCmd++] = aWebpage[i].zName;
741 }
742 multi_column_list(aCmd, nCmd);
743 }
744
745
746 /*
747 ** COMMAND: version
748 **
749 ** Usage: %fossil version
@@ -758,32 +910,54 @@
758
759 /*
760 ** COMMAND: help
761 **
762 ** Usage: %fossil help COMMAND
 
 
 
 
763 **
764 ** Display information on how to use COMMAND
 
 
 
765 */
766 void help_cmd(void){
767 int rc, idx;
768 const char *z;
769 if( g.argc<3 ){
770 fossil_print("Usage: %s help COMMAND.\nAvailable COMMANDs:\n",
771 fossil_nameofexe());
772 cmd_cmd_list(0);
 
 
 
773 version_cmd();
774 return;
 
 
 
 
 
 
 
 
 
 
 
 
775 }
776 rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
777 if( rc==1 ){
778 fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]);
779 cmd_cmd_list(0);
780 fossil_exit(1);
781 }else if( rc==2 ){
782 fossil_print("ambiguous command prefix: %s\nMatching commands:\n",
783 g.argv[2]);
784 cmd_cmd_list(g.argv[2]);
785 fossil_exit(1);
786 }
787 z = aCmdHelp[idx];
788 if( z==0 ){
789 fossil_fatal("no help available for the %s command",
@@ -1033,10 +1207,16 @@
1033
1034 if( szFile<1024 ){
1035 if( zNotFound ){
1036 cgi_redirect(zNotFound);
1037 }else{
 
 
 
 
 
 
1038 @ <h1>Not Found</h1>
1039 cgi_set_status(404, "not found");
1040 cgi_reply();
1041 }
1042 return;
@@ -1064,11 +1244,17 @@
1064 zPathInfo = "/xfer";
1065 }
1066 set_base_url();
1067 if( zPathInfo==0 || zPathInfo[0]==0
1068 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
1069 fossil_redirect_home();
 
 
 
 
 
 
1070 }else{
1071 zPath = mprintf("%s", zPathInfo);
1072 }
1073
1074 /* Make g.zPath point to the first element of the path. Make
@@ -1125,10 +1311,12 @@
1125 break;
1126 }
1127 if( g.zExtra ){
1128 /* CGI parameters get this treatment elsewhere, but places like getfile
1129 ** will use g.zExtra directly.
 
 
1130 */
1131 dehttpize(g.zExtra);
1132 cgi_set_parameter_nocopy("name", g.zExtra);
1133 }
1134
@@ -1135,17 +1323,31 @@
1135 /* Locate the method specified by the path and execute the function
1136 ** that implements that method.
1137 */
1138 if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) &&
1139 name_search("not_found", aWebpage, count(aWebpage), &idx) ){
1140 cgi_set_status(404,"Not Found");
1141 @ <h1>Not Found</h1>
1142 @ <p>Page not found: %h(g.zPath)</p>
 
 
 
 
 
 
 
1143 }else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){
1144 @ <h1>Server Configuration Error</h1>
1145 @ <p>The database schema on the server is out-of-date. Please ask
1146 @ the administrator to run <b>fossil rebuild</b>.</p>
 
 
 
 
 
 
 
1147 }else{
1148 aWebpage[idx].xFunc();
1149 }
1150
1151 /* Return the result.
@@ -1152,11 +1354,11 @@
1152 */
1153 cgi_reply();
1154 }
1155
1156 /*
1157 ** COMMAND: cgi
1158 **
1159 ** Usage: %fossil ?cgi? SCRIPT
1160 **
1161 ** The SCRIPT argument is the name of a file that is the CGI script
1162 ** that is being run. The command name, "cgi", may be omitted if
@@ -1339,11 +1541,11 @@
1339 **
1340 ** fossil http REPOSITORY INFILE OUTFILE IPADDR
1341 **
1342 ** The argv==6 form is used by the win32 server only.
1343 **
1344 ** COMMAND: http
1345 **
1346 ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https]
1347 **
1348 ** Handle a single HTTP request appearing on stdin. The resulting webpage
1349 ** is delivered on stdout. This method is used to launch an HTTP request
@@ -1440,11 +1642,11 @@
1440 }
1441 #endif
1442 #endif
1443
1444 /*
1445 ** COMMAND: server
1446 ** COMMAND: ui
1447 **
1448 ** Usage: %fossil server ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
1449 ** Or: %fossil ui ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
1450 **
1451
--- src/main.c
+++ src/main.c
@@ -23,13 +23,20 @@
23 #include <string.h>
24 #include <time.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdlib.h> /* atexit() */
29
30 #if INTERFACE
31 #ifdef FOSSIL_ENABLE_JSON
32 # include "cson_amalgamation.h" /* JSON API. Needed inside the INTERFACE block! */
33 # include "json_detail.h"
34 #endif
35 #ifdef FOSSIL_ENABLE_TCL
36 #include "tcl.h"
37 #endif
38
39 /*
40 ** Number of elements in an array
41 */
42 #define count(X) (sizeof(X)/sizeof(X[0]))
@@ -69,10 +76,23 @@
76 char TktFmt; /* t: create new ticket report formats */
77 char RdAddr; /* e: read email addresses or other private data */
78 char Zip; /* z: download zipped artifact via /zip URL */
79 char Private; /* x: can send and receive private content */
80 };
81
82 #ifdef FOSSIL_ENABLE_TCL
83 /*
84 ** All Tcl related context information is in this structure. This structure
85 ** definition has been copied from and should be kept in sync with the one in
86 ** "th_tcl.c".
87 */
88 struct TclContext {
89 int argc;
90 char **argv;
91 Tcl_Interp *interp;
92 };
93 #endif
94
95 /*
96 ** All global variables are in this structure.
97 */
98 struct Global {
@@ -117,10 +137,11 @@
137 int xlinkClusterOnly; /* Set when cloning. Only process clusters */
138 int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */
139 int *aCommitFile; /* Array of files to be committed */
140 int markPrivate; /* All new artifacts are private if true */
141 int clockSkewSeen; /* True if clocks on client and server out of sync */
142 int isHTTP; /* True if running in server/CGI modes, else assume CLI. */
143
144 int urlIsFile; /* True if a "file:" url */
145 int urlIsHttps; /* True if a "https:" url */
146 int urlIsSsh; /* True if an "ssh:" url */
147 char *urlName; /* Hostname for http: or filename for file: */
@@ -133,11 +154,11 @@
154 char *urlPasswd; /* Password for http: */
155 char *urlCanonical; /* Canonical representation of the URL */
156 char *urlProxyAuth; /* Proxy-Authorizer: string */
157 char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */
158 int dontKeepUrl; /* Do not persist the URL */
159
160 const char *zLogin; /* Login name. "" if not logged in. */
161 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */
162 int useLocalauth; /* No login required if from 127.0.0.1 */
163 int noPswd; /* Logged in without password (on 127.0.0.1) */
164 int userUid; /* Integer user id */
@@ -147,10 +168,15 @@
168 char *zIpAddr; /* The remote IP address */
169 char *zNonce; /* The nonce used for login */
170
171 /* permissions used by the server */
172 struct FossilUserPerms perm;
173
174 #ifdef FOSSIL_ENABLE_TCL
175 /* all Tcl related context necessary for integration */
176 struct TclContext tcl;
177 #endif
178
179 /* For defense against Cross-site Request Forgery attacks */
180 char zCsrfToken[12]; /* Value of the anti-CSRF token */
181 int okCsrf; /* Anti-CSRF token is present and valid */
182
@@ -168,10 +194,66 @@
194 const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */
195 const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
196 int anAuxCols[MX_AUX]; /* Number of columns for option() values */
197
198 int allowSymlinks; /* Cached "allow-symlinks" option */
199
200 #ifdef FOSSIL_ENABLE_JSON
201 struct FossilJsonBits {
202 int isJsonMode; /* True if running in JSON mode, else
203 false. This changes how errors are
204 reported. In JSON mode we try to
205 always output JSON-form error
206 responses and always exit() with
207 code 0 to avoid an HTTP 500 error.
208 */
209 int resultCode; /* used for passing back specific codes from /json callbacks. */
210 int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
211 cson_output_opt outOpt; /* formatting options for JSON mode. */
212 cson_value * authToken; /* authentication token */
213 char const * jsonp; /* Name of JSONP function wrapper. */
214 unsigned char dispatchDepth /* Tells JSON command dispatching
215 which argument we are currently
216 working on. For this purpose, arg#0
217 is the "json" path/CLI arg.
218 */;
219 struct { /* "garbage collector" */
220 cson_value * v;
221 cson_array * a;
222 } gc;
223 struct { /* JSON POST data. */
224 cson_value * v;
225 cson_array * a;
226 int offset; /* Tells us which PATH_INFO/CLI args
227 part holds the "json" command, so
228 that we can account for sub-repos
229 and path prefixes. This is handled
230 differently for CLI and CGI modes.
231 */
232 char const * commandStr /*"command" request param.*/;
233 } cmd;
234 struct { /* JSON POST data. */
235 cson_value * v;
236 cson_object * o;
237 } post;
238 struct { /* GET/COOKIE params in JSON mode.
239 FIXME (stephan): verify that this is
240 still used and remove if it is not.
241 */
242 cson_value * v;
243 cson_object * o;
244 } param;
245 struct {
246 cson_value * v;
247 cson_object * o;
248 } reqPayload; /* request payload object (if any) */
249 struct { /* response warnings */
250 cson_value * v;
251 cson_array * a;
252 } warnings;
253 } json;
254 #endif /* FOSSIL_ENABLE_JSON */
255 };
256
257 /*
258 ** Macro for debugging:
259 */
@@ -232,10 +314,25 @@
314 *pIndex = m;
315 return 0;
316 }
317 return 1+(cnt>1);
318 }
319
320 /*
321 ** atexit() handler which frees up "some" of the resources
322 ** used by fossil.
323 */
324 void fossil_atexit(void) {
325 #ifdef FOSSIL_ENABLE_JSON
326 cson_value_free(g.json.gc.v);
327 memset(&g.json, 0, sizeof(g.json));
328 #endif
329 free(g.zErrMsg);
330 if(g.db){
331 db_close(0);
332 }
333 }
334
335 /*
336 ** Search g.argv for arguments "--args FILENAME". If found, then
337 ** (1) remove the two arguments from g.argv
338 ** (2) Read the file FILENAME
@@ -315,27 +412,52 @@
412 int main(int argc, char **argv){
413 const char *zCmdName = "unknown";
414 int idx;
415 int rc;
416 int i;
417
418 #ifdef FOSSIL_ENABLE_TCL
419 g.tcl.argc = argc;
420 g.tcl.argv = argv;
421 g.tcl.interp = 0;
422 #endif
423
424 sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
425 memset(&g, 0, sizeof(g));
426 g.now = time(0);
427 g.argc = argc;
428 g.argv = argv;
429 #ifdef FOSSIL_ENABLE_JSON
430 #if defined(NDEBUG)
431 g.json.errorDetailParanoia = 2 /* FIXME: make configurable
432 One problem we have here is that this
433 code is needed before the db is opened,
434 so we can't sql for it.*/;
435 #else
436 g.json.errorDetailParanoia = 0;
437 #endif
438 g.json.outOpt = cson_output_opt_empty;
439 g.json.outOpt.addNewline = 1;
440 g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
441 #endif /* FOSSIL_ENABLE_JSON */
442 expand_args_option();
443 argc = g.argc;
444 argv = g.argv;
445 for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
446 if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
447 zCmdName = "cgi";
448 g.isHTTP = 1;
449 }else if( argc<2 ){
450 fossil_print(
451 "Usage: %s COMMAND ...\n"
452 " or: %s help -- for a list of common commands\n"
453 " or: %s help COMMMAND -- for help with the named command\n"
454 " or: %s commands -- for a list of all commands\n",
455 argv[0], argv[0], argv[0], argv[0]);
456 fossil_exit(1);
457 }else{
458 g.isHTTP = 0;
459 g.fQuiet = find_option("quiet", 0, 0)!=0;
460 g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
461 g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
462 g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
463 if( g.fSqlTrace ) g.fSqlStats = 1;
@@ -370,15 +492,17 @@
492 for(i=0; i<count(aCommand); i++){
493 if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){
494 blob_appendf(&couldbe, " %s", aCommand[i].zName);
495 }
496 }
497 fossil_print("%s: ambiguous command prefix: %s\n"
498 "%s: could be any of:%s\n"
499 "%s: use \"help\" for more information\n",
500 argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]);
501 fossil_exit(1);
502 }
503 atexit( fossil_atexit );
504 aCommand[idx].xFunc();
505 fossil_exit(0);
506 /*NOT_REACHED*/
507 return 0;
508 }
@@ -414,43 +538,70 @@
538 ** routines never return.
539 */
540 NORETURN void fossil_panic(const char *zFormat, ...){
541 char *z;
542 va_list ap;
543 int rc = 1;
544 static int once = 1;
545 mainInFatalError = 1;
546 va_start(ap, zFormat);
547 z = vmprintf(zFormat, ap);
548 va_end(ap);
549 #ifdef FOSSIL_ENABLE_JSON
550 if( g.json.isJsonMode ){
551 json_err( 0, z, 1 );
552 if( g.isHTTP ){
553 rc = 0 /* avoid HTTP 500 */;
554 }
555 }
556 else
557 #endif
558 {
559 if( g.cgiOutput && once ){
560 once = 0;
561 cgi_printf("<p class=\"generalError\">%h</p>", z);
562 cgi_reply();
563 }else{
564 char *zOut = mprintf("%s: %s\n", fossil_nameofexe(), z);
565 fossil_puts(zOut, 1);
566 }
567 }
568 free(z);
569 db_force_rollback();
570 fossil_exit(rc);
571 }
572
573 NORETURN void fossil_fatal(const char *zFormat, ...){
574 char *z;
575 int rc = 1;
576 va_list ap;
577 mainInFatalError = 1;
578 va_start(ap, zFormat);
579 z = vmprintf(zFormat, ap);
580 va_end(ap);
581 #ifdef FOSSIL_ENABLE_JSON
582 if( g.json.isJsonMode ){
583 json_err( g.json.resultCode, z, 1 );
584 if( g.isHTTP ){
585 rc = 0 /* avoid HTTP 500 */;
586 }
587 }
588 else
589 #endif
590 {
591 if( g.cgiOutput ){
592 g.cgiOutput = 0;
593 cgi_printf("<p class=\"generalError\">%h</p>", z);
594 cgi_reply();
595 }else{
596 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
597 fossil_puts(zOut, 1);
598 }
599 }
600 free(z);
601 db_force_rollback();
602 fossil_exit(rc);
603 }
604
605 /* This routine works like fossil_fatal() except that if called
606 ** recursively, the recursive call is a no-op.
607 **
@@ -461,25 +612,37 @@
612 ** be prepared for this routine to return.
613 */
614 void fossil_fatal_recursive(const char *zFormat, ...){
615 char *z;
616 va_list ap;
617 int rc = 1;
618 if( mainInFatalError ) return;
619 mainInFatalError = 1;
620 va_start(ap, zFormat);
621 z = vmprintf(zFormat, ap);
622 va_end(ap);
623 #ifdef FOSSIL_ENABLE_JSON
624 if( g.json.isJsonMode ){
625 json_err( g.json.resultCode, z, 1 );
626 if( g.isHTTP ){
627 rc = 0 /* avoid HTTP 500 */;
628 }
629 } else
630 #endif
631 {
632 if( g.cgiOutput ){
633 g.cgiOutput = 0;
634 cgi_printf("<p class=\"generalError\">%h</p>", z);
635 cgi_reply();
636 }else{
637 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
638 fossil_puts(zOut, 1);
639 free(zOut);
640 }
641 }
642 db_force_rollback();
643 fossil_exit(rc);
644 }
645
646
647 /* Print a warning message */
648 void fossil_warning(const char *zFormat, ...){
@@ -486,17 +649,25 @@
649 char *z;
650 va_list ap;
651 va_start(ap, zFormat);
652 z = vmprintf(zFormat, ap);
653 va_end(ap);
654 #ifdef FOSSIL_ENABLE_JSON
655 if(g.json.isJsonMode){
656 json_warn( FSL_JSON_W_UNKNOWN, z );
657 }else
658 #endif
659 {
660 if( g.cgiOutput ){
661 cgi_printf("<p class=\"generalError\">%h</p>", z);
662 }else{
663 char *zOut = mprintf("\r%s: %s\n", fossil_nameofexe(), z);
664 fossil_puts(zOut, 1);
665 free(zOut);
666 }
667 }
668 free(z);
669 }
670
671 /*
672 ** Malloc and free routines that cannot fail
673 */
@@ -695,40 +866,22 @@
866 }
867
868 /*
869 ** List of commands starting with zPrefix, or all commands if zPrefix is NULL.
870 */
871 static void command_list(const char *zPrefix, int cmdMask){
872 int i, nCmd;
873 int nPrefix = zPrefix ? strlen(zPrefix) : 0;
874 const char *aCmd[count(aCommand)];
875 for(i=nCmd=0; i<count(aCommand); i++){
876 const char *z = aCommand[i].zName;
877 if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue;
878 if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue;
879 aCmd[nCmd++] = aCommand[i].zName;
880 }
881 multi_column_list(aCmd, nCmd);
882 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883
884 /*
885 ** COMMAND: test-list-webpage
886 **
887 ** List all web pages
@@ -739,11 +892,10 @@
892 for(i=nCmd=0; i<count(aWebpage); i++){
893 aCmd[nCmd++] = aWebpage[i].zName;
894 }
895 multi_column_list(aCmd, nCmd);
896 }
 
897
898 /*
899 ** COMMAND: version
900 **
901 ** Usage: %fossil version
@@ -758,32 +910,54 @@
910
911 /*
912 ** COMMAND: help
913 **
914 ** Usage: %fossil help COMMAND
915 ** or: %fossil COMMAND -help
916 **
917 ** Display information on how to use COMMAND. To display a list of
918 ** available commands one of:
919 **
920 ** %fossil help Show common commands
921 ** %fossil help --all Show both command and auxiliary commands
922 ** %fossil help --test Show test commands only
923 ** %fossil help --aux Show auxiliary commands only
924 */
925 void help_cmd(void){
926 int rc, idx;
927 const char *z;
928 if( g.argc<3 ){
929 z = fossil_nameofexe();
930 fossil_print(
931 "Usage: %s help COMMAND\n"
932 "Common COMMANDs: (use \"%s help --all\" for a complete list)\n",
933 z, z);
934 command_list(0, CMDFLAG_1ST_TIER);
935 version_cmd();
936 return;
937 }
938 if( find_option("all",0,0) ){
939 command_list(0, CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER);
940 return;
941 }
942 if( find_option("aux",0,0) ){
943 command_list(0, CMDFLAG_2ND_TIER);
944 return;
945 }
946 if( find_option("test",0,0) ){
947 command_list(0, CMDFLAG_TEST);
948 return;
949 }
950 rc = name_search(g.argv[2], aCommand, count(aCommand), &idx);
951 if( rc==1 ){
952 fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]);
953 command_list(0, 0xff);
954 fossil_exit(1);
955 }else if( rc==2 ){
956 fossil_print("ambiguous command prefix: %s\nMatching commands:\n",
957 g.argv[2]);
958 command_list(g.argv[2], 0xff);
959 fossil_exit(1);
960 }
961 z = aCmdHelp[idx];
962 if( z==0 ){
963 fossil_fatal("no help available for the %s command",
@@ -1033,10 +1207,16 @@
1207
1208 if( szFile<1024 ){
1209 if( zNotFound ){
1210 cgi_redirect(zNotFound);
1211 }else{
1212 #ifdef FOSSIL_ENABLE_JSON
1213 if(g.json.isJsonMode){
1214 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1215 return;
1216 }
1217 #endif
1218 @ <h1>Not Found</h1>
1219 cgi_set_status(404, "not found");
1220 cgi_reply();
1221 }
1222 return;
@@ -1064,11 +1244,17 @@
1244 zPathInfo = "/xfer";
1245 }
1246 set_base_url();
1247 if( zPathInfo==0 || zPathInfo[0]==0
1248 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
1249 #ifdef FOSSIL_ENABLE_JSON
1250 if(g.json.isJsonMode){
1251 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
1252 fossil_exit(0);
1253 }
1254 #endif
1255 fossil_redirect_home() /*does not return*/;
1256 }else{
1257 zPath = mprintf("%s", zPathInfo);
1258 }
1259
1260 /* Make g.zPath point to the first element of the path. Make
@@ -1125,10 +1311,12 @@
1311 break;
1312 }
1313 if( g.zExtra ){
1314 /* CGI parameters get this treatment elsewhere, but places like getfile
1315 ** will use g.zExtra directly.
1316 ** Reminder: the login mechanism uses 'name' differently, and may
1317 ** eventually have a problem/collision with this.
1318 */
1319 dehttpize(g.zExtra);
1320 cgi_set_parameter_nocopy("name", g.zExtra);
1321 }
1322
@@ -1135,17 +1323,31 @@
1323 /* Locate the method specified by the path and execute the function
1324 ** that implements that method.
1325 */
1326 if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) &&
1327 name_search("not_found", aWebpage, count(aWebpage), &idx) ){
1328 #ifdef FOSSIL_ENABLE_JSON
1329 if(g.json.isJsonMode){
1330 json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
1331 }else
1332 #endif
1333 {
1334 cgi_set_status(404,"Not Found");
1335 @ <h1>Not Found</h1>
1336 @ <p>Page not found: %h(g.zPath)</p>
1337 }
1338 }else if( aWebpage[idx].xFunc!=page_xfer && db_schema_is_outofdate() ){
1339 #ifdef FOSSIL_ENABLE_JSON
1340 if(g.json.isJsonMode){
1341 json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
1342 }else
1343 #endif
1344 {
1345 @ <h1>Server Configuration Error</h1>
1346 @ <p>The database schema on the server is out-of-date. Please ask
1347 @ the administrator to run <b>fossil rebuild</b>.</p>
1348 }
1349 }else{
1350 aWebpage[idx].xFunc();
1351 }
1352
1353 /* Return the result.
@@ -1152,11 +1354,11 @@
1354 */
1355 cgi_reply();
1356 }
1357
1358 /*
1359 ** COMMAND: cgi*
1360 **
1361 ** Usage: %fossil ?cgi? SCRIPT
1362 **
1363 ** The SCRIPT argument is the name of a file that is the CGI script
1364 ** that is being run. The command name, "cgi", may be omitted if
@@ -1339,11 +1541,11 @@
1541 **
1542 ** fossil http REPOSITORY INFILE OUTFILE IPADDR
1543 **
1544 ** The argv==6 form is used by the win32 server only.
1545 **
1546 ** COMMAND: http*
1547 **
1548 ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https]
1549 **
1550 ** Handle a single HTTP request appearing on stdin. The resulting webpage
1551 ** is delivered on stdout. This method is used to launch an HTTP request
@@ -1440,11 +1642,11 @@
1642 }
1643 #endif
1644 #endif
1645
1646 /*
1647 ** COMMAND: server*
1648 ** COMMAND: ui
1649 **
1650 ** Usage: %fossil server ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
1651 ** Or: %fossil ui ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY?
1652 **
1653
+126 -4
--- src/main.mk
+++ src/main.mk
@@ -47,10 +47,21 @@
4747
$(SRCDIR)/http_socket.c \
4848
$(SRCDIR)/http_ssl.c \
4949
$(SRCDIR)/http_transport.c \
5050
$(SRCDIR)/import.c \
5151
$(SRCDIR)/info.c \
52
+ $(SRCDIR)/json.c \
53
+ $(SRCDIR)/json_artifact.c \
54
+ $(SRCDIR)/json_branch.c \
55
+ $(SRCDIR)/json_diff.c \
56
+ $(SRCDIR)/json_login.c \
57
+ $(SRCDIR)/json_query.c \
58
+ $(SRCDIR)/json_report.c \
59
+ $(SRCDIR)/json_tag.c \
60
+ $(SRCDIR)/json_timeline.c \
61
+ $(SRCDIR)/json_user.c \
62
+ $(SRCDIR)/json_wiki.c \
5263
$(SRCDIR)/leaf.c \
5364
$(SRCDIR)/login.c \
5465
$(SRCDIR)/main.c \
5566
$(SRCDIR)/manifest.c \
5667
$(SRCDIR)/md5.c \
@@ -131,10 +142,21 @@
131142
$(OBJDIR)/http_socket_.c \
132143
$(OBJDIR)/http_ssl_.c \
133144
$(OBJDIR)/http_transport_.c \
134145
$(OBJDIR)/import_.c \
135146
$(OBJDIR)/info_.c \
147
+ $(OBJDIR)/json_.c \
148
+ $(OBJDIR)/json_artifact_.c \
149
+ $(OBJDIR)/json_branch_.c \
150
+ $(OBJDIR)/json_diff_.c \
151
+ $(OBJDIR)/json_login_.c \
152
+ $(OBJDIR)/json_query_.c \
153
+ $(OBJDIR)/json_report_.c \
154
+ $(OBJDIR)/json_tag_.c \
155
+ $(OBJDIR)/json_timeline_.c \
156
+ $(OBJDIR)/json_user_.c \
157
+ $(OBJDIR)/json_wiki_.c \
136158
$(OBJDIR)/leaf_.c \
137159
$(OBJDIR)/login_.c \
138160
$(OBJDIR)/main_.c \
139161
$(OBJDIR)/manifest_.c \
140162
$(OBJDIR)/md5_.c \
@@ -215,10 +237,21 @@
215237
$(OBJDIR)/http_socket.o \
216238
$(OBJDIR)/http_ssl.o \
217239
$(OBJDIR)/http_transport.o \
218240
$(OBJDIR)/import.o \
219241
$(OBJDIR)/info.o \
242
+ $(OBJDIR)/json.o \
243
+ $(OBJDIR)/json_artifact.o \
244
+ $(OBJDIR)/json_branch.o \
245
+ $(OBJDIR)/json_diff.o \
246
+ $(OBJDIR)/json_login.o \
247
+ $(OBJDIR)/json_query.o \
248
+ $(OBJDIR)/json_report.o \
249
+ $(OBJDIR)/json_tag.o \
250
+ $(OBJDIR)/json_timeline.o \
251
+ $(OBJDIR)/json_user.o \
252
+ $(OBJDIR)/json_wiki.o \
220253
$(OBJDIR)/leaf.o \
221254
$(OBJDIR)/login.o \
222255
$(OBJDIR)/main.o \
223256
$(OBJDIR)/manifest.o \
224257
$(OBJDIR)/md5.o \
@@ -287,12 +320,12 @@
287320
$(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c
288321
289322
# WARNING. DANGER. Running the testsuite modifies the repository the
290323
# build is done from, i.e. the checkout belongs to. Do not sync/push
291324
# the repository after running the tests.
292
-test: $(APPNAME)
293
- $(TCLSH) test/tester.tcl $(APPNAME)
325
+test: $(OBJDIR) $(APPNAME)
326
+ $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)
294327
295328
$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
296329
$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
297330
298331
# The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
@@ -301,11 +334,15 @@
301334
# using -lsqlite3.
302335
SQLITE3_OBJ.1 =
303336
SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
304337
SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
305338
306
-EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o
339
+TCL_OBJ.1 =
340
+TCL_OBJ.0 = $(OBJDIR)/th_tcl.o
341
+TCL_OBJ. = $(TCL_OBJ.0)
342
+
343
+EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) $(OBJDIR)/cson_amalgamation.o
307344
308345
$(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
309346
$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
310347
311348
# This rule prevents make from using its default rules to try build
@@ -319,13 +356,14 @@
319356
320357
321358
$(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
322359
$(OBJDIR)/mkindex $(TRANS_SRC) >$@
323360
$(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
324
- $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
361
+ $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
325362
touch $(OBJDIR)/headers
326363
$(OBJDIR)/headers: Makefile
364
+$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
327365
Makefile:
328366
$(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate
329367
$(OBJDIR)/translate $(SRCDIR)/add.c >$(OBJDIR)/add_.c
330368
331369
$(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h
@@ -589,10 +627,87 @@
589627
590628
$(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h
591629
$(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c
592630
593631
$(OBJDIR)/info.h: $(OBJDIR)/headers
632
+$(OBJDIR)/json_.c: $(SRCDIR)/json.c $(OBJDIR)/translate
633
+ $(OBJDIR)/translate $(SRCDIR)/json.c >$(OBJDIR)/json_.c
634
+
635
+$(OBJDIR)/json.o: $(OBJDIR)/json_.c $(OBJDIR)/json.h $(SRCDIR)/config.h
636
+ $(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c
637
+
638
+$(OBJDIR)/json.h: $(OBJDIR)/headers
639
+$(OBJDIR)/json_artifact_.c: $(SRCDIR)/json_artifact.c $(OBJDIR)/translate
640
+ $(OBJDIR)/translate $(SRCDIR)/json_artifact.c >$(OBJDIR)/json_artifact_.c
641
+
642
+$(OBJDIR)/json_artifact.o: $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h $(SRCDIR)/config.h
643
+ $(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c
644
+
645
+$(OBJDIR)/json_artifact.h: $(OBJDIR)/headers
646
+$(OBJDIR)/json_branch_.c: $(SRCDIR)/json_branch.c $(OBJDIR)/translate
647
+ $(OBJDIR)/translate $(SRCDIR)/json_branch.c >$(OBJDIR)/json_branch_.c
648
+
649
+$(OBJDIR)/json_branch.o: $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h $(SRCDIR)/config.h
650
+ $(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c
651
+
652
+$(OBJDIR)/json_branch.h: $(OBJDIR)/headers
653
+$(OBJDIR)/json_diff_.c: $(SRCDIR)/json_diff.c $(OBJDIR)/translate
654
+ $(OBJDIR)/translate $(SRCDIR)/json_diff.c >$(OBJDIR)/json_diff_.c
655
+
656
+$(OBJDIR)/json_diff.o: $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h $(SRCDIR)/config.h
657
+ $(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c
658
+
659
+$(OBJDIR)/json_diff.h: $(OBJDIR)/headers
660
+$(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(OBJDIR)/translate
661
+ $(OBJDIR)/translate $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c
662
+
663
+$(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR)/config.h
664
+ $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c
665
+
666
+$(OBJDIR)/json_login.h: $(OBJDIR)/headers
667
+$(OBJDIR)/json_query_.c: $(SRCDIR)/json_query.c $(OBJDIR)/translate
668
+ $(OBJDIR)/translate $(SRCDIR)/json_query.c >$(OBJDIR)/json_query_.c
669
+
670
+$(OBJDIR)/json_query.o: $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h $(SRCDIR)/config.h
671
+ $(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c
672
+
673
+$(OBJDIR)/json_query.h: $(OBJDIR)/headers
674
+$(OBJDIR)/json_report_.c: $(SRCDIR)/json_report.c $(OBJDIR)/translate
675
+ $(OBJDIR)/translate $(SRCDIR)/json_report.c >$(OBJDIR)/json_report_.c
676
+
677
+$(OBJDIR)/json_report.o: $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h $(SRCDIR)/config.h
678
+ $(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c
679
+
680
+$(OBJDIR)/json_report.h: $(OBJDIR)/headers
681
+$(OBJDIR)/json_tag_.c: $(SRCDIR)/json_tag.c $(OBJDIR)/translate
682
+ $(OBJDIR)/translate $(SRCDIR)/json_tag.c >$(OBJDIR)/json_tag_.c
683
+
684
+$(OBJDIR)/json_tag.o: $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h $(SRCDIR)/config.h
685
+ $(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c
686
+
687
+$(OBJDIR)/json_tag.h: $(OBJDIR)/headers
688
+$(OBJDIR)/json_timeline_.c: $(SRCDIR)/json_timeline.c $(OBJDIR)/translate
689
+ $(OBJDIR)/translate $(SRCDIR)/json_timeline.c >$(OBJDIR)/json_timeline_.c
690
+
691
+$(OBJDIR)/json_timeline.o: $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h $(SRCDIR)/config.h
692
+ $(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c
693
+
694
+$(OBJDIR)/json_timeline.h: $(OBJDIR)/headers
695
+$(OBJDIR)/json_user_.c: $(SRCDIR)/json_user.c $(OBJDIR)/translate
696
+ $(OBJDIR)/translate $(SRCDIR)/json_user.c >$(OBJDIR)/json_user_.c
697
+
698
+$(OBJDIR)/json_user.o: $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h $(SRCDIR)/config.h
699
+ $(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c
700
+
701
+$(OBJDIR)/json_user.h: $(OBJDIR)/headers
702
+$(OBJDIR)/json_wiki_.c: $(SRCDIR)/json_wiki.c $(OBJDIR)/translate
703
+ $(OBJDIR)/translate $(SRCDIR)/json_wiki.c >$(OBJDIR)/json_wiki_.c
704
+
705
+$(OBJDIR)/json_wiki.o: $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h $(SRCDIR)/config.h
706
+ $(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c
707
+
708
+$(OBJDIR)/json_wiki.h: $(OBJDIR)/headers
594709
$(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate
595710
$(OBJDIR)/translate $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c
596711
597712
$(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h
598713
$(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c
@@ -909,5 +1024,12 @@
9091024
$(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
9101025
9111026
$(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
9121027
$(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
9131028
1029
+$(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c
1030
+ $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o
1031
+
1032
+
1033
+$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
1034
+ $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE
1035
+
9141036
--- src/main.mk
+++ src/main.mk
@@ -47,10 +47,21 @@
47 $(SRCDIR)/http_socket.c \
48 $(SRCDIR)/http_ssl.c \
49 $(SRCDIR)/http_transport.c \
50 $(SRCDIR)/import.c \
51 $(SRCDIR)/info.c \
 
 
 
 
 
 
 
 
 
 
 
52 $(SRCDIR)/leaf.c \
53 $(SRCDIR)/login.c \
54 $(SRCDIR)/main.c \
55 $(SRCDIR)/manifest.c \
56 $(SRCDIR)/md5.c \
@@ -131,10 +142,21 @@
131 $(OBJDIR)/http_socket_.c \
132 $(OBJDIR)/http_ssl_.c \
133 $(OBJDIR)/http_transport_.c \
134 $(OBJDIR)/import_.c \
135 $(OBJDIR)/info_.c \
 
 
 
 
 
 
 
 
 
 
 
136 $(OBJDIR)/leaf_.c \
137 $(OBJDIR)/login_.c \
138 $(OBJDIR)/main_.c \
139 $(OBJDIR)/manifest_.c \
140 $(OBJDIR)/md5_.c \
@@ -215,10 +237,21 @@
215 $(OBJDIR)/http_socket.o \
216 $(OBJDIR)/http_ssl.o \
217 $(OBJDIR)/http_transport.o \
218 $(OBJDIR)/import.o \
219 $(OBJDIR)/info.o \
 
 
 
 
 
 
 
 
 
 
 
220 $(OBJDIR)/leaf.o \
221 $(OBJDIR)/login.o \
222 $(OBJDIR)/main.o \
223 $(OBJDIR)/manifest.o \
224 $(OBJDIR)/md5.o \
@@ -287,12 +320,12 @@
287 $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c
288
289 # WARNING. DANGER. Running the testsuite modifies the repository the
290 # build is done from, i.e. the checkout belongs to. Do not sync/push
291 # the repository after running the tests.
292 test: $(APPNAME)
293 $(TCLSH) test/tester.tcl $(APPNAME)
294
295 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
296 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
297
298 # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
@@ -301,11 +334,15 @@
301 # using -lsqlite3.
302 SQLITE3_OBJ.1 =
303 SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
304 SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
305
306 EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o
 
 
 
 
307
308 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
309 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
310
311 # This rule prevents make from using its default rules to try build
@@ -319,13 +356,14 @@
319
320
321 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
322 $(OBJDIR)/mkindex $(TRANS_SRC) >$@
323 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
324 $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
325 touch $(OBJDIR)/headers
326 $(OBJDIR)/headers: Makefile
 
327 Makefile:
328 $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate
329 $(OBJDIR)/translate $(SRCDIR)/add.c >$(OBJDIR)/add_.c
330
331 $(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h
@@ -589,10 +627,87 @@
589
590 $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h
591 $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c
592
593 $(OBJDIR)/info.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
594 $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate
595 $(OBJDIR)/translate $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c
596
597 $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h
598 $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c
@@ -909,5 +1024,12 @@
909 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
910
911 $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
912 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
913
 
 
 
 
 
 
 
914
--- src/main.mk
+++ src/main.mk
@@ -47,10 +47,21 @@
47 $(SRCDIR)/http_socket.c \
48 $(SRCDIR)/http_ssl.c \
49 $(SRCDIR)/http_transport.c \
50 $(SRCDIR)/import.c \
51 $(SRCDIR)/info.c \
52 $(SRCDIR)/json.c \
53 $(SRCDIR)/json_artifact.c \
54 $(SRCDIR)/json_branch.c \
55 $(SRCDIR)/json_diff.c \
56 $(SRCDIR)/json_login.c \
57 $(SRCDIR)/json_query.c \
58 $(SRCDIR)/json_report.c \
59 $(SRCDIR)/json_tag.c \
60 $(SRCDIR)/json_timeline.c \
61 $(SRCDIR)/json_user.c \
62 $(SRCDIR)/json_wiki.c \
63 $(SRCDIR)/leaf.c \
64 $(SRCDIR)/login.c \
65 $(SRCDIR)/main.c \
66 $(SRCDIR)/manifest.c \
67 $(SRCDIR)/md5.c \
@@ -131,10 +142,21 @@
142 $(OBJDIR)/http_socket_.c \
143 $(OBJDIR)/http_ssl_.c \
144 $(OBJDIR)/http_transport_.c \
145 $(OBJDIR)/import_.c \
146 $(OBJDIR)/info_.c \
147 $(OBJDIR)/json_.c \
148 $(OBJDIR)/json_artifact_.c \
149 $(OBJDIR)/json_branch_.c \
150 $(OBJDIR)/json_diff_.c \
151 $(OBJDIR)/json_login_.c \
152 $(OBJDIR)/json_query_.c \
153 $(OBJDIR)/json_report_.c \
154 $(OBJDIR)/json_tag_.c \
155 $(OBJDIR)/json_timeline_.c \
156 $(OBJDIR)/json_user_.c \
157 $(OBJDIR)/json_wiki_.c \
158 $(OBJDIR)/leaf_.c \
159 $(OBJDIR)/login_.c \
160 $(OBJDIR)/main_.c \
161 $(OBJDIR)/manifest_.c \
162 $(OBJDIR)/md5_.c \
@@ -215,10 +237,21 @@
237 $(OBJDIR)/http_socket.o \
238 $(OBJDIR)/http_ssl.o \
239 $(OBJDIR)/http_transport.o \
240 $(OBJDIR)/import.o \
241 $(OBJDIR)/info.o \
242 $(OBJDIR)/json.o \
243 $(OBJDIR)/json_artifact.o \
244 $(OBJDIR)/json_branch.o \
245 $(OBJDIR)/json_diff.o \
246 $(OBJDIR)/json_login.o \
247 $(OBJDIR)/json_query.o \
248 $(OBJDIR)/json_report.o \
249 $(OBJDIR)/json_tag.o \
250 $(OBJDIR)/json_timeline.o \
251 $(OBJDIR)/json_user.o \
252 $(OBJDIR)/json_wiki.o \
253 $(OBJDIR)/leaf.o \
254 $(OBJDIR)/login.o \
255 $(OBJDIR)/main.o \
256 $(OBJDIR)/manifest.o \
257 $(OBJDIR)/md5.o \
@@ -287,12 +320,12 @@
320 $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c
321
322 # WARNING. DANGER. Running the testsuite modifies the repository the
323 # build is done from, i.e. the checkout belongs to. Do not sync/push
324 # the repository after running the tests.
325 test: $(OBJDIR) $(APPNAME)
326 $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)
327
328 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
329 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
330
331 # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set
@@ -301,11 +334,15 @@
334 # using -lsqlite3.
335 SQLITE3_OBJ.1 =
336 SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
337 SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
338
339 TCL_OBJ.1 =
340 TCL_OBJ.0 = $(OBJDIR)/th_tcl.o
341 TCL_OBJ. = $(TCL_OBJ.0)
342
343 EXTRAOBJ = $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) $(OBJDIR)/cson_amalgamation.o
344
345 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
346 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
347
348 # This rule prevents make from using its default rules to try build
@@ -319,13 +356,14 @@
356
357
358 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
359 $(OBJDIR)/mkindex $(TRANS_SRC) >$@
360 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
361 $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
362 touch $(OBJDIR)/headers
363 $(OBJDIR)/headers: Makefile
364 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
365 Makefile:
366 $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate
367 $(OBJDIR)/translate $(SRCDIR)/add.c >$(OBJDIR)/add_.c
368
369 $(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h
@@ -589,10 +627,87 @@
627
628 $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h
629 $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c
630
631 $(OBJDIR)/info.h: $(OBJDIR)/headers
632 $(OBJDIR)/json_.c: $(SRCDIR)/json.c $(OBJDIR)/translate
633 $(OBJDIR)/translate $(SRCDIR)/json.c >$(OBJDIR)/json_.c
634
635 $(OBJDIR)/json.o: $(OBJDIR)/json_.c $(OBJDIR)/json.h $(SRCDIR)/config.h
636 $(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c
637
638 $(OBJDIR)/json.h: $(OBJDIR)/headers
639 $(OBJDIR)/json_artifact_.c: $(SRCDIR)/json_artifact.c $(OBJDIR)/translate
640 $(OBJDIR)/translate $(SRCDIR)/json_artifact.c >$(OBJDIR)/json_artifact_.c
641
642 $(OBJDIR)/json_artifact.o: $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h $(SRCDIR)/config.h
643 $(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c
644
645 $(OBJDIR)/json_artifact.h: $(OBJDIR)/headers
646 $(OBJDIR)/json_branch_.c: $(SRCDIR)/json_branch.c $(OBJDIR)/translate
647 $(OBJDIR)/translate $(SRCDIR)/json_branch.c >$(OBJDIR)/json_branch_.c
648
649 $(OBJDIR)/json_branch.o: $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h $(SRCDIR)/config.h
650 $(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c
651
652 $(OBJDIR)/json_branch.h: $(OBJDIR)/headers
653 $(OBJDIR)/json_diff_.c: $(SRCDIR)/json_diff.c $(OBJDIR)/translate
654 $(OBJDIR)/translate $(SRCDIR)/json_diff.c >$(OBJDIR)/json_diff_.c
655
656 $(OBJDIR)/json_diff.o: $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h $(SRCDIR)/config.h
657 $(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c
658
659 $(OBJDIR)/json_diff.h: $(OBJDIR)/headers
660 $(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(OBJDIR)/translate
661 $(OBJDIR)/translate $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c
662
663 $(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR)/config.h
664 $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c
665
666 $(OBJDIR)/json_login.h: $(OBJDIR)/headers
667 $(OBJDIR)/json_query_.c: $(SRCDIR)/json_query.c $(OBJDIR)/translate
668 $(OBJDIR)/translate $(SRCDIR)/json_query.c >$(OBJDIR)/json_query_.c
669
670 $(OBJDIR)/json_query.o: $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h $(SRCDIR)/config.h
671 $(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c
672
673 $(OBJDIR)/json_query.h: $(OBJDIR)/headers
674 $(OBJDIR)/json_report_.c: $(SRCDIR)/json_report.c $(OBJDIR)/translate
675 $(OBJDIR)/translate $(SRCDIR)/json_report.c >$(OBJDIR)/json_report_.c
676
677 $(OBJDIR)/json_report.o: $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h $(SRCDIR)/config.h
678 $(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c
679
680 $(OBJDIR)/json_report.h: $(OBJDIR)/headers
681 $(OBJDIR)/json_tag_.c: $(SRCDIR)/json_tag.c $(OBJDIR)/translate
682 $(OBJDIR)/translate $(SRCDIR)/json_tag.c >$(OBJDIR)/json_tag_.c
683
684 $(OBJDIR)/json_tag.o: $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h $(SRCDIR)/config.h
685 $(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c
686
687 $(OBJDIR)/json_tag.h: $(OBJDIR)/headers
688 $(OBJDIR)/json_timeline_.c: $(SRCDIR)/json_timeline.c $(OBJDIR)/translate
689 $(OBJDIR)/translate $(SRCDIR)/json_timeline.c >$(OBJDIR)/json_timeline_.c
690
691 $(OBJDIR)/json_timeline.o: $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h $(SRCDIR)/config.h
692 $(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c
693
694 $(OBJDIR)/json_timeline.h: $(OBJDIR)/headers
695 $(OBJDIR)/json_user_.c: $(SRCDIR)/json_user.c $(OBJDIR)/translate
696 $(OBJDIR)/translate $(SRCDIR)/json_user.c >$(OBJDIR)/json_user_.c
697
698 $(OBJDIR)/json_user.o: $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h $(SRCDIR)/config.h
699 $(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c
700
701 $(OBJDIR)/json_user.h: $(OBJDIR)/headers
702 $(OBJDIR)/json_wiki_.c: $(SRCDIR)/json_wiki.c $(OBJDIR)/translate
703 $(OBJDIR)/translate $(SRCDIR)/json_wiki.c >$(OBJDIR)/json_wiki_.c
704
705 $(OBJDIR)/json_wiki.o: $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h $(SRCDIR)/config.h
706 $(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c
707
708 $(OBJDIR)/json_wiki.h: $(OBJDIR)/headers
709 $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate
710 $(OBJDIR)/translate $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c
711
712 $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h
713 $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c
@@ -909,5 +1024,12 @@
1024 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
1025
1026 $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
1027 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
1028
1029 $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c
1030 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_tcl.c -o $(OBJDIR)/th_tcl.o
1031
1032
1033 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
1034 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE
1035
1036
--- src/makeheaders.c
+++ src/makeheaders.c
@@ -327,13 +327,17 @@
327327
328328
/*
329329
** The following text line appears at the top of every file generated
330330
** by this program. By recognizing this line, the program can be sure
331331
** never to read a file that it generated itself.
332
+**
333
+** The "#undef INTERFACE" part is a hack to work around a name collision
334
+** in MSVC 2008.
332335
*/
333336
const char zTopLine[] =
334
- "/* \aThis file was automatically generated. Do not edit! */\n";
337
+ "/* \aThis file was automatically generated. Do not edit! */\n"
338
+ "#undef INTERFACE\n";
335339
#define nTopLine (sizeof(zTopLine)-1)
336340
337341
/*
338342
** The name of the file currently being parsed.
339343
*/
340344
--- src/makeheaders.c
+++ src/makeheaders.c
@@ -327,13 +327,17 @@
327
328 /*
329 ** The following text line appears at the top of every file generated
330 ** by this program. By recognizing this line, the program can be sure
331 ** never to read a file that it generated itself.
 
 
 
332 */
333 const char zTopLine[] =
334 "/* \aThis file was automatically generated. Do not edit! */\n";
 
335 #define nTopLine (sizeof(zTopLine)-1)
336
337 /*
338 ** The name of the file currently being parsed.
339 */
340
--- src/makeheaders.c
+++ src/makeheaders.c
@@ -327,13 +327,17 @@
327
328 /*
329 ** The following text line appears at the top of every file generated
330 ** by this program. By recognizing this line, the program can be sure
331 ** never to read a file that it generated itself.
332 **
333 ** The "#undef INTERFACE" part is a hack to work around a name collision
334 ** in MSVC 2008.
335 */
336 const char zTopLine[] =
337 "/* \aThis file was automatically generated. Do not edit! */\n"
338 "#undef INTERFACE\n";
339 #define nTopLine (sizeof(zTopLine)-1)
340
341 /*
342 ** The name of the file currently being parsed.
343 */
344
+70 -6
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -53,10 +53,21 @@
5353
http
5454
http_socket
5555
http_transport
5656
import
5757
info
58
+ json
59
+ json_artifact
60
+ json_branch
61
+ json_diff
62
+ json_login
63
+ json_query
64
+ json_report
65
+ json_tag
66
+ json_timeline
67
+ json_user
68
+ json_wiki
5869
leaf
5970
login
6071
main
6172
manifest
6273
md5
@@ -182,12 +193,12 @@
182193
$(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c
183194
184195
# WARNING. DANGER. Running the testsuite modifies the repository the
185196
# build is done from, i.e. the checkout belongs to. Do not sync/push
186197
# the repository after running the tests.
187
-test: $(APPNAME)
188
- $(TCLSH) test/tester.tcl $(APPNAME)
198
+test: $(OBJDIR) $(APPNAME)
199
+ $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)
189200
190201
$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
191202
$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \
192203
$(SRCDIR)/../manifest \
193204
$(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
@@ -198,15 +209,21 @@
198209
# using -lsqlite3.
199210
SQLITE3_OBJ.1 =
200211
SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
201212
SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
202213
214
+TCL_OBJ.1 =
215
+TCL_OBJ.0 = $(OBJDIR)/th_tcl.o
216
+TCL_OBJ. = $(TCL_OBJ.0)
217
+
203218
EXTRAOBJ = \
204219
$(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \
205220
$(OBJDIR)/shell.o \
206221
$(OBJDIR)/th.o \
207
- $(OBJDIR)/th_lang.o
222
+ $(OBJDIR)/th_lang.o \
223
+ $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) \
224
+ $(OBJDIR)/cson_amalgamation.o
208225
209226
$(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
210227
$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
211228
212229
# This rule prevents make from using its default rules to try build
@@ -225,17 +242,19 @@
225242
append mhargs " \$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h"
226243
set extra_h($s) {}
227244
}
228245
append mhargs " \$(SRCDIR)/sqlite3.h"
229246
append mhargs " \$(SRCDIR)/th.h"
247
+#append mhargs " \$(SRCDIR)/cson_amalgamation.h"
230248
append mhargs " \$(OBJDIR)/VERSION.h"
231249
writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex"
232250
writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >$@"
233251
writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h"
234252
writeln "\t\$(OBJDIR)/makeheaders $mhargs"
235253
writeln "\ttouch \$(OBJDIR)/headers"
236254
writeln "\$(OBJDIR)/headers: Makefile"
255
+writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h"
237256
writeln "Makefile:"
238257
set extra_h(main) \$(OBJDIR)/page_index.h
239258
240259
foreach s [lsort $src] {
241260
writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate"
@@ -264,10 +283,19 @@
264283
writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
265284
writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
266285
267286
writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
268287
writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
288
+
289
+writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
290
+writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n"
291
+
292
+set opt {}
293
+writeln {
294
+$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
295
+ $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE
296
+}
269297
270298
close $output_file
271299
#
272300
# End of the main.mk output
273301
##############################################################################
@@ -412,11 +440,12 @@
412440
413441
EXTRAOBJ = \
414442
$(OBJDIR)/sqlite3.o \
415443
$(OBJDIR)/shell.o \
416444
$(OBJDIR)/th.o \
417
- $(OBJDIR)/th_lang.o
445
+ $(OBJDIR)/th_lang.o \
446
+ $(OBJDIR)/cson_amalgamation.o
418447
419448
$(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/icon.o
420449
$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/icon.o
421450
422451
# This rule prevents make from using its default rules to try build
@@ -466,10 +495,15 @@
466495
467496
writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
468497
set opt $SQLITE_OPTIONS
469498
writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
470499
500
+set opt {}
501
+writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
502
+writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE\n"
503
+writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h"
504
+
471505
writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h"
472506
set opt {-Dmain=sqlite3_shell}
473507
append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1"
474508
writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n"
475509
@@ -577,10 +611,13 @@
577611
$(OBJDIR)\th$O : $(SRCDIR)\th.c
578612
$(TCC) -o$@ -c $**
579613
580614
$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
581615
$(TCC) -o$@ -c $**
616
+
617
+$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
618
+ cp $@ $@
582619
583620
VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
584621
+$** > $@
585622
586623
page_index.h: mkindex$E $(SRC)
@@ -590,10 +627,23 @@
590627
-del $(OBJDIR)\*.obj
591628
-del *.obj *_.c *.h *.map
592629
593630
realclean:
594631
-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
632
+
633
+$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
634
+$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
635
+$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
636
+$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
637
+$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
638
+$(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
639
+$(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
640
+$(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
641
+$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
642
+$(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
643
+$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
644
+
595645
596646
}
597647
foreach s [lsort $src] {
598648
writeln "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
599649
writeln "\t\$(TCC) -o\$@ -c ${s}_.c\n"
@@ -603,11 +653,11 @@
603653
604654
writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E "
605655
foreach s [lsort $src] {
606656
writeln -nonewline "${s}_.c:$s.h "
607657
}
608
-writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
658
+writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h"
609659
writeln "\t@copy /Y nul: headers"
610660
611661
close $output_file
612662
#
613663
# End of the win/Makefile.dmc output
@@ -717,10 +767,12 @@
717767
$(OX)\th_lang$O : $(SRCDIR)\th_lang.c
718768
$(TCC) /Fo$@ -c $**
719769
720770
VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
721771
$** > $@
772
+$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
773
+ cp $(SRCDIR)\cson_amalgamation.h $@
722774
723775
page_index.h: mkindex$E $(SRC)
724776
$** > $@
725777
726778
clean:
@@ -729,10 +781,22 @@
729781
-del headers linkopts
730782
731783
realclean:
732784
-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
733785
786
+$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
787
+$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
788
+$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
789
+$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
790
+$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
791
+$(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
792
+$(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
793
+$(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
794
+$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
795
+$(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
796
+$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
797
+
734798
}
735799
foreach s [lsort $src] {
736800
writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h"
737801
writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
738802
writeln "${s}_.c : \$(SRCDIR)\\$s.c"
@@ -741,11 +805,11 @@
741805
742806
writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E "
743807
foreach s [lsort $src] {
744808
writeln -nonewline "${s}_.c:$s.h "
745809
}
746
-writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
810
+writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h"
747811
writeln "\t@copy /Y nul: headers"
748812
749813
750814
close $output_file
751815
#
752816
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -53,10 +53,21 @@
53 http
54 http_socket
55 http_transport
56 import
57 info
 
 
 
 
 
 
 
 
 
 
 
58 leaf
59 login
60 main
61 manifest
62 md5
@@ -182,12 +193,12 @@
182 $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c
183
184 # WARNING. DANGER. Running the testsuite modifies the repository the
185 # build is done from, i.e. the checkout belongs to. Do not sync/push
186 # the repository after running the tests.
187 test: $(APPNAME)
188 $(TCLSH) test/tester.tcl $(APPNAME)
189
190 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
191 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \
192 $(SRCDIR)/../manifest \
193 $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
@@ -198,15 +209,21 @@
198 # using -lsqlite3.
199 SQLITE3_OBJ.1 =
200 SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
201 SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
202
 
 
 
 
203 EXTRAOBJ = \
204 $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \
205 $(OBJDIR)/shell.o \
206 $(OBJDIR)/th.o \
207 $(OBJDIR)/th_lang.o
 
 
208
209 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
210 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
211
212 # This rule prevents make from using its default rules to try build
@@ -225,17 +242,19 @@
225 append mhargs " \$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h"
226 set extra_h($s) {}
227 }
228 append mhargs " \$(SRCDIR)/sqlite3.h"
229 append mhargs " \$(SRCDIR)/th.h"
 
230 append mhargs " \$(OBJDIR)/VERSION.h"
231 writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex"
232 writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >$@"
233 writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h"
234 writeln "\t\$(OBJDIR)/makeheaders $mhargs"
235 writeln "\ttouch \$(OBJDIR)/headers"
236 writeln "\$(OBJDIR)/headers: Makefile"
 
237 writeln "Makefile:"
238 set extra_h(main) \$(OBJDIR)/page_index.h
239
240 foreach s [lsort $src] {
241 writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate"
@@ -264,10 +283,19 @@
264 writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
265 writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
266
267 writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
268 writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
 
 
 
 
 
 
 
 
 
269
270 close $output_file
271 #
272 # End of the main.mk output
273 ##############################################################################
@@ -412,11 +440,12 @@
412
413 EXTRAOBJ = \
414 $(OBJDIR)/sqlite3.o \
415 $(OBJDIR)/shell.o \
416 $(OBJDIR)/th.o \
417 $(OBJDIR)/th_lang.o
 
418
419 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/icon.o
420 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/icon.o
421
422 # This rule prevents make from using its default rules to try build
@@ -466,10 +495,15 @@
466
467 writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
468 set opt $SQLITE_OPTIONS
469 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
470
 
 
 
 
 
471 writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h"
472 set opt {-Dmain=sqlite3_shell}
473 append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1"
474 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n"
475
@@ -577,10 +611,13 @@
577 $(OBJDIR)\th$O : $(SRCDIR)\th.c
578 $(TCC) -o$@ -c $**
579
580 $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
581 $(TCC) -o$@ -c $**
 
 
 
582
583 VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
584 +$** > $@
585
586 page_index.h: mkindex$E $(SRC)
@@ -590,10 +627,23 @@
590 -del $(OBJDIR)\*.obj
591 -del *.obj *_.c *.h *.map
592
593 realclean:
594 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
 
 
 
 
 
 
 
 
 
 
 
 
 
595
596 }
597 foreach s [lsort $src] {
598 writeln "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
599 writeln "\t\$(TCC) -o\$@ -c ${s}_.c\n"
@@ -603,11 +653,11 @@
603
604 writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E "
605 foreach s [lsort $src] {
606 writeln -nonewline "${s}_.c:$s.h "
607 }
608 writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
609 writeln "\t@copy /Y nul: headers"
610
611 close $output_file
612 #
613 # End of the win/Makefile.dmc output
@@ -717,10 +767,12 @@
717 $(OX)\th_lang$O : $(SRCDIR)\th_lang.c
718 $(TCC) /Fo$@ -c $**
719
720 VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
721 $** > $@
 
 
722
723 page_index.h: mkindex$E $(SRC)
724 $** > $@
725
726 clean:
@@ -729,10 +781,22 @@
729 -del headers linkopts
730
731 realclean:
732 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
733
 
 
 
 
 
 
 
 
 
 
 
 
734 }
735 foreach s [lsort $src] {
736 writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h"
737 writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
738 writeln "${s}_.c : \$(SRCDIR)\\$s.c"
@@ -741,11 +805,11 @@
741
742 writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E "
743 foreach s [lsort $src] {
744 writeln -nonewline "${s}_.c:$s.h "
745 }
746 writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h"
747 writeln "\t@copy /Y nul: headers"
748
749
750 close $output_file
751 #
752
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -53,10 +53,21 @@
53 http
54 http_socket
55 http_transport
56 import
57 info
58 json
59 json_artifact
60 json_branch
61 json_diff
62 json_login
63 json_query
64 json_report
65 json_tag
66 json_timeline
67 json_user
68 json_wiki
69 leaf
70 login
71 main
72 manifest
73 md5
@@ -182,12 +193,12 @@
193 $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c
194
195 # WARNING. DANGER. Running the testsuite modifies the repository the
196 # build is done from, i.e. the checkout belongs to. Do not sync/push
197 # the repository after running the tests.
198 test: $(OBJDIR) $(APPNAME)
199 $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME)
200
201 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion
202 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \
203 $(SRCDIR)/../manifest \
204 $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
@@ -198,15 +209,21 @@
209 # using -lsqlite3.
210 SQLITE3_OBJ.1 =
211 SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o
212 SQLITE3_OBJ. = $(SQLITE3_OBJ.0)
213
214 TCL_OBJ.1 =
215 TCL_OBJ.0 = $(OBJDIR)/th_tcl.o
216 TCL_OBJ. = $(TCL_OBJ.0)
217
218 EXTRAOBJ = \
219 $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \
220 $(OBJDIR)/shell.o \
221 $(OBJDIR)/th.o \
222 $(OBJDIR)/th_lang.o \
223 $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) \
224 $(OBJDIR)/cson_amalgamation.o
225
226 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ)
227 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)
228
229 # This rule prevents make from using its default rules to try build
@@ -225,17 +242,19 @@
242 append mhargs " \$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h"
243 set extra_h($s) {}
244 }
245 append mhargs " \$(SRCDIR)/sqlite3.h"
246 append mhargs " \$(SRCDIR)/th.h"
247 #append mhargs " \$(SRCDIR)/cson_amalgamation.h"
248 append mhargs " \$(OBJDIR)/VERSION.h"
249 writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex"
250 writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >$@"
251 writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h"
252 writeln "\t\$(OBJDIR)/makeheaders $mhargs"
253 writeln "\ttouch \$(OBJDIR)/headers"
254 writeln "\$(OBJDIR)/headers: Makefile"
255 writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h"
256 writeln "Makefile:"
257 set extra_h(main) \$(OBJDIR)/page_index.h
258
259 foreach s [lsort $src] {
260 writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate"
@@ -264,10 +283,19 @@
283 writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
284 writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n"
285
286 writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
287 writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_lang.c -o \$(OBJDIR)/th_lang.o\n"
288
289 writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
290 writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th_tcl.c -o \$(OBJDIR)/th_tcl.o\n"
291
292 set opt {}
293 writeln {
294 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
295 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE
296 }
297
298 close $output_file
299 #
300 # End of the main.mk output
301 ##############################################################################
@@ -412,11 +440,12 @@
440
441 EXTRAOBJ = \
442 $(OBJDIR)/sqlite3.o \
443 $(OBJDIR)/shell.o \
444 $(OBJDIR)/th.o \
445 $(OBJDIR)/th_lang.o \
446 $(OBJDIR)/cson_amalgamation.o
447
448 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/icon.o
449 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/icon.o
450
451 # This rule prevents make from using its default rules to try build
@@ -466,10 +495,15 @@
495
496 writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c"
497 set opt $SQLITE_OPTIONS
498 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n"
499
500 set opt {}
501 writeln "\$(OBJDIR)/cson_amalgamation.o:\t\$(SRCDIR)/cson_amalgamation.c"
502 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/cson_amalgamation.c -o \$(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE\n"
503 writeln "\$(OBJDIR)/json.o \$(OBJDIR)/json_artifact.o \$(OBJDIR)/json_branch.o \$(OBJDIR)/json_diff.o \$(OBJDIR)/json_login.o \$(OBJDIR)/json_query.o \$(OBJDIR)/json_report.o \$(OBJDIR)/json_tag.o \$(OBJDIR)/json_timeline.o \$(OBJDIR)/json_user.o \$(OBJDIR)/json_wiki.o : \$(SRCDIR)/json_detail.h"
504
505 writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h"
506 set opt {-Dmain=sqlite3_shell}
507 append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1"
508 writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n"
509
@@ -577,10 +611,13 @@
611 $(OBJDIR)\th$O : $(SRCDIR)\th.c
612 $(TCC) -o$@ -c $**
613
614 $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
615 $(TCC) -o$@ -c $**
616
617 $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
618 cp $@ $@
619
620 VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
621 +$** > $@
622
623 page_index.h: mkindex$E $(SRC)
@@ -590,10 +627,23 @@
627 -del $(OBJDIR)\*.obj
628 -del *.obj *_.c *.h *.map
629
630 realclean:
631 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
632
633 $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
634 $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
635 $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
636 $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
637 $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
638 $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
639 $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
640 $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
641 $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
642 $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
643 $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
644
645
646 }
647 foreach s [lsort $src] {
648 writeln "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h"
649 writeln "\t\$(TCC) -o\$@ -c ${s}_.c\n"
@@ -603,11 +653,11 @@
653
654 writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E "
655 foreach s [lsort $src] {
656 writeln -nonewline "${s}_.c:$s.h "
657 }
658 writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h"
659 writeln "\t@copy /Y nul: headers"
660
661 close $output_file
662 #
663 # End of the win/Makefile.dmc output
@@ -717,10 +767,12 @@
767 $(OX)\th_lang$O : $(SRCDIR)\th_lang.c
768 $(TCC) /Fo$@ -c $**
769
770 VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
771 $** > $@
772 $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
773 cp $(SRCDIR)\cson_amalgamation.h $@
774
775 page_index.h: mkindex$E $(SRC)
776 $** > $@
777
778 clean:
@@ -729,10 +781,22 @@
781 -del headers linkopts
782
783 realclean:
784 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
785
786 $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
787 $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
788 $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
789 $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
790 $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
791 $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
792 $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
793 $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
794 $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
795 $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
796 $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
797
798 }
799 foreach s [lsort $src] {
800 writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h"
801 writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n"
802 writeln "${s}_.c : \$(SRCDIR)\\$s.c"
@@ -741,11 +805,11 @@
805
806 writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E "
807 foreach s [lsort $src] {
808 writeln -nonewline "${s}_.c:$s.h "
809 }
810 writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h"
811 writeln "\t@copy /Y nul: headers"
812
813
814 close $output_file
815 #
816
+60 -7
--- src/manifest.c
+++ src/manifest.c
@@ -1334,18 +1334,33 @@
13341334
}
13351335
}
13361336
}
13371337
if( pParent->zBaseline && pChild->zBaseline ){
13381338
/* Both parent and child are delta manifests. Look for files that
1339
- ** are marked as deleted in the parent but which reappear in the child
1340
- ** and show such files as being added in the child. */
1339
+ ** are deleted or modified in the parent but which reappear or revert
1340
+ ** to baseline in the child and show such files as being added or changed
1341
+ ** in the child. */
13411342
for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){
1342
- if( pParentFile->zUuid ) continue;
1343
- pChildFile = manifest_file_seek(pChild, pParentFile->zName);
1344
- if( pChildFile ){
1345
- add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1346
- isPublic, manifest_file_mperm(pChildFile));
1343
+ if( pParentFile->zUuid ){
1344
+ pChildFile = manifest_file_seek_base(pChild, pParentFile->zName);
1345
+ if( pChildFile==0 ){
1346
+ /* The child file reverts to baseline. Show this as a change */
1347
+ pChildFile = manifest_file_seek(pChild, pParentFile->zName);
1348
+ if( pChildFile ){
1349
+ add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1350
+ pChildFile->zName, 0, isPublic,
1351
+ manifest_file_mperm(pChildFile));
1352
+ }
1353
+ }
1354
+ }else{
1355
+ pChildFile = manifest_file_seek(pChild, pParentFile->zName);
1356
+ if( pChildFile ){
1357
+ /* File resurrected in the child after having been deleted in
1358
+ ** the parent. Show this as an added file. */
1359
+ add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1360
+ isPublic, manifest_file_mperm(pChildFile));
1361
+ }
13471362
}
13481363
}
13491364
}else if( pChild->zBaseline==0 ){
13501365
/* pChild is a baseline. Look for files that are present in pParent
13511366
** but are missing from pChild and mark them as having been deleted. */
@@ -1819,10 +1834,48 @@
18191834
"VALUES('t',%.17g,%d,%Q,%Q)",
18201835
p->rDate, rid, p->zUser, zComment
18211836
);
18221837
free(zComment);
18231838
}
1839
+ }
1840
+ if( p->type==CFTYPE_CONTROL ){
1841
+ Blob comment;
1842
+ int i;
1843
+ const char *zName;
1844
+ const char *zValue;
1845
+ const char *zUuid;
1846
+ blob_zero(&comment);
1847
+ for(i=0; i<p->nTag; i++){
1848
+ zUuid = p->aTag[i].zUuid;
1849
+ if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){
1850
+ if( i>0 ) blob_append(&comment, " ", 1);
1851
+ blob_appendf(&comment, "Tag changes on [/timeline?dp=%S&n=4 | %S]:",
1852
+ zUuid, zUuid);
1853
+ }
1854
+ zName = p->aTag[i].zName;
1855
+ zValue = p->aTag[i].zValue;
1856
+ if( zName[0]=='-' ){
1857
+ blob_appendf(&comment, " Cancel");
1858
+ }else if( zName[0]=='+' ){
1859
+ blob_appendf(&comment, " Add");
1860
+ }else{
1861
+ blob_appendf(&comment, " Add propagating");
1862
+ }
1863
+ if( memcmp(&zName[1], "sym-",4)==0 ){
1864
+ blob_appendf(&comment, " symbolic tag \"%h\".", &zName[5]);
1865
+ }else if( fossil_strcmp(&zName[1], "comment")!=0 && zValue && zValue[0] ){
1866
+ blob_appendf(&comment, " %h=%h.", &zName[1], zValue);
1867
+ }else{
1868
+ blob_appendf(&comment, " %h.", &zName[1]);
1869
+ }
1870
+ }
1871
+ db_multi_exec(
1872
+ "REPLACE INTO event(type,mtime,objid,user,comment)"
1873
+ "VALUES('g',%.17g,%d,%Q,%Q)",
1874
+ p->rDate, rid, p->zUser, blob_str(&comment)
1875
+ );
1876
+ blob_reset(&comment);
18241877
}
18251878
db_end_transaction(0);
18261879
if( p->type==CFTYPE_MANIFEST ){
18271880
manifest_cache_insert(p);
18281881
}else{
18291882
--- src/manifest.c
+++ src/manifest.c
@@ -1334,18 +1334,33 @@
1334 }
1335 }
1336 }
1337 if( pParent->zBaseline && pChild->zBaseline ){
1338 /* Both parent and child are delta manifests. Look for files that
1339 ** are marked as deleted in the parent but which reappear in the child
1340 ** and show such files as being added in the child. */
 
1341 for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){
1342 if( pParentFile->zUuid ) continue;
1343 pChildFile = manifest_file_seek(pChild, pParentFile->zName);
1344 if( pChildFile ){
1345 add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1346 isPublic, manifest_file_mperm(pChildFile));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1347 }
1348 }
1349 }else if( pChild->zBaseline==0 ){
1350 /* pChild is a baseline. Look for files that are present in pParent
1351 ** but are missing from pChild and mark them as having been deleted. */
@@ -1819,10 +1834,48 @@
1819 "VALUES('t',%.17g,%d,%Q,%Q)",
1820 p->rDate, rid, p->zUser, zComment
1821 );
1822 free(zComment);
1823 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1824 }
1825 db_end_transaction(0);
1826 if( p->type==CFTYPE_MANIFEST ){
1827 manifest_cache_insert(p);
1828 }else{
1829
--- src/manifest.c
+++ src/manifest.c
@@ -1334,18 +1334,33 @@
1334 }
1335 }
1336 }
1337 if( pParent->zBaseline && pChild->zBaseline ){
1338 /* Both parent and child are delta manifests. Look for files that
1339 ** are deleted or modified in the parent but which reappear or revert
1340 ** to baseline in the child and show such files as being added or changed
1341 ** in the child. */
1342 for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){
1343 if( pParentFile->zUuid ){
1344 pChildFile = manifest_file_seek_base(pChild, pParentFile->zName);
1345 if( pChildFile==0 ){
1346 /* The child file reverts to baseline. Show this as a change */
1347 pChildFile = manifest_file_seek(pChild, pParentFile->zName);
1348 if( pChildFile ){
1349 add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid,
1350 pChildFile->zName, 0, isPublic,
1351 manifest_file_mperm(pChildFile));
1352 }
1353 }
1354 }else{
1355 pChildFile = manifest_file_seek(pChild, pParentFile->zName);
1356 if( pChildFile ){
1357 /* File resurrected in the child after having been deleted in
1358 ** the parent. Show this as an added file. */
1359 add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0,
1360 isPublic, manifest_file_mperm(pChildFile));
1361 }
1362 }
1363 }
1364 }else if( pChild->zBaseline==0 ){
1365 /* pChild is a baseline. Look for files that are present in pParent
1366 ** but are missing from pChild and mark them as having been deleted. */
@@ -1819,10 +1834,48 @@
1834 "VALUES('t',%.17g,%d,%Q,%Q)",
1835 p->rDate, rid, p->zUser, zComment
1836 );
1837 free(zComment);
1838 }
1839 }
1840 if( p->type==CFTYPE_CONTROL ){
1841 Blob comment;
1842 int i;
1843 const char *zName;
1844 const char *zValue;
1845 const char *zUuid;
1846 blob_zero(&comment);
1847 for(i=0; i<p->nTag; i++){
1848 zUuid = p->aTag[i].zUuid;
1849 if( i==0 || fossil_strcmp(zUuid, p->aTag[i-1].zUuid)!=0 ){
1850 if( i>0 ) blob_append(&comment, " ", 1);
1851 blob_appendf(&comment, "Tag changes on [/timeline?dp=%S&n=4 | %S]:",
1852 zUuid, zUuid);
1853 }
1854 zName = p->aTag[i].zName;
1855 zValue = p->aTag[i].zValue;
1856 if( zName[0]=='-' ){
1857 blob_appendf(&comment, " Cancel");
1858 }else if( zName[0]=='+' ){
1859 blob_appendf(&comment, " Add");
1860 }else{
1861 blob_appendf(&comment, " Add propagating");
1862 }
1863 if( memcmp(&zName[1], "sym-",4)==0 ){
1864 blob_appendf(&comment, " symbolic tag \"%h\".", &zName[5]);
1865 }else if( fossil_strcmp(&zName[1], "comment")!=0 && zValue && zValue[0] ){
1866 blob_appendf(&comment, " %h=%h.", &zName[1], zValue);
1867 }else{
1868 blob_appendf(&comment, " %h.", &zName[1]);
1869 }
1870 }
1871 db_multi_exec(
1872 "REPLACE INTO event(type,mtime,objid,user,comment)"
1873 "VALUES('g',%.17g,%d,%Q,%Q)",
1874 p->rDate, rid, p->zUser, blob_str(&comment)
1875 );
1876 blob_reset(&comment);
1877 }
1878 db_end_transaction(0);
1879 if( p->type==CFTYPE_MANIFEST ){
1880 manifest_cache_insert(p);
1881 }else{
1882
+3 -2
--- src/md5.c
+++ src/md5.c
@@ -421,14 +421,15 @@
421421
return 0;
422422
}
423423
424424
425425
/*
426
-** COMMAND: test-md5sum
426
+** COMMAND: md5sum*
427
+** Usage: %fossil md5sum FILES....
427428
**
428429
** Compute an MD5 checksum of all files named on the command-line.
429
-** If an file is named "-" then take its content from standard input.
430
+** If a file is named "-" then content is read from standard input.
430431
*/
431432
void md5sum_test(void){
432433
int i;
433434
Blob in;
434435
Blob cksum;
435436
--- src/md5.c
+++ src/md5.c
@@ -421,14 +421,15 @@
421 return 0;
422 }
423
424
425 /*
426 ** COMMAND: test-md5sum
 
427 **
428 ** Compute an MD5 checksum of all files named on the command-line.
429 ** If an file is named "-" then take its content from standard input.
430 */
431 void md5sum_test(void){
432 int i;
433 Blob in;
434 Blob cksum;
435
--- src/md5.c
+++ src/md5.c
@@ -421,14 +421,15 @@
421 return 0;
422 }
423
424
425 /*
426 ** COMMAND: md5sum*
427 ** Usage: %fossil md5sum FILES....
428 **
429 ** Compute an MD5 checksum of all files named on the command-line.
430 ** If a file is named "-" then content is read from standard input.
431 */
432 void md5sum_test(void){
433 int i;
434 Blob in;
435 Blob cksum;
436
+18 -4
--- src/merge.c
+++ src/merge.c
@@ -73,10 +73,11 @@
7373
int debugFlag; /* True if --debug is present */
7474
int nChng; /* Number of file name changes */
7575
int *aChng; /* An array of file name changes */
7676
int i; /* Loop counter */
7777
int nConflict = 0; /* Number of conflicts seen */
78
+ int nOverwrite = 0; /* Number of unmanaged files overwritten */
7879
int caseSensitive; /* True for case-sensitive filenames */
7980
Stmt q;
8081
8182
8283
/* Notation:
@@ -319,19 +320,27 @@
319320
while( db_step(&q)==SQLITE_ROW ){
320321
int idm = db_column_int(&q, 0);
321322
int rowid = db_column_int(&q, 1);
322323
int idv;
323324
const char *zName;
325
+ char *zFullName;
324326
db_multi_exec(
325327
"INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
326328
" SELECT %d,3,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
327329
vid, idm
328330
);
329331
idv = db_last_insert_rowid();
330332
db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
331333
zName = db_column_text(&q, 2);
332
- fossil_print("ADDED %s\n", zName);
334
+ zFullName = mprintf("%s%s", g.zLocalRoot, zName);
335
+ if( file_wd_isfile_or_link(zFullName) ){
336
+ fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
337
+ nOverwrite++;
338
+ }else{
339
+ fossil_print("ADDED %s\n", zName);
340
+ }
341
+ fossil_free(zFullName);
333342
if( !nochangeFlag ){
334343
undo_save(zName);
335344
vfile_to_disk(0, idm, 0, 0);
336345
}
337346
}
@@ -495,13 +504,18 @@
495504
db_finalize(&q);
496505
497506
498507
/* Report on conflicts
499508
*/
500
- if( nConflict && !nochangeFlag ){
501
- fossil_warning(
502
- "WARNING: merge conflicts - see messages above for details.\n");
509
+ if( !nochangeFlag ){
510
+ if( nConflict ){
511
+ fossil_print("WARNING: %d merge conflicts", nConflict);
512
+ }
513
+ if( nOverwrite ){
514
+ fossil_warning("WARNING: %d unmanaged files were overwritten",
515
+ nOverwrite);
516
+ }
503517
}
504518
505519
/*
506520
** Clean up the mid and pid VFILE entries. Then commit the changes.
507521
*/
508522
--- src/merge.c
+++ src/merge.c
@@ -73,10 +73,11 @@
73 int debugFlag; /* True if --debug is present */
74 int nChng; /* Number of file name changes */
75 int *aChng; /* An array of file name changes */
76 int i; /* Loop counter */
77 int nConflict = 0; /* Number of conflicts seen */
 
78 int caseSensitive; /* True for case-sensitive filenames */
79 Stmt q;
80
81
82 /* Notation:
@@ -319,19 +320,27 @@
319 while( db_step(&q)==SQLITE_ROW ){
320 int idm = db_column_int(&q, 0);
321 int rowid = db_column_int(&q, 1);
322 int idv;
323 const char *zName;
 
324 db_multi_exec(
325 "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
326 " SELECT %d,3,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
327 vid, idm
328 );
329 idv = db_last_insert_rowid();
330 db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
331 zName = db_column_text(&q, 2);
332 fossil_print("ADDED %s\n", zName);
 
 
 
 
 
 
 
333 if( !nochangeFlag ){
334 undo_save(zName);
335 vfile_to_disk(0, idm, 0, 0);
336 }
337 }
@@ -495,13 +504,18 @@
495 db_finalize(&q);
496
497
498 /* Report on conflicts
499 */
500 if( nConflict && !nochangeFlag ){
501 fossil_warning(
502 "WARNING: merge conflicts - see messages above for details.\n");
 
 
 
 
 
503 }
504
505 /*
506 ** Clean up the mid and pid VFILE entries. Then commit the changes.
507 */
508
--- src/merge.c
+++ src/merge.c
@@ -73,10 +73,11 @@
73 int debugFlag; /* True if --debug is present */
74 int nChng; /* Number of file name changes */
75 int *aChng; /* An array of file name changes */
76 int i; /* Loop counter */
77 int nConflict = 0; /* Number of conflicts seen */
78 int nOverwrite = 0; /* Number of unmanaged files overwritten */
79 int caseSensitive; /* True for case-sensitive filenames */
80 Stmt q;
81
82
83 /* Notation:
@@ -319,19 +320,27 @@
320 while( db_step(&q)==SQLITE_ROW ){
321 int idm = db_column_int(&q, 0);
322 int rowid = db_column_int(&q, 1);
323 int idv;
324 const char *zName;
325 char *zFullName;
326 db_multi_exec(
327 "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)"
328 " SELECT %d,3,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d",
329 vid, idm
330 );
331 idv = db_last_insert_rowid();
332 db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid);
333 zName = db_column_text(&q, 2);
334 zFullName = mprintf("%s%s", g.zLocalRoot, zName);
335 if( file_wd_isfile_or_link(zFullName) ){
336 fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
337 nOverwrite++;
338 }else{
339 fossil_print("ADDED %s\n", zName);
340 }
341 fossil_free(zFullName);
342 if( !nochangeFlag ){
343 undo_save(zName);
344 vfile_to_disk(0, idm, 0, 0);
345 }
346 }
@@ -495,13 +504,18 @@
504 db_finalize(&q);
505
506
507 /* Report on conflicts
508 */
509 if( !nochangeFlag ){
510 if( nConflict ){
511 fossil_print("WARNING: %d merge conflicts", nConflict);
512 }
513 if( nOverwrite ){
514 fossil_warning("WARNING: %d unmanaged files were overwritten",
515 nOverwrite);
516 }
517 }
518
519 /*
520 ** Clean up the mid and pid VFILE entries. Then commit the changes.
521 */
522
+5 -3
--- src/merge3.c
+++ src/merge3.c
@@ -27,11 +27,13 @@
2727
#define DEBUG(X)
2828
#define ISDEBUG 0
2929
#endif
3030
3131
/* The minimum of two integers */
32
-#define min(A,B) (A<B?A:B)
32
+#ifndef min
33
+# define min(A,B) (A<B?A:B)
34
+#endif
3335
3436
/*
3537
** Compare N lines of text from pV1 and pV2. If the lines
3638
** are the same, return true. Return false if one or more of the N
3739
** lines are different.
@@ -171,12 +173,12 @@
171173
** is the number of lines of text to copy directly from the pivot,
172174
** the second integer is the number of lines of text to omit from the
173175
** pivot, and the third integer is the number of lines of text that are
174176
** inserted. The edit array ends with a triple of 0,0,0.
175177
*/
176
- aC1 = text_diff(pPivot, pV1, 0, 0, 0);
177
- aC2 = text_diff(pPivot, pV2, 0, 0, 0);
178
+ aC1 = text_diff(pPivot, pV1, 0, 0);
179
+ aC2 = text_diff(pPivot, pV2, 0, 0);
178180
if( aC1==0 || aC2==0 ){
179181
free(aC1);
180182
free(aC2);
181183
return -1;
182184
}
183185
--- src/merge3.c
+++ src/merge3.c
@@ -27,11 +27,13 @@
27 #define DEBUG(X)
28 #define ISDEBUG 0
29 #endif
30
31 /* The minimum of two integers */
32 #define min(A,B) (A<B?A:B)
 
 
33
34 /*
35 ** Compare N lines of text from pV1 and pV2. If the lines
36 ** are the same, return true. Return false if one or more of the N
37 ** lines are different.
@@ -171,12 +173,12 @@
171 ** is the number of lines of text to copy directly from the pivot,
172 ** the second integer is the number of lines of text to omit from the
173 ** pivot, and the third integer is the number of lines of text that are
174 ** inserted. The edit array ends with a triple of 0,0,0.
175 */
176 aC1 = text_diff(pPivot, pV1, 0, 0, 0);
177 aC2 = text_diff(pPivot, pV2, 0, 0, 0);
178 if( aC1==0 || aC2==0 ){
179 free(aC1);
180 free(aC2);
181 return -1;
182 }
183
--- src/merge3.c
+++ src/merge3.c
@@ -27,11 +27,13 @@
27 #define DEBUG(X)
28 #define ISDEBUG 0
29 #endif
30
31 /* The minimum of two integers */
32 #ifndef min
33 # define min(A,B) (A<B?A:B)
34 #endif
35
36 /*
37 ** Compare N lines of text from pV1 and pV2. If the lines
38 ** are the same, return true. Return false if one or more of the N
39 ** lines are different.
@@ -171,12 +173,12 @@
173 ** is the number of lines of text to copy directly from the pivot,
174 ** the second integer is the number of lines of text to omit from the
175 ** pivot, and the third integer is the number of lines of text that are
176 ** inserted. The edit array ends with a triple of 0,0,0.
177 */
178 aC1 = text_diff(pPivot, pV1, 0, 0);
179 aC2 = text_diff(pPivot, pV2, 0, 0);
180 if( aC1==0 || aC2==0 ){
181 free(aC1);
182 free(aC2);
183 return -1;
184 }
185
+67 -7
--- src/mkindex.c
+++ src/mkindex.c
@@ -36,11 +36,13 @@
3636
** We also scan for comments lines of this form:
3737
**
3838
** COMMAND: cmdname
3939
**
4040
** These entries build a constant table used to map command names into
41
-** functions.
41
+** functions. If cmdname ends with "*" then the command is a second-tier
42
+** command that is not displayed by the "fossil help" command. The
43
+** final "*" is not considered to be part of the command name.
4244
**
4345
** Comment text following COMMAND: through the end of the comment is
4446
** understood to be help text for the command specified. This help
4547
** text is accumulated and a table containing the text for each command
4648
** is generated. That table is used implement the "fossil help" command
@@ -59,10 +61,11 @@
5961
/*
6062
** Each entry looks like this:
6163
*/
6264
typedef struct Entry {
6365
int eType;
66
+ char *zIf;
6467
char *zFunc;
6568
char *zPath;
6669
char *zHelp;
6770
} Entry;
6871
@@ -85,10 +88,15 @@
8588
** Current help message accumulator
8689
*/
8790
char zHelp[MX_HELP];
8891
int nHelp;
8992
93
+/*
94
+** Most recently encountered #if
95
+*/
96
+char zIf[200];
97
+
9098
/*
9199
** How many entries are used
92100
*/
93101
int nUsed;
94102
int nFixed;
@@ -133,10 +141,30 @@
133141
aEntry[nUsed].eType = eType;
134142
aEntry[nUsed].zPath = string_dup(&zLine[i], j);
135143
aEntry[nUsed].zFunc = 0;
136144
nUsed++;
137145
}
146
+
147
+/*
148
+** Check to see if the current line is an #if and if it is, add it to
149
+** the zIf[] string. If the current line is an #endif or #else or #elif
150
+** then cancel the current zIf[] string.
151
+*/
152
+void scan_for_if(const char *zLine){
153
+ int i;
154
+ int len;
155
+ if( zLine[0]!='#' ) return;
156
+ for(i=1; isspace(zLine[i]); i++){}
157
+ if( zLine[i]==0 ) return;
158
+ len = strlen(&zLine[i]);
159
+ if( memcmp(&zLine[i],"if",2)==0 ){
160
+ zIf[0] = '#';
161
+ memcpy(&zIf[1], &zLine[i], len+1);
162
+ }else if( zLine[i]=='e' ){
163
+ zIf[0] = 0;
164
+ }
165
+}
138166
139167
/*
140168
** Scan a line for a function that implements a web page or command.
141169
*/
142170
void scan_for_func(char *zLine){
@@ -177,10 +205,11 @@
177205
z = string_dup(&zHelp[k], nHelp-k);
178206
}else{
179207
z = 0;
180208
}
181209
for(k=nFixed; k<nUsed; k++){
210
+ aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0;
182211
aEntry[k].zFunc = string_dup(&zLine[i], j);
183212
aEntry[k].zHelp = z;
184213
}
185214
i+=j;
186215
while( isspace(zLine[i]) ){ i++; }
@@ -217,41 +246,68 @@
217246
int i;
218247
int nType0;
219248
220249
qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare);
221250
for(i=0; i<nFixed; i++){
251
+ if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
222252
printf("extern void %s(void);\n", aEntry[i].zFunc);
253
+ if( aEntry[i].zIf ) printf("#endif\n");
223254
}
224255
printf(
225256
"typedef struct NameMap NameMap;\n"
226257
"struct NameMap {\n"
227258
" const char *zName;\n"
228259
" void (*xFunc)(void);\n"
260
+ " char cmdFlags;\n"
229261
"};\n"
262
+ "#define CMDFLAG_1ST_TIER 0x01\n"
263
+ "#define CMDFLAG_2ND_TIER 0x02\n"
264
+ "#define CMDFLAG_TEST 0x04\n"
230265
"static const NameMap aWebpage[] = {\n"
231266
);
232267
for(i=0; i<nFixed && aEntry[i].eType==0; i++){
233
- printf(" { \"%s\",%*s %s },\n",
234
- aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "",
235
- aEntry[i].zFunc
268
+ const char *z = aEntry[i].zPath;
269
+ int n = strlen(z);
270
+ if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
271
+ printf(" { \"%s\",%*s %s,%*s 1 },\n",
272
+ z,
273
+ 25-n, "",
274
+ aEntry[i].zFunc,
275
+ (int)(35-strlen(aEntry[i].zFunc)), ""
236276
);
277
+ if( aEntry[i].zIf ) printf("#endif\n");
237278
}
238279
printf("};\n");
239280
nType0 = i;
240281
printf(
241282
"static const NameMap aCommand[] = {\n"
242283
);
243284
for(i=nType0; i<nFixed && aEntry[i].eType==1; i++){
244
- printf(" { \"%s\",%*s %s },\n",
245
- aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "",
246
- aEntry[i].zFunc
285
+ const char *z = aEntry[i].zPath;
286
+ int n = strlen(z);
287
+ int cmdFlags = 0x01;
288
+ if( z[n-1]=='*' ){
289
+ n--;
290
+ cmdFlags = 0x02;
291
+ }else if( memcmp(z, "test-", 5)==0 ){
292
+ cmdFlags = 0x04;
293
+ }
294
+ if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
295
+ printf(" { \"%.*s\",%*s %s,%*s %d },\n",
296
+ n, z,
297
+ 25-n, "",
298
+ aEntry[i].zFunc,
299
+ (int)(35-strlen(aEntry[i].zFunc)), "",
300
+ cmdFlags
247301
);
302
+ if( aEntry[i].zIf ) printf("#endif\n");
248303
}
249304
printf("};\n");
250305
for(i=nType0; i<nFixed; i++){
251306
char *z = aEntry[i].zHelp;
252307
if( z && z[0] ){
308
+ if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
253309
printf("static const char zHelp_%s[] = \n", aEntry[i].zFunc);
254310
printf(" \"");
255311
while( *z ){
256312
if( *z=='\n' ){
257313
printf("\\n\"\n \"");
@@ -261,22 +317,25 @@
261317
putchar(*z);
262318
}
263319
z++;
264320
}
265321
printf("\";\n");
322
+ if( aEntry[i].zIf ) printf("#endif\n");
266323
aEntry[i].zHelp[0] = 0;
267324
}
268325
}
269326
printf(
270327
"static const char * const aCmdHelp[] = {\n"
271328
);
272329
for(i=nType0; i<nFixed; i++){
330
+ if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
273331
if( aEntry[i].zHelp==0 ){
274332
printf(" 0,\n");
275333
}else{
276334
printf(" zHelp_%s,\n", aEntry[i].zFunc);
277335
}
336
+ if( aEntry[i].zIf ) printf("#endif\n");
278337
}
279338
printf("};\n");
280339
}
281340
282341
/*
@@ -290,10 +349,11 @@
290349
return;
291350
}
292351
nLine = 0;
293352
while( fgets(zLine, sizeof(zLine), in) ){
294353
nLine++;
354
+ scan_for_if(zLine);
295355
scan_for_label("WEBPAGE:",zLine,0);
296356
scan_for_label("COMMAND:",zLine,1);
297357
scan_for_func(zLine);
298358
}
299359
fclose(in);
300360
--- src/mkindex.c
+++ src/mkindex.c
@@ -36,11 +36,13 @@
36 ** We also scan for comments lines of this form:
37 **
38 ** COMMAND: cmdname
39 **
40 ** These entries build a constant table used to map command names into
41 ** functions.
 
 
42 **
43 ** Comment text following COMMAND: through the end of the comment is
44 ** understood to be help text for the command specified. This help
45 ** text is accumulated and a table containing the text for each command
46 ** is generated. That table is used implement the "fossil help" command
@@ -59,10 +61,11 @@
59 /*
60 ** Each entry looks like this:
61 */
62 typedef struct Entry {
63 int eType;
 
64 char *zFunc;
65 char *zPath;
66 char *zHelp;
67 } Entry;
68
@@ -85,10 +88,15 @@
85 ** Current help message accumulator
86 */
87 char zHelp[MX_HELP];
88 int nHelp;
89
 
 
 
 
 
90 /*
91 ** How many entries are used
92 */
93 int nUsed;
94 int nFixed;
@@ -133,10 +141,30 @@
133 aEntry[nUsed].eType = eType;
134 aEntry[nUsed].zPath = string_dup(&zLine[i], j);
135 aEntry[nUsed].zFunc = 0;
136 nUsed++;
137 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
139 /*
140 ** Scan a line for a function that implements a web page or command.
141 */
142 void scan_for_func(char *zLine){
@@ -177,10 +205,11 @@
177 z = string_dup(&zHelp[k], nHelp-k);
178 }else{
179 z = 0;
180 }
181 for(k=nFixed; k<nUsed; k++){
 
182 aEntry[k].zFunc = string_dup(&zLine[i], j);
183 aEntry[k].zHelp = z;
184 }
185 i+=j;
186 while( isspace(zLine[i]) ){ i++; }
@@ -217,41 +246,68 @@
217 int i;
218 int nType0;
219
220 qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare);
221 for(i=0; i<nFixed; i++){
 
222 printf("extern void %s(void);\n", aEntry[i].zFunc);
 
223 }
224 printf(
225 "typedef struct NameMap NameMap;\n"
226 "struct NameMap {\n"
227 " const char *zName;\n"
228 " void (*xFunc)(void);\n"
 
229 "};\n"
 
 
 
230 "static const NameMap aWebpage[] = {\n"
231 );
232 for(i=0; i<nFixed && aEntry[i].eType==0; i++){
233 printf(" { \"%s\",%*s %s },\n",
234 aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "",
235 aEntry[i].zFunc
 
 
 
 
 
236 );
 
237 }
238 printf("};\n");
239 nType0 = i;
240 printf(
241 "static const NameMap aCommand[] = {\n"
242 );
243 for(i=nType0; i<nFixed && aEntry[i].eType==1; i++){
244 printf(" { \"%s\",%*s %s },\n",
245 aEntry[i].zPath, (int)(25-strlen(aEntry[i].zPath)), "",
246 aEntry[i].zFunc
 
 
 
 
 
 
 
 
 
 
 
 
 
247 );
 
248 }
249 printf("};\n");
250 for(i=nType0; i<nFixed; i++){
251 char *z = aEntry[i].zHelp;
252 if( z && z[0] ){
 
253 printf("static const char zHelp_%s[] = \n", aEntry[i].zFunc);
254 printf(" \"");
255 while( *z ){
256 if( *z=='\n' ){
257 printf("\\n\"\n \"");
@@ -261,22 +317,25 @@
261 putchar(*z);
262 }
263 z++;
264 }
265 printf("\";\n");
 
266 aEntry[i].zHelp[0] = 0;
267 }
268 }
269 printf(
270 "static const char * const aCmdHelp[] = {\n"
271 );
272 for(i=nType0; i<nFixed; i++){
 
273 if( aEntry[i].zHelp==0 ){
274 printf(" 0,\n");
275 }else{
276 printf(" zHelp_%s,\n", aEntry[i].zFunc);
277 }
 
278 }
279 printf("};\n");
280 }
281
282 /*
@@ -290,10 +349,11 @@
290 return;
291 }
292 nLine = 0;
293 while( fgets(zLine, sizeof(zLine), in) ){
294 nLine++;
 
295 scan_for_label("WEBPAGE:",zLine,0);
296 scan_for_label("COMMAND:",zLine,1);
297 scan_for_func(zLine);
298 }
299 fclose(in);
300
--- src/mkindex.c
+++ src/mkindex.c
@@ -36,11 +36,13 @@
36 ** We also scan for comments lines of this form:
37 **
38 ** COMMAND: cmdname
39 **
40 ** These entries build a constant table used to map command names into
41 ** functions. If cmdname ends with "*" then the command is a second-tier
42 ** command that is not displayed by the "fossil help" command. The
43 ** final "*" is not considered to be part of the command name.
44 **
45 ** Comment text following COMMAND: through the end of the comment is
46 ** understood to be help text for the command specified. This help
47 ** text is accumulated and a table containing the text for each command
48 ** is generated. That table is used implement the "fossil help" command
@@ -59,10 +61,11 @@
61 /*
62 ** Each entry looks like this:
63 */
64 typedef struct Entry {
65 int eType;
66 char *zIf;
67 char *zFunc;
68 char *zPath;
69 char *zHelp;
70 } Entry;
71
@@ -85,10 +88,15 @@
88 ** Current help message accumulator
89 */
90 char zHelp[MX_HELP];
91 int nHelp;
92
93 /*
94 ** Most recently encountered #if
95 */
96 char zIf[200];
97
98 /*
99 ** How many entries are used
100 */
101 int nUsed;
102 int nFixed;
@@ -133,10 +141,30 @@
141 aEntry[nUsed].eType = eType;
142 aEntry[nUsed].zPath = string_dup(&zLine[i], j);
143 aEntry[nUsed].zFunc = 0;
144 nUsed++;
145 }
146
147 /*
148 ** Check to see if the current line is an #if and if it is, add it to
149 ** the zIf[] string. If the current line is an #endif or #else or #elif
150 ** then cancel the current zIf[] string.
151 */
152 void scan_for_if(const char *zLine){
153 int i;
154 int len;
155 if( zLine[0]!='#' ) return;
156 for(i=1; isspace(zLine[i]); i++){}
157 if( zLine[i]==0 ) return;
158 len = strlen(&zLine[i]);
159 if( memcmp(&zLine[i],"if",2)==0 ){
160 zIf[0] = '#';
161 memcpy(&zIf[1], &zLine[i], len+1);
162 }else if( zLine[i]=='e' ){
163 zIf[0] = 0;
164 }
165 }
166
167 /*
168 ** Scan a line for a function that implements a web page or command.
169 */
170 void scan_for_func(char *zLine){
@@ -177,10 +205,11 @@
205 z = string_dup(&zHelp[k], nHelp-k);
206 }else{
207 z = 0;
208 }
209 for(k=nFixed; k<nUsed; k++){
210 aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0;
211 aEntry[k].zFunc = string_dup(&zLine[i], j);
212 aEntry[k].zHelp = z;
213 }
214 i+=j;
215 while( isspace(zLine[i]) ){ i++; }
@@ -217,41 +246,68 @@
246 int i;
247 int nType0;
248
249 qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare);
250 for(i=0; i<nFixed; i++){
251 if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
252 printf("extern void %s(void);\n", aEntry[i].zFunc);
253 if( aEntry[i].zIf ) printf("#endif\n");
254 }
255 printf(
256 "typedef struct NameMap NameMap;\n"
257 "struct NameMap {\n"
258 " const char *zName;\n"
259 " void (*xFunc)(void);\n"
260 " char cmdFlags;\n"
261 "};\n"
262 "#define CMDFLAG_1ST_TIER 0x01\n"
263 "#define CMDFLAG_2ND_TIER 0x02\n"
264 "#define CMDFLAG_TEST 0x04\n"
265 "static const NameMap aWebpage[] = {\n"
266 );
267 for(i=0; i<nFixed && aEntry[i].eType==0; i++){
268 const char *z = aEntry[i].zPath;
269 int n = strlen(z);
270 if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
271 printf(" { \"%s\",%*s %s,%*s 1 },\n",
272 z,
273 25-n, "",
274 aEntry[i].zFunc,
275 (int)(35-strlen(aEntry[i].zFunc)), ""
276 );
277 if( aEntry[i].zIf ) printf("#endif\n");
278 }
279 printf("};\n");
280 nType0 = i;
281 printf(
282 "static const NameMap aCommand[] = {\n"
283 );
284 for(i=nType0; i<nFixed && aEntry[i].eType==1; i++){
285 const char *z = aEntry[i].zPath;
286 int n = strlen(z);
287 int cmdFlags = 0x01;
288 if( z[n-1]=='*' ){
289 n--;
290 cmdFlags = 0x02;
291 }else if( memcmp(z, "test-", 5)==0 ){
292 cmdFlags = 0x04;
293 }
294 if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
295 printf(" { \"%.*s\",%*s %s,%*s %d },\n",
296 n, z,
297 25-n, "",
298 aEntry[i].zFunc,
299 (int)(35-strlen(aEntry[i].zFunc)), "",
300 cmdFlags
301 );
302 if( aEntry[i].zIf ) printf("#endif\n");
303 }
304 printf("};\n");
305 for(i=nType0; i<nFixed; i++){
306 char *z = aEntry[i].zHelp;
307 if( z && z[0] ){
308 if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
309 printf("static const char zHelp_%s[] = \n", aEntry[i].zFunc);
310 printf(" \"");
311 while( *z ){
312 if( *z=='\n' ){
313 printf("\\n\"\n \"");
@@ -261,22 +317,25 @@
317 putchar(*z);
318 }
319 z++;
320 }
321 printf("\";\n");
322 if( aEntry[i].zIf ) printf("#endif\n");
323 aEntry[i].zHelp[0] = 0;
324 }
325 }
326 printf(
327 "static const char * const aCmdHelp[] = {\n"
328 );
329 for(i=nType0; i<nFixed; i++){
330 if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf);
331 if( aEntry[i].zHelp==0 ){
332 printf(" 0,\n");
333 }else{
334 printf(" zHelp_%s,\n", aEntry[i].zFunc);
335 }
336 if( aEntry[i].zIf ) printf("#endif\n");
337 }
338 printf("};\n");
339 }
340
341 /*
@@ -290,10 +349,11 @@
349 return;
350 }
351 nLine = 0;
352 while( fgets(zLine, sizeof(zLine), in) ){
353 nLine++;
354 scan_for_if(zLine);
355 scan_for_label("WEBPAGE:",zLine,0);
356 scan_for_label("COMMAND:",zLine,1);
357 scan_for_func(zLine);
358 }
359 fclose(in);
360
+336 -262
--- src/name.c
+++ src/name.c
@@ -22,10 +22,220 @@
2222
** not necessarily in canonical form.
2323
*/
2424
#include "config.h"
2525
#include "name.h"
2626
#include <assert.h>
27
+
28
+/*
29
+** Return TRUE if the string begins with something that looks roughly
30
+** like an ISO date/time string. The SQLite date/time functions will
31
+** have the final say-so about whether or not the date/time string is
32
+** well-formed.
33
+*/
34
+static int is_date(const char *z){
35
+ if( !fossil_isdigit(z[0]) ) return 0;
36
+ if( !fossil_isdigit(z[1]) ) return 0;
37
+ if( !fossil_isdigit(z[2]) ) return 0;
38
+ if( !fossil_isdigit(z[3]) ) return 0;
39
+ if( z[4]!='-') return 0;
40
+ if( !fossil_isdigit(z[5]) ) return 0;
41
+ if( !fossil_isdigit(z[6]) ) return 0;
42
+ if( z[7]!='-') return 0;
43
+ if( !fossil_isdigit(z[8]) ) return 0;
44
+ if( !fossil_isdigit(z[9]) ) return 0;
45
+ return 1;
46
+}
47
+
48
+/*
49
+** Convert a symbolic name into a RID. Acceptable forms:
50
+**
51
+** * SHA1 hash
52
+** * SHA1 hash prefix of at least 4 characters
53
+** * Symbolic Name
54
+** * "tag:" + symbolic name
55
+** * Date or date-time
56
+** * "date:" + Date or date-time
57
+** * symbolic-name ":" date-time
58
+** * "tip"
59
+**
60
+** The following additional forms are available in local checkouts:
61
+**
62
+** * "current"
63
+** * "prev" or "previous"
64
+** * "next"
65
+**
66
+** Return the RID of the matching artifact. Or return 0 if the name does not
67
+** match any known object. Or return -1 if the name is ambiguious.
68
+**
69
+** The zType parameter specifies the type of artifact: ci, t, w, e, g.
70
+** If zType is NULL or "" or "*" then any type of artifact will serve.
71
+** zType is "ci" in most use cases since we are usually searching for
72
+** a check-in.
73
+*/
74
+static int symbolic_name_to_rid(const char *zTag, const char *zType){
75
+ int vid;
76
+ int rid = 0;
77
+ int nTag;
78
+ int i;
79
+
80
+ if( zType==0 || zType[0]==0 ) zType = "*";
81
+ if( zTag==0 || zTag[0]==0 ) return 0;
82
+
83
+ /* special keyword: "tip" */
84
+ if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){
85
+ rid = db_int(0,
86
+ "SELECT objid"
87
+ " FROM event"
88
+ " WHERE type='ci'"
89
+ " ORDER BY event.mtime DESC"
90
+ );
91
+ if( rid ) return rid;
92
+ }
93
+
94
+ /* special keywords: "prev", "previous", "current", and "next" */
95
+ if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){
96
+ if( fossil_strcmp(zTag, "current")==0 ){
97
+ rid = vid;
98
+ }else if( fossil_strcmp(zTag, "prev")==0
99
+ || fossil_strcmp(zTag, "previous")==0 ){
100
+ rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid);
101
+ }else if( fossil_strcmp(zTag, "next")==0 ){
102
+ rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
103
+ " ORDER BY isprim DESC, mtime DESC", vid);
104
+ }
105
+ if( rid ) return rid;
106
+ }
107
+
108
+ /* Date and times */
109
+ if( memcmp(zTag, "date:", 5)==0 ){
110
+ rid = db_int(0,
111
+ "SELECT objid FROM event"
112
+ " WHERE mtime<=julianday(%Q) AND type GLOB '%q'"
113
+ " ORDER BY mtime DESC LIMIT 1",
114
+ &zTag[5], zType);
115
+ return rid;
116
+ }
117
+ if( is_date(zTag) ){
118
+ rid = db_int(0,
119
+ "SELECT objid FROM event"
120
+ " WHERE mtime<=julianday(%Q) AND type GLOB '%q'"
121
+ " ORDER BY mtime DESC LIMIT 1",
122
+ zTag, zType);
123
+ if( rid) return rid;
124
+ }
125
+
126
+ /* Deprecated date & time formats: "local:" + date-time and
127
+ ** "utc:" + date-time */
128
+ if( memcmp(zTag, "local:", 6)==0 ){
129
+ rid = db_int(0,
130
+ "SELECT objid FROM event"
131
+ " WHERE mtime<=julianday(%Q) AND type GLOB '%q'"
132
+ " ORDER BY mtime DESC LIMIT 1",
133
+ &zTag[6], zType);
134
+ return rid;
135
+ }
136
+ if( memcmp(zTag, "utc:", 4)==0 ){
137
+ rid = db_int(0,
138
+ "SELECT objid FROM event"
139
+ " WHERE mtime<=julianday('%qz') AND type GLOB '%q'"
140
+ " ORDER BY mtime DESC LIMIT 1",
141
+ &zTag[4], zType);
142
+ return rid;
143
+ }
144
+
145
+ /* "tag:" + symbolic-name */
146
+ if( memcmp(zTag, "tag:", 4)==0 ){
147
+ rid = db_int(0,
148
+ "SELECT event.objid"
149
+ " FROM tag, tagxref, event"
150
+ " WHERE tag.tagname='sym-%q' "
151
+ " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
152
+ " AND event.objid=tagxref.rid "
153
+ " AND event.type GLOB '%q'"
154
+ " ORDER BY event.mtime DESC /*sort*/",
155
+ &zTag[4], zType
156
+ );
157
+ return rid;
158
+ }
159
+
160
+ /* symbolic-name ":" date-time */
161
+ nTag = strlen(zTag);
162
+ for(i=0; i<nTag-10 && zTag[i]!=':'; i++){}
163
+ if( zTag[i]==':' && is_date(&zTag[i+1]) ){
164
+ char *zDate = mprintf("%s", &zTag[i+1]);
165
+ char *zTagBase = mprintf("%.*s", i, zTag);
166
+ int nDate = strlen(zDate);
167
+ if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
168
+ zDate[nDate-3] = 'z';
169
+ zDate[nDate-2] = 0;
170
+ }
171
+ rid = db_int(0,
172
+ "SELECT event.objid"
173
+ " FROM tag, tagxref, event"
174
+ " WHERE tag.tagname='sym-%q' "
175
+ " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
176
+ " AND event.objid=tagxref.rid "
177
+ " AND event.mtime<=julianday(%Q)"
178
+ " AND event.type GLOB '%q'"
179
+ " ORDER BY event.mtime DESC /*sort*/ ",
180
+ zTagBase, zDate, zType
181
+ );
182
+ return rid;
183
+ }
184
+
185
+ /* SHA1 hash or prefix */
186
+ if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){
187
+ Stmt q;
188
+ char zUuid[UUID_SIZE+1];
189
+ memcpy(zUuid, zTag, nTag+1);
190
+ canonical16(zUuid, nTag);
191
+ rid = 0;
192
+ if( zType[0]=='*' ){
193
+ db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid);
194
+ }else{
195
+ db_prepare(&q,
196
+ "SELECT blob.rid"
197
+ " FROM blob, event"
198
+ " WHERE blob.uuid GLOB '%s*'"
199
+ " AND event.objid=blob.rid"
200
+ " AND event.type GLOB '%q'",
201
+ zUuid, zType
202
+ );
203
+ }
204
+ if( db_step(&q)==SQLITE_ROW ){
205
+ rid = db_column_int(&q, 0);
206
+ if( db_step(&q)==SQLITE_ROW ) rid = -1;
207
+ }
208
+ db_finalize(&q);
209
+ if( rid ) return rid;
210
+ }
211
+
212
+ /* Symbolic name */
213
+ rid = db_int(0,
214
+ "SELECT event.objid"
215
+ " FROM tag, tagxref, event"
216
+ " WHERE tag.tagname='sym-%q' "
217
+ " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
218
+ " AND event.objid=tagxref.rid "
219
+ " AND event.type GLOB '%q'"
220
+ " ORDER BY event.mtime DESC /*sort*/ ",
221
+ zTag, zType
222
+ );
223
+ if( rid>0 ) return rid;
224
+
225
+ /* Undocumented: numeric tags get translated directly into the RID */
226
+ for(i=0; fossil_isdigit(zTag[i]); i++){}
227
+ if( zTag[i]==0 ){
228
+ rid = db_int(0,
229
+ "SELECT event.objid"
230
+ " FROM event"
231
+ " WHERE event.objid=%s"
232
+ " AND event.type GLOB '%q'", zTag, zType);
233
+ }
234
+ return rid;
235
+}
236
+
27237
28238
/*
29239
** This routine takes a user-entered UUID which might be in mixed
30240
** case and might only be a prefix of the full UUID and converts it
31241
** into the full-length UUID in canonical form.
@@ -41,228 +251,23 @@
41251
**
42252
** Return 0 on success. Return 1 if the name cannot be resolved.
43253
** Return 2 name is ambiguous.
44254
*/
45255
int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){
46
- int rc;
47
- int sz;
48
- sz = blob_size(pName);
49
- if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){
50
- char *zUuid;
51
- const char *zName = blob_str(pName);
52
- if( memcmp(zName, "tag:", 4)==0 ){
53
- zName += 4;
54
- zUuid = tag_to_uuid(zName, zType);
55
- }else{
56
- zUuid = tag_to_uuid(zName, zType);
57
- if( zUuid==0 ){
58
- zUuid = date_to_uuid(zName, zType);
59
- }
60
- }
61
- if( zUuid ){
62
- blob_reset(pName);
63
- blob_append(pName, zUuid, -1);
64
- free(zUuid);
65
- return 0;
66
- }
67
- fossil_error(iErrPriority, "not a valid object name: %s", zName);
68
- return 1;
69
- }
70
- blob_materialize(pName);
71
- canonical16(blob_buffer(pName), sz);
72
- if( sz==UUID_SIZE ){
73
- rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName);
74
- if( rc ){
75
- fossil_error(iErrPriority, "no such artifact: %b", pName);
76
- blob_reset(pName);
77
- }
78
- }else if( sz<UUID_SIZE && sz>=4 ){
79
- Stmt q;
80
- db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName);
81
- if( db_step(&q)!=SQLITE_ROW ){
82
- char *zUuid;
83
- db_finalize(&q);
84
- zUuid = tag_to_uuid(blob_str(pName), "*");
85
- if( zUuid ){
86
- blob_reset(pName);
87
- blob_append(pName, zUuid, -1);
88
- free(zUuid);
89
- return 0;
90
- }
91
- fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName);
92
- return 1;
93
- }
94
- blob_reset(pName);
95
- blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0));
96
- if( db_step(&q)==SQLITE_ROW ){
97
- fossil_error(iErrPriority,
98
- "multiple artifacts match"
99
- );
100
- blob_reset(pName);
101
- db_finalize(&q);
102
- return 2;
103
- }
104
- db_finalize(&q);
105
- rc = 0;
106
- }else{
107
- rc = 0;
108
- }
109
- return rc;
110
-}
111
-
112
-/*
113
-** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD.
114
-*/
115
-static int is_date(const char *z){
116
- if( !fossil_isdigit(z[0]) ) return 0;
117
- if( !fossil_isdigit(z[1]) ) return 0;
118
- if( !fossil_isdigit(z[2]) ) return 0;
119
- if( !fossil_isdigit(z[3]) ) return 0;
120
- if( z[4]!='-') return 0;
121
- if( !fossil_isdigit(z[5]) ) return 0;
122
- if( !fossil_isdigit(z[6]) ) return 0;
123
- if( z[7]!='-') return 0;
124
- if( !fossil_isdigit(z[8]) ) return 0;
125
- if( !fossil_isdigit(z[9]) ) return 0;
126
- return 1;
127
-}
128
-
129
-/*
130
-** Convert a symbolic tag name into the UUID of a check-in that contains
131
-** that tag. If the tag appears on multiple check-ins, return the UUID
132
-** of the most recent check-in with the tag.
133
-**
134
-** If the input string is of the form:
135
-**
136
-** tag:date
137
-**
138
-** Then return the UUID of the oldest check-in with that tag that is
139
-** not older than 'date'.
140
-**
141
-** An input of "tip" returns the most recent check-in.
142
-**
143
-** Memory to hold the returned string comes from malloc() and needs to
144
-** be freed by the caller.
145
-*/
146
-char *tag_to_uuid(const char *zTag, const char *zType){
147
- int vid;
148
- char *zUuid;
149
-
150
- if( zType==0 || zType[0]==0 ) zType = "*";
151
- zUuid = db_text(0,
152
- "SELECT blob.uuid"
153
- " FROM tag, tagxref, event, blob"
154
- " WHERE tag.tagname='sym-%q' "
155
- " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
156
- " AND event.objid=tagxref.rid "
157
- " AND blob.rid=event.objid "
158
- " AND event.type GLOB '%q'"
159
- " ORDER BY event.mtime DESC /*sort*/",
160
- zTag, zType
161
- );
162
- if( zUuid==0 ){
163
- int nTag = strlen(zTag);
164
- int i;
165
- for(i=0; i<nTag-10; i++){
166
- if( zTag[i]==':' && is_date(&zTag[i+1]) ){
167
- char *zDate = mprintf("%s", &zTag[i+1]);
168
- char *zTagBase = mprintf("%.*s", i, zTag);
169
- int nDate = strlen(zDate);
170
- int useUtc = 0;
171
- if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
172
- nDate -= 3;
173
- zDate[nDate] = 0;
174
- useUtc = 1;
175
- }
176
- zUuid = db_text(0,
177
- "SELECT blob.uuid"
178
- " FROM tag, tagxref, event, blob"
179
- " WHERE tag.tagname='sym-%q' "
180
- " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
181
- " AND event.objid=tagxref.rid "
182
- " AND blob.rid=event.objid "
183
- " AND event.mtime<=julianday(%Q %s)"
184
- " AND event.type GLOB '%q'"
185
- " ORDER BY event.mtime DESC /*sort*/ ",
186
- zTagBase, zDate, (useUtc ? "" : ",'utc'"), zType
187
- );
188
- break;
189
- }
190
- }
191
- if( zUuid==0 && fossil_strcmp(zTag, "tip")==0 ){
192
- zUuid = db_text(0,
193
- "SELECT blob.uuid"
194
- " FROM event, blob"
195
- " WHERE event.type='ci'"
196
- " AND blob.rid=event.objid"
197
- " ORDER BY event.mtime DESC"
198
- );
199
- }
200
- if( zUuid==0 && g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){
201
- if( fossil_strcmp(zTag, "current")==0 ){
202
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
203
- }else if( fossil_strcmp(zTag, "prev")==0
204
- || fossil_strcmp(zTag, "previous")==0 ){
205
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid="
206
- "(SELECT pid FROM plink WHERE cid=%d AND isprim)",
207
- vid);
208
- }else if( fossil_strcmp(zTag, "next")==0 ){
209
- zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid="
210
- "(SELECT cid FROM plink WHERE pid=%d"
211
- " ORDER BY isprim DESC, mtime DESC)",
212
- vid);
213
- }
214
- }
215
- }
216
- return zUuid;
217
-}
218
-
219
-/*
220
-** Convert a date/time string into a UUID.
221
-**
222
-** Input forms accepted:
223
-**
224
-** date:DATE
225
-** local:DATE
226
-** utc:DATE
227
-**
228
-** The DATE is interpreted as localtime unless the "utc:" prefix is used
229
-** or a "utc" string appears at the end of the DATE string.
230
-*/
231
-char *date_to_uuid(const char *zDate, const char *zType){
232
- int useUtc = 0;
233
- int n;
234
- char *zCopy = 0;
235
- char *zUuid;
236
-
237
- if( memcmp(zDate, "date:", 5)==0 ){
238
- zDate += 5;
239
- }else if( memcmp(zDate, "local:", 6)==0 ){
240
- zDate += 6;
241
- }else if( memcmp(zDate, "utc:", 4)==0 ){
242
- zDate += 4;
243
- useUtc = 1;
244
- }
245
- n = strlen(zDate);
246
- if( n<10 || !is_date(zDate) ) return 0;
247
- if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){
248
- zCopy = mprintf("%s", zDate);
249
- zCopy[n-3] = 0;
250
- zDate = zCopy;
251
- n -= 3;
252
- useUtc = 1;
253
- }
254
- if( zType==0 || zType[0]==0 ) zType = "*";
255
- zUuid = db_text(0,
256
- "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)"
257
- " FROM event"
258
- " WHERE mtime<=julianday(%Q %s) AND type GLOB '%q'"
259
- " ORDER BY mtime DESC LIMIT 1",
260
- zDate, useUtc ? "" : ",'utc'", zType
261
- );
262
- free(zCopy);
263
- return zUuid;
256
+ char *zName = blob_str(pName);
257
+ int rid = symbolic_name_to_rid(zName, zType);
258
+ if( rid<0 ){
259
+ fossil_error(iErrPriority, "ambiguous name: %s", zName);
260
+ return 2;
261
+ }else if( rid==0 ){
262
+ fossil_error(iErrPriority, "not found: %s", zName);
263
+ return 1;
264
+ }else{
265
+ blob_reset(pName);
266
+ db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
267
+ return 0;
268
+ }
264269
}
265270
266271
/*
267272
** COMMAND: test-name-to-id
268273
**
@@ -284,41 +289,38 @@
284289
blob_reset(&name);
285290
}
286291
}
287292
288293
/*
289
-** Convert a name to a rid. If the name is a small integer value then
290
-** just use atoi() to do the conversion. If the name contains alphabetic
291
-** characters or is not an existing rid, then use name_to_uuid then
292
-** convert the uuid to a rid.
294
+** Convert a name to a rid. If the name can be any of the various forms
295
+** accepted:
296
+**
297
+** * SHA1 hash or prefix thereof
298
+** * symbolic name
299
+** * date
300
+** * label:date
301
+** * prev, previous
302
+** * next
303
+** * tip
293304
**
294305
** This routine is used by command-line routines to resolve command-line inputs
295306
** into a rid.
296307
*/
297308
int name_to_typed_rid(const char *zName, const char *zType){
298
- int i;
299309
int rid;
300
- Blob name;
301310
302311
if( zName==0 || zName[0]==0 ) return 0;
303
- blob_init(&name, zName, -1);
304
- if( name_to_uuid(&name, -1, zType) ){
305
- blob_reset(&name);
306
- for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){}
307
- if( zName[i]==0 ){
308
- rid = atoi(zName);
309
- if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
310
- return rid;
311
- }
312
- }
313
- fossil_error(1, "no such artifact: %s", zName);
312
+ rid = symbolic_name_to_rid(zName, zType);
313
+ if( rid<0 ){
314
+ fossil_error(1, "ambiguous name: %s", zName);
315
+ return 0;
316
+ }else if( rid==0 ){
317
+ fossil_error(1, "not found: %s", zName);
314318
return 0;
315319
}else{
316
- rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name);
317
- blob_reset(&name);
320
+ return rid;
318321
}
319
- return rid;
320322
}
321323
int name_to_rid(const char *zName){
322324
return name_to_typed_rid(zName, "*");
323325
}
324326
@@ -362,32 +364,104 @@
362364
** rid. If the CGI parameter is missing or is not a valid artifact tag,
363365
** return 0. If the CGI parameter is ambiguous, redirect to a page that
364366
** shows all possibilities and do not return.
365367
*/
366368
int name_to_rid_www(const char *zParamName){
367
- int i, rc;
368369
int rid;
369370
const char *zName = P(zParamName);
370
- Blob name;
371
-
372
- if( zName==0 || zName[0]==0 ) return 0;
373
- blob_init(&name, zName, -1);
374
- rc = name_to_uuid(&name, -1, "*");
375
- if( rc==1 ){
376
- blob_reset(&name);
377
- for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){}
378
- if( zName[i]==0 ){
379
- rid = atoi(zName);
380
- if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
381
- return rid;
382
- }
383
- }
384
- return 0;
385
- }else if( rc==2 ){
386
- cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath);
387
- return 0;
388
- }else{
389
- rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name);
390
- blob_reset(&name);
391
- }
392
- return rid;
371
+#ifdef FOSSIL_ENABLE_JSON
372
+ if(!zName && fossil_has_json()){
373
+ zName = json_find_option_cstr(zParamName,NULL,NULL);
374
+ }
375
+#endif
376
+ if( zName==0 || zName[0]==0 ) return 0;
377
+ rid = symbolic_name_to_rid(zName, "*");
378
+ if( rid<0 ){
379
+ cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath);
380
+ rid = 0;
381
+ }
382
+ return rid;
383
+}
384
+
385
+/*
386
+** COMMAND: whatis*
387
+** Usage: %fossil whatis NAME
388
+**
389
+** Resolve the symbol NAME into its canonical 40-character SHA1-hash
390
+** artifact name and provide a description of what role that artifact
391
+** plays.
392
+*/
393
+void whatis_cmd(void){
394
+ int rid;
395
+ const char *zName;
396
+ int fExtra;
397
+ db_find_and_open_repository(0,0);
398
+ fExtra = find_option("verbose","v",0)!=0;
399
+ if( g.argc!=3 ) usage("whatis NAME");
400
+ zName = g.argv[2];
401
+ rid = symbolic_name_to_rid(zName, 0);
402
+ if( rid<0 ){
403
+ fossil_print("Ambiguous artifact name prefix: %s\n", zName);
404
+ }else if( rid==0 ){
405
+ fossil_print("Unknown artifact: %s\n", zName);
406
+ }else{
407
+ Stmt q;
408
+ db_prepare(&q, "SELECT uuid, size, datetime(mtime, 'localtime'), ipaddr"
409
+ " FROM blob, rcvfrom"
410
+ " WHERE rid=%d"
411
+ " AND rcvfrom.rcvid=blob.rcvid",
412
+ rid);
413
+ if( db_step(&q)==SQLITE_ROW ){
414
+ if( fExtra ){
415
+ fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid);
416
+ fossil_print("size: %d bytes\n", db_column_int(&q,1));
417
+ fossil_print("received: %s from %s\n",
418
+ db_column_text(&q, 2),
419
+ db_column_text(&q, 3));
420
+ }else{
421
+ fossil_print("artifact: %s\n", db_column_text(&q,0));
422
+ fossil_print("size: %d bytes\n", db_column_int(&q,1));
423
+ }
424
+ }
425
+ db_finalize(&q);
426
+ db_prepare(&q,
427
+ "SELECT type, datetime(mtime,'localtime'),"
428
+ " coalesce(euser,user), coalesce(ecomment,comment)"
429
+ " FROM event WHERE objid=%d", rid);
430
+ if( db_step(&q)==SQLITE_ROW ){
431
+ const char *zType;
432
+ switch( db_column_text(&q,0)[0] ){
433
+ case 'c': zType = "Check-in"; break;
434
+ case 'w': zType = "Wiki-edit"; break;
435
+ case 'e': zType = "Event"; break;
436
+ case 't': zType = "Ticket-change"; break;
437
+ case 'g': zType = "Tag-change"; break;
438
+ default: zType = "Unknown"; break;
439
+ }
440
+ fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2),
441
+ db_column_text(&q, 1));
442
+ fossil_print("comment: ");
443
+ comment_print(db_column_text(&q,3), 10, 78);
444
+ }
445
+ db_finalize(&q);
446
+ db_prepare(&q,
447
+ "SELECT filename.name, blob.uuid, datetime(event.mtime,'localtime'),"
448
+ " coalesce(euser,user), coalesce(ecomment,comment)"
449
+ " FROM mlink, filename, blob, event"
450
+ " WHERE mlink.fid=%d"
451
+ " AND filename.fnid=mlink.fnid"
452
+ " AND event.objid=mlink.mid"
453
+ " AND blob.rid=mlink.mid"
454
+ " ORDER BY event.mtime DESC /*sort*/",
455
+ rid);
456
+ while( db_step(&q)==SQLITE_ROW ){
457
+ fossil_print("file: %s\n", db_column_text(&q,0));
458
+ fossil_print(" part of [%.10s] by %s on %s\n",
459
+ db_column_text(&q, 1),
460
+ db_column_text(&q, 3),
461
+ db_column_text(&q, 2));
462
+ fossil_print(" ");
463
+ comment_print(db_column_text(&q,4), 10, 78);
464
+ }
465
+ db_finalize(&q);
466
+ }
393467
}
394468
--- src/name.c
+++ src/name.c
@@ -22,10 +22,220 @@
22 ** not necessarily in canonical form.
23 */
24 #include "config.h"
25 #include "name.h"
26 #include <assert.h>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
28 /*
29 ** This routine takes a user-entered UUID which might be in mixed
30 ** case and might only be a prefix of the full UUID and converts it
31 ** into the full-length UUID in canonical form.
@@ -41,228 +251,23 @@
41 **
42 ** Return 0 on success. Return 1 if the name cannot be resolved.
43 ** Return 2 name is ambiguous.
44 */
45 int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){
46 int rc;
47 int sz;
48 sz = blob_size(pName);
49 if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){
50 char *zUuid;
51 const char *zName = blob_str(pName);
52 if( memcmp(zName, "tag:", 4)==0 ){
53 zName += 4;
54 zUuid = tag_to_uuid(zName, zType);
55 }else{
56 zUuid = tag_to_uuid(zName, zType);
57 if( zUuid==0 ){
58 zUuid = date_to_uuid(zName, zType);
59 }
60 }
61 if( zUuid ){
62 blob_reset(pName);
63 blob_append(pName, zUuid, -1);
64 free(zUuid);
65 return 0;
66 }
67 fossil_error(iErrPriority, "not a valid object name: %s", zName);
68 return 1;
69 }
70 blob_materialize(pName);
71 canonical16(blob_buffer(pName), sz);
72 if( sz==UUID_SIZE ){
73 rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName);
74 if( rc ){
75 fossil_error(iErrPriority, "no such artifact: %b", pName);
76 blob_reset(pName);
77 }
78 }else if( sz<UUID_SIZE && sz>=4 ){
79 Stmt q;
80 db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName);
81 if( db_step(&q)!=SQLITE_ROW ){
82 char *zUuid;
83 db_finalize(&q);
84 zUuid = tag_to_uuid(blob_str(pName), "*");
85 if( zUuid ){
86 blob_reset(pName);
87 blob_append(pName, zUuid, -1);
88 free(zUuid);
89 return 0;
90 }
91 fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName);
92 return 1;
93 }
94 blob_reset(pName);
95 blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0));
96 if( db_step(&q)==SQLITE_ROW ){
97 fossil_error(iErrPriority,
98 "multiple artifacts match"
99 );
100 blob_reset(pName);
101 db_finalize(&q);
102 return 2;
103 }
104 db_finalize(&q);
105 rc = 0;
106 }else{
107 rc = 0;
108 }
109 return rc;
110 }
111
112 /*
113 ** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD.
114 */
115 static int is_date(const char *z){
116 if( !fossil_isdigit(z[0]) ) return 0;
117 if( !fossil_isdigit(z[1]) ) return 0;
118 if( !fossil_isdigit(z[2]) ) return 0;
119 if( !fossil_isdigit(z[3]) ) return 0;
120 if( z[4]!='-') return 0;
121 if( !fossil_isdigit(z[5]) ) return 0;
122 if( !fossil_isdigit(z[6]) ) return 0;
123 if( z[7]!='-') return 0;
124 if( !fossil_isdigit(z[8]) ) return 0;
125 if( !fossil_isdigit(z[9]) ) return 0;
126 return 1;
127 }
128
129 /*
130 ** Convert a symbolic tag name into the UUID of a check-in that contains
131 ** that tag. If the tag appears on multiple check-ins, return the UUID
132 ** of the most recent check-in with the tag.
133 **
134 ** If the input string is of the form:
135 **
136 ** tag:date
137 **
138 ** Then return the UUID of the oldest check-in with that tag that is
139 ** not older than 'date'.
140 **
141 ** An input of "tip" returns the most recent check-in.
142 **
143 ** Memory to hold the returned string comes from malloc() and needs to
144 ** be freed by the caller.
145 */
146 char *tag_to_uuid(const char *zTag, const char *zType){
147 int vid;
148 char *zUuid;
149
150 if( zType==0 || zType[0]==0 ) zType = "*";
151 zUuid = db_text(0,
152 "SELECT blob.uuid"
153 " FROM tag, tagxref, event, blob"
154 " WHERE tag.tagname='sym-%q' "
155 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
156 " AND event.objid=tagxref.rid "
157 " AND blob.rid=event.objid "
158 " AND event.type GLOB '%q'"
159 " ORDER BY event.mtime DESC /*sort*/",
160 zTag, zType
161 );
162 if( zUuid==0 ){
163 int nTag = strlen(zTag);
164 int i;
165 for(i=0; i<nTag-10; i++){
166 if( zTag[i]==':' && is_date(&zTag[i+1]) ){
167 char *zDate = mprintf("%s", &zTag[i+1]);
168 char *zTagBase = mprintf("%.*s", i, zTag);
169 int nDate = strlen(zDate);
170 int useUtc = 0;
171 if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
172 nDate -= 3;
173 zDate[nDate] = 0;
174 useUtc = 1;
175 }
176 zUuid = db_text(0,
177 "SELECT blob.uuid"
178 " FROM tag, tagxref, event, blob"
179 " WHERE tag.tagname='sym-%q' "
180 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
181 " AND event.objid=tagxref.rid "
182 " AND blob.rid=event.objid "
183 " AND event.mtime<=julianday(%Q %s)"
184 " AND event.type GLOB '%q'"
185 " ORDER BY event.mtime DESC /*sort*/ ",
186 zTagBase, zDate, (useUtc ? "" : ",'utc'"), zType
187 );
188 break;
189 }
190 }
191 if( zUuid==0 && fossil_strcmp(zTag, "tip")==0 ){
192 zUuid = db_text(0,
193 "SELECT blob.uuid"
194 " FROM event, blob"
195 " WHERE event.type='ci'"
196 " AND blob.rid=event.objid"
197 " ORDER BY event.mtime DESC"
198 );
199 }
200 if( zUuid==0 && g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){
201 if( fossil_strcmp(zTag, "current")==0 ){
202 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid);
203 }else if( fossil_strcmp(zTag, "prev")==0
204 || fossil_strcmp(zTag, "previous")==0 ){
205 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid="
206 "(SELECT pid FROM plink WHERE cid=%d AND isprim)",
207 vid);
208 }else if( fossil_strcmp(zTag, "next")==0 ){
209 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid="
210 "(SELECT cid FROM plink WHERE pid=%d"
211 " ORDER BY isprim DESC, mtime DESC)",
212 vid);
213 }
214 }
215 }
216 return zUuid;
217 }
218
219 /*
220 ** Convert a date/time string into a UUID.
221 **
222 ** Input forms accepted:
223 **
224 ** date:DATE
225 ** local:DATE
226 ** utc:DATE
227 **
228 ** The DATE is interpreted as localtime unless the "utc:" prefix is used
229 ** or a "utc" string appears at the end of the DATE string.
230 */
231 char *date_to_uuid(const char *zDate, const char *zType){
232 int useUtc = 0;
233 int n;
234 char *zCopy = 0;
235 char *zUuid;
236
237 if( memcmp(zDate, "date:", 5)==0 ){
238 zDate += 5;
239 }else if( memcmp(zDate, "local:", 6)==0 ){
240 zDate += 6;
241 }else if( memcmp(zDate, "utc:", 4)==0 ){
242 zDate += 4;
243 useUtc = 1;
244 }
245 n = strlen(zDate);
246 if( n<10 || !is_date(zDate) ) return 0;
247 if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){
248 zCopy = mprintf("%s", zDate);
249 zCopy[n-3] = 0;
250 zDate = zCopy;
251 n -= 3;
252 useUtc = 1;
253 }
254 if( zType==0 || zType[0]==0 ) zType = "*";
255 zUuid = db_text(0,
256 "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)"
257 " FROM event"
258 " WHERE mtime<=julianday(%Q %s) AND type GLOB '%q'"
259 " ORDER BY mtime DESC LIMIT 1",
260 zDate, useUtc ? "" : ",'utc'", zType
261 );
262 free(zCopy);
263 return zUuid;
264 }
265
266 /*
267 ** COMMAND: test-name-to-id
268 **
@@ -284,41 +289,38 @@
284 blob_reset(&name);
285 }
286 }
287
288 /*
289 ** Convert a name to a rid. If the name is a small integer value then
290 ** just use atoi() to do the conversion. If the name contains alphabetic
291 ** characters or is not an existing rid, then use name_to_uuid then
292 ** convert the uuid to a rid.
 
 
 
 
 
 
293 **
294 ** This routine is used by command-line routines to resolve command-line inputs
295 ** into a rid.
296 */
297 int name_to_typed_rid(const char *zName, const char *zType){
298 int i;
299 int rid;
300 Blob name;
301
302 if( zName==0 || zName[0]==0 ) return 0;
303 blob_init(&name, zName, -1);
304 if( name_to_uuid(&name, -1, zType) ){
305 blob_reset(&name);
306 for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){}
307 if( zName[i]==0 ){
308 rid = atoi(zName);
309 if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
310 return rid;
311 }
312 }
313 fossil_error(1, "no such artifact: %s", zName);
314 return 0;
315 }else{
316 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name);
317 blob_reset(&name);
318 }
319 return rid;
320 }
321 int name_to_rid(const char *zName){
322 return name_to_typed_rid(zName, "*");
323 }
324
@@ -362,32 +364,104 @@
362 ** rid. If the CGI parameter is missing or is not a valid artifact tag,
363 ** return 0. If the CGI parameter is ambiguous, redirect to a page that
364 ** shows all possibilities and do not return.
365 */
366 int name_to_rid_www(const char *zParamName){
367 int i, rc;
368 int rid;
369 const char *zName = P(zParamName);
370 Blob name;
371
372 if( zName==0 || zName[0]==0 ) return 0;
373 blob_init(&name, zName, -1);
374 rc = name_to_uuid(&name, -1, "*");
375 if( rc==1 ){
376 blob_reset(&name);
377 for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){}
378 if( zName[i]==0 ){
379 rid = atoi(zName);
380 if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){
381 return rid;
382 }
383 }
384 return 0;
385 }else if( rc==2 ){
386 cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath);
387 return 0;
388 }else{
389 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name);
390 blob_reset(&name);
391 }
392 return rid;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393 }
394
--- src/name.c
+++ src/name.c
@@ -22,10 +22,220 @@
22 ** not necessarily in canonical form.
23 */
24 #include "config.h"
25 #include "name.h"
26 #include <assert.h>
27
28 /*
29 ** Return TRUE if the string begins with something that looks roughly
30 ** like an ISO date/time string. The SQLite date/time functions will
31 ** have the final say-so about whether or not the date/time string is
32 ** well-formed.
33 */
34 static int is_date(const char *z){
35 if( !fossil_isdigit(z[0]) ) return 0;
36 if( !fossil_isdigit(z[1]) ) return 0;
37 if( !fossil_isdigit(z[2]) ) return 0;
38 if( !fossil_isdigit(z[3]) ) return 0;
39 if( z[4]!='-') return 0;
40 if( !fossil_isdigit(z[5]) ) return 0;
41 if( !fossil_isdigit(z[6]) ) return 0;
42 if( z[7]!='-') return 0;
43 if( !fossil_isdigit(z[8]) ) return 0;
44 if( !fossil_isdigit(z[9]) ) return 0;
45 return 1;
46 }
47
48 /*
49 ** Convert a symbolic name into a RID. Acceptable forms:
50 **
51 ** * SHA1 hash
52 ** * SHA1 hash prefix of at least 4 characters
53 ** * Symbolic Name
54 ** * "tag:" + symbolic name
55 ** * Date or date-time
56 ** * "date:" + Date or date-time
57 ** * symbolic-name ":" date-time
58 ** * "tip"
59 **
60 ** The following additional forms are available in local checkouts:
61 **
62 ** * "current"
63 ** * "prev" or "previous"
64 ** * "next"
65 **
66 ** Return the RID of the matching artifact. Or return 0 if the name does not
67 ** match any known object. Or return -1 if the name is ambiguious.
68 **
69 ** The zType parameter specifies the type of artifact: ci, t, w, e, g.
70 ** If zType is NULL or "" or "*" then any type of artifact will serve.
71 ** zType is "ci" in most use cases since we are usually searching for
72 ** a check-in.
73 */
74 static int symbolic_name_to_rid(const char *zTag, const char *zType){
75 int vid;
76 int rid = 0;
77 int nTag;
78 int i;
79
80 if( zType==0 || zType[0]==0 ) zType = "*";
81 if( zTag==0 || zTag[0]==0 ) return 0;
82
83 /* special keyword: "tip" */
84 if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){
85 rid = db_int(0,
86 "SELECT objid"
87 " FROM event"
88 " WHERE type='ci'"
89 " ORDER BY event.mtime DESC"
90 );
91 if( rid ) return rid;
92 }
93
94 /* special keywords: "prev", "previous", "current", and "next" */
95 if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){
96 if( fossil_strcmp(zTag, "current")==0 ){
97 rid = vid;
98 }else if( fossil_strcmp(zTag, "prev")==0
99 || fossil_strcmp(zTag, "previous")==0 ){
100 rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid);
101 }else if( fossil_strcmp(zTag, "next")==0 ){
102 rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
103 " ORDER BY isprim DESC, mtime DESC", vid);
104 }
105 if( rid ) return rid;
106 }
107
108 /* Date and times */
109 if( memcmp(zTag, "date:", 5)==0 ){
110 rid = db_int(0,
111 "SELECT objid FROM event"
112 " WHERE mtime<=julianday(%Q) AND type GLOB '%q'"
113 " ORDER BY mtime DESC LIMIT 1",
114 &zTag[5], zType);
115 return rid;
116 }
117 if( is_date(zTag) ){
118 rid = db_int(0,
119 "SELECT objid FROM event"
120 " WHERE mtime<=julianday(%Q) AND type GLOB '%q'"
121 " ORDER BY mtime DESC LIMIT 1",
122 zTag, zType);
123 if( rid) return rid;
124 }
125
126 /* Deprecated date & time formats: "local:" + date-time and
127 ** "utc:" + date-time */
128 if( memcmp(zTag, "local:", 6)==0 ){
129 rid = db_int(0,
130 "SELECT objid FROM event"
131 " WHERE mtime<=julianday(%Q) AND type GLOB '%q'"
132 " ORDER BY mtime DESC LIMIT 1",
133 &zTag[6], zType);
134 return rid;
135 }
136 if( memcmp(zTag, "utc:", 4)==0 ){
137 rid = db_int(0,
138 "SELECT objid FROM event"
139 " WHERE mtime<=julianday('%qz') AND type GLOB '%q'"
140 " ORDER BY mtime DESC LIMIT 1",
141 &zTag[4], zType);
142 return rid;
143 }
144
145 /* "tag:" + symbolic-name */
146 if( memcmp(zTag, "tag:", 4)==0 ){
147 rid = db_int(0,
148 "SELECT event.objid"
149 " FROM tag, tagxref, event"
150 " WHERE tag.tagname='sym-%q' "
151 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
152 " AND event.objid=tagxref.rid "
153 " AND event.type GLOB '%q'"
154 " ORDER BY event.mtime DESC /*sort*/",
155 &zTag[4], zType
156 );
157 return rid;
158 }
159
160 /* symbolic-name ":" date-time */
161 nTag = strlen(zTag);
162 for(i=0; i<nTag-10 && zTag[i]!=':'; i++){}
163 if( zTag[i]==':' && is_date(&zTag[i+1]) ){
164 char *zDate = mprintf("%s", &zTag[i+1]);
165 char *zTagBase = mprintf("%.*s", i, zTag);
166 int nDate = strlen(zDate);
167 if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
168 zDate[nDate-3] = 'z';
169 zDate[nDate-2] = 0;
170 }
171 rid = db_int(0,
172 "SELECT event.objid"
173 " FROM tag, tagxref, event"
174 " WHERE tag.tagname='sym-%q' "
175 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
176 " AND event.objid=tagxref.rid "
177 " AND event.mtime<=julianday(%Q)"
178 " AND event.type GLOB '%q'"
179 " ORDER BY event.mtime DESC /*sort*/ ",
180 zTagBase, zDate, zType
181 );
182 return rid;
183 }
184
185 /* SHA1 hash or prefix */
186 if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){
187 Stmt q;
188 char zUuid[UUID_SIZE+1];
189 memcpy(zUuid, zTag, nTag+1);
190 canonical16(zUuid, nTag);
191 rid = 0;
192 if( zType[0]=='*' ){
193 db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid);
194 }else{
195 db_prepare(&q,
196 "SELECT blob.rid"
197 " FROM blob, event"
198 " WHERE blob.uuid GLOB '%s*'"
199 " AND event.objid=blob.rid"
200 " AND event.type GLOB '%q'",
201 zUuid, zType
202 );
203 }
204 if( db_step(&q)==SQLITE_ROW ){
205 rid = db_column_int(&q, 0);
206 if( db_step(&q)==SQLITE_ROW ) rid = -1;
207 }
208 db_finalize(&q);
209 if( rid ) return rid;
210 }
211
212 /* Symbolic name */
213 rid = db_int(0,
214 "SELECT event.objid"
215 " FROM tag, tagxref, event"
216 " WHERE tag.tagname='sym-%q' "
217 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
218 " AND event.objid=tagxref.rid "
219 " AND event.type GLOB '%q'"
220 " ORDER BY event.mtime DESC /*sort*/ ",
221 zTag, zType
222 );
223 if( rid>0 ) return rid;
224
225 /* Undocumented: numeric tags get translated directly into the RID */
226 for(i=0; fossil_isdigit(zTag[i]); i++){}
227 if( zTag[i]==0 ){
228 rid = db_int(0,
229 "SELECT event.objid"
230 " FROM event"
231 " WHERE event.objid=%s"
232 " AND event.type GLOB '%q'", zTag, zType);
233 }
234 return rid;
235 }
236
237
238 /*
239 ** This routine takes a user-entered UUID which might be in mixed
240 ** case and might only be a prefix of the full UUID and converts it
241 ** into the full-length UUID in canonical form.
@@ -41,228 +251,23 @@
251 **
252 ** Return 0 on success. Return 1 if the name cannot be resolved.
253 ** Return 2 name is ambiguous.
254 */
255 int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){
256 char *zName = blob_str(pName);
257 int rid = symbolic_name_to_rid(zName, zType);
258 if( rid<0 ){
259 fossil_error(iErrPriority, "ambiguous name: %s", zName);
260 return 2;
261 }else if( rid==0 ){
262 fossil_error(iErrPriority, "not found: %s", zName);
263 return 1;
264 }else{
265 blob_reset(pName);
266 db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
267 return 0;
268 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269 }
270
271 /*
272 ** COMMAND: test-name-to-id
273 **
@@ -284,41 +289,38 @@
289 blob_reset(&name);
290 }
291 }
292
293 /*
294 ** Convert a name to a rid. If the name can be any of the various forms
295 ** accepted:
296 **
297 ** * SHA1 hash or prefix thereof
298 ** * symbolic name
299 ** * date
300 ** * label:date
301 ** * prev, previous
302 ** * next
303 ** * tip
304 **
305 ** This routine is used by command-line routines to resolve command-line inputs
306 ** into a rid.
307 */
308 int name_to_typed_rid(const char *zName, const char *zType){
 
309 int rid;
 
310
311 if( zName==0 || zName[0]==0 ) return 0;
312 rid = symbolic_name_to_rid(zName, zType);
313 if( rid<0 ){
314 fossil_error(1, "ambiguous name: %s", zName);
315 return 0;
316 }else if( rid==0 ){
317 fossil_error(1, "not found: %s", zName);
 
 
 
 
 
318 return 0;
319 }else{
320 return rid;
 
321 }
 
322 }
323 int name_to_rid(const char *zName){
324 return name_to_typed_rid(zName, "*");
325 }
326
@@ -362,32 +364,104 @@
364 ** rid. If the CGI parameter is missing or is not a valid artifact tag,
365 ** return 0. If the CGI parameter is ambiguous, redirect to a page that
366 ** shows all possibilities and do not return.
367 */
368 int name_to_rid_www(const char *zParamName){
 
369 int rid;
370 const char *zName = P(zParamName);
371 #ifdef FOSSIL_ENABLE_JSON
372 if(!zName && fossil_has_json()){
373 zName = json_find_option_cstr(zParamName,NULL,NULL);
374 }
375 #endif
376 if( zName==0 || zName[0]==0 ) return 0;
377 rid = symbolic_name_to_rid(zName, "*");
378 if( rid<0 ){
379 cgi_redirectf("%s/ambiguous/%T?src=%t", g.zTop, zName, g.zPath);
380 rid = 0;
381 }
382 return rid;
383 }
384
385 /*
386 ** COMMAND: whatis*
387 ** Usage: %fossil whatis NAME
388 **
389 ** Resolve the symbol NAME into its canonical 40-character SHA1-hash
390 ** artifact name and provide a description of what role that artifact
391 ** plays.
392 */
393 void whatis_cmd(void){
394 int rid;
395 const char *zName;
396 int fExtra;
397 db_find_and_open_repository(0,0);
398 fExtra = find_option("verbose","v",0)!=0;
399 if( g.argc!=3 ) usage("whatis NAME");
400 zName = g.argv[2];
401 rid = symbolic_name_to_rid(zName, 0);
402 if( rid<0 ){
403 fossil_print("Ambiguous artifact name prefix: %s\n", zName);
404 }else if( rid==0 ){
405 fossil_print("Unknown artifact: %s\n", zName);
406 }else{
407 Stmt q;
408 db_prepare(&q, "SELECT uuid, size, datetime(mtime, 'localtime'), ipaddr"
409 " FROM blob, rcvfrom"
410 " WHERE rid=%d"
411 " AND rcvfrom.rcvid=blob.rcvid",
412 rid);
413 if( db_step(&q)==SQLITE_ROW ){
414 if( fExtra ){
415 fossil_print("artifact: %s (%d)\n", db_column_text(&q,0), rid);
416 fossil_print("size: %d bytes\n", db_column_int(&q,1));
417 fossil_print("received: %s from %s\n",
418 db_column_text(&q, 2),
419 db_column_text(&q, 3));
420 }else{
421 fossil_print("artifact: %s\n", db_column_text(&q,0));
422 fossil_print("size: %d bytes\n", db_column_int(&q,1));
423 }
424 }
425 db_finalize(&q);
426 db_prepare(&q,
427 "SELECT type, datetime(mtime,'localtime'),"
428 " coalesce(euser,user), coalesce(ecomment,comment)"
429 " FROM event WHERE objid=%d", rid);
430 if( db_step(&q)==SQLITE_ROW ){
431 const char *zType;
432 switch( db_column_text(&q,0)[0] ){
433 case 'c': zType = "Check-in"; break;
434 case 'w': zType = "Wiki-edit"; break;
435 case 'e': zType = "Event"; break;
436 case 't': zType = "Ticket-change"; break;
437 case 'g': zType = "Tag-change"; break;
438 default: zType = "Unknown"; break;
439 }
440 fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2),
441 db_column_text(&q, 1));
442 fossil_print("comment: ");
443 comment_print(db_column_text(&q,3), 10, 78);
444 }
445 db_finalize(&q);
446 db_prepare(&q,
447 "SELECT filename.name, blob.uuid, datetime(event.mtime,'localtime'),"
448 " coalesce(euser,user), coalesce(ecomment,comment)"
449 " FROM mlink, filename, blob, event"
450 " WHERE mlink.fid=%d"
451 " AND filename.fnid=mlink.fnid"
452 " AND event.objid=mlink.mid"
453 " AND blob.rid=mlink.mid"
454 " ORDER BY event.mtime DESC /*sort*/",
455 rid);
456 while( db_step(&q)==SQLITE_ROW ){
457 fossil_print("file: %s\n", db_column_text(&q,0));
458 fossil_print(" part of [%.10s] by %s on %s\n",
459 db_column_text(&q, 1),
460 db_column_text(&q, 3),
461 db_column_text(&q, 2));
462 fossil_print(" ");
463 comment_print(db_column_text(&q,4), 10, 78);
464 }
465 db_finalize(&q);
466 }
467 }
468
+1 -1
--- src/printf.c
+++ src/printf.c
@@ -545,11 +545,11 @@
545545
buf[0] = '%';
546546
bufpt = buf;
547547
length = 1;
548548
break;
549549
case etCHARX:
550
- c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
550
+ c = buf[0] = va_arg(ap,int);
551551
if( precision>=0 ){
552552
for(idx=1; idx<precision; idx++) buf[idx] = c;
553553
length = precision;
554554
}else{
555555
length =1;
556556
--- src/printf.c
+++ src/printf.c
@@ -545,11 +545,11 @@
545 buf[0] = '%';
546 bufpt = buf;
547 length = 1;
548 break;
549 case etCHARX:
550 c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
551 if( precision>=0 ){
552 for(idx=1; idx<precision; idx++) buf[idx] = c;
553 length = precision;
554 }else{
555 length =1;
556
--- src/printf.c
+++ src/printf.c
@@ -545,11 +545,11 @@
545 buf[0] = '%';
546 bufpt = buf;
547 length = 1;
548 break;
549 case etCHARX:
550 c = buf[0] = va_arg(ap,int);
551 if( precision>=0 ){
552 for(idx=1; idx<precision; idx++) buf[idx] = c;
553 length = precision;
554 }else{
555 length =1;
556
+9 -5
--- src/rebuild.c
+++ src/rebuild.c
@@ -370,11 +370,15 @@
370370
" WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))"
371371
);
372372
db_multi_exec(
373373
"DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')"
374374
);
375
- totalSize = db_int(0, "SELECT count(*) FROM blob");
375
+
376
+ /* The following should be count(*) instead of max(rid). max(rid) is
377
+ ** an adequate approximation, however, and is much faster for large
378
+ ** repositories. */
379
+ totalSize = db_int(0, "SELECT max(rid) FROM blob");
376380
incrSize = totalSize/100;
377381
totalSize += incrSize*2;
378382
db_prepare(&s,
379383
"SELECT rid, size FROM blob /*scan*/"
380384
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
@@ -483,11 +487,11 @@
483487
484488
db_end_transaction(0);
485489
}
486490
487491
/*
488
-** COMMAND: rebuild
492
+** COMMAND: rebuild
489493
**
490494
** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS?
491495
**
492496
** Reconstruct the named repository database from the core
493497
** records. Run this command after updating the fossil
@@ -716,11 +720,11 @@
716720
db_finalize(&q);
717721
}
718722
}
719723
720724
/*
721
-** COMMAND: scrub
725
+** COMMAND: scrub*
722726
** %fossil scrub ?OPTIONS? ?REPOSITORY?
723727
**
724728
** The command removes sensitive information (such as passwords) from a
725729
** repository so that the respository can be sent to an untrusted reader.
726730
**
@@ -850,11 +854,11 @@
850854
}
851855
fossil_mbcs_free(zMbcsPath);
852856
}
853857
854858
/*
855
-** COMMAND: reconstruct
859
+** COMMAND: reconstruct*
856860
**
857861
** Usage: %fossil reconstruct FILENAME DIRECTORY
858862
**
859863
** This command studies the artifacts (files) in DIRECTORY and
860864
** reconstructs the fossil record from them. It places the new
@@ -911,11 +915,11 @@
911915
zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
912916
fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword);
913917
}
914918
915919
/*
916
-** COMMAND: deconstruct
920
+** COMMAND: deconstruct*
917921
**
918922
** Usage %fossil deconstruct ?OPTIONS? DESTINATION
919923
**
920924
**
921925
** This command exports all artifacts of a given repository and
922926
--- src/rebuild.c
+++ src/rebuild.c
@@ -370,11 +370,15 @@
370 " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))"
371 );
372 db_multi_exec(
373 "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')"
374 );
375 totalSize = db_int(0, "SELECT count(*) FROM blob");
 
 
 
 
376 incrSize = totalSize/100;
377 totalSize += incrSize*2;
378 db_prepare(&s,
379 "SELECT rid, size FROM blob /*scan*/"
380 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
@@ -483,11 +487,11 @@
483
484 db_end_transaction(0);
485 }
486
487 /*
488 ** COMMAND: rebuild
489 **
490 ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS?
491 **
492 ** Reconstruct the named repository database from the core
493 ** records. Run this command after updating the fossil
@@ -716,11 +720,11 @@
716 db_finalize(&q);
717 }
718 }
719
720 /*
721 ** COMMAND: scrub
722 ** %fossil scrub ?OPTIONS? ?REPOSITORY?
723 **
724 ** The command removes sensitive information (such as passwords) from a
725 ** repository so that the respository can be sent to an untrusted reader.
726 **
@@ -850,11 +854,11 @@
850 }
851 fossil_mbcs_free(zMbcsPath);
852 }
853
854 /*
855 ** COMMAND: reconstruct
856 **
857 ** Usage: %fossil reconstruct FILENAME DIRECTORY
858 **
859 ** This command studies the artifacts (files) in DIRECTORY and
860 ** reconstructs the fossil record from them. It places the new
@@ -911,11 +915,11 @@
911 zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
912 fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword);
913 }
914
915 /*
916 ** COMMAND: deconstruct
917 **
918 ** Usage %fossil deconstruct ?OPTIONS? DESTINATION
919 **
920 **
921 ** This command exports all artifacts of a given repository and
922
--- src/rebuild.c
+++ src/rebuild.c
@@ -370,11 +370,15 @@
370 " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))"
371 );
372 db_multi_exec(
373 "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')"
374 );
375
376 /* The following should be count(*) instead of max(rid). max(rid) is
377 ** an adequate approximation, however, and is much faster for large
378 ** repositories. */
379 totalSize = db_int(0, "SELECT max(rid) FROM blob");
380 incrSize = totalSize/100;
381 totalSize += incrSize*2;
382 db_prepare(&s,
383 "SELECT rid, size FROM blob /*scan*/"
384 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
@@ -483,11 +487,11 @@
487
488 db_end_transaction(0);
489 }
490
491 /*
492 ** COMMAND: rebuild
493 **
494 ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS?
495 **
496 ** Reconstruct the named repository database from the core
497 ** records. Run this command after updating the fossil
@@ -716,11 +720,11 @@
720 db_finalize(&q);
721 }
722 }
723
724 /*
725 ** COMMAND: scrub*
726 ** %fossil scrub ?OPTIONS? ?REPOSITORY?
727 **
728 ** The command removes sensitive information (such as passwords) from a
729 ** repository so that the respository can be sent to an untrusted reader.
730 **
@@ -850,11 +854,11 @@
854 }
855 fossil_mbcs_free(zMbcsPath);
856 }
857
858 /*
859 ** COMMAND: reconstruct*
860 **
861 ** Usage: %fossil reconstruct FILENAME DIRECTORY
862 **
863 ** This command studies the artifacts (files) in DIRECTORY and
864 ** reconstructs the fossil record from them. It places the new
@@ -911,11 +915,11 @@
915 zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
916 fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword);
917 }
918
919 /*
920 ** COMMAND: deconstruct*
921 **
922 ** Usage %fossil deconstruct ?OPTIONS? DESTINATION
923 **
924 **
925 ** This command exports all artifacts of a given repository and
926
+7 -19
--- src/report.c
+++ src/report.c
@@ -19,10 +19,13 @@
1919
*/
2020
#include "config.h"
2121
#include <time.h>
2222
#include "report.h"
2323
#include <assert.h>
24
+#ifdef FOSSIL_ENABLE_JSON
25
+# include "cson_amalgamation.h"
26
+#endif
2427
2528
/* Forward references to static routines */
2629
static void report_format_hints(void);
2730
2831
/*
@@ -630,17 +633,11 @@
630633
char **azName /* Names of the columns */
631634
){
632635
struct GenerateHTML *pState = (struct GenerateHTML*)pUser;
633636
int i;
634637
const char *zTid; /* Ticket UUID. (value of column named '#') */
635
- int rn; /* Report number */
636638
char *zBg = 0; /* Use this background color */
637
- char zPage[30]; /* Text version of the ticket number */
638
-
639
- /* Get the report number
640
- */
641
- rn = pState->rn;
642639
643640
/* Do initialization
644641
*/
645642
if( pState->nCount==0 ){
646643
/* Turn off the authorizer. It is no longer doing anything since the
@@ -719,11 +716,10 @@
719716
*/
720717
zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0;
721718
if( zBg==0 ) zBg = "white";
722719
@ <tr style="background-color:%h(zBg)">
723720
zTid = 0;
724
- zPage[0] = 0;
725721
for(i=0; i<nArg; i++){
726722
char *zData;
727723
if( i==pState->iBg ) continue;
728724
zData = azArg[i];
729725
if( zData==0 ) zData = "";
@@ -1108,41 +1104,32 @@
11081104
const char *zFilter,
11091105
tTktShowEncoding enc
11101106
){
11111107
Stmt q;
11121108
char *zSql;
1113
- const char *zTitle;
1114
- const char *zOwner;
1115
- const char *zClrKey;
11161109
char *zErr1 = 0;
11171110
char *zErr2 = 0;
11181111
int count = 0;
11191112
int rn;
11201113
11211114
if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){
1122
- zTitle = zFullTicketRptTitle;
11231115
zSql = "SELECT * FROM ticket";
1124
- zOwner = g.zLogin;
1125
- zClrKey = "";
11261116
}else{
11271117
rn = atoi(zRep);
11281118
if( rn ){
11291119
db_prepare(&q,
1130
- "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn);
1120
+ "SELECT sqlcode FROM reportfmt WHERE rn=%d", rn);
11311121
}else{
11321122
db_prepare(&q,
1133
- "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE title='%s'", zRep);
1123
+ "SELECT sqlcode FROM reportfmt WHERE title=%Q", zRep);
11341124
}
11351125
if( db_step(&q)!=SQLITE_ROW ){
11361126
db_finalize(&q);
11371127
rpt_list_reports();
11381128
fossil_fatal("unknown report format(%s)!",zRep);
11391129
}
1140
- zTitle = db_column_malloc(&q, 0);
1141
- zSql = db_column_malloc(&q, 1);
1142
- zOwner = db_column_malloc(&q, 2);
1143
- zClrKey = db_column_malloc(&q, 3);
1130
+ zSql = db_column_malloc(&q, 0);
11441131
db_finalize(&q);
11451132
}
11461133
if( zFilter ){
11471134
zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter);
11481135
}
@@ -1154,5 +1141,6 @@
11541141
report_unrestrict_sql();
11551142
if( zFilter ){
11561143
free(zSql);
11571144
}
11581145
}
1146
+
11591147
--- src/report.c
+++ src/report.c
@@ -19,10 +19,13 @@
19 */
20 #include "config.h"
21 #include <time.h>
22 #include "report.h"
23 #include <assert.h>
 
 
 
24
25 /* Forward references to static routines */
26 static void report_format_hints(void);
27
28 /*
@@ -630,17 +633,11 @@
630 char **azName /* Names of the columns */
631 ){
632 struct GenerateHTML *pState = (struct GenerateHTML*)pUser;
633 int i;
634 const char *zTid; /* Ticket UUID. (value of column named '#') */
635 int rn; /* Report number */
636 char *zBg = 0; /* Use this background color */
637 char zPage[30]; /* Text version of the ticket number */
638
639 /* Get the report number
640 */
641 rn = pState->rn;
642
643 /* Do initialization
644 */
645 if( pState->nCount==0 ){
646 /* Turn off the authorizer. It is no longer doing anything since the
@@ -719,11 +716,10 @@
719 */
720 zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0;
721 if( zBg==0 ) zBg = "white";
722 @ <tr style="background-color:%h(zBg)">
723 zTid = 0;
724 zPage[0] = 0;
725 for(i=0; i<nArg; i++){
726 char *zData;
727 if( i==pState->iBg ) continue;
728 zData = azArg[i];
729 if( zData==0 ) zData = "";
@@ -1108,41 +1104,32 @@
1108 const char *zFilter,
1109 tTktShowEncoding enc
1110 ){
1111 Stmt q;
1112 char *zSql;
1113 const char *zTitle;
1114 const char *zOwner;
1115 const char *zClrKey;
1116 char *zErr1 = 0;
1117 char *zErr2 = 0;
1118 int count = 0;
1119 int rn;
1120
1121 if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){
1122 zTitle = zFullTicketRptTitle;
1123 zSql = "SELECT * FROM ticket";
1124 zOwner = g.zLogin;
1125 zClrKey = "";
1126 }else{
1127 rn = atoi(zRep);
1128 if( rn ){
1129 db_prepare(&q,
1130 "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn);
1131 }else{
1132 db_prepare(&q,
1133 "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE title='%s'", zRep);
1134 }
1135 if( db_step(&q)!=SQLITE_ROW ){
1136 db_finalize(&q);
1137 rpt_list_reports();
1138 fossil_fatal("unknown report format(%s)!",zRep);
1139 }
1140 zTitle = db_column_malloc(&q, 0);
1141 zSql = db_column_malloc(&q, 1);
1142 zOwner = db_column_malloc(&q, 2);
1143 zClrKey = db_column_malloc(&q, 3);
1144 db_finalize(&q);
1145 }
1146 if( zFilter ){
1147 zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter);
1148 }
@@ -1154,5 +1141,6 @@
1154 report_unrestrict_sql();
1155 if( zFilter ){
1156 free(zSql);
1157 }
1158 }
 
1159
--- src/report.c
+++ src/report.c
@@ -19,10 +19,13 @@
19 */
20 #include "config.h"
21 #include <time.h>
22 #include "report.h"
23 #include <assert.h>
24 #ifdef FOSSIL_ENABLE_JSON
25 # include "cson_amalgamation.h"
26 #endif
27
28 /* Forward references to static routines */
29 static void report_format_hints(void);
30
31 /*
@@ -630,17 +633,11 @@
633 char **azName /* Names of the columns */
634 ){
635 struct GenerateHTML *pState = (struct GenerateHTML*)pUser;
636 int i;
637 const char *zTid; /* Ticket UUID. (value of column named '#') */
 
638 char *zBg = 0; /* Use this background color */
 
 
 
 
 
639
640 /* Do initialization
641 */
642 if( pState->nCount==0 ){
643 /* Turn off the authorizer. It is no longer doing anything since the
@@ -719,11 +716,10 @@
716 */
717 zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0;
718 if( zBg==0 ) zBg = "white";
719 @ <tr style="background-color:%h(zBg)">
720 zTid = 0;
 
721 for(i=0; i<nArg; i++){
722 char *zData;
723 if( i==pState->iBg ) continue;
724 zData = azArg[i];
725 if( zData==0 ) zData = "";
@@ -1108,41 +1104,32 @@
1104 const char *zFilter,
1105 tTktShowEncoding enc
1106 ){
1107 Stmt q;
1108 char *zSql;
 
 
 
1109 char *zErr1 = 0;
1110 char *zErr2 = 0;
1111 int count = 0;
1112 int rn;
1113
1114 if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){
 
1115 zSql = "SELECT * FROM ticket";
 
 
1116 }else{
1117 rn = atoi(zRep);
1118 if( rn ){
1119 db_prepare(&q,
1120 "SELECT sqlcode FROM reportfmt WHERE rn=%d", rn);
1121 }else{
1122 db_prepare(&q,
1123 "SELECT sqlcode FROM reportfmt WHERE title=%Q", zRep);
1124 }
1125 if( db_step(&q)!=SQLITE_ROW ){
1126 db_finalize(&q);
1127 rpt_list_reports();
1128 fossil_fatal("unknown report format(%s)!",zRep);
1129 }
1130 zSql = db_column_malloc(&q, 0);
 
 
 
1131 db_finalize(&q);
1132 }
1133 if( zFilter ){
1134 zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter);
1135 }
@@ -1154,5 +1141,6 @@
1141 report_unrestrict_sql();
1142 if( zFilter ){
1143 free(zSql);
1144 }
1145 }
1146
1147
+1 -1
--- src/schema.c
+++ src/schema.c
@@ -251,11 +251,11 @@
251251
@ CREATE TABLE leaf(rid INTEGER PRIMARY KEY);
252252
@
253253
@ -- Events used to generate a timeline
254254
@ --
255255
@ CREATE TABLE event(
256
-@ type TEXT, -- Type of event: 'ci', 'w', 'e', 't'
256
+@ type TEXT, -- Type of event: 'ci', 'w', 'e', 't', 'g'
257257
@ mtime DATETIME, -- Time of occurrence. Julian day.
258258
@ objid INTEGER PRIMARY KEY, -- Associated record ID
259259
@ tagid INTEGER, -- Associated ticket or wiki name tag
260260
@ uid INTEGER REFERENCES user, -- User who caused the event
261261
@ bgcolor TEXT, -- Color set by 'bgcolor' property
262262
--- src/schema.c
+++ src/schema.c
@@ -251,11 +251,11 @@
251 @ CREATE TABLE leaf(rid INTEGER PRIMARY KEY);
252 @
253 @ -- Events used to generate a timeline
254 @ --
255 @ CREATE TABLE event(
256 @ type TEXT, -- Type of event: 'ci', 'w', 'e', 't'
257 @ mtime DATETIME, -- Time of occurrence. Julian day.
258 @ objid INTEGER PRIMARY KEY, -- Associated record ID
259 @ tagid INTEGER, -- Associated ticket or wiki name tag
260 @ uid INTEGER REFERENCES user, -- User who caused the event
261 @ bgcolor TEXT, -- Color set by 'bgcolor' property
262
--- src/schema.c
+++ src/schema.c
@@ -251,11 +251,11 @@
251 @ CREATE TABLE leaf(rid INTEGER PRIMARY KEY);
252 @
253 @ -- Events used to generate a timeline
254 @ --
255 @ CREATE TABLE event(
256 @ type TEXT, -- Type of event: 'ci', 'w', 'e', 't', 'g'
257 @ mtime DATETIME, -- Time of occurrence. Julian day.
258 @ objid INTEGER PRIMARY KEY, -- Associated record ID
259 @ tagid INTEGER, -- Associated ticket or wiki name tag
260 @ uid INTEGER REFERENCES user, -- User who caused the event
261 @ bgcolor TEXT, -- Color set by 'bgcolor' property
262
+1 -1
--- src/search.c
+++ src/search.c
@@ -165,11 +165,11 @@
165165
}
166166
167167
/*
168168
** Testing the search function.
169169
**
170
-** COMMAND: search
170
+** COMMAND: search*
171171
** %fossil search pattern...
172172
**
173173
** Search for timeline entries matching the pattern.
174174
*/
175175
void search_cmd(void){
176176
--- src/search.c
+++ src/search.c
@@ -165,11 +165,11 @@
165 }
166
167 /*
168 ** Testing the search function.
169 **
170 ** COMMAND: search
171 ** %fossil search pattern...
172 **
173 ** Search for timeline entries matching the pattern.
174 */
175 void search_cmd(void){
176
--- src/search.c
+++ src/search.c
@@ -165,11 +165,11 @@
165 }
166
167 /*
168 ** Testing the search function.
169 **
170 ** COMMAND: search*
171 ** %fossil search pattern...
172 **
173 ** Search for timeline entries matching the pattern.
174 */
175 void search_cmd(void){
176
+19 -1
--- src/setup.c
+++ src/setup.c
@@ -250,11 +250,11 @@
250250
char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
251251
char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
252252
char *oat, *oau, *oav, *oab, *oax, *oaz;
253253
const char *zGroup;
254254
const char *zOldLogin;
255
- const char *inherit[128];
255
+ char *inherit[128];
256256
int doWrite;
257257
int uid;
258258
int higherUser = 0; /* True if user being edited is SETUP and the */
259259
/* user doing the editing is ADMIN. Disallow editing */
260260
@@ -864,10 +864,17 @@
864864
"remote_user_ok", "remote_user_ok", 0);
865865
@ <p>When enabled, if the REMOTE_USER environment variable is set to the
866866
@ login name of a valid user and no other login credentials are available,
867867
@ then the REMOTE_USER is accepted as an authenticated user.
868868
@ </p>
869
+ @
870
+ @ <hr />
871
+ entry_attribute("IP address turns used in login cookie", 3, "ip-prefix-terms", "ipt",
872
+ "2");
873
+ @ <p>The number of octets of of the IP address used in the login cookie. Set to
874
+ @ zero to omit the IP address from the login cookie. A value of 2 is recommended.
875
+ @ </p>
869876
@
870877
@ <hr />
871878
entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
872879
@ <p>The number of hours for which a login is valid. This must be a
873880
@ positive number. The default is 8760 hours which is approximately equal
@@ -879,10 +886,21 @@
879886
@ <p>Fossil tries to limit out-bound sync, clone, and pull packets
880887
@ to this many bytes, uncompressed. If the client requires more data
881888
@ than this, then the client will issue multiple HTTP requests.
882889
@ Values below 1 million are not recommended. 5 million is a
883890
@ reasonable number.</p>
891
+
892
+ @ <hr />
893
+ onoff_attribute("Enable hyperlinks for \"nobody\" based on User-Agent",
894
+ "auto-enable-hyperlinks", "autohyperlink", 1);
895
+ @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users
896
+ @ including user "nobody", as long as the User-Agent string in the HTTP header
897
+ @ indicates that the request is coming from an actual human being and not a
898
+ @ a robot or script. Note: Bots can specify whatever User-Agent string they
899
+ @ that want. So a bot that wants to impersonate a human can easily do so.
900
+ @ Hence, this technique does not necessarily exclude malicious bots.
901
+ @ </p>
884902
885903
@ <hr />
886904
onoff_attribute("Allow users to register themselves",
887905
"self-register", "selfregister", 0);
888906
@ <p>Allow users to register themselves through the HTTP UI.
889907
--- src/setup.c
+++ src/setup.c
@@ -250,11 +250,11 @@
250 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
251 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
252 char *oat, *oau, *oav, *oab, *oax, *oaz;
253 const char *zGroup;
254 const char *zOldLogin;
255 const char *inherit[128];
256 int doWrite;
257 int uid;
258 int higherUser = 0; /* True if user being edited is SETUP and the */
259 /* user doing the editing is ADMIN. Disallow editing */
260
@@ -864,10 +864,17 @@
864 "remote_user_ok", "remote_user_ok", 0);
865 @ <p>When enabled, if the REMOTE_USER environment variable is set to the
866 @ login name of a valid user and no other login credentials are available,
867 @ then the REMOTE_USER is accepted as an authenticated user.
868 @ </p>
 
 
 
 
 
 
 
869 @
870 @ <hr />
871 entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
872 @ <p>The number of hours for which a login is valid. This must be a
873 @ positive number. The default is 8760 hours which is approximately equal
@@ -879,10 +886,21 @@
879 @ <p>Fossil tries to limit out-bound sync, clone, and pull packets
880 @ to this many bytes, uncompressed. If the client requires more data
881 @ than this, then the client will issue multiple HTTP requests.
882 @ Values below 1 million are not recommended. 5 million is a
883 @ reasonable number.</p>
 
 
 
 
 
 
 
 
 
 
 
884
885 @ <hr />
886 onoff_attribute("Allow users to register themselves",
887 "self-register", "selfregister", 0);
888 @ <p>Allow users to register themselves through the HTTP UI.
889
--- src/setup.c
+++ src/setup.c
@@ -250,11 +250,11 @@
250 char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
251 char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
252 char *oat, *oau, *oav, *oab, *oax, *oaz;
253 const char *zGroup;
254 const char *zOldLogin;
255 char *inherit[128];
256 int doWrite;
257 int uid;
258 int higherUser = 0; /* True if user being edited is SETUP and the */
259 /* user doing the editing is ADMIN. Disallow editing */
260
@@ -864,10 +864,17 @@
864 "remote_user_ok", "remote_user_ok", 0);
865 @ <p>When enabled, if the REMOTE_USER environment variable is set to the
866 @ login name of a valid user and no other login credentials are available,
867 @ then the REMOTE_USER is accepted as an authenticated user.
868 @ </p>
869 @
870 @ <hr />
871 entry_attribute("IP address turns used in login cookie", 3, "ip-prefix-terms", "ipt",
872 "2");
873 @ <p>The number of octets of of the IP address used in the login cookie. Set to
874 @ zero to omit the IP address from the login cookie. A value of 2 is recommended.
875 @ </p>
876 @
877 @ <hr />
878 entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766");
879 @ <p>The number of hours for which a login is valid. This must be a
880 @ positive number. The default is 8760 hours which is approximately equal
@@ -879,10 +886,21 @@
886 @ <p>Fossil tries to limit out-bound sync, clone, and pull packets
887 @ to this many bytes, uncompressed. If the client requires more data
888 @ than this, then the client will issue multiple HTTP requests.
889 @ Values below 1 million are not recommended. 5 million is a
890 @ reasonable number.</p>
891
892 @ <hr />
893 onoff_attribute("Enable hyperlinks for \"nobody\" based on User-Agent",
894 "auto-enable-hyperlinks", "autohyperlink", 1);
895 @ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users
896 @ including user "nobody", as long as the User-Agent string in the HTTP header
897 @ indicates that the request is coming from an actual human being and not a
898 @ a robot or script. Note: Bots can specify whatever User-Agent string they
899 @ that want. So a bot that wants to impersonate a human can easily do so.
900 @ Hence, this technique does not necessarily exclude malicious bots.
901 @ </p>
902
903 @ <hr />
904 onoff_attribute("Allow users to register themselves",
905 "self-register", "selfregister", 0);
906 @ <p>Allow users to register themselves through the HTTP UI.
907
+1 -1
--- src/sha1.c
+++ src/sha1.c
@@ -437,11 +437,11 @@
437437
sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1,
438438
fossil_free);
439439
}
440440
441441
/*
442
-** COMMAND: sha1sum
442
+** COMMAND: sha1sum*
443443
** %fossil sha1sum FILE...
444444
**
445445
** Compute an SHA1 checksum of all files named on the command-line.
446446
** If an file is named "-" then take its content from standard input.
447447
*/
448448
--- src/sha1.c
+++ src/sha1.c
@@ -437,11 +437,11 @@
437 sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1,
438 fossil_free);
439 }
440
441 /*
442 ** COMMAND: sha1sum
443 ** %fossil sha1sum FILE...
444 **
445 ** Compute an SHA1 checksum of all files named on the command-line.
446 ** If an file is named "-" then take its content from standard input.
447 */
448
--- src/sha1.c
+++ src/sha1.c
@@ -437,11 +437,11 @@
437 sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1,
438 fossil_free);
439 }
440
441 /*
442 ** COMMAND: sha1sum*
443 ** %fossil sha1sum FILE...
444 **
445 ** Compute an SHA1 checksum of all files named on the command-line.
446 ** If an file is named "-" then take its content from standard input.
447 */
448
+278 -68
--- src/skins.c
+++ src/skins.c
@@ -86,29 +86,30 @@
8686
@ text-align: center;
8787
@ letter-spacing: 1px;
8888
@ background-color: #404040;
8989
@ color: white;
9090
@ }
91
-@
91
+@
9292
@ /* The submenu bar that *sometimes* appears below the main menu */
93
-@ div.submenu {
93
+@ div.submenu, div.sectionmenu {
9494
@ padding: 3px 10px 3px 0px;
9595
@ font-size: 0.9em;
9696
@ text-align: center;
9797
@ background-color: #606060;
9898
@ color: white;
9999
@ }
100
-@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
100
+@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
101
+@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
101102
@ padding: 3px 10px 3px 10px;
102103
@ color: white;
103104
@ text-decoration: none;
104105
@ }
105
-@ div.mainmenu a:hover, div.submenu a:hover {
106
+@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
106107
@ color: #404040;
107108
@ background-color: white;
108109
@ }
109
-@
110
+@
110111
@ /* All page content from the bottom of the menu or submenu down to
111112
@ ** the footer */
112113
@ div.content {
113114
@ padding: 0ex 0ex 0ex 0ex;
114115
@ }
@@ -152,10 +153,55 @@
152153
@ /* The label/value pairs on (for example) the vinfo page */
153154
@ table.label-value th {
154155
@ vertical-align: top;
155156
@ text-align: right;
156157
@ padding: 0.2ex 2ex;
158
+@ }
159
+@
160
+@ /* Side-by-side diff */
161
+@ table.sbsdiff {
162
+@ background-color: white;
163
+@ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
164
+@ font-size: 8pt;
165
+@ border-collapse:collapse;
166
+@ white-space: pre;
167
+@ width: 98%;
168
+@ border: 1px #000 dashed;
169
+@ }
170
+@
171
+@ table.sbsdiff th.diffhdr {
172
+@ border-bottom: dotted;
173
+@ border-width: 1px;
174
+@ }
175
+@
176
+@ table.sbsdiff tr td {
177
+@ white-space: pre;
178
+@ padding-left: 3px;
179
+@ padding-right: 3px;
180
+@ margin: 0px;
181
+@ }
182
+@
183
+@ table.sbsdiff tr td.lineno {
184
+@ text-align: right;
185
+@ }
186
+@
187
+@ table.sbsdiff tr td.meta {
188
+@ color: white;
189
+@ background-color: rgb(20, 20, 20);
190
+@ text-align: center;
191
+@ }
192
+@
193
+@ table.sbsdiff tr td.added {
194
+@ background-color: rgb(230, 230, 230);
195
+@ }
196
+@
197
+@ table.sbsdiff tr td.removed {
198
+@ background-color: rgb(200, 200, 200);
199
+@ }
200
+@
201
+@ table.sbsdiff tr td.changed {
202
+@ background-color: rgb(220, 220, 220);
157203
@ }');
158204
@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
159205
@ <head>
160206
@ <title>$<project_name>: $<title></title>
161207
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -163,49 +209,47 @@
163209
@ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
164210
@ media="screen">
165211
@ </head>
166212
@ <body>
167213
@ <div class="header">
168
-@ <div class="logo">
169
-@ <img src="$home/logo" alt="logo">
170
-@ </div>
171214
@ <div class="title"><small>$<project_name></small><br />$<title></div>
172215
@ <div class="status"><nobr><th1>
173216
@ if {[info exists login]} {
174217
@ puts "Logged in as $login"
175218
@ } else {
176219
@ puts "Not logged in"
177220
@ }
178221
@ </th1></nobr></div>
179222
@ </div>
180
-@ <div class="mainmenu"><th1>
181
-@ html "<a href=''$home$index_page''>Home</a> "
223
+@ <div class="mainmenu">
224
+@ <th1>
225
+@ html "<a href=''$home$index_page''>Home</a>\n"
182226
@ if {[anycap jor]} {
183
-@ html "<a href=''$home/timeline''>Timeline</a> "
227
+@ html "<a href=''$home/timeline''>Timeline</a>\n"
184228
@ }
185229
@ if {[hascap oh]} {
186
-@ html "<a href=''$home/dir?ci=tip''>Files</a> "
230
+@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
187231
@ }
188232
@ if {[hascap o]} {
189
-@ html "<a href=''$home/brlist''>Branches</a> "
190
-@ html "<a href=''$home/taglist''>Tags</a> "
233
+@ html "<a href=''$home/brlist''>Branches</a>\n"
234
+@ html "<a href=''$home/taglist''>Tags</a>\n"
191235
@ }
192236
@ if {[hascap r]} {
193
-@ html "<a href=''$home/reportlist''>Tickets</a> "
237
+@ html "<a href=''$home/reportlist''>Tickets</a>\n"
194238
@ }
195239
@ if {[hascap j]} {
196
-@ html "<a href=''$home/wiki''>Wiki</a> "
240
+@ html "<a href=''$home/wiki''>Wiki</a>\n"
197241
@ }
198242
@ if {[hascap s]} {
199
-@ html "<a href=''$home/setup''>Admin</a> "
243
+@ html "<a href=''$home/setup''>Admin</a>\n"
200244
@ } elseif {[hascap a]} {
201
-@ html "<a href=''$home/setup_ulist''>Users</a> "
245
+@ html "<a href=''$home/setup_ulist''>Users</a>\n"
202246
@ }
203247
@ if {[info exists login]} {
204
-@ html "<a href=''$home/login''>Logout</a> "
248
+@ html "<a href=''$home/login''>Logout</a>\n"
205249
@ } else {
206
-@ html "<a href=''$home/login''>Login</a> "
250
+@ html "<a href=''$home/login''>Login</a>\n"
207251
@ }
208252
@ </th1></div>
209253
@ ');
210254
@ REPLACE INTO config(name,mtime,value)
211255
@ VALUES('footer',now(),'<div class="footer">
@@ -279,23 +323,24 @@
279323
@ background-color: #a09048;
280324
@ color: black;
281325
@ }
282326
@
283327
@ /* The submenu bar that *sometimes* appears below the main menu */
284
-@ div.submenu {
328
+@ div.submenu, div.sectionmenu {
285329
@ padding: 3px 10px 3px 0px;
286330
@ font-size: 0.9em;
287331
@ text-align: center;
288332
@ background-color: #c0af58;
289333
@ color: white;
290334
@ }
291
-@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
335
+@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
336
+@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
292337
@ padding: 3px 10px 3px 10px;
293338
@ color: white;
294339
@ text-decoration: none;
295340
@ }
296
-@ div.mainmenu a:hover, div.submenu a:hover {
341
+@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
297342
@ color: #a09048;
298343
@ background-color: white;
299344
@ }
300345
@
301346
@ /* All page content from the bottom of the menu or submenu down to
@@ -356,11 +401,54 @@
356401
@ table.label-value th {
357402
@ vertical-align: top;
358403
@ text-align: right;
359404
@ padding: 0.2ex 2ex;
360405
@ }
361
-@ ');
406
+@
407
+@ /* Side-by-side diff */
408
+@ table.sbsdiff {
409
+@ background-color: #ffffc5;
410
+@ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
411
+@ font-size: 8pt;
412
+@ border-collapse:collapse;
413
+@ white-space: pre;
414
+@ width: 98%;
415
+@ border: 1px #000 dashed;
416
+@ }
417
+@
418
+@ table.sbsdiff th.diffhdr {
419
+@ border-bottom: dotted;
420
+@ border-width: 1px;
421
+@ }
422
+@
423
+@ table.sbsdiff tr td {
424
+@ white-space: pre;
425
+@ padding-left: 3px;
426
+@ padding-right: 3px;
427
+@ margin: 0px;
428
+@ }
429
+@
430
+@ table.sbsdiff tr td.lineno {
431
+@ text-align: right;
432
+@ }
433
+@
434
+@ table.sbsdiff tr td.meta {
435
+@ background-color: #a09048;
436
+@ text-align: center;
437
+@ }
438
+@
439
+@ table.sbsdiff tr td.added {
440
+@ background-color: rgb(210, 210, 100);
441
+@ }
442
+@
443
+@ table.sbsdiff tr td.removed {
444
+@ background-color: rgb(190, 200, 110);
445
+@ }
446
+@
447
+@ table.sbsdiff tr td.changed {
448
+@ background-color: rgb(200, 210, 120);
449
+@ }');
362450
@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
363451
@ <head>
364452
@ <title>$<project_name>: $<title></title>
365453
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
366454
@ href="$home/timeline.rss">
@@ -378,37 +466,38 @@
378466
@ } else {
379467
@ puts "Not logged in"
380468
@ }
381469
@ </th1></nobr></div>
382470
@ </div>
383
-@ <div class="mainmenu"><th1>
384
-@ html "<a href=''$home$index_page''>Home</a> "
471
+@ <div class="mainmenu">
472
+@ <th1>
473
+@ html "<a href=''$home$index_page''>Home</a>\n"
385474
@ if {[anycap jor]} {
386
-@ html "<a href=''$home/timeline''>Timeline</a> "
475
+@ html "<a href=''$home/timeline''>Timeline</a>\n"
387476
@ }
388477
@ if {[hascap oh]} {
389
-@ html "<a href=''$home/dir?ci=tip''>Files</a> "
478
+@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
390479
@ }
391480
@ if {[hascap o]} {
392
-@ html "<a href=''$home/brlist''>Branches</a> "
393
-@ html "<a href=''$home/taglist''>Tags</a> "
481
+@ html "<a href=''$home/brlist''>Branches</a>\n"
482
+@ html "<a href=''$home/taglist''>Tags</a>\n"
394483
@ }
395484
@ if {[hascap r]} {
396
-@ html "<a href=''$home/reportlist''>Tickets</a> "
485
+@ html "<a href=''$home/reportlist''>Tickets</a>\n"
397486
@ }
398487
@ if {[hascap j]} {
399
-@ html "<a href=''$home/wiki''>Wiki</a> "
488
+@ html "<a href=''$home/wiki''>Wiki</a>\n"
400489
@ }
401490
@ if {[hascap s]} {
402
-@ html "<a href=''$home/setup''>Admin</a> "
491
+@ html "<a href=''$home/setup''>Admin</a>\n"
403492
@ } elseif {[hascap a]} {
404
-@ html "<a href=''$home/setup_ulist''>Users</a> "
493
+@ html "<a href=''$home/setup_ulist''>Users</a>\n"
405494
@ }
406495
@ if {[info exists login]} {
407
-@ html "<a href=''$home/login''>Logout</a> "
496
+@ html "<a href=''$home/login''>Logout</a>\n"
408497
@ } else {
409
-@ html "<a href=''$home/login''>Login</a> "
498
+@ html "<a href=''$home/login''>Login</a>\n"
410499
@ }
411500
@ </th1></div>
412501
@ ');
413502
@ REPLACE INTO config(name,mtime,value)
414503
@ VALUES('footer',now(),'<div class="footer">
@@ -517,25 +606,26 @@
517606
@ #container {
518607
@ padding-left: 9em;
519608
@ }
520609
@
521610
@ /* The submenu bar that *sometimes* appears below the main menu */
522
-@ div.submenu {
611
+@ div.submenu, div.sectionmenu {
523612
@ padding: 3px 10px 3px 10px;
524613
@ font-size: 0.9em;
525614
@ text-align: center;
526615
@ border:1px solid #999;
527616
@ border-width:1px 0px;
528617
@ background-color: #eee;
529618
@ color: #333;
530619
@ }
531
-@ div.submenu a, div.submenu a:visited {
620
+@ div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link,
621
+@ div.sectionmenu>a.button:visited {
532622
@ padding: 3px 10px 3px 10px;
533623
@ color: #333;
534624
@ text-decoration: none;
535625
@ }
536
-@ div.submenu a:hover {
626
+@ div.submenu a:hover, div.sectionmenu>a.button:hover {
537627
@ color: #eee;
538628
@ background-color: #333;
539629
@ }
540630
@
541631
@ /* All page content from the bottom of the menu or submenu down to
@@ -590,10 +680,56 @@
590680
@ /* The label/value pairs on (for example) the ci page */
591681
@ table.label-value th {
592682
@ vertical-align: top;
593683
@ text-align: right;
594684
@ padding: 0.2ex 2ex;
685
+@ }
686
+@
687
+@ /* Side-by-side diff */
688
+@ table.sbsdiff {
689
+@ background-color: white;
690
+@ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
691
+@ font-size: 6pt;
692
+@ border-collapse:collapse;
693
+@ white-space: pre;
694
+@ width: 98%;
695
+@ border: 1px #000 dashed;
696
+@ }
697
+@
698
+@ table.sbsdiff th.diffhdr {
699
+@ border-bottom: dotted;
700
+@ border-width: 1px;
701
+@ }
702
+@
703
+@ table.sbsdiff tr td {
704
+@ white-space: pre;
705
+@ padding-left: 3px;
706
+@ padding-right: 3px;
707
+@ margin: 0px;
708
+@ }
709
+@
710
+@ table.sbsdiff tr td.lineno {
711
+@ text-align: right;
712
+@ }
713
+@
714
+@ table.sbsdiff tr td.meta {
715
+@ color: white;
716
+@ background-color: black;
717
+@ text-align: center;
718
+@ }
719
+@
720
+@ table.sbsdiff tr td.added {
721
+@ background-color: white;
722
+@ }
723
+@
724
+@ table.sbsdiff tr td.removed {
725
+@ background-color: white;
726
+@ text-decoration: line-through;
727
+@ }
728
+@
729
+@ table.sbsdiff tr td.changed {
730
+@ background-color: white;
595731
@ }');
596732
@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
597733
@ <head>
598734
@ <title>$<project_name>: $<title></title>
599735
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -614,37 +750,38 @@
614750
@ } else {
615751
@ puts "Not logged in"
616752
@ }
617753
@ </th1></nobr></div>
618754
@ </div>
619
-@ <div class="mainmenu"><th1>
620
-@ html "<li><a href=''$home$index_page''>Home</a></li>"
755
+@ <div class="mainmenu">
756
+@ <th1>
757
+@ html "<a href=''$home$index_page''>Home</a>\n"
621758
@ if {[anycap jor]} {
622
-@ html "<li><a href=''$home/timeline''>Timeline</a></li>"
759
+@ html "<a href=''$home/timeline''>Timeline</a>\n"
623760
@ }
624761
@ if {[hascap oh]} {
625
-@ html "<li><a href=''$home/dir?ci=tip''>Files</a></li>"
762
+@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
626763
@ }
627764
@ if {[hascap o]} {
628
-@ html "<li><a href=''$home/brlist''>Branches</a></li>"
629
-@ html "<li><a href=''$home/taglist''>Tags</a></li>"
765
+@ html "<a href=''$home/brlist''>Branches</a>\n"
766
+@ html "<a href=''$home/taglist''>Tags</a>\n"
630767
@ }
631768
@ if {[hascap r]} {
632
-@ html "<li><a href=''$home/reportlist''>Tickets</a></li>"
769
+@ html "<a href=''$home/reportlist''>Tickets</a>\n"
633770
@ }
634771
@ if {[hascap j]} {
635
-@ html "<li><a href=''$home/wiki''>Wiki</a></li>"
772
+@ html "<a href=''$home/wiki''>Wiki</a>\n"
636773
@ }
637774
@ if {[hascap s]} {
638
-@ html "<li><a href=''$home/setup''>Admin</a></li>"
775
+@ html "<a href=''$home/setup''>Admin</a>\n"
639776
@ } elseif {[hascap a]} {
640
-@ html "<li><a href=''$home/setup_ulist''>Users</a></li>"
777
+@ html "<a href=''$home/setup_ulist''>Users</a>\n"
641778
@ }
642779
@ if {[info exists login]} {
643
-@ html "<li><a href=''$home/login''>Logout</a></li>"
780
+@ html "<a href=''$home/login''>Logout</a>\n"
644781
@ } else {
645
-@ html "<li><a href=''$home/login''>Login</a></li>"
782
+@ html "<a href=''$home/login''>Login</a>\n"
646783
@ }
647784
@ </th1></ul></div>
648785
@ <div id="container">
649786
@ ');
650787
@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
@@ -725,12 +862,13 @@
725862
@ -webkit-border-top-left-radius: 5px;
726863
@ -border-top-right-radius: 5px;
727864
@ -border-top-left-radius: 5px;
728865
@ border-top-left-radius: 5px;
729866
@ border-top-right-radius: 5px;
730
-@ vertical-align: center;
731
-@ min-height: 2em;
867
+@ vertical-align: middle;
868
+@ padding-top: 8px;
869
+@ padding-bottom: 8px;
732870
@ background-color: #446979;
733871
@ background: -webkit-gradient(linear,left bottom,left top, color-stop(0.02, rgb(51,81,94)), color-stop(0.76, rgb(85,129,149)));
734872
@ background: -moz-linear-gradient(center bottom,rgb(51,81,94) 2%, rgb(85,129,149) 76%);
735873
@ -webkit-box-shadow: 0px 3px 4px #333333;
736874
@ -moz-box-shadow: 0px 3px 4px #333333;
@@ -753,11 +891,12 @@
753891
@ div.mainmenu a, div.mainmenu a:visited {
754892
@ padding: 3px 10px 3px 10px;
755893
@ color: white;
756894
@ text-decoration: none;
757895
@ }
758
-@ div.submenu a, div.submenu a:visited {
896
+@ div.submenu a, div.submenu a:visited, a.button,
897
+@ div.sectionmenu>a.button:link, div.sectinmenu>a.button:visited {
759898
@ padding: 2px 8px;
760899
@ color: #000;
761900
@ font-family: Arial;
762901
@ text-decoration: none;
763902
@ margin:auto;
@@ -775,11 +914,11 @@
775914
@ div.mainmenu a:hover {
776915
@ color: #000;
777916
@ background-color: white;
778917
@ }
779918
@
780
-@ div.submenu a:hover {
919
+@ div.submenu a:hover, div.sectionmenu>a.button:hover {
781920
@ background: -webkit-gradient(linear,left bottom, left top, color-stop(0, rgb(214,214,214)), color-stop(0.75, rgb(184,184,184)));
782921
@ background: -moz-linear-gradient(center bottom, rgb(214,214,214) 0%, rgb(184,184,184) 75%);
783922
@ background-color: #c0c0c0 ;
784923
@ }
785924
@
@@ -885,10 +1024,77 @@
8851024
@ padding: 3px 5px;
8861025
@ }
8871026
@
8881027
@ textarea {
8891028
@ font-size: 1em;
1029
+@ }
1030
+@
1031
+@ /* Side-by-side diff */
1032
+@ table.sbsdiff {
1033
+@ background-color: white;
1034
+@ font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
1035
+@ font-size: 6pt;
1036
+@ border-collapse:collapse;
1037
+@ width: 98%;
1038
+@ border: 1px #000 dashed;
1039
+@ margin-left: auto;
1040
+@ margin-right: auto;
1041
+@ }
1042
+@
1043
+@ table.sbsdiff th.diffhdr {
1044
+@ border-bottom: dotted;
1045
+@ border-width: 1px;
1046
+@ }
1047
+@
1048
+@ table.sbsdiff tr td {
1049
+@ padding-left: 3px;
1050
+@ padding-right: 3px;
1051
+@ margin: 0px;
1052
+@ vertical-align: top;
1053
+@ white-space: pre-wrap;
1054
+@ }
1055
+@
1056
+@ table.sbsdiff tr td.lineno {
1057
+@ text-align: right;
1058
+@ /* border-bottom: 1px solid rgb(220, 220, 220); */
1059
+@ }
1060
+@
1061
+@ table.sbsdiff tr td.srcline {
1062
+@ /* max-width: 400px; */
1063
+@ /* Note: May partially hide long lines without whitespaces */
1064
+@ /* overflow: hidden; */
1065
+@ /* border-bottom: 1px solid rgb(220, 220, 220); */
1066
+@ }
1067
+@
1068
+@ table.sbsdiff tr td.meta {
1069
+@ background-color: rgb(170, 160, 255);
1070
+@ padding-top: 0.25em;
1071
+@ padding-bottom: 0.25em;
1072
+@ text-align: center;
1073
+@ -moz-border-radius: 5px;
1074
+@ -moz-border-radius: 5px;
1075
+@ -webkit-border-radius: 5px;
1076
+@ -webkit-border-radius: 5px;
1077
+@ -border-radius: 5px;
1078
+@ -border-radius: 5px;
1079
+@ border-radius: 5px;
1080
+@ border-radius: 5px;
1081
+@ }
1082
+@
1083
+@ table.sbsdiff tr td.added {
1084
+@ background-color: rgb(180, 250, 180);
1085
+@ /* border-bottom: 1px solid rgb(160, 230, 160); */
1086
+@ }
1087
+@
1088
+@ table.sbsdiff tr td.removed {
1089
+@ background-color: rgb(250, 130, 130);
1090
+@ /* border-bottom: 1px solid rgb(230, 110, 110); */
1091
+@ }
1092
+@
1093
+@ table.sbsdiff tr td.changed {
1094
+@ background-color: rgb(210, 210, 200);
1095
+@ /* border-bottom: 1px solid rgb(190, 190, 180); */
8901096
@ }');
8911097
@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
8921098
@ <head>
8931099
@ <title>$<project_name>: $<title></title>
8941100
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -909,39 +1115,40 @@
9091115
@ } else {
9101116
@ puts "Not logged in"
9111117
@ }
9121118
@ </th1></nobr></div>
9131119
@ </div>
914
-@ <div class="mainmenu"><ul><th1>
915
-@ html "<a href=''$home$index_page''>Home</a>"
1120
+@ <div class="mainmenu">
1121
+@ <th1>
1122
+@ html "<a href=''$home$index_page''>Home</a>\n"
9161123
@ if {[anycap jor]} {
917
-@ html "<a href=''$home/timeline''>Timeline</a>"
1124
+@ html "<a href=''$home/timeline''>Timeline</a>\n"
9181125
@ }
9191126
@ if {[hascap oh]} {
920
-@ html "<a href=''$home/dir?ci=tip''>Files</a>"
1127
+@ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
9211128
@ }
9221129
@ if {[hascap o]} {
923
-@ html "<a href=''$home/brlist''>Branches</a>"
924
-@ html "<a href=''$home/taglist''>Tags</a>"
1130
+@ html "<a href=''$home/brlist''>Branches</a>\n"
1131
+@ html "<a href=''$home/taglist''>Tags</a>\n"
9251132
@ }
9261133
@ if {[hascap r]} {
927
-@ html "<a href=''$home/reportlist''>Tickets</a>"
1134
+@ html "<a href=''$home/reportlist''>Tickets</a>\n"
9281135
@ }
9291136
@ if {[hascap j]} {
930
-@ html "<a href=''$home/wiki''>Wiki</a>"
1137
+@ html "<a href=''$home/wiki''>Wiki</a>\n"
9311138
@ }
9321139
@ if {[hascap s]} {
933
-@ html "<a href=''$home/setup''>Admin</a>"
1140
+@ html "<a href=''$home/setup''>Admin</a>\n"
9341141
@ } elseif {[hascap a]} {
935
-@ html "<a href=''$home/setup_ulist''>Users</a>"
1142
+@ html "<a href=''$home/setup_ulist''>Users</a>\n"
9361143
@ }
9371144
@ if {[info exists login]} {
938
-@ html "<a href=''$home/login''>Logout</a>"
1145
+@ html "<a href=''$home/login''>Logout</a>\n"
9391146
@ } else {
940
-@ html "<a href=''$home/login''>Login</a>"
1147
+@ html "<a href=''$home/login''>Login</a>\n"
9411148
@ }
942
-@ </th1></ul></div>
1149
+@ </th1></div>
9431150
@ <div id="container">
9441151
@ ');
9451152
@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
9461153
@ <div class="footer">
9471154
@ Fossil version $manifest_version $manifest_date
@@ -1100,10 +1307,13 @@
11001307
db_multi_exec("%s", zCurrent);
11011308
}
11021309
}
11031310
11041311
style_header("Skins");
1312
+ if( zErr ){
1313
+ @ <p><font color="red">%h(zErr)</font></p>
1314
+ }
11051315
@ <p>A "skin" is a combination of
11061316
@ <a href="setup_editcss">CSS</a>,
11071317
@ <a href="setup_header">Header</a>,
11081318
@ <a href="setup_footer">Footer</a>, and
11091319
@ <a href="setup_logo">Logo</a> that determines the look and feel
11101320
--- src/skins.c
+++ src/skins.c
@@ -86,29 +86,30 @@
86 @ text-align: center;
87 @ letter-spacing: 1px;
88 @ background-color: #404040;
89 @ color: white;
90 @ }
91 @
92 @ /* The submenu bar that *sometimes* appears below the main menu */
93 @ div.submenu {
94 @ padding: 3px 10px 3px 0px;
95 @ font-size: 0.9em;
96 @ text-align: center;
97 @ background-color: #606060;
98 @ color: white;
99 @ }
100 @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
 
101 @ padding: 3px 10px 3px 10px;
102 @ color: white;
103 @ text-decoration: none;
104 @ }
105 @ div.mainmenu a:hover, div.submenu a:hover {
106 @ color: #404040;
107 @ background-color: white;
108 @ }
109 @
110 @ /* All page content from the bottom of the menu or submenu down to
111 @ ** the footer */
112 @ div.content {
113 @ padding: 0ex 0ex 0ex 0ex;
114 @ }
@@ -152,10 +153,55 @@
152 @ /* The label/value pairs on (for example) the vinfo page */
153 @ table.label-value th {
154 @ vertical-align: top;
155 @ text-align: right;
156 @ padding: 0.2ex 2ex;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157 @ }');
158 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
159 @ <head>
160 @ <title>$<project_name>: $<title></title>
161 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -163,49 +209,47 @@
163 @ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
164 @ media="screen">
165 @ </head>
166 @ <body>
167 @ <div class="header">
168 @ <div class="logo">
169 @ <img src="$home/logo" alt="logo">
170 @ </div>
171 @ <div class="title"><small>$<project_name></small><br />$<title></div>
172 @ <div class="status"><nobr><th1>
173 @ if {[info exists login]} {
174 @ puts "Logged in as $login"
175 @ } else {
176 @ puts "Not logged in"
177 @ }
178 @ </th1></nobr></div>
179 @ </div>
180 @ <div class="mainmenu"><th1>
181 @ html "<a href=''$home$index_page''>Home</a> "
 
182 @ if {[anycap jor]} {
183 @ html "<a href=''$home/timeline''>Timeline</a> "
184 @ }
185 @ if {[hascap oh]} {
186 @ html "<a href=''$home/dir?ci=tip''>Files</a> "
187 @ }
188 @ if {[hascap o]} {
189 @ html "<a href=''$home/brlist''>Branches</a> "
190 @ html "<a href=''$home/taglist''>Tags</a> "
191 @ }
192 @ if {[hascap r]} {
193 @ html "<a href=''$home/reportlist''>Tickets</a> "
194 @ }
195 @ if {[hascap j]} {
196 @ html "<a href=''$home/wiki''>Wiki</a> "
197 @ }
198 @ if {[hascap s]} {
199 @ html "<a href=''$home/setup''>Admin</a> "
200 @ } elseif {[hascap a]} {
201 @ html "<a href=''$home/setup_ulist''>Users</a> "
202 @ }
203 @ if {[info exists login]} {
204 @ html "<a href=''$home/login''>Logout</a> "
205 @ } else {
206 @ html "<a href=''$home/login''>Login</a> "
207 @ }
208 @ </th1></div>
209 @ ');
210 @ REPLACE INTO config(name,mtime,value)
211 @ VALUES('footer',now(),'<div class="footer">
@@ -279,23 +323,24 @@
279 @ background-color: #a09048;
280 @ color: black;
281 @ }
282 @
283 @ /* The submenu bar that *sometimes* appears below the main menu */
284 @ div.submenu {
285 @ padding: 3px 10px 3px 0px;
286 @ font-size: 0.9em;
287 @ text-align: center;
288 @ background-color: #c0af58;
289 @ color: white;
290 @ }
291 @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
 
292 @ padding: 3px 10px 3px 10px;
293 @ color: white;
294 @ text-decoration: none;
295 @ }
296 @ div.mainmenu a:hover, div.submenu a:hover {
297 @ color: #a09048;
298 @ background-color: white;
299 @ }
300 @
301 @ /* All page content from the bottom of the menu or submenu down to
@@ -356,11 +401,54 @@
356 @ table.label-value th {
357 @ vertical-align: top;
358 @ text-align: right;
359 @ padding: 0.2ex 2ex;
360 @ }
361 @ ');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
363 @ <head>
364 @ <title>$<project_name>: $<title></title>
365 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
366 @ href="$home/timeline.rss">
@@ -378,37 +466,38 @@
378 @ } else {
379 @ puts "Not logged in"
380 @ }
381 @ </th1></nobr></div>
382 @ </div>
383 @ <div class="mainmenu"><th1>
384 @ html "<a href=''$home$index_page''>Home</a> "
 
385 @ if {[anycap jor]} {
386 @ html "<a href=''$home/timeline''>Timeline</a> "
387 @ }
388 @ if {[hascap oh]} {
389 @ html "<a href=''$home/dir?ci=tip''>Files</a> "
390 @ }
391 @ if {[hascap o]} {
392 @ html "<a href=''$home/brlist''>Branches</a> "
393 @ html "<a href=''$home/taglist''>Tags</a> "
394 @ }
395 @ if {[hascap r]} {
396 @ html "<a href=''$home/reportlist''>Tickets</a> "
397 @ }
398 @ if {[hascap j]} {
399 @ html "<a href=''$home/wiki''>Wiki</a> "
400 @ }
401 @ if {[hascap s]} {
402 @ html "<a href=''$home/setup''>Admin</a> "
403 @ } elseif {[hascap a]} {
404 @ html "<a href=''$home/setup_ulist''>Users</a> "
405 @ }
406 @ if {[info exists login]} {
407 @ html "<a href=''$home/login''>Logout</a> "
408 @ } else {
409 @ html "<a href=''$home/login''>Login</a> "
410 @ }
411 @ </th1></div>
412 @ ');
413 @ REPLACE INTO config(name,mtime,value)
414 @ VALUES('footer',now(),'<div class="footer">
@@ -517,25 +606,26 @@
517 @ #container {
518 @ padding-left: 9em;
519 @ }
520 @
521 @ /* The submenu bar that *sometimes* appears below the main menu */
522 @ div.submenu {
523 @ padding: 3px 10px 3px 10px;
524 @ font-size: 0.9em;
525 @ text-align: center;
526 @ border:1px solid #999;
527 @ border-width:1px 0px;
528 @ background-color: #eee;
529 @ color: #333;
530 @ }
531 @ div.submenu a, div.submenu a:visited {
 
532 @ padding: 3px 10px 3px 10px;
533 @ color: #333;
534 @ text-decoration: none;
535 @ }
536 @ div.submenu a:hover {
537 @ color: #eee;
538 @ background-color: #333;
539 @ }
540 @
541 @ /* All page content from the bottom of the menu or submenu down to
@@ -590,10 +680,56 @@
590 @ /* The label/value pairs on (for example) the ci page */
591 @ table.label-value th {
592 @ vertical-align: top;
593 @ text-align: right;
594 @ padding: 0.2ex 2ex;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
595 @ }');
596 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
597 @ <head>
598 @ <title>$<project_name>: $<title></title>
599 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -614,37 +750,38 @@
614 @ } else {
615 @ puts "Not logged in"
616 @ }
617 @ </th1></nobr></div>
618 @ </div>
619 @ <div class="mainmenu"><th1>
620 @ html "<li><a href=''$home$index_page''>Home</a></li>"
 
621 @ if {[anycap jor]} {
622 @ html "<li><a href=''$home/timeline''>Timeline</a></li>"
623 @ }
624 @ if {[hascap oh]} {
625 @ html "<li><a href=''$home/dir?ci=tip''>Files</a></li>"
626 @ }
627 @ if {[hascap o]} {
628 @ html "<li><a href=''$home/brlist''>Branches</a></li>"
629 @ html "<li><a href=''$home/taglist''>Tags</a></li>"
630 @ }
631 @ if {[hascap r]} {
632 @ html "<li><a href=''$home/reportlist''>Tickets</a></li>"
633 @ }
634 @ if {[hascap j]} {
635 @ html "<li><a href=''$home/wiki''>Wiki</a></li>"
636 @ }
637 @ if {[hascap s]} {
638 @ html "<li><a href=''$home/setup''>Admin</a></li>"
639 @ } elseif {[hascap a]} {
640 @ html "<li><a href=''$home/setup_ulist''>Users</a></li>"
641 @ }
642 @ if {[info exists login]} {
643 @ html "<li><a href=''$home/login''>Logout</a></li>"
644 @ } else {
645 @ html "<li><a href=''$home/login''>Login</a></li>"
646 @ }
647 @ </th1></ul></div>
648 @ <div id="container">
649 @ ');
650 @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
@@ -725,12 +862,13 @@
725 @ -webkit-border-top-left-radius: 5px;
726 @ -border-top-right-radius: 5px;
727 @ -border-top-left-radius: 5px;
728 @ border-top-left-radius: 5px;
729 @ border-top-right-radius: 5px;
730 @ vertical-align: center;
731 @ min-height: 2em;
 
732 @ background-color: #446979;
733 @ background: -webkit-gradient(linear,left bottom,left top, color-stop(0.02, rgb(51,81,94)), color-stop(0.76, rgb(85,129,149)));
734 @ background: -moz-linear-gradient(center bottom,rgb(51,81,94) 2%, rgb(85,129,149) 76%);
735 @ -webkit-box-shadow: 0px 3px 4px #333333;
736 @ -moz-box-shadow: 0px 3px 4px #333333;
@@ -753,11 +891,12 @@
753 @ div.mainmenu a, div.mainmenu a:visited {
754 @ padding: 3px 10px 3px 10px;
755 @ color: white;
756 @ text-decoration: none;
757 @ }
758 @ div.submenu a, div.submenu a:visited {
 
759 @ padding: 2px 8px;
760 @ color: #000;
761 @ font-family: Arial;
762 @ text-decoration: none;
763 @ margin:auto;
@@ -775,11 +914,11 @@
775 @ div.mainmenu a:hover {
776 @ color: #000;
777 @ background-color: white;
778 @ }
779 @
780 @ div.submenu a:hover {
781 @ background: -webkit-gradient(linear,left bottom, left top, color-stop(0, rgb(214,214,214)), color-stop(0.75, rgb(184,184,184)));
782 @ background: -moz-linear-gradient(center bottom, rgb(214,214,214) 0%, rgb(184,184,184) 75%);
783 @ background-color: #c0c0c0 ;
784 @ }
785 @
@@ -885,10 +1024,77 @@
885 @ padding: 3px 5px;
886 @ }
887 @
888 @ textarea {
889 @ font-size: 1em;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
890 @ }');
891 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
892 @ <head>
893 @ <title>$<project_name>: $<title></title>
894 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -909,39 +1115,40 @@
909 @ } else {
910 @ puts "Not logged in"
911 @ }
912 @ </th1></nobr></div>
913 @ </div>
914 @ <div class="mainmenu"><ul><th1>
915 @ html "<a href=''$home$index_page''>Home</a>"
 
916 @ if {[anycap jor]} {
917 @ html "<a href=''$home/timeline''>Timeline</a>"
918 @ }
919 @ if {[hascap oh]} {
920 @ html "<a href=''$home/dir?ci=tip''>Files</a>"
921 @ }
922 @ if {[hascap o]} {
923 @ html "<a href=''$home/brlist''>Branches</a>"
924 @ html "<a href=''$home/taglist''>Tags</a>"
925 @ }
926 @ if {[hascap r]} {
927 @ html "<a href=''$home/reportlist''>Tickets</a>"
928 @ }
929 @ if {[hascap j]} {
930 @ html "<a href=''$home/wiki''>Wiki</a>"
931 @ }
932 @ if {[hascap s]} {
933 @ html "<a href=''$home/setup''>Admin</a>"
934 @ } elseif {[hascap a]} {
935 @ html "<a href=''$home/setup_ulist''>Users</a>"
936 @ }
937 @ if {[info exists login]} {
938 @ html "<a href=''$home/login''>Logout</a>"
939 @ } else {
940 @ html "<a href=''$home/login''>Login</a>"
941 @ }
942 @ </th1></ul></div>
943 @ <div id="container">
944 @ ');
945 @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
946 @ <div class="footer">
947 @ Fossil version $manifest_version $manifest_date
@@ -1100,10 +1307,13 @@
1100 db_multi_exec("%s", zCurrent);
1101 }
1102 }
1103
1104 style_header("Skins");
 
 
 
1105 @ <p>A "skin" is a combination of
1106 @ <a href="setup_editcss">CSS</a>,
1107 @ <a href="setup_header">Header</a>,
1108 @ <a href="setup_footer">Footer</a>, and
1109 @ <a href="setup_logo">Logo</a> that determines the look and feel
1110
--- src/skins.c
+++ src/skins.c
@@ -86,29 +86,30 @@
86 @ text-align: center;
87 @ letter-spacing: 1px;
88 @ background-color: #404040;
89 @ color: white;
90 @ }
91 @
92 @ /* The submenu bar that *sometimes* appears below the main menu */
93 @ div.submenu, div.sectionmenu {
94 @ padding: 3px 10px 3px 0px;
95 @ font-size: 0.9em;
96 @ text-align: center;
97 @ background-color: #606060;
98 @ color: white;
99 @ }
100 @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
101 @ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
102 @ padding: 3px 10px 3px 10px;
103 @ color: white;
104 @ text-decoration: none;
105 @ }
106 @ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
107 @ color: #404040;
108 @ background-color: white;
109 @ }
110 @
111 @ /* All page content from the bottom of the menu or submenu down to
112 @ ** the footer */
113 @ div.content {
114 @ padding: 0ex 0ex 0ex 0ex;
115 @ }
@@ -152,10 +153,55 @@
153 @ /* The label/value pairs on (for example) the vinfo page */
154 @ table.label-value th {
155 @ vertical-align: top;
156 @ text-align: right;
157 @ padding: 0.2ex 2ex;
158 @ }
159 @
160 @ /* Side-by-side diff */
161 @ table.sbsdiff {
162 @ background-color: white;
163 @ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
164 @ font-size: 8pt;
165 @ border-collapse:collapse;
166 @ white-space: pre;
167 @ width: 98%;
168 @ border: 1px #000 dashed;
169 @ }
170 @
171 @ table.sbsdiff th.diffhdr {
172 @ border-bottom: dotted;
173 @ border-width: 1px;
174 @ }
175 @
176 @ table.sbsdiff tr td {
177 @ white-space: pre;
178 @ padding-left: 3px;
179 @ padding-right: 3px;
180 @ margin: 0px;
181 @ }
182 @
183 @ table.sbsdiff tr td.lineno {
184 @ text-align: right;
185 @ }
186 @
187 @ table.sbsdiff tr td.meta {
188 @ color: white;
189 @ background-color: rgb(20, 20, 20);
190 @ text-align: center;
191 @ }
192 @
193 @ table.sbsdiff tr td.added {
194 @ background-color: rgb(230, 230, 230);
195 @ }
196 @
197 @ table.sbsdiff tr td.removed {
198 @ background-color: rgb(200, 200, 200);
199 @ }
200 @
201 @ table.sbsdiff tr td.changed {
202 @ background-color: rgb(220, 220, 220);
203 @ }');
204 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
205 @ <head>
206 @ <title>$<project_name>: $<title></title>
207 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -163,49 +209,47 @@
209 @ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css"
210 @ media="screen">
211 @ </head>
212 @ <body>
213 @ <div class="header">
 
 
 
214 @ <div class="title"><small>$<project_name></small><br />$<title></div>
215 @ <div class="status"><nobr><th1>
216 @ if {[info exists login]} {
217 @ puts "Logged in as $login"
218 @ } else {
219 @ puts "Not logged in"
220 @ }
221 @ </th1></nobr></div>
222 @ </div>
223 @ <div class="mainmenu">
224 @ <th1>
225 @ html "<a href=''$home$index_page''>Home</a>\n"
226 @ if {[anycap jor]} {
227 @ html "<a href=''$home/timeline''>Timeline</a>\n"
228 @ }
229 @ if {[hascap oh]} {
230 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
231 @ }
232 @ if {[hascap o]} {
233 @ html "<a href=''$home/brlist''>Branches</a>\n"
234 @ html "<a href=''$home/taglist''>Tags</a>\n"
235 @ }
236 @ if {[hascap r]} {
237 @ html "<a href=''$home/reportlist''>Tickets</a>\n"
238 @ }
239 @ if {[hascap j]} {
240 @ html "<a href=''$home/wiki''>Wiki</a>\n"
241 @ }
242 @ if {[hascap s]} {
243 @ html "<a href=''$home/setup''>Admin</a>\n"
244 @ } elseif {[hascap a]} {
245 @ html "<a href=''$home/setup_ulist''>Users</a>\n"
246 @ }
247 @ if {[info exists login]} {
248 @ html "<a href=''$home/login''>Logout</a>\n"
249 @ } else {
250 @ html "<a href=''$home/login''>Login</a>\n"
251 @ }
252 @ </th1></div>
253 @ ');
254 @ REPLACE INTO config(name,mtime,value)
255 @ VALUES('footer',now(),'<div class="footer">
@@ -279,23 +323,24 @@
323 @ background-color: #a09048;
324 @ color: black;
325 @ }
326 @
327 @ /* The submenu bar that *sometimes* appears below the main menu */
328 @ div.submenu, div.sectionmenu {
329 @ padding: 3px 10px 3px 0px;
330 @ font-size: 0.9em;
331 @ text-align: center;
332 @ background-color: #c0af58;
333 @ color: white;
334 @ }
335 @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
336 @ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
337 @ padding: 3px 10px 3px 10px;
338 @ color: white;
339 @ text-decoration: none;
340 @ }
341 @ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
342 @ color: #a09048;
343 @ background-color: white;
344 @ }
345 @
346 @ /* All page content from the bottom of the menu or submenu down to
@@ -356,11 +401,54 @@
401 @ table.label-value th {
402 @ vertical-align: top;
403 @ text-align: right;
404 @ padding: 0.2ex 2ex;
405 @ }
406 @
407 @ /* Side-by-side diff */
408 @ table.sbsdiff {
409 @ background-color: #ffffc5;
410 @ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
411 @ font-size: 8pt;
412 @ border-collapse:collapse;
413 @ white-space: pre;
414 @ width: 98%;
415 @ border: 1px #000 dashed;
416 @ }
417 @
418 @ table.sbsdiff th.diffhdr {
419 @ border-bottom: dotted;
420 @ border-width: 1px;
421 @ }
422 @
423 @ table.sbsdiff tr td {
424 @ white-space: pre;
425 @ padding-left: 3px;
426 @ padding-right: 3px;
427 @ margin: 0px;
428 @ }
429 @
430 @ table.sbsdiff tr td.lineno {
431 @ text-align: right;
432 @ }
433 @
434 @ table.sbsdiff tr td.meta {
435 @ background-color: #a09048;
436 @ text-align: center;
437 @ }
438 @
439 @ table.sbsdiff tr td.added {
440 @ background-color: rgb(210, 210, 100);
441 @ }
442 @
443 @ table.sbsdiff tr td.removed {
444 @ background-color: rgb(190, 200, 110);
445 @ }
446 @
447 @ table.sbsdiff tr td.changed {
448 @ background-color: rgb(200, 210, 120);
449 @ }');
450 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
451 @ <head>
452 @ <title>$<project_name>: $<title></title>
453 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
454 @ href="$home/timeline.rss">
@@ -378,37 +466,38 @@
466 @ } else {
467 @ puts "Not logged in"
468 @ }
469 @ </th1></nobr></div>
470 @ </div>
471 @ <div class="mainmenu">
472 @ <th1>
473 @ html "<a href=''$home$index_page''>Home</a>\n"
474 @ if {[anycap jor]} {
475 @ html "<a href=''$home/timeline''>Timeline</a>\n"
476 @ }
477 @ if {[hascap oh]} {
478 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
479 @ }
480 @ if {[hascap o]} {
481 @ html "<a href=''$home/brlist''>Branches</a>\n"
482 @ html "<a href=''$home/taglist''>Tags</a>\n"
483 @ }
484 @ if {[hascap r]} {
485 @ html "<a href=''$home/reportlist''>Tickets</a>\n"
486 @ }
487 @ if {[hascap j]} {
488 @ html "<a href=''$home/wiki''>Wiki</a>\n"
489 @ }
490 @ if {[hascap s]} {
491 @ html "<a href=''$home/setup''>Admin</a>\n"
492 @ } elseif {[hascap a]} {
493 @ html "<a href=''$home/setup_ulist''>Users</a>\n"
494 @ }
495 @ if {[info exists login]} {
496 @ html "<a href=''$home/login''>Logout</a>\n"
497 @ } else {
498 @ html "<a href=''$home/login''>Login</a>\n"
499 @ }
500 @ </th1></div>
501 @ ');
502 @ REPLACE INTO config(name,mtime,value)
503 @ VALUES('footer',now(),'<div class="footer">
@@ -517,25 +606,26 @@
606 @ #container {
607 @ padding-left: 9em;
608 @ }
609 @
610 @ /* The submenu bar that *sometimes* appears below the main menu */
611 @ div.submenu, div.sectionmenu {
612 @ padding: 3px 10px 3px 10px;
613 @ font-size: 0.9em;
614 @ text-align: center;
615 @ border:1px solid #999;
616 @ border-width:1px 0px;
617 @ background-color: #eee;
618 @ color: #333;
619 @ }
620 @ div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link,
621 @ div.sectionmenu>a.button:visited {
622 @ padding: 3px 10px 3px 10px;
623 @ color: #333;
624 @ text-decoration: none;
625 @ }
626 @ div.submenu a:hover, div.sectionmenu>a.button:hover {
627 @ color: #eee;
628 @ background-color: #333;
629 @ }
630 @
631 @ /* All page content from the bottom of the menu or submenu down to
@@ -590,10 +680,56 @@
680 @ /* The label/value pairs on (for example) the ci page */
681 @ table.label-value th {
682 @ vertical-align: top;
683 @ text-align: right;
684 @ padding: 0.2ex 2ex;
685 @ }
686 @
687 @ /* Side-by-side diff */
688 @ table.sbsdiff {
689 @ background-color: white;
690 @ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
691 @ font-size: 6pt;
692 @ border-collapse:collapse;
693 @ white-space: pre;
694 @ width: 98%;
695 @ border: 1px #000 dashed;
696 @ }
697 @
698 @ table.sbsdiff th.diffhdr {
699 @ border-bottom: dotted;
700 @ border-width: 1px;
701 @ }
702 @
703 @ table.sbsdiff tr td {
704 @ white-space: pre;
705 @ padding-left: 3px;
706 @ padding-right: 3px;
707 @ margin: 0px;
708 @ }
709 @
710 @ table.sbsdiff tr td.lineno {
711 @ text-align: right;
712 @ }
713 @
714 @ table.sbsdiff tr td.meta {
715 @ color: white;
716 @ background-color: black;
717 @ text-align: center;
718 @ }
719 @
720 @ table.sbsdiff tr td.added {
721 @ background-color: white;
722 @ }
723 @
724 @ table.sbsdiff tr td.removed {
725 @ background-color: white;
726 @ text-decoration: line-through;
727 @ }
728 @
729 @ table.sbsdiff tr td.changed {
730 @ background-color: white;
731 @ }');
732 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
733 @ <head>
734 @ <title>$<project_name>: $<title></title>
735 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -614,37 +750,38 @@
750 @ } else {
751 @ puts "Not logged in"
752 @ }
753 @ </th1></nobr></div>
754 @ </div>
755 @ <div class="mainmenu">
756 @ <th1>
757 @ html "<a href=''$home$index_page''>Home</a>\n"
758 @ if {[anycap jor]} {
759 @ html "<a href=''$home/timeline''>Timeline</a>\n"
760 @ }
761 @ if {[hascap oh]} {
762 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
763 @ }
764 @ if {[hascap o]} {
765 @ html "<a href=''$home/brlist''>Branches</a>\n"
766 @ html "<a href=''$home/taglist''>Tags</a>\n"
767 @ }
768 @ if {[hascap r]} {
769 @ html "<a href=''$home/reportlist''>Tickets</a>\n"
770 @ }
771 @ if {[hascap j]} {
772 @ html "<a href=''$home/wiki''>Wiki</a>\n"
773 @ }
774 @ if {[hascap s]} {
775 @ html "<a href=''$home/setup''>Admin</a>\n"
776 @ } elseif {[hascap a]} {
777 @ html "<a href=''$home/setup_ulist''>Users</a>\n"
778 @ }
779 @ if {[info exists login]} {
780 @ html "<a href=''$home/login''>Logout</a>\n"
781 @ } else {
782 @ html "<a href=''$home/login''>Login</a>\n"
783 @ }
784 @ </th1></ul></div>
785 @ <div id="container">
786 @ ');
787 @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
@@ -725,12 +862,13 @@
862 @ -webkit-border-top-left-radius: 5px;
863 @ -border-top-right-radius: 5px;
864 @ -border-top-left-radius: 5px;
865 @ border-top-left-radius: 5px;
866 @ border-top-right-radius: 5px;
867 @ vertical-align: middle;
868 @ padding-top: 8px;
869 @ padding-bottom: 8px;
870 @ background-color: #446979;
871 @ background: -webkit-gradient(linear,left bottom,left top, color-stop(0.02, rgb(51,81,94)), color-stop(0.76, rgb(85,129,149)));
872 @ background: -moz-linear-gradient(center bottom,rgb(51,81,94) 2%, rgb(85,129,149) 76%);
873 @ -webkit-box-shadow: 0px 3px 4px #333333;
874 @ -moz-box-shadow: 0px 3px 4px #333333;
@@ -753,11 +891,12 @@
891 @ div.mainmenu a, div.mainmenu a:visited {
892 @ padding: 3px 10px 3px 10px;
893 @ color: white;
894 @ text-decoration: none;
895 @ }
896 @ div.submenu a, div.submenu a:visited, a.button,
897 @ div.sectionmenu>a.button:link, div.sectinmenu>a.button:visited {
898 @ padding: 2px 8px;
899 @ color: #000;
900 @ font-family: Arial;
901 @ text-decoration: none;
902 @ margin:auto;
@@ -775,11 +914,11 @@
914 @ div.mainmenu a:hover {
915 @ color: #000;
916 @ background-color: white;
917 @ }
918 @
919 @ div.submenu a:hover, div.sectionmenu>a.button:hover {
920 @ background: -webkit-gradient(linear,left bottom, left top, color-stop(0, rgb(214,214,214)), color-stop(0.75, rgb(184,184,184)));
921 @ background: -moz-linear-gradient(center bottom, rgb(214,214,214) 0%, rgb(184,184,184) 75%);
922 @ background-color: #c0c0c0 ;
923 @ }
924 @
@@ -885,10 +1024,77 @@
1024 @ padding: 3px 5px;
1025 @ }
1026 @
1027 @ textarea {
1028 @ font-size: 1em;
1029 @ }
1030 @
1031 @ /* Side-by-side diff */
1032 @ table.sbsdiff {
1033 @ background-color: white;
1034 @ font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace;
1035 @ font-size: 6pt;
1036 @ border-collapse:collapse;
1037 @ width: 98%;
1038 @ border: 1px #000 dashed;
1039 @ margin-left: auto;
1040 @ margin-right: auto;
1041 @ }
1042 @
1043 @ table.sbsdiff th.diffhdr {
1044 @ border-bottom: dotted;
1045 @ border-width: 1px;
1046 @ }
1047 @
1048 @ table.sbsdiff tr td {
1049 @ padding-left: 3px;
1050 @ padding-right: 3px;
1051 @ margin: 0px;
1052 @ vertical-align: top;
1053 @ white-space: pre-wrap;
1054 @ }
1055 @
1056 @ table.sbsdiff tr td.lineno {
1057 @ text-align: right;
1058 @ /* border-bottom: 1px solid rgb(220, 220, 220); */
1059 @ }
1060 @
1061 @ table.sbsdiff tr td.srcline {
1062 @ /* max-width: 400px; */
1063 @ /* Note: May partially hide long lines without whitespaces */
1064 @ /* overflow: hidden; */
1065 @ /* border-bottom: 1px solid rgb(220, 220, 220); */
1066 @ }
1067 @
1068 @ table.sbsdiff tr td.meta {
1069 @ background-color: rgb(170, 160, 255);
1070 @ padding-top: 0.25em;
1071 @ padding-bottom: 0.25em;
1072 @ text-align: center;
1073 @ -moz-border-radius: 5px;
1074 @ -moz-border-radius: 5px;
1075 @ -webkit-border-radius: 5px;
1076 @ -webkit-border-radius: 5px;
1077 @ -border-radius: 5px;
1078 @ -border-radius: 5px;
1079 @ border-radius: 5px;
1080 @ border-radius: 5px;
1081 @ }
1082 @
1083 @ table.sbsdiff tr td.added {
1084 @ background-color: rgb(180, 250, 180);
1085 @ /* border-bottom: 1px solid rgb(160, 230, 160); */
1086 @ }
1087 @
1088 @ table.sbsdiff tr td.removed {
1089 @ background-color: rgb(250, 130, 130);
1090 @ /* border-bottom: 1px solid rgb(230, 110, 110); */
1091 @ }
1092 @
1093 @ table.sbsdiff tr td.changed {
1094 @ background-color: rgb(210, 210, 200);
1095 @ /* border-bottom: 1px solid rgb(190, 190, 180); */
1096 @ }');
1097 @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html>
1098 @ <head>
1099 @ <title>$<project_name>: $<title></title>
1100 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed"
@@ -909,39 +1115,40 @@
1115 @ } else {
1116 @ puts "Not logged in"
1117 @ }
1118 @ </th1></nobr></div>
1119 @ </div>
1120 @ <div class="mainmenu">
1121 @ <th1>
1122 @ html "<a href=''$home$index_page''>Home</a>\n"
1123 @ if {[anycap jor]} {
1124 @ html "<a href=''$home/timeline''>Timeline</a>\n"
1125 @ }
1126 @ if {[hascap oh]} {
1127 @ html "<a href=''$home/dir?ci=tip''>Files</a>\n"
1128 @ }
1129 @ if {[hascap o]} {
1130 @ html "<a href=''$home/brlist''>Branches</a>\n"
1131 @ html "<a href=''$home/taglist''>Tags</a>\n"
1132 @ }
1133 @ if {[hascap r]} {
1134 @ html "<a href=''$home/reportlist''>Tickets</a>\n"
1135 @ }
1136 @ if {[hascap j]} {
1137 @ html "<a href=''$home/wiki''>Wiki</a>\n"
1138 @ }
1139 @ if {[hascap s]} {
1140 @ html "<a href=''$home/setup''>Admin</a>\n"
1141 @ } elseif {[hascap a]} {
1142 @ html "<a href=''$home/setup_ulist''>Users</a>\n"
1143 @ }
1144 @ if {[info exists login]} {
1145 @ html "<a href=''$home/login''>Logout</a>\n"
1146 @ } else {
1147 @ html "<a href=''$home/login''>Login</a>\n"
1148 @ }
1149 @ </th1></div>
1150 @ <div id="container">
1151 @ ');
1152 @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div>
1153 @ <div class="footer">
1154 @ Fossil version $manifest_version $manifest_date
@@ -1100,10 +1307,13 @@
1307 db_multi_exec("%s", zCurrent);
1308 }
1309 }
1310
1311 style_header("Skins");
1312 if( zErr ){
1313 @ <p><font color="red">%h(zErr)</font></p>
1314 }
1315 @ <p>A "skin" is a combination of
1316 @ <a href="setup_editcss">CSS</a>,
1317 @ <a href="setup_header">Header</a>,
1318 @ <a href="setup_footer">Footer</a>, and
1319 @ <a href="setup_logo">Logo</a> that determines the look and feel
1320
+670 -233
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -314,17 +314,10 @@
314314
#endif
315315
#ifdef HAVE_INTTYPES_H
316316
#include <inttypes.h>
317317
#endif
318318
319
-/*
320
-** The number of samples of an index that SQLite takes in order to
321
-** construct a histogram of the table content when running ANALYZE
322
-** and with SQLITE_ENABLE_STAT2
323
-*/
324
-#define SQLITE_INDEX_SAMPLES 10
325
-
326319
/*
327320
** The following macros are used to cast pointers to integers and
328321
** integers to pointers. The way you do this varies from one compiler
329322
** to the next, so we have developed the following set of #if statements
330323
** to generate appropriate macros for a wide range of compilers.
@@ -656,11 +649,11 @@
656649
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
657650
** [sqlite_version()] and [sqlite_source_id()].
658651
*/
659652
#define SQLITE_VERSION "3.7.9"
660653
#define SQLITE_VERSION_NUMBER 3007009
661
-#define SQLITE_SOURCE_ID "2011-10-15 00:16:30 39408702a989f907261c298bf0947f3e68bd10fe"
654
+#define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf"
662655
663656
/*
664657
** CAPI3REF: Run-Time Library Version Numbers
665658
** KEYWORDS: sqlite3_version, sqlite3_sourceid
666659
**
@@ -1951,12 +1944,12 @@
19511944
** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
19521945
** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
19531946
** allocator is engaged to handle all of SQLites memory allocation needs.
19541947
** The first pointer (the memory pointer) must be aligned to an 8-byte
19551948
** boundary or subsequent behavior of SQLite will be undefined.
1956
-** The minimum allocation size is capped at 2^12. Reasonable values
1957
-** for the minimum allocation size are 2^5 through 2^8.</dd>
1949
+** The minimum allocation size is capped at 2**12. Reasonable values
1950
+** for the minimum allocation size are 2**5 through 2**8.</dd>
19581951
**
19591952
** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
19601953
** <dd> ^(This option takes a single argument which is a pointer to an
19611954
** instance of the [sqlite3_mutex_methods] structure. The argument specifies
19621955
** alternative low-level mutex routines to be used in place
@@ -8792,10 +8785,11 @@
87928785
SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
87938786
SQLITE_PRIVATE int sqlite3PagerNosync(Pager*);
87948787
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
87958788
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
87968789
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
8790
+SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *);
87978791
87988792
/* Functions used to truncate the database file. */
87998793
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
88008794
88018795
#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
@@ -10189,12 +10183,13 @@
1018910183
IndexSample *aSample; /* Samples of the left-most key */
1019010184
#endif
1019110185
};
1019210186
1019310187
/*
10194
-** Each sample stored in the sqlite_stat2 table is represented in memory
10195
-** using a structure of this type.
10188
+** Each sample stored in the sqlite_stat3 table is represented in memory
10189
+** using a structure of this type. See documentation at the top of the
10190
+** analyze.c source file for additional information.
1019610191
*/
1019710192
struct IndexSample {
1019810193
union {
1019910194
char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
1020010195
double r; /* Value if eType is SQLITE_FLOAT */
@@ -12293,13 +12288,10 @@
1229312288
"ENABLE_OVERSIZE_CELL_CHECK",
1229412289
#endif
1229512290
#ifdef SQLITE_ENABLE_RTREE
1229612291
"ENABLE_RTREE",
1229712292
#endif
12298
-#ifdef SQLITE_ENABLE_STAT2
12299
- "ENABLE_STAT2",
12300
-#endif
1230112293
#ifdef SQLITE_ENABLE_STAT3
1230212294
"ENABLE_STAT3",
1230312295
#endif
1230412296
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
1230512297
"ENABLE_UNLOCK_NOTIFY",
@@ -12996,10 +12988,11 @@
1299612988
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
1299712989
SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
1299812990
SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*);
1299912991
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
1300012992
SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem);
12993
+SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p);
1300112994
1300212995
#ifdef SQLITE_OMIT_MERGE_SORT
1300312996
# define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK
1300412997
# define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK
1300512998
# define sqlite3VdbeSorterClose(Y,Z)
@@ -20969,11 +20962,11 @@
2096920962
}else if( *z=='+' ){
2097020963
z+=incr;
2097120964
}
2097220965
/* copy digits to exponent */
2097320966
while( z<zEnd && sqlite3Isdigit(*z) ){
20974
- e = e*10 + (*z - '0');
20967
+ e = e<10000 ? (e*10 + (*z - '0')) : 10000;
2097520968
z+=incr;
2097620969
eValid = 1;
2097720970
}
2097820971
}
2097920972
@@ -21020,10 +21013,16 @@
2102021013
result /= 1.0e+308;
2102121014
}else{
2102221015
result = s * scale;
2102321016
result *= 1.0e+308;
2102421017
}
21018
+ }else if( e>=342 ){
21019
+ if( esign<0 ){
21020
+ result = 0.0*s;
21021
+ }else{
21022
+ result = 1e308*1e308*s; /* Infinity */
21023
+ }
2102521024
}else{
2102621025
/* 1.0e+22 is the largest power of 10 than can be
2102721026
** represented exactly. */
2102821027
while( e%22 ) { scale *= 1.0e+1; e -= 1; }
2102921028
while( e>0 ) { scale *= 1.0e+22; e -= 22; }
@@ -29477,17 +29476,17 @@
2947729476
** "<path to db>-journal"
2947829477
** "<path to db>-wal"
2947929478
** "<path to db>-journalNN"
2948029479
** "<path to db>-walNN"
2948129480
**
29482
- ** where NN is a 4 digit decimal number. The NN naming schemes are
29481
+ ** where NN is a decimal number. The NN naming schemes are
2948329482
** used by the test_multiplex.c module.
2948429483
*/
2948529484
nDb = sqlite3Strlen30(zPath) - 1;
2948629485
#ifdef SQLITE_ENABLE_8_3_NAMES
29487
- while( nDb>0 && zPath[nDb]!='-' && zPath[nDb]!='/' ) nDb--;
29488
- if( nDb==0 || zPath[nDb]=='/' ) return SQLITE_OK;
29486
+ while( nDb>0 && !sqlite3Isalnum(zPath[nDb]) ) nDb--;
29487
+ if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK;
2948929488
#else
2949029489
while( zPath[nDb]!='-' ){
2949129490
assert( nDb>0 );
2949229491
assert( zPath[nDb]!='\n' );
2949329492
nDb--;
@@ -44157,10 +44156,17 @@
4415744156
pPager->pWal = 0;
4415844157
}
4415944158
}
4416044159
return rc;
4416144160
}
44161
+
44162
+/*
44163
+** Unless this is an in-memory or temporary database, clear the pager cache.
44164
+*/
44165
+SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *pPager){
44166
+ if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager);
44167
+}
4416244168
4416344169
#ifdef SQLITE_HAS_CODEC
4416444170
/*
4416544171
** This function is called by the wal module when writing page content
4416644172
** into the log file.
@@ -57000,10 +57006,12 @@
5700057006
sqlite3_backup_step(&b, 0x7FFFFFFF);
5700157007
assert( b.rc!=SQLITE_OK );
5700257008
rc = sqlite3_backup_finish(&b);
5700357009
if( rc==SQLITE_OK ){
5700457010
pTo->pBt->pageSizeFixed = 0;
57011
+ }else{
57012
+ sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
5700557013
}
5700657014
5700757015
assert( sqlite3BtreeIsInTrans(pTo)==0 );
5700857016
sqlite3BtreeLeave(pFrom);
5700957017
sqlite3BtreeLeave(pTo);
@@ -60474,10 +60482,34 @@
6047460482
** in p->rc. This routine sets that result back to SQLITE_OK.
6047560483
*/
6047660484
SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){
6047760485
p->rc = SQLITE_OK;
6047860486
}
60487
+
60488
+/*
60489
+** Copy the error code and error message belonging to the VDBE passed
60490
+** as the first argument to its database handle (so that they will be
60491
+** returned by calls to sqlite3_errcode() and sqlite3_errmsg()).
60492
+**
60493
+** This function does not clear the VDBE error code or message, just
60494
+** copies them to the database handle.
60495
+*/
60496
+SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){
60497
+ sqlite3 *db = p->db;
60498
+ int rc = p->rc;
60499
+ if( p->zErrMsg ){
60500
+ u8 mallocFailed = db->mallocFailed;
60501
+ sqlite3BeginBenignMalloc();
60502
+ sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
60503
+ sqlite3EndBenignMalloc();
60504
+ db->mallocFailed = mallocFailed;
60505
+ db->errCode = rc;
60506
+ }else{
60507
+ sqlite3Error(db, rc, 0);
60508
+ }
60509
+ return rc;
60510
+}
6047960511
6048060512
/*
6048160513
** Clean up a VDBE after execution but do not delete the VDBE just yet.
6048260514
** Write any error messages into *pzErrMsg. Return the result code.
6048360515
**
@@ -60502,22 +60534,13 @@
6050260534
** and error message from the VDBE into the main database structure. But
6050360535
** if the VDBE has just been set to run but has not actually executed any
6050460536
** instructions yet, leave the main database error information unchanged.
6050560537
*/
6050660538
if( p->pc>=0 ){
60507
- if( p->zErrMsg ){
60508
- sqlite3BeginBenignMalloc();
60509
- sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT);
60510
- sqlite3EndBenignMalloc();
60511
- db->errCode = p->rc;
60512
- sqlite3DbFree(db, p->zErrMsg);
60513
- p->zErrMsg = 0;
60514
- }else if( p->rc ){
60515
- sqlite3Error(db, p->rc, 0);
60516
- }else{
60517
- sqlite3Error(db, SQLITE_OK, 0);
60518
- }
60539
+ sqlite3VdbeTransferError(p);
60540
+ sqlite3DbFree(db, p->zErrMsg);
60541
+ p->zErrMsg = 0;
6051960542
if( p->runOnlyOnce ) p->expired = 1;
6052060543
}else if( p->rc && p->expired ){
6052160544
/* The expired flag was set on the VDBE before the first call
6052260545
** to sqlite3_step(). For consistency (since sqlite3_step() was
6052360546
** called), set the database error in this case as well.
@@ -61859,11 +61882,11 @@
6185961882
if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
6186061883
/* If this statement was prepared using sqlite3_prepare_v2(), and an
6186161884
** error has occured, then return the error code in p->rc to the
6186261885
** caller. Set the error code in the database handle to the same value.
6186361886
*/
61864
- rc = db->errCode = p->rc;
61887
+ rc = sqlite3VdbeTransferError(p);
6186561888
}
6186661889
return (rc&db->errMask);
6186761890
}
6186861891
6186961892
/*
@@ -67759,13 +67782,12 @@
6775967782
6776067783
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
6776167784
u.bn.pC = p->apCsr[pOp->p1];
6776267785
assert( u.bn.pC!=0 );
6776367786
u.bn.pCrsr = u.bn.pC->pCursor;
67764
- if( NEVER(u.bn.pCrsr==0) ){
67765
- u.bn.res = 1;
67766
- }else{
67787
+ u.bn.res = 0;
67788
+ if( ALWAYS(u.bn.pCrsr!=0) ){
6776767789
rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res);
6776867790
}
6776967791
u.bn.pC->nullRow = (u8)u.bn.res;
6777067792
u.bn.pC->deferredMoveto = 0;
6777167793
u.bn.pC->rowidIsValid = 0;
@@ -68962,11 +68984,11 @@
6896268984
6896368985
/* Do not allow a transition to journal_mode=WAL for a database
6896468986
** in temporary storage or if the VFS does not support shared memory
6896568987
*/
6896668988
if( u.ch.eNew==PAGER_JOURNALMODE_WAL
68967
- && (u.ch.zFilename[0]==0 /* Temp file */
68989
+ && (sqlite3Strlen30(u.ch.zFilename)==0 /* Temp file */
6896868990
|| !sqlite3PagerWalSupported(u.ch.pPager)) /* No shared-memory support */
6896968991
){
6897068992
u.ch.eNew = u.ch.eOld;
6897168993
}
6897268994
@@ -69397,14 +69419,19 @@
6939769419
u.co.pName = &aMem[pOp->p1];
6939869420
assert( u.co.pVtab->pModule->xRename );
6939969421
assert( memIsValid(u.co.pName) );
6940069422
REGISTER_TRACE(pOp->p1, u.co.pName);
6940169423
assert( u.co.pName->flags & MEM_Str );
69402
- rc = u.co.pVtab->pModule->xRename(u.co.pVtab, u.co.pName->z);
69403
- importVtabErrMsg(p, u.co.pVtab);
69404
- p->expired = 0;
69405
-
69424
+ testcase( u.co.pName->enc==SQLITE_UTF8 );
69425
+ testcase( u.co.pName->enc==SQLITE_UTF16BE );
69426
+ testcase( u.co.pName->enc==SQLITE_UTF16LE );
69427
+ rc = sqlite3VdbeChangeEncoding(u.co.pName, SQLITE_UTF8);
69428
+ if( rc==SQLITE_OK ){
69429
+ rc = u.co.pVtab->pModule->xRename(u.co.pVtab, u.co.pName->z);
69430
+ importVtabErrMsg(p, u.co.pVtab);
69431
+ p->expired = 0;
69432
+ }
6940669433
break;
6940769434
}
6940869435
#endif
6940969436
6941069437
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -71758,10 +71785,28 @@
7175871785
ExprSetProperty(pExpr, EP_Static);
7175971786
sqlite3ExprDelete(db, pExpr);
7176071787
memcpy(pExpr, pDup, sizeof(*pExpr));
7176171788
sqlite3DbFree(db, pDup);
7176271789
}
71790
+
71791
+
71792
+/*
71793
+** Return TRUE if the name zCol occurs anywhere in the USING clause.
71794
+**
71795
+** Return FALSE if the USING clause is NULL or if it does not contain
71796
+** zCol.
71797
+*/
71798
+static int nameInUsingClause(IdList *pUsing, const char *zCol){
71799
+ if( pUsing ){
71800
+ int k;
71801
+ for(k=0; k<pUsing->nId; k++){
71802
+ if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1;
71803
+ }
71804
+ }
71805
+ return 0;
71806
+}
71807
+
7176371808
7176471809
/*
7176571810
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
7176671811
** that name in the set of source tables in pSrcList and make the pExpr
7176771812
** expression node refer back to that source column. The following changes
@@ -71850,38 +71895,25 @@
7185071895
pSchema = pTab->pSchema;
7185171896
pMatch = pItem;
7185271897
}
7185371898
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
7185471899
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
71855
- IdList *pUsing;
71900
+ /* If there has been exactly one prior match and this match
71901
+ ** is for the right-hand table of a NATURAL JOIN or is in a
71902
+ ** USING clause, then skip this match.
71903
+ */
71904
+ if( cnt==1 ){
71905
+ if( pItem->jointype & JT_NATURAL ) continue;
71906
+ if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
71907
+ }
7185671908
cnt++;
7185771909
pExpr->iTable = pItem->iCursor;
7185871910
pExpr->pTab = pTab;
7185971911
pMatch = pItem;
7186071912
pSchema = pTab->pSchema;
7186171913
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
7186271914
pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
71863
- if( i<pSrcList->nSrc-1 ){
71864
- if( pItem[1].jointype & JT_NATURAL ){
71865
- /* If this match occurred in the left table of a natural join,
71866
- ** then skip the right table to avoid a duplicate match */
71867
- pItem++;
71868
- i++;
71869
- }else if( (pUsing = pItem[1].pUsing)!=0 ){
71870
- /* If this match occurs on a column that is in the USING clause
71871
- ** of a join, skip the search of the right table of the join
71872
- ** to avoid a duplicate match there. */
71873
- int k;
71874
- for(k=0; k<pUsing->nId; k++){
71875
- if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){
71876
- pItem++;
71877
- i++;
71878
- break;
71879
- }
71880
- }
71881
- }
71882
- }
7188371915
break;
7188471916
}
7188571917
}
7188671918
}
7188771919
}
@@ -77594,20 +77626,20 @@
7759477626
#ifndef SQLITE_OMIT_ANALYZE
7759577627
7759677628
/*
7759777629
** This routine generates code that opens the sqlite_stat1 table for
7759877630
** writing with cursor iStatCur. If the library was built with the
77599
-** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is
77631
+** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is
7760077632
** opened for writing using cursor (iStatCur+1)
7760177633
**
7760277634
** If the sqlite_stat1 tables does not previously exist, it is created.
77603
-** Similarly, if the sqlite_stat2 table does not exist and the library
77604
-** is compiled with SQLITE_ENABLE_STAT2 defined, it is created.
77635
+** Similarly, if the sqlite_stat3 table does not exist and the library
77636
+** is compiled with SQLITE_ENABLE_STAT3 defined, it is created.
7760577637
**
7760677638
** Argument zWhere may be a pointer to a buffer containing a table name,
7760777639
** or it may be a NULL pointer. If it is not NULL, then all entries in
77608
-** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated
77640
+** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated
7760977641
** with the named table are deleted. If zWhere==0, then code is generated
7761077642
** to delete all stat table entries.
7761177643
*/
7761277644
static void openStatTable(
7761377645
Parse *pParse, /* Parsing context */
@@ -81388,31 +81420,28 @@
8138881420
}
8138981421
#endif
8139081422
}
8139181423
8139281424
/*
81393
-** Remove entries from the sqlite_stat1 and sqlite_stat2 tables
81425
+** Remove entries from the sqlite_statN tables (for N in (1,2,3))
8139481426
** after a DROP INDEX or DROP TABLE command.
8139581427
*/
8139681428
static void sqlite3ClearStatTables(
8139781429
Parse *pParse, /* The parsing context */
8139881430
int iDb, /* The database number */
8139981431
const char *zType, /* "idx" or "tbl" */
8140081432
const char *zName /* Name of index or table */
8140181433
){
81402
- static const char *azStatTab[] = {
81403
- "sqlite_stat1",
81404
- "sqlite_stat2",
81405
- "sqlite_stat3",
81406
- };
8140781434
int i;
8140881435
const char *zDbName = pParse->db->aDb[iDb].zName;
81409
- for(i=0; i<ArraySize(azStatTab); i++){
81410
- if( sqlite3FindTable(pParse->db, azStatTab[i], zDbName) ){
81436
+ for(i=1; i<=3; i++){
81437
+ char zTab[24];
81438
+ sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
81439
+ if( sqlite3FindTable(pParse->db, zTab, zDbName) ){
8141181440
sqlite3NestedParse(pParse,
8141281441
"DELETE FROM %Q.%s WHERE %s=%Q",
81413
- zDbName, azStatTab[i], zType, zName
81442
+ zDbName, zTab, zType, zName
8141481443
);
8141581444
}
8141681445
}
8141781446
}
8141881447
@@ -89234,12 +89263,14 @@
8923489263
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
8923589264
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
8923689265
int (*busy_timeout)(sqlite3*,int ms);
8923789266
int (*changes)(sqlite3*);
8923889267
int (*close)(sqlite3*);
89239
- int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
89240
- int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
89268
+ int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
89269
+ int eTextRep,const char*));
89270
+ int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
89271
+ int eTextRep,const void*));
8924189272
const void * (*column_blob)(sqlite3_stmt*,int iCol);
8924289273
int (*column_bytes)(sqlite3_stmt*,int iCol);
8924389274
int (*column_bytes16)(sqlite3_stmt*,int iCol);
8924489275
int (*column_count)(sqlite3_stmt*pStmt);
8924589276
const char * (*column_database_name)(sqlite3_stmt*,int);
@@ -89260,14 +89291,22 @@
8926089291
int (*column_type)(sqlite3_stmt*,int iCol);
8926189292
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
8926289293
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
8926389294
int (*complete)(const char*sql);
8926489295
int (*complete16)(const void*sql);
89265
- int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
89266
- int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*));
89267
- int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
89268
- int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
89296
+ int (*create_collation)(sqlite3*,const char*,int,void*,
89297
+ int(*)(void*,int,const void*,int,const void*));
89298
+ int (*create_collation16)(sqlite3*,const void*,int,void*,
89299
+ int(*)(void*,int,const void*,int,const void*));
89300
+ int (*create_function)(sqlite3*,const char*,int,int,void*,
89301
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
89302
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
89303
+ void (*xFinal)(sqlite3_context*));
89304
+ int (*create_function16)(sqlite3*,const void*,int,int,void*,
89305
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
89306
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
89307
+ void (*xFinal)(sqlite3_context*));
8926989308
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
8927089309
int (*data_count)(sqlite3_stmt*pStmt);
8927189310
sqlite3 * (*db_handle)(sqlite3_stmt*);
8927289311
int (*declare_vtab)(sqlite3*,const char*);
8927389312
int (*enable_shared_cache)(int);
@@ -89308,20 +89347,23 @@
8930889347
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
8930989348
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
8931089349
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
8931189350
void (*result_value)(sqlite3_context*,sqlite3_value*);
8931289351
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
89313
- int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*);
89352
+ int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
89353
+ const char*,const char*),void*);
8931489354
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
8931589355
char * (*snprintf)(int,char*,const char*,...);
8931689356
int (*step)(sqlite3_stmt*);
89317
- int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*);
89357
+ int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
89358
+ char const**,char const**,int*,int*,int*);
8931889359
void (*thread_cleanup)(void);
8931989360
int (*total_changes)(sqlite3*);
8932089361
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
8932189362
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
89322
- void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*);
89363
+ void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
89364
+ sqlite_int64),void*);
8932389365
void * (*user_data)(sqlite3_context*);
8932489366
const void * (*value_blob)(sqlite3_value*);
8932589367
int (*value_bytes)(sqlite3_value*);
8932689368
int (*value_bytes16)(sqlite3_value*);
8932789369
double (*value_double)(sqlite3_value*);
@@ -89339,19 +89381,23 @@
8933989381
/* Added by 3.3.13 */
8934089382
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
8934189383
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
8934289384
int (*clear_bindings)(sqlite3_stmt*);
8934389385
/* Added by 3.4.1 */
89344
- int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *));
89386
+ int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
89387
+ void (*xDestroy)(void *));
8934589388
/* Added by 3.5.0 */
8934689389
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
8934789390
int (*blob_bytes)(sqlite3_blob*);
8934889391
int (*blob_close)(sqlite3_blob*);
89349
- int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**);
89392
+ int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
89393
+ int,sqlite3_blob**);
8935089394
int (*blob_read)(sqlite3_blob*,void*,int,int);
8935189395
int (*blob_write)(sqlite3_blob*,const void*,int,int);
89352
- int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*));
89396
+ int (*create_collation_v2)(sqlite3*,const char*,int,void*,
89397
+ int(*)(void*,int,const void*,int,const void*),
89398
+ void(*)(void*));
8935389399
int (*file_control)(sqlite3*,const char*,int,void*);
8935489400
sqlite3_int64 (*memory_highwater)(int);
8935589401
sqlite3_int64 (*memory_used)(void);
8935689402
sqlite3_mutex *(*mutex_alloc)(int);
8935789403
void (*mutex_enter)(sqlite3_mutex*);
@@ -89383,11 +89429,15 @@
8938389429
int (*backup_pagecount)(sqlite3_backup*);
8938489430
int (*backup_remaining)(sqlite3_backup*);
8938589431
int (*backup_step)(sqlite3_backup*,int);
8938689432
const char *(*compileoption_get)(int);
8938789433
int (*compileoption_used)(const char*);
89388
- int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*));
89434
+ int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
89435
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
89436
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
89437
+ void (*xFinal)(sqlite3_context*),
89438
+ void(*xDestroy)(void*));
8938989439
int (*db_config)(sqlite3*,int,...);
8939089440
sqlite3_mutex *(*db_mutex)(sqlite3*);
8939189441
int (*db_status)(sqlite3*,int,int*,int*,int);
8939289442
int (*extended_errcode)(sqlite3*);
8939389443
void (*log)(int,const char*,...);
@@ -100469,11 +100519,11 @@
100469100519
if( db->aVTrans ){
100470100520
int i;
100471100521
for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
100472100522
VTable *pVTab = db->aVTrans[i];
100473100523
const sqlite3_module *pMod = pVTab->pMod->pModule;
100474
- if( pMod->iVersion>=2 ){
100524
+ if( pVTab->pVtab && pMod->iVersion>=2 ){
100475100525
int (*xMethod)(sqlite3_vtab *, int);
100476100526
switch( op ){
100477100527
case SAVEPOINT_BEGIN:
100478100528
xMethod = pMod->xSavepoint;
100479100529
pVTab->iSavepoint = iSavepoint+1;
@@ -100484,11 +100534,11 @@
100484100534
default:
100485100535
xMethod = pMod->xRelease;
100486100536
break;
100487100537
}
100488100538
if( xMethod && pVTab->iSavepoint>iSavepoint ){
100489
- rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint);
100539
+ rc = xMethod(pVTab->pVtab, iSavepoint);
100490100540
}
100491100541
}
100492100542
}
100493100543
}
100494100544
return rc;
@@ -101351,11 +101401,11 @@
101351101401
int iCol = pRight->iColumn;
101352101402
pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE);
101353101403
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
101354101404
z = (char *)sqlite3_value_text(pVal);
101355101405
}
101356
- sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-31526-56213 */
101406
+ sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
101357101407
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
101358101408
}else if( op==TK_STRING ){
101359101409
z = pRight->u.zToken;
101360101410
}
101361101411
if( z ){
@@ -101369,11 +101419,11 @@
101369101419
pPrefix = sqlite3Expr(db, TK_STRING, z);
101370101420
if( pPrefix ) pPrefix->u.zToken[cnt] = 0;
101371101421
*ppPrefix = pPrefix;
101372101422
if( op==TK_VARIABLE ){
101373101423
Vdbe *v = pParse->pVdbe;
101374
- sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-31526-56213 */
101424
+ sqlite3VdbeSetVarmask(v, pRight->iColumn);
101375101425
if( *pisComplete && pRight->u.zToken[1] ){
101376101426
/* If the rhs of the LIKE expression is a variable, and the current
101377101427
** value of the variable means there is no need to invoke the LIKE
101378101428
** function, then no OP_Variable will be added to the program.
101379101429
** This causes problems for the sqlite3_bind_parameter_name()
@@ -102501,10 +102551,11 @@
102501102551
tempWC.pParse = pWC->pParse;
102502102552
tempWC.pMaskSet = pWC->pMaskSet;
102503102553
tempWC.pOuter = pWC;
102504102554
tempWC.op = TK_AND;
102505102555
tempWC.a = pOrTerm;
102556
+ tempWC.wctrlFlags = 0;
102506102557
tempWC.nTerm = 1;
102507102558
bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost);
102508102559
}else{
102509102560
continue;
102510102561
}
@@ -103282,11 +103333,11 @@
103282103333
){
103283103334
if( pExpr->op==TK_VARIABLE
103284103335
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
103285103336
){
103286103337
int iVar = pExpr->iColumn;
103287
- sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-31526-56213 */
103338
+ sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
103288103339
*pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
103289103340
return SQLITE_OK;
103290103341
}
103291103342
return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp);
103292103343
}
@@ -103812,11 +103863,11 @@
103812103863
** a table or index. The actual times can vary, with the size of
103813103864
** records being an important factor. Both moves and searches are
103814103865
** slower with larger records, presumably because fewer records fit
103815103866
** on one page and hence more pages have to be fetched.
103816103867
**
103817
- ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do
103868
+ ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do
103818103869
** not give us data on the relative sizes of table and index records.
103819103870
** So this computation assumes table records are about twice as big
103820103871
** as index records
103821103872
*/
103822103873
if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){
@@ -114528,10 +114579,11 @@
114528114579
const char *zDb; /* logical database name */
114529114580
const char *zName; /* virtual table name */
114530114581
int nColumn; /* number of named columns in virtual table */
114531114582
char **azColumn; /* column names. malloced */
114532114583
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
114584
+ char *zContentTbl; /* content=xxx option, or NULL */
114533114585
114534114586
/* Precompiled statements used by the implementation. Each of these
114535114587
** statements is run and reset within a single virtual table API call.
114536114588
*/
114537114589
sqlite3_stmt *aStmt[27];
@@ -114568,11 +114620,11 @@
114568114620
} *aIndex;
114569114621
int nMaxPendingData; /* Max pending data before flush to disk */
114570114622
int nPendingData; /* Current bytes of pending data */
114571114623
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
114572114624
114573
-#if defined(SQLITE_DEBUG)
114625
+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
114574114626
/* State variables used for validating that the transaction control
114575114627
** methods of the virtual table are called at appropriate times. These
114576114628
** values do not contribution to the FTS computation; they are used for
114577114629
** verifying the SQLite core.
114578114630
*/
@@ -114653,10 +114705,11 @@
114653114705
*/
114654114706
struct Fts3PhraseToken {
114655114707
char *z; /* Text of the token */
114656114708
int n; /* Number of bytes in buffer z */
114657114709
int isPrefix; /* True if token ends with a "*" character */
114710
+ int bFirst; /* True if token must appear at position 0 */
114658114711
114659114712
/* Variables above this point are populated when the expression is
114660114713
** parsed (by code in fts3_expr.c). Below this point the variables are
114661114714
** used when evaluating the expression. */
114662114715
Fts3DeferredToken *pDeferred; /* Deferred token object for this token */
@@ -114771,10 +114824,11 @@
114771114824
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
114772114825
#define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002
114773114826
#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
114774114827
#define FTS3_SEGMENT_PREFIX 0x00000008
114775114828
#define FTS3_SEGMENT_SCAN 0x00000010
114829
+#define FTS3_SEGMENT_FIRST 0x00000020
114776114830
114777114831
/* Type passed as 4th argument to SegmentReaderIterate() */
114778114832
struct Fts3SegFilter {
114779114833
const char *zTerm;
114780114834
int nTerm;
@@ -114810,12 +114864,12 @@
114810114864
SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
114811114865
SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *);
114812114866
SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64);
114813114867
SQLITE_PRIVATE void sqlite3Fts3Dequote(char *);
114814114868
SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
114815
-
114816114869
SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
114870
+SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
114817114871
114818114872
/* fts3_tokenizer.c */
114819114873
SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
114820114874
SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
114821114875
SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
@@ -114830,11 +114884,11 @@
114830114884
);
114831114885
SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
114832114886
114833114887
/* fts3_expr.c */
114834114888
SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *,
114835
- char **, int, int, const char *, int, Fts3Expr **
114889
+ char **, int, int, int, const char *, int, Fts3Expr **
114836114890
);
114837114891
SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *);
114838114892
#ifdef SQLITE_TEST
114839114893
SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
114840114894
SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db);
@@ -115031,10 +115085,11 @@
115031115085
sqlite3_finalize(p->aStmt[i]);
115032115086
}
115033115087
sqlite3_free(p->zSegmentsTbl);
115034115088
sqlite3_free(p->zReadExprlist);
115035115089
sqlite3_free(p->zWriteExprlist);
115090
+ sqlite3_free(p->zContentTbl);
115036115091
115037115092
/* Invoke the tokenizer destructor to free the tokenizer. */
115038115093
p->pTokenizer->pModule->xDestroy(p->pTokenizer);
115039115094
115040115095
sqlite3_free(p);
@@ -115070,20 +115125,23 @@
115070115125
115071115126
/*
115072115127
** The xDestroy() virtual table method.
115073115128
*/
115074115129
static int fts3DestroyMethod(sqlite3_vtab *pVtab){
115075
- int rc = SQLITE_OK; /* Return code */
115076115130
Fts3Table *p = (Fts3Table *)pVtab;
115077
- sqlite3 *db = p->db;
115131
+ int rc = SQLITE_OK; /* Return code */
115132
+ const char *zDb = p->zDb; /* Name of database (e.g. "main", "temp") */
115133
+ sqlite3 *db = p->db; /* Database handle */
115078115134
115079115135
/* Drop the shadow tables */
115080
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", p->zDb, p->zName);
115081
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", p->zDb,p->zName);
115082
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", p->zDb, p->zName);
115083
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", p->zDb, p->zName);
115084
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", p->zDb, p->zName);
115136
+ if( p->zContentTbl==0 ){
115137
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", zDb, p->zName);
115138
+ }
115139
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", zDb,p->zName);
115140
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", zDb, p->zName);
115141
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", zDb, p->zName);
115142
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", zDb, p->zName);
115085115143
115086115144
/* If everything has worked, invoke fts3DisconnectMethod() to free the
115087115145
** memory associated with the Fts3Table structure and return SQLITE_OK.
115088115146
** Otherwise, return an SQLite error code.
115089115147
*/
@@ -115141,27 +115199,31 @@
115141115199
** %_stat tables required by FTS4.
115142115200
*/
115143115201
static int fts3CreateTables(Fts3Table *p){
115144115202
int rc = SQLITE_OK; /* Return code */
115145115203
int i; /* Iterator variable */
115146
- char *zContentCols; /* Columns of %_content table */
115147115204
sqlite3 *db = p->db; /* The database connection */
115148115205
115149
- /* Create a list of user columns for the content table */
115150
- zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
115151
- for(i=0; zContentCols && i<p->nColumn; i++){
115152
- char *z = p->azColumn[i];
115153
- zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
115154
- }
115155
- if( zContentCols==0 ) rc = SQLITE_NOMEM;
115156
-
115157
- /* Create the content table */
115158
- fts3DbExec(&rc, db,
115159
- "CREATE TABLE %Q.'%q_content'(%s)",
115160
- p->zDb, p->zName, zContentCols
115161
- );
115162
- sqlite3_free(zContentCols);
115206
+ if( p->zContentTbl==0 ){
115207
+ char *zContentCols; /* Columns of %_content table */
115208
+
115209
+ /* Create a list of user columns for the content table */
115210
+ zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
115211
+ for(i=0; zContentCols && i<p->nColumn; i++){
115212
+ char *z = p->azColumn[i];
115213
+ zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
115214
+ }
115215
+ if( zContentCols==0 ) rc = SQLITE_NOMEM;
115216
+
115217
+ /* Create the content table */
115218
+ fts3DbExec(&rc, db,
115219
+ "CREATE TABLE %Q.'%q_content'(%s)",
115220
+ p->zDb, p->zName, zContentCols
115221
+ );
115222
+ sqlite3_free(zContentCols);
115223
+ }
115224
+
115163115225
/* Create other tables */
115164115226
fts3DbExec(&rc, db,
115165115227
"CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);",
115166115228
p->zDb, p->zName
115167115229
);
@@ -115308,12 +115370,12 @@
115308115370
}
115309115371
return zRet;
115310115372
}
115311115373
115312115374
/*
115313
-** Return a list of comma separated SQL expressions that could be used
115314
-** in a SELECT statement such as the following:
115375
+** Return a list of comma separated SQL expressions and a FROM clause that
115376
+** could be used in a SELECT statement such as the following:
115315115377
**
115316115378
** SELECT <list of expressions> FROM %_content AS x ...
115317115379
**
115318115380
** to return the docid, followed by each column of text data in order
115319115381
** from left to write. If parameter zFunc is not NULL, then instead of
@@ -115320,11 +115382,11 @@
115320115382
** being returned directly each column of text data is passed to an SQL
115321115383
** function named zFunc first. For example, if zFunc is "unzip" and the
115322115384
** table has the three user-defined columns "a", "b", and "c", the following
115323115385
** string is returned:
115324115386
**
115325
-** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c')"
115387
+** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c') FROM %_content AS x"
115326115388
**
115327115389
** The pointer returned points to a buffer allocated by sqlite3_malloc(). It
115328115390
** is the responsibility of the caller to eventually free it.
115329115391
**
115330115392
** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and
@@ -115336,20 +115398,32 @@
115336115398
char *zRet = 0;
115337115399
char *zFree = 0;
115338115400
char *zFunction;
115339115401
int i;
115340115402
115341
- if( !zFunc ){
115342
- zFunction = "";
115403
+ if( p->zContentTbl==0 ){
115404
+ if( !zFunc ){
115405
+ zFunction = "";
115406
+ }else{
115407
+ zFree = zFunction = fts3QuoteId(zFunc);
115408
+ }
115409
+ fts3Appendf(pRc, &zRet, "docid");
115410
+ for(i=0; i<p->nColumn; i++){
115411
+ fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
115412
+ }
115413
+ sqlite3_free(zFree);
115343115414
}else{
115344
- zFree = zFunction = fts3QuoteId(zFunc);
115415
+ fts3Appendf(pRc, &zRet, "rowid");
115416
+ for(i=0; i<p->nColumn; i++){
115417
+ fts3Appendf(pRc, &zRet, ", x.'%q'", p->azColumn[i]);
115418
+ }
115345115419
}
115346
- fts3Appendf(pRc, &zRet, "docid");
115347
- for(i=0; i<p->nColumn; i++){
115348
- fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
115349
- }
115350
- sqlite3_free(zFree);
115420
+ fts3Appendf(pRc, &zRet, "FROM '%q'.'%q%s' AS x",
115421
+ p->zDb,
115422
+ (p->zContentTbl ? p->zContentTbl : p->zName),
115423
+ (p->zContentTbl ? "" : "_content")
115424
+ );
115351115425
return zRet;
115352115426
}
115353115427
115354115428
/*
115355115429
** Return a list of N comma separated question marks, where N is the number
@@ -115468,10 +115542,95 @@
115468115542
}
115469115543
}
115470115544
115471115545
return SQLITE_OK;
115472115546
}
115547
+
115548
+/*
115549
+** This function is called when initializing an FTS4 table that uses the
115550
+** content=xxx option. It determines the number of and names of the columns
115551
+** of the new FTS4 table.
115552
+**
115553
+** The third argument passed to this function is the value passed to the
115554
+** config=xxx option (i.e. "xxx"). This function queries the database for
115555
+** a table of that name. If found, the output variables are populated
115556
+** as follows:
115557
+**
115558
+** *pnCol: Set to the number of columns table xxx has,
115559
+**
115560
+** *pnStr: Set to the total amount of space required to store a copy
115561
+** of each columns name, including the nul-terminator.
115562
+**
115563
+** *pazCol: Set to point to an array of *pnCol strings. Each string is
115564
+** the name of the corresponding column in table xxx. The array
115565
+** and its contents are allocated using a single allocation. It
115566
+** is the responsibility of the caller to free this allocation
115567
+** by eventually passing the *pazCol value to sqlite3_free().
115568
+**
115569
+** If the table cannot be found, an error code is returned and the output
115570
+** variables are undefined. Or, if an OOM is encountered, SQLITE_NOMEM is
115571
+** returned (and the output variables are undefined).
115572
+*/
115573
+static int fts3ContentColumns(
115574
+ sqlite3 *db, /* Database handle */
115575
+ const char *zDb, /* Name of db (i.e. "main", "temp" etc.) */
115576
+ const char *zTbl, /* Name of content table */
115577
+ const char ***pazCol, /* OUT: Malloc'd array of column names */
115578
+ int *pnCol, /* OUT: Size of array *pazCol */
115579
+ int *pnStr /* OUT: Bytes of string content */
115580
+){
115581
+ int rc = SQLITE_OK; /* Return code */
115582
+ char *zSql; /* "SELECT *" statement on zTbl */
115583
+ sqlite3_stmt *pStmt = 0; /* Compiled version of zSql */
115584
+
115585
+ zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zTbl);
115586
+ if( !zSql ){
115587
+ rc = SQLITE_NOMEM;
115588
+ }else{
115589
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
115590
+ }
115591
+ sqlite3_free(zSql);
115592
+
115593
+ if( rc==SQLITE_OK ){
115594
+ const char **azCol; /* Output array */
115595
+ int nStr = 0; /* Size of all column names (incl. 0x00) */
115596
+ int nCol; /* Number of table columns */
115597
+ int i; /* Used to iterate through columns */
115598
+
115599
+ /* Loop through the returned columns. Set nStr to the number of bytes of
115600
+ ** space required to store a copy of each column name, including the
115601
+ ** nul-terminator byte. */
115602
+ nCol = sqlite3_column_count(pStmt);
115603
+ for(i=0; i<nCol; i++){
115604
+ const char *zCol = sqlite3_column_name(pStmt, i);
115605
+ nStr += strlen(zCol) + 1;
115606
+ }
115607
+
115608
+ /* Allocate and populate the array to return. */
115609
+ azCol = (const char **)sqlite3_malloc(sizeof(char *) * nCol + nStr);
115610
+ if( azCol==0 ){
115611
+ rc = SQLITE_NOMEM;
115612
+ }else{
115613
+ char *p = (char *)&azCol[nCol];
115614
+ for(i=0; i<nCol; i++){
115615
+ const char *zCol = sqlite3_column_name(pStmt, i);
115616
+ int n = strlen(zCol)+1;
115617
+ memcpy(p, zCol, n);
115618
+ azCol[i] = p;
115619
+ p += n;
115620
+ }
115621
+ }
115622
+ sqlite3_finalize(pStmt);
115623
+
115624
+ /* Set the output variables. */
115625
+ *pnCol = nCol;
115626
+ *pnStr = nStr;
115627
+ *pazCol = azCol;
115628
+ }
115629
+
115630
+ return rc;
115631
+}
115473115632
115474115633
/*
115475115634
** This function is the implementation of both the xConnect and xCreate
115476115635
** methods of the FTS3 virtual table.
115477115636
**
@@ -115513,10 +115672,11 @@
115513115672
int bNoDocsize = 0; /* True to omit %_docsize table */
115514115673
int bDescIdx = 0; /* True to store descending indexes */
115515115674
char *zPrefix = 0; /* Prefix parameter value (or NULL) */
115516115675
char *zCompress = 0; /* compress=? parameter (or NULL) */
115517115676
char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
115677
+ char *zContent = 0; /* content=? parameter (or NULL) */
115518115678
115519115679
assert( strlen(argv[0])==4 );
115520115680
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
115521115681
|| (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4)
115522115682
);
@@ -115556,17 +115716,17 @@
115556115716
/* Check if it is an FTS4 special argument. */
115557115717
else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){
115558115718
struct Fts4Option {
115559115719
const char *zOpt;
115560115720
int nOpt;
115561
- char **pzVar;
115562115721
} aFts4Opt[] = {
115563
- { "matchinfo", 9, 0 }, /* 0 -> MATCHINFO */
115564
- { "prefix", 6, 0 }, /* 1 -> PREFIX */
115565
- { "compress", 8, 0 }, /* 2 -> COMPRESS */
115566
- { "uncompress", 10, 0 }, /* 3 -> UNCOMPRESS */
115567
- { "order", 5, 0 } /* 4 -> ORDER */
115722
+ { "matchinfo", 9 }, /* 0 -> MATCHINFO */
115723
+ { "prefix", 6 }, /* 1 -> PREFIX */
115724
+ { "compress", 8 }, /* 2 -> COMPRESS */
115725
+ { "uncompress", 10 }, /* 3 -> UNCOMPRESS */
115726
+ { "order", 5 }, /* 4 -> ORDER */
115727
+ { "content", 7 } /* 5 -> CONTENT */
115568115728
};
115569115729
115570115730
int iOpt;
115571115731
if( !zVal ){
115572115732
rc = SQLITE_NOMEM;
@@ -115608,17 +115768,24 @@
115608115768
zVal = 0;
115609115769
break;
115610115770
115611115771
case 4: /* ORDER */
115612115772
if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
115613
- && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 3))
115773
+ && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4))
115614115774
){
115615115775
*pzErr = sqlite3_mprintf("unrecognized order: %s", zVal);
115616115776
rc = SQLITE_ERROR;
115617115777
}
115618115778
bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
115619115779
break;
115780
+
115781
+ default: /* CONTENT */
115782
+ assert( iOpt==5 );
115783
+ sqlite3_free(zUncompress);
115784
+ zContent = zVal;
115785
+ zVal = 0;
115786
+ break;
115620115787
}
115621115788
}
115622115789
sqlite3_free(zVal);
115623115790
}
115624115791
}
@@ -115627,10 +115794,30 @@
115627115794
else {
115628115795
nString += (int)(strlen(z) + 1);
115629115796
aCol[nCol++] = z;
115630115797
}
115631115798
}
115799
+
115800
+ /* If a content=xxx option was specified, the following:
115801
+ **
115802
+ ** 1. Ignore any compress= and uncompress= options.
115803
+ **
115804
+ ** 2. If no column names were specified as part of the CREATE VIRTUAL
115805
+ ** TABLE statement, use all columns from the content table.
115806
+ */
115807
+ if( rc==SQLITE_OK && zContent ){
115808
+ sqlite3_free(zCompress);
115809
+ sqlite3_free(zUncompress);
115810
+ zCompress = 0;
115811
+ zUncompress = 0;
115812
+ if( nCol==0 ){
115813
+ sqlite3_free((void*)aCol);
115814
+ aCol = 0;
115815
+ rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString);
115816
+ }
115817
+ assert( rc!=SQLITE_OK || nCol>0 );
115818
+ }
115632115819
if( rc!=SQLITE_OK ) goto fts3_init_out;
115633115820
115634115821
if( nCol==0 ){
115635115822
assert( nString==0 );
115636115823
aCol[0] = "content";
@@ -115671,10 +115858,12 @@
115671115858
p->pTokenizer = pTokenizer;
115672115859
p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
115673115860
p->bHasDocsize = (isFts4 && bNoDocsize==0);
115674115861
p->bHasStat = isFts4;
115675115862
p->bDescIdx = bDescIdx;
115863
+ p->zContentTbl = zContent;
115864
+ zContent = 0;
115676115865
TESTONLY( p->inTransaction = -1 );
115677115866
TESTONLY( p->mxSavepoint = -1 );
115678115867
115679115868
p->aIndex = (struct Fts3Index *)&p->azColumn[nCol];
115680115869
memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex);
@@ -115732,10 +115921,11 @@
115732115921
fts3_init_out:
115733115922
sqlite3_free(zPrefix);
115734115923
sqlite3_free(aIndex);
115735115924
sqlite3_free(zCompress);
115736115925
sqlite3_free(zUncompress);
115926
+ sqlite3_free(zContent);
115737115927
sqlite3_free((void *)aCol);
115738115928
if( rc!=SQLITE_OK ){
115739115929
if( p ){
115740115930
fts3DisconnectMethod((sqlite3_vtab *)p);
115741115931
}else if( pTokenizer ){
@@ -115882,40 +116072,69 @@
115882116072
sqlite3_free(pCsr->aMatchinfo);
115883116073
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
115884116074
sqlite3_free(pCsr);
115885116075
return SQLITE_OK;
115886116076
}
116077
+
116078
+/*
116079
+** If pCsr->pStmt has not been prepared (i.e. if pCsr->pStmt==0), then
116080
+** compose and prepare an SQL statement of the form:
116081
+**
116082
+** "SELECT <columns> FROM %_content WHERE rowid = ?"
116083
+**
116084
+** (or the equivalent for a content=xxx table) and set pCsr->pStmt to
116085
+** it. If an error occurs, return an SQLite error code.
116086
+**
116087
+** Otherwise, set *ppStmt to point to pCsr->pStmt and return SQLITE_OK.
116088
+*/
116089
+static int fts3CursorSeekStmt(Fts3Cursor *pCsr, sqlite3_stmt **ppStmt){
116090
+ int rc = SQLITE_OK;
116091
+ if( pCsr->pStmt==0 ){
116092
+ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
116093
+ char *zSql;
116094
+ zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
116095
+ if( !zSql ) return SQLITE_NOMEM;
116096
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
116097
+ sqlite3_free(zSql);
116098
+ }
116099
+ *ppStmt = pCsr->pStmt;
116100
+ return rc;
116101
+}
115887116102
115888116103
/*
115889116104
** Position the pCsr->pStmt statement so that it is on the row
115890116105
** of the %_content table that contains the last match. Return
115891116106
** SQLITE_OK on success.
115892116107
*/
115893116108
static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
116109
+ int rc = SQLITE_OK;
115894116110
if( pCsr->isRequireSeek ){
115895
- sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
115896
- pCsr->isRequireSeek = 0;
115897
- if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
115898
- return SQLITE_OK;
115899
- }else{
115900
- int rc = sqlite3_reset(pCsr->pStmt);
115901
- if( rc==SQLITE_OK ){
115902
- /* If no row was found and no error has occured, then the %_content
115903
- ** table is missing a row that is present in the full-text index.
115904
- ** The data structures are corrupt.
115905
- */
115906
- rc = FTS_CORRUPT_VTAB;
115907
- }
115908
- pCsr->isEof = 1;
115909
- if( pContext ){
115910
- sqlite3_result_error_code(pContext, rc);
115911
- }
115912
- return rc;
115913
- }
115914
- }else{
115915
- return SQLITE_OK;
115916
- }
116111
+ sqlite3_stmt *pStmt = 0;
116112
+
116113
+ rc = fts3CursorSeekStmt(pCsr, &pStmt);
116114
+ if( rc==SQLITE_OK ){
116115
+ sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
116116
+ pCsr->isRequireSeek = 0;
116117
+ if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
116118
+ return SQLITE_OK;
116119
+ }else{
116120
+ rc = sqlite3_reset(pCsr->pStmt);
116121
+ if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){
116122
+ /* If no row was found and no error has occured, then the %_content
116123
+ ** table is missing a row that is present in the full-text index.
116124
+ ** The data structures are corrupt. */
116125
+ rc = FTS_CORRUPT_VTAB;
116126
+ pCsr->isEof = 1;
116127
+ }
116128
+ }
116129
+ }
116130
+ }
116131
+
116132
+ if( rc!=SQLITE_OK && pContext ){
116133
+ sqlite3_result_error_code(pContext, rc);
116134
+ }
116135
+ return rc;
115917116136
}
115918116137
115919116138
/*
115920116139
** This function is used to process a single interior node when searching
115921116140
** a b-tree for a term or term prefix. The node data is passed to this
@@ -116351,20 +116570,20 @@
116351116570
int isSaveLeft, /* Save the left position */
116352116571
int isExact, /* If *pp1 is exactly nTokens before *pp2 */
116353116572
char **pp1, /* IN/OUT: Left input list */
116354116573
char **pp2 /* IN/OUT: Right input list */
116355116574
){
116356
- char *p = (pp ? *pp : 0);
116575
+ char *p = *pp;
116357116576
char *p1 = *pp1;
116358116577
char *p2 = *pp2;
116359116578
int iCol1 = 0;
116360116579
int iCol2 = 0;
116361116580
116362116581
/* Never set both isSaveLeft and isExact for the same invocation. */
116363116582
assert( isSaveLeft==0 || isExact==0 );
116364116583
116365
- assert( *p1!=0 && *p2!=0 );
116584
+ assert( p!=0 && *p1!=0 && *p2!=0 );
116366116585
if( *p1==POS_COLUMN ){
116367116586
p1++;
116368116587
p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
116369116588
}
116370116589
if( *p2==POS_COLUMN ){
@@ -116377,11 +116596,11 @@
116377116596
char *pSave = p;
116378116597
sqlite3_int64 iPrev = 0;
116379116598
sqlite3_int64 iPos1 = 0;
116380116599
sqlite3_int64 iPos2 = 0;
116381116600
116382
- if( pp && iCol1 ){
116601
+ if( iCol1 ){
116383116602
*p++ = POS_COLUMN;
116384116603
p += sqlite3Fts3PutVarint(p, iCol1);
116385116604
}
116386116605
116387116606
assert( *p1!=POS_END && *p1!=POS_COLUMN );
@@ -116392,20 +116611,14 @@
116392116611
while( 1 ){
116393116612
if( iPos2==iPos1+nToken
116394116613
|| (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken)
116395116614
){
116396116615
sqlite3_int64 iSave;
116397
- if( !pp ){
116398
- fts3PoslistCopy(0, &p2);
116399
- fts3PoslistCopy(0, &p1);
116400
- *pp1 = p1;
116401
- *pp2 = p2;
116402
- return 1;
116403
- }
116404116616
iSave = isSaveLeft ? iPos1 : iPos2;
116405116617
fts3PutDeltaVarint(&p, &iPrev, iSave+2); iPrev -= 2;
116406116618
pSave = 0;
116619
+ assert( p );
116407116620
}
116408116621
if( (!isSaveLeft && iPos2<=(iPos1+nToken)) || iPos2<=iPos1 ){
116409116622
if( (*p2&0xFE)==0 ) break;
116410116623
fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2;
116411116624
}else{
@@ -116450,11 +116663,11 @@
116450116663
116451116664
fts3PoslistCopy(0, &p2);
116452116665
fts3PoslistCopy(0, &p1);
116453116666
*pp1 = p1;
116454116667
*pp2 = p2;
116455
- if( !pp || *pp==p ){
116668
+ if( *pp==p ){
116456116669
return 0;
116457116670
}
116458116671
*p++ = 0x00;
116459116672
*pp = p;
116460116673
return 1;
@@ -116752,10 +116965,60 @@
116752116965
}
116753116966
}
116754116967
116755116968
*pnRight = p - aOut;
116756116969
}
116970
+
116971
+/*
116972
+** Argument pList points to a position list nList bytes in size. This
116973
+** function checks to see if the position list contains any entries for
116974
+** a token in position 0 (of any column). If so, it writes argument iDelta
116975
+** to the output buffer pOut, followed by a position list consisting only
116976
+** of the entries from pList at position 0, and terminated by an 0x00 byte.
116977
+** The value returned is the number of bytes written to pOut (if any).
116978
+*/
116979
+SQLITE_PRIVATE int sqlite3Fts3FirstFilter(
116980
+ sqlite3_int64 iDelta, /* Varint that may be written to pOut */
116981
+ char *pList, /* Position list (no 0x00 term) */
116982
+ int nList, /* Size of pList in bytes */
116983
+ char *pOut /* Write output here */
116984
+){
116985
+ int nOut = 0;
116986
+ int bWritten = 0; /* True once iDelta has been written */
116987
+ char *p = pList;
116988
+ char *pEnd = &pList[nList];
116989
+
116990
+ if( *p!=0x01 ){
116991
+ if( *p==0x02 ){
116992
+ nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta);
116993
+ pOut[nOut++] = 0x02;
116994
+ bWritten = 1;
116995
+ }
116996
+ fts3ColumnlistCopy(0, &p);
116997
+ }
116998
+
116999
+ while( p<pEnd && *p==0x01 ){
117000
+ sqlite3_int64 iCol;
117001
+ p++;
117002
+ p += sqlite3Fts3GetVarint(p, &iCol);
117003
+ if( *p==0x02 ){
117004
+ if( bWritten==0 ){
117005
+ nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta);
117006
+ bWritten = 1;
117007
+ }
117008
+ pOut[nOut++] = 0x01;
117009
+ nOut += sqlite3Fts3PutVarint(&pOut[nOut], iCol);
117010
+ pOut[nOut++] = 0x02;
117011
+ }
117012
+ fts3ColumnlistCopy(0, &p);
117013
+ }
117014
+ if( bWritten ){
117015
+ pOut[nOut++] = 0x00;
117016
+ }
117017
+
117018
+ return nOut;
117019
+}
116757117020
116758117021
116759117022
/*
116760117023
** Merge all doclists in the TermSelect.aaOutput[] array into a single
116761117024
** doclist stored in TermSelect.aaOutput[0]. If successful, delete all
@@ -117109,10 +117372,11 @@
117109117372
pSegcsr = pTok->pSegcsr;
117110117373
memset(&tsc, 0, sizeof(TermSelect));
117111117374
117112117375
filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | FTS3_SEGMENT_REQUIRE_POS
117113117376
| (pTok->isPrefix ? FTS3_SEGMENT_PREFIX : 0)
117377
+ | (pTok->bFirst ? FTS3_SEGMENT_FIRST : 0)
117114117378
| (iColumn<p->nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0);
117115117379
filter.iCol = iColumn;
117116117380
filter.zTerm = pTok->z;
117117117381
filter.nTerm = pTok->n;
117118117382
@@ -117249,12 +117513,12 @@
117249117513
117250117514
if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
117251117515
return SQLITE_NOMEM;
117252117516
}
117253117517
117254
- rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn,
117255
- iCol, zQuery, -1, &pCsr->pExpr
117518
+ rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->bHasStat,
117519
+ p->nColumn, iCol, zQuery, -1, &pCsr->pExpr
117256117520
);
117257117521
if( rc!=SQLITE_OK ){
117258117522
if( rc==SQLITE_ERROR ){
117259117523
static const char *zErr = "malformed MATCH expression: [%s]";
117260117524
p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery);
@@ -117277,26 +117541,27 @@
117277117541
** statement loops through all rows of the %_content table. For a
117278117542
** full-text query or docid lookup, the statement retrieves a single
117279117543
** row by docid.
117280117544
*/
117281117545
if( idxNum==FTS3_FULLSCAN_SEARCH ){
117282
- const char *zSort = (pCsr->bDesc ? "DESC" : "ASC");
117283
- const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x ORDER BY docid %s";
117284
- zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName, zSort);
117285
- }else{
117286
- const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?";
117287
- zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName);
117288
- }
117289
- if( !zSql ) return SQLITE_NOMEM;
117290
- rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
117291
- sqlite3_free(zSql);
117292
- if( rc!=SQLITE_OK ) return rc;
117293
-
117294
- if( idxNum==FTS3_DOCID_SEARCH ){
117295
- rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
117296
- if( rc!=SQLITE_OK ) return rc;
117297
- }
117546
+ zSql = sqlite3_mprintf(
117547
+ "SELECT %s ORDER BY rowid %s",
117548
+ p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC")
117549
+ );
117550
+ if( zSql ){
117551
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
117552
+ sqlite3_free(zSql);
117553
+ }else{
117554
+ rc = SQLITE_NOMEM;
117555
+ }
117556
+ }else if( idxNum==FTS3_DOCID_SEARCH ){
117557
+ rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt);
117558
+ if( rc==SQLITE_OK ){
117559
+ rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
117560
+ }
117561
+ }
117562
+ if( rc!=SQLITE_OK ) return rc;
117298117563
117299117564
return fts3NextMethod(pCursor);
117300117565
}
117301117566
117302117567
/*
@@ -117345,11 +117610,11 @@
117345117610
** Return a blob which is a pointer to the cursor.
117346117611
*/
117347117612
sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
117348117613
}else{
117349117614
rc = fts3CursorSeek(0, pCsr);
117350
- if( rc==SQLITE_OK ){
117615
+ if( rc==SQLITE_OK && sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){
117351117616
sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
117352117617
}
117353117618
}
117354117619
117355117620
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
@@ -117638,19 +117903,26 @@
117638117903
){
117639117904
Fts3Table *p = (Fts3Table *)pVtab;
117640117905
sqlite3 *db = p->db; /* Database connection */
117641117906
int rc; /* Return Code */
117642117907
117908
+ /* As it happens, the pending terms table is always empty here. This is
117909
+ ** because an "ALTER TABLE RENAME TABLE" statement inside a transaction
117910
+ ** always opens a savepoint transaction. And the xSavepoint() method
117911
+ ** flushes the pending terms table. But leave the (no-op) call to
117912
+ ** PendingTermsFlush() in in case that changes.
117913
+ */
117914
+ assert( p->nPendingData==0 );
117643117915
rc = sqlite3Fts3PendingTermsFlush(p);
117644
- if( rc!=SQLITE_OK ){
117645
- return rc;
117916
+
117917
+ if( p->zContentTbl==0 ){
117918
+ fts3DbExec(&rc, db,
117919
+ "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
117920
+ p->zDb, p->zName, zName
117921
+ );
117646117922
}
117647117923
117648
- fts3DbExec(&rc, db,
117649
- "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
117650
- p->zDb, p->zName, zName
117651
- );
117652117924
if( p->bHasDocsize ){
117653117925
fts3DbExec(&rc, db,
117654117926
"ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';",
117655117927
p->zDb, p->zName, zName
117656117928
);
@@ -118005,25 +118277,24 @@
118005118277
**
118006118278
** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
118007118279
*/
118008118280
static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
118009118281
int iToken; /* Used to iterate through phrase tokens */
118010
- int rc = SQLITE_OK; /* Return code */
118011118282
char *aPoslist = 0; /* Position list for deferred tokens */
118012118283
int nPoslist = 0; /* Number of bytes in aPoslist */
118013118284
int iPrev = -1; /* Token number of previous deferred token */
118014118285
118015118286
assert( pPhrase->doclist.bFreeList==0 );
118016118287
118017
- for(iToken=0; rc==SQLITE_OK && iToken<pPhrase->nToken; iToken++){
118288
+ for(iToken=0; iToken<pPhrase->nToken; iToken++){
118018118289
Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
118019118290
Fts3DeferredToken *pDeferred = pToken->pDeferred;
118020118291
118021118292
if( pDeferred ){
118022118293
char *pList;
118023118294
int nList;
118024
- rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList);
118295
+ int rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList);
118025118296
if( rc!=SQLITE_OK ) return rc;
118026118297
118027118298
if( pList==0 ){
118028118299
sqlite3_free(aPoslist);
118029118300
pPhrase->doclist.pList = 0;
@@ -118120,10 +118391,11 @@
118120118391
if( pCsr->bDesc==pTab->bDescIdx
118121118392
&& bOptOk==1
118122118393
&& p->nToken==1
118123118394
&& pFirst->pSegcsr
118124118395
&& pFirst->pSegcsr->bLookup
118396
+ && pFirst->bFirst==0
118125118397
){
118126118398
/* Use the incremental approach. */
118127118399
int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
118128118400
rc = sqlite3Fts3MsrIncrStart(
118129118401
pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
@@ -118349,11 +118621,11 @@
118349118621
Fts3Expr *pExpr, /* Expression to consider */
118350118622
Fts3TokenAndCost **ppTC, /* Write new entries to *(*ppTC)++ */
118351118623
Fts3Expr ***ppOr, /* Write new OR root to *(*ppOr)++ */
118352118624
int *pRc /* IN/OUT: Error code */
118353118625
){
118354
- if( *pRc==SQLITE_OK && pExpr ){
118626
+ if( *pRc==SQLITE_OK ){
118355118627
if( pExpr->eType==FTSQUERY_PHRASE ){
118356118628
Fts3Phrase *pPhrase = pExpr->pPhrase;
118357118629
int i;
118358118630
for(i=0; *pRc==SQLITE_OK && i<pPhrase->nToken; i++){
118359118631
Fts3TokenAndCost *pTC = (*ppTC)++;
@@ -118363,10 +118635,15 @@
118363118635
pTC->pToken = &pPhrase->aToken[i];
118364118636
pTC->iCol = pPhrase->iColumn;
118365118637
*pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl);
118366118638
}
118367118639
}else if( pExpr->eType!=FTSQUERY_NOT ){
118640
+ assert( pExpr->eType==FTSQUERY_OR
118641
+ || pExpr->eType==FTSQUERY_AND
118642
+ || pExpr->eType==FTSQUERY_NEAR
118643
+ );
118644
+ assert( pExpr->pLeft && pExpr->pRight );
118368118645
if( pExpr->eType==FTSQUERY_OR ){
118369118646
pRoot = pExpr->pLeft;
118370118647
**ppOr = pRoot;
118371118648
(*ppOr)++;
118372118649
}
@@ -118466,10 +118743,19 @@
118466118743
int nOvfl = 0; /* Total overflow pages used by doclists */
118467118744
int nToken = 0; /* Total number of tokens in cluster */
118468118745
118469118746
int nMinEst = 0; /* The minimum count for any phrase so far. */
118470118747
int nLoad4 = 1; /* (Phrases that will be loaded)^4. */
118748
+
118749
+ /* Tokens are never deferred for FTS tables created using the content=xxx
118750
+ ** option. The reason being that it is not guaranteed that the content
118751
+ ** table actually contains the same data as the index. To prevent this from
118752
+ ** causing any problems, the deferred token optimization is completely
118753
+ ** disabled for content=xxx tables. */
118754
+ if( pTab->zContentTbl ){
118755
+ return SQLITE_OK;
118756
+ }
118471118757
118472118758
/* Count the tokens in this AND/NEAR cluster. If none of the doclists
118473118759
** associated with the tokens spill onto overflow pages, or if there is
118474118760
** only 1 token, exit early. No tokens to defer in this case. */
118475118761
for(ii=0; ii<nTC; ii++){
@@ -118529,11 +118815,15 @@
118529118815
Fts3PhraseToken *pToken = pTC->pToken;
118530118816
rc = sqlite3Fts3DeferToken(pCsr, pToken, pTC->iCol);
118531118817
fts3SegReaderCursorFree(pToken->pSegcsr);
118532118818
pToken->pSegcsr = 0;
118533118819
}else{
118534
- nLoad4 = nLoad4*4;
118820
+ /* Set nLoad4 to the value of (4^nOther) for the next iteration of the
118821
+ ** for-loop. Except, limit the value to 2^24 to prevent it from
118822
+ ** overflowing the 32-bit integer it is stored in. */
118823
+ if( ii<12 ) nLoad4 = nLoad4*4;
118824
+
118535118825
if( ii==0 || pTC->pPhrase->nToken>1 ){
118536118826
/* Either this is the cheapest token in the entire query, or it is
118537118827
** part of a multi-token phrase. Either way, the entire doclist will
118538118828
** (eventually) be loaded into memory. It may as well be now. */
118539118829
Fts3PhraseToken *pToken = pTC->pToken;
@@ -119990,10 +120280,11 @@
119990120280
*/
119991120281
typedef struct ParseContext ParseContext;
119992120282
struct ParseContext {
119993120283
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
119994120284
const char **azCol; /* Array of column names for fts3 table */
120285
+ int bFts4; /* True to allow FTS4-only syntax */
119995120286
int nCol; /* Number of entries in azCol[] */
119996120287
int iDefaultCol; /* Default column to query */
119997120288
int isNot; /* True if getNextNode() sees a unary - */
119998120289
sqlite3_context *pCtx; /* Write error message here */
119999120290
int nNest; /* Number of nested brackets */
@@ -120077,13 +120368,25 @@
120077120368
120078120369
if( iEnd<n && z[iEnd]=='*' ){
120079120370
pRet->pPhrase->aToken[0].isPrefix = 1;
120080120371
iEnd++;
120081120372
}
120082
- if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
120083
- pParse->isNot = 1;
120373
+
120374
+ while( 1 ){
120375
+ if( !sqlite3_fts3_enable_parentheses
120376
+ && iStart>0 && z[iStart-1]=='-'
120377
+ ){
120378
+ pParse->isNot = 1;
120379
+ iStart--;
120380
+ }else if( pParse->bFts4 && iStart>0 && z[iStart-1]=='^' ){
120381
+ pRet->pPhrase->aToken[0].bFirst = 1;
120382
+ iStart--;
120383
+ }else{
120384
+ break;
120385
+ }
120084120386
}
120387
+
120085120388
}
120086120389
nConsumed = iEnd;
120087120390
}
120088120391
120089120392
pModule->xClose(pCursor);
@@ -120178,10 +120481,11 @@
120178120481
memcpy(&zTemp[nTemp], zByte, nByte);
120179120482
nTemp += nByte;
120180120483
120181120484
pToken->n = nByte;
120182120485
pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
120486
+ pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
120183120487
nToken = ii+1;
120184120488
}
120185120489
}
120186120490
120187120491
pModule->xClose(pCursor);
@@ -120629,10 +120933,11 @@
120629120933
** match any table column.
120630120934
*/
120631120935
SQLITE_PRIVATE int sqlite3Fts3ExprParse(
120632120936
sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
120633120937
char **azCol, /* Array of column names for fts3 table */
120938
+ int bFts4, /* True to allow FTS4-only syntax */
120634120939
int nCol, /* Number of entries in azCol[] */
120635120940
int iDefaultCol, /* Default column to query */
120636120941
const char *z, int n, /* Text of MATCH query */
120637120942
Fts3Expr **ppExpr /* OUT: Parsed query structure */
120638120943
){
@@ -120642,10 +120947,11 @@
120642120947
sParse.pTokenizer = pTokenizer;
120643120948
sParse.azCol = (const char **)azCol;
120644120949
sParse.nCol = nCol;
120645120950
sParse.iDefaultCol = iDefaultCol;
120646120951
sParse.nNest = 0;
120952
+ sParse.bFts4 = bFts4;
120647120953
if( z==0 ){
120648120954
*ppExpr = 0;
120649120955
return SQLITE_OK;
120650120956
}
120651120957
if( n<0 ){
@@ -120831,11 +121137,11 @@
120831121137
for(ii=0; ii<nCol; ii++){
120832121138
azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]);
120833121139
}
120834121140
120835121141
rc = sqlite3Fts3ExprParse(
120836
- pTokenizer, azCol, nCol, nCol, zExpr, nExpr, &pExpr
121142
+ pTokenizer, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
120837121143
);
120838121144
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
120839121145
sqlite3_result_error(context, "Error parsing expression", -1);
120840121146
}else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
120841121147
sqlite3_result_error_nomem(context);
@@ -122878,11 +123184,11 @@
122878123184
/* 2 */ "DELETE FROM %Q.'%q_content'",
122879123185
/* 3 */ "DELETE FROM %Q.'%q_segments'",
122880123186
/* 4 */ "DELETE FROM %Q.'%q_segdir'",
122881123187
/* 5 */ "DELETE FROM %Q.'%q_docsize'",
122882123188
/* 6 */ "DELETE FROM %Q.'%q_stat'",
122883
-/* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?",
123189
+/* 7 */ "SELECT %s WHERE rowid=?",
122884123190
/* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1",
122885123191
/* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)",
122886123192
/* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
122887123193
/* 11 */ "INSERT INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)",
122888123194
@@ -122920,11 +123226,11 @@
122920123226
if( !pStmt ){
122921123227
char *zSql;
122922123228
if( eStmt==SQL_CONTENT_INSERT ){
122923123229
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
122924123230
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
122925
- zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName);
123231
+ zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
122926123232
}else{
122927123233
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
122928123234
}
122929123235
if( !zSql ){
122930123236
rc = SQLITE_NOMEM;
@@ -123031,21 +123337,28 @@
123031123337
** We try to avoid this because if FTS3 returns any error when committing
123032123338
** a transaction, the whole transaction will be rolled back. And this is
123033123339
** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can
123034123340
** still happen if the user reads data directly from the %_segments or
123035123341
** %_segdir tables instead of going through FTS3 though.
123342
+**
123343
+** This reasoning does not apply to a content=xxx table.
123036123344
*/
123037123345
SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){
123038123346
int rc; /* Return code */
123039123347
sqlite3_stmt *pStmt; /* Statement used to obtain lock */
123040123348
123041
- rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
123042
- if( rc==SQLITE_OK ){
123043
- sqlite3_bind_null(pStmt, 1);
123044
- sqlite3_step(pStmt);
123045
- rc = sqlite3_reset(pStmt);
123349
+ if( p->zContentTbl==0 ){
123350
+ rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
123351
+ if( rc==SQLITE_OK ){
123352
+ sqlite3_bind_null(pStmt, 1);
123353
+ sqlite3_step(pStmt);
123354
+ rc = sqlite3_reset(pStmt);
123355
+ }
123356
+ }else{
123357
+ rc = SQLITE_OK;
123046123358
}
123359
+
123047123360
return rc;
123048123361
}
123049123362
123050123363
/*
123051123364
** Set *ppStmt to a statement handle that may be used to iterate through
@@ -123401,10 +123714,22 @@
123401123714
sqlite3_value **apVal, /* Array of values to insert */
123402123715
sqlite3_int64 *piDocid /* OUT: Docid for row just inserted */
123403123716
){
123404123717
int rc; /* Return code */
123405123718
sqlite3_stmt *pContentInsert; /* INSERT INTO %_content VALUES(...) */
123719
+
123720
+ if( p->zContentTbl ){
123721
+ sqlite3_value *pRowid = apVal[p->nColumn+3];
123722
+ if( sqlite3_value_type(pRowid)==SQLITE_NULL ){
123723
+ pRowid = apVal[1];
123724
+ }
123725
+ if( sqlite3_value_type(pRowid)!=SQLITE_INTEGER ){
123726
+ return SQLITE_CONSTRAINT;
123727
+ }
123728
+ *piDocid = sqlite3_value_int64(pRowid);
123729
+ return SQLITE_OK;
123730
+ }
123406123731
123407123732
/* Locate the statement handle used to insert data into the %_content
123408123733
** table. The SQL for this statement is:
123409123734
**
123410123735
** INSERT INTO %_content VALUES(?, ?, ?, ...)
@@ -123452,18 +123777,20 @@
123452123777
123453123778
/*
123454123779
** Remove all data from the FTS3 table. Clear the hash table containing
123455123780
** pending terms.
123456123781
*/
123457
-static int fts3DeleteAll(Fts3Table *p){
123782
+static int fts3DeleteAll(Fts3Table *p, int bContent){
123458123783
int rc = SQLITE_OK; /* Return code */
123459123784
123460123785
/* Discard the contents of the pending-terms hash table. */
123461123786
sqlite3Fts3PendingTermsClear(p);
123462123787
123463
- /* Delete everything from the %_content, %_segments and %_segdir tables. */
123464
- fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
123788
+ /* Delete everything from the shadow tables. Except, leave %_content as
123789
+ ** is if bContent is false. */
123790
+ assert( p->zContentTbl==0 || bContent==0 );
123791
+ if( bContent ) fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
123465123792
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0);
123466123793
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0);
123467123794
if( p->bHasDocsize ){
123468123795
fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0);
123469123796
}
@@ -124747,16 +125074,22 @@
124747125074
** error occurs, an SQLite error code is returned.
124748125075
*/
124749125076
static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
124750125077
sqlite3_stmt *pStmt;
124751125078
int rc;
124752
- rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
124753
- if( rc==SQLITE_OK ){
124754
- if( SQLITE_ROW==sqlite3_step(pStmt) ){
124755
- *pisEmpty = sqlite3_column_int(pStmt, 0);
125079
+ if( p->zContentTbl ){
125080
+ /* If using the content=xxx option, assume the table is never empty */
125081
+ *pisEmpty = 0;
125082
+ rc = SQLITE_OK;
125083
+ }else{
125084
+ rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
125085
+ if( rc==SQLITE_OK ){
125086
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
125087
+ *pisEmpty = sqlite3_column_int(pStmt, 0);
125088
+ }
125089
+ rc = sqlite3_reset(pStmt);
124756125090
}
124757
- rc = sqlite3_reset(pStmt);
124758125091
}
124759125092
return rc;
124760125093
}
124761125094
124762125095
/*
@@ -125104,10 +125437,11 @@
125104125437
int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY);
125105125438
int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS);
125106125439
int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
125107125440
int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX);
125108125441
int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN);
125442
+ int isFirst = (pCsr->pFilter->flags & FTS3_SEGMENT_FIRST);
125109125443
125110125444
Fts3SegReader **apSegment = pCsr->apSegment;
125111125445
int nSegment = pCsr->nSegment;
125112125446
Fts3SegFilter *pFilter = pCsr->pFilter;
125113125447
int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = (
@@ -125163,10 +125497,11 @@
125163125497
}
125164125498
125165125499
assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
125166125500
if( nMerge==1
125167125501
&& !isIgnoreEmpty
125502
+ && !isFirst
125168125503
&& (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0)
125169125504
){
125170125505
pCsr->nDoclist = apSegment[0]->nDoclist;
125171125506
if( fts3SegReaderIsPending(apSegment[0]) ){
125172125507
rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist);
@@ -125228,16 +125563,28 @@
125228125563
if( !aNew ){
125229125564
return SQLITE_NOMEM;
125230125565
}
125231125566
pCsr->aBuffer = aNew;
125232125567
}
125233
- nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
125234
- iPrev = iDocid;
125235
- if( isRequirePos ){
125236
- memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
125237
- nDoclist += nList;
125238
- pCsr->aBuffer[nDoclist++] = '\0';
125568
+
125569
+ if( isFirst ){
125570
+ char *a = &pCsr->aBuffer[nDoclist];
125571
+ int nWrite;
125572
+
125573
+ nWrite = sqlite3Fts3FirstFilter(iDelta, pList, nList, a);
125574
+ if( nWrite ){
125575
+ iPrev = iDocid;
125576
+ nDoclist += nWrite;
125577
+ }
125578
+ }else{
125579
+ nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
125580
+ iPrev = iDocid;
125581
+ if( isRequirePos ){
125582
+ memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
125583
+ nDoclist += nList;
125584
+ pCsr->aBuffer[nDoclist++] = '\0';
125585
+ }
125239125586
}
125240125587
}
125241125588
125242125589
fts3SegReaderSort(apSegment, nMerge, j, xCmp);
125243125590
}
@@ -125409,13 +125756,13 @@
125409125756
** Insert the sizes (in tokens) for each column of the document
125410125757
** with docid equal to p->iPrevDocid. The sizes are encoded as
125411125758
** a blob of varints.
125412125759
*/
125413125760
static void fts3InsertDocsize(
125414
- int *pRC, /* Result code */
125415
- Fts3Table *p, /* Table into which to insert */
125416
- u32 *aSz /* Sizes of each column */
125761
+ int *pRC, /* Result code */
125762
+ Fts3Table *p, /* Table into which to insert */
125763
+ u32 *aSz /* Sizes of each column, in tokens */
125417125764
){
125418125765
char *pBlob; /* The BLOB encoding of the document size */
125419125766
int nBlob; /* Number of bytes in the BLOB */
125420125767
sqlite3_stmt *pStmt; /* Statement used to insert the encoding */
125421125768
int rc; /* Result code from subfunctions */
@@ -125532,10 +125879,90 @@
125532125879
sqlite3Fts3SegmentsClose(p);
125533125880
sqlite3Fts3PendingTermsClear(p);
125534125881
125535125882
return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
125536125883
}
125884
+
125885
+/*
125886
+** This function is called when the user executes the following statement:
125887
+**
125888
+** INSERT INTO <tbl>(<tbl>) VALUES('rebuild');
125889
+**
125890
+** The entire FTS index is discarded and rebuilt. If the table is one
125891
+** created using the content=xxx option, then the new index is based on
125892
+** the current contents of the xxx table. Otherwise, it is rebuilt based
125893
+** on the contents of the %_content table.
125894
+*/
125895
+static int fts3DoRebuild(Fts3Table *p){
125896
+ int rc; /* Return Code */
125897
+
125898
+ rc = fts3DeleteAll(p, 0);
125899
+ if( rc==SQLITE_OK ){
125900
+ u32 *aSz = 0;
125901
+ u32 *aSzIns = 0;
125902
+ u32 *aSzDel = 0;
125903
+ sqlite3_stmt *pStmt = 0;
125904
+ int nEntry = 0;
125905
+
125906
+ /* Compose and prepare an SQL statement to loop through the content table */
125907
+ char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist);
125908
+ if( !zSql ){
125909
+ rc = SQLITE_NOMEM;
125910
+ }else{
125911
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
125912
+ sqlite3_free(zSql);
125913
+ }
125914
+
125915
+ if( rc==SQLITE_OK ){
125916
+ int nByte = sizeof(u32) * (p->nColumn+1)*3;
125917
+ aSz = (u32 *)sqlite3_malloc(nByte);
125918
+ if( aSz==0 ){
125919
+ rc = SQLITE_NOMEM;
125920
+ }else{
125921
+ memset(aSz, 0, nByte);
125922
+ aSzIns = &aSz[p->nColumn+1];
125923
+ aSzDel = &aSzIns[p->nColumn+1];
125924
+ }
125925
+ }
125926
+
125927
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
125928
+ int iCol;
125929
+ rc = fts3PendingTermsDocid(p, sqlite3_column_int64(pStmt, 0));
125930
+ aSz[p->nColumn] = 0;
125931
+ for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
125932
+ const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
125933
+ rc = fts3PendingTermsAdd(p, z, iCol, &aSz[iCol]);
125934
+ aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
125935
+ }
125936
+ if( p->bHasDocsize ){
125937
+ fts3InsertDocsize(&rc, p, aSz);
125938
+ }
125939
+ if( rc!=SQLITE_OK ){
125940
+ sqlite3_finalize(pStmt);
125941
+ pStmt = 0;
125942
+ }else{
125943
+ nEntry++;
125944
+ for(iCol=0; iCol<=p->nColumn; iCol++){
125945
+ aSzIns[iCol] += aSz[iCol];
125946
+ }
125947
+ }
125948
+ }
125949
+ if( p->bHasStat ){
125950
+ fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nEntry);
125951
+ }
125952
+ sqlite3_free(aSz);
125953
+
125954
+ if( pStmt ){
125955
+ int rc2 = sqlite3_finalize(pStmt);
125956
+ if( rc==SQLITE_OK ){
125957
+ rc = rc2;
125958
+ }
125959
+ }
125960
+ }
125961
+
125962
+ return rc;
125963
+}
125537125964
125538125965
/*
125539125966
** Handle a 'special' INSERT of the form:
125540125967
**
125541125968
** "INSERT INTO tbl(tbl) VALUES(<expr>)"
@@ -125550,10 +125977,12 @@
125550125977
125551125978
if( !zVal ){
125552125979
return SQLITE_NOMEM;
125553125980
}else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
125554125981
rc = fts3DoOptimize(p, 0);
125982
+ }else if( nVal==7 && 0==sqlite3_strnicmp(zVal, "rebuild", 7) ){
125983
+ rc = fts3DoRebuild(p);
125555125984
#ifdef SQLITE_TEST
125556125985
}else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
125557125986
p->nNodeSize = atoi(&zVal[9]);
125558125987
rc = SQLITE_OK;
125559125988
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
@@ -125630,10 +126059,11 @@
125630126059
pTC->pTokenizer = pT;
125631126060
rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
125632126061
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
125633126062
Fts3PhraseToken *pPT = pDef->pToken;
125634126063
if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
126064
+ && (pPT->bFirst==0 || iPos==0)
125635126065
&& (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
125636126066
&& (0==memcmp(zToken, pPT->z, pPT->n))
125637126067
){
125638126068
fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
125639126069
}
@@ -125721,18 +126151,22 @@
125721126151
if( rc==SQLITE_OK ){
125722126152
if( isEmpty ){
125723126153
/* Deleting this row means the whole table is empty. In this case
125724126154
** delete the contents of all three tables and throw away any
125725126155
** data in the pendingTerms hash table. */
125726
- rc = fts3DeleteAll(p);
126156
+ rc = fts3DeleteAll(p, 1);
125727126157
*pnDoc = *pnDoc - 1;
125728126158
}else{
125729126159
sqlite3_int64 iRemove = sqlite3_value_int64(pRowid);
125730126160
rc = fts3PendingTermsDocid(p, iRemove);
125731126161
fts3DeleteTerms(&rc, p, pRowid, aSzDel);
125732
- fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
125733
- if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
126162
+ if( p->zContentTbl==0 ){
126163
+ fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
126164
+ if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
126165
+ }else{
126166
+ *pnDoc = *pnDoc - 1;
126167
+ }
125734126168
if( p->bHasDocsize ){
125735126169
fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
125736126170
}
125737126171
}
125738126172
}
@@ -125788,11 +126222,11 @@
125788126222
** should be deleted from the database before inserting the new row. Or,
125789126223
** if the on-conflict mode is other than REPLACE, then this method must
125790126224
** detect the conflict and return SQLITE_CONSTRAINT before beginning to
125791126225
** modify the database file.
125792126226
*/
125793
- if( nArg>1 ){
126227
+ if( nArg>1 && p->zContentTbl==0 ){
125794126228
/* Find the value object that holds the new rowid value. */
125795126229
sqlite3_value *pNewRowid = apVal[3+p->nColumn];
125796126230
if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
125797126231
pNewRowid = apVal[1];
125798126232
}
@@ -125839,11 +126273,13 @@
125839126273
125840126274
/* If this is an INSERT or UPDATE operation, insert the new record. */
125841126275
if( nArg>1 && rc==SQLITE_OK ){
125842126276
if( bInsertDone==0 ){
125843126277
rc = fts3InsertData(p, apVal, pRowid);
125844
- if( rc==SQLITE_CONSTRAINT ) rc = FTS_CORRUPT_VTAB;
126278
+ if( rc==SQLITE_CONSTRAINT && p->zContentTbl==0 ){
126279
+ rc = FTS_CORRUPT_VTAB;
126280
+ }
125845126281
}
125846126282
if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){
125847126283
rc = fts3PendingTermsDocid(p, *pRowid);
125848126284
}
125849126285
if( rc==SQLITE_OK ){
@@ -126259,10 +126695,11 @@
126259126695
pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
126260126696
if( pCsr ){
126261126697
int iFirst = 0;
126262126698
pPhrase->pList = pCsr;
126263126699
fts3GetDeltaPosition(&pCsr, &iFirst);
126700
+ assert( iFirst>=0 );
126264126701
pPhrase->pHead = pCsr;
126265126702
pPhrase->pTail = pCsr;
126266126703
pPhrase->iHead = iFirst;
126267126704
pPhrase->iTail = iFirst;
126268126705
}else{
@@ -127300,11 +127737,11 @@
127300127737
}
127301127738
}
127302127739
127303127740
if( !pTerm ){
127304127741
/* All offsets for this column have been gathered. */
127305
- break;
127742
+ rc = SQLITE_DONE;
127306127743
}else{
127307127744
assert( iCurrent<=iMinPos );
127308127745
if( 0==(0xFE&*pTerm->pList) ){
127309127746
pTerm->pList = 0;
127310127747
}else{
@@ -127317,11 +127754,11 @@
127317127754
char aBuffer[64];
127318127755
sqlite3_snprintf(sizeof(aBuffer), aBuffer,
127319127756
"%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart
127320127757
);
127321127758
rc = fts3StringAppend(&res, aBuffer, -1);
127322
- }else if( rc==SQLITE_DONE ){
127759
+ }else if( rc==SQLITE_DONE && pTab->zContentTbl==0 ){
127323127760
rc = FTS_CORRUPT_VTAB;
127324127761
}
127325127762
}
127326127763
}
127327127764
if( rc==SQLITE_DONE ){
127328127765
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -314,17 +314,10 @@
314 #endif
315 #ifdef HAVE_INTTYPES_H
316 #include <inttypes.h>
317 #endif
318
319 /*
320 ** The number of samples of an index that SQLite takes in order to
321 ** construct a histogram of the table content when running ANALYZE
322 ** and with SQLITE_ENABLE_STAT2
323 */
324 #define SQLITE_INDEX_SAMPLES 10
325
326 /*
327 ** The following macros are used to cast pointers to integers and
328 ** integers to pointers. The way you do this varies from one compiler
329 ** to the next, so we have developed the following set of #if statements
330 ** to generate appropriate macros for a wide range of compilers.
@@ -656,11 +649,11 @@
656 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
657 ** [sqlite_version()] and [sqlite_source_id()].
658 */
659 #define SQLITE_VERSION "3.7.9"
660 #define SQLITE_VERSION_NUMBER 3007009
661 #define SQLITE_SOURCE_ID "2011-10-15 00:16:30 39408702a989f907261c298bf0947f3e68bd10fe"
662
663 /*
664 ** CAPI3REF: Run-Time Library Version Numbers
665 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
666 **
@@ -1951,12 +1944,12 @@
1951 ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
1952 ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
1953 ** allocator is engaged to handle all of SQLites memory allocation needs.
1954 ** The first pointer (the memory pointer) must be aligned to an 8-byte
1955 ** boundary or subsequent behavior of SQLite will be undefined.
1956 ** The minimum allocation size is capped at 2^12. Reasonable values
1957 ** for the minimum allocation size are 2^5 through 2^8.</dd>
1958 **
1959 ** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
1960 ** <dd> ^(This option takes a single argument which is a pointer to an
1961 ** instance of the [sqlite3_mutex_methods] structure. The argument specifies
1962 ** alternative low-level mutex routines to be used in place
@@ -8792,10 +8785,11 @@
8792 SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
8793 SQLITE_PRIVATE int sqlite3PagerNosync(Pager*);
8794 SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
8795 SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
8796 SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
 
8797
8798 /* Functions used to truncate the database file. */
8799 SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
8800
8801 #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
@@ -10189,12 +10183,13 @@
10189 IndexSample *aSample; /* Samples of the left-most key */
10190 #endif
10191 };
10192
10193 /*
10194 ** Each sample stored in the sqlite_stat2 table is represented in memory
10195 ** using a structure of this type.
 
10196 */
10197 struct IndexSample {
10198 union {
10199 char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
10200 double r; /* Value if eType is SQLITE_FLOAT */
@@ -12293,13 +12288,10 @@
12293 "ENABLE_OVERSIZE_CELL_CHECK",
12294 #endif
12295 #ifdef SQLITE_ENABLE_RTREE
12296 "ENABLE_RTREE",
12297 #endif
12298 #ifdef SQLITE_ENABLE_STAT2
12299 "ENABLE_STAT2",
12300 #endif
12301 #ifdef SQLITE_ENABLE_STAT3
12302 "ENABLE_STAT3",
12303 #endif
12304 #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
12305 "ENABLE_UNLOCK_NOTIFY",
@@ -12996,10 +12988,11 @@
12996 SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
12997 SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
12998 SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*);
12999 SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
13000 SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem);
 
13001
13002 #ifdef SQLITE_OMIT_MERGE_SORT
13003 # define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK
13004 # define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK
13005 # define sqlite3VdbeSorterClose(Y,Z)
@@ -20969,11 +20962,11 @@
20969 }else if( *z=='+' ){
20970 z+=incr;
20971 }
20972 /* copy digits to exponent */
20973 while( z<zEnd && sqlite3Isdigit(*z) ){
20974 e = e*10 + (*z - '0');
20975 z+=incr;
20976 eValid = 1;
20977 }
20978 }
20979
@@ -21020,10 +21013,16 @@
21020 result /= 1.0e+308;
21021 }else{
21022 result = s * scale;
21023 result *= 1.0e+308;
21024 }
 
 
 
 
 
 
21025 }else{
21026 /* 1.0e+22 is the largest power of 10 than can be
21027 ** represented exactly. */
21028 while( e%22 ) { scale *= 1.0e+1; e -= 1; }
21029 while( e>0 ) { scale *= 1.0e+22; e -= 22; }
@@ -29477,17 +29476,17 @@
29477 ** "<path to db>-journal"
29478 ** "<path to db>-wal"
29479 ** "<path to db>-journalNN"
29480 ** "<path to db>-walNN"
29481 **
29482 ** where NN is a 4 digit decimal number. The NN naming schemes are
29483 ** used by the test_multiplex.c module.
29484 */
29485 nDb = sqlite3Strlen30(zPath) - 1;
29486 #ifdef SQLITE_ENABLE_8_3_NAMES
29487 while( nDb>0 && zPath[nDb]!='-' && zPath[nDb]!='/' ) nDb--;
29488 if( nDb==0 || zPath[nDb]=='/' ) return SQLITE_OK;
29489 #else
29490 while( zPath[nDb]!='-' ){
29491 assert( nDb>0 );
29492 assert( zPath[nDb]!='\n' );
29493 nDb--;
@@ -44157,10 +44156,17 @@
44157 pPager->pWal = 0;
44158 }
44159 }
44160 return rc;
44161 }
 
 
 
 
 
 
 
44162
44163 #ifdef SQLITE_HAS_CODEC
44164 /*
44165 ** This function is called by the wal module when writing page content
44166 ** into the log file.
@@ -57000,10 +57006,12 @@
57000 sqlite3_backup_step(&b, 0x7FFFFFFF);
57001 assert( b.rc!=SQLITE_OK );
57002 rc = sqlite3_backup_finish(&b);
57003 if( rc==SQLITE_OK ){
57004 pTo->pBt->pageSizeFixed = 0;
 
 
57005 }
57006
57007 assert( sqlite3BtreeIsInTrans(pTo)==0 );
57008 sqlite3BtreeLeave(pFrom);
57009 sqlite3BtreeLeave(pTo);
@@ -60474,10 +60482,34 @@
60474 ** in p->rc. This routine sets that result back to SQLITE_OK.
60475 */
60476 SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){
60477 p->rc = SQLITE_OK;
60478 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60479
60480 /*
60481 ** Clean up a VDBE after execution but do not delete the VDBE just yet.
60482 ** Write any error messages into *pzErrMsg. Return the result code.
60483 **
@@ -60502,22 +60534,13 @@
60502 ** and error message from the VDBE into the main database structure. But
60503 ** if the VDBE has just been set to run but has not actually executed any
60504 ** instructions yet, leave the main database error information unchanged.
60505 */
60506 if( p->pc>=0 ){
60507 if( p->zErrMsg ){
60508 sqlite3BeginBenignMalloc();
60509 sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT);
60510 sqlite3EndBenignMalloc();
60511 db->errCode = p->rc;
60512 sqlite3DbFree(db, p->zErrMsg);
60513 p->zErrMsg = 0;
60514 }else if( p->rc ){
60515 sqlite3Error(db, p->rc, 0);
60516 }else{
60517 sqlite3Error(db, SQLITE_OK, 0);
60518 }
60519 if( p->runOnlyOnce ) p->expired = 1;
60520 }else if( p->rc && p->expired ){
60521 /* The expired flag was set on the VDBE before the first call
60522 ** to sqlite3_step(). For consistency (since sqlite3_step() was
60523 ** called), set the database error in this case as well.
@@ -61859,11 +61882,11 @@
61859 if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
61860 /* If this statement was prepared using sqlite3_prepare_v2(), and an
61861 ** error has occured, then return the error code in p->rc to the
61862 ** caller. Set the error code in the database handle to the same value.
61863 */
61864 rc = db->errCode = p->rc;
61865 }
61866 return (rc&db->errMask);
61867 }
61868
61869 /*
@@ -67759,13 +67782,12 @@
67759
67760 assert( pOp->p1>=0 && pOp->p1<p->nCursor );
67761 u.bn.pC = p->apCsr[pOp->p1];
67762 assert( u.bn.pC!=0 );
67763 u.bn.pCrsr = u.bn.pC->pCursor;
67764 if( NEVER(u.bn.pCrsr==0) ){
67765 u.bn.res = 1;
67766 }else{
67767 rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res);
67768 }
67769 u.bn.pC->nullRow = (u8)u.bn.res;
67770 u.bn.pC->deferredMoveto = 0;
67771 u.bn.pC->rowidIsValid = 0;
@@ -68962,11 +68984,11 @@
68962
68963 /* Do not allow a transition to journal_mode=WAL for a database
68964 ** in temporary storage or if the VFS does not support shared memory
68965 */
68966 if( u.ch.eNew==PAGER_JOURNALMODE_WAL
68967 && (u.ch.zFilename[0]==0 /* Temp file */
68968 || !sqlite3PagerWalSupported(u.ch.pPager)) /* No shared-memory support */
68969 ){
68970 u.ch.eNew = u.ch.eOld;
68971 }
68972
@@ -69397,14 +69419,19 @@
69397 u.co.pName = &aMem[pOp->p1];
69398 assert( u.co.pVtab->pModule->xRename );
69399 assert( memIsValid(u.co.pName) );
69400 REGISTER_TRACE(pOp->p1, u.co.pName);
69401 assert( u.co.pName->flags & MEM_Str );
69402 rc = u.co.pVtab->pModule->xRename(u.co.pVtab, u.co.pName->z);
69403 importVtabErrMsg(p, u.co.pVtab);
69404 p->expired = 0;
69405
 
 
 
 
 
69406 break;
69407 }
69408 #endif
69409
69410 #ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -71758,10 +71785,28 @@
71758 ExprSetProperty(pExpr, EP_Static);
71759 sqlite3ExprDelete(db, pExpr);
71760 memcpy(pExpr, pDup, sizeof(*pExpr));
71761 sqlite3DbFree(db, pDup);
71762 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71763
71764 /*
71765 ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
71766 ** that name in the set of source tables in pSrcList and make the pExpr
71767 ** expression node refer back to that source column. The following changes
@@ -71850,38 +71895,25 @@
71850 pSchema = pTab->pSchema;
71851 pMatch = pItem;
71852 }
71853 for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
71854 if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
71855 IdList *pUsing;
 
 
 
 
 
 
 
71856 cnt++;
71857 pExpr->iTable = pItem->iCursor;
71858 pExpr->pTab = pTab;
71859 pMatch = pItem;
71860 pSchema = pTab->pSchema;
71861 /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
71862 pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
71863 if( i<pSrcList->nSrc-1 ){
71864 if( pItem[1].jointype & JT_NATURAL ){
71865 /* If this match occurred in the left table of a natural join,
71866 ** then skip the right table to avoid a duplicate match */
71867 pItem++;
71868 i++;
71869 }else if( (pUsing = pItem[1].pUsing)!=0 ){
71870 /* If this match occurs on a column that is in the USING clause
71871 ** of a join, skip the search of the right table of the join
71872 ** to avoid a duplicate match there. */
71873 int k;
71874 for(k=0; k<pUsing->nId; k++){
71875 if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){
71876 pItem++;
71877 i++;
71878 break;
71879 }
71880 }
71881 }
71882 }
71883 break;
71884 }
71885 }
71886 }
71887 }
@@ -77594,20 +77626,20 @@
77594 #ifndef SQLITE_OMIT_ANALYZE
77595
77596 /*
77597 ** This routine generates code that opens the sqlite_stat1 table for
77598 ** writing with cursor iStatCur. If the library was built with the
77599 ** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is
77600 ** opened for writing using cursor (iStatCur+1)
77601 **
77602 ** If the sqlite_stat1 tables does not previously exist, it is created.
77603 ** Similarly, if the sqlite_stat2 table does not exist and the library
77604 ** is compiled with SQLITE_ENABLE_STAT2 defined, it is created.
77605 **
77606 ** Argument zWhere may be a pointer to a buffer containing a table name,
77607 ** or it may be a NULL pointer. If it is not NULL, then all entries in
77608 ** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated
77609 ** with the named table are deleted. If zWhere==0, then code is generated
77610 ** to delete all stat table entries.
77611 */
77612 static void openStatTable(
77613 Parse *pParse, /* Parsing context */
@@ -81388,31 +81420,28 @@
81388 }
81389 #endif
81390 }
81391
81392 /*
81393 ** Remove entries from the sqlite_stat1 and sqlite_stat2 tables
81394 ** after a DROP INDEX or DROP TABLE command.
81395 */
81396 static void sqlite3ClearStatTables(
81397 Parse *pParse, /* The parsing context */
81398 int iDb, /* The database number */
81399 const char *zType, /* "idx" or "tbl" */
81400 const char *zName /* Name of index or table */
81401 ){
81402 static const char *azStatTab[] = {
81403 "sqlite_stat1",
81404 "sqlite_stat2",
81405 "sqlite_stat3",
81406 };
81407 int i;
81408 const char *zDbName = pParse->db->aDb[iDb].zName;
81409 for(i=0; i<ArraySize(azStatTab); i++){
81410 if( sqlite3FindTable(pParse->db, azStatTab[i], zDbName) ){
 
 
81411 sqlite3NestedParse(pParse,
81412 "DELETE FROM %Q.%s WHERE %s=%Q",
81413 zDbName, azStatTab[i], zType, zName
81414 );
81415 }
81416 }
81417 }
81418
@@ -89234,12 +89263,14 @@
89234 int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
89235 int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
89236 int (*busy_timeout)(sqlite3*,int ms);
89237 int (*changes)(sqlite3*);
89238 int (*close)(sqlite3*);
89239 int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
89240 int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
 
 
89241 const void * (*column_blob)(sqlite3_stmt*,int iCol);
89242 int (*column_bytes)(sqlite3_stmt*,int iCol);
89243 int (*column_bytes16)(sqlite3_stmt*,int iCol);
89244 int (*column_count)(sqlite3_stmt*pStmt);
89245 const char * (*column_database_name)(sqlite3_stmt*,int);
@@ -89260,14 +89291,22 @@
89260 int (*column_type)(sqlite3_stmt*,int iCol);
89261 sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
89262 void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
89263 int (*complete)(const char*sql);
89264 int (*complete16)(const void*sql);
89265 int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
89266 int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*));
89267 int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
89268 int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
 
 
 
 
 
 
 
 
89269 int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
89270 int (*data_count)(sqlite3_stmt*pStmt);
89271 sqlite3 * (*db_handle)(sqlite3_stmt*);
89272 int (*declare_vtab)(sqlite3*,const char*);
89273 int (*enable_shared_cache)(int);
@@ -89308,20 +89347,23 @@
89308 void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
89309 void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
89310 void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
89311 void (*result_value)(sqlite3_context*,sqlite3_value*);
89312 void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
89313 int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*);
 
89314 void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
89315 char * (*snprintf)(int,char*,const char*,...);
89316 int (*step)(sqlite3_stmt*);
89317 int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*);
 
89318 void (*thread_cleanup)(void);
89319 int (*total_changes)(sqlite3*);
89320 void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
89321 int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
89322 void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*);
 
89323 void * (*user_data)(sqlite3_context*);
89324 const void * (*value_blob)(sqlite3_value*);
89325 int (*value_bytes)(sqlite3_value*);
89326 int (*value_bytes16)(sqlite3_value*);
89327 double (*value_double)(sqlite3_value*);
@@ -89339,19 +89381,23 @@
89339 /* Added by 3.3.13 */
89340 int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
89341 int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
89342 int (*clear_bindings)(sqlite3_stmt*);
89343 /* Added by 3.4.1 */
89344 int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *));
 
89345 /* Added by 3.5.0 */
89346 int (*bind_zeroblob)(sqlite3_stmt*,int,int);
89347 int (*blob_bytes)(sqlite3_blob*);
89348 int (*blob_close)(sqlite3_blob*);
89349 int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**);
 
89350 int (*blob_read)(sqlite3_blob*,void*,int,int);
89351 int (*blob_write)(sqlite3_blob*,const void*,int,int);
89352 int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*));
 
 
89353 int (*file_control)(sqlite3*,const char*,int,void*);
89354 sqlite3_int64 (*memory_highwater)(int);
89355 sqlite3_int64 (*memory_used)(void);
89356 sqlite3_mutex *(*mutex_alloc)(int);
89357 void (*mutex_enter)(sqlite3_mutex*);
@@ -89383,11 +89429,15 @@
89383 int (*backup_pagecount)(sqlite3_backup*);
89384 int (*backup_remaining)(sqlite3_backup*);
89385 int (*backup_step)(sqlite3_backup*,int);
89386 const char *(*compileoption_get)(int);
89387 int (*compileoption_used)(const char*);
89388 int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*));
 
 
 
 
89389 int (*db_config)(sqlite3*,int,...);
89390 sqlite3_mutex *(*db_mutex)(sqlite3*);
89391 int (*db_status)(sqlite3*,int,int*,int*,int);
89392 int (*extended_errcode)(sqlite3*);
89393 void (*log)(int,const char*,...);
@@ -100469,11 +100519,11 @@
100469 if( db->aVTrans ){
100470 int i;
100471 for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
100472 VTable *pVTab = db->aVTrans[i];
100473 const sqlite3_module *pMod = pVTab->pMod->pModule;
100474 if( pMod->iVersion>=2 ){
100475 int (*xMethod)(sqlite3_vtab *, int);
100476 switch( op ){
100477 case SAVEPOINT_BEGIN:
100478 xMethod = pMod->xSavepoint;
100479 pVTab->iSavepoint = iSavepoint+1;
@@ -100484,11 +100534,11 @@
100484 default:
100485 xMethod = pMod->xRelease;
100486 break;
100487 }
100488 if( xMethod && pVTab->iSavepoint>iSavepoint ){
100489 rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint);
100490 }
100491 }
100492 }
100493 }
100494 return rc;
@@ -101351,11 +101401,11 @@
101351 int iCol = pRight->iColumn;
101352 pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE);
101353 if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
101354 z = (char *)sqlite3_value_text(pVal);
101355 }
101356 sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-31526-56213 */
101357 assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
101358 }else if( op==TK_STRING ){
101359 z = pRight->u.zToken;
101360 }
101361 if( z ){
@@ -101369,11 +101419,11 @@
101369 pPrefix = sqlite3Expr(db, TK_STRING, z);
101370 if( pPrefix ) pPrefix->u.zToken[cnt] = 0;
101371 *ppPrefix = pPrefix;
101372 if( op==TK_VARIABLE ){
101373 Vdbe *v = pParse->pVdbe;
101374 sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-31526-56213 */
101375 if( *pisComplete && pRight->u.zToken[1] ){
101376 /* If the rhs of the LIKE expression is a variable, and the current
101377 ** value of the variable means there is no need to invoke the LIKE
101378 ** function, then no OP_Variable will be added to the program.
101379 ** This causes problems for the sqlite3_bind_parameter_name()
@@ -102501,10 +102551,11 @@
102501 tempWC.pParse = pWC->pParse;
102502 tempWC.pMaskSet = pWC->pMaskSet;
102503 tempWC.pOuter = pWC;
102504 tempWC.op = TK_AND;
102505 tempWC.a = pOrTerm;
 
102506 tempWC.nTerm = 1;
102507 bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost);
102508 }else{
102509 continue;
102510 }
@@ -103282,11 +103333,11 @@
103282 ){
103283 if( pExpr->op==TK_VARIABLE
103284 || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
103285 ){
103286 int iVar = pExpr->iColumn;
103287 sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-31526-56213 */
103288 *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
103289 return SQLITE_OK;
103290 }
103291 return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp);
103292 }
@@ -103812,11 +103863,11 @@
103812 ** a table or index. The actual times can vary, with the size of
103813 ** records being an important factor. Both moves and searches are
103814 ** slower with larger records, presumably because fewer records fit
103815 ** on one page and hence more pages have to be fetched.
103816 **
103817 ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do
103818 ** not give us data on the relative sizes of table and index records.
103819 ** So this computation assumes table records are about twice as big
103820 ** as index records
103821 */
103822 if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){
@@ -114528,10 +114579,11 @@
114528 const char *zDb; /* logical database name */
114529 const char *zName; /* virtual table name */
114530 int nColumn; /* number of named columns in virtual table */
114531 char **azColumn; /* column names. malloced */
114532 sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
 
114533
114534 /* Precompiled statements used by the implementation. Each of these
114535 ** statements is run and reset within a single virtual table API call.
114536 */
114537 sqlite3_stmt *aStmt[27];
@@ -114568,11 +114620,11 @@
114568 } *aIndex;
114569 int nMaxPendingData; /* Max pending data before flush to disk */
114570 int nPendingData; /* Current bytes of pending data */
114571 sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
114572
114573 #if defined(SQLITE_DEBUG)
114574 /* State variables used for validating that the transaction control
114575 ** methods of the virtual table are called at appropriate times. These
114576 ** values do not contribution to the FTS computation; they are used for
114577 ** verifying the SQLite core.
114578 */
@@ -114653,10 +114705,11 @@
114653 */
114654 struct Fts3PhraseToken {
114655 char *z; /* Text of the token */
114656 int n; /* Number of bytes in buffer z */
114657 int isPrefix; /* True if token ends with a "*" character */
 
114658
114659 /* Variables above this point are populated when the expression is
114660 ** parsed (by code in fts3_expr.c). Below this point the variables are
114661 ** used when evaluating the expression. */
114662 Fts3DeferredToken *pDeferred; /* Deferred token object for this token */
@@ -114771,10 +114824,11 @@
114771 #define FTS3_SEGMENT_REQUIRE_POS 0x00000001
114772 #define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002
114773 #define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
114774 #define FTS3_SEGMENT_PREFIX 0x00000008
114775 #define FTS3_SEGMENT_SCAN 0x00000010
 
114776
114777 /* Type passed as 4th argument to SegmentReaderIterate() */
114778 struct Fts3SegFilter {
114779 const char *zTerm;
114780 int nTerm;
@@ -114810,12 +114864,12 @@
114810 SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
114811 SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *);
114812 SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64);
114813 SQLITE_PRIVATE void sqlite3Fts3Dequote(char *);
114814 SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
114815
114816 SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
 
114817
114818 /* fts3_tokenizer.c */
114819 SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
114820 SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
114821 SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
@@ -114830,11 +114884,11 @@
114830 );
114831 SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
114832
114833 /* fts3_expr.c */
114834 SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *,
114835 char **, int, int, const char *, int, Fts3Expr **
114836 );
114837 SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *);
114838 #ifdef SQLITE_TEST
114839 SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
114840 SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db);
@@ -115031,10 +115085,11 @@
115031 sqlite3_finalize(p->aStmt[i]);
115032 }
115033 sqlite3_free(p->zSegmentsTbl);
115034 sqlite3_free(p->zReadExprlist);
115035 sqlite3_free(p->zWriteExprlist);
 
115036
115037 /* Invoke the tokenizer destructor to free the tokenizer. */
115038 p->pTokenizer->pModule->xDestroy(p->pTokenizer);
115039
115040 sqlite3_free(p);
@@ -115070,20 +115125,23 @@
115070
115071 /*
115072 ** The xDestroy() virtual table method.
115073 */
115074 static int fts3DestroyMethod(sqlite3_vtab *pVtab){
115075 int rc = SQLITE_OK; /* Return code */
115076 Fts3Table *p = (Fts3Table *)pVtab;
115077 sqlite3 *db = p->db;
 
 
115078
115079 /* Drop the shadow tables */
115080 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", p->zDb, p->zName);
115081 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", p->zDb,p->zName);
115082 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", p->zDb, p->zName);
115083 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", p->zDb, p->zName);
115084 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", p->zDb, p->zName);
 
 
115085
115086 /* If everything has worked, invoke fts3DisconnectMethod() to free the
115087 ** memory associated with the Fts3Table structure and return SQLITE_OK.
115088 ** Otherwise, return an SQLite error code.
115089 */
@@ -115141,27 +115199,31 @@
115141 ** %_stat tables required by FTS4.
115142 */
115143 static int fts3CreateTables(Fts3Table *p){
115144 int rc = SQLITE_OK; /* Return code */
115145 int i; /* Iterator variable */
115146 char *zContentCols; /* Columns of %_content table */
115147 sqlite3 *db = p->db; /* The database connection */
115148
115149 /* Create a list of user columns for the content table */
115150 zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
115151 for(i=0; zContentCols && i<p->nColumn; i++){
115152 char *z = p->azColumn[i];
115153 zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
115154 }
115155 if( zContentCols==0 ) rc = SQLITE_NOMEM;
115156
115157 /* Create the content table */
115158 fts3DbExec(&rc, db,
115159 "CREATE TABLE %Q.'%q_content'(%s)",
115160 p->zDb, p->zName, zContentCols
115161 );
115162 sqlite3_free(zContentCols);
 
 
 
 
 
115163 /* Create other tables */
115164 fts3DbExec(&rc, db,
115165 "CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);",
115166 p->zDb, p->zName
115167 );
@@ -115308,12 +115370,12 @@
115308 }
115309 return zRet;
115310 }
115311
115312 /*
115313 ** Return a list of comma separated SQL expressions that could be used
115314 ** in a SELECT statement such as the following:
115315 **
115316 ** SELECT <list of expressions> FROM %_content AS x ...
115317 **
115318 ** to return the docid, followed by each column of text data in order
115319 ** from left to write. If parameter zFunc is not NULL, then instead of
@@ -115320,11 +115382,11 @@
115320 ** being returned directly each column of text data is passed to an SQL
115321 ** function named zFunc first. For example, if zFunc is "unzip" and the
115322 ** table has the three user-defined columns "a", "b", and "c", the following
115323 ** string is returned:
115324 **
115325 ** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c')"
115326 **
115327 ** The pointer returned points to a buffer allocated by sqlite3_malloc(). It
115328 ** is the responsibility of the caller to eventually free it.
115329 **
115330 ** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and
@@ -115336,20 +115398,32 @@
115336 char *zRet = 0;
115337 char *zFree = 0;
115338 char *zFunction;
115339 int i;
115340
115341 if( !zFunc ){
115342 zFunction = "";
 
 
 
 
 
 
 
 
 
115343 }else{
115344 zFree = zFunction = fts3QuoteId(zFunc);
 
 
 
115345 }
115346 fts3Appendf(pRc, &zRet, "docid");
115347 for(i=0; i<p->nColumn; i++){
115348 fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
115349 }
115350 sqlite3_free(zFree);
115351 return zRet;
115352 }
115353
115354 /*
115355 ** Return a list of N comma separated question marks, where N is the number
@@ -115468,10 +115542,95 @@
115468 }
115469 }
115470
115471 return SQLITE_OK;
115472 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115473
115474 /*
115475 ** This function is the implementation of both the xConnect and xCreate
115476 ** methods of the FTS3 virtual table.
115477 **
@@ -115513,10 +115672,11 @@
115513 int bNoDocsize = 0; /* True to omit %_docsize table */
115514 int bDescIdx = 0; /* True to store descending indexes */
115515 char *zPrefix = 0; /* Prefix parameter value (or NULL) */
115516 char *zCompress = 0; /* compress=? parameter (or NULL) */
115517 char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
 
115518
115519 assert( strlen(argv[0])==4 );
115520 assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
115521 || (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4)
115522 );
@@ -115556,17 +115716,17 @@
115556 /* Check if it is an FTS4 special argument. */
115557 else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){
115558 struct Fts4Option {
115559 const char *zOpt;
115560 int nOpt;
115561 char **pzVar;
115562 } aFts4Opt[] = {
115563 { "matchinfo", 9, 0 }, /* 0 -> MATCHINFO */
115564 { "prefix", 6, 0 }, /* 1 -> PREFIX */
115565 { "compress", 8, 0 }, /* 2 -> COMPRESS */
115566 { "uncompress", 10, 0 }, /* 3 -> UNCOMPRESS */
115567 { "order", 5, 0 } /* 4 -> ORDER */
 
115568 };
115569
115570 int iOpt;
115571 if( !zVal ){
115572 rc = SQLITE_NOMEM;
@@ -115608,17 +115768,24 @@
115608 zVal = 0;
115609 break;
115610
115611 case 4: /* ORDER */
115612 if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
115613 && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 3))
115614 ){
115615 *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal);
115616 rc = SQLITE_ERROR;
115617 }
115618 bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
115619 break;
 
 
 
 
 
 
 
115620 }
115621 }
115622 sqlite3_free(zVal);
115623 }
115624 }
@@ -115627,10 +115794,30 @@
115627 else {
115628 nString += (int)(strlen(z) + 1);
115629 aCol[nCol++] = z;
115630 }
115631 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115632 if( rc!=SQLITE_OK ) goto fts3_init_out;
115633
115634 if( nCol==0 ){
115635 assert( nString==0 );
115636 aCol[0] = "content";
@@ -115671,10 +115858,12 @@
115671 p->pTokenizer = pTokenizer;
115672 p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
115673 p->bHasDocsize = (isFts4 && bNoDocsize==0);
115674 p->bHasStat = isFts4;
115675 p->bDescIdx = bDescIdx;
 
 
115676 TESTONLY( p->inTransaction = -1 );
115677 TESTONLY( p->mxSavepoint = -1 );
115678
115679 p->aIndex = (struct Fts3Index *)&p->azColumn[nCol];
115680 memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex);
@@ -115732,10 +115921,11 @@
115732 fts3_init_out:
115733 sqlite3_free(zPrefix);
115734 sqlite3_free(aIndex);
115735 sqlite3_free(zCompress);
115736 sqlite3_free(zUncompress);
 
115737 sqlite3_free((void *)aCol);
115738 if( rc!=SQLITE_OK ){
115739 if( p ){
115740 fts3DisconnectMethod((sqlite3_vtab *)p);
115741 }else if( pTokenizer ){
@@ -115882,40 +116072,69 @@
115882 sqlite3_free(pCsr->aMatchinfo);
115883 assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
115884 sqlite3_free(pCsr);
115885 return SQLITE_OK;
115886 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115887
115888 /*
115889 ** Position the pCsr->pStmt statement so that it is on the row
115890 ** of the %_content table that contains the last match. Return
115891 ** SQLITE_OK on success.
115892 */
115893 static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
 
115894 if( pCsr->isRequireSeek ){
115895 sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
115896 pCsr->isRequireSeek = 0;
115897 if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
115898 return SQLITE_OK;
115899 }else{
115900 int rc = sqlite3_reset(pCsr->pStmt);
115901 if( rc==SQLITE_OK ){
115902 /* If no row was found and no error has occured, then the %_content
115903 ** table is missing a row that is present in the full-text index.
115904 ** The data structures are corrupt.
115905 */
115906 rc = FTS_CORRUPT_VTAB;
115907 }
115908 pCsr->isEof = 1;
115909 if( pContext ){
115910 sqlite3_result_error_code(pContext, rc);
115911 }
115912 return rc;
115913 }
115914 }else{
115915 return SQLITE_OK;
115916 }
 
 
 
115917 }
115918
115919 /*
115920 ** This function is used to process a single interior node when searching
115921 ** a b-tree for a term or term prefix. The node data is passed to this
@@ -116351,20 +116570,20 @@
116351 int isSaveLeft, /* Save the left position */
116352 int isExact, /* If *pp1 is exactly nTokens before *pp2 */
116353 char **pp1, /* IN/OUT: Left input list */
116354 char **pp2 /* IN/OUT: Right input list */
116355 ){
116356 char *p = (pp ? *pp : 0);
116357 char *p1 = *pp1;
116358 char *p2 = *pp2;
116359 int iCol1 = 0;
116360 int iCol2 = 0;
116361
116362 /* Never set both isSaveLeft and isExact for the same invocation. */
116363 assert( isSaveLeft==0 || isExact==0 );
116364
116365 assert( *p1!=0 && *p2!=0 );
116366 if( *p1==POS_COLUMN ){
116367 p1++;
116368 p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
116369 }
116370 if( *p2==POS_COLUMN ){
@@ -116377,11 +116596,11 @@
116377 char *pSave = p;
116378 sqlite3_int64 iPrev = 0;
116379 sqlite3_int64 iPos1 = 0;
116380 sqlite3_int64 iPos2 = 0;
116381
116382 if( pp && iCol1 ){
116383 *p++ = POS_COLUMN;
116384 p += sqlite3Fts3PutVarint(p, iCol1);
116385 }
116386
116387 assert( *p1!=POS_END && *p1!=POS_COLUMN );
@@ -116392,20 +116611,14 @@
116392 while( 1 ){
116393 if( iPos2==iPos1+nToken
116394 || (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken)
116395 ){
116396 sqlite3_int64 iSave;
116397 if( !pp ){
116398 fts3PoslistCopy(0, &p2);
116399 fts3PoslistCopy(0, &p1);
116400 *pp1 = p1;
116401 *pp2 = p2;
116402 return 1;
116403 }
116404 iSave = isSaveLeft ? iPos1 : iPos2;
116405 fts3PutDeltaVarint(&p, &iPrev, iSave+2); iPrev -= 2;
116406 pSave = 0;
 
116407 }
116408 if( (!isSaveLeft && iPos2<=(iPos1+nToken)) || iPos2<=iPos1 ){
116409 if( (*p2&0xFE)==0 ) break;
116410 fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2;
116411 }else{
@@ -116450,11 +116663,11 @@
116450
116451 fts3PoslistCopy(0, &p2);
116452 fts3PoslistCopy(0, &p1);
116453 *pp1 = p1;
116454 *pp2 = p2;
116455 if( !pp || *pp==p ){
116456 return 0;
116457 }
116458 *p++ = 0x00;
116459 *pp = p;
116460 return 1;
@@ -116752,10 +116965,60 @@
116752 }
116753 }
116754
116755 *pnRight = p - aOut;
116756 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116757
116758
116759 /*
116760 ** Merge all doclists in the TermSelect.aaOutput[] array into a single
116761 ** doclist stored in TermSelect.aaOutput[0]. If successful, delete all
@@ -117109,10 +117372,11 @@
117109 pSegcsr = pTok->pSegcsr;
117110 memset(&tsc, 0, sizeof(TermSelect));
117111
117112 filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | FTS3_SEGMENT_REQUIRE_POS
117113 | (pTok->isPrefix ? FTS3_SEGMENT_PREFIX : 0)
 
117114 | (iColumn<p->nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0);
117115 filter.iCol = iColumn;
117116 filter.zTerm = pTok->z;
117117 filter.nTerm = pTok->n;
117118
@@ -117249,12 +117513,12 @@
117249
117250 if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
117251 return SQLITE_NOMEM;
117252 }
117253
117254 rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn,
117255 iCol, zQuery, -1, &pCsr->pExpr
117256 );
117257 if( rc!=SQLITE_OK ){
117258 if( rc==SQLITE_ERROR ){
117259 static const char *zErr = "malformed MATCH expression: [%s]";
117260 p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery);
@@ -117277,26 +117541,27 @@
117277 ** statement loops through all rows of the %_content table. For a
117278 ** full-text query or docid lookup, the statement retrieves a single
117279 ** row by docid.
117280 */
117281 if( idxNum==FTS3_FULLSCAN_SEARCH ){
117282 const char *zSort = (pCsr->bDesc ? "DESC" : "ASC");
117283 const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x ORDER BY docid %s";
117284 zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName, zSort);
117285 }else{
117286 const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?";
117287 zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName);
117288 }
117289 if( !zSql ) return SQLITE_NOMEM;
117290 rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
117291 sqlite3_free(zSql);
117292 if( rc!=SQLITE_OK ) return rc;
117293
117294 if( idxNum==FTS3_DOCID_SEARCH ){
117295 rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
117296 if( rc!=SQLITE_OK ) return rc;
117297 }
 
117298
117299 return fts3NextMethod(pCursor);
117300 }
117301
117302 /*
@@ -117345,11 +117610,11 @@
117345 ** Return a blob which is a pointer to the cursor.
117346 */
117347 sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
117348 }else{
117349 rc = fts3CursorSeek(0, pCsr);
117350 if( rc==SQLITE_OK ){
117351 sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
117352 }
117353 }
117354
117355 assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
@@ -117638,19 +117903,26 @@
117638 ){
117639 Fts3Table *p = (Fts3Table *)pVtab;
117640 sqlite3 *db = p->db; /* Database connection */
117641 int rc; /* Return Code */
117642
 
 
 
 
 
 
 
117643 rc = sqlite3Fts3PendingTermsFlush(p);
117644 if( rc!=SQLITE_OK ){
117645 return rc;
 
 
 
 
117646 }
117647
117648 fts3DbExec(&rc, db,
117649 "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
117650 p->zDb, p->zName, zName
117651 );
117652 if( p->bHasDocsize ){
117653 fts3DbExec(&rc, db,
117654 "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';",
117655 p->zDb, p->zName, zName
117656 );
@@ -118005,25 +118277,24 @@
118005 **
118006 ** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
118007 */
118008 static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
118009 int iToken; /* Used to iterate through phrase tokens */
118010 int rc = SQLITE_OK; /* Return code */
118011 char *aPoslist = 0; /* Position list for deferred tokens */
118012 int nPoslist = 0; /* Number of bytes in aPoslist */
118013 int iPrev = -1; /* Token number of previous deferred token */
118014
118015 assert( pPhrase->doclist.bFreeList==0 );
118016
118017 for(iToken=0; rc==SQLITE_OK && iToken<pPhrase->nToken; iToken++){
118018 Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
118019 Fts3DeferredToken *pDeferred = pToken->pDeferred;
118020
118021 if( pDeferred ){
118022 char *pList;
118023 int nList;
118024 rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList);
118025 if( rc!=SQLITE_OK ) return rc;
118026
118027 if( pList==0 ){
118028 sqlite3_free(aPoslist);
118029 pPhrase->doclist.pList = 0;
@@ -118120,10 +118391,11 @@
118120 if( pCsr->bDesc==pTab->bDescIdx
118121 && bOptOk==1
118122 && p->nToken==1
118123 && pFirst->pSegcsr
118124 && pFirst->pSegcsr->bLookup
 
118125 ){
118126 /* Use the incremental approach. */
118127 int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
118128 rc = sqlite3Fts3MsrIncrStart(
118129 pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
@@ -118349,11 +118621,11 @@
118349 Fts3Expr *pExpr, /* Expression to consider */
118350 Fts3TokenAndCost **ppTC, /* Write new entries to *(*ppTC)++ */
118351 Fts3Expr ***ppOr, /* Write new OR root to *(*ppOr)++ */
118352 int *pRc /* IN/OUT: Error code */
118353 ){
118354 if( *pRc==SQLITE_OK && pExpr ){
118355 if( pExpr->eType==FTSQUERY_PHRASE ){
118356 Fts3Phrase *pPhrase = pExpr->pPhrase;
118357 int i;
118358 for(i=0; *pRc==SQLITE_OK && i<pPhrase->nToken; i++){
118359 Fts3TokenAndCost *pTC = (*ppTC)++;
@@ -118363,10 +118635,15 @@
118363 pTC->pToken = &pPhrase->aToken[i];
118364 pTC->iCol = pPhrase->iColumn;
118365 *pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl);
118366 }
118367 }else if( pExpr->eType!=FTSQUERY_NOT ){
 
 
 
 
 
118368 if( pExpr->eType==FTSQUERY_OR ){
118369 pRoot = pExpr->pLeft;
118370 **ppOr = pRoot;
118371 (*ppOr)++;
118372 }
@@ -118466,10 +118743,19 @@
118466 int nOvfl = 0; /* Total overflow pages used by doclists */
118467 int nToken = 0; /* Total number of tokens in cluster */
118468
118469 int nMinEst = 0; /* The minimum count for any phrase so far. */
118470 int nLoad4 = 1; /* (Phrases that will be loaded)^4. */
 
 
 
 
 
 
 
 
 
118471
118472 /* Count the tokens in this AND/NEAR cluster. If none of the doclists
118473 ** associated with the tokens spill onto overflow pages, or if there is
118474 ** only 1 token, exit early. No tokens to defer in this case. */
118475 for(ii=0; ii<nTC; ii++){
@@ -118529,11 +118815,15 @@
118529 Fts3PhraseToken *pToken = pTC->pToken;
118530 rc = sqlite3Fts3DeferToken(pCsr, pToken, pTC->iCol);
118531 fts3SegReaderCursorFree(pToken->pSegcsr);
118532 pToken->pSegcsr = 0;
118533 }else{
118534 nLoad4 = nLoad4*4;
 
 
 
 
118535 if( ii==0 || pTC->pPhrase->nToken>1 ){
118536 /* Either this is the cheapest token in the entire query, or it is
118537 ** part of a multi-token phrase. Either way, the entire doclist will
118538 ** (eventually) be loaded into memory. It may as well be now. */
118539 Fts3PhraseToken *pToken = pTC->pToken;
@@ -119990,10 +120280,11 @@
119990 */
119991 typedef struct ParseContext ParseContext;
119992 struct ParseContext {
119993 sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
119994 const char **azCol; /* Array of column names for fts3 table */
 
119995 int nCol; /* Number of entries in azCol[] */
119996 int iDefaultCol; /* Default column to query */
119997 int isNot; /* True if getNextNode() sees a unary - */
119998 sqlite3_context *pCtx; /* Write error message here */
119999 int nNest; /* Number of nested brackets */
@@ -120077,13 +120368,25 @@
120077
120078 if( iEnd<n && z[iEnd]=='*' ){
120079 pRet->pPhrase->aToken[0].isPrefix = 1;
120080 iEnd++;
120081 }
120082 if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
120083 pParse->isNot = 1;
 
 
 
 
 
 
 
 
 
 
 
120084 }
 
120085 }
120086 nConsumed = iEnd;
120087 }
120088
120089 pModule->xClose(pCursor);
@@ -120178,10 +120481,11 @@
120178 memcpy(&zTemp[nTemp], zByte, nByte);
120179 nTemp += nByte;
120180
120181 pToken->n = nByte;
120182 pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
 
120183 nToken = ii+1;
120184 }
120185 }
120186
120187 pModule->xClose(pCursor);
@@ -120629,10 +120933,11 @@
120629 ** match any table column.
120630 */
120631 SQLITE_PRIVATE int sqlite3Fts3ExprParse(
120632 sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
120633 char **azCol, /* Array of column names for fts3 table */
 
120634 int nCol, /* Number of entries in azCol[] */
120635 int iDefaultCol, /* Default column to query */
120636 const char *z, int n, /* Text of MATCH query */
120637 Fts3Expr **ppExpr /* OUT: Parsed query structure */
120638 ){
@@ -120642,10 +120947,11 @@
120642 sParse.pTokenizer = pTokenizer;
120643 sParse.azCol = (const char **)azCol;
120644 sParse.nCol = nCol;
120645 sParse.iDefaultCol = iDefaultCol;
120646 sParse.nNest = 0;
 
120647 if( z==0 ){
120648 *ppExpr = 0;
120649 return SQLITE_OK;
120650 }
120651 if( n<0 ){
@@ -120831,11 +121137,11 @@
120831 for(ii=0; ii<nCol; ii++){
120832 azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]);
120833 }
120834
120835 rc = sqlite3Fts3ExprParse(
120836 pTokenizer, azCol, nCol, nCol, zExpr, nExpr, &pExpr
120837 );
120838 if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
120839 sqlite3_result_error(context, "Error parsing expression", -1);
120840 }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
120841 sqlite3_result_error_nomem(context);
@@ -122878,11 +123184,11 @@
122878 /* 2 */ "DELETE FROM %Q.'%q_content'",
122879 /* 3 */ "DELETE FROM %Q.'%q_segments'",
122880 /* 4 */ "DELETE FROM %Q.'%q_segdir'",
122881 /* 5 */ "DELETE FROM %Q.'%q_docsize'",
122882 /* 6 */ "DELETE FROM %Q.'%q_stat'",
122883 /* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?",
122884 /* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1",
122885 /* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)",
122886 /* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
122887 /* 11 */ "INSERT INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)",
122888
@@ -122920,11 +123226,11 @@
122920 if( !pStmt ){
122921 char *zSql;
122922 if( eStmt==SQL_CONTENT_INSERT ){
122923 zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
122924 }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
122925 zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName);
122926 }else{
122927 zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
122928 }
122929 if( !zSql ){
122930 rc = SQLITE_NOMEM;
@@ -123031,21 +123337,28 @@
123031 ** We try to avoid this because if FTS3 returns any error when committing
123032 ** a transaction, the whole transaction will be rolled back. And this is
123033 ** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can
123034 ** still happen if the user reads data directly from the %_segments or
123035 ** %_segdir tables instead of going through FTS3 though.
 
 
123036 */
123037 SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){
123038 int rc; /* Return code */
123039 sqlite3_stmt *pStmt; /* Statement used to obtain lock */
123040
123041 rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
123042 if( rc==SQLITE_OK ){
123043 sqlite3_bind_null(pStmt, 1);
123044 sqlite3_step(pStmt);
123045 rc = sqlite3_reset(pStmt);
 
 
 
 
123046 }
 
123047 return rc;
123048 }
123049
123050 /*
123051 ** Set *ppStmt to a statement handle that may be used to iterate through
@@ -123401,10 +123714,22 @@
123401 sqlite3_value **apVal, /* Array of values to insert */
123402 sqlite3_int64 *piDocid /* OUT: Docid for row just inserted */
123403 ){
123404 int rc; /* Return code */
123405 sqlite3_stmt *pContentInsert; /* INSERT INTO %_content VALUES(...) */
 
 
 
 
 
 
 
 
 
 
 
 
123406
123407 /* Locate the statement handle used to insert data into the %_content
123408 ** table. The SQL for this statement is:
123409 **
123410 ** INSERT INTO %_content VALUES(?, ?, ?, ...)
@@ -123452,18 +123777,20 @@
123452
123453 /*
123454 ** Remove all data from the FTS3 table. Clear the hash table containing
123455 ** pending terms.
123456 */
123457 static int fts3DeleteAll(Fts3Table *p){
123458 int rc = SQLITE_OK; /* Return code */
123459
123460 /* Discard the contents of the pending-terms hash table. */
123461 sqlite3Fts3PendingTermsClear(p);
123462
123463 /* Delete everything from the %_content, %_segments and %_segdir tables. */
123464 fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
 
 
123465 fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0);
123466 fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0);
123467 if( p->bHasDocsize ){
123468 fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0);
123469 }
@@ -124747,16 +125074,22 @@
124747 ** error occurs, an SQLite error code is returned.
124748 */
124749 static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
124750 sqlite3_stmt *pStmt;
124751 int rc;
124752 rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
124753 if( rc==SQLITE_OK ){
124754 if( SQLITE_ROW==sqlite3_step(pStmt) ){
124755 *pisEmpty = sqlite3_column_int(pStmt, 0);
 
 
 
 
 
 
 
124756 }
124757 rc = sqlite3_reset(pStmt);
124758 }
124759 return rc;
124760 }
124761
124762 /*
@@ -125104,10 +125437,11 @@
125104 int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY);
125105 int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS);
125106 int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
125107 int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX);
125108 int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN);
 
125109
125110 Fts3SegReader **apSegment = pCsr->apSegment;
125111 int nSegment = pCsr->nSegment;
125112 Fts3SegFilter *pFilter = pCsr->pFilter;
125113 int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = (
@@ -125163,10 +125497,11 @@
125163 }
125164
125165 assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
125166 if( nMerge==1
125167 && !isIgnoreEmpty
 
125168 && (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0)
125169 ){
125170 pCsr->nDoclist = apSegment[0]->nDoclist;
125171 if( fts3SegReaderIsPending(apSegment[0]) ){
125172 rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist);
@@ -125228,16 +125563,28 @@
125228 if( !aNew ){
125229 return SQLITE_NOMEM;
125230 }
125231 pCsr->aBuffer = aNew;
125232 }
125233 nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
125234 iPrev = iDocid;
125235 if( isRequirePos ){
125236 memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
125237 nDoclist += nList;
125238 pCsr->aBuffer[nDoclist++] = '\0';
 
 
 
 
 
 
 
 
 
 
 
 
125239 }
125240 }
125241
125242 fts3SegReaderSort(apSegment, nMerge, j, xCmp);
125243 }
@@ -125409,13 +125756,13 @@
125409 ** Insert the sizes (in tokens) for each column of the document
125410 ** with docid equal to p->iPrevDocid. The sizes are encoded as
125411 ** a blob of varints.
125412 */
125413 static void fts3InsertDocsize(
125414 int *pRC, /* Result code */
125415 Fts3Table *p, /* Table into which to insert */
125416 u32 *aSz /* Sizes of each column */
125417 ){
125418 char *pBlob; /* The BLOB encoding of the document size */
125419 int nBlob; /* Number of bytes in the BLOB */
125420 sqlite3_stmt *pStmt; /* Statement used to insert the encoding */
125421 int rc; /* Result code from subfunctions */
@@ -125532,10 +125879,90 @@
125532 sqlite3Fts3SegmentsClose(p);
125533 sqlite3Fts3PendingTermsClear(p);
125534
125535 return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
125536 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125537
125538 /*
125539 ** Handle a 'special' INSERT of the form:
125540 **
125541 ** "INSERT INTO tbl(tbl) VALUES(<expr>)"
@@ -125550,10 +125977,12 @@
125550
125551 if( !zVal ){
125552 return SQLITE_NOMEM;
125553 }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
125554 rc = fts3DoOptimize(p, 0);
 
 
125555 #ifdef SQLITE_TEST
125556 }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
125557 p->nNodeSize = atoi(&zVal[9]);
125558 rc = SQLITE_OK;
125559 }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
@@ -125630,10 +126059,11 @@
125630 pTC->pTokenizer = pT;
125631 rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
125632 for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
125633 Fts3PhraseToken *pPT = pDef->pToken;
125634 if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
 
125635 && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
125636 && (0==memcmp(zToken, pPT->z, pPT->n))
125637 ){
125638 fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
125639 }
@@ -125721,18 +126151,22 @@
125721 if( rc==SQLITE_OK ){
125722 if( isEmpty ){
125723 /* Deleting this row means the whole table is empty. In this case
125724 ** delete the contents of all three tables and throw away any
125725 ** data in the pendingTerms hash table. */
125726 rc = fts3DeleteAll(p);
125727 *pnDoc = *pnDoc - 1;
125728 }else{
125729 sqlite3_int64 iRemove = sqlite3_value_int64(pRowid);
125730 rc = fts3PendingTermsDocid(p, iRemove);
125731 fts3DeleteTerms(&rc, p, pRowid, aSzDel);
125732 fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
125733 if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
 
 
 
 
125734 if( p->bHasDocsize ){
125735 fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
125736 }
125737 }
125738 }
@@ -125788,11 +126222,11 @@
125788 ** should be deleted from the database before inserting the new row. Or,
125789 ** if the on-conflict mode is other than REPLACE, then this method must
125790 ** detect the conflict and return SQLITE_CONSTRAINT before beginning to
125791 ** modify the database file.
125792 */
125793 if( nArg>1 ){
125794 /* Find the value object that holds the new rowid value. */
125795 sqlite3_value *pNewRowid = apVal[3+p->nColumn];
125796 if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
125797 pNewRowid = apVal[1];
125798 }
@@ -125839,11 +126273,13 @@
125839
125840 /* If this is an INSERT or UPDATE operation, insert the new record. */
125841 if( nArg>1 && rc==SQLITE_OK ){
125842 if( bInsertDone==0 ){
125843 rc = fts3InsertData(p, apVal, pRowid);
125844 if( rc==SQLITE_CONSTRAINT ) rc = FTS_CORRUPT_VTAB;
 
 
125845 }
125846 if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){
125847 rc = fts3PendingTermsDocid(p, *pRowid);
125848 }
125849 if( rc==SQLITE_OK ){
@@ -126259,10 +126695,11 @@
126259 pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
126260 if( pCsr ){
126261 int iFirst = 0;
126262 pPhrase->pList = pCsr;
126263 fts3GetDeltaPosition(&pCsr, &iFirst);
 
126264 pPhrase->pHead = pCsr;
126265 pPhrase->pTail = pCsr;
126266 pPhrase->iHead = iFirst;
126267 pPhrase->iTail = iFirst;
126268 }else{
@@ -127300,11 +127737,11 @@
127300 }
127301 }
127302
127303 if( !pTerm ){
127304 /* All offsets for this column have been gathered. */
127305 break;
127306 }else{
127307 assert( iCurrent<=iMinPos );
127308 if( 0==(0xFE&*pTerm->pList) ){
127309 pTerm->pList = 0;
127310 }else{
@@ -127317,11 +127754,11 @@
127317 char aBuffer[64];
127318 sqlite3_snprintf(sizeof(aBuffer), aBuffer,
127319 "%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart
127320 );
127321 rc = fts3StringAppend(&res, aBuffer, -1);
127322 }else if( rc==SQLITE_DONE ){
127323 rc = FTS_CORRUPT_VTAB;
127324 }
127325 }
127326 }
127327 if( rc==SQLITE_DONE ){
127328
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -314,17 +314,10 @@
314 #endif
315 #ifdef HAVE_INTTYPES_H
316 #include <inttypes.h>
317 #endif
318
 
 
 
 
 
 
 
319 /*
320 ** The following macros are used to cast pointers to integers and
321 ** integers to pointers. The way you do this varies from one compiler
322 ** to the next, so we have developed the following set of #if statements
323 ** to generate appropriate macros for a wide range of compilers.
@@ -656,11 +649,11 @@
649 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
650 ** [sqlite_version()] and [sqlite_source_id()].
651 */
652 #define SQLITE_VERSION "3.7.9"
653 #define SQLITE_VERSION_NUMBER 3007009
654 #define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf"
655
656 /*
657 ** CAPI3REF: Run-Time Library Version Numbers
658 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
659 **
@@ -1951,12 +1944,12 @@
1944 ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
1945 ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
1946 ** allocator is engaged to handle all of SQLites memory allocation needs.
1947 ** The first pointer (the memory pointer) must be aligned to an 8-byte
1948 ** boundary or subsequent behavior of SQLite will be undefined.
1949 ** The minimum allocation size is capped at 2**12. Reasonable values
1950 ** for the minimum allocation size are 2**5 through 2**8.</dd>
1951 **
1952 ** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
1953 ** <dd> ^(This option takes a single argument which is a pointer to an
1954 ** instance of the [sqlite3_mutex_methods] structure. The argument specifies
1955 ** alternative low-level mutex routines to be used in place
@@ -8792,10 +8785,11 @@
8785 SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
8786 SQLITE_PRIVATE int sqlite3PagerNosync(Pager*);
8787 SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
8788 SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
8789 SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
8790 SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *);
8791
8792 /* Functions used to truncate the database file. */
8793 SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
8794
8795 #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
@@ -10189,12 +10183,13 @@
10183 IndexSample *aSample; /* Samples of the left-most key */
10184 #endif
10185 };
10186
10187 /*
10188 ** Each sample stored in the sqlite_stat3 table is represented in memory
10189 ** using a structure of this type. See documentation at the top of the
10190 ** analyze.c source file for additional information.
10191 */
10192 struct IndexSample {
10193 union {
10194 char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
10195 double r; /* Value if eType is SQLITE_FLOAT */
@@ -12293,13 +12288,10 @@
12288 "ENABLE_OVERSIZE_CELL_CHECK",
12289 #endif
12290 #ifdef SQLITE_ENABLE_RTREE
12291 "ENABLE_RTREE",
12292 #endif
 
 
 
12293 #ifdef SQLITE_ENABLE_STAT3
12294 "ENABLE_STAT3",
12295 #endif
12296 #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
12297 "ENABLE_UNLOCK_NOTIFY",
@@ -12996,10 +12988,11 @@
12988 SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
12989 SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
12990 SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*);
12991 SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
12992 SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem);
12993 SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p);
12994
12995 #ifdef SQLITE_OMIT_MERGE_SORT
12996 # define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK
12997 # define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK
12998 # define sqlite3VdbeSorterClose(Y,Z)
@@ -20969,11 +20962,11 @@
20962 }else if( *z=='+' ){
20963 z+=incr;
20964 }
20965 /* copy digits to exponent */
20966 while( z<zEnd && sqlite3Isdigit(*z) ){
20967 e = e<10000 ? (e*10 + (*z - '0')) : 10000;
20968 z+=incr;
20969 eValid = 1;
20970 }
20971 }
20972
@@ -21020,10 +21013,16 @@
21013 result /= 1.0e+308;
21014 }else{
21015 result = s * scale;
21016 result *= 1.0e+308;
21017 }
21018 }else if( e>=342 ){
21019 if( esign<0 ){
21020 result = 0.0*s;
21021 }else{
21022 result = 1e308*1e308*s; /* Infinity */
21023 }
21024 }else{
21025 /* 1.0e+22 is the largest power of 10 than can be
21026 ** represented exactly. */
21027 while( e%22 ) { scale *= 1.0e+1; e -= 1; }
21028 while( e>0 ) { scale *= 1.0e+22; e -= 22; }
@@ -29477,17 +29476,17 @@
29476 ** "<path to db>-journal"
29477 ** "<path to db>-wal"
29478 ** "<path to db>-journalNN"
29479 ** "<path to db>-walNN"
29480 **
29481 ** where NN is a decimal number. The NN naming schemes are
29482 ** used by the test_multiplex.c module.
29483 */
29484 nDb = sqlite3Strlen30(zPath) - 1;
29485 #ifdef SQLITE_ENABLE_8_3_NAMES
29486 while( nDb>0 && !sqlite3Isalnum(zPath[nDb]) ) nDb--;
29487 if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK;
29488 #else
29489 while( zPath[nDb]!='-' ){
29490 assert( nDb>0 );
29491 assert( zPath[nDb]!='\n' );
29492 nDb--;
@@ -44157,10 +44156,17 @@
44156 pPager->pWal = 0;
44157 }
44158 }
44159 return rc;
44160 }
44161
44162 /*
44163 ** Unless this is an in-memory or temporary database, clear the pager cache.
44164 */
44165 SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *pPager){
44166 if( !MEMDB && pPager->tempFile==0 ) pager_reset(pPager);
44167 }
44168
44169 #ifdef SQLITE_HAS_CODEC
44170 /*
44171 ** This function is called by the wal module when writing page content
44172 ** into the log file.
@@ -57000,10 +57006,12 @@
57006 sqlite3_backup_step(&b, 0x7FFFFFFF);
57007 assert( b.rc!=SQLITE_OK );
57008 rc = sqlite3_backup_finish(&b);
57009 if( rc==SQLITE_OK ){
57010 pTo->pBt->pageSizeFixed = 0;
57011 }else{
57012 sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
57013 }
57014
57015 assert( sqlite3BtreeIsInTrans(pTo)==0 );
57016 sqlite3BtreeLeave(pFrom);
57017 sqlite3BtreeLeave(pTo);
@@ -60474,10 +60482,34 @@
60482 ** in p->rc. This routine sets that result back to SQLITE_OK.
60483 */
60484 SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){
60485 p->rc = SQLITE_OK;
60486 }
60487
60488 /*
60489 ** Copy the error code and error message belonging to the VDBE passed
60490 ** as the first argument to its database handle (so that they will be
60491 ** returned by calls to sqlite3_errcode() and sqlite3_errmsg()).
60492 **
60493 ** This function does not clear the VDBE error code or message, just
60494 ** copies them to the database handle.
60495 */
60496 SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){
60497 sqlite3 *db = p->db;
60498 int rc = p->rc;
60499 if( p->zErrMsg ){
60500 u8 mallocFailed = db->mallocFailed;
60501 sqlite3BeginBenignMalloc();
60502 sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
60503 sqlite3EndBenignMalloc();
60504 db->mallocFailed = mallocFailed;
60505 db->errCode = rc;
60506 }else{
60507 sqlite3Error(db, rc, 0);
60508 }
60509 return rc;
60510 }
60511
60512 /*
60513 ** Clean up a VDBE after execution but do not delete the VDBE just yet.
60514 ** Write any error messages into *pzErrMsg. Return the result code.
60515 **
@@ -60502,22 +60534,13 @@
60534 ** and error message from the VDBE into the main database structure. But
60535 ** if the VDBE has just been set to run but has not actually executed any
60536 ** instructions yet, leave the main database error information unchanged.
60537 */
60538 if( p->pc>=0 ){
60539 sqlite3VdbeTransferError(p);
60540 sqlite3DbFree(db, p->zErrMsg);
60541 p->zErrMsg = 0;
 
 
 
 
 
 
 
 
 
60542 if( p->runOnlyOnce ) p->expired = 1;
60543 }else if( p->rc && p->expired ){
60544 /* The expired flag was set on the VDBE before the first call
60545 ** to sqlite3_step(). For consistency (since sqlite3_step() was
60546 ** called), set the database error in this case as well.
@@ -61859,11 +61882,11 @@
61882 if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
61883 /* If this statement was prepared using sqlite3_prepare_v2(), and an
61884 ** error has occured, then return the error code in p->rc to the
61885 ** caller. Set the error code in the database handle to the same value.
61886 */
61887 rc = sqlite3VdbeTransferError(p);
61888 }
61889 return (rc&db->errMask);
61890 }
61891
61892 /*
@@ -67759,13 +67782,12 @@
67782
67783 assert( pOp->p1>=0 && pOp->p1<p->nCursor );
67784 u.bn.pC = p->apCsr[pOp->p1];
67785 assert( u.bn.pC!=0 );
67786 u.bn.pCrsr = u.bn.pC->pCursor;
67787 u.bn.res = 0;
67788 if( ALWAYS(u.bn.pCrsr!=0) ){
 
67789 rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res);
67790 }
67791 u.bn.pC->nullRow = (u8)u.bn.res;
67792 u.bn.pC->deferredMoveto = 0;
67793 u.bn.pC->rowidIsValid = 0;
@@ -68962,11 +68984,11 @@
68984
68985 /* Do not allow a transition to journal_mode=WAL for a database
68986 ** in temporary storage or if the VFS does not support shared memory
68987 */
68988 if( u.ch.eNew==PAGER_JOURNALMODE_WAL
68989 && (sqlite3Strlen30(u.ch.zFilename)==0 /* Temp file */
68990 || !sqlite3PagerWalSupported(u.ch.pPager)) /* No shared-memory support */
68991 ){
68992 u.ch.eNew = u.ch.eOld;
68993 }
68994
@@ -69397,14 +69419,19 @@
69419 u.co.pName = &aMem[pOp->p1];
69420 assert( u.co.pVtab->pModule->xRename );
69421 assert( memIsValid(u.co.pName) );
69422 REGISTER_TRACE(pOp->p1, u.co.pName);
69423 assert( u.co.pName->flags & MEM_Str );
69424 testcase( u.co.pName->enc==SQLITE_UTF8 );
69425 testcase( u.co.pName->enc==SQLITE_UTF16BE );
69426 testcase( u.co.pName->enc==SQLITE_UTF16LE );
69427 rc = sqlite3VdbeChangeEncoding(u.co.pName, SQLITE_UTF8);
69428 if( rc==SQLITE_OK ){
69429 rc = u.co.pVtab->pModule->xRename(u.co.pVtab, u.co.pName->z);
69430 importVtabErrMsg(p, u.co.pVtab);
69431 p->expired = 0;
69432 }
69433 break;
69434 }
69435 #endif
69436
69437 #ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -71758,10 +71785,28 @@
71785 ExprSetProperty(pExpr, EP_Static);
71786 sqlite3ExprDelete(db, pExpr);
71787 memcpy(pExpr, pDup, sizeof(*pExpr));
71788 sqlite3DbFree(db, pDup);
71789 }
71790
71791
71792 /*
71793 ** Return TRUE if the name zCol occurs anywhere in the USING clause.
71794 **
71795 ** Return FALSE if the USING clause is NULL or if it does not contain
71796 ** zCol.
71797 */
71798 static int nameInUsingClause(IdList *pUsing, const char *zCol){
71799 if( pUsing ){
71800 int k;
71801 for(k=0; k<pUsing->nId; k++){
71802 if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1;
71803 }
71804 }
71805 return 0;
71806 }
71807
71808
71809 /*
71810 ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
71811 ** that name in the set of source tables in pSrcList and make the pExpr
71812 ** expression node refer back to that source column. The following changes
@@ -71850,38 +71895,25 @@
71895 pSchema = pTab->pSchema;
71896 pMatch = pItem;
71897 }
71898 for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
71899 if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
71900 /* If there has been exactly one prior match and this match
71901 ** is for the right-hand table of a NATURAL JOIN or is in a
71902 ** USING clause, then skip this match.
71903 */
71904 if( cnt==1 ){
71905 if( pItem->jointype & JT_NATURAL ) continue;
71906 if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
71907 }
71908 cnt++;
71909 pExpr->iTable = pItem->iCursor;
71910 pExpr->pTab = pTab;
71911 pMatch = pItem;
71912 pSchema = pTab->pSchema;
71913 /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
71914 pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71915 break;
71916 }
71917 }
71918 }
71919 }
@@ -77594,20 +77626,20 @@
77626 #ifndef SQLITE_OMIT_ANALYZE
77627
77628 /*
77629 ** This routine generates code that opens the sqlite_stat1 table for
77630 ** writing with cursor iStatCur. If the library was built with the
77631 ** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is
77632 ** opened for writing using cursor (iStatCur+1)
77633 **
77634 ** If the sqlite_stat1 tables does not previously exist, it is created.
77635 ** Similarly, if the sqlite_stat3 table does not exist and the library
77636 ** is compiled with SQLITE_ENABLE_STAT3 defined, it is created.
77637 **
77638 ** Argument zWhere may be a pointer to a buffer containing a table name,
77639 ** or it may be a NULL pointer. If it is not NULL, then all entries in
77640 ** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated
77641 ** with the named table are deleted. If zWhere==0, then code is generated
77642 ** to delete all stat table entries.
77643 */
77644 static void openStatTable(
77645 Parse *pParse, /* Parsing context */
@@ -81388,31 +81420,28 @@
81420 }
81421 #endif
81422 }
81423
81424 /*
81425 ** Remove entries from the sqlite_statN tables (for N in (1,2,3))
81426 ** after a DROP INDEX or DROP TABLE command.
81427 */
81428 static void sqlite3ClearStatTables(
81429 Parse *pParse, /* The parsing context */
81430 int iDb, /* The database number */
81431 const char *zType, /* "idx" or "tbl" */
81432 const char *zName /* Name of index or table */
81433 ){
 
 
 
 
 
81434 int i;
81435 const char *zDbName = pParse->db->aDb[iDb].zName;
81436 for(i=1; i<=3; i++){
81437 char zTab[24];
81438 sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
81439 if( sqlite3FindTable(pParse->db, zTab, zDbName) ){
81440 sqlite3NestedParse(pParse,
81441 "DELETE FROM %Q.%s WHERE %s=%Q",
81442 zDbName, zTab, zType, zName
81443 );
81444 }
81445 }
81446 }
81447
@@ -89234,12 +89263,14 @@
89263 int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
89264 int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
89265 int (*busy_timeout)(sqlite3*,int ms);
89266 int (*changes)(sqlite3*);
89267 int (*close)(sqlite3*);
89268 int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
89269 int eTextRep,const char*));
89270 int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
89271 int eTextRep,const void*));
89272 const void * (*column_blob)(sqlite3_stmt*,int iCol);
89273 int (*column_bytes)(sqlite3_stmt*,int iCol);
89274 int (*column_bytes16)(sqlite3_stmt*,int iCol);
89275 int (*column_count)(sqlite3_stmt*pStmt);
89276 const char * (*column_database_name)(sqlite3_stmt*,int);
@@ -89260,14 +89291,22 @@
89291 int (*column_type)(sqlite3_stmt*,int iCol);
89292 sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
89293 void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
89294 int (*complete)(const char*sql);
89295 int (*complete16)(const void*sql);
89296 int (*create_collation)(sqlite3*,const char*,int,void*,
89297 int(*)(void*,int,const void*,int,const void*));
89298 int (*create_collation16)(sqlite3*,const void*,int,void*,
89299 int(*)(void*,int,const void*,int,const void*));
89300 int (*create_function)(sqlite3*,const char*,int,int,void*,
89301 void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
89302 void (*xStep)(sqlite3_context*,int,sqlite3_value**),
89303 void (*xFinal)(sqlite3_context*));
89304 int (*create_function16)(sqlite3*,const void*,int,int,void*,
89305 void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
89306 void (*xStep)(sqlite3_context*,int,sqlite3_value**),
89307 void (*xFinal)(sqlite3_context*));
89308 int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
89309 int (*data_count)(sqlite3_stmt*pStmt);
89310 sqlite3 * (*db_handle)(sqlite3_stmt*);
89311 int (*declare_vtab)(sqlite3*,const char*);
89312 int (*enable_shared_cache)(int);
@@ -89308,20 +89347,23 @@
89347 void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
89348 void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
89349 void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
89350 void (*result_value)(sqlite3_context*,sqlite3_value*);
89351 void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
89352 int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
89353 const char*,const char*),void*);
89354 void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
89355 char * (*snprintf)(int,char*,const char*,...);
89356 int (*step)(sqlite3_stmt*);
89357 int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
89358 char const**,char const**,int*,int*,int*);
89359 void (*thread_cleanup)(void);
89360 int (*total_changes)(sqlite3*);
89361 void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
89362 int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
89363 void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
89364 sqlite_int64),void*);
89365 void * (*user_data)(sqlite3_context*);
89366 const void * (*value_blob)(sqlite3_value*);
89367 int (*value_bytes)(sqlite3_value*);
89368 int (*value_bytes16)(sqlite3_value*);
89369 double (*value_double)(sqlite3_value*);
@@ -89339,19 +89381,23 @@
89381 /* Added by 3.3.13 */
89382 int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
89383 int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
89384 int (*clear_bindings)(sqlite3_stmt*);
89385 /* Added by 3.4.1 */
89386 int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
89387 void (*xDestroy)(void *));
89388 /* Added by 3.5.0 */
89389 int (*bind_zeroblob)(sqlite3_stmt*,int,int);
89390 int (*blob_bytes)(sqlite3_blob*);
89391 int (*blob_close)(sqlite3_blob*);
89392 int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
89393 int,sqlite3_blob**);
89394 int (*blob_read)(sqlite3_blob*,void*,int,int);
89395 int (*blob_write)(sqlite3_blob*,const void*,int,int);
89396 int (*create_collation_v2)(sqlite3*,const char*,int,void*,
89397 int(*)(void*,int,const void*,int,const void*),
89398 void(*)(void*));
89399 int (*file_control)(sqlite3*,const char*,int,void*);
89400 sqlite3_int64 (*memory_highwater)(int);
89401 sqlite3_int64 (*memory_used)(void);
89402 sqlite3_mutex *(*mutex_alloc)(int);
89403 void (*mutex_enter)(sqlite3_mutex*);
@@ -89383,11 +89429,15 @@
89429 int (*backup_pagecount)(sqlite3_backup*);
89430 int (*backup_remaining)(sqlite3_backup*);
89431 int (*backup_step)(sqlite3_backup*,int);
89432 const char *(*compileoption_get)(int);
89433 int (*compileoption_used)(const char*);
89434 int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
89435 void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
89436 void (*xStep)(sqlite3_context*,int,sqlite3_value**),
89437 void (*xFinal)(sqlite3_context*),
89438 void(*xDestroy)(void*));
89439 int (*db_config)(sqlite3*,int,...);
89440 sqlite3_mutex *(*db_mutex)(sqlite3*);
89441 int (*db_status)(sqlite3*,int,int*,int*,int);
89442 int (*extended_errcode)(sqlite3*);
89443 void (*log)(int,const char*,...);
@@ -100469,11 +100519,11 @@
100519 if( db->aVTrans ){
100520 int i;
100521 for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
100522 VTable *pVTab = db->aVTrans[i];
100523 const sqlite3_module *pMod = pVTab->pMod->pModule;
100524 if( pVTab->pVtab && pMod->iVersion>=2 ){
100525 int (*xMethod)(sqlite3_vtab *, int);
100526 switch( op ){
100527 case SAVEPOINT_BEGIN:
100528 xMethod = pMod->xSavepoint;
100529 pVTab->iSavepoint = iSavepoint+1;
@@ -100484,11 +100534,11 @@
100534 default:
100535 xMethod = pMod->xRelease;
100536 break;
100537 }
100538 if( xMethod && pVTab->iSavepoint>iSavepoint ){
100539 rc = xMethod(pVTab->pVtab, iSavepoint);
100540 }
100541 }
100542 }
100543 }
100544 return rc;
@@ -101351,11 +101401,11 @@
101401 int iCol = pRight->iColumn;
101402 pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE);
101403 if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
101404 z = (char *)sqlite3_value_text(pVal);
101405 }
101406 sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
101407 assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
101408 }else if( op==TK_STRING ){
101409 z = pRight->u.zToken;
101410 }
101411 if( z ){
@@ -101369,11 +101419,11 @@
101419 pPrefix = sqlite3Expr(db, TK_STRING, z);
101420 if( pPrefix ) pPrefix->u.zToken[cnt] = 0;
101421 *ppPrefix = pPrefix;
101422 if( op==TK_VARIABLE ){
101423 Vdbe *v = pParse->pVdbe;
101424 sqlite3VdbeSetVarmask(v, pRight->iColumn);
101425 if( *pisComplete && pRight->u.zToken[1] ){
101426 /* If the rhs of the LIKE expression is a variable, and the current
101427 ** value of the variable means there is no need to invoke the LIKE
101428 ** function, then no OP_Variable will be added to the program.
101429 ** This causes problems for the sqlite3_bind_parameter_name()
@@ -102501,10 +102551,11 @@
102551 tempWC.pParse = pWC->pParse;
102552 tempWC.pMaskSet = pWC->pMaskSet;
102553 tempWC.pOuter = pWC;
102554 tempWC.op = TK_AND;
102555 tempWC.a = pOrTerm;
102556 tempWC.wctrlFlags = 0;
102557 tempWC.nTerm = 1;
102558 bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost);
102559 }else{
102560 continue;
102561 }
@@ -103282,11 +103333,11 @@
103333 ){
103334 if( pExpr->op==TK_VARIABLE
103335 || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
103336 ){
103337 int iVar = pExpr->iColumn;
103338 sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
103339 *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
103340 return SQLITE_OK;
103341 }
103342 return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp);
103343 }
@@ -103812,11 +103863,11 @@
103863 ** a table or index. The actual times can vary, with the size of
103864 ** records being an important factor. Both moves and searches are
103865 ** slower with larger records, presumably because fewer records fit
103866 ** on one page and hence more pages have to be fetched.
103867 **
103868 ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do
103869 ** not give us data on the relative sizes of table and index records.
103870 ** So this computation assumes table records are about twice as big
103871 ** as index records
103872 */
103873 if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){
@@ -114528,10 +114579,11 @@
114579 const char *zDb; /* logical database name */
114580 const char *zName; /* virtual table name */
114581 int nColumn; /* number of named columns in virtual table */
114582 char **azColumn; /* column names. malloced */
114583 sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
114584 char *zContentTbl; /* content=xxx option, or NULL */
114585
114586 /* Precompiled statements used by the implementation. Each of these
114587 ** statements is run and reset within a single virtual table API call.
114588 */
114589 sqlite3_stmt *aStmt[27];
@@ -114568,11 +114620,11 @@
114620 } *aIndex;
114621 int nMaxPendingData; /* Max pending data before flush to disk */
114622 int nPendingData; /* Current bytes of pending data */
114623 sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
114624
114625 #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
114626 /* State variables used for validating that the transaction control
114627 ** methods of the virtual table are called at appropriate times. These
114628 ** values do not contribution to the FTS computation; they are used for
114629 ** verifying the SQLite core.
114630 */
@@ -114653,10 +114705,11 @@
114705 */
114706 struct Fts3PhraseToken {
114707 char *z; /* Text of the token */
114708 int n; /* Number of bytes in buffer z */
114709 int isPrefix; /* True if token ends with a "*" character */
114710 int bFirst; /* True if token must appear at position 0 */
114711
114712 /* Variables above this point are populated when the expression is
114713 ** parsed (by code in fts3_expr.c). Below this point the variables are
114714 ** used when evaluating the expression. */
114715 Fts3DeferredToken *pDeferred; /* Deferred token object for this token */
@@ -114771,10 +114824,11 @@
114824 #define FTS3_SEGMENT_REQUIRE_POS 0x00000001
114825 #define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002
114826 #define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
114827 #define FTS3_SEGMENT_PREFIX 0x00000008
114828 #define FTS3_SEGMENT_SCAN 0x00000010
114829 #define FTS3_SEGMENT_FIRST 0x00000020
114830
114831 /* Type passed as 4th argument to SegmentReaderIterate() */
114832 struct Fts3SegFilter {
114833 const char *zTerm;
114834 int nTerm;
@@ -114810,12 +114864,12 @@
114864 SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
114865 SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *);
114866 SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64);
114867 SQLITE_PRIVATE void sqlite3Fts3Dequote(char *);
114868 SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
 
114869 SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
114870 SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
114871
114872 /* fts3_tokenizer.c */
114873 SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
114874 SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
114875 SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
@@ -114830,11 +114884,11 @@
114884 );
114885 SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
114886
114887 /* fts3_expr.c */
114888 SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *,
114889 char **, int, int, int, const char *, int, Fts3Expr **
114890 );
114891 SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *);
114892 #ifdef SQLITE_TEST
114893 SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
114894 SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db);
@@ -115031,10 +115085,11 @@
115085 sqlite3_finalize(p->aStmt[i]);
115086 }
115087 sqlite3_free(p->zSegmentsTbl);
115088 sqlite3_free(p->zReadExprlist);
115089 sqlite3_free(p->zWriteExprlist);
115090 sqlite3_free(p->zContentTbl);
115091
115092 /* Invoke the tokenizer destructor to free the tokenizer. */
115093 p->pTokenizer->pModule->xDestroy(p->pTokenizer);
115094
115095 sqlite3_free(p);
@@ -115070,20 +115125,23 @@
115125
115126 /*
115127 ** The xDestroy() virtual table method.
115128 */
115129 static int fts3DestroyMethod(sqlite3_vtab *pVtab){
 
115130 Fts3Table *p = (Fts3Table *)pVtab;
115131 int rc = SQLITE_OK; /* Return code */
115132 const char *zDb = p->zDb; /* Name of database (e.g. "main", "temp") */
115133 sqlite3 *db = p->db; /* Database handle */
115134
115135 /* Drop the shadow tables */
115136 if( p->zContentTbl==0 ){
115137 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", zDb, p->zName);
115138 }
115139 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", zDb,p->zName);
115140 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", zDb, p->zName);
115141 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", zDb, p->zName);
115142 fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", zDb, p->zName);
115143
115144 /* If everything has worked, invoke fts3DisconnectMethod() to free the
115145 ** memory associated with the Fts3Table structure and return SQLITE_OK.
115146 ** Otherwise, return an SQLite error code.
115147 */
@@ -115141,27 +115199,31 @@
115199 ** %_stat tables required by FTS4.
115200 */
115201 static int fts3CreateTables(Fts3Table *p){
115202 int rc = SQLITE_OK; /* Return code */
115203 int i; /* Iterator variable */
 
115204 sqlite3 *db = p->db; /* The database connection */
115205
115206 if( p->zContentTbl==0 ){
115207 char *zContentCols; /* Columns of %_content table */
115208
115209 /* Create a list of user columns for the content table */
115210 zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
115211 for(i=0; zContentCols && i<p->nColumn; i++){
115212 char *z = p->azColumn[i];
115213 zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
115214 }
115215 if( zContentCols==0 ) rc = SQLITE_NOMEM;
115216
115217 /* Create the content table */
115218 fts3DbExec(&rc, db,
115219 "CREATE TABLE %Q.'%q_content'(%s)",
115220 p->zDb, p->zName, zContentCols
115221 );
115222 sqlite3_free(zContentCols);
115223 }
115224
115225 /* Create other tables */
115226 fts3DbExec(&rc, db,
115227 "CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);",
115228 p->zDb, p->zName
115229 );
@@ -115308,12 +115370,12 @@
115370 }
115371 return zRet;
115372 }
115373
115374 /*
115375 ** Return a list of comma separated SQL expressions and a FROM clause that
115376 ** could be used in a SELECT statement such as the following:
115377 **
115378 ** SELECT <list of expressions> FROM %_content AS x ...
115379 **
115380 ** to return the docid, followed by each column of text data in order
115381 ** from left to write. If parameter zFunc is not NULL, then instead of
@@ -115320,11 +115382,11 @@
115382 ** being returned directly each column of text data is passed to an SQL
115383 ** function named zFunc first. For example, if zFunc is "unzip" and the
115384 ** table has the three user-defined columns "a", "b", and "c", the following
115385 ** string is returned:
115386 **
115387 ** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c') FROM %_content AS x"
115388 **
115389 ** The pointer returned points to a buffer allocated by sqlite3_malloc(). It
115390 ** is the responsibility of the caller to eventually free it.
115391 **
115392 ** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and
@@ -115336,20 +115398,32 @@
115398 char *zRet = 0;
115399 char *zFree = 0;
115400 char *zFunction;
115401 int i;
115402
115403 if( p->zContentTbl==0 ){
115404 if( !zFunc ){
115405 zFunction = "";
115406 }else{
115407 zFree = zFunction = fts3QuoteId(zFunc);
115408 }
115409 fts3Appendf(pRc, &zRet, "docid");
115410 for(i=0; i<p->nColumn; i++){
115411 fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
115412 }
115413 sqlite3_free(zFree);
115414 }else{
115415 fts3Appendf(pRc, &zRet, "rowid");
115416 for(i=0; i<p->nColumn; i++){
115417 fts3Appendf(pRc, &zRet, ", x.'%q'", p->azColumn[i]);
115418 }
115419 }
115420 fts3Appendf(pRc, &zRet, "FROM '%q'.'%q%s' AS x",
115421 p->zDb,
115422 (p->zContentTbl ? p->zContentTbl : p->zName),
115423 (p->zContentTbl ? "" : "_content")
115424 );
115425 return zRet;
115426 }
115427
115428 /*
115429 ** Return a list of N comma separated question marks, where N is the number
@@ -115468,10 +115542,95 @@
115542 }
115543 }
115544
115545 return SQLITE_OK;
115546 }
115547
115548 /*
115549 ** This function is called when initializing an FTS4 table that uses the
115550 ** content=xxx option. It determines the number of and names of the columns
115551 ** of the new FTS4 table.
115552 **
115553 ** The third argument passed to this function is the value passed to the
115554 ** config=xxx option (i.e. "xxx"). This function queries the database for
115555 ** a table of that name. If found, the output variables are populated
115556 ** as follows:
115557 **
115558 ** *pnCol: Set to the number of columns table xxx has,
115559 **
115560 ** *pnStr: Set to the total amount of space required to store a copy
115561 ** of each columns name, including the nul-terminator.
115562 **
115563 ** *pazCol: Set to point to an array of *pnCol strings. Each string is
115564 ** the name of the corresponding column in table xxx. The array
115565 ** and its contents are allocated using a single allocation. It
115566 ** is the responsibility of the caller to free this allocation
115567 ** by eventually passing the *pazCol value to sqlite3_free().
115568 **
115569 ** If the table cannot be found, an error code is returned and the output
115570 ** variables are undefined. Or, if an OOM is encountered, SQLITE_NOMEM is
115571 ** returned (and the output variables are undefined).
115572 */
115573 static int fts3ContentColumns(
115574 sqlite3 *db, /* Database handle */
115575 const char *zDb, /* Name of db (i.e. "main", "temp" etc.) */
115576 const char *zTbl, /* Name of content table */
115577 const char ***pazCol, /* OUT: Malloc'd array of column names */
115578 int *pnCol, /* OUT: Size of array *pazCol */
115579 int *pnStr /* OUT: Bytes of string content */
115580 ){
115581 int rc = SQLITE_OK; /* Return code */
115582 char *zSql; /* "SELECT *" statement on zTbl */
115583 sqlite3_stmt *pStmt = 0; /* Compiled version of zSql */
115584
115585 zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zTbl);
115586 if( !zSql ){
115587 rc = SQLITE_NOMEM;
115588 }else{
115589 rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
115590 }
115591 sqlite3_free(zSql);
115592
115593 if( rc==SQLITE_OK ){
115594 const char **azCol; /* Output array */
115595 int nStr = 0; /* Size of all column names (incl. 0x00) */
115596 int nCol; /* Number of table columns */
115597 int i; /* Used to iterate through columns */
115598
115599 /* Loop through the returned columns. Set nStr to the number of bytes of
115600 ** space required to store a copy of each column name, including the
115601 ** nul-terminator byte. */
115602 nCol = sqlite3_column_count(pStmt);
115603 for(i=0; i<nCol; i++){
115604 const char *zCol = sqlite3_column_name(pStmt, i);
115605 nStr += strlen(zCol) + 1;
115606 }
115607
115608 /* Allocate and populate the array to return. */
115609 azCol = (const char **)sqlite3_malloc(sizeof(char *) * nCol + nStr);
115610 if( azCol==0 ){
115611 rc = SQLITE_NOMEM;
115612 }else{
115613 char *p = (char *)&azCol[nCol];
115614 for(i=0; i<nCol; i++){
115615 const char *zCol = sqlite3_column_name(pStmt, i);
115616 int n = strlen(zCol)+1;
115617 memcpy(p, zCol, n);
115618 azCol[i] = p;
115619 p += n;
115620 }
115621 }
115622 sqlite3_finalize(pStmt);
115623
115624 /* Set the output variables. */
115625 *pnCol = nCol;
115626 *pnStr = nStr;
115627 *pazCol = azCol;
115628 }
115629
115630 return rc;
115631 }
115632
115633 /*
115634 ** This function is the implementation of both the xConnect and xCreate
115635 ** methods of the FTS3 virtual table.
115636 **
@@ -115513,10 +115672,11 @@
115672 int bNoDocsize = 0; /* True to omit %_docsize table */
115673 int bDescIdx = 0; /* True to store descending indexes */
115674 char *zPrefix = 0; /* Prefix parameter value (or NULL) */
115675 char *zCompress = 0; /* compress=? parameter (or NULL) */
115676 char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
115677 char *zContent = 0; /* content=? parameter (or NULL) */
115678
115679 assert( strlen(argv[0])==4 );
115680 assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
115681 || (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4)
115682 );
@@ -115556,17 +115716,17 @@
115716 /* Check if it is an FTS4 special argument. */
115717 else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){
115718 struct Fts4Option {
115719 const char *zOpt;
115720 int nOpt;
 
115721 } aFts4Opt[] = {
115722 { "matchinfo", 9 }, /* 0 -> MATCHINFO */
115723 { "prefix", 6 }, /* 1 -> PREFIX */
115724 { "compress", 8 }, /* 2 -> COMPRESS */
115725 { "uncompress", 10 }, /* 3 -> UNCOMPRESS */
115726 { "order", 5 }, /* 4 -> ORDER */
115727 { "content", 7 } /* 5 -> CONTENT */
115728 };
115729
115730 int iOpt;
115731 if( !zVal ){
115732 rc = SQLITE_NOMEM;
@@ -115608,17 +115768,24 @@
115768 zVal = 0;
115769 break;
115770
115771 case 4: /* ORDER */
115772 if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
115773 && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4))
115774 ){
115775 *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal);
115776 rc = SQLITE_ERROR;
115777 }
115778 bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
115779 break;
115780
115781 default: /* CONTENT */
115782 assert( iOpt==5 );
115783 sqlite3_free(zUncompress);
115784 zContent = zVal;
115785 zVal = 0;
115786 break;
115787 }
115788 }
115789 sqlite3_free(zVal);
115790 }
115791 }
@@ -115627,10 +115794,30 @@
115794 else {
115795 nString += (int)(strlen(z) + 1);
115796 aCol[nCol++] = z;
115797 }
115798 }
115799
115800 /* If a content=xxx option was specified, the following:
115801 **
115802 ** 1. Ignore any compress= and uncompress= options.
115803 **
115804 ** 2. If no column names were specified as part of the CREATE VIRTUAL
115805 ** TABLE statement, use all columns from the content table.
115806 */
115807 if( rc==SQLITE_OK && zContent ){
115808 sqlite3_free(zCompress);
115809 sqlite3_free(zUncompress);
115810 zCompress = 0;
115811 zUncompress = 0;
115812 if( nCol==0 ){
115813 sqlite3_free((void*)aCol);
115814 aCol = 0;
115815 rc = fts3ContentColumns(db, argv[1], zContent, &aCol, &nCol, &nString);
115816 }
115817 assert( rc!=SQLITE_OK || nCol>0 );
115818 }
115819 if( rc!=SQLITE_OK ) goto fts3_init_out;
115820
115821 if( nCol==0 ){
115822 assert( nString==0 );
115823 aCol[0] = "content";
@@ -115671,10 +115858,12 @@
115858 p->pTokenizer = pTokenizer;
115859 p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
115860 p->bHasDocsize = (isFts4 && bNoDocsize==0);
115861 p->bHasStat = isFts4;
115862 p->bDescIdx = bDescIdx;
115863 p->zContentTbl = zContent;
115864 zContent = 0;
115865 TESTONLY( p->inTransaction = -1 );
115866 TESTONLY( p->mxSavepoint = -1 );
115867
115868 p->aIndex = (struct Fts3Index *)&p->azColumn[nCol];
115869 memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex);
@@ -115732,10 +115921,11 @@
115921 fts3_init_out:
115922 sqlite3_free(zPrefix);
115923 sqlite3_free(aIndex);
115924 sqlite3_free(zCompress);
115925 sqlite3_free(zUncompress);
115926 sqlite3_free(zContent);
115927 sqlite3_free((void *)aCol);
115928 if( rc!=SQLITE_OK ){
115929 if( p ){
115930 fts3DisconnectMethod((sqlite3_vtab *)p);
115931 }else if( pTokenizer ){
@@ -115882,40 +116072,69 @@
116072 sqlite3_free(pCsr->aMatchinfo);
116073 assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
116074 sqlite3_free(pCsr);
116075 return SQLITE_OK;
116076 }
116077
116078 /*
116079 ** If pCsr->pStmt has not been prepared (i.e. if pCsr->pStmt==0), then
116080 ** compose and prepare an SQL statement of the form:
116081 **
116082 ** "SELECT <columns> FROM %_content WHERE rowid = ?"
116083 **
116084 ** (or the equivalent for a content=xxx table) and set pCsr->pStmt to
116085 ** it. If an error occurs, return an SQLite error code.
116086 **
116087 ** Otherwise, set *ppStmt to point to pCsr->pStmt and return SQLITE_OK.
116088 */
116089 static int fts3CursorSeekStmt(Fts3Cursor *pCsr, sqlite3_stmt **ppStmt){
116090 int rc = SQLITE_OK;
116091 if( pCsr->pStmt==0 ){
116092 Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
116093 char *zSql;
116094 zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
116095 if( !zSql ) return SQLITE_NOMEM;
116096 rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
116097 sqlite3_free(zSql);
116098 }
116099 *ppStmt = pCsr->pStmt;
116100 return rc;
116101 }
116102
116103 /*
116104 ** Position the pCsr->pStmt statement so that it is on the row
116105 ** of the %_content table that contains the last match. Return
116106 ** SQLITE_OK on success.
116107 */
116108 static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
116109 int rc = SQLITE_OK;
116110 if( pCsr->isRequireSeek ){
116111 sqlite3_stmt *pStmt = 0;
116112
116113 rc = fts3CursorSeekStmt(pCsr, &pStmt);
116114 if( rc==SQLITE_OK ){
116115 sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
116116 pCsr->isRequireSeek = 0;
116117 if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
116118 return SQLITE_OK;
116119 }else{
116120 rc = sqlite3_reset(pCsr->pStmt);
116121 if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){
116122 /* If no row was found and no error has occured, then the %_content
116123 ** table is missing a row that is present in the full-text index.
116124 ** The data structures are corrupt. */
116125 rc = FTS_CORRUPT_VTAB;
116126 pCsr->isEof = 1;
116127 }
116128 }
116129 }
116130 }
116131
116132 if( rc!=SQLITE_OK && pContext ){
116133 sqlite3_result_error_code(pContext, rc);
116134 }
116135 return rc;
116136 }
116137
116138 /*
116139 ** This function is used to process a single interior node when searching
116140 ** a b-tree for a term or term prefix. The node data is passed to this
@@ -116351,20 +116570,20 @@
116570 int isSaveLeft, /* Save the left position */
116571 int isExact, /* If *pp1 is exactly nTokens before *pp2 */
116572 char **pp1, /* IN/OUT: Left input list */
116573 char **pp2 /* IN/OUT: Right input list */
116574 ){
116575 char *p = *pp;
116576 char *p1 = *pp1;
116577 char *p2 = *pp2;
116578 int iCol1 = 0;
116579 int iCol2 = 0;
116580
116581 /* Never set both isSaveLeft and isExact for the same invocation. */
116582 assert( isSaveLeft==0 || isExact==0 );
116583
116584 assert( p!=0 && *p1!=0 && *p2!=0 );
116585 if( *p1==POS_COLUMN ){
116586 p1++;
116587 p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
116588 }
116589 if( *p2==POS_COLUMN ){
@@ -116377,11 +116596,11 @@
116596 char *pSave = p;
116597 sqlite3_int64 iPrev = 0;
116598 sqlite3_int64 iPos1 = 0;
116599 sqlite3_int64 iPos2 = 0;
116600
116601 if( iCol1 ){
116602 *p++ = POS_COLUMN;
116603 p += sqlite3Fts3PutVarint(p, iCol1);
116604 }
116605
116606 assert( *p1!=POS_END && *p1!=POS_COLUMN );
@@ -116392,20 +116611,14 @@
116611 while( 1 ){
116612 if( iPos2==iPos1+nToken
116613 || (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken)
116614 ){
116615 sqlite3_int64 iSave;
 
 
 
 
 
 
 
116616 iSave = isSaveLeft ? iPos1 : iPos2;
116617 fts3PutDeltaVarint(&p, &iPrev, iSave+2); iPrev -= 2;
116618 pSave = 0;
116619 assert( p );
116620 }
116621 if( (!isSaveLeft && iPos2<=(iPos1+nToken)) || iPos2<=iPos1 ){
116622 if( (*p2&0xFE)==0 ) break;
116623 fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2;
116624 }else{
@@ -116450,11 +116663,11 @@
116663
116664 fts3PoslistCopy(0, &p2);
116665 fts3PoslistCopy(0, &p1);
116666 *pp1 = p1;
116667 *pp2 = p2;
116668 if( *pp==p ){
116669 return 0;
116670 }
116671 *p++ = 0x00;
116672 *pp = p;
116673 return 1;
@@ -116752,10 +116965,60 @@
116965 }
116966 }
116967
116968 *pnRight = p - aOut;
116969 }
116970
116971 /*
116972 ** Argument pList points to a position list nList bytes in size. This
116973 ** function checks to see if the position list contains any entries for
116974 ** a token in position 0 (of any column). If so, it writes argument iDelta
116975 ** to the output buffer pOut, followed by a position list consisting only
116976 ** of the entries from pList at position 0, and terminated by an 0x00 byte.
116977 ** The value returned is the number of bytes written to pOut (if any).
116978 */
116979 SQLITE_PRIVATE int sqlite3Fts3FirstFilter(
116980 sqlite3_int64 iDelta, /* Varint that may be written to pOut */
116981 char *pList, /* Position list (no 0x00 term) */
116982 int nList, /* Size of pList in bytes */
116983 char *pOut /* Write output here */
116984 ){
116985 int nOut = 0;
116986 int bWritten = 0; /* True once iDelta has been written */
116987 char *p = pList;
116988 char *pEnd = &pList[nList];
116989
116990 if( *p!=0x01 ){
116991 if( *p==0x02 ){
116992 nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta);
116993 pOut[nOut++] = 0x02;
116994 bWritten = 1;
116995 }
116996 fts3ColumnlistCopy(0, &p);
116997 }
116998
116999 while( p<pEnd && *p==0x01 ){
117000 sqlite3_int64 iCol;
117001 p++;
117002 p += sqlite3Fts3GetVarint(p, &iCol);
117003 if( *p==0x02 ){
117004 if( bWritten==0 ){
117005 nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta);
117006 bWritten = 1;
117007 }
117008 pOut[nOut++] = 0x01;
117009 nOut += sqlite3Fts3PutVarint(&pOut[nOut], iCol);
117010 pOut[nOut++] = 0x02;
117011 }
117012 fts3ColumnlistCopy(0, &p);
117013 }
117014 if( bWritten ){
117015 pOut[nOut++] = 0x00;
117016 }
117017
117018 return nOut;
117019 }
117020
117021
117022 /*
117023 ** Merge all doclists in the TermSelect.aaOutput[] array into a single
117024 ** doclist stored in TermSelect.aaOutput[0]. If successful, delete all
@@ -117109,10 +117372,11 @@
117372 pSegcsr = pTok->pSegcsr;
117373 memset(&tsc, 0, sizeof(TermSelect));
117374
117375 filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | FTS3_SEGMENT_REQUIRE_POS
117376 | (pTok->isPrefix ? FTS3_SEGMENT_PREFIX : 0)
117377 | (pTok->bFirst ? FTS3_SEGMENT_FIRST : 0)
117378 | (iColumn<p->nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0);
117379 filter.iCol = iColumn;
117380 filter.zTerm = pTok->z;
117381 filter.nTerm = pTok->n;
117382
@@ -117249,12 +117513,12 @@
117513
117514 if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
117515 return SQLITE_NOMEM;
117516 }
117517
117518 rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->bHasStat,
117519 p->nColumn, iCol, zQuery, -1, &pCsr->pExpr
117520 );
117521 if( rc!=SQLITE_OK ){
117522 if( rc==SQLITE_ERROR ){
117523 static const char *zErr = "malformed MATCH expression: [%s]";
117524 p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery);
@@ -117277,26 +117541,27 @@
117541 ** statement loops through all rows of the %_content table. For a
117542 ** full-text query or docid lookup, the statement retrieves a single
117543 ** row by docid.
117544 */
117545 if( idxNum==FTS3_FULLSCAN_SEARCH ){
117546 zSql = sqlite3_mprintf(
117547 "SELECT %s ORDER BY rowid %s",
117548 p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC")
117549 );
117550 if( zSql ){
117551 rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
117552 sqlite3_free(zSql);
117553 }else{
117554 rc = SQLITE_NOMEM;
117555 }
117556 }else if( idxNum==FTS3_DOCID_SEARCH ){
117557 rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt);
117558 if( rc==SQLITE_OK ){
117559 rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
117560 }
117561 }
117562 if( rc!=SQLITE_OK ) return rc;
117563
117564 return fts3NextMethod(pCursor);
117565 }
117566
117567 /*
@@ -117345,11 +117610,11 @@
117610 ** Return a blob which is a pointer to the cursor.
117611 */
117612 sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
117613 }else{
117614 rc = fts3CursorSeek(0, pCsr);
117615 if( rc==SQLITE_OK && sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){
117616 sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
117617 }
117618 }
117619
117620 assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
@@ -117638,19 +117903,26 @@
117903 ){
117904 Fts3Table *p = (Fts3Table *)pVtab;
117905 sqlite3 *db = p->db; /* Database connection */
117906 int rc; /* Return Code */
117907
117908 /* As it happens, the pending terms table is always empty here. This is
117909 ** because an "ALTER TABLE RENAME TABLE" statement inside a transaction
117910 ** always opens a savepoint transaction. And the xSavepoint() method
117911 ** flushes the pending terms table. But leave the (no-op) call to
117912 ** PendingTermsFlush() in in case that changes.
117913 */
117914 assert( p->nPendingData==0 );
117915 rc = sqlite3Fts3PendingTermsFlush(p);
117916
117917 if( p->zContentTbl==0 ){
117918 fts3DbExec(&rc, db,
117919 "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
117920 p->zDb, p->zName, zName
117921 );
117922 }
117923
 
 
 
 
117924 if( p->bHasDocsize ){
117925 fts3DbExec(&rc, db,
117926 "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';",
117927 p->zDb, p->zName, zName
117928 );
@@ -118005,25 +118277,24 @@
118277 **
118278 ** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
118279 */
118280 static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
118281 int iToken; /* Used to iterate through phrase tokens */
 
118282 char *aPoslist = 0; /* Position list for deferred tokens */
118283 int nPoslist = 0; /* Number of bytes in aPoslist */
118284 int iPrev = -1; /* Token number of previous deferred token */
118285
118286 assert( pPhrase->doclist.bFreeList==0 );
118287
118288 for(iToken=0; iToken<pPhrase->nToken; iToken++){
118289 Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
118290 Fts3DeferredToken *pDeferred = pToken->pDeferred;
118291
118292 if( pDeferred ){
118293 char *pList;
118294 int nList;
118295 int rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList);
118296 if( rc!=SQLITE_OK ) return rc;
118297
118298 if( pList==0 ){
118299 sqlite3_free(aPoslist);
118300 pPhrase->doclist.pList = 0;
@@ -118120,10 +118391,11 @@
118391 if( pCsr->bDesc==pTab->bDescIdx
118392 && bOptOk==1
118393 && p->nToken==1
118394 && pFirst->pSegcsr
118395 && pFirst->pSegcsr->bLookup
118396 && pFirst->bFirst==0
118397 ){
118398 /* Use the incremental approach. */
118399 int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
118400 rc = sqlite3Fts3MsrIncrStart(
118401 pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
@@ -118349,11 +118621,11 @@
118621 Fts3Expr *pExpr, /* Expression to consider */
118622 Fts3TokenAndCost **ppTC, /* Write new entries to *(*ppTC)++ */
118623 Fts3Expr ***ppOr, /* Write new OR root to *(*ppOr)++ */
118624 int *pRc /* IN/OUT: Error code */
118625 ){
118626 if( *pRc==SQLITE_OK ){
118627 if( pExpr->eType==FTSQUERY_PHRASE ){
118628 Fts3Phrase *pPhrase = pExpr->pPhrase;
118629 int i;
118630 for(i=0; *pRc==SQLITE_OK && i<pPhrase->nToken; i++){
118631 Fts3TokenAndCost *pTC = (*ppTC)++;
@@ -118363,10 +118635,15 @@
118635 pTC->pToken = &pPhrase->aToken[i];
118636 pTC->iCol = pPhrase->iColumn;
118637 *pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl);
118638 }
118639 }else if( pExpr->eType!=FTSQUERY_NOT ){
118640 assert( pExpr->eType==FTSQUERY_OR
118641 || pExpr->eType==FTSQUERY_AND
118642 || pExpr->eType==FTSQUERY_NEAR
118643 );
118644 assert( pExpr->pLeft && pExpr->pRight );
118645 if( pExpr->eType==FTSQUERY_OR ){
118646 pRoot = pExpr->pLeft;
118647 **ppOr = pRoot;
118648 (*ppOr)++;
118649 }
@@ -118466,10 +118743,19 @@
118743 int nOvfl = 0; /* Total overflow pages used by doclists */
118744 int nToken = 0; /* Total number of tokens in cluster */
118745
118746 int nMinEst = 0; /* The minimum count for any phrase so far. */
118747 int nLoad4 = 1; /* (Phrases that will be loaded)^4. */
118748
118749 /* Tokens are never deferred for FTS tables created using the content=xxx
118750 ** option. The reason being that it is not guaranteed that the content
118751 ** table actually contains the same data as the index. To prevent this from
118752 ** causing any problems, the deferred token optimization is completely
118753 ** disabled for content=xxx tables. */
118754 if( pTab->zContentTbl ){
118755 return SQLITE_OK;
118756 }
118757
118758 /* Count the tokens in this AND/NEAR cluster. If none of the doclists
118759 ** associated with the tokens spill onto overflow pages, or if there is
118760 ** only 1 token, exit early. No tokens to defer in this case. */
118761 for(ii=0; ii<nTC; ii++){
@@ -118529,11 +118815,15 @@
118815 Fts3PhraseToken *pToken = pTC->pToken;
118816 rc = sqlite3Fts3DeferToken(pCsr, pToken, pTC->iCol);
118817 fts3SegReaderCursorFree(pToken->pSegcsr);
118818 pToken->pSegcsr = 0;
118819 }else{
118820 /* Set nLoad4 to the value of (4^nOther) for the next iteration of the
118821 ** for-loop. Except, limit the value to 2^24 to prevent it from
118822 ** overflowing the 32-bit integer it is stored in. */
118823 if( ii<12 ) nLoad4 = nLoad4*4;
118824
118825 if( ii==0 || pTC->pPhrase->nToken>1 ){
118826 /* Either this is the cheapest token in the entire query, or it is
118827 ** part of a multi-token phrase. Either way, the entire doclist will
118828 ** (eventually) be loaded into memory. It may as well be now. */
118829 Fts3PhraseToken *pToken = pTC->pToken;
@@ -119990,10 +120280,11 @@
120280 */
120281 typedef struct ParseContext ParseContext;
120282 struct ParseContext {
120283 sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
120284 const char **azCol; /* Array of column names for fts3 table */
120285 int bFts4; /* True to allow FTS4-only syntax */
120286 int nCol; /* Number of entries in azCol[] */
120287 int iDefaultCol; /* Default column to query */
120288 int isNot; /* True if getNextNode() sees a unary - */
120289 sqlite3_context *pCtx; /* Write error message here */
120290 int nNest; /* Number of nested brackets */
@@ -120077,13 +120368,25 @@
120368
120369 if( iEnd<n && z[iEnd]=='*' ){
120370 pRet->pPhrase->aToken[0].isPrefix = 1;
120371 iEnd++;
120372 }
120373
120374 while( 1 ){
120375 if( !sqlite3_fts3_enable_parentheses
120376 && iStart>0 && z[iStart-1]=='-'
120377 ){
120378 pParse->isNot = 1;
120379 iStart--;
120380 }else if( pParse->bFts4 && iStart>0 && z[iStart-1]=='^' ){
120381 pRet->pPhrase->aToken[0].bFirst = 1;
120382 iStart--;
120383 }else{
120384 break;
120385 }
120386 }
120387
120388 }
120389 nConsumed = iEnd;
120390 }
120391
120392 pModule->xClose(pCursor);
@@ -120178,10 +120481,11 @@
120481 memcpy(&zTemp[nTemp], zByte, nByte);
120482 nTemp += nByte;
120483
120484 pToken->n = nByte;
120485 pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
120486 pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
120487 nToken = ii+1;
120488 }
120489 }
120490
120491 pModule->xClose(pCursor);
@@ -120629,10 +120933,11 @@
120933 ** match any table column.
120934 */
120935 SQLITE_PRIVATE int sqlite3Fts3ExprParse(
120936 sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
120937 char **azCol, /* Array of column names for fts3 table */
120938 int bFts4, /* True to allow FTS4-only syntax */
120939 int nCol, /* Number of entries in azCol[] */
120940 int iDefaultCol, /* Default column to query */
120941 const char *z, int n, /* Text of MATCH query */
120942 Fts3Expr **ppExpr /* OUT: Parsed query structure */
120943 ){
@@ -120642,10 +120947,11 @@
120947 sParse.pTokenizer = pTokenizer;
120948 sParse.azCol = (const char **)azCol;
120949 sParse.nCol = nCol;
120950 sParse.iDefaultCol = iDefaultCol;
120951 sParse.nNest = 0;
120952 sParse.bFts4 = bFts4;
120953 if( z==0 ){
120954 *ppExpr = 0;
120955 return SQLITE_OK;
120956 }
120957 if( n<0 ){
@@ -120831,11 +121137,11 @@
121137 for(ii=0; ii<nCol; ii++){
121138 azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]);
121139 }
121140
121141 rc = sqlite3Fts3ExprParse(
121142 pTokenizer, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
121143 );
121144 if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
121145 sqlite3_result_error(context, "Error parsing expression", -1);
121146 }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
121147 sqlite3_result_error_nomem(context);
@@ -122878,11 +123184,11 @@
123184 /* 2 */ "DELETE FROM %Q.'%q_content'",
123185 /* 3 */ "DELETE FROM %Q.'%q_segments'",
123186 /* 4 */ "DELETE FROM %Q.'%q_segdir'",
123187 /* 5 */ "DELETE FROM %Q.'%q_docsize'",
123188 /* 6 */ "DELETE FROM %Q.'%q_stat'",
123189 /* 7 */ "SELECT %s WHERE rowid=?",
123190 /* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1",
123191 /* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)",
123192 /* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
123193 /* 11 */ "INSERT INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)",
123194
@@ -122920,11 +123226,11 @@
123226 if( !pStmt ){
123227 char *zSql;
123228 if( eStmt==SQL_CONTENT_INSERT ){
123229 zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
123230 }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
123231 zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
123232 }else{
123233 zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
123234 }
123235 if( !zSql ){
123236 rc = SQLITE_NOMEM;
@@ -123031,21 +123337,28 @@
123337 ** We try to avoid this because if FTS3 returns any error when committing
123338 ** a transaction, the whole transaction will be rolled back. And this is
123339 ** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can
123340 ** still happen if the user reads data directly from the %_segments or
123341 ** %_segdir tables instead of going through FTS3 though.
123342 **
123343 ** This reasoning does not apply to a content=xxx table.
123344 */
123345 SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){
123346 int rc; /* Return code */
123347 sqlite3_stmt *pStmt; /* Statement used to obtain lock */
123348
123349 if( p->zContentTbl==0 ){
123350 rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
123351 if( rc==SQLITE_OK ){
123352 sqlite3_bind_null(pStmt, 1);
123353 sqlite3_step(pStmt);
123354 rc = sqlite3_reset(pStmt);
123355 }
123356 }else{
123357 rc = SQLITE_OK;
123358 }
123359
123360 return rc;
123361 }
123362
123363 /*
123364 ** Set *ppStmt to a statement handle that may be used to iterate through
@@ -123401,10 +123714,22 @@
123714 sqlite3_value **apVal, /* Array of values to insert */
123715 sqlite3_int64 *piDocid /* OUT: Docid for row just inserted */
123716 ){
123717 int rc; /* Return code */
123718 sqlite3_stmt *pContentInsert; /* INSERT INTO %_content VALUES(...) */
123719
123720 if( p->zContentTbl ){
123721 sqlite3_value *pRowid = apVal[p->nColumn+3];
123722 if( sqlite3_value_type(pRowid)==SQLITE_NULL ){
123723 pRowid = apVal[1];
123724 }
123725 if( sqlite3_value_type(pRowid)!=SQLITE_INTEGER ){
123726 return SQLITE_CONSTRAINT;
123727 }
123728 *piDocid = sqlite3_value_int64(pRowid);
123729 return SQLITE_OK;
123730 }
123731
123732 /* Locate the statement handle used to insert data into the %_content
123733 ** table. The SQL for this statement is:
123734 **
123735 ** INSERT INTO %_content VALUES(?, ?, ?, ...)
@@ -123452,18 +123777,20 @@
123777
123778 /*
123779 ** Remove all data from the FTS3 table. Clear the hash table containing
123780 ** pending terms.
123781 */
123782 static int fts3DeleteAll(Fts3Table *p, int bContent){
123783 int rc = SQLITE_OK; /* Return code */
123784
123785 /* Discard the contents of the pending-terms hash table. */
123786 sqlite3Fts3PendingTermsClear(p);
123787
123788 /* Delete everything from the shadow tables. Except, leave %_content as
123789 ** is if bContent is false. */
123790 assert( p->zContentTbl==0 || bContent==0 );
123791 if( bContent ) fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
123792 fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0);
123793 fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0);
123794 if( p->bHasDocsize ){
123795 fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0);
123796 }
@@ -124747,16 +125074,22 @@
125074 ** error occurs, an SQLite error code is returned.
125075 */
125076 static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
125077 sqlite3_stmt *pStmt;
125078 int rc;
125079 if( p->zContentTbl ){
125080 /* If using the content=xxx option, assume the table is never empty */
125081 *pisEmpty = 0;
125082 rc = SQLITE_OK;
125083 }else{
125084 rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
125085 if( rc==SQLITE_OK ){
125086 if( SQLITE_ROW==sqlite3_step(pStmt) ){
125087 *pisEmpty = sqlite3_column_int(pStmt, 0);
125088 }
125089 rc = sqlite3_reset(pStmt);
125090 }
 
125091 }
125092 return rc;
125093 }
125094
125095 /*
@@ -125104,10 +125437,11 @@
125437 int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY);
125438 int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS);
125439 int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
125440 int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX);
125441 int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN);
125442 int isFirst = (pCsr->pFilter->flags & FTS3_SEGMENT_FIRST);
125443
125444 Fts3SegReader **apSegment = pCsr->apSegment;
125445 int nSegment = pCsr->nSegment;
125446 Fts3SegFilter *pFilter = pCsr->pFilter;
125447 int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = (
@@ -125163,10 +125497,11 @@
125497 }
125498
125499 assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
125500 if( nMerge==1
125501 && !isIgnoreEmpty
125502 && !isFirst
125503 && (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0)
125504 ){
125505 pCsr->nDoclist = apSegment[0]->nDoclist;
125506 if( fts3SegReaderIsPending(apSegment[0]) ){
125507 rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist);
@@ -125228,16 +125563,28 @@
125563 if( !aNew ){
125564 return SQLITE_NOMEM;
125565 }
125566 pCsr->aBuffer = aNew;
125567 }
125568
125569 if( isFirst ){
125570 char *a = &pCsr->aBuffer[nDoclist];
125571 int nWrite;
125572
125573 nWrite = sqlite3Fts3FirstFilter(iDelta, pList, nList, a);
125574 if( nWrite ){
125575 iPrev = iDocid;
125576 nDoclist += nWrite;
125577 }
125578 }else{
125579 nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
125580 iPrev = iDocid;
125581 if( isRequirePos ){
125582 memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
125583 nDoclist += nList;
125584 pCsr->aBuffer[nDoclist++] = '\0';
125585 }
125586 }
125587 }
125588
125589 fts3SegReaderSort(apSegment, nMerge, j, xCmp);
125590 }
@@ -125409,13 +125756,13 @@
125756 ** Insert the sizes (in tokens) for each column of the document
125757 ** with docid equal to p->iPrevDocid. The sizes are encoded as
125758 ** a blob of varints.
125759 */
125760 static void fts3InsertDocsize(
125761 int *pRC, /* Result code */
125762 Fts3Table *p, /* Table into which to insert */
125763 u32 *aSz /* Sizes of each column, in tokens */
125764 ){
125765 char *pBlob; /* The BLOB encoding of the document size */
125766 int nBlob; /* Number of bytes in the BLOB */
125767 sqlite3_stmt *pStmt; /* Statement used to insert the encoding */
125768 int rc; /* Result code from subfunctions */
@@ -125532,10 +125879,90 @@
125879 sqlite3Fts3SegmentsClose(p);
125880 sqlite3Fts3PendingTermsClear(p);
125881
125882 return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc;
125883 }
125884
125885 /*
125886 ** This function is called when the user executes the following statement:
125887 **
125888 ** INSERT INTO <tbl>(<tbl>) VALUES('rebuild');
125889 **
125890 ** The entire FTS index is discarded and rebuilt. If the table is one
125891 ** created using the content=xxx option, then the new index is based on
125892 ** the current contents of the xxx table. Otherwise, it is rebuilt based
125893 ** on the contents of the %_content table.
125894 */
125895 static int fts3DoRebuild(Fts3Table *p){
125896 int rc; /* Return Code */
125897
125898 rc = fts3DeleteAll(p, 0);
125899 if( rc==SQLITE_OK ){
125900 u32 *aSz = 0;
125901 u32 *aSzIns = 0;
125902 u32 *aSzDel = 0;
125903 sqlite3_stmt *pStmt = 0;
125904 int nEntry = 0;
125905
125906 /* Compose and prepare an SQL statement to loop through the content table */
125907 char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist);
125908 if( !zSql ){
125909 rc = SQLITE_NOMEM;
125910 }else{
125911 rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
125912 sqlite3_free(zSql);
125913 }
125914
125915 if( rc==SQLITE_OK ){
125916 int nByte = sizeof(u32) * (p->nColumn+1)*3;
125917 aSz = (u32 *)sqlite3_malloc(nByte);
125918 if( aSz==0 ){
125919 rc = SQLITE_NOMEM;
125920 }else{
125921 memset(aSz, 0, nByte);
125922 aSzIns = &aSz[p->nColumn+1];
125923 aSzDel = &aSzIns[p->nColumn+1];
125924 }
125925 }
125926
125927 while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
125928 int iCol;
125929 rc = fts3PendingTermsDocid(p, sqlite3_column_int64(pStmt, 0));
125930 aSz[p->nColumn] = 0;
125931 for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
125932 const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
125933 rc = fts3PendingTermsAdd(p, z, iCol, &aSz[iCol]);
125934 aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
125935 }
125936 if( p->bHasDocsize ){
125937 fts3InsertDocsize(&rc, p, aSz);
125938 }
125939 if( rc!=SQLITE_OK ){
125940 sqlite3_finalize(pStmt);
125941 pStmt = 0;
125942 }else{
125943 nEntry++;
125944 for(iCol=0; iCol<=p->nColumn; iCol++){
125945 aSzIns[iCol] += aSz[iCol];
125946 }
125947 }
125948 }
125949 if( p->bHasStat ){
125950 fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nEntry);
125951 }
125952 sqlite3_free(aSz);
125953
125954 if( pStmt ){
125955 int rc2 = sqlite3_finalize(pStmt);
125956 if( rc==SQLITE_OK ){
125957 rc = rc2;
125958 }
125959 }
125960 }
125961
125962 return rc;
125963 }
125964
125965 /*
125966 ** Handle a 'special' INSERT of the form:
125967 **
125968 ** "INSERT INTO tbl(tbl) VALUES(<expr>)"
@@ -125550,10 +125977,12 @@
125977
125978 if( !zVal ){
125979 return SQLITE_NOMEM;
125980 }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
125981 rc = fts3DoOptimize(p, 0);
125982 }else if( nVal==7 && 0==sqlite3_strnicmp(zVal, "rebuild", 7) ){
125983 rc = fts3DoRebuild(p);
125984 #ifdef SQLITE_TEST
125985 }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
125986 p->nNodeSize = atoi(&zVal[9]);
125987 rc = SQLITE_OK;
125988 }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
@@ -125630,10 +126059,11 @@
126059 pTC->pTokenizer = pT;
126060 rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
126061 for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
126062 Fts3PhraseToken *pPT = pDef->pToken;
126063 if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
126064 && (pPT->bFirst==0 || iPos==0)
126065 && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
126066 && (0==memcmp(zToken, pPT->z, pPT->n))
126067 ){
126068 fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
126069 }
@@ -125721,18 +126151,22 @@
126151 if( rc==SQLITE_OK ){
126152 if( isEmpty ){
126153 /* Deleting this row means the whole table is empty. In this case
126154 ** delete the contents of all three tables and throw away any
126155 ** data in the pendingTerms hash table. */
126156 rc = fts3DeleteAll(p, 1);
126157 *pnDoc = *pnDoc - 1;
126158 }else{
126159 sqlite3_int64 iRemove = sqlite3_value_int64(pRowid);
126160 rc = fts3PendingTermsDocid(p, iRemove);
126161 fts3DeleteTerms(&rc, p, pRowid, aSzDel);
126162 if( p->zContentTbl==0 ){
126163 fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
126164 if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
126165 }else{
126166 *pnDoc = *pnDoc - 1;
126167 }
126168 if( p->bHasDocsize ){
126169 fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
126170 }
126171 }
126172 }
@@ -125788,11 +126222,11 @@
126222 ** should be deleted from the database before inserting the new row. Or,
126223 ** if the on-conflict mode is other than REPLACE, then this method must
126224 ** detect the conflict and return SQLITE_CONSTRAINT before beginning to
126225 ** modify the database file.
126226 */
126227 if( nArg>1 && p->zContentTbl==0 ){
126228 /* Find the value object that holds the new rowid value. */
126229 sqlite3_value *pNewRowid = apVal[3+p->nColumn];
126230 if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
126231 pNewRowid = apVal[1];
126232 }
@@ -125839,11 +126273,13 @@
126273
126274 /* If this is an INSERT or UPDATE operation, insert the new record. */
126275 if( nArg>1 && rc==SQLITE_OK ){
126276 if( bInsertDone==0 ){
126277 rc = fts3InsertData(p, apVal, pRowid);
126278 if( rc==SQLITE_CONSTRAINT && p->zContentTbl==0 ){
126279 rc = FTS_CORRUPT_VTAB;
126280 }
126281 }
126282 if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){
126283 rc = fts3PendingTermsDocid(p, *pRowid);
126284 }
126285 if( rc==SQLITE_OK ){
@@ -126259,10 +126695,11 @@
126695 pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
126696 if( pCsr ){
126697 int iFirst = 0;
126698 pPhrase->pList = pCsr;
126699 fts3GetDeltaPosition(&pCsr, &iFirst);
126700 assert( iFirst>=0 );
126701 pPhrase->pHead = pCsr;
126702 pPhrase->pTail = pCsr;
126703 pPhrase->iHead = iFirst;
126704 pPhrase->iTail = iFirst;
126705 }else{
@@ -127300,11 +127737,11 @@
127737 }
127738 }
127739
127740 if( !pTerm ){
127741 /* All offsets for this column have been gathered. */
127742 rc = SQLITE_DONE;
127743 }else{
127744 assert( iCurrent<=iMinPos );
127745 if( 0==(0xFE&*pTerm->pList) ){
127746 pTerm->pList = 0;
127747 }else{
@@ -127317,11 +127754,11 @@
127754 char aBuffer[64];
127755 sqlite3_snprintf(sizeof(aBuffer), aBuffer,
127756 "%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart
127757 );
127758 rc = fts3StringAppend(&res, aBuffer, -1);
127759 }else if( rc==SQLITE_DONE && pTab->zContentTbl==0 ){
127760 rc = FTS_CORRUPT_VTAB;
127761 }
127762 }
127763 }
127764 if( rc==SQLITE_DONE ){
127765
+3 -3
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -107,11 +107,11 @@
107107
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108108
** [sqlite_version()] and [sqlite_source_id()].
109109
*/
110110
#define SQLITE_VERSION "3.7.9"
111111
#define SQLITE_VERSION_NUMBER 3007009
112
-#define SQLITE_SOURCE_ID "2011-10-15 00:16:30 39408702a989f907261c298bf0947f3e68bd10fe"
112
+#define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf"
113113
114114
/*
115115
** CAPI3REF: Run-Time Library Version Numbers
116116
** KEYWORDS: sqlite3_version, sqlite3_sourceid
117117
**
@@ -1402,12 +1402,12 @@
14021402
** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
14031403
** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
14041404
** allocator is engaged to handle all of SQLites memory allocation needs.
14051405
** The first pointer (the memory pointer) must be aligned to an 8-byte
14061406
** boundary or subsequent behavior of SQLite will be undefined.
1407
-** The minimum allocation size is capped at 2^12. Reasonable values
1408
-** for the minimum allocation size are 2^5 through 2^8.</dd>
1407
+** The minimum allocation size is capped at 2**12. Reasonable values
1408
+** for the minimum allocation size are 2**5 through 2**8.</dd>
14091409
**
14101410
** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
14111411
** <dd> ^(This option takes a single argument which is a pointer to an
14121412
** instance of the [sqlite3_mutex_methods] structure. The argument specifies
14131413
** alternative low-level mutex routines to be used in place
14141414
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -107,11 +107,11 @@
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.7.9"
111 #define SQLITE_VERSION_NUMBER 3007009
112 #define SQLITE_SOURCE_ID "2011-10-15 00:16:30 39408702a989f907261c298bf0947f3e68bd10fe"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -1402,12 +1402,12 @@
1402 ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
1403 ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
1404 ** allocator is engaged to handle all of SQLites memory allocation needs.
1405 ** The first pointer (the memory pointer) must be aligned to an 8-byte
1406 ** boundary or subsequent behavior of SQLite will be undefined.
1407 ** The minimum allocation size is capped at 2^12. Reasonable values
1408 ** for the minimum allocation size are 2^5 through 2^8.</dd>
1409 **
1410 ** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
1411 ** <dd> ^(This option takes a single argument which is a pointer to an
1412 ** instance of the [sqlite3_mutex_methods] structure. The argument specifies
1413 ** alternative low-level mutex routines to be used in place
1414
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -107,11 +107,11 @@
107 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
108 ** [sqlite_version()] and [sqlite_source_id()].
109 */
110 #define SQLITE_VERSION "3.7.9"
111 #define SQLITE_VERSION_NUMBER 3007009
112 #define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf"
113
114 /*
115 ** CAPI3REF: Run-Time Library Version Numbers
116 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
117 **
@@ -1402,12 +1402,12 @@
1402 ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
1403 ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
1404 ** allocator is engaged to handle all of SQLites memory allocation needs.
1405 ** The first pointer (the memory pointer) must be aligned to an 8-byte
1406 ** boundary or subsequent behavior of SQLite will be undefined.
1407 ** The minimum allocation size is capped at 2**12. Reasonable values
1408 ** for the minimum allocation size are 2**5 through 2**8.</dd>
1409 **
1410 ** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
1411 ** <dd> ^(This option takes a single argument which is a pointer to an
1412 ** instance of the [sqlite3_mutex_methods] structure. The argument specifies
1413 ** alternative low-level mutex routines to be used in place
1414
+68 -36
--- src/stash.c
+++ src/stash.c
@@ -170,11 +170,11 @@
170170
int i;
171171
for(i=3; i<g.argc; i++){
172172
stash_add_file_or_dir(stashid, vid, g.argv[i]);
173173
}
174174
}else{
175
- stash_add_file_or_dir(stashid, vid, ".");
175
+ stash_add_file_or_dir(stashid, vid, g.zLocalRoot);
176176
}
177177
return stashid;
178178
}
179179
180180
/*
@@ -258,19 +258,20 @@
258258
file_delete(zOPath);
259259
}
260260
}
261261
db_finalize(&q);
262262
if( nConflict ){
263
- fossil_print("WARNING: %d merge conflicts - see messages above for details.\n",
264
- nConflict);
263
+ fossil_print(
264
+ "WARNING: %d merge conflicts - see messages above for details.\n",
265
+ nConflict);
265266
}
266267
}
267268
268269
/*
269270
** Show the diffs associate with a single stash.
270271
*/
271
-static void stash_diff(int stashid, const char *zDiffCmd){
272
+static void stash_diff(int stashid, const char *zDiffCmd, int diffFlags){
272273
Stmt q;
273274
Blob empty;
274275
blob_zero(&empty);
275276
db_prepare(&q,
276277
"SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
@@ -286,21 +287,21 @@
286287
char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
287288
Blob delta;
288289
if( rid==0 ){
289290
db_ephemeral_blob(&q, 6, &delta);
290291
fossil_print("ADDED %s\n", zNew);
291
- diff_print_index(zNew);
292
- diff_file_mem(&empty, &delta, zNew, zDiffCmd, 0);
292
+ diff_print_index(zNew, diffFlags);
293
+ diff_file_mem(&empty, &delta, zNew, zDiffCmd, diffFlags);
293294
}else if( isRemoved ){
294295
fossil_print("DELETE %s\n", zOrig);
295296
if( file_wd_islink(zOPath) ){
296297
blob_read_link(&delta, zOPath);
297298
}else{
298299
blob_read_from_file(&delta, zOPath);
299300
}
300
- diff_print_index(zNew);
301
- diff_file_mem(&delta, &empty, zOrig, zDiffCmd, 0);
301
+ diff_print_index(zNew, diffFlags);
302
+ diff_file_mem(&delta, &empty, zOrig, zDiffCmd, diffFlags);
302303
}else{
303304
Blob a, b, disk;
304305
int isOrigLink = file_wd_islink(zOPath);
305306
db_ephemeral_blob(&q, 6, &delta);
306307
if( isOrigLink ){
@@ -308,17 +309,17 @@
308309
}else{
309310
blob_read_from_file(&disk, zOPath);
310311
}
311312
fossil_print("CHANGED %s\n", zNew);
312313
if( !isOrigLink != !isLink ){
313
- diff_print_index(zNew);
314
- printf("--- %s\n+++ %s\n", zOrig, zNew);
314
+ diff_print_index(zNew, diffFlags);
315
+ diff_print_filenames(zOrig, zNew, diffFlags);
315316
printf("cannot compute difference between symlink and regular file\n");
316317
}else{
317318
content_get(rid, &a);
318319
blob_delta_apply(&a, &delta, &b);
319
- diff_file_mem(&disk, &b, zNew, zDiffCmd, 0);
320
+ diff_file_mem(&disk, &b, zNew, zDiffCmd, diffFlags);
320321
blob_reset(&a);
321322
blob_reset(&b);
322323
}
323324
blob_reset(&disk);
324325
}
@@ -363,35 +364,32 @@
363364
**
364365
** Usage: %fossil stash SUBCOMMAND ARGS...
365366
**
366367
** fossil stash
367368
** fossil stash save ?-m COMMENT? ?FILES...?
369
+** fossil stash snapshot ?-m COMMENT? ?FILES...?
368370
**
369371
** Save the current changes in the working tree as a new stash.
370372
** Then revert the changes back to the last check-in. If FILES
371373
** are listed, then only stash and revert the named files. The
372374
** "save" verb can be omitted if and only if there are no other
373
-** arguments.
375
+** arguments. The "snapshot" verb works the same as "save" but
376
+** omits the revert, keeping the check-out unchanged.
374377
**
375
-** fossil stash list
376
-** fossil stash ls
378
+** fossil stash list ?--detail?
379
+** fossil stash ls ?-l?
377380
**
378
-** List all changes sets currently stashed.
381
+** List all changes sets currently stashed. Show information about
382
+** individual files in each changeset if --detail or -l is used.
379383
**
380384
** fossil stash pop
381
-**
382
-** Apply the most recently create stash to the current working
383
-** check-out. Then delete that stash. This is equivalent to
384
-** doing an "apply" and a "drop" against the most recent stash.
385
-** This command is undoable.
386
-**
387385
** fossil stash apply ?STASHID?
388386
**
389
-** Apply the identified stash to the current working check-out.
390
-** If no STASHID is specified, use the most recent stash. Unlike
391
-** the "pop" command, the stash is retained so that it can be used
392
-** again. This command is undoable.
387
+** Apply STASHID or the most recently create stash to the current
388
+** working check-out. The "pop" command deletes that changeset from
389
+** the stash after applying it but the "apply" command retains the
390
+** changeset.
393391
**
394392
** fossil stash goto ?STASHID?
395393
**
396394
** Update to the baseline checkout for STASHID then apply the
397395
** changes of STASHID. Keep STASHID so that it can be reused
@@ -401,15 +399,10 @@
401399
** fossil stash rm ?STASHID? ?--all?
402400
**
403401
** Forget everything about STASHID. Forget the whole stash if the
404402
** --all flag is used. Individual drops are undoable but --all is not.
405403
**
406
-** fossil stash snapshot ?-m COMMENT? ?FILES...?
407
-**
408
-** Save the current changes in the working tree as a new stash
409
-** but, unlike "save", do not revert those changes.
410
-**
411404
** fossil stash diff ?STASHID?
412405
** fossil stash gdiff ?STASHID?
413406
**
414407
** Show diffs of the current working directory and what that
415408
** directory would be if STASHID were applied.
@@ -455,40 +448,71 @@
455448
}else
456449
if( memcmp(zCmd, "snapshot", nCmd)==0 ){
457450
stash_create();
458451
}else
459452
if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
460
- Stmt q;
453
+ Stmt q, q2;
461454
int n = 0;
455
+ int fDetail = find_option("detail","l",0)!=0;
462456
verify_all_options();
463457
db_prepare(&q,
464458
"SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid),"
465459
" comment, datetime(ctime) FROM stash"
466460
" ORDER BY ctime DESC"
467461
);
462
+ if( fDetail ){
463
+ db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname"
464
+ " FROM stashfile WHERE stashid=$id");
465
+ }
468466
while( db_step(&q)==SQLITE_ROW ){
467
+ int stashid = db_column_int(&q, 0);
469468
const char *zCom;
470469
n++;
471470
fossil_print("%5d: [%.14s] on %s\n",
472
- db_column_int(&q, 0),
471
+ stashid,
473472
db_column_text(&q, 1),
474473
db_column_text(&q, 3)
475474
);
476475
zCom = db_column_text(&q, 2);
477476
if( zCom && zCom[0] ){
478477
fossil_print(" ");
479478
comment_print(zCom, 7, 79);
480479
}
480
+ if( fDetail ){
481
+ db_bind_int(&q2, "$id", stashid);
482
+ while( db_step(&q2)==SQLITE_ROW ){
483
+ int isAdded = db_column_int(&q2, 0);
484
+ int isRemoved = db_column_int(&q2, 1);
485
+ const char *zOrig = db_column_text(&q2, 2);
486
+ const char *zNew = db_column_text(&q2, 3);
487
+ if( isAdded ){
488
+ fossil_print(" ADD %s\n", zNew);
489
+ }else if( isRemoved ){
490
+ fossil_print(" REMOVE %s\n", zOrig);
491
+ }else if( fossil_strcmp(zOrig,zNew)!=0 ){
492
+ fossil_print(" RENAME %s -> %s\n", zOrig, zNew);
493
+ }else{
494
+ fossil_print(" EDIT %s\n", zOrig);
495
+ }
496
+ }
497
+ db_reset(&q2);
498
+ }
481499
}
482500
db_finalize(&q);
501
+ if( fDetail ) db_finalize(&q2);
483502
if( n==0 ) fossil_print("empty stash\n");
484503
}else
485504
if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){
486505
int allFlag = find_option("all", 0, 0)!=0;
487
- if( g.argc>4 ) usage("apply STASHID");
506
+ if( g.argc>4 ) usage("drop STASHID");
488507
if( allFlag ){
489
- db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;");
508
+ Blob ans;
509
+ blob_zero(&ans);
510
+ prompt_user("This action is not undoable. Continue (y/N)? ", &ans);
511
+ if( blob_str(&ans)[0]=='y' ){
512
+ db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;");
513
+ }
490514
}else{
491515
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
492516
undo_begin();
493517
undo_save_stash(stashid);
494518
stash_drop(stashid);
@@ -526,20 +550,28 @@
526550
stashid);
527551
undo_finish();
528552
}else
529553
if( memcmp(zCmd, "diff", nCmd)==0 ){
530554
const char *zDiffCmd = db_get("diff-command", 0);
555
+ int diffFlags = diff_options();
531556
if( g.argc>4 ) usage("diff STASHID");
532557
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
533
- stash_diff(stashid, zDiffCmd);
558
+ stash_diff(stashid, zDiffCmd, diffFlags);
534559
}else
535560
if( memcmp(zCmd, "gdiff", nCmd)==0 ){
536561
const char *zDiffCmd = db_get("gdiff-command", 0);
562
+ int diffFlags = diff_options();
537563
if( g.argc>4 ) usage("diff STASHID");
538564
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
539
- stash_diff(stashid, zDiffCmd);
565
+ stash_diff(stashid, zDiffCmd, diffFlags);
566
+ }else
567
+ if( memcmp(zCmd, "help", nCmd)==0 ){
568
+ g.argv[1] = "help";
569
+ g.argv[2] = "stash";
570
+ g.argc = 3;
571
+ help_cmd();
540572
}else
541573
{
542574
usage("SUBCOMMAND ARGS...");
543575
}
544576
db_end_transaction(0);
545577
}
546578
--- src/stash.c
+++ src/stash.c
@@ -170,11 +170,11 @@
170 int i;
171 for(i=3; i<g.argc; i++){
172 stash_add_file_or_dir(stashid, vid, g.argv[i]);
173 }
174 }else{
175 stash_add_file_or_dir(stashid, vid, ".");
176 }
177 return stashid;
178 }
179
180 /*
@@ -258,19 +258,20 @@
258 file_delete(zOPath);
259 }
260 }
261 db_finalize(&q);
262 if( nConflict ){
263 fossil_print("WARNING: %d merge conflicts - see messages above for details.\n",
264 nConflict);
 
265 }
266 }
267
268 /*
269 ** Show the diffs associate with a single stash.
270 */
271 static void stash_diff(int stashid, const char *zDiffCmd){
272 Stmt q;
273 Blob empty;
274 blob_zero(&empty);
275 db_prepare(&q,
276 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
@@ -286,21 +287,21 @@
286 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
287 Blob delta;
288 if( rid==0 ){
289 db_ephemeral_blob(&q, 6, &delta);
290 fossil_print("ADDED %s\n", zNew);
291 diff_print_index(zNew);
292 diff_file_mem(&empty, &delta, zNew, zDiffCmd, 0);
293 }else if( isRemoved ){
294 fossil_print("DELETE %s\n", zOrig);
295 if( file_wd_islink(zOPath) ){
296 blob_read_link(&delta, zOPath);
297 }else{
298 blob_read_from_file(&delta, zOPath);
299 }
300 diff_print_index(zNew);
301 diff_file_mem(&delta, &empty, zOrig, zDiffCmd, 0);
302 }else{
303 Blob a, b, disk;
304 int isOrigLink = file_wd_islink(zOPath);
305 db_ephemeral_blob(&q, 6, &delta);
306 if( isOrigLink ){
@@ -308,17 +309,17 @@
308 }else{
309 blob_read_from_file(&disk, zOPath);
310 }
311 fossil_print("CHANGED %s\n", zNew);
312 if( !isOrigLink != !isLink ){
313 diff_print_index(zNew);
314 printf("--- %s\n+++ %s\n", zOrig, zNew);
315 printf("cannot compute difference between symlink and regular file\n");
316 }else{
317 content_get(rid, &a);
318 blob_delta_apply(&a, &delta, &b);
319 diff_file_mem(&disk, &b, zNew, zDiffCmd, 0);
320 blob_reset(&a);
321 blob_reset(&b);
322 }
323 blob_reset(&disk);
324 }
@@ -363,35 +364,32 @@
363 **
364 ** Usage: %fossil stash SUBCOMMAND ARGS...
365 **
366 ** fossil stash
367 ** fossil stash save ?-m COMMENT? ?FILES...?
 
368 **
369 ** Save the current changes in the working tree as a new stash.
370 ** Then revert the changes back to the last check-in. If FILES
371 ** are listed, then only stash and revert the named files. The
372 ** "save" verb can be omitted if and only if there are no other
373 ** arguments.
 
374 **
375 ** fossil stash list
376 ** fossil stash ls
377 **
378 ** List all changes sets currently stashed.
 
379 **
380 ** fossil stash pop
381 **
382 ** Apply the most recently create stash to the current working
383 ** check-out. Then delete that stash. This is equivalent to
384 ** doing an "apply" and a "drop" against the most recent stash.
385 ** This command is undoable.
386 **
387 ** fossil stash apply ?STASHID?
388 **
389 ** Apply the identified stash to the current working check-out.
390 ** If no STASHID is specified, use the most recent stash. Unlike
391 ** the "pop" command, the stash is retained so that it can be used
392 ** again. This command is undoable.
393 **
394 ** fossil stash goto ?STASHID?
395 **
396 ** Update to the baseline checkout for STASHID then apply the
397 ** changes of STASHID. Keep STASHID so that it can be reused
@@ -401,15 +399,10 @@
401 ** fossil stash rm ?STASHID? ?--all?
402 **
403 ** Forget everything about STASHID. Forget the whole stash if the
404 ** --all flag is used. Individual drops are undoable but --all is not.
405 **
406 ** fossil stash snapshot ?-m COMMENT? ?FILES...?
407 **
408 ** Save the current changes in the working tree as a new stash
409 ** but, unlike "save", do not revert those changes.
410 **
411 ** fossil stash diff ?STASHID?
412 ** fossil stash gdiff ?STASHID?
413 **
414 ** Show diffs of the current working directory and what that
415 ** directory would be if STASHID were applied.
@@ -455,40 +448,71 @@
455 }else
456 if( memcmp(zCmd, "snapshot", nCmd)==0 ){
457 stash_create();
458 }else
459 if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
460 Stmt q;
461 int n = 0;
 
462 verify_all_options();
463 db_prepare(&q,
464 "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid),"
465 " comment, datetime(ctime) FROM stash"
466 " ORDER BY ctime DESC"
467 );
 
 
 
 
468 while( db_step(&q)==SQLITE_ROW ){
 
469 const char *zCom;
470 n++;
471 fossil_print("%5d: [%.14s] on %s\n",
472 db_column_int(&q, 0),
473 db_column_text(&q, 1),
474 db_column_text(&q, 3)
475 );
476 zCom = db_column_text(&q, 2);
477 if( zCom && zCom[0] ){
478 fossil_print(" ");
479 comment_print(zCom, 7, 79);
480 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
481 }
482 db_finalize(&q);
 
483 if( n==0 ) fossil_print("empty stash\n");
484 }else
485 if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){
486 int allFlag = find_option("all", 0, 0)!=0;
487 if( g.argc>4 ) usage("apply STASHID");
488 if( allFlag ){
489 db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;");
 
 
 
 
 
490 }else{
491 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
492 undo_begin();
493 undo_save_stash(stashid);
494 stash_drop(stashid);
@@ -526,20 +550,28 @@
526 stashid);
527 undo_finish();
528 }else
529 if( memcmp(zCmd, "diff", nCmd)==0 ){
530 const char *zDiffCmd = db_get("diff-command", 0);
 
531 if( g.argc>4 ) usage("diff STASHID");
532 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
533 stash_diff(stashid, zDiffCmd);
534 }else
535 if( memcmp(zCmd, "gdiff", nCmd)==0 ){
536 const char *zDiffCmd = db_get("gdiff-command", 0);
 
537 if( g.argc>4 ) usage("diff STASHID");
538 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
539 stash_diff(stashid, zDiffCmd);
 
 
 
 
 
 
540 }else
541 {
542 usage("SUBCOMMAND ARGS...");
543 }
544 db_end_transaction(0);
545 }
546
--- src/stash.c
+++ src/stash.c
@@ -170,11 +170,11 @@
170 int i;
171 for(i=3; i<g.argc; i++){
172 stash_add_file_or_dir(stashid, vid, g.argv[i]);
173 }
174 }else{
175 stash_add_file_or_dir(stashid, vid, g.zLocalRoot);
176 }
177 return stashid;
178 }
179
180 /*
@@ -258,19 +258,20 @@
258 file_delete(zOPath);
259 }
260 }
261 db_finalize(&q);
262 if( nConflict ){
263 fossil_print(
264 "WARNING: %d merge conflicts - see messages above for details.\n",
265 nConflict);
266 }
267 }
268
269 /*
270 ** Show the diffs associate with a single stash.
271 */
272 static void stash_diff(int stashid, const char *zDiffCmd, int diffFlags){
273 Stmt q;
274 Blob empty;
275 blob_zero(&empty);
276 db_prepare(&q,
277 "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta"
@@ -286,21 +287,21 @@
287 char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
288 Blob delta;
289 if( rid==0 ){
290 db_ephemeral_blob(&q, 6, &delta);
291 fossil_print("ADDED %s\n", zNew);
292 diff_print_index(zNew, diffFlags);
293 diff_file_mem(&empty, &delta, zNew, zDiffCmd, diffFlags);
294 }else if( isRemoved ){
295 fossil_print("DELETE %s\n", zOrig);
296 if( file_wd_islink(zOPath) ){
297 blob_read_link(&delta, zOPath);
298 }else{
299 blob_read_from_file(&delta, zOPath);
300 }
301 diff_print_index(zNew, diffFlags);
302 diff_file_mem(&delta, &empty, zOrig, zDiffCmd, diffFlags);
303 }else{
304 Blob a, b, disk;
305 int isOrigLink = file_wd_islink(zOPath);
306 db_ephemeral_blob(&q, 6, &delta);
307 if( isOrigLink ){
@@ -308,17 +309,17 @@
309 }else{
310 blob_read_from_file(&disk, zOPath);
311 }
312 fossil_print("CHANGED %s\n", zNew);
313 if( !isOrigLink != !isLink ){
314 diff_print_index(zNew, diffFlags);
315 diff_print_filenames(zOrig, zNew, diffFlags);
316 printf("cannot compute difference between symlink and regular file\n");
317 }else{
318 content_get(rid, &a);
319 blob_delta_apply(&a, &delta, &b);
320 diff_file_mem(&disk, &b, zNew, zDiffCmd, diffFlags);
321 blob_reset(&a);
322 blob_reset(&b);
323 }
324 blob_reset(&disk);
325 }
@@ -363,35 +364,32 @@
364 **
365 ** Usage: %fossil stash SUBCOMMAND ARGS...
366 **
367 ** fossil stash
368 ** fossil stash save ?-m COMMENT? ?FILES...?
369 ** fossil stash snapshot ?-m COMMENT? ?FILES...?
370 **
371 ** Save the current changes in the working tree as a new stash.
372 ** Then revert the changes back to the last check-in. If FILES
373 ** are listed, then only stash and revert the named files. The
374 ** "save" verb can be omitted if and only if there are no other
375 ** arguments. The "snapshot" verb works the same as "save" but
376 ** omits the revert, keeping the check-out unchanged.
377 **
378 ** fossil stash list ?--detail?
379 ** fossil stash ls ?-l?
380 **
381 ** List all changes sets currently stashed. Show information about
382 ** individual files in each changeset if --detail or -l is used.
383 **
384 ** fossil stash pop
 
 
 
 
 
 
385 ** fossil stash apply ?STASHID?
386 **
387 ** Apply STASHID or the most recently create stash to the current
388 ** working check-out. The "pop" command deletes that changeset from
389 ** the stash after applying it but the "apply" command retains the
390 ** changeset.
391 **
392 ** fossil stash goto ?STASHID?
393 **
394 ** Update to the baseline checkout for STASHID then apply the
395 ** changes of STASHID. Keep STASHID so that it can be reused
@@ -401,15 +399,10 @@
399 ** fossil stash rm ?STASHID? ?--all?
400 **
401 ** Forget everything about STASHID. Forget the whole stash if the
402 ** --all flag is used. Individual drops are undoable but --all is not.
403 **
 
 
 
 
 
404 ** fossil stash diff ?STASHID?
405 ** fossil stash gdiff ?STASHID?
406 **
407 ** Show diffs of the current working directory and what that
408 ** directory would be if STASHID were applied.
@@ -455,40 +448,71 @@
448 }else
449 if( memcmp(zCmd, "snapshot", nCmd)==0 ){
450 stash_create();
451 }else
452 if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
453 Stmt q, q2;
454 int n = 0;
455 int fDetail = find_option("detail","l",0)!=0;
456 verify_all_options();
457 db_prepare(&q,
458 "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid),"
459 " comment, datetime(ctime) FROM stash"
460 " ORDER BY ctime DESC"
461 );
462 if( fDetail ){
463 db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname"
464 " FROM stashfile WHERE stashid=$id");
465 }
466 while( db_step(&q)==SQLITE_ROW ){
467 int stashid = db_column_int(&q, 0);
468 const char *zCom;
469 n++;
470 fossil_print("%5d: [%.14s] on %s\n",
471 stashid,
472 db_column_text(&q, 1),
473 db_column_text(&q, 3)
474 );
475 zCom = db_column_text(&q, 2);
476 if( zCom && zCom[0] ){
477 fossil_print(" ");
478 comment_print(zCom, 7, 79);
479 }
480 if( fDetail ){
481 db_bind_int(&q2, "$id", stashid);
482 while( db_step(&q2)==SQLITE_ROW ){
483 int isAdded = db_column_int(&q2, 0);
484 int isRemoved = db_column_int(&q2, 1);
485 const char *zOrig = db_column_text(&q2, 2);
486 const char *zNew = db_column_text(&q2, 3);
487 if( isAdded ){
488 fossil_print(" ADD %s\n", zNew);
489 }else if( isRemoved ){
490 fossil_print(" REMOVE %s\n", zOrig);
491 }else if( fossil_strcmp(zOrig,zNew)!=0 ){
492 fossil_print(" RENAME %s -> %s\n", zOrig, zNew);
493 }else{
494 fossil_print(" EDIT %s\n", zOrig);
495 }
496 }
497 db_reset(&q2);
498 }
499 }
500 db_finalize(&q);
501 if( fDetail ) db_finalize(&q2);
502 if( n==0 ) fossil_print("empty stash\n");
503 }else
504 if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){
505 int allFlag = find_option("all", 0, 0)!=0;
506 if( g.argc>4 ) usage("drop STASHID");
507 if( allFlag ){
508 Blob ans;
509 blob_zero(&ans);
510 prompt_user("This action is not undoable. Continue (y/N)? ", &ans);
511 if( blob_str(&ans)[0]=='y' ){
512 db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;");
513 }
514 }else{
515 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
516 undo_begin();
517 undo_save_stash(stashid);
518 stash_drop(stashid);
@@ -526,20 +550,28 @@
550 stashid);
551 undo_finish();
552 }else
553 if( memcmp(zCmd, "diff", nCmd)==0 ){
554 const char *zDiffCmd = db_get("diff-command", 0);
555 int diffFlags = diff_options();
556 if( g.argc>4 ) usage("diff STASHID");
557 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
558 stash_diff(stashid, zDiffCmd, diffFlags);
559 }else
560 if( memcmp(zCmd, "gdiff", nCmd)==0 ){
561 const char *zDiffCmd = db_get("gdiff-command", 0);
562 int diffFlags = diff_options();
563 if( g.argc>4 ) usage("diff STASHID");
564 stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
565 stash_diff(stashid, zDiffCmd, diffFlags);
566 }else
567 if( memcmp(zCmd, "help", nCmd)==0 ){
568 g.argv[1] = "help";
569 g.argv[2] = "stash";
570 g.argc = 3;
571 help_cmd();
572 }else
573 {
574 usage("SUBCOMMAND ARGS...");
575 }
576 db_end_transaction(0);
577 }
578
+86 -25
--- src/style.c
+++ src/style.c
@@ -210,37 +210,38 @@
210210
@ } else {
211211
@ puts "Not logged in"
212212
@ }
213213
@ </th1></div>
214214
@ </div>
215
-@ <div class="mainmenu"><th1>
216
-@ html "<a href='$home$index_page'>Home</a> "
215
+@ <div class="mainmenu">
216
+@ <th1>
217
+@ html "<a href='$home$index_page'>Home</a>\n"
217218
@ if {[anycap jor]} {
218
-@ html "<a href='$home/timeline'>Timeline</a> "
219
+@ html "<a href='$home/timeline'>Timeline</a>\n"
219220
@ }
220221
@ if {[hascap oh]} {
221
-@ html "<a href='$home/dir?ci=tip'>Files</a> "
222
+@ html "<a href='$home/dir?ci=tip'>Files</a>\n"
222223
@ }
223224
@ if {[hascap o]} {
224
-@ html "<a href='$home/brlist'>Branches</a> "
225
-@ html "<a href='$home/taglist'>Tags</a> "
225
+@ html "<a href='$home/brlist'>Branches</a>\n"
226
+@ html "<a href='$home/taglist'>Tags</a>\n"
226227
@ }
227228
@ if {[hascap r]} {
228
-@ html "<a href='$home/reportlist'>Tickets</a> "
229
+@ html "<a href='$home/reportlist'>Tickets</a>\n"
229230
@ }
230231
@ if {[hascap j]} {
231
-@ html "<a href='$home/wiki'>Wiki</a> "
232
+@ html "<a href='$home/wiki'>Wiki</a>\n"
232233
@ }
233234
@ if {[hascap s]} {
234
-@ html "<a href='$home/setup'>Admin</a> "
235
+@ html "<a href='$home/setup'>Admin</a>\n"
235236
@ } elseif {[hascap a]} {
236
-@ html "<a href='$home/setup_ulist'>Users</a> "
237
+@ html "<a href='$home/setup_ulist'>Users</a>\n"
237238
@ }
238239
@ if {[info exists login]} {
239
-@ html "<a href='$home/login'>Logout</a> "
240
+@ html "<a href='$home/login'>Logout</a>\n"
240241
@ } else {
241
-@ html "<a href='$home/login'>Login</a> "
242
+@ html "<a href='$home/login'>Login</a>\n"
242243
@ }
243244
@ </th1></div>
244245
;
245246
246247
/*
@@ -320,23 +321,24 @@
320321
@ background-color: #558195;
321322
@ color: white;
322323
@ }
323324
@
324325
@ /* The submenu bar that *sometimes* appears below the main menu */
325
-@ div.submenu {
326
+@ div.submenu, div.sectionmenu {
326327
@ padding: 3px 10px 3px 0px;
327328
@ font-size: 0.9em;
328329
@ text-align: center;
329330
@ background-color: #456878;
330331
@ color: white;
331332
@ }
332
-@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
333
+@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
334
+@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
333335
@ padding: 3px 10px 3px 10px;
334336
@ color: white;
335337
@ text-decoration: none;
336338
@ }
337
-@ div.mainmenu a:hover, div.submenu a:hover {
339
+@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
338340
@ color: #558195;
339341
@ background-color: white;
340342
@ }
341343
@
342344
@ /* All page content from the bottom of the menu or submenu down to
@@ -396,10 +398,69 @@
396398
@ table.label-value th {
397399
@ vertical-align: top;
398400
@ text-align: right;
399401
@ padding: 0.2ex 2ex;
400402
@ }
403
+@
404
+@ /* Side-by-side diff */
405
+@ table.sbsdiff {
406
+@ background-color: white;
407
+@ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
408
+@ font-size: 8pt;
409
+@ border-collapse:collapse;
410
+@ white-space: pre;
411
+@ width: 98%;
412
+@ border: 1px #000 dashed;
413
+@ margin-left: auto;
414
+@ margin-right: auto;
415
+@ }
416
+@
417
+@ table.sbsdiff th.diffhdr {
418
+@ border-bottom: dotted;
419
+@ border-width: 1px;
420
+@ }
421
+@
422
+@ table.sbsdiff tr td {
423
+@ white-space: pre;
424
+@ padding-left: 3px;
425
+@ padding-right: 3px;
426
+@ margin: 0px;
427
+@ vertical-align: top;
428
+@ }
429
+@
430
+@ table.sbsdiff tr td.lineno {
431
+@ text-align: right;
432
+@ }
433
+@
434
+@ table.sbsdiff tr td.srcline {
435
+@ }
436
+@
437
+@ table.sbsdiff tr td.meta {
438
+@ background-color: rgb(170, 160, 255);
439
+@ text-align: center;
440
+@ }
441
+@
442
+@ table.sbsdiff tr td.added {
443
+@ background-color: rgb(180, 250, 180);
444
+@ }
445
+@ table.sbsdiff tr td.addedvoid {
446
+@ background-color: rgb(190, 190, 180);
447
+@ }
448
+@
449
+@ table.sbsdiff tr td.removed {
450
+@ background-color: rgb(250, 130, 130);
451
+@ }
452
+@ table.sbsdiff tr td.removedvoid {
453
+@ background-color: rgb(190, 190, 180);
454
+@ }
455
+@
456
+@ table.sbsdiff tr td.changed {
457
+@ background-color: rgb(210, 210, 200);
458
+@ }
459
+@ table.sbsdiff tr td.changedvoid {
460
+@ background-color: rgb(190, 190, 180);
461
+@ }
401462
@
402463
;
403464
404465
405466
/* The following table contains bits of default CSS that must
@@ -470,11 +531,11 @@
470531
@ vertical-align: top;
471532
@ text-align: right;
472533
},
473534
{ "td.timelineGraph",
474535
"the format for the grap placeholder cells in timelines",
475
- @ width: 20;
536
+ @ width: 20px;
476537
@ text-align: left;
477538
@ vertical-align: top;
478539
},
479540
{ "a.tagLink",
480541
"the format for the tag links",
@@ -564,11 +625,11 @@
564625
@ vertical-align: top
565626
},
566627
{ "table.usetupUserList",
567628
"format for the user list table on the user setup page",
568629
@ outline-style: double;
569
- @ outline-width: 1;
630
+ @ outline-width: 1px;
570631
@ padding: 10px;
571632
},
572633
{ "th.usetupListUser",
573634
"format for table header user in user list on user setup page",
574635
@ text-align: right;
@@ -688,17 +749,17 @@
688749
@ border-color: #000000;
689750
@ border-style: solid;
690751
},
691752
{ "input.checkinUserColor",
692753
"format for user color input on checkin edit page",
693
- @ # no special definitions, class defined, to enable color pickers, f.e.:
694
- @ # add the color picker found at http:jscolor.com as java script include
695
- @ # to the header and configure the java script file with
696
- @ # 1. use as bindClass :checkinUserColor
697
- @ # 2. change the default hash adding behaviour to ON
698
- @ # or change the class defition of element identified by id="clrcust"
699
- @ # to a standard jscolor definition with java script in the footer.
754
+ @ /* no special definitions, class defined, to enable color pickers, f.e.:
755
+ @ ** add the color picker found at http:jscolor.com as java script include
756
+ @ ** to the header and configure the java script file with
757
+ @ ** 1. use as bindClass :checkinUserColor
758
+ @ ** 2. change the default hash adding behaviour to ON
759
+ @ ** or change the class defition of element identified by id="clrcust"
760
+ @ ** to a standard jscolor definition with java script in the footer. */
700761
},
701762
{ "div.endContent",
702763
"format for end of content area, to be used to clear page flow(sidebox on branch,..",
703764
@ clear: both;
704765
},
@@ -718,11 +779,11 @@
718779
},
719780
{ "span.thTrace",
720781
"format for th script trace messages",
721782
@ color: red;
722783
},
723
- { "p:reportError",
784
+ { "p.reportError",
724785
"format for report configuration errors",
725786
@ color: red;
726787
@ font-weight: bold;
727788
},
728789
{ "blockquote.reportError",
729790
--- src/style.c
+++ src/style.c
@@ -210,37 +210,38 @@
210 @ } else {
211 @ puts "Not logged in"
212 @ }
213 @ </th1></div>
214 @ </div>
215 @ <div class="mainmenu"><th1>
216 @ html "<a href='$home$index_page'>Home</a> "
 
217 @ if {[anycap jor]} {
218 @ html "<a href='$home/timeline'>Timeline</a> "
219 @ }
220 @ if {[hascap oh]} {
221 @ html "<a href='$home/dir?ci=tip'>Files</a> "
222 @ }
223 @ if {[hascap o]} {
224 @ html "<a href='$home/brlist'>Branches</a> "
225 @ html "<a href='$home/taglist'>Tags</a> "
226 @ }
227 @ if {[hascap r]} {
228 @ html "<a href='$home/reportlist'>Tickets</a> "
229 @ }
230 @ if {[hascap j]} {
231 @ html "<a href='$home/wiki'>Wiki</a> "
232 @ }
233 @ if {[hascap s]} {
234 @ html "<a href='$home/setup'>Admin</a> "
235 @ } elseif {[hascap a]} {
236 @ html "<a href='$home/setup_ulist'>Users</a> "
237 @ }
238 @ if {[info exists login]} {
239 @ html "<a href='$home/login'>Logout</a> "
240 @ } else {
241 @ html "<a href='$home/login'>Login</a> "
242 @ }
243 @ </th1></div>
244 ;
245
246 /*
@@ -320,23 +321,24 @@
320 @ background-color: #558195;
321 @ color: white;
322 @ }
323 @
324 @ /* The submenu bar that *sometimes* appears below the main menu */
325 @ div.submenu {
326 @ padding: 3px 10px 3px 0px;
327 @ font-size: 0.9em;
328 @ text-align: center;
329 @ background-color: #456878;
330 @ color: white;
331 @ }
332 @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited {
 
333 @ padding: 3px 10px 3px 10px;
334 @ color: white;
335 @ text-decoration: none;
336 @ }
337 @ div.mainmenu a:hover, div.submenu a:hover {
338 @ color: #558195;
339 @ background-color: white;
340 @ }
341 @
342 @ /* All page content from the bottom of the menu or submenu down to
@@ -396,10 +398,69 @@
396 @ table.label-value th {
397 @ vertical-align: top;
398 @ text-align: right;
399 @ padding: 0.2ex 2ex;
400 @ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401 @
402 ;
403
404
405 /* The following table contains bits of default CSS that must
@@ -470,11 +531,11 @@
470 @ vertical-align: top;
471 @ text-align: right;
472 },
473 { "td.timelineGraph",
474 "the format for the grap placeholder cells in timelines",
475 @ width: 20;
476 @ text-align: left;
477 @ vertical-align: top;
478 },
479 { "a.tagLink",
480 "the format for the tag links",
@@ -564,11 +625,11 @@
564 @ vertical-align: top
565 },
566 { "table.usetupUserList",
567 "format for the user list table on the user setup page",
568 @ outline-style: double;
569 @ outline-width: 1;
570 @ padding: 10px;
571 },
572 { "th.usetupListUser",
573 "format for table header user in user list on user setup page",
574 @ text-align: right;
@@ -688,17 +749,17 @@
688 @ border-color: #000000;
689 @ border-style: solid;
690 },
691 { "input.checkinUserColor",
692 "format for user color input on checkin edit page",
693 @ # no special definitions, class defined, to enable color pickers, f.e.:
694 @ # add the color picker found at http:jscolor.com as java script include
695 @ # to the header and configure the java script file with
696 @ # 1. use as bindClass :checkinUserColor
697 @ # 2. change the default hash adding behaviour to ON
698 @ # or change the class defition of element identified by id="clrcust"
699 @ # to a standard jscolor definition with java script in the footer.
700 },
701 { "div.endContent",
702 "format for end of content area, to be used to clear page flow(sidebox on branch,..",
703 @ clear: both;
704 },
@@ -718,11 +779,11 @@
718 },
719 { "span.thTrace",
720 "format for th script trace messages",
721 @ color: red;
722 },
723 { "p:reportError",
724 "format for report configuration errors",
725 @ color: red;
726 @ font-weight: bold;
727 },
728 { "blockquote.reportError",
729
--- src/style.c
+++ src/style.c
@@ -210,37 +210,38 @@
210 @ } else {
211 @ puts "Not logged in"
212 @ }
213 @ </th1></div>
214 @ </div>
215 @ <div class="mainmenu">
216 @ <th1>
217 @ html "<a href='$home$index_page'>Home</a>\n"
218 @ if {[anycap jor]} {
219 @ html "<a href='$home/timeline'>Timeline</a>\n"
220 @ }
221 @ if {[hascap oh]} {
222 @ html "<a href='$home/dir?ci=tip'>Files</a>\n"
223 @ }
224 @ if {[hascap o]} {
225 @ html "<a href='$home/brlist'>Branches</a>\n"
226 @ html "<a href='$home/taglist'>Tags</a>\n"
227 @ }
228 @ if {[hascap r]} {
229 @ html "<a href='$home/reportlist'>Tickets</a>\n"
230 @ }
231 @ if {[hascap j]} {
232 @ html "<a href='$home/wiki'>Wiki</a>\n"
233 @ }
234 @ if {[hascap s]} {
235 @ html "<a href='$home/setup'>Admin</a>\n"
236 @ } elseif {[hascap a]} {
237 @ html "<a href='$home/setup_ulist'>Users</a>\n"
238 @ }
239 @ if {[info exists login]} {
240 @ html "<a href='$home/login'>Logout</a>\n"
241 @ } else {
242 @ html "<a href='$home/login'>Login</a>\n"
243 @ }
244 @ </th1></div>
245 ;
246
247 /*
@@ -320,23 +321,24 @@
321 @ background-color: #558195;
322 @ color: white;
323 @ }
324 @
325 @ /* The submenu bar that *sometimes* appears below the main menu */
326 @ div.submenu, div.sectionmenu {
327 @ padding: 3px 10px 3px 0px;
328 @ font-size: 0.9em;
329 @ text-align: center;
330 @ background-color: #456878;
331 @ color: white;
332 @ }
333 @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited,
334 @ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited {
335 @ padding: 3px 10px 3px 10px;
336 @ color: white;
337 @ text-decoration: none;
338 @ }
339 @ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover {
340 @ color: #558195;
341 @ background-color: white;
342 @ }
343 @
344 @ /* All page content from the bottom of the menu or submenu down to
@@ -396,10 +398,69 @@
398 @ table.label-value th {
399 @ vertical-align: top;
400 @ text-align: right;
401 @ padding: 0.2ex 2ex;
402 @ }
403 @
404 @ /* Side-by-side diff */
405 @ table.sbsdiff {
406 @ background-color: white;
407 @ font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
408 @ font-size: 8pt;
409 @ border-collapse:collapse;
410 @ white-space: pre;
411 @ width: 98%;
412 @ border: 1px #000 dashed;
413 @ margin-left: auto;
414 @ margin-right: auto;
415 @ }
416 @
417 @ table.sbsdiff th.diffhdr {
418 @ border-bottom: dotted;
419 @ border-width: 1px;
420 @ }
421 @
422 @ table.sbsdiff tr td {
423 @ white-space: pre;
424 @ padding-left: 3px;
425 @ padding-right: 3px;
426 @ margin: 0px;
427 @ vertical-align: top;
428 @ }
429 @
430 @ table.sbsdiff tr td.lineno {
431 @ text-align: right;
432 @ }
433 @
434 @ table.sbsdiff tr td.srcline {
435 @ }
436 @
437 @ table.sbsdiff tr td.meta {
438 @ background-color: rgb(170, 160, 255);
439 @ text-align: center;
440 @ }
441 @
442 @ table.sbsdiff tr td.added {
443 @ background-color: rgb(180, 250, 180);
444 @ }
445 @ table.sbsdiff tr td.addedvoid {
446 @ background-color: rgb(190, 190, 180);
447 @ }
448 @
449 @ table.sbsdiff tr td.removed {
450 @ background-color: rgb(250, 130, 130);
451 @ }
452 @ table.sbsdiff tr td.removedvoid {
453 @ background-color: rgb(190, 190, 180);
454 @ }
455 @
456 @ table.sbsdiff tr td.changed {
457 @ background-color: rgb(210, 210, 200);
458 @ }
459 @ table.sbsdiff tr td.changedvoid {
460 @ background-color: rgb(190, 190, 180);
461 @ }
462 @
463 ;
464
465
466 /* The following table contains bits of default CSS that must
@@ -470,11 +531,11 @@
531 @ vertical-align: top;
532 @ text-align: right;
533 },
534 { "td.timelineGraph",
535 "the format for the grap placeholder cells in timelines",
536 @ width: 20px;
537 @ text-align: left;
538 @ vertical-align: top;
539 },
540 { "a.tagLink",
541 "the format for the tag links",
@@ -564,11 +625,11 @@
625 @ vertical-align: top
626 },
627 { "table.usetupUserList",
628 "format for the user list table on the user setup page",
629 @ outline-style: double;
630 @ outline-width: 1px;
631 @ padding: 10px;
632 },
633 { "th.usetupListUser",
634 "format for table header user in user list on user setup page",
635 @ text-align: right;
@@ -688,17 +749,17 @@
749 @ border-color: #000000;
750 @ border-style: solid;
751 },
752 { "input.checkinUserColor",
753 "format for user color input on checkin edit page",
754 @ /* no special definitions, class defined, to enable color pickers, f.e.:
755 @ ** add the color picker found at http:jscolor.com as java script include
756 @ ** to the header and configure the java script file with
757 @ ** 1. use as bindClass :checkinUserColor
758 @ ** 2. change the default hash adding behaviour to ON
759 @ ** or change the class defition of element identified by id="clrcust"
760 @ ** to a standard jscolor definition with java script in the footer. */
761 },
762 { "div.endContent",
763 "format for end of content area, to be used to clear page flow(sidebox on branch,..",
764 @ clear: both;
765 },
@@ -718,11 +779,11 @@
779 },
780 { "span.thTrace",
781 "format for th script trace messages",
782 @ color: red;
783 },
784 { "p.reportError",
785 "format for report configuration errors",
786 @ color: red;
787 @ font-weight: bold;
788 },
789 { "blockquote.reportError",
790
+1 -1
--- src/tar.c
+++ src/tar.c
@@ -523,11 +523,11 @@
523523
blob_reset(&filename);
524524
tar_finish(pTar);
525525
}
526526
527527
/*
528
-** COMMAND: tarball
528
+** COMMAND: tarball*
529529
**
530530
** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME]
531531
**
532532
** Generate a compressed tarball for a specified version. If the --name
533533
** option is used, its argument becomes the name of the top-level directory
534534
--- src/tar.c
+++ src/tar.c
@@ -523,11 +523,11 @@
523 blob_reset(&filename);
524 tar_finish(pTar);
525 }
526
527 /*
528 ** COMMAND: tarball
529 **
530 ** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME]
531 **
532 ** Generate a compressed tarball for a specified version. If the --name
533 ** option is used, its argument becomes the name of the top-level directory
534
--- src/tar.c
+++ src/tar.c
@@ -523,11 +523,11 @@
523 blob_reset(&filename);
524 tar_finish(pTar);
525 }
526
527 /*
528 ** COMMAND: tarball*
529 **
530 ** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME]
531 **
532 ** Generate a compressed tarball for a specified version. If the --name
533 ** option is used, its argument becomes the name of the top-level directory
534
+1
--- src/th.h
+++ src/th.h
@@ -154,10 +154,11 @@
154154
*/
155155
int th_register_language(Th_Interp *interp); /* th_lang.c */
156156
int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */
157157
int th_register_vfs(Th_Interp *interp); /* th_vfs.c */
158158
int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */
159
+int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */
159160
160161
/*
161162
** General purpose hash table from th_lang.c.
162163
*/
163164
typedef struct Th_Hash Th_Hash;
164165
--- src/th.h
+++ src/th.h
@@ -154,10 +154,11 @@
154 */
155 int th_register_language(Th_Interp *interp); /* th_lang.c */
156 int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */
157 int th_register_vfs(Th_Interp *interp); /* th_vfs.c */
158 int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */
 
159
160 /*
161 ** General purpose hash table from th_lang.c.
162 */
163 typedef struct Th_Hash Th_Hash;
164
--- src/th.h
+++ src/th.h
@@ -154,10 +154,11 @@
154 */
155 int th_register_language(Th_Interp *interp); /* th_lang.c */
156 int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */
157 int th_register_vfs(Th_Interp *interp); /* th_vfs.c */
158 int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */
159 int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */
160
161 /*
162 ** General purpose hash table from th_lang.c.
163 */
164 typedef struct Th_Hash Th_Hash;
165
+4 -2
--- src/th_lang.c
+++ src/th_lang.c
@@ -1054,17 +1054,19 @@
10541054
{"return", return_command, 0},
10551055
{"break", simple_command, (void *)TH_BREAK},
10561056
{"continue", simple_command, (void *)TH_CONTINUE},
10571057
{"error", simple_command, (void *)TH_ERROR},
10581058
1059
- {0, 0}
1059
+ {0, 0, 0}
10601060
};
10611061
int i;
10621062
10631063
/* Add the language commands. */
10641064
for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
1065
- void *ctx = aCommand[i].pContext;
1065
+ void *ctx;
1066
+ if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
1067
+ ctx = aCommand[i].pContext;
10661068
Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0);
10671069
}
10681070
10691071
return TH_OK;
10701072
}
10711073
--- src/th_lang.c
+++ src/th_lang.c
@@ -1054,17 +1054,19 @@
1054 {"return", return_command, 0},
1055 {"break", simple_command, (void *)TH_BREAK},
1056 {"continue", simple_command, (void *)TH_CONTINUE},
1057 {"error", simple_command, (void *)TH_ERROR},
1058
1059 {0, 0}
1060 };
1061 int i;
1062
1063 /* Add the language commands. */
1064 for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
1065 void *ctx = aCommand[i].pContext;
 
 
1066 Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0);
1067 }
1068
1069 return TH_OK;
1070 }
1071
--- src/th_lang.c
+++ src/th_lang.c
@@ -1054,17 +1054,19 @@
1054 {"return", return_command, 0},
1055 {"break", simple_command, (void *)TH_BREAK},
1056 {"continue", simple_command, (void *)TH_CONTINUE},
1057 {"error", simple_command, (void *)TH_ERROR},
1058
1059 {0, 0, 0}
1060 };
1061 int i;
1062
1063 /* Add the language commands. */
1064 for(i=0; i<(sizeof(aCommand)/sizeof(aCommand[0])); i++){
1065 void *ctx;
1066 if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
1067 ctx = aCommand[i].pContext;
1068 Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0);
1069 }
1070
1071 return TH_OK;
1072 }
1073
+44 -3
--- src/th_main.c
+++ src/th_main.c
@@ -93,10 +93,11 @@
9393
}
9494
if( g.cgiOutput ){
9595
cgi_append_content(z, n);
9696
}else{
9797
fwrite(z, 1, n, stdout);
98
+ fflush(stdout);
9899
}
99100
if( encode ) free((char*)z);
100101
}
101102
}
102103
@@ -334,10 +335,39 @@
334335
if( n<iMin ) n = iMin;
335336
if( n>iMax ) n = iMax;
336337
Th_SetResultInt(interp, n);
337338
return TH_OK;
338339
}
340
+
341
+/*
342
+** TH1 command: repository ?BOOLEAN?
343
+**
344
+** Return the fully qualified file name of the open repository or an empty
345
+** string if one is not currently open. Optionally, it will attempt to open
346
+** the repository if the boolean argument is non-zero.
347
+*/
348
+static int repositoryCmd(
349
+ Th_Interp *interp,
350
+ void *p,
351
+ int argc,
352
+ const char **argv,
353
+ int *argl
354
+){
355
+ int openRepository;
356
+
357
+ if( argc!=1 && argc!=2 ){
358
+ return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
359
+ }
360
+ if( argc==2 ){
361
+ if( Th_ToInt(interp, argv[1], argl[1], &openRepository) ){
362
+ return TH_ERROR;
363
+ }
364
+ if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0);
365
+ }
366
+ Th_SetResult(interp, g.zRepositoryName, -1);
367
+ return TH_OK;
368
+}
339369
340370
/*
341371
** Make sure the interpreter has been initialized. Initialize it if
342372
** it has not been already.
343373
**
@@ -357,16 +387,24 @@
357387
{"htmlize", htmlizeCmd, 0},
358388
{"date", dateCmd, 0},
359389
{"html", putsCmd, 0},
360390
{"puts", putsCmd, (void*)1},
361391
{"wiki", wikiCmd, 0},
392
+ {"repository", repositoryCmd, 0},
393
+ {0, 0, 0}
362394
};
363395
if( g.interp==0 ){
364396
int i;
365397
g.interp = Th_CreateInterp(&vtab);
366398
th_register_language(g.interp); /* Basic scripting commands. */
399
+#ifdef FOSSIL_ENABLE_TCL
400
+ if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
401
+ th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
402
+ }
403
+#endif
367404
for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
405
+ if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
368406
Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
369407
aCommand[i].pContext, 0);
370408
}
371409
}
372410
}
@@ -482,25 +520,27 @@
482520
Th_FossilInit();
483521
while( z[i] ){
484522
if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
485523
const char *zVar;
486524
int nVar;
525
+ int encode = 1;
487526
sendText(z, i, 0);
488527
if( z[i+1]=='<' ){
489
- /* Variables of the form $<aaa> */
528
+ /* Variables of the form $<aaa> are html escaped */
490529
zVar = &z[i+2];
491530
nVar = n-2;
492531
}else{
493
- /* Variables of the form $aaa */
532
+ /* Variables of the form $aaa are output raw */
494533
zVar = &z[i+1];
495534
nVar = n;
535
+ encode = 0;
496536
}
497537
rc = Th_GetVar(g.interp, (char*)zVar, nVar);
498538
z += i+1+n;
499539
i = 0;
500540
zResult = (char*)Th_GetResult(g.interp, &n);
501
- sendText((char*)zResult, n, n>nVar);
541
+ sendText((char*)zResult, n, encode);
502542
}else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
503543
sendText(z, i, 0);
504544
z += i+5;
505545
for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
506546
rc = Th_Eval(g.interp, 0, (const char*)z, i);
@@ -529,9 +569,10 @@
529569
void test_th_render(void){
530570
Blob in;
531571
if( g.argc<3 ){
532572
usage("FILE");
533573
}
574
+ db_open_config(0); /* Needed for "tcl" setting. */
534575
blob_zero(&in);
535576
blob_read_from_file(&in, g.argv[2]);
536577
Th_Render(blob_str(&in));
537578
}
538579
539580
ADDED src/th_tcl.c
--- src/th_main.c
+++ src/th_main.c
@@ -93,10 +93,11 @@
93 }
94 if( g.cgiOutput ){
95 cgi_append_content(z, n);
96 }else{
97 fwrite(z, 1, n, stdout);
 
98 }
99 if( encode ) free((char*)z);
100 }
101 }
102
@@ -334,10 +335,39 @@
334 if( n<iMin ) n = iMin;
335 if( n>iMax ) n = iMax;
336 Th_SetResultInt(interp, n);
337 return TH_OK;
338 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
340 /*
341 ** Make sure the interpreter has been initialized. Initialize it if
342 ** it has not been already.
343 **
@@ -357,16 +387,24 @@
357 {"htmlize", htmlizeCmd, 0},
358 {"date", dateCmd, 0},
359 {"html", putsCmd, 0},
360 {"puts", putsCmd, (void*)1},
361 {"wiki", wikiCmd, 0},
 
 
362 };
363 if( g.interp==0 ){
364 int i;
365 g.interp = Th_CreateInterp(&vtab);
366 th_register_language(g.interp); /* Basic scripting commands. */
 
 
 
 
 
367 for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
 
368 Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
369 aCommand[i].pContext, 0);
370 }
371 }
372 }
@@ -482,25 +520,27 @@
482 Th_FossilInit();
483 while( z[i] ){
484 if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
485 const char *zVar;
486 int nVar;
 
487 sendText(z, i, 0);
488 if( z[i+1]=='<' ){
489 /* Variables of the form $<aaa> */
490 zVar = &z[i+2];
491 nVar = n-2;
492 }else{
493 /* Variables of the form $aaa */
494 zVar = &z[i+1];
495 nVar = n;
 
496 }
497 rc = Th_GetVar(g.interp, (char*)zVar, nVar);
498 z += i+1+n;
499 i = 0;
500 zResult = (char*)Th_GetResult(g.interp, &n);
501 sendText((char*)zResult, n, n>nVar);
502 }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
503 sendText(z, i, 0);
504 z += i+5;
505 for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
506 rc = Th_Eval(g.interp, 0, (const char*)z, i);
@@ -529,9 +569,10 @@
529 void test_th_render(void){
530 Blob in;
531 if( g.argc<3 ){
532 usage("FILE");
533 }
 
534 blob_zero(&in);
535 blob_read_from_file(&in, g.argv[2]);
536 Th_Render(blob_str(&in));
537 }
538
539 DDED src/th_tcl.c
--- src/th_main.c
+++ src/th_main.c
@@ -93,10 +93,11 @@
93 }
94 if( g.cgiOutput ){
95 cgi_append_content(z, n);
96 }else{
97 fwrite(z, 1, n, stdout);
98 fflush(stdout);
99 }
100 if( encode ) free((char*)z);
101 }
102 }
103
@@ -334,10 +335,39 @@
335 if( n<iMin ) n = iMin;
336 if( n>iMax ) n = iMax;
337 Th_SetResultInt(interp, n);
338 return TH_OK;
339 }
340
341 /*
342 ** TH1 command: repository ?BOOLEAN?
343 **
344 ** Return the fully qualified file name of the open repository or an empty
345 ** string if one is not currently open. Optionally, it will attempt to open
346 ** the repository if the boolean argument is non-zero.
347 */
348 static int repositoryCmd(
349 Th_Interp *interp,
350 void *p,
351 int argc,
352 const char **argv,
353 int *argl
354 ){
355 int openRepository;
356
357 if( argc!=1 && argc!=2 ){
358 return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
359 }
360 if( argc==2 ){
361 if( Th_ToInt(interp, argv[1], argl[1], &openRepository) ){
362 return TH_ERROR;
363 }
364 if( openRepository ) db_find_and_open_repository(OPEN_OK_NOT_FOUND, 0);
365 }
366 Th_SetResult(interp, g.zRepositoryName, -1);
367 return TH_OK;
368 }
369
370 /*
371 ** Make sure the interpreter has been initialized. Initialize it if
372 ** it has not been already.
373 **
@@ -357,16 +387,24 @@
387 {"htmlize", htmlizeCmd, 0},
388 {"date", dateCmd, 0},
389 {"html", putsCmd, 0},
390 {"puts", putsCmd, (void*)1},
391 {"wiki", wikiCmd, 0},
392 {"repository", repositoryCmd, 0},
393 {0, 0, 0}
394 };
395 if( g.interp==0 ){
396 int i;
397 g.interp = Th_CreateInterp(&vtab);
398 th_register_language(g.interp); /* Basic scripting commands. */
399 #ifdef FOSSIL_ENABLE_TCL
400 if( getenv("TH1_ENABLE_TCL")!=0 || db_get_boolean("tcl", 0) ){
401 th_register_tcl(g.interp, &g.tcl); /* Tcl integration commands. */
402 }
403 #endif
404 for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){
405 if ( !aCommand[i].zName || !aCommand[i].xProc ) continue;
406 Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc,
407 aCommand[i].pContext, 0);
408 }
409 }
410 }
@@ -482,25 +520,27 @@
520 Th_FossilInit();
521 while( z[i] ){
522 if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){
523 const char *zVar;
524 int nVar;
525 int encode = 1;
526 sendText(z, i, 0);
527 if( z[i+1]=='<' ){
528 /* Variables of the form $<aaa> are html escaped */
529 zVar = &z[i+2];
530 nVar = n-2;
531 }else{
532 /* Variables of the form $aaa are output raw */
533 zVar = &z[i+1];
534 nVar = n;
535 encode = 0;
536 }
537 rc = Th_GetVar(g.interp, (char*)zVar, nVar);
538 z += i+1+n;
539 i = 0;
540 zResult = (char*)Th_GetResult(g.interp, &n);
541 sendText((char*)zResult, n, encode);
542 }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
543 sendText(z, i, 0);
544 z += i+5;
545 for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
546 rc = Th_Eval(g.interp, 0, (const char*)z, i);
@@ -529,9 +569,10 @@
569 void test_th_render(void){
570 Blob in;
571 if( g.argc<3 ){
572 usage("FILE");
573 }
574 db_open_config(0); /* Needed for "tcl" setting. */
575 blob_zero(&in);
576 blob_read_from_file(&in, g.argv[2]);
577 Th_Render(blob_str(&in));
578 }
579
580 DDED src/th_tcl.c
--- a/src/th_tcl.c
+++ b/src/th_tcl.c
@@ -0,0 +1,8 @@
1
+// ( argc>0 && argv ) {
2
+ [0]if( argc>1 ( argc>0 && argv ) {
3
+ [0]// ( argc>0 && argv ) {
4
+ [0]if( argc>1 ( argc>0 && argv ) {
5
+ [0]tclContext->argv[0]); (tclContext->argc > 0) {
6
+ int - 1;
7
+ char **argv// ( argc>0 && argv ) {
8
+if ( tclContext->argc>0}
--- a/src/th_tcl.c
+++ b/src/th_tcl.c
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
--- a/src/th_tcl.c
+++ b/src/th_tcl.c
@@ -0,0 +1,8 @@
1 // ( argc>0 && argv ) {
2 [0]if( argc>1 ( argc>0 && argv ) {
3 [0]// ( argc>0 && argv ) {
4 [0]if( argc>1 ( argc>0 && argv ) {
5 [0]tclContext->argv[0]); (tclContext->argc > 0) {
6 int - 1;
7 char **argv// ( argc>0 && argv ) {
8 if ( tclContext->argc>0}
+50 -32
--- src/timeline.c
+++ src/timeline.c
@@ -20,10 +20,13 @@
2020
*/
2121
#include <string.h>
2222
#include <time.h>
2323
#include "config.h"
2424
#include "timeline.h"
25
+#ifdef FOSSIL_ENABLE_JSON
26
+# include "cson_amalgamation.h"
27
+#endif
2528
2629
/*
2730
** Shorten a UUID so that is the minimum length needed to contain
2831
** at least one digit in the range 'a'..'f'. The minimum length is 10.
2932
*/
@@ -179,11 +182,11 @@
179182
** 2. Date/Time
180183
** 3. Comment string
181184
** 4. User
182185
** 5. True if is a leaf
183186
** 6. background color
184
-** 7. type ("ci", "w", "t", "e", "div")
187
+** 7. type ("ci", "w", "t", "e", "g", "div")
185188
** 8. list of symbolic tags.
186189
** 9. tagid for ticket or wiki or event
187190
** 10. Short comment to user for repeated tickets and wiki
188191
*/
189192
void www_print_timeline(
@@ -316,10 +319,13 @@
316319
@</td>
317320
if( zBgClr && zBgClr[0] ){
318321
@ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
319322
}else{
320323
@ <td class="timelineTableCell">
324
+ }
325
+ if( pGraph && zType[0]!='c' ){
326
+ @ &bull;
321327
}
322328
if( zType[0]=='c' ){
323329
hyperlink_to_uuid(zUuid);
324330
if( isLeaf ){
325331
if( db_exists("SELECT 1 FROM tagxref"
@@ -765,24 +771,24 @@
765771
*/
766772
const char *timeline_query_for_www(void){
767773
static char *zBase = 0;
768774
static const char zBaseSql[] =
769775
@ SELECT
770
- @ blob.rid,
771
- @ uuid,
776
+ @ blob.rid AS blobRid,
777
+ @ uuid AS uuid,
772778
@ datetime(event.mtime,'localtime') AS timestamp,
773
- @ coalesce(ecomment, comment),
774
- @ coalesce(euser, user),
775
- @ blob.rid IN leaf,
776
- @ bgcolor,
777
- @ event.type,
779
+ @ coalesce(ecomment, comment) AS comment,
780
+ @ coalesce(euser, user) AS user,
781
+ @ blob.rid IN leaf AS leaf,
782
+ @ bgcolor AS bgColor,
783
+ @ event.type AS eventType,
778784
@ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref
779785
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
780
- @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0),
781
- @ tagid,
782
- @ brief,
783
- @ event.mtime
786
+ @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0) AS tags,
787
+ @ tagid AS tagid,
788
+ @ brief AS brief,
789
+ @ event.mtime AS mtime
784790
@ FROM event JOIN blob
785791
@ WHERE blob.rid=event.objid
786792
;
787793
if( zBase==0 ){
788794
zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH);
@@ -840,23 +846,24 @@
840846
**
841847
** a=TIMESTAMP after this date
842848
** b=TIMESTAMP before this date.
843849
** c=TIMESTAMP "circa" this date.
844850
** n=COUNT number of events in output
845
-** p=RID artifact RID and up to COUNT parents and ancestors
846
-** d=RID artifact RID and up to COUNT descendants
851
+** p=UUID artifact and up to COUNT parents and ancestors
852
+** d=UUID artifact and up to COUNT descendants
853
+** dp=UUUID The same as d=UUID&p=UUID
847854
** t=TAGID show only check-ins with the given tagid
848855
** r=TAGID show check-ins related to tagid
849856
** u=USER only if belonging to this user
850857
** y=TYPE 'ci', 'w', 't', 'e'
851858
** s=TEXT string search (comment and brief)
852859
** ng Suppress the graph if present
853860
** nd Suppress "divider" lines
854861
** fc Show details of files changed
855
-** f=RID Show family (immediate parents and children) of RID
856
-** from=RID Path from...
857
-** to=RID ... to this
862
+** f=UUID Show family (immediate parents and children) of UUID
863
+** from=UUID Path from...
864
+** to=UUID ... to this
858865
** nomerge ... avoid merge links on the path
859866
** brbg Background color from branch name
860867
** ubg Background color from user
861868
**
862869
** p= and d= can appear individually or together. If either p= or d=
@@ -892,15 +899,23 @@
892899
int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
893900
int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */
894901
int noMerge = P("nomerge")!=0; /* Do not follow merge links */
895902
int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
896903
int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
904
+ int pd_rid;
897905
898906
/* To view the timeline, must have permission to read project data.
899907
*/
908
+ pd_rid = name_to_typed_rid(P("dp"),"ci");
909
+ if( pd_rid ){
910
+ p_rid = d_rid = pd_rid;
911
+ }
900912
login_check_credentials();
901
- if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){ login_needed(); return; }
913
+ if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){
914
+ login_needed();
915
+ return;
916
+ }
902917
if( zTagName && g.perm.Read ){
903918
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName);
904919
zThisTag = zTagName;
905920
}else if( zBrName && g.perm.Read ){
906921
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",zBrName);
@@ -988,14 +1003,12 @@
9881003
blob_appendf(&sql, " AND event.objid IN ok");
9891004
nd = 0;
9901005
if( d_rid ){
9911006
compute_descendants(d_rid, nEntry+1);
9921007
nd = db_int(0, "SELECT count(*)-1 FROM ok");
993
- if( nd>=0 ){
994
- db_multi_exec("%s", blob_str(&sql));
995
- blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
996
- }
1008
+ if( nd>=0 ) db_multi_exec("%s", blob_str(&sql));
1009
+ if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
9971010
if( useDividers ) timeline_add_dividers(0, d_rid);
9981011
db_multi_exec("DELETE FROM ok");
9991012
}
10001013
if( p_rid ){
10011014
compute_ancestors(p_rid, nEntry+1);
@@ -1075,19 +1088,20 @@
10751088
}
10761089
if( (zType[0]=='w' && !g.perm.RdWiki)
10771090
|| (zType[0]=='t' && !g.perm.RdTkt)
10781091
|| (zType[0]=='e' && !g.perm.RdWiki)
10791092
|| (zType[0]=='c' && !g.perm.Read)
1093
+ || (zType[0]=='g' && !g.perm.Read)
10801094
){
10811095
zType = "all";
10821096
}
10831097
if( zType[0]=='a' ){
10841098
if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){
10851099
char cSep = '(';
10861100
blob_appendf(&sql, " AND event.type IN ");
10871101
if( g.perm.Read ){
1088
- blob_appendf(&sql, "%c'ci'", cSep);
1102
+ blob_appendf(&sql, "%c'ci','g'", cSep);
10891103
cSep = ',';
10901104
}
10911105
if( g.perm.RdWiki ){
10921106
blob_appendf(&sql, "%c'w','e'", cSep);
10931107
cSep = ',';
@@ -1107,10 +1121,12 @@
11071121
zEType = "wiki edit";
11081122
}else if( zType[0]=='t' ){
11091123
zEType = "ticket change";
11101124
}else if( zType[0]=='e' ){
11111125
zEType = "event";
1126
+ }else if( zType[0]=='g' ){
1127
+ zEType = "tag";
11121128
}
11131129
}
11141130
if( zUser ){
11151131
blob_appendf(&sql, " AND (event.user=%Q OR event.euser=%Q)",
11161132
zUser, zUser);
@@ -1226,10 +1242,13 @@
12261242
timeline_submenu(&url, "Tickets Only", "y", "t", 0);
12271243
}
12281244
if( zType[0]!='e' && g.perm.RdWiki ){
12291245
timeline_submenu(&url, "Events Only", "y", "e", 0);
12301246
}
1247
+ if( zType[0]!='g' && g.perm.Read ){
1248
+ timeline_submenu(&url, "Tags Only", "y", "g", 0);
1249
+ }
12311250
}
12321251
if( nEntry>20 ){
12331252
timeline_submenu(&url, "20 Entries", "n", "20", 0);
12341253
}
12351254
if( nEntry<200 ){
@@ -1365,24 +1384,24 @@
13651384
** a timeline query for display on a TTY.
13661385
*/
13671386
const char *timeline_query_for_tty(void){
13681387
static const char zBaseSql[] =
13691388
@ SELECT
1370
- @ blob.rid,
1389
+ @ blob.rid AS rid,
13711390
@ uuid,
1372
- @ datetime(event.mtime,'localtime'),
1391
+ @ datetime(event.mtime,'localtime') AS mDateTime,
13731392
@ coalesce(ecomment,comment)
13741393
@ || ' (user: ' || coalesce(euser,user,'?')
13751394
@ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
13761395
@ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
13771396
@ FROM tag, tagxref
13781397
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
13791398
@ AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
1380
- @ || ')',
1381
- @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),
1382
- @ (SELECT count(*) FROM plink WHERE cid=blob.rid),
1383
- @ event.mtime
1399
+ @ || ')' as comment,
1400
+ @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount,
1401
+ @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
1402
+ @ event.mtime AS mtime
13841403
@ FROM event, blob
13851404
@ WHERE blob.rid=event.objid
13861405
;
13871406
return zBaseSql;
13881407
}
@@ -1425,12 +1444,12 @@
14251444
**
14261445
** w = wiki commits only
14271446
** ci = file commits only
14281447
** t = tickets only
14291448
**
1430
-** The optional showfiles argument if specified prints the list of
1431
-** files changed in a checkin after the checkin comment
1449
+** The optional showfiles argument, if specified, prints the list of
1450
+** files changed in a checkin after the checkin comment.
14321451
**
14331452
*/
14341453
void timeline_cmd(void){
14351454
Stmt q;
14361455
int n, k;
@@ -1524,11 +1543,10 @@
15241543
blob_appendf(&sql, " AND blob.rid IN ok");
15251544
}
15261545
if( zType && (zType[0]!='a') ){
15271546
blob_appendf(&sql, " AND event.type=%Q ", zType);
15281547
}
1529
-
15301548
blob_appendf(&sql, " ORDER BY event.mtime DESC");
15311549
db_prepare(&q, blob_str(&sql));
15321550
blob_reset(&sql);
15331551
print_timeline(&q, n, showfilesFlag);
15341552
db_finalize(&q);
15351553
--- src/timeline.c
+++ src/timeline.c
@@ -20,10 +20,13 @@
20 */
21 #include <string.h>
22 #include <time.h>
23 #include "config.h"
24 #include "timeline.h"
 
 
 
25
26 /*
27 ** Shorten a UUID so that is the minimum length needed to contain
28 ** at least one digit in the range 'a'..'f'. The minimum length is 10.
29 */
@@ -179,11 +182,11 @@
179 ** 2. Date/Time
180 ** 3. Comment string
181 ** 4. User
182 ** 5. True if is a leaf
183 ** 6. background color
184 ** 7. type ("ci", "w", "t", "e", "div")
185 ** 8. list of symbolic tags.
186 ** 9. tagid for ticket or wiki or event
187 ** 10. Short comment to user for repeated tickets and wiki
188 */
189 void www_print_timeline(
@@ -316,10 +319,13 @@
316 @</td>
317 if( zBgClr && zBgClr[0] ){
318 @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
319 }else{
320 @ <td class="timelineTableCell">
 
 
 
321 }
322 if( zType[0]=='c' ){
323 hyperlink_to_uuid(zUuid);
324 if( isLeaf ){
325 if( db_exists("SELECT 1 FROM tagxref"
@@ -765,24 +771,24 @@
765 */
766 const char *timeline_query_for_www(void){
767 static char *zBase = 0;
768 static const char zBaseSql[] =
769 @ SELECT
770 @ blob.rid,
771 @ uuid,
772 @ datetime(event.mtime,'localtime') AS timestamp,
773 @ coalesce(ecomment, comment),
774 @ coalesce(euser, user),
775 @ blob.rid IN leaf,
776 @ bgcolor,
777 @ event.type,
778 @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref
779 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
780 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0),
781 @ tagid,
782 @ brief,
783 @ event.mtime
784 @ FROM event JOIN blob
785 @ WHERE blob.rid=event.objid
786 ;
787 if( zBase==0 ){
788 zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH);
@@ -840,23 +846,24 @@
840 **
841 ** a=TIMESTAMP after this date
842 ** b=TIMESTAMP before this date.
843 ** c=TIMESTAMP "circa" this date.
844 ** n=COUNT number of events in output
845 ** p=RID artifact RID and up to COUNT parents and ancestors
846 ** d=RID artifact RID and up to COUNT descendants
 
847 ** t=TAGID show only check-ins with the given tagid
848 ** r=TAGID show check-ins related to tagid
849 ** u=USER only if belonging to this user
850 ** y=TYPE 'ci', 'w', 't', 'e'
851 ** s=TEXT string search (comment and brief)
852 ** ng Suppress the graph if present
853 ** nd Suppress "divider" lines
854 ** fc Show details of files changed
855 ** f=RID Show family (immediate parents and children) of RID
856 ** from=RID Path from...
857 ** to=RID ... to this
858 ** nomerge ... avoid merge links on the path
859 ** brbg Background color from branch name
860 ** ubg Background color from user
861 **
862 ** p= and d= can appear individually or together. If either p= or d=
@@ -892,15 +899,23 @@
892 int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
893 int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */
894 int noMerge = P("nomerge")!=0; /* Do not follow merge links */
895 int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
896 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
 
897
898 /* To view the timeline, must have permission to read project data.
899 */
 
 
 
 
900 login_check_credentials();
901 if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){ login_needed(); return; }
 
 
 
902 if( zTagName && g.perm.Read ){
903 tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName);
904 zThisTag = zTagName;
905 }else if( zBrName && g.perm.Read ){
906 tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",zBrName);
@@ -988,14 +1003,12 @@
988 blob_appendf(&sql, " AND event.objid IN ok");
989 nd = 0;
990 if( d_rid ){
991 compute_descendants(d_rid, nEntry+1);
992 nd = db_int(0, "SELECT count(*)-1 FROM ok");
993 if( nd>=0 ){
994 db_multi_exec("%s", blob_str(&sql));
995 blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
996 }
997 if( useDividers ) timeline_add_dividers(0, d_rid);
998 db_multi_exec("DELETE FROM ok");
999 }
1000 if( p_rid ){
1001 compute_ancestors(p_rid, nEntry+1);
@@ -1075,19 +1088,20 @@
1075 }
1076 if( (zType[0]=='w' && !g.perm.RdWiki)
1077 || (zType[0]=='t' && !g.perm.RdTkt)
1078 || (zType[0]=='e' && !g.perm.RdWiki)
1079 || (zType[0]=='c' && !g.perm.Read)
 
1080 ){
1081 zType = "all";
1082 }
1083 if( zType[0]=='a' ){
1084 if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){
1085 char cSep = '(';
1086 blob_appendf(&sql, " AND event.type IN ");
1087 if( g.perm.Read ){
1088 blob_appendf(&sql, "%c'ci'", cSep);
1089 cSep = ',';
1090 }
1091 if( g.perm.RdWiki ){
1092 blob_appendf(&sql, "%c'w','e'", cSep);
1093 cSep = ',';
@@ -1107,10 +1121,12 @@
1107 zEType = "wiki edit";
1108 }else if( zType[0]=='t' ){
1109 zEType = "ticket change";
1110 }else if( zType[0]=='e' ){
1111 zEType = "event";
 
 
1112 }
1113 }
1114 if( zUser ){
1115 blob_appendf(&sql, " AND (event.user=%Q OR event.euser=%Q)",
1116 zUser, zUser);
@@ -1226,10 +1242,13 @@
1226 timeline_submenu(&url, "Tickets Only", "y", "t", 0);
1227 }
1228 if( zType[0]!='e' && g.perm.RdWiki ){
1229 timeline_submenu(&url, "Events Only", "y", "e", 0);
1230 }
 
 
 
1231 }
1232 if( nEntry>20 ){
1233 timeline_submenu(&url, "20 Entries", "n", "20", 0);
1234 }
1235 if( nEntry<200 ){
@@ -1365,24 +1384,24 @@
1365 ** a timeline query for display on a TTY.
1366 */
1367 const char *timeline_query_for_tty(void){
1368 static const char zBaseSql[] =
1369 @ SELECT
1370 @ blob.rid,
1371 @ uuid,
1372 @ datetime(event.mtime,'localtime'),
1373 @ coalesce(ecomment,comment)
1374 @ || ' (user: ' || coalesce(euser,user,'?')
1375 @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
1376 @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
1377 @ FROM tag, tagxref
1378 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
1379 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
1380 @ || ')',
1381 @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim),
1382 @ (SELECT count(*) FROM plink WHERE cid=blob.rid),
1383 @ event.mtime
1384 @ FROM event, blob
1385 @ WHERE blob.rid=event.objid
1386 ;
1387 return zBaseSql;
1388 }
@@ -1425,12 +1444,12 @@
1425 **
1426 ** w = wiki commits only
1427 ** ci = file commits only
1428 ** t = tickets only
1429 **
1430 ** The optional showfiles argument if specified prints the list of
1431 ** files changed in a checkin after the checkin comment
1432 **
1433 */
1434 void timeline_cmd(void){
1435 Stmt q;
1436 int n, k;
@@ -1524,11 +1543,10 @@
1524 blob_appendf(&sql, " AND blob.rid IN ok");
1525 }
1526 if( zType && (zType[0]!='a') ){
1527 blob_appendf(&sql, " AND event.type=%Q ", zType);
1528 }
1529
1530 blob_appendf(&sql, " ORDER BY event.mtime DESC");
1531 db_prepare(&q, blob_str(&sql));
1532 blob_reset(&sql);
1533 print_timeline(&q, n, showfilesFlag);
1534 db_finalize(&q);
1535
--- src/timeline.c
+++ src/timeline.c
@@ -20,10 +20,13 @@
20 */
21 #include <string.h>
22 #include <time.h>
23 #include "config.h"
24 #include "timeline.h"
25 #ifdef FOSSIL_ENABLE_JSON
26 # include "cson_amalgamation.h"
27 #endif
28
29 /*
30 ** Shorten a UUID so that is the minimum length needed to contain
31 ** at least one digit in the range 'a'..'f'. The minimum length is 10.
32 */
@@ -179,11 +182,11 @@
182 ** 2. Date/Time
183 ** 3. Comment string
184 ** 4. User
185 ** 5. True if is a leaf
186 ** 6. background color
187 ** 7. type ("ci", "w", "t", "e", "g", "div")
188 ** 8. list of symbolic tags.
189 ** 9. tagid for ticket or wiki or event
190 ** 10. Short comment to user for repeated tickets and wiki
191 */
192 void www_print_timeline(
@@ -316,10 +319,13 @@
319 @</td>
320 if( zBgClr && zBgClr[0] ){
321 @ <td class="timelineTableCell" style="background-color: %h(zBgClr);">
322 }else{
323 @ <td class="timelineTableCell">
324 }
325 if( pGraph && zType[0]!='c' ){
326 @ &bull;
327 }
328 if( zType[0]=='c' ){
329 hyperlink_to_uuid(zUuid);
330 if( isLeaf ){
331 if( db_exists("SELECT 1 FROM tagxref"
@@ -765,24 +771,24 @@
771 */
772 const char *timeline_query_for_www(void){
773 static char *zBase = 0;
774 static const char zBaseSql[] =
775 @ SELECT
776 @ blob.rid AS blobRid,
777 @ uuid AS uuid,
778 @ datetime(event.mtime,'localtime') AS timestamp,
779 @ coalesce(ecomment, comment) AS comment,
780 @ coalesce(euser, user) AS user,
781 @ blob.rid IN leaf AS leaf,
782 @ bgcolor AS bgColor,
783 @ event.type AS eventType,
784 @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref
785 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
786 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0) AS tags,
787 @ tagid AS tagid,
788 @ brief AS brief,
789 @ event.mtime AS mtime
790 @ FROM event JOIN blob
791 @ WHERE blob.rid=event.objid
792 ;
793 if( zBase==0 ){
794 zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH);
@@ -840,23 +846,24 @@
846 **
847 ** a=TIMESTAMP after this date
848 ** b=TIMESTAMP before this date.
849 ** c=TIMESTAMP "circa" this date.
850 ** n=COUNT number of events in output
851 ** p=UUID artifact and up to COUNT parents and ancestors
852 ** d=UUID artifact and up to COUNT descendants
853 ** dp=UUUID The same as d=UUID&p=UUID
854 ** t=TAGID show only check-ins with the given tagid
855 ** r=TAGID show check-ins related to tagid
856 ** u=USER only if belonging to this user
857 ** y=TYPE 'ci', 'w', 't', 'e'
858 ** s=TEXT string search (comment and brief)
859 ** ng Suppress the graph if present
860 ** nd Suppress "divider" lines
861 ** fc Show details of files changed
862 ** f=UUID Show family (immediate parents and children) of UUID
863 ** from=UUID Path from...
864 ** to=UUID ... to this
865 ** nomerge ... avoid merge links on the path
866 ** brbg Background color from branch name
867 ** ubg Background color from user
868 **
869 ** p= and d= can appear individually or together. If either p= or d=
@@ -892,15 +899,23 @@
899 int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
900 int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */
901 int noMerge = P("nomerge")!=0; /* Do not follow merge links */
902 int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
903 int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
904 int pd_rid;
905
906 /* To view the timeline, must have permission to read project data.
907 */
908 pd_rid = name_to_typed_rid(P("dp"),"ci");
909 if( pd_rid ){
910 p_rid = d_rid = pd_rid;
911 }
912 login_check_credentials();
913 if( !g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki ){
914 login_needed();
915 return;
916 }
917 if( zTagName && g.perm.Read ){
918 tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName);
919 zThisTag = zTagName;
920 }else if( zBrName && g.perm.Read ){
921 tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",zBrName);
@@ -988,14 +1003,12 @@
1003 blob_appendf(&sql, " AND event.objid IN ok");
1004 nd = 0;
1005 if( d_rid ){
1006 compute_descendants(d_rid, nEntry+1);
1007 nd = db_int(0, "SELECT count(*)-1 FROM ok");
1008 if( nd>=0 ) db_multi_exec("%s", blob_str(&sql));
1009 if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
 
 
1010 if( useDividers ) timeline_add_dividers(0, d_rid);
1011 db_multi_exec("DELETE FROM ok");
1012 }
1013 if( p_rid ){
1014 compute_ancestors(p_rid, nEntry+1);
@@ -1075,19 +1088,20 @@
1088 }
1089 if( (zType[0]=='w' && !g.perm.RdWiki)
1090 || (zType[0]=='t' && !g.perm.RdTkt)
1091 || (zType[0]=='e' && !g.perm.RdWiki)
1092 || (zType[0]=='c' && !g.perm.Read)
1093 || (zType[0]=='g' && !g.perm.Read)
1094 ){
1095 zType = "all";
1096 }
1097 if( zType[0]=='a' ){
1098 if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){
1099 char cSep = '(';
1100 blob_appendf(&sql, " AND event.type IN ");
1101 if( g.perm.Read ){
1102 blob_appendf(&sql, "%c'ci','g'", cSep);
1103 cSep = ',';
1104 }
1105 if( g.perm.RdWiki ){
1106 blob_appendf(&sql, "%c'w','e'", cSep);
1107 cSep = ',';
@@ -1107,10 +1121,12 @@
1121 zEType = "wiki edit";
1122 }else if( zType[0]=='t' ){
1123 zEType = "ticket change";
1124 }else if( zType[0]=='e' ){
1125 zEType = "event";
1126 }else if( zType[0]=='g' ){
1127 zEType = "tag";
1128 }
1129 }
1130 if( zUser ){
1131 blob_appendf(&sql, " AND (event.user=%Q OR event.euser=%Q)",
1132 zUser, zUser);
@@ -1226,10 +1242,13 @@
1242 timeline_submenu(&url, "Tickets Only", "y", "t", 0);
1243 }
1244 if( zType[0]!='e' && g.perm.RdWiki ){
1245 timeline_submenu(&url, "Events Only", "y", "e", 0);
1246 }
1247 if( zType[0]!='g' && g.perm.Read ){
1248 timeline_submenu(&url, "Tags Only", "y", "g", 0);
1249 }
1250 }
1251 if( nEntry>20 ){
1252 timeline_submenu(&url, "20 Entries", "n", "20", 0);
1253 }
1254 if( nEntry<200 ){
@@ -1365,24 +1384,24 @@
1384 ** a timeline query for display on a TTY.
1385 */
1386 const char *timeline_query_for_tty(void){
1387 static const char zBaseSql[] =
1388 @ SELECT
1389 @ blob.rid AS rid,
1390 @ uuid,
1391 @ datetime(event.mtime,'localtime') AS mDateTime,
1392 @ coalesce(ecomment,comment)
1393 @ || ' (user: ' || coalesce(euser,user,'?')
1394 @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
1395 @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
1396 @ FROM tag, tagxref
1397 @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
1398 @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0))
1399 @ || ')' as comment,
1400 @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim) AS primPlinkCount,
1401 @ (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
1402 @ event.mtime AS mtime
1403 @ FROM event, blob
1404 @ WHERE blob.rid=event.objid
1405 ;
1406 return zBaseSql;
1407 }
@@ -1425,12 +1444,12 @@
1444 **
1445 ** w = wiki commits only
1446 ** ci = file commits only
1447 ** t = tickets only
1448 **
1449 ** The optional showfiles argument, if specified, prints the list of
1450 ** files changed in a checkin after the checkin comment.
1451 **
1452 */
1453 void timeline_cmd(void){
1454 Stmt q;
1455 int n, k;
@@ -1524,11 +1543,10 @@
1543 blob_appendf(&sql, " AND blob.rid IN ok");
1544 }
1545 if( zType && (zType[0]!='a') ){
1546 blob_appendf(&sql, " AND event.type=%Q ", zType);
1547 }
 
1548 blob_appendf(&sql, " ORDER BY event.mtime DESC");
1549 db_prepare(&q, blob_str(&sql));
1550 blob_reset(&sql);
1551 print_timeline(&q, n, showfilesFlag);
1552 db_finalize(&q);
1553
+3 -7
--- src/tkt.c
+++ src/tkt.c
@@ -170,11 +170,10 @@
170170
*/
171171
int ticket_insert(const Manifest *p, int createFlag, int rid){
172172
Blob sql;
173173
Stmt q;
174174
int i;
175
- const char *zSep;
176175
int rc = 0;
177176
178177
getAllTicketFields();
179178
if( createFlag ){
180179
db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) "
@@ -181,11 +180,10 @@
181180
"VALUES(%Q, 0)", p->zTicketUuid);
182181
rc = db_changes();
183182
}
184183
blob_zero(&sql);
185184
blob_appendf(&sql, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
186
- zSep = "SET";
187185
for(i=0; i<p->nField; i++){
188186
const char *zName = p->aField[i].zName;
189187
if( zName[0]=='+' ){
190188
zName++;
191189
if( fieldId(zName)<0 ) continue;
@@ -839,11 +837,11 @@
839837
}
840838
@ </ol>
841839
}
842840
843841
/*
844
-** COMMAND: ticket
842
+** COMMAND: ticket*
845843
** Usage: %fossil ticket SUBCOMMAND ...
846844
**
847845
** Run various subcommands to control tickets
848846
**
849847
** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
@@ -855,10 +853,11 @@
855853
**
856854
** Run the ticket report, identified by the report format title
857855
** used in the gui. The data is written as flat file on stdout,
858856
** using "," as separator. The separator "," can be changed using
859857
** the -l or --limit option.
858
+**
860859
** If TICKETFILTER is given on the commandline, the query is
861860
** limited with a new WHERE-condition.
862861
** example: Report lists a column # with the uuid
863862
** TICKETFILTER may be [#]='uuuuuuuuu'
864863
** example: Report only lists rows with status not open
@@ -865,11 +864,11 @@
865864
** TICKETFILTER: status != 'open'
866865
** If the option -q|--quote is used, the tickets are encoded by
867866
** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
868867
** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
869868
** Otherwise, the simplified encoding as on the show report raw
870
-** page in the gui is used.
869
+** page in the gui is used. This has no effect in JSON mode.
871870
**
872871
** Instead of the report title its possible to use the report
873872
** number. Using the special report number 0 list all columns,
874873
** defined in the ticket table.
875874
**
@@ -959,22 +958,19 @@
959958
usage("show REPORTNR");
960959
}else{
961960
const char *zRep = 0;
962961
const char *zSep = 0;
963962
const char *zFilterUuid = 0;
964
-
965963
zSep = find_option("limit","l",1);
966964
zRep = g.argv[3];
967965
if( !strcmp(zRep,"0") ){
968966
zRep = 0;
969967
}
970968
if( g.argc>4 ){
971969
zFilterUuid = g.argv[4];
972970
}
973
-
974971
rptshow( zRep, zSep, zFilterUuid, tktEncoding );
975
-
976972
}
977973
}else{
978974
/* add a new ticket or update an existing ticket */
979975
enum { set,add,history,err } eCmd = err;
980976
int i = 0;
981977
--- src/tkt.c
+++ src/tkt.c
@@ -170,11 +170,10 @@
170 */
171 int ticket_insert(const Manifest *p, int createFlag, int rid){
172 Blob sql;
173 Stmt q;
174 int i;
175 const char *zSep;
176 int rc = 0;
177
178 getAllTicketFields();
179 if( createFlag ){
180 db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) "
@@ -181,11 +180,10 @@
181 "VALUES(%Q, 0)", p->zTicketUuid);
182 rc = db_changes();
183 }
184 blob_zero(&sql);
185 blob_appendf(&sql, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
186 zSep = "SET";
187 for(i=0; i<p->nField; i++){
188 const char *zName = p->aField[i].zName;
189 if( zName[0]=='+' ){
190 zName++;
191 if( fieldId(zName)<0 ) continue;
@@ -839,11 +837,11 @@
839 }
840 @ </ol>
841 }
842
843 /*
844 ** COMMAND: ticket
845 ** Usage: %fossil ticket SUBCOMMAND ...
846 **
847 ** Run various subcommands to control tickets
848 **
849 ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
@@ -855,10 +853,11 @@
855 **
856 ** Run the ticket report, identified by the report format title
857 ** used in the gui. The data is written as flat file on stdout,
858 ** using "," as separator. The separator "," can be changed using
859 ** the -l or --limit option.
 
860 ** If TICKETFILTER is given on the commandline, the query is
861 ** limited with a new WHERE-condition.
862 ** example: Report lists a column # with the uuid
863 ** TICKETFILTER may be [#]='uuuuuuuuu'
864 ** example: Report only lists rows with status not open
@@ -865,11 +864,11 @@
865 ** TICKETFILTER: status != 'open'
866 ** If the option -q|--quote is used, the tickets are encoded by
867 ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
868 ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
869 ** Otherwise, the simplified encoding as on the show report raw
870 ** page in the gui is used.
871 **
872 ** Instead of the report title its possible to use the report
873 ** number. Using the special report number 0 list all columns,
874 ** defined in the ticket table.
875 **
@@ -959,22 +958,19 @@
959 usage("show REPORTNR");
960 }else{
961 const char *zRep = 0;
962 const char *zSep = 0;
963 const char *zFilterUuid = 0;
964
965 zSep = find_option("limit","l",1);
966 zRep = g.argv[3];
967 if( !strcmp(zRep,"0") ){
968 zRep = 0;
969 }
970 if( g.argc>4 ){
971 zFilterUuid = g.argv[4];
972 }
973
974 rptshow( zRep, zSep, zFilterUuid, tktEncoding );
975
976 }
977 }else{
978 /* add a new ticket or update an existing ticket */
979 enum { set,add,history,err } eCmd = err;
980 int i = 0;
981
--- src/tkt.c
+++ src/tkt.c
@@ -170,11 +170,10 @@
170 */
171 int ticket_insert(const Manifest *p, int createFlag, int rid){
172 Blob sql;
173 Stmt q;
174 int i;
 
175 int rc = 0;
176
177 getAllTicketFields();
178 if( createFlag ){
179 db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) "
@@ -181,11 +180,10 @@
180 "VALUES(%Q, 0)", p->zTicketUuid);
181 rc = db_changes();
182 }
183 blob_zero(&sql);
184 blob_appendf(&sql, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime");
 
185 for(i=0; i<p->nField; i++){
186 const char *zName = p->aField[i].zName;
187 if( zName[0]=='+' ){
188 zName++;
189 if( fieldId(zName)<0 ) continue;
@@ -839,11 +837,11 @@
837 }
838 @ </ol>
839 }
840
841 /*
842 ** COMMAND: ticket*
843 ** Usage: %fossil ticket SUBCOMMAND ...
844 **
845 ** Run various subcommands to control tickets
846 **
847 ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options?
@@ -855,10 +853,11 @@
853 **
854 ** Run the ticket report, identified by the report format title
855 ** used in the gui. The data is written as flat file on stdout,
856 ** using "," as separator. The separator "," can be changed using
857 ** the -l or --limit option.
858 **
859 ** If TICKETFILTER is given on the commandline, the query is
860 ** limited with a new WHERE-condition.
861 ** example: Report lists a column # with the uuid
862 ** TICKETFILTER may be [#]='uuuuuuuuu'
863 ** example: Report only lists rows with status not open
@@ -865,11 +864,11 @@
864 ** TICKETFILTER: status != 'open'
865 ** If the option -q|--quote is used, the tickets are encoded by
866 ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n,
867 ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\).
868 ** Otherwise, the simplified encoding as on the show report raw
869 ** page in the gui is used. This has no effect in JSON mode.
870 **
871 ** Instead of the report title its possible to use the report
872 ** number. Using the special report number 0 list all columns,
873 ** defined in the ticket table.
874 **
@@ -959,22 +958,19 @@
958 usage("show REPORTNR");
959 }else{
960 const char *zRep = 0;
961 const char *zSep = 0;
962 const char *zFilterUuid = 0;
 
963 zSep = find_option("limit","l",1);
964 zRep = g.argv[3];
965 if( !strcmp(zRep,"0") ){
966 zRep = 0;
967 }
968 if( g.argc>4 ){
969 zFilterUuid = g.argv[4];
970 }
 
971 rptshow( zRep, zSep, zFilterUuid, tktEncoding );
 
972 }
973 }else{
974 /* add a new ticket or update an existing ticket */
975 enum { set,add,history,err } eCmd = err;
976 int i = 0;
977
--- src/translate.c
+++ src/translate.c
@@ -160,15 +160,25 @@
160160
}
161161
}
162162
163163
int main(int argc, char **argv){
164164
if( argc==2 ){
165
+ char *arg;
165166
FILE *in = fopen(argv[1], "r");
166167
if( in==0 ){
167168
fprintf(stderr,"can not open %s\n", argv[1]);
168169
exit(1);
169170
}
171
+ printf("#line 1 \"");
172
+ for(arg=argv[1]; *arg; arg++){
173
+ if( *arg!='\\' ){
174
+ printf("%c", *arg);
175
+ }else{
176
+ printf("\\\\");
177
+ }
178
+ }
179
+ printf("\"\n");
170180
trans(in, stdout);
171181
fclose(in);
172182
}else{
173183
trans(stdin, stdout);
174184
}
175185
--- src/translate.c
+++ src/translate.c
@@ -160,15 +160,25 @@
160 }
161 }
162
163 int main(int argc, char **argv){
164 if( argc==2 ){
 
165 FILE *in = fopen(argv[1], "r");
166 if( in==0 ){
167 fprintf(stderr,"can not open %s\n", argv[1]);
168 exit(1);
169 }
 
 
 
 
 
 
 
 
 
170 trans(in, stdout);
171 fclose(in);
172 }else{
173 trans(stdin, stdout);
174 }
175
--- src/translate.c
+++ src/translate.c
@@ -160,15 +160,25 @@
160 }
161 }
162
163 int main(int argc, char **argv){
164 if( argc==2 ){
165 char *arg;
166 FILE *in = fopen(argv[1], "r");
167 if( in==0 ){
168 fprintf(stderr,"can not open %s\n", argv[1]);
169 exit(1);
170 }
171 printf("#line 1 \"");
172 for(arg=argv[1]; *arg; arg++){
173 if( *arg!='\\' ){
174 printf("%c", *arg);
175 }else{
176 printf("\\\\");
177 }
178 }
179 printf("\"\n");
180 trans(in, stdout);
181 fclose(in);
182 }else{
183 trans(stdin, stdout);
184 }
185
+1 -1
--- src/undo.c
+++ src/undo.c
@@ -348,11 +348,11 @@
348348
undo_all_filesystem(0);
349349
}
350350
351351
/*
352352
** COMMAND: undo
353
-** COMMAND: redo
353
+** COMMAND: redo*
354354
**
355355
** Usage: %fossil undo ?--explain? ?FILENAME...?
356356
** or: %fossil redo ?--explain? ?FILENAME...?
357357
**
358358
** Undo the changes to the working checkout caused by the most recent
359359
--- src/undo.c
+++ src/undo.c
@@ -348,11 +348,11 @@
348 undo_all_filesystem(0);
349 }
350
351 /*
352 ** COMMAND: undo
353 ** COMMAND: redo
354 **
355 ** Usage: %fossil undo ?--explain? ?FILENAME...?
356 ** or: %fossil redo ?--explain? ?FILENAME...?
357 **
358 ** Undo the changes to the working checkout caused by the most recent
359
--- src/undo.c
+++ src/undo.c
@@ -348,11 +348,11 @@
348 undo_all_filesystem(0);
349 }
350
351 /*
352 ** COMMAND: undo
353 ** COMMAND: redo*
354 **
355 ** Usage: %fossil undo ?--explain? ?FILENAME...?
356 ** or: %fossil redo ?--explain? ?FILENAME...?
357 **
358 ** Undo the changes to the working checkout caused by the most recent
359
+19 -8
--- src/update.c
+++ src/update.c
@@ -96,10 +96,11 @@
9696
int debugFlag; /* --debug option */
9797
int nChng; /* Number of file renames */
9898
int *aChng; /* Array of file renames */
9999
int i; /* Loop counter */
100100
int nConflict = 0; /* Number of merge conflicts */
101
+ int nOverwrite = 0; /* Number of unmanaged files overwritten */
101102
Stmt mtimeXfer; /* Statment to transfer mtimes */
102103
103104
if( !internalUpdate ){
104105
undo_capture_command_line();
105106
url_proxy_options();
@@ -359,11 +360,16 @@
359360
*/
360361
fossil_print("CONFLICT %s\n", zName);
361362
nConflict++;
362363
}else if( idt>0 && idv==0 ){
363364
/* File added in the target. */
364
- fossil_print("ADD %s\n", zName);
365
+ if( file_wd_isfile_or_link(zFullPath) ){
366
+ fossil_print("ADD %s (overwrites an unmanaged file)\n", zName);
367
+ nOverwrite++;
368
+ }else{
369
+ fossil_print("ADD %s\n", zName);
370
+ }
365371
undo_save(zName);
366372
if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0);
367373
}else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){
368374
/* The file is unedited. Change it to the target version */
369375
undo_save(zName);
@@ -449,17 +455,22 @@
449455
fossil_print("--------------\n");
450456
show_common_info(tid, "updated-to:", 1, 0);
451457
452458
/* Report on conflicts
453459
*/
454
- if( nConflict && !nochangeFlag ){
455
- if( internalUpdate ){
456
- internalConflictCnt = nConflict;
457
- }else{
458
- fossil_print(
459
- "WARNING: %d merge conflicts - see messages above for details.\n",
460
- nConflict);
460
+ if( !nochangeFlag ){
461
+ if( nConflict ){
462
+ if( internalUpdate ){
463
+ internalConflictCnt = nConflict;
464
+ nConflict = 0;
465
+ }else{
466
+ fossil_print("WARNING: %d merge conflicts", nConflict);
467
+ }
468
+ }
469
+ if( nOverwrite ){
470
+ fossil_warning("WARNING: %d unmanaged files were overwritten",
471
+ nOverwrite);
461472
}
462473
}
463474
464475
/*
465476
** Clean up the mid and pid VFILE entries. Then commit the changes.
466477
--- src/update.c
+++ src/update.c
@@ -96,10 +96,11 @@
96 int debugFlag; /* --debug option */
97 int nChng; /* Number of file renames */
98 int *aChng; /* Array of file renames */
99 int i; /* Loop counter */
100 int nConflict = 0; /* Number of merge conflicts */
 
101 Stmt mtimeXfer; /* Statment to transfer mtimes */
102
103 if( !internalUpdate ){
104 undo_capture_command_line();
105 url_proxy_options();
@@ -359,11 +360,16 @@
359 */
360 fossil_print("CONFLICT %s\n", zName);
361 nConflict++;
362 }else if( idt>0 && idv==0 ){
363 /* File added in the target. */
364 fossil_print("ADD %s\n", zName);
 
 
 
 
 
365 undo_save(zName);
366 if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0);
367 }else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){
368 /* The file is unedited. Change it to the target version */
369 undo_save(zName);
@@ -449,17 +455,22 @@
449 fossil_print("--------------\n");
450 show_common_info(tid, "updated-to:", 1, 0);
451
452 /* Report on conflicts
453 */
454 if( nConflict && !nochangeFlag ){
455 if( internalUpdate ){
456 internalConflictCnt = nConflict;
457 }else{
458 fossil_print(
459 "WARNING: %d merge conflicts - see messages above for details.\n",
460 nConflict);
 
 
 
 
 
461 }
462 }
463
464 /*
465 ** Clean up the mid and pid VFILE entries. Then commit the changes.
466
--- src/update.c
+++ src/update.c
@@ -96,10 +96,11 @@
96 int debugFlag; /* --debug option */
97 int nChng; /* Number of file renames */
98 int *aChng; /* Array of file renames */
99 int i; /* Loop counter */
100 int nConflict = 0; /* Number of merge conflicts */
101 int nOverwrite = 0; /* Number of unmanaged files overwritten */
102 Stmt mtimeXfer; /* Statment to transfer mtimes */
103
104 if( !internalUpdate ){
105 undo_capture_command_line();
106 url_proxy_options();
@@ -359,11 +360,16 @@
360 */
361 fossil_print("CONFLICT %s\n", zName);
362 nConflict++;
363 }else if( idt>0 && idv==0 ){
364 /* File added in the target. */
365 if( file_wd_isfile_or_link(zFullPath) ){
366 fossil_print("ADD %s (overwrites an unmanaged file)\n", zName);
367 nOverwrite++;
368 }else{
369 fossil_print("ADD %s\n", zName);
370 }
371 undo_save(zName);
372 if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0);
373 }else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){
374 /* The file is unedited. Change it to the target version */
375 undo_save(zName);
@@ -449,17 +455,22 @@
455 fossil_print("--------------\n");
456 show_common_info(tid, "updated-to:", 1, 0);
457
458 /* Report on conflicts
459 */
460 if( !nochangeFlag ){
461 if( nConflict ){
462 if( internalUpdate ){
463 internalConflictCnt = nConflict;
464 nConflict = 0;
465 }else{
466 fossil_print("WARNING: %d merge conflicts", nConflict);
467 }
468 }
469 if( nOverwrite ){
470 fossil_warning("WARNING: %d unmanaged files were overwritten",
471 nOverwrite);
472 }
473 }
474
475 /*
476 ** Clean up the mid and pid VFILE entries. Then commit the changes.
477
+1 -1
--- src/user.c
+++ src/user.c
@@ -142,11 +142,11 @@
142142
}
143143
}
144144
145145
146146
/*
147
-** COMMAND: user
147
+** COMMAND: user*
148148
**
149149
** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE?
150150
**
151151
** Run various subcommands on users of the open repository or of
152152
** the repository identified by the -R or --repository option.
153153
--- src/user.c
+++ src/user.c
@@ -142,11 +142,11 @@
142 }
143 }
144
145
146 /*
147 ** COMMAND: user
148 **
149 ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE?
150 **
151 ** Run various subcommands on users of the open repository or of
152 ** the repository identified by the -R or --repository option.
153
--- src/user.c
+++ src/user.c
@@ -142,11 +142,11 @@
142 }
143 }
144
145
146 /*
147 ** COMMAND: user*
148 **
149 ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE?
150 **
151 ** Run various subcommands on users of the open repository or of
152 ** the repository identified by the -R or --repository option.
153
+26 -9
--- src/wiki.c
+++ src/wiki.c
@@ -621,19 +621,36 @@
621621
blob_zero(&w2);
622622
if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
623623
blob_init(&w2, pW2->zWiki, -1);
624624
}
625625
blob_zero(&d);
626
- text_diff(&w2, &w1, &d, 5, 1);
626
+ text_diff(&w2, &w1, &d, 5 | DIFF_IGNORE_EOLWS);
627627
@ <pre>
628628
@ %h(blob_str(&d))
629629
@ </pre>
630630
manifest_destroy(pW1);
631631
manifest_destroy(pW2);
632632
style_footer();
633633
}
634634
635
+/*
636
+** prepare()s pStmt with a query requesting:
637
+**
638
+** - wiki page name
639
+** - tagxref (whatever that really is!)
640
+**
641
+** Used by wcontent_page() and the JSON wiki code.
642
+*/
643
+void wiki_prepare_page_list( Stmt * pStmt ){
644
+ db_prepare(pStmt,
645
+ "SELECT"
646
+ " substr(tagname, 6) as name,"
647
+ " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC) as tagXref"
648
+ " FROM tag WHERE tagname GLOB 'wiki-*'"
649
+ " ORDER BY lower(tagname) /*sort*/"
650
+ );
651
+}
635652
/*
636653
** WEBPAGE: wcontent
637654
**
638655
** all=1 Show deleted pages
639656
**
@@ -650,17 +667,11 @@
650667
style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
651668
}else{
652669
style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
653670
}
654671
@ <ul>
655
- db_prepare(&q,
656
- "SELECT"
657
- " substr(tagname, 6),"
658
- " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC)"
659
- " FROM tag WHERE tagname GLOB 'wiki-*'"
660
- " ORDER BY lower(tagname) /*sort*/"
661
- );
672
+ wiki_prepare_page_list(&q);
662673
while( db_step(&q)==SQLITE_ROW ){
663674
const char *zName = db_column_text(&q, 0);
664675
int size = db_column_int(&q, 1);
665676
if( size>0 ){
666677
@ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li>
@@ -790,13 +801,19 @@
790801
" WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
791802
" ORDER BY x.mtime DESC LIMIT 1",
792803
zPageName
793804
);
794805
if( rid==0 && !isNew ){
806
+#ifdef FOSSIL_ENABLE_JSON
807
+ g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
808
+#endif
795809
fossil_fatal("no such wiki page: %s", zPageName);
796810
}
797811
if( rid!=0 && isNew ){
812
+#ifdef FOSSIL_ENABLE_JSON
813
+ g.json.resultCode = FSL_JSON_E_RESOURCE_ALREADY_EXISTS;
814
+#endif
798815
fossil_fatal("wiki page %s already exists", zPageName);
799816
}
800817
801818
blob_zero(&wiki);
802819
zDate = date_in_standard_format("now");
@@ -826,11 +843,11 @@
826843
db_end_transaction(0);
827844
return 1;
828845
}
829846
830847
/*
831
-** COMMAND: wiki
848
+** COMMAND: wiki*
832849
**
833850
** Usage: %fossil wiki (export|create|commit|list) WikiName
834851
**
835852
** Run various subcommands to work with wiki entries.
836853
**
837854
--- src/wiki.c
+++ src/wiki.c
@@ -621,19 +621,36 @@
621 blob_zero(&w2);
622 if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
623 blob_init(&w2, pW2->zWiki, -1);
624 }
625 blob_zero(&d);
626 text_diff(&w2, &w1, &d, 5, 1);
627 @ <pre>
628 @ %h(blob_str(&d))
629 @ </pre>
630 manifest_destroy(pW1);
631 manifest_destroy(pW2);
632 style_footer();
633 }
634
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635 /*
636 ** WEBPAGE: wcontent
637 **
638 ** all=1 Show deleted pages
639 **
@@ -650,17 +667,11 @@
650 style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
651 }else{
652 style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
653 }
654 @ <ul>
655 db_prepare(&q,
656 "SELECT"
657 " substr(tagname, 6),"
658 " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC)"
659 " FROM tag WHERE tagname GLOB 'wiki-*'"
660 " ORDER BY lower(tagname) /*sort*/"
661 );
662 while( db_step(&q)==SQLITE_ROW ){
663 const char *zName = db_column_text(&q, 0);
664 int size = db_column_int(&q, 1);
665 if( size>0 ){
666 @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li>
@@ -790,13 +801,19 @@
790 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
791 " ORDER BY x.mtime DESC LIMIT 1",
792 zPageName
793 );
794 if( rid==0 && !isNew ){
 
 
 
795 fossil_fatal("no such wiki page: %s", zPageName);
796 }
797 if( rid!=0 && isNew ){
 
 
 
798 fossil_fatal("wiki page %s already exists", zPageName);
799 }
800
801 blob_zero(&wiki);
802 zDate = date_in_standard_format("now");
@@ -826,11 +843,11 @@
826 db_end_transaction(0);
827 return 1;
828 }
829
830 /*
831 ** COMMAND: wiki
832 **
833 ** Usage: %fossil wiki (export|create|commit|list) WikiName
834 **
835 ** Run various subcommands to work with wiki entries.
836 **
837
--- src/wiki.c
+++ src/wiki.c
@@ -621,19 +621,36 @@
621 blob_zero(&w2);
622 if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){
623 blob_init(&w2, pW2->zWiki, -1);
624 }
625 blob_zero(&d);
626 text_diff(&w2, &w1, &d, 5 | DIFF_IGNORE_EOLWS);
627 @ <pre>
628 @ %h(blob_str(&d))
629 @ </pre>
630 manifest_destroy(pW1);
631 manifest_destroy(pW2);
632 style_footer();
633 }
634
635 /*
636 ** prepare()s pStmt with a query requesting:
637 **
638 ** - wiki page name
639 ** - tagxref (whatever that really is!)
640 **
641 ** Used by wcontent_page() and the JSON wiki code.
642 */
643 void wiki_prepare_page_list( Stmt * pStmt ){
644 db_prepare(pStmt,
645 "SELECT"
646 " substr(tagname, 6) as name,"
647 " (SELECT value FROM tagxref WHERE tagid=tag.tagid ORDER BY mtime DESC) as tagXref"
648 " FROM tag WHERE tagname GLOB 'wiki-*'"
649 " ORDER BY lower(tagname) /*sort*/"
650 );
651 }
652 /*
653 ** WEBPAGE: wcontent
654 **
655 ** all=1 Show deleted pages
656 **
@@ -650,17 +667,11 @@
667 style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop);
668 }else{
669 style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop);
670 }
671 @ <ul>
672 wiki_prepare_page_list(&q);
 
 
 
 
 
 
673 while( db_step(&q)==SQLITE_ROW ){
674 const char *zName = db_column_text(&q, 0);
675 int size = db_column_int(&q, 1);
676 if( size>0 ){
677 @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li>
@@ -790,13 +801,19 @@
801 " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'"
802 " ORDER BY x.mtime DESC LIMIT 1",
803 zPageName
804 );
805 if( rid==0 && !isNew ){
806 #ifdef FOSSIL_ENABLE_JSON
807 g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
808 #endif
809 fossil_fatal("no such wiki page: %s", zPageName);
810 }
811 if( rid!=0 && isNew ){
812 #ifdef FOSSIL_ENABLE_JSON
813 g.json.resultCode = FSL_JSON_E_RESOURCE_ALREADY_EXISTS;
814 #endif
815 fossil_fatal("wiki page %s already exists", zPageName);
816 }
817
818 blob_zero(&wiki);
819 zDate = date_in_standard_format("now");
@@ -826,11 +843,11 @@
843 db_end_transaction(0);
844 return 1;
845 }
846
847 /*
848 ** COMMAND: wiki*
849 **
850 ** Usage: %fossil wiki (export|create|commit|list) WikiName
851 **
852 ** Run various subcommands to work with wiki entries.
853 **
854
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -1708,20 +1708,18 @@
17081708
/* Enter <verbatim> processing. With verbatim enabled, all other
17091709
** markup other than the corresponding end-tag with the same ID is
17101710
** ignored.
17111711
*/
17121712
if( markup.iCode==MARKUP_VERBATIM ){
1713
- int vAttrIdx, vAttrDidAppend=0;
1713
+ int vAttrIdx;
17141714
renderer.zVerbatimId = 0;
17151715
renderer.inVerbatim = 1;
17161716
renderer.preVerbState = renderer.state;
17171717
renderer.state &= ~ALLOW_WIKI;
17181718
for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
17191719
if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
17201720
renderer.zVerbatimId = markup.aAttr[0].zValue;
1721
- }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
1722
- vAttrDidAppend=1;
17231721
}
17241722
}
17251723
renderer.wantAutoParagraph = 0;
17261724
}
17271725
17281726
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -1708,20 +1708,18 @@
1708 /* Enter <verbatim> processing. With verbatim enabled, all other
1709 ** markup other than the corresponding end-tag with the same ID is
1710 ** ignored.
1711 */
1712 if( markup.iCode==MARKUP_VERBATIM ){
1713 int vAttrIdx, vAttrDidAppend=0;
1714 renderer.zVerbatimId = 0;
1715 renderer.inVerbatim = 1;
1716 renderer.preVerbState = renderer.state;
1717 renderer.state &= ~ALLOW_WIKI;
1718 for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
1719 if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
1720 renderer.zVerbatimId = markup.aAttr[0].zValue;
1721 }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
1722 vAttrDidAppend=1;
1723 }
1724 }
1725 renderer.wantAutoParagraph = 0;
1726 }
1727
1728
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -1708,20 +1708,18 @@
1708 /* Enter <verbatim> processing. With verbatim enabled, all other
1709 ** markup other than the corresponding end-tag with the same ID is
1710 ** ignored.
1711 */
1712 if( markup.iCode==MARKUP_VERBATIM ){
1713 int vAttrIdx;
1714 renderer.zVerbatimId = 0;
1715 renderer.inVerbatim = 1;
1716 renderer.preVerbState = renderer.state;
1717 renderer.state &= ~ALLOW_WIKI;
1718 for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
1719 if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
1720 renderer.zVerbatimId = markup.aAttr[0].zValue;
 
 
1721 }
1722 }
1723 renderer.wantAutoParagraph = 0;
1724 }
1725
1726
+1 -10
--- src/winhttp.c
+++ src/winhttp.c
@@ -432,11 +432,11 @@
432432
}
433433
return 0;
434434
}
435435
436436
/*
437
-** COMMAND: winsrv
437
+** COMMAND: winsrv*
438438
** Usage: fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS?
439439
**
440440
** Where METHOD is one of: create delete show start stop.
441441
**
442442
** The winsrv command manages Fossil as a Windows service. This allows
@@ -848,15 +848,6 @@
848848
" create delete show start stop");
849849
}
850850
return;
851851
}
852852
853
-#else /* _WIN32 -- This code is for win32 only */
854
-#include "winhttp.h"
855
-
856
-void cmd_win32_service(void){
857
- fossil_fatal("The winsrv command is platform specific "
858
- "and not available on this platform.");
859
- return;
860
-}
861
-
862853
#endif /* _WIN32 -- This code is for win32 only */
863854
--- src/winhttp.c
+++ src/winhttp.c
@@ -432,11 +432,11 @@
432 }
433 return 0;
434 }
435
436 /*
437 ** COMMAND: winsrv
438 ** Usage: fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS?
439 **
440 ** Where METHOD is one of: create delete show start stop.
441 **
442 ** The winsrv command manages Fossil as a Windows service. This allows
@@ -848,15 +848,6 @@
848 " create delete show start stop");
849 }
850 return;
851 }
852
853 #else /* _WIN32 -- This code is for win32 only */
854 #include "winhttp.h"
855
856 void cmd_win32_service(void){
857 fossil_fatal("The winsrv command is platform specific "
858 "and not available on this platform.");
859 return;
860 }
861
862 #endif /* _WIN32 -- This code is for win32 only */
863
--- src/winhttp.c
+++ src/winhttp.c
@@ -432,11 +432,11 @@
432 }
433 return 0;
434 }
435
436 /*
437 ** COMMAND: winsrv*
438 ** Usage: fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS?
439 **
440 ** Where METHOD is one of: create delete show start stop.
441 **
442 ** The winsrv command manages Fossil as a Windows service. This allows
@@ -848,15 +848,6 @@
848 " create delete show start stop");
849 }
850 return;
851 }
852
 
 
 
 
 
 
 
 
 
853 #endif /* _WIN32 -- This code is for win32 only */
854
+3 -4
--- src/xfer.c
+++ src/xfer.c
@@ -637,15 +637,17 @@
637637
Stmt q;
638638
int nUncl;
639639
int nRow = 0;
640640
int rid;
641641
642
+#if 0
642643
/* We should not ever get any private artifacts in the unclustered table.
643644
** But if we do (because of a bug) now is a good time to delete them. */
644645
db_multi_exec(
645646
"DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)"
646647
);
648
+#endif
647649
648650
nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/"
649651
" WHERE NOT EXISTS(SELECT 1 FROM phantom"
650652
" WHERE rid=unclustered.rid)");
651653
if( nUncl>=100 ){
@@ -1198,20 +1200,19 @@
11981200
**
11991201
** gdb fossil
12001202
** r test-xfer out.txt
12011203
*/
12021204
void cmd_test_xfer(void){
1203
- int notUsed;
12041205
db_find_and_open_repository(0,0);
12051206
if( g.argc!=2 && g.argc!=3 ){
12061207
usage("?MESSAGEFILE?");
12071208
}
12081209
blob_zero(&g.cgiIn);
12091210
blob_read_from_file(&g.cgiIn, g.argc==2 ? "-" : g.argv[2]);
12101211
disableLogin = 1;
12111212
page_xfer();
1212
- fossil_print("%s\n", cgi_extract_content(&notUsed));
1213
+ fossil_print("%s\n", cgi_extract_content());
12131214
}
12141215
12151216
/*
12161217
** Format strings for progress reporting.
12171218
*/
@@ -1238,11 +1239,10 @@
12381239
int go = 1; /* Loop until zero */
12391240
int nCardSent = 0; /* Number of cards sent */
12401241
int nCardRcvd = 0; /* Number of cards received */
12411242
int nCycle = 0; /* Number of round trips to the server */
12421243
int size; /* Size of a config value */
1243
- int nFileSend = 0;
12441244
int origConfigRcvMask; /* Original value of configRcvMask */
12451245
int nFileRecv; /* Number of files received */
12461246
int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
12471247
const char *zCookie; /* Server cookie */
12481248
i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
@@ -1380,11 +1380,10 @@
13801380
zRandomness = db_text(0, "SELECT hex(randomblob(20))");
13811381
blob_appendf(&send, "# %s\n", zRandomness);
13821382
free(zRandomness);
13831383
13841384
/* Exchange messages with the server */
1385
- nFileSend = xfer.nFileSent + xfer.nDeltaSent;
13861385
fossil_print(zValueFormat, "Sent:",
13871386
blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
13881387
xfer.nFileSent, xfer.nDeltaSent);
13891388
nCardSent = 0;
13901389
nCardRcvd = 0;
13911390
--- src/xfer.c
+++ src/xfer.c
@@ -637,15 +637,17 @@
637 Stmt q;
638 int nUncl;
639 int nRow = 0;
640 int rid;
641
 
642 /* We should not ever get any private artifacts in the unclustered table.
643 ** But if we do (because of a bug) now is a good time to delete them. */
644 db_multi_exec(
645 "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)"
646 );
 
647
648 nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/"
649 " WHERE NOT EXISTS(SELECT 1 FROM phantom"
650 " WHERE rid=unclustered.rid)");
651 if( nUncl>=100 ){
@@ -1198,20 +1200,19 @@
1198 **
1199 ** gdb fossil
1200 ** r test-xfer out.txt
1201 */
1202 void cmd_test_xfer(void){
1203 int notUsed;
1204 db_find_and_open_repository(0,0);
1205 if( g.argc!=2 && g.argc!=3 ){
1206 usage("?MESSAGEFILE?");
1207 }
1208 blob_zero(&g.cgiIn);
1209 blob_read_from_file(&g.cgiIn, g.argc==2 ? "-" : g.argv[2]);
1210 disableLogin = 1;
1211 page_xfer();
1212 fossil_print("%s\n", cgi_extract_content(&notUsed));
1213 }
1214
1215 /*
1216 ** Format strings for progress reporting.
1217 */
@@ -1238,11 +1239,10 @@
1238 int go = 1; /* Loop until zero */
1239 int nCardSent = 0; /* Number of cards sent */
1240 int nCardRcvd = 0; /* Number of cards received */
1241 int nCycle = 0; /* Number of round trips to the server */
1242 int size; /* Size of a config value */
1243 int nFileSend = 0;
1244 int origConfigRcvMask; /* Original value of configRcvMask */
1245 int nFileRecv; /* Number of files received */
1246 int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
1247 const char *zCookie; /* Server cookie */
1248 i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
@@ -1380,11 +1380,10 @@
1380 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1381 blob_appendf(&send, "# %s\n", zRandomness);
1382 free(zRandomness);
1383
1384 /* Exchange messages with the server */
1385 nFileSend = xfer.nFileSent + xfer.nDeltaSent;
1386 fossil_print(zValueFormat, "Sent:",
1387 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1388 xfer.nFileSent, xfer.nDeltaSent);
1389 nCardSent = 0;
1390 nCardRcvd = 0;
1391
--- src/xfer.c
+++ src/xfer.c
@@ -637,15 +637,17 @@
637 Stmt q;
638 int nUncl;
639 int nRow = 0;
640 int rid;
641
642 #if 0
643 /* We should not ever get any private artifacts in the unclustered table.
644 ** But if we do (because of a bug) now is a good time to delete them. */
645 db_multi_exec(
646 "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)"
647 );
648 #endif
649
650 nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/"
651 " WHERE NOT EXISTS(SELECT 1 FROM phantom"
652 " WHERE rid=unclustered.rid)");
653 if( nUncl>=100 ){
@@ -1198,20 +1200,19 @@
1200 **
1201 ** gdb fossil
1202 ** r test-xfer out.txt
1203 */
1204 void cmd_test_xfer(void){
 
1205 db_find_and_open_repository(0,0);
1206 if( g.argc!=2 && g.argc!=3 ){
1207 usage("?MESSAGEFILE?");
1208 }
1209 blob_zero(&g.cgiIn);
1210 blob_read_from_file(&g.cgiIn, g.argc==2 ? "-" : g.argv[2]);
1211 disableLogin = 1;
1212 page_xfer();
1213 fossil_print("%s\n", cgi_extract_content());
1214 }
1215
1216 /*
1217 ** Format strings for progress reporting.
1218 */
@@ -1238,11 +1239,10 @@
1239 int go = 1; /* Loop until zero */
1240 int nCardSent = 0; /* Number of cards sent */
1241 int nCardRcvd = 0; /* Number of cards received */
1242 int nCycle = 0; /* Number of round trips to the server */
1243 int size; /* Size of a config value */
 
1244 int origConfigRcvMask; /* Original value of configRcvMask */
1245 int nFileRecv; /* Number of files received */
1246 int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
1247 const char *zCookie; /* Server cookie */
1248 i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
@@ -1380,11 +1380,10 @@
1380 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1381 blob_appendf(&send, "# %s\n", zRandomness);
1382 free(zRandomness);
1383
1384 /* Exchange messages with the server */
 
1385 fossil_print(zValueFormat, "Sent:",
1386 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1387 xfer.nFileSent, xfer.nDeltaSent);
1388 nCardSent = 0;
1389 nCardRcvd = 0;
1390
+1 -1
--- src/zip.c
+++ src/zip.c
@@ -377,11 +377,11 @@
377377
blob_reset(&filename);
378378
zip_close(pZip);
379379
}
380380
381381
/*
382
-** COMMAND: zip
382
+** COMMAND: zip*
383383
**
384384
** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME]
385385
**
386386
** Generate a ZIP archive for a specified version. If the --name option is
387387
** used, it argument becomes the name of the top-level directory in the
388388
--- src/zip.c
+++ src/zip.c
@@ -377,11 +377,11 @@
377 blob_reset(&filename);
378 zip_close(pZip);
379 }
380
381 /*
382 ** COMMAND: zip
383 **
384 ** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME]
385 **
386 ** Generate a ZIP archive for a specified version. If the --name option is
387 ** used, it argument becomes the name of the top-level directory in the
388
--- src/zip.c
+++ src/zip.c
@@ -377,11 +377,11 @@
377 blob_reset(&filename);
378 zip_close(pZip);
379 }
380
381 /*
382 ** COMMAND: zip*
383 **
384 ** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME]
385 **
386 ** Generate a ZIP archive for a specified version. If the --name option is
387 ** used, it argument becomes the name of the top-level directory in the
388
--- test/merge5.test
+++ test/merge5.test
@@ -38,14 +38,18 @@
3838
}
3939
}
4040
4141
catch {exec $::fossilexe info} res
4242
puts res=$res
43
-if {![regexp {not within an open checkout} $res]} {
43
+if {![regexp {use --repository} $res]} {
4444
puts stderr "Cannot run this test within an open checkout"
4545
return
4646
}
47
+#
48
+# Fossil will write data on $HOME, running 'fossil open' here.
49
+# We need not to clutter the $HOME of the test caller.
50
+set env(HOME) [pwd]
4751
4852
# Construct a test repository
4953
#
5054
exec sqlite3 m5.fossil <$testdir/${testfile}_repo.sql
5155
fossil rebuild m5.fossil
5256
--- test/merge5.test
+++ test/merge5.test
@@ -38,14 +38,18 @@
38 }
39 }
40
41 catch {exec $::fossilexe info} res
42 puts res=$res
43 if {![regexp {not within an open checkout} $res]} {
44 puts stderr "Cannot run this test within an open checkout"
45 return
46 }
 
 
 
 
47
48 # Construct a test repository
49 #
50 exec sqlite3 m5.fossil <$testdir/${testfile}_repo.sql
51 fossil rebuild m5.fossil
52
--- test/merge5.test
+++ test/merge5.test
@@ -38,14 +38,18 @@
38 }
39 }
40
41 catch {exec $::fossilexe info} res
42 puts res=$res
43 if {![regexp {use --repository} $res]} {
44 puts stderr "Cannot run this test within an open checkout"
45 return
46 }
47 #
48 # Fossil will write data on $HOME, running 'fossil open' here.
49 # We need not to clutter the $HOME of the test caller.
50 set env(HOME) [pwd]
51
52 # Construct a test repository
53 #
54 exec sqlite3 m5.fossil <$testdir/${testfile}_repo.sql
55 fossil rebuild m5.fossil
56
--- test/merge_renames.test
+++ test/merge_renames.test
@@ -7,10 +7,16 @@
77
puts res=$res
88
if {![regexp {use --repository} $res]} {
99
puts stderr "Cannot run this test within an open checkout"
1010
return
1111
}
12
+
13
+
14
+# Fossil will write data on $HOME, running 'fossil new' here.
15
+# We need not to clutter the $HOME of the test caller.
16
+set env(HOME) [pwd]
17
+
1218
1319
######################################
1420
# Test 1 #
1521
# Reported: Ticket [554f44ee74e3d] #
1622
######################################
1723
1824
ADDED test/th1-tcl.test
1925
ADDED test/th1-tcl1.txt
2026
ADDED test/th1-tcl2.txt
2127
ADDED test/th1-tcl3.txt
2228
ADDED test/th1-tcl4.txt
2329
ADDED test/th1-tcl5.txt
2430
ADDED test/th1-tcl6.txt
2531
ADDED test/th1-tcl7.txt
2632
ADDED test/th1-tcl8.txt
--- test/merge_renames.test
+++ test/merge_renames.test
@@ -7,10 +7,16 @@
7 puts res=$res
8 if {![regexp {use --repository} $res]} {
9 puts stderr "Cannot run this test within an open checkout"
10 return
11 }
 
 
 
 
 
 
12
13 ######################################
14 # Test 1 #
15 # Reported: Ticket [554f44ee74e3d] #
16 ######################################
17
18 DDED test/th1-tcl.test
19 DDED test/th1-tcl1.txt
20 DDED test/th1-tcl2.txt
21 DDED test/th1-tcl3.txt
22 DDED test/th1-tcl4.txt
23 DDED test/th1-tcl5.txt
24 DDED test/th1-tcl6.txt
25 DDED test/th1-tcl7.txt
26 DDED test/th1-tcl8.txt
--- test/merge_renames.test
+++ test/merge_renames.test
@@ -7,10 +7,16 @@
7 puts res=$res
8 if {![regexp {use --repository} $res]} {
9 puts stderr "Cannot run this test within an open checkout"
10 return
11 }
12
13
14 # Fossil will write data on $HOME, running 'fossil new' here.
15 # We need not to clutter the $HOME of the test caller.
16 set env(HOME) [pwd]
17
18
19 ######################################
20 # Test 1 #
21 # Reported: Ticket [554f44ee74e3d] #
22 ######################################
23
24 DDED test/th1-tcl.test
25 DDED test/th1-tcl1.txt
26 DDED test/th1-tcl2.txt
27 DDED test/th1-tcl3.txt
28 DDED test/th1-tcl4.txt
29 DDED test/th1-tcl5.txt
30 DDED test/th1-tcl6.txt
31 DDED test/th1-tcl7.txt
32 DDED test/th1-tcl8.txt
--- a/test/th1-tcl.test
+++ b/test/th1-tcl.test
@@ -0,0 +1,82 @@
1
+; return
2
+}
3
+
4
+h1-tcl3.txt]#
5
+# Copyright (c) 2011 D. Richard Hipp
6
+#
7
+# This progra#
8
+
9
+test_setup
10
+
11
+###############################################################################
12
+
13
+set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test.
14
+
15
+###############################################################################
16
+
17
+fossil test-th-render --open-config \
18
+ [file nativename [file join $path th1-tcl1.txt]]
19
+
20
+test tdir th1-tcl1.txt]]
21
+
22
+test th1-tcl-1 {[regexp -- {^tclReady\(before\) = 0
23
+tclReady\(after\) = 1
24
+\d+
25
+\d+
26
+\d+
27
+via Tcl invoke
28
+4
29
+4
30
+two words
31
+one_word
32
+three words now
33
+\d+
34
+two words
35
+4
36
+\d+
37
+two words
38
+4
39
+\d+
40
+one_word
41
+three words now$} [normalize_result]]}
42
+
43
+############################################t be enabled for this test.
44
+
45
+###############################################################################
46
+
47
+fossil test-th-render --open-confth\
48
+ [file nativenamd Hipp
49
+#
50
+# Thisafter\) = 1
51
+\d+
52
+\d+
53
+\d+
54
+via Tcl invoke
55
+4
56
+4
57
+two words
58
+one_word
59
+three words now
60
+\d+
61
+two words
62
+4
63
+\d+
64
+two words
65
+4
66
+\d+
67
+one_word
68
+three words now$} [normalize_result]
69
+$} [stringprogra#
70
+
71
+test_setup
72
+
73
+#######################config \
74
+ [file nativename [fi
75
+
76
+set env(tth\
77
+ [file nativenam2 Hipp
78
+#
79
+# Thisafter\)#########################################################config \
80
+ [file nativename [fiexp -- {^tcscript} [normalize_result]
81
+lth\
82
+ [file nativenam3 Hipp
--- a/test/th1-tcl.test
+++ b/test/th1-tcl.test
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/th1-tcl.test
+++ b/test/th1-tcl.test
@@ -0,0 +1,82 @@
1 ; return
2 }
3
4 h1-tcl3.txt]#
5 # Copyright (c) 2011 D. Richard Hipp
6 #
7 # This progra#
8
9 test_setup
10
11 ###############################################################################
12
13 set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test.
14
15 ###############################################################################
16
17 fossil test-th-render --open-config \
18 [file nativename [file join $path th1-tcl1.txt]]
19
20 test tdir th1-tcl1.txt]]
21
22 test th1-tcl-1 {[regexp -- {^tclReady\(before\) = 0
23 tclReady\(after\) = 1
24 \d+
25 \d+
26 \d+
27 via Tcl invoke
28 4
29 4
30 two words
31 one_word
32 three words now
33 \d+
34 two words
35 4
36 \d+
37 two words
38 4
39 \d+
40 one_word
41 three words now$} [normalize_result]]}
42
43 ############################################t be enabled for this test.
44
45 ###############################################################################
46
47 fossil test-th-render --open-confth\
48 [file nativenamd Hipp
49 #
50 # Thisafter\) = 1
51 \d+
52 \d+
53 \d+
54 via Tcl invoke
55 4
56 4
57 two words
58 one_word
59 three words now
60 \d+
61 two words
62 4
63 \d+
64 two words
65 4
66 \d+
67 one_word
68 three words now$} [normalize_result]
69 $} [stringprogra#
70
71 test_setup
72
73 #######################config \
74 [file nativename [fi
75
76 set env(tth\
77 [file nativenam2 Hipp
78 #
79 # Thisafter\)#########################################################config \
80 [file nativename [fiexp -- {^tcscript} [normalize_result]
81 lth\
82 [file nativenam3 Hipp
--- a/test/th1-tcl1.txt
+++ b/test/th1-tcl1.txt
@@ -0,0 +1,22 @@
1
+<th1>
2
+ #
3
+ # This is a "TH1 fragment" used to test the TH1.
4
+ #e e
5
+ # test-script
6
+ set chan test-th-render
7
+ # #
8
+ # This is<th1>
9
+ #
10
+ # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
11
+ # The corresponding test file executes this file using the test-th-render
12
+ # Fossil command.
13
+ #
14
+ proc doOut {msg} {puts $msg; puts \n}
15
+ doOut "tclReady(before) = [tclReady]"
16
+ set channel stdout; tclInvoke set channel $channel
17
+ doOut "tclReady(after) = [tclReady]"
18
+ doOut [tclEval clock seconds]
19
+ dobridgeEval {set y "two words"}]bridgedoOut [set z [tclInvoke th1Expr {2+2}]]
20
+ doOut $x
21
+ doOut $y
22
+ doOut $th1th1
--- a/test/th1-tcl1.txt
+++ b/test/th1-tcl1.txt
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/th1-tcl1.txt
+++ b/test/th1-tcl1.txt
@@ -0,0 +1,22 @@
1 <th1>
2 #
3 # This is a "TH1 fragment" used to test the TH1.
4 #e e
5 # test-script
6 set chan test-th-render
7 # #
8 # This is<th1>
9 #
10 # This is a "TH1 fragment" used to test the Tcl integration features of TH1.
11 # The corresponding test file executes this file using the test-th-render
12 # Fossil command.
13 #
14 proc doOut {msg} {puts $msg; puts \n}
15 doOut "tclReady(before) = [tclReady]"
16 set channel stdout; tclInvoke set channel $channel
17 doOut "tclReady(after) = [tclReady]"
18 doOut [tclEval clock seconds]
19 dobridgeEval {set y "two words"}]bridgedoOut [set z [tclInvoke th1Expr {2+2}]]
20 doOut $x
21 doOut $y
22 doOut $th1th1
--- a/test/th1-tcl2.txt
+++ b/test/th1-tcl2.txt
@@ -0,0 +1,6 @@
1
+<th1>
2
+ #
3
+ # This is a "TH1 fragment" used to test the TH1.
4
+ #e e
5
+ # test-script-rendersing test-th-render
6
+ #
--- a/test/th1-tcl2.txt
+++ b/test/th1-tcl2.txt
@@ -0,0 +1,6 @@
 
 
 
 
 
 
--- a/test/th1-tcl2.txt
+++ b/test/th1-tcl2.txt
@@ -0,0 +1,6 @@
1 <th1>
2 #
3 # This is a "TH1 fragment" used to test the TH1.
4 #e e
5 # test-script-rendersing test-th-render
6 #
--- a/test/th1-tcl3.txt
+++ b/test/th1-tcl3.txt
@@ -0,0 +1,9 @@
1
+<th1>
2
+ #
3
+ # This is a "TH1 fragment" used to test the TH1.
4
+ # The corresponding test file e test-th-render
5
+ # the
6
+ # test-script-render Fossil command.
7
+ #
8
+ proc doOut {msg} {puts $msg; puts \n}
9
+ doOut [t
--- a/test/th1-tcl3.txt
+++ b/test/th1-tcl3.txt
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
--- a/test/th1-tcl3.txt
+++ b/test/th1-tcl3.txt
@@ -0,0 +1,9 @@
1 <th1>
2 #
3 # This is a "TH1 fragment" used to test the TH1.
4 # The corresponding test file e test-th-render
5 # the
6 # test-script-render Fossil command.
7 #
8 proc doOut {msg} {puts $msg; puts \n}
9 doOut [t
--- a/test/th1-tcl4.txt
+++ b/test/th1-tcl4.txt
@@ -0,0 +1,9 @@
1
+<th1>
2
+ #
3
+ # This is a "TH1 fragment" used to test the TH1.
4
+ # The corresponding test file e test-th-render
5
+ # the
6
+ # test-script-render Fossil command.
7
+ #
8
+ proc doOut {msg} {puts $msg; puts \n}
9
+
--- a/test/th1-tcl4.txt
+++ b/test/th1-tcl4.txt
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
--- a/test/th1-tcl4.txt
+++ b/test/th1-tcl4.txt
@@ -0,0 +1,9 @@
1 <th1>
2 #
3 # This is a "TH1 fragment" used to test the TH1.
4 # The corresponding test file e test-th-render
5 # the
6 # test-script-render Fossil command.
7 #
8 proc doOut {msg} {puts $msg; puts \n}
9
--- a/test/th1-tcl5.txt
+++ b/test/th1-tcl5.txt
@@ -0,0 +1,5 @@
1
+<th1>
2
+ #
3
+ # This is a "TH1 fragment" used to test the TH1.
4
+ # The corresponding test file e test-th-render
5
+ #
--- a/test/th1-tcl5.txt
+++ b/test/th1-tcl5.txt
@@ -0,0 +1,5 @@
 
 
 
 
 
--- a/test/th1-tcl5.txt
+++ b/test/th1-tcl5.txt
@@ -0,0 +1,5 @@
1 <th1>
2 #
3 # This is a "TH1 fragment" used to test the TH1.
4 # The corresponding test file e test-th-render
5 #
--- a/test/th1-tcl6.txt
+++ b/test/th1-tcl6.txt
@@ -0,0 +1,9 @@
1
+<th1>
2
+ #
3
+ # This is a "TH1 fragment" used to test the TH1.
4
+ # The corresponding test file e test-th-render
5
+ # the
6
+ # test-script-render Fossil command.
7
+ #
8
+ proc doOut {msg} {puts $msg;th1Eval bad_command]
9
+</th1>
--- a/test/th1-tcl6.txt
+++ b/test/th1-tcl6.txt
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
--- a/test/th1-tcl6.txt
+++ b/test/th1-tcl6.txt
@@ -0,0 +1,9 @@
1 <th1>
2 #
3 # This is a "TH1 fragment" used to test the TH1.
4 # The corresponding test file e test-th-render
5 # the
6 # test-script-render Fossil command.
7 #
8 proc doOut {msg} {puts $msg;th1Eval bad_command]
9 </th1>
--- a/test/th1-tcl7.txt
+++ b/test/th1-tcl7.txt
@@ -0,0 +1,18 @@
1
+<th1>
2
+ #
3
+ # This is a "TH1 fragment" used to test the TH1.
4
+ # The corresponding test file e test-th-render
5
+ # the
6
+ # test-script-render Fossil command.
7
+ #
8
+ proc doOut {msg} {puts $msg; puts \n}
9
+
10
+ #
11
+ # BUGBUG: Attempting to divide by zero will crash TH1 with the error:
12
+ # "child killed: floating-point exceptith1Expr 2/0]
13
+
14
+ #
15
+ # NOTE: For now, just cause an expression syntax error.
16
+ #
17
+ doOut [tclEval th1Expr 2**0]
18
+</th1>
--- a/test/th1-tcl7.txt
+++ b/test/th1-tcl7.txt
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/th1-tcl7.txt
+++ b/test/th1-tcl7.txt
@@ -0,0 +1,18 @@
1 <th1>
2 #
3 # This is a "TH1 fragment" used to test the TH1.
4 # The corresponding test file e test-th-render
5 # the
6 # test-script-render Fossil command.
7 #
8 proc doOut {msg} {puts $msg; puts \n}
9
10 #
11 # BUGBUG: Attempting to divide by zero will crash TH1 with the error:
12 # "child killed: floating-point exceptith1Expr 2/0]
13
14 #
15 # NOTE: For now, just cause an expression syntax error.
16 #
17 doOut [tclEval th1Expr 2**0]
18 </th1>
--- a/test/th1-tcl8.txt
+++ b/test/th1-tcl8.txt
@@ -0,0 +1,11 @@
1
+<th1>
2
+ #
3
+ # This is a "TH1 fragment" used to test the TH1.
4
+ #e e
5
+ # test-script-rendersing test-th-render
6
+ # Fossil command.
7
+ #
8
+ proc doOut {msg} {puts $msg; puts \n}
9
+
10
+ if {[tclInvoke set tcl_version] >= 8.6} {
11
+ doOut [tclInvoke tailcadoOut "This test requires Tcl
--- a/test/th1-tcl8.txt
+++ b/test/th1-tcl8.txt
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
--- a/test/th1-tcl8.txt
+++ b/test/th1-tcl8.txt
@@ -0,0 +1,11 @@
1 <th1>
2 #
3 # This is a "TH1 fragment" used to test the TH1.
4 #e e
5 # test-script-rendersing test-th-render
6 # Fossil command.
7 #
8 proc doOut {msg} {puts $msg; puts \n}
9
10 if {[tclInvoke set tcl_version] >= 8.6} {
11 doOut [tclInvoke tailcadoOut "This test requires Tcl
+86 -4
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -22,13 +22,13 @@
2222
TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
2323
LIBS = $(DMDIR)\extra\lib\ zlib wsock32
2424
2525
SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
2626
27
-SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
27
+SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_diff_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
2828
29
-OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
29
+OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
3030
3131
3232
RC=$(DMDIR)\bin\rcc
3333
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
3434
@@ -42,11 +42,11 @@
4242
4343
$(OBJDIR)\fossil.res: $B\win\fossil.rc
4444
$(RC) $(RCFLAGS) -o$@ $**
4545
4646
$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
47
- +echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo glob graph gzip http http_socket http_ssl http_transport import info leaf login main manifest md5 merge merge3 name path pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip shell sqlite3 th th_lang > $@
47
+ +echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_diff json_login json_query json_report json_tag json_timeline json_user json_wiki leaf login main manifest md5 merge merge3 name path pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip shell sqlite3 th th_lang > $@
4848
+echo fossil >> $@
4949
+echo fossil >> $@
5050
+echo $(LIBS) >> $@
5151
+echo. >> $@
5252
+echo fossil >> $@
@@ -72,10 +72,13 @@
7272
$(OBJDIR)\th$O : $(SRCDIR)\th.c
7373
$(TCC) -o$@ -c $**
7474
7575
$(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
7676
$(TCC) -o$@ -c $**
77
+
78
+$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
79
+ cp $@ $@
7780
7881
VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
7982
+$** > $@
8083
8184
page_index.h: mkindex$E $(SRC)
@@ -85,10 +88,23 @@
8588
-del $(OBJDIR)\*.obj
8689
-del *.obj *_.c *.h *.map
8790
8891
realclean:
8992
-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
93
+
94
+$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
95
+$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
96
+$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
97
+$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
98
+$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
99
+$(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
100
+$(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
101
+$(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
102
+$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
103
+$(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
104
+$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
105
+
90106
91107
92108
$(OBJDIR)\add$O : add_.c add.h
93109
$(TCC) -o$@ -c add_.c
94110
@@ -314,10 +330,76 @@
314330
$(OBJDIR)\info$O : info_.c info.h
315331
$(TCC) -o$@ -c info_.c
316332
317333
info_.c : $(SRCDIR)\info.c
318334
+translate$E $** > $@
335
+
336
+$(OBJDIR)\json$O : json_.c json.h
337
+ $(TCC) -o$@ -c json_.c
338
+
339
+json_.c : $(SRCDIR)\json.c
340
+ +translate$E $** > $@
341
+
342
+$(OBJDIR)\json_artifact$O : json_artifact_.c json_artifact.h
343
+ $(TCC) -o$@ -c json_artifact_.c
344
+
345
+json_artifact_.c : $(SRCDIR)\json_artifact.c
346
+ +translate$E $** > $@
347
+
348
+$(OBJDIR)\json_branch$O : json_branch_.c json_branch.h
349
+ $(TCC) -o$@ -c json_branch_.c
350
+
351
+json_branch_.c : $(SRCDIR)\json_branch.c
352
+ +translate$E $** > $@
353
+
354
+$(OBJDIR)\json_diff$O : json_diff_.c json_diff.h
355
+ $(TCC) -o$@ -c json_diff_.c
356
+
357
+json_diff_.c : $(SRCDIR)\json_diff.c
358
+ +translate$E $** > $@
359
+
360
+$(OBJDIR)\json_login$O : json_login_.c json_login.h
361
+ $(TCC) -o$@ -c json_login_.c
362
+
363
+json_login_.c : $(SRCDIR)\json_login.c
364
+ +translate$E $** > $@
365
+
366
+$(OBJDIR)\json_query$O : json_query_.c json_query.h
367
+ $(TCC) -o$@ -c json_query_.c
368
+
369
+json_query_.c : $(SRCDIR)\json_query.c
370
+ +translate$E $** > $@
371
+
372
+$(OBJDIR)\json_report$O : json_report_.c json_report.h
373
+ $(TCC) -o$@ -c json_report_.c
374
+
375
+json_report_.c : $(SRCDIR)\json_report.c
376
+ +translate$E $** > $@
377
+
378
+$(OBJDIR)\json_tag$O : json_tag_.c json_tag.h
379
+ $(TCC) -o$@ -c json_tag_.c
380
+
381
+json_tag_.c : $(SRCDIR)\json_tag.c
382
+ +translate$E $** > $@
383
+
384
+$(OBJDIR)\json_timeline$O : json_timeline_.c json_timeline.h
385
+ $(TCC) -o$@ -c json_timeline_.c
386
+
387
+json_timeline_.c : $(SRCDIR)\json_timeline.c
388
+ +translate$E $** > $@
389
+
390
+$(OBJDIR)\json_user$O : json_user_.c json_user.h
391
+ $(TCC) -o$@ -c json_user_.c
392
+
393
+json_user_.c : $(SRCDIR)\json_user.c
394
+ +translate$E $** > $@
395
+
396
+$(OBJDIR)\json_wiki$O : json_wiki_.c json_wiki.h
397
+ $(TCC) -o$@ -c json_wiki_.c
398
+
399
+json_wiki_.c : $(SRCDIR)\json_wiki.c
400
+ +translate$E $** > $@
319401
320402
$(OBJDIR)\leaf$O : leaf_.c leaf.h
321403
$(TCC) -o$@ -c leaf_.c
322404
323405
leaf_.c : $(SRCDIR)\leaf.c
@@ -580,7 +662,7 @@
580662
581663
zip_.c : $(SRCDIR)\zip.c
582664
+translate$E $** > $@
583665
584666
headers: makeheaders$E page_index.h VERSION.h
585
- +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h
667
+ +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_diff_.c:json_diff.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
586668
@copy /Y nul: headers
587669
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -22,13 +22,13 @@
22 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
23 LIBS = $(DMDIR)\extra\lib\ zlib wsock32
24
25 SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
26
27 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
28
29 OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
30
31
32 RC=$(DMDIR)\bin\rcc
33 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
34
@@ -42,11 +42,11 @@
42
43 $(OBJDIR)\fossil.res: $B\win\fossil.rc
44 $(RC) $(RCFLAGS) -o$@ $**
45
46 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
47 +echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo glob graph gzip http http_socket http_ssl http_transport import info leaf login main manifest md5 merge merge3 name path pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip shell sqlite3 th th_lang > $@
48 +echo fossil >> $@
49 +echo fossil >> $@
50 +echo $(LIBS) >> $@
51 +echo. >> $@
52 +echo fossil >> $@
@@ -72,10 +72,13 @@
72 $(OBJDIR)\th$O : $(SRCDIR)\th.c
73 $(TCC) -o$@ -c $**
74
75 $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
76 $(TCC) -o$@ -c $**
 
 
 
77
78 VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
79 +$** > $@
80
81 page_index.h: mkindex$E $(SRC)
@@ -85,10 +88,23 @@
85 -del $(OBJDIR)\*.obj
86 -del *.obj *_.c *.h *.map
87
88 realclean:
89 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
 
 
 
 
 
 
 
 
 
 
 
 
 
90
91
92 $(OBJDIR)\add$O : add_.c add.h
93 $(TCC) -o$@ -c add_.c
94
@@ -314,10 +330,76 @@
314 $(OBJDIR)\info$O : info_.c info.h
315 $(TCC) -o$@ -c info_.c
316
317 info_.c : $(SRCDIR)\info.c
318 +translate$E $** > $@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
320 $(OBJDIR)\leaf$O : leaf_.c leaf.h
321 $(TCC) -o$@ -c leaf_.c
322
323 leaf_.c : $(SRCDIR)\leaf.c
@@ -580,7 +662,7 @@
580
581 zip_.c : $(SRCDIR)\zip.c
582 +translate$E $** > $@
583
584 headers: makeheaders$E page_index.h VERSION.h
585 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h
586 @copy /Y nul: headers
587
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -22,13 +22,13 @@
22 TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
23 LIBS = $(DMDIR)\extra\lib\ zlib wsock32
24
25 SQLITE_OPTIONS = -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0
26
27 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_diff_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
28
29 OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
30
31
32 RC=$(DMDIR)\bin\rcc
33 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
34
@@ -42,11 +42,11 @@
42
43 $(OBJDIR)\fossil.res: $B\win\fossil.rc
44 $(RC) $(RCFLAGS) -o$@ $**
45
46 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
47 +echo add allrepo attach bag bisect blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo glob graph gzip http http_socket http_ssl http_transport import info json json_artifact json_branch json_diff json_login json_query json_report json_tag json_timeline json_user json_wiki leaf login main manifest md5 merge merge3 name path pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip shell sqlite3 th th_lang > $@
48 +echo fossil >> $@
49 +echo fossil >> $@
50 +echo $(LIBS) >> $@
51 +echo. >> $@
52 +echo fossil >> $@
@@ -72,10 +72,13 @@
72 $(OBJDIR)\th$O : $(SRCDIR)\th.c
73 $(TCC) -o$@ -c $**
74
75 $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c
76 $(TCC) -o$@ -c $**
77
78 $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
79 cp $@ $@
80
81 VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION
82 +$** > $@
83
84 page_index.h: mkindex$E $(SRC)
@@ -85,10 +88,23 @@
88 -del $(OBJDIR)\*.obj
89 -del *.obj *_.c *.h *.map
90
91 realclean:
92 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
93
94 $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
95 $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
96 $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
97 $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
98 $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
99 $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
100 $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
101 $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
102 $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
103 $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
104 $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
105
106
107
108 $(OBJDIR)\add$O : add_.c add.h
109 $(TCC) -o$@ -c add_.c
110
@@ -314,10 +330,76 @@
330 $(OBJDIR)\info$O : info_.c info.h
331 $(TCC) -o$@ -c info_.c
332
333 info_.c : $(SRCDIR)\info.c
334 +translate$E $** > $@
335
336 $(OBJDIR)\json$O : json_.c json.h
337 $(TCC) -o$@ -c json_.c
338
339 json_.c : $(SRCDIR)\json.c
340 +translate$E $** > $@
341
342 $(OBJDIR)\json_artifact$O : json_artifact_.c json_artifact.h
343 $(TCC) -o$@ -c json_artifact_.c
344
345 json_artifact_.c : $(SRCDIR)\json_artifact.c
346 +translate$E $** > $@
347
348 $(OBJDIR)\json_branch$O : json_branch_.c json_branch.h
349 $(TCC) -o$@ -c json_branch_.c
350
351 json_branch_.c : $(SRCDIR)\json_branch.c
352 +translate$E $** > $@
353
354 $(OBJDIR)\json_diff$O : json_diff_.c json_diff.h
355 $(TCC) -o$@ -c json_diff_.c
356
357 json_diff_.c : $(SRCDIR)\json_diff.c
358 +translate$E $** > $@
359
360 $(OBJDIR)\json_login$O : json_login_.c json_login.h
361 $(TCC) -o$@ -c json_login_.c
362
363 json_login_.c : $(SRCDIR)\json_login.c
364 +translate$E $** > $@
365
366 $(OBJDIR)\json_query$O : json_query_.c json_query.h
367 $(TCC) -o$@ -c json_query_.c
368
369 json_query_.c : $(SRCDIR)\json_query.c
370 +translate$E $** > $@
371
372 $(OBJDIR)\json_report$O : json_report_.c json_report.h
373 $(TCC) -o$@ -c json_report_.c
374
375 json_report_.c : $(SRCDIR)\json_report.c
376 +translate$E $** > $@
377
378 $(OBJDIR)\json_tag$O : json_tag_.c json_tag.h
379 $(TCC) -o$@ -c json_tag_.c
380
381 json_tag_.c : $(SRCDIR)\json_tag.c
382 +translate$E $** > $@
383
384 $(OBJDIR)\json_timeline$O : json_timeline_.c json_timeline.h
385 $(TCC) -o$@ -c json_timeline_.c
386
387 json_timeline_.c : $(SRCDIR)\json_timeline.c
388 +translate$E $** > $@
389
390 $(OBJDIR)\json_user$O : json_user_.c json_user.h
391 $(TCC) -o$@ -c json_user_.c
392
393 json_user_.c : $(SRCDIR)\json_user.c
394 +translate$E $** > $@
395
396 $(OBJDIR)\json_wiki$O : json_wiki_.c json_wiki.h
397 $(TCC) -o$@ -c json_wiki_.c
398
399 json_wiki_.c : $(SRCDIR)\json_wiki.c
400 +translate$E $** > $@
401
402 $(OBJDIR)\leaf$O : leaf_.c leaf.h
403 $(TCC) -o$@ -c leaf_.c
404
405 leaf_.c : $(SRCDIR)\leaf.c
@@ -580,7 +662,7 @@
662
663 zip_.c : $(SRCDIR)\zip.c
664 +translate$E $** > $@
665
666 headers: makeheaders$E page_index.h VERSION.h
667 +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_diff_.c:json_diff.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
668 @copy /Y nul: headers
669
+116 -2
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -110,10 +110,21 @@
110110
$(SRCDIR)/http_socket.c \
111111
$(SRCDIR)/http_ssl.c \
112112
$(SRCDIR)/http_transport.c \
113113
$(SRCDIR)/import.c \
114114
$(SRCDIR)/info.c \
115
+ $(SRCDIR)/json.c \
116
+ $(SRCDIR)/json_artifact.c \
117
+ $(SRCDIR)/json_branch.c \
118
+ $(SRCDIR)/json_diff.c \
119
+ $(SRCDIR)/json_login.c \
120
+ $(SRCDIR)/json_query.c \
121
+ $(SRCDIR)/json_report.c \
122
+ $(SRCDIR)/json_tag.c \
123
+ $(SRCDIR)/json_timeline.c \
124
+ $(SRCDIR)/json_user.c \
125
+ $(SRCDIR)/json_wiki.c \
115126
$(SRCDIR)/leaf.c \
116127
$(SRCDIR)/login.c \
117128
$(SRCDIR)/main.c \
118129
$(SRCDIR)/manifest.c \
119130
$(SRCDIR)/md5.c \
@@ -194,10 +205,21 @@
194205
$(OBJDIR)/http_socket_.c \
195206
$(OBJDIR)/http_ssl_.c \
196207
$(OBJDIR)/http_transport_.c \
197208
$(OBJDIR)/import_.c \
198209
$(OBJDIR)/info_.c \
210
+ $(OBJDIR)/json_.c \
211
+ $(OBJDIR)/json_artifact_.c \
212
+ $(OBJDIR)/json_branch_.c \
213
+ $(OBJDIR)/json_diff_.c \
214
+ $(OBJDIR)/json_login_.c \
215
+ $(OBJDIR)/json_query_.c \
216
+ $(OBJDIR)/json_report_.c \
217
+ $(OBJDIR)/json_tag_.c \
218
+ $(OBJDIR)/json_timeline_.c \
219
+ $(OBJDIR)/json_user_.c \
220
+ $(OBJDIR)/json_wiki_.c \
199221
$(OBJDIR)/leaf_.c \
200222
$(OBJDIR)/login_.c \
201223
$(OBJDIR)/main_.c \
202224
$(OBJDIR)/manifest_.c \
203225
$(OBJDIR)/md5_.c \
@@ -278,10 +300,21 @@
278300
$(OBJDIR)/http_socket.o \
279301
$(OBJDIR)/http_ssl.o \
280302
$(OBJDIR)/http_transport.o \
281303
$(OBJDIR)/import.o \
282304
$(OBJDIR)/info.o \
305
+ $(OBJDIR)/json.o \
306
+ $(OBJDIR)/json_artifact.o \
307
+ $(OBJDIR)/json_branch.o \
308
+ $(OBJDIR)/json_diff.o \
309
+ $(OBJDIR)/json_login.o \
310
+ $(OBJDIR)/json_query.o \
311
+ $(OBJDIR)/json_report.o \
312
+ $(OBJDIR)/json_tag.o \
313
+ $(OBJDIR)/json_timeline.o \
314
+ $(OBJDIR)/json_user.o \
315
+ $(OBJDIR)/json_wiki.o \
283316
$(OBJDIR)/leaf.o \
284317
$(OBJDIR)/login.o \
285318
$(OBJDIR)/main.o \
286319
$(OBJDIR)/manifest.o \
287320
$(OBJDIR)/md5.o \
@@ -363,11 +396,11 @@
363396
$(TCLSH) test/tester.tcl $(APPNAME)
364397
365398
$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)
366399
$(VERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
367400
368
-EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o
401
+EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(OBJDIR)/cson_amalgamation.o
369402
370403
$(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/icon.o
371404
$(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/icon.o
372405
373406
# This rule prevents make from using its default rules to try build
@@ -388,11 +421,11 @@
388421
389422
390423
$(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
391424
$(MKINDEX) $(TRANS_SRC) >$@
392425
$(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
393
- $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
426
+ $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
394427
echo Done >$(OBJDIR)/headers
395428
396429
$(OBJDIR)/headers: Makefile
397430
Makefile:
398431
$(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate
@@ -659,10 +692,87 @@
659692
660693
$(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h
661694
$(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c
662695
663696
info.h: $(OBJDIR)/headers
697
+$(OBJDIR)/json_.c: $(SRCDIR)/json.c $(OBJDIR)/translate
698
+ $(TRANSLATE) $(SRCDIR)/json.c >$(OBJDIR)/json_.c
699
+
700
+$(OBJDIR)/json.o: $(OBJDIR)/json_.c $(OBJDIR)/json.h $(SRCDIR)/config.h
701
+ $(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c
702
+
703
+json.h: $(OBJDIR)/headers
704
+$(OBJDIR)/json_artifact_.c: $(SRCDIR)/json_artifact.c $(OBJDIR)/translate
705
+ $(TRANSLATE) $(SRCDIR)/json_artifact.c >$(OBJDIR)/json_artifact_.c
706
+
707
+$(OBJDIR)/json_artifact.o: $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h $(SRCDIR)/config.h
708
+ $(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c
709
+
710
+json_artifact.h: $(OBJDIR)/headers
711
+$(OBJDIR)/json_branch_.c: $(SRCDIR)/json_branch.c $(OBJDIR)/translate
712
+ $(TRANSLATE) $(SRCDIR)/json_branch.c >$(OBJDIR)/json_branch_.c
713
+
714
+$(OBJDIR)/json_branch.o: $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h $(SRCDIR)/config.h
715
+ $(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c
716
+
717
+json_branch.h: $(OBJDIR)/headers
718
+$(OBJDIR)/json_diff_.c: $(SRCDIR)/json_diff.c $(OBJDIR)/translate
719
+ $(TRANSLATE) $(SRCDIR)/json_diff.c >$(OBJDIR)/json_diff_.c
720
+
721
+$(OBJDIR)/json_diff.o: $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h $(SRCDIR)/config.h
722
+ $(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c
723
+
724
+json_diff.h: $(OBJDIR)/headers
725
+$(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(OBJDIR)/translate
726
+ $(TRANSLATE) $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c
727
+
728
+$(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR)/config.h
729
+ $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c
730
+
731
+json_login.h: $(OBJDIR)/headers
732
+$(OBJDIR)/json_query_.c: $(SRCDIR)/json_query.c $(OBJDIR)/translate
733
+ $(TRANSLATE) $(SRCDIR)/json_query.c >$(OBJDIR)/json_query_.c
734
+
735
+$(OBJDIR)/json_query.o: $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h $(SRCDIR)/config.h
736
+ $(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c
737
+
738
+json_query.h: $(OBJDIR)/headers
739
+$(OBJDIR)/json_report_.c: $(SRCDIR)/json_report.c $(OBJDIR)/translate
740
+ $(TRANSLATE) $(SRCDIR)/json_report.c >$(OBJDIR)/json_report_.c
741
+
742
+$(OBJDIR)/json_report.o: $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h $(SRCDIR)/config.h
743
+ $(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c
744
+
745
+json_report.h: $(OBJDIR)/headers
746
+$(OBJDIR)/json_tag_.c: $(SRCDIR)/json_tag.c $(OBJDIR)/translate
747
+ $(TRANSLATE) $(SRCDIR)/json_tag.c >$(OBJDIR)/json_tag_.c
748
+
749
+$(OBJDIR)/json_tag.o: $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h $(SRCDIR)/config.h
750
+ $(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c
751
+
752
+json_tag.h: $(OBJDIR)/headers
753
+$(OBJDIR)/json_timeline_.c: $(SRCDIR)/json_timeline.c $(OBJDIR)/translate
754
+ $(TRANSLATE) $(SRCDIR)/json_timeline.c >$(OBJDIR)/json_timeline_.c
755
+
756
+$(OBJDIR)/json_timeline.o: $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h $(SRCDIR)/config.h
757
+ $(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c
758
+
759
+json_timeline.h: $(OBJDIR)/headers
760
+$(OBJDIR)/json_user_.c: $(SRCDIR)/json_user.c $(OBJDIR)/translate
761
+ $(TRANSLATE) $(SRCDIR)/json_user.c >$(OBJDIR)/json_user_.c
762
+
763
+$(OBJDIR)/json_user.o: $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h $(SRCDIR)/config.h
764
+ $(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c
765
+
766
+json_user.h: $(OBJDIR)/headers
767
+$(OBJDIR)/json_wiki_.c: $(SRCDIR)/json_wiki.c $(OBJDIR)/translate
768
+ $(TRANSLATE) $(SRCDIR)/json_wiki.c >$(OBJDIR)/json_wiki_.c
769
+
770
+$(OBJDIR)/json_wiki.o: $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h $(SRCDIR)/config.h
771
+ $(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c
772
+
773
+json_wiki.h: $(OBJDIR)/headers
664774
$(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate
665775
$(TRANSLATE) $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c
666776
667777
$(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h
668778
$(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c
@@ -970,14 +1080,18 @@
9701080
9711081
zip.h: $(OBJDIR)/headers
9721082
$(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c
9731083
$(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
9741084
1085
+$(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
1086
+ $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE
1087
+
1088
+$(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
9751089
$(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
9761090
$(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
9771091
9781092
$(OBJDIR)/th.o: $(SRCDIR)/th.c
9791093
$(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
9801094
9811095
$(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
9821096
$(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
9831097
9841098
9851099
ADDED win/Makefile.mingw.mistachkin
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -110,10 +110,21 @@
110 $(SRCDIR)/http_socket.c \
111 $(SRCDIR)/http_ssl.c \
112 $(SRCDIR)/http_transport.c \
113 $(SRCDIR)/import.c \
114 $(SRCDIR)/info.c \
 
 
 
 
 
 
 
 
 
 
 
115 $(SRCDIR)/leaf.c \
116 $(SRCDIR)/login.c \
117 $(SRCDIR)/main.c \
118 $(SRCDIR)/manifest.c \
119 $(SRCDIR)/md5.c \
@@ -194,10 +205,21 @@
194 $(OBJDIR)/http_socket_.c \
195 $(OBJDIR)/http_ssl_.c \
196 $(OBJDIR)/http_transport_.c \
197 $(OBJDIR)/import_.c \
198 $(OBJDIR)/info_.c \
 
 
 
 
 
 
 
 
 
 
 
199 $(OBJDIR)/leaf_.c \
200 $(OBJDIR)/login_.c \
201 $(OBJDIR)/main_.c \
202 $(OBJDIR)/manifest_.c \
203 $(OBJDIR)/md5_.c \
@@ -278,10 +300,21 @@
278 $(OBJDIR)/http_socket.o \
279 $(OBJDIR)/http_ssl.o \
280 $(OBJDIR)/http_transport.o \
281 $(OBJDIR)/import.o \
282 $(OBJDIR)/info.o \
 
 
 
 
 
 
 
 
 
 
 
283 $(OBJDIR)/leaf.o \
284 $(OBJDIR)/login.o \
285 $(OBJDIR)/main.o \
286 $(OBJDIR)/manifest.o \
287 $(OBJDIR)/md5.o \
@@ -363,11 +396,11 @@
363 $(TCLSH) test/tester.tcl $(APPNAME)
364
365 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)
366 $(VERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
367
368 EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o
369
370 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/icon.o
371 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/icon.o
372
373 # This rule prevents make from using its default rules to try build
@@ -388,11 +421,11 @@
388
389
390 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
391 $(MKINDEX) $(TRANS_SRC) >$@
392 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
393 $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
394 echo Done >$(OBJDIR)/headers
395
396 $(OBJDIR)/headers: Makefile
397 Makefile:
398 $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate
@@ -659,10 +692,87 @@
659
660 $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h
661 $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c
662
663 info.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664 $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate
665 $(TRANSLATE) $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c
666
667 $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h
668 $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c
@@ -970,14 +1080,18 @@
970
971 zip.h: $(OBJDIR)/headers
972 $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c
973 $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
974
 
 
 
 
975 $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
976 $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
977
978 $(OBJDIR)/th.o: $(SRCDIR)/th.c
979 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
980
981 $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
982 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
983
984
985 DDED win/Makefile.mingw.mistachkin
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -110,10 +110,21 @@
110 $(SRCDIR)/http_socket.c \
111 $(SRCDIR)/http_ssl.c \
112 $(SRCDIR)/http_transport.c \
113 $(SRCDIR)/import.c \
114 $(SRCDIR)/info.c \
115 $(SRCDIR)/json.c \
116 $(SRCDIR)/json_artifact.c \
117 $(SRCDIR)/json_branch.c \
118 $(SRCDIR)/json_diff.c \
119 $(SRCDIR)/json_login.c \
120 $(SRCDIR)/json_query.c \
121 $(SRCDIR)/json_report.c \
122 $(SRCDIR)/json_tag.c \
123 $(SRCDIR)/json_timeline.c \
124 $(SRCDIR)/json_user.c \
125 $(SRCDIR)/json_wiki.c \
126 $(SRCDIR)/leaf.c \
127 $(SRCDIR)/login.c \
128 $(SRCDIR)/main.c \
129 $(SRCDIR)/manifest.c \
130 $(SRCDIR)/md5.c \
@@ -194,10 +205,21 @@
205 $(OBJDIR)/http_socket_.c \
206 $(OBJDIR)/http_ssl_.c \
207 $(OBJDIR)/http_transport_.c \
208 $(OBJDIR)/import_.c \
209 $(OBJDIR)/info_.c \
210 $(OBJDIR)/json_.c \
211 $(OBJDIR)/json_artifact_.c \
212 $(OBJDIR)/json_branch_.c \
213 $(OBJDIR)/json_diff_.c \
214 $(OBJDIR)/json_login_.c \
215 $(OBJDIR)/json_query_.c \
216 $(OBJDIR)/json_report_.c \
217 $(OBJDIR)/json_tag_.c \
218 $(OBJDIR)/json_timeline_.c \
219 $(OBJDIR)/json_user_.c \
220 $(OBJDIR)/json_wiki_.c \
221 $(OBJDIR)/leaf_.c \
222 $(OBJDIR)/login_.c \
223 $(OBJDIR)/main_.c \
224 $(OBJDIR)/manifest_.c \
225 $(OBJDIR)/md5_.c \
@@ -278,10 +300,21 @@
300 $(OBJDIR)/http_socket.o \
301 $(OBJDIR)/http_ssl.o \
302 $(OBJDIR)/http_transport.o \
303 $(OBJDIR)/import.o \
304 $(OBJDIR)/info.o \
305 $(OBJDIR)/json.o \
306 $(OBJDIR)/json_artifact.o \
307 $(OBJDIR)/json_branch.o \
308 $(OBJDIR)/json_diff.o \
309 $(OBJDIR)/json_login.o \
310 $(OBJDIR)/json_query.o \
311 $(OBJDIR)/json_report.o \
312 $(OBJDIR)/json_tag.o \
313 $(OBJDIR)/json_timeline.o \
314 $(OBJDIR)/json_user.o \
315 $(OBJDIR)/json_wiki.o \
316 $(OBJDIR)/leaf.o \
317 $(OBJDIR)/login.o \
318 $(OBJDIR)/main.o \
319 $(OBJDIR)/manifest.o \
320 $(OBJDIR)/md5.o \
@@ -363,11 +396,11 @@
396 $(TCLSH) test/tester.tcl $(APPNAME)
397
398 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION)
399 $(VERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
400
401 EXTRAOBJ = $(OBJDIR)/sqlite3.o $(OBJDIR)/shell.o $(OBJDIR)/th.o $(OBJDIR)/th_lang.o $(OBJDIR)/cson_amalgamation.o
402
403 $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/icon.o
404 $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/icon.o
405
406 # This rule prevents make from using its default rules to try build
@@ -388,11 +421,11 @@
421
422
423 $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex
424 $(MKINDEX) $(TRANS_SRC) >$@
425 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
426 $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h $(OBJDIR)/browse_.c:$(OBJDIR)/browse.h $(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h $(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h $(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h $(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h $(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h $(OBJDIR)/clone_.c:$(OBJDIR)/clone.h $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h $(OBJDIR)/content_.c:$(OBJDIR)/content.h $(OBJDIR)/db_.c:$(OBJDIR)/db.h $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h $(OBJDIR)/event_.c:$(OBJDIR)/event.h $(OBJDIR)/export_.c:$(OBJDIR)/export.h $(OBJDIR)/file_.c:$(OBJDIR)/file.h $(OBJDIR)/finfo_.c:$(OBJDIR)/finfo.h $(OBJDIR)/glob_.c:$(OBJDIR)/glob.h $(OBJDIR)/graph_.c:$(OBJDIR)/graph.h $(OBJDIR)/gzip_.c:$(OBJDIR)/gzip.h $(OBJDIR)/http_.c:$(OBJDIR)/http.h $(OBJDIR)/http_socket_.c:$(OBJDIR)/http_socket.h $(OBJDIR)/http_ssl_.c:$(OBJDIR)/http_ssl.h $(OBJDIR)/http_transport_.c:$(OBJDIR)/http_transport.h $(OBJDIR)/import_.c:$(OBJDIR)/import.h $(OBJDIR)/info_.c:$(OBJDIR)/info.h $(OBJDIR)/json_.c:$(OBJDIR)/json.h $(OBJDIR)/json_artifact_.c:$(OBJDIR)/json_artifact.h $(OBJDIR)/json_branch_.c:$(OBJDIR)/json_branch.h $(OBJDIR)/json_diff_.c:$(OBJDIR)/json_diff.h $(OBJDIR)/json_login_.c:$(OBJDIR)/json_login.h $(OBJDIR)/json_query_.c:$(OBJDIR)/json_query.h $(OBJDIR)/json_report_.c:$(OBJDIR)/json_report.h $(OBJDIR)/json_tag_.c:$(OBJDIR)/json_tag.h $(OBJDIR)/json_timeline_.c:$(OBJDIR)/json_timeline.h $(OBJDIR)/json_user_.c:$(OBJDIR)/json_user.h $(OBJDIR)/json_wiki_.c:$(OBJDIR)/json_wiki.h $(OBJDIR)/leaf_.c:$(OBJDIR)/leaf.h $(OBJDIR)/login_.c:$(OBJDIR)/login.h $(OBJDIR)/main_.c:$(OBJDIR)/main.h $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h $(OBJDIR)/name_.c:$(OBJDIR)/name.h $(OBJDIR)/path_.c:$(OBJDIR)/path.h $(OBJDIR)/pivot_.c:$(OBJDIR)/pivot.h $(OBJDIR)/popen_.c:$(OBJDIR)/popen.h $(OBJDIR)/pqueue_.c:$(OBJDIR)/pqueue.h $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h $(OBJDIR)/report_.c:$(OBJDIR)/report.h $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h $(OBJDIR)/search_.c:$(OBJDIR)/search.h $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h $(OBJDIR)/shun_.c:$(OBJDIR)/shun.h $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h $(OBJDIR)/style_.c:$(OBJDIR)/style.h $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h $(OBJDIR)/update_.c:$(OBJDIR)/update.h $(OBJDIR)/url_.c:$(OBJDIR)/url.h $(OBJDIR)/user_.c:$(OBJDIR)/user.h $(OBJDIR)/verify_.c:$(OBJDIR)/verify.h $(OBJDIR)/vfile_.c:$(OBJDIR)/vfile.h $(OBJDIR)/wiki_.c:$(OBJDIR)/wiki.h $(OBJDIR)/wikiformat_.c:$(OBJDIR)/wikiformat.h $(OBJDIR)/winhttp_.c:$(OBJDIR)/winhttp.h $(OBJDIR)/xfer_.c:$(OBJDIR)/xfer.h $(OBJDIR)/zip_.c:$(OBJDIR)/zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h $(OBJDIR)/VERSION.h
427 echo Done >$(OBJDIR)/headers
428
429 $(OBJDIR)/headers: Makefile
430 Makefile:
431 $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate
@@ -659,10 +692,87 @@
692
693 $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h
694 $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c
695
696 info.h: $(OBJDIR)/headers
697 $(OBJDIR)/json_.c: $(SRCDIR)/json.c $(OBJDIR)/translate
698 $(TRANSLATE) $(SRCDIR)/json.c >$(OBJDIR)/json_.c
699
700 $(OBJDIR)/json.o: $(OBJDIR)/json_.c $(OBJDIR)/json.h $(SRCDIR)/config.h
701 $(XTCC) -o $(OBJDIR)/json.o -c $(OBJDIR)/json_.c
702
703 json.h: $(OBJDIR)/headers
704 $(OBJDIR)/json_artifact_.c: $(SRCDIR)/json_artifact.c $(OBJDIR)/translate
705 $(TRANSLATE) $(SRCDIR)/json_artifact.c >$(OBJDIR)/json_artifact_.c
706
707 $(OBJDIR)/json_artifact.o: $(OBJDIR)/json_artifact_.c $(OBJDIR)/json_artifact.h $(SRCDIR)/config.h
708 $(XTCC) -o $(OBJDIR)/json_artifact.o -c $(OBJDIR)/json_artifact_.c
709
710 json_artifact.h: $(OBJDIR)/headers
711 $(OBJDIR)/json_branch_.c: $(SRCDIR)/json_branch.c $(OBJDIR)/translate
712 $(TRANSLATE) $(SRCDIR)/json_branch.c >$(OBJDIR)/json_branch_.c
713
714 $(OBJDIR)/json_branch.o: $(OBJDIR)/json_branch_.c $(OBJDIR)/json_branch.h $(SRCDIR)/config.h
715 $(XTCC) -o $(OBJDIR)/json_branch.o -c $(OBJDIR)/json_branch_.c
716
717 json_branch.h: $(OBJDIR)/headers
718 $(OBJDIR)/json_diff_.c: $(SRCDIR)/json_diff.c $(OBJDIR)/translate
719 $(TRANSLATE) $(SRCDIR)/json_diff.c >$(OBJDIR)/json_diff_.c
720
721 $(OBJDIR)/json_diff.o: $(OBJDIR)/json_diff_.c $(OBJDIR)/json_diff.h $(SRCDIR)/config.h
722 $(XTCC) -o $(OBJDIR)/json_diff.o -c $(OBJDIR)/json_diff_.c
723
724 json_diff.h: $(OBJDIR)/headers
725 $(OBJDIR)/json_login_.c: $(SRCDIR)/json_login.c $(OBJDIR)/translate
726 $(TRANSLATE) $(SRCDIR)/json_login.c >$(OBJDIR)/json_login_.c
727
728 $(OBJDIR)/json_login.o: $(OBJDIR)/json_login_.c $(OBJDIR)/json_login.h $(SRCDIR)/config.h
729 $(XTCC) -o $(OBJDIR)/json_login.o -c $(OBJDIR)/json_login_.c
730
731 json_login.h: $(OBJDIR)/headers
732 $(OBJDIR)/json_query_.c: $(SRCDIR)/json_query.c $(OBJDIR)/translate
733 $(TRANSLATE) $(SRCDIR)/json_query.c >$(OBJDIR)/json_query_.c
734
735 $(OBJDIR)/json_query.o: $(OBJDIR)/json_query_.c $(OBJDIR)/json_query.h $(SRCDIR)/config.h
736 $(XTCC) -o $(OBJDIR)/json_query.o -c $(OBJDIR)/json_query_.c
737
738 json_query.h: $(OBJDIR)/headers
739 $(OBJDIR)/json_report_.c: $(SRCDIR)/json_report.c $(OBJDIR)/translate
740 $(TRANSLATE) $(SRCDIR)/json_report.c >$(OBJDIR)/json_report_.c
741
742 $(OBJDIR)/json_report.o: $(OBJDIR)/json_report_.c $(OBJDIR)/json_report.h $(SRCDIR)/config.h
743 $(XTCC) -o $(OBJDIR)/json_report.o -c $(OBJDIR)/json_report_.c
744
745 json_report.h: $(OBJDIR)/headers
746 $(OBJDIR)/json_tag_.c: $(SRCDIR)/json_tag.c $(OBJDIR)/translate
747 $(TRANSLATE) $(SRCDIR)/json_tag.c >$(OBJDIR)/json_tag_.c
748
749 $(OBJDIR)/json_tag.o: $(OBJDIR)/json_tag_.c $(OBJDIR)/json_tag.h $(SRCDIR)/config.h
750 $(XTCC) -o $(OBJDIR)/json_tag.o -c $(OBJDIR)/json_tag_.c
751
752 json_tag.h: $(OBJDIR)/headers
753 $(OBJDIR)/json_timeline_.c: $(SRCDIR)/json_timeline.c $(OBJDIR)/translate
754 $(TRANSLATE) $(SRCDIR)/json_timeline.c >$(OBJDIR)/json_timeline_.c
755
756 $(OBJDIR)/json_timeline.o: $(OBJDIR)/json_timeline_.c $(OBJDIR)/json_timeline.h $(SRCDIR)/config.h
757 $(XTCC) -o $(OBJDIR)/json_timeline.o -c $(OBJDIR)/json_timeline_.c
758
759 json_timeline.h: $(OBJDIR)/headers
760 $(OBJDIR)/json_user_.c: $(SRCDIR)/json_user.c $(OBJDIR)/translate
761 $(TRANSLATE) $(SRCDIR)/json_user.c >$(OBJDIR)/json_user_.c
762
763 $(OBJDIR)/json_user.o: $(OBJDIR)/json_user_.c $(OBJDIR)/json_user.h $(SRCDIR)/config.h
764 $(XTCC) -o $(OBJDIR)/json_user.o -c $(OBJDIR)/json_user_.c
765
766 json_user.h: $(OBJDIR)/headers
767 $(OBJDIR)/json_wiki_.c: $(SRCDIR)/json_wiki.c $(OBJDIR)/translate
768 $(TRANSLATE) $(SRCDIR)/json_wiki.c >$(OBJDIR)/json_wiki_.c
769
770 $(OBJDIR)/json_wiki.o: $(OBJDIR)/json_wiki_.c $(OBJDIR)/json_wiki.h $(SRCDIR)/config.h
771 $(XTCC) -o $(OBJDIR)/json_wiki.o -c $(OBJDIR)/json_wiki_.c
772
773 json_wiki.h: $(OBJDIR)/headers
774 $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate
775 $(TRANSLATE) $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c
776
777 $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h
778 $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c
@@ -970,14 +1080,18 @@
1080
1081 zip.h: $(OBJDIR)/headers
1082 $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c
1083 $(XTCC) -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_STAT3 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 -c $(SRCDIR)/sqlite3.c -o $(OBJDIR)/sqlite3.o
1084
1085 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR)/cson_amalgamation.c
1086 $(XTCC) -c $(SRCDIR)/cson_amalgamation.c -o $(OBJDIR)/cson_amalgamation.o -DCSON_FOSSIL_MODE
1087
1088 $(OBJDIR)/json.o $(OBJDIR)/json_artifact.o $(OBJDIR)/json_branch.o $(OBJDIR)/json_diff.o $(OBJDIR)/json_login.o $(OBJDIR)/json_query.o $(OBJDIR)/json_report.o $(OBJDIR)/json_tag.o $(OBJDIR)/json_timeline.o $(OBJDIR)/json_user.o $(OBJDIR)/json_wiki.o : $(SRCDIR)/json_detail.h
1089 $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h
1090 $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o
1091
1092 $(OBJDIR)/th.o: $(SRCDIR)/th.c
1093 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o
1094
1095 $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
1096 $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o
1097
1098
1099 DDED win/Makefile.mingw.mistachkin
--- a/win/Makefile.mingw.mistachkin
+++ b/win/Makefile.mingw.mistachkin
@@ -0,0 +1,212 @@
1
+markdown supportMARon windows using mingwD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
2
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
3
+#
4
+Zzlib-1.2.7
5
+ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
6
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
7
+TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
8
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
9
+RMARKDOWDjson_json_usermarkdTE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEa#KDOW :$(OBJDIR)/cgMARKDOWMwikiT==W With markdownMARKDOWNMARKDOWN=1
10
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
11
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagt=1
12
+
13
+#### The directory where the Lite package to TclTCL_SQLITE
14
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
15
+TTCL5
16
+Z -DCSON_FOSSIL_MODEartzlib-1.2.5upportMARKDOW With markdownMARKDOOpenSSL_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
17
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITEmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
18
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
19
+RMARKDOWDjson_json_usermarkdW ILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
20
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
21
+#
22
+Zzlib-1.2.7
23
+ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
24
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
25
+TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
26
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
27
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
28
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEa#KDOW :$(OBJDIR)/cgMARKDOWMARKDOWNMARKDOW With markdownMARKDmarkdowarkdownarkdown_htmld5arkdowmarkdown_htmld5ergmerge3arkdownmarkdown_htmlmd0eportMARKDOW With markdownMARKDOWNMARKDOWN=1
29
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
30
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
31
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
32
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
33
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
34
+RMARKDOWDjson_json_usermarkdovtajson_uikiwikitagtatagccWindhe SQLite package to TclTCL_SQLITE
35
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
36
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
37
+#
38
+Zzlib-1.2.7
39
+ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
40
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
41
+TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
42
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
43
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
44
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEa#KDOW :$(OBJDIR)/cgMARKDOWMARKDOWNMARKDOW With markdownMARKDmarkmarkdown supprtMARKDOW With markdownMARKDOWNMARKDOWN=1
45
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
46
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
47
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
48
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
49
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide t (statically linked) Provide the SQLite p -DBUILD_sqliteTCL_SQLITE=1 -DBU -DSTATIC_BUILDkdovtajson_userjson_wikiTCL=1DOWNMARKDOWN=1
50
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
51
+RMARKDOWDjsLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
52
+RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW Wi.hlogin.hportMARKDOW WithMARKDqutMARKDOW WiLIB += -municodearkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
53
+RMARKDOWtjson_rkdown supportMmaKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
54
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
55
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
56
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
57
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
58
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
59
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
60
+RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IUarkdown_.c:$(OBJDIR)/markdown.h $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_htmlarkdownmarkdownMARKDOWNMmarkdoarkdown.c >markdowrkdown.o: $(OBJDIR)/markdownrkdownmarkdowrkdowrkdowikitagtatagccWindows/Linux/DarQLITmarkdown suppmarkdownMARKDOWNMARKDOWN=1
61
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
62
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
63
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
64
+#
65
+Zzlib-1.2.7
66
+ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
67
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
68
+TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
69
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
70
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
71
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
72
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
73
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
74
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
75
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupoergenameathivotverifyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
76
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
77
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW th.o upportMARKmarkdowSRCDIR)/th.h TCL_SQLITE=1 -DBUILDjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
78
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupoergenameathivotverifyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
79
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
80
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
81
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
82
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
83
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
84
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
85
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
86
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
87
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
88
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
89
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
90
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
91
+RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IU,a@5Ir,Z@5JV,I@2wG,1::I@BLk,I@BNl,H@BP~,X@5LM,X@5Lw,e@5MW,L@2y0,1::I@2y0,3:.h 9@40l,O:/tkt_.c:$(OBJDIR)/tkt.h M@BmW,I@BmW,3:.h Z@5PG,K@BvW,G@BvW,3:.h H@B~0,A@Ce7,6:url.h H@C4~,H::$(OBJDIR)/user.hK@CEy,1::H@3Zk,1:hG@CJ0,M@5TF,I@CKG,H@CMV,k@5UG,K@CXG,1::H@CXG,3:.h e@5Vl,H@CdG,I@5Wj,M@Ck0,1::J@Ck0,3:.h X@5Xq,I@CsW,G:h $(SRCDIR)/th.hL@3sV,45l@5ZP,2:d5G@9x0,2:d5k@Cgk,7:md5.c >G@9qG,BT@9py,22A@A5E,wc@CB_,_UpXJ;markdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
92
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
93
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
94
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
95
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
96
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
97
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
98
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
99
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
100
+#
101
+Zzlib-1.2.7
102
+ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
103
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
104
+TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
105
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
106
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
107
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
108
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
109
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
110
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
111
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupoergenameathivotverifyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
112
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
113
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
114
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
115
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
116
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
117
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
118
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
119
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
120
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
121
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
122
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
123
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
124
+RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IU,a@5Ir,Z@5JV,I@2wG,1::I@BLk,I@BNl,H@BP~,X@5LM,X@5Lw,e@5MW,L@2y0,1::I@2y0,3:.h 9@40l,O:/tkt_.c:$(OBJDIR)/tkt.h M@BmW,I@BmW,3:.h Z@5PG,K@BvW,G@BvW,3:.h H@B~0,A@Ce7,6:url.h H@C4~,H::$(OBJDIR)/user.hK@CEy,1::H@3Zk,1:hG@CJ0,M@5TF,I@CKG,H@CMV,k@5UG,K@CXG,1::H@CXG,3:.h e@5Vl,H@CdG,I@5Wj,M@Ck0,1::J@Ck0,3:.h X@5Xq,I@CsW,G:h $(SRCDIR)/th.hL@3sV,45l@5ZP,2:d5G@9x0,2:d5k@Cgk,7:md5.c >G@9qG,BT@9py,22A@A5E,wc@CB_,_UpXJ; -DBUILD_sn_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
125
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
126
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
127
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
128
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
129
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
130
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
131
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
132
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
133
+RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IU,a@5Ir,Z@5JV,I@2wG,1::I@BLk,I@BNl,H@BP~,X@5LM,X@5Lw,e@5MW,L@2y0,1::I@2y0,3:.h 9@40l,O:/tkt_.c:$(OBJDIR)/tkt.h M@BmW,I@BmW,d5QLITE# Provide tyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
134
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
135
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
136
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
137
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
138
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
139
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
140
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
141
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
142
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
143
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
144
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
145
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
146
+RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IU,a@5Ir,Z@5JV,I@2wG,1::I@BLk,I@BNl,H@BP~,X@5LM,X@5Lw,e@5MW,L@2y0,1::I@2y0,3:.h 9@40l,O:/tkt_.c:$(OBJDIR)/tkt.h M@BmW,I@BmW,3:.h Z@5PG,K@BvW,G@BvW,3:.h H@B~0,A@Ce7,6:url.h H@C4~,H::$(OBJDIR)/user.hK@CEy,1::H@3Zk,1:hG@CJ0,M@5TF,I@CKG,H@CMV,k@5UG,K@CXG,1::H@CXG,3:.h e@5Vl,H@CdG,I@5Wj,M@Ck0,1::J@Ck0,3:.h X@5Xq,I@CsW,G:h $(SRCDIR)/th.hL@3sV,45l@5ZP,2:d5G@9x0,2:d5k@Cgk,7:md5.c >G@9qG,BT@9py,22A@A5E,wc@CB_,_UpXJ;markdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
147
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
148
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
149
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
150
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
151
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
152
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
153
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
154
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
155
+#
156
+Zzlib-1.2.7
157
+ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
158
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
159
+TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
160
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
161
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
162
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
163
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEad5_.c:md5.hsupportMARKDOW WitharkdownMARKDOWNMARKDOWN=1
164
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
165
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
166
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
167
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
168
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
169
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
170
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
171
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
172
+#
173
+Zzlib-1.2.7
174
+ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
175
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
176
+TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
177
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
178
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite packagemarkdown supportownMARKDOWNMARKDOMARKDOW the SQLite package to TclTCL_SQLITE
179
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
180
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQ With markdown$(OBJDIR)/tkt.h M@BmW,I@BmW,3MARKDOWMARKDOWNMARKDOW With markdownMARKDmarkdowarkdownarkdown_htmld5arkdowmarkdown_htmld5ergmerge3arkdownmarkdown_htmlmd5merg Provide the SQLimarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
181
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
182
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
183
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
184
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
185
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
186
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
187
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
188
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
189
+#
190
+Zzlib-1.2.7
191
+ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
192
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
193
+TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
194
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
195
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
196
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
197
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
198
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
199
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
200
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupoergenameathivotverifyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
201
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
202
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
203
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
204
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
205
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
206
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
207
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
208
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
209
+TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
210
+RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
211
+RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# ProthD_sqlite=1make -DOWDjson_json_usermarkdovc $(SRCDIR)/thQLITE=1 -DBUILD_th.o
212
+e=1make -DCth_langD_sqlite=1make -DlangDOWDjson_json_usermarkdovcite=1make -th_langQLITE=1 -DBUILD_th_langkdown supportMARKDOW With ma_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacp
--- a/win/Makefile.mingw.mistachkin
+++ b/win/Makefile.mingw.mistachkin
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/win/Makefile.mingw.mistachkin
+++ b/win/Makefile.mingw.mistachkin
@@ -0,0 +1,212 @@
1 markdown supportMARon windows using mingwD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
2 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
3 #
4 Zzlib-1.2.7
5 ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
6 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
7 TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
8 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
9 RMARKDOWDjson_json_usermarkdTE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEa#KDOW :$(OBJDIR)/cgMARKDOWMwikiT==W With markdownMARKDOWNMARKDOWN=1
10 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
11 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagt=1
12
13 #### The directory where the Lite package to TclTCL_SQLITE
14 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
15 TTCL5
16 Z -DCSON_FOSSIL_MODEartzlib-1.2.5upportMARKDOW With markdownMARKDOOpenSSL_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
17 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITEmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
18 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
19 RMARKDOWDjson_json_usermarkdW ILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
20 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
21 #
22 Zzlib-1.2.7
23 ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
24 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
25 TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
26 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
27 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
28 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEa#KDOW :$(OBJDIR)/cgMARKDOWMARKDOWNMARKDOW With markdownMARKDmarkdowarkdownarkdown_htmld5arkdowmarkdown_htmld5ergmerge3arkdownmarkdown_htmlmd0eportMARKDOW With markdownMARKDOWNMARKDOWN=1
29 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
30 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
31 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
32 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
33 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
34 RMARKDOWDjson_json_usermarkdovtajson_uikiwikitagtatagccWindhe SQLite package to TclTCL_SQLITE
35 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
36 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
37 #
38 Zzlib-1.2.7
39 ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
40 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
41 TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
42 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
43 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
44 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEa#KDOW :$(OBJDIR)/cgMARKDOWMARKDOWNMARKDOW With markdownMARKDmarkmarkdown supprtMARKDOW With markdownMARKDOWNMARKDOWN=1
45 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
46 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
47 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
48 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
49 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide t (statically linked) Provide the SQLite p -DBUILD_sqliteTCL_SQLITE=1 -DBU -DSTATIC_BUILDkdovtajson_userjson_wikiTCL=1DOWNMARKDOWN=1
50 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
51 RMARKDOWDjsLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
52 RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW Wi.hlogin.hportMARKDOW WithMARKDqutMARKDOW WiLIB += -municodearkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
53 RMARKDOWtjson_rkdown supportMmaKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
54 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
55 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
56 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
57 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
58 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
59 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
60 RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IUarkdown_.c:$(OBJDIR)/markdown.h $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_htmlarkdownmarkdownMARKDOWNMmarkdoarkdown.c >markdowrkdown.o: $(OBJDIR)/markdownrkdownmarkdowrkdowrkdowikitagtatagccWindows/Linux/DarQLITmarkdown suppmarkdownMARKDOWNMARKDOWN=1
61 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
62 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
63 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
64 #
65 Zzlib-1.2.7
66 ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
67 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
68 TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
69 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
70 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
71 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
72 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
73 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
74 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
75 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupoergenameathivotverifyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
76 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
77 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW th.o upportMARKmarkdowSRCDIR)/th.h TCL_SQLITE=1 -DBUILDjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
78 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupoergenameathivotverifyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
79 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
80 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
81 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
82 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
83 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
84 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
85 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
86 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
87 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
88 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
89 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
90 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
91 RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IU,a@5Ir,Z@5JV,I@2wG,1::I@BLk,I@BNl,H@BP~,X@5LM,X@5Lw,e@5MW,L@2y0,1::I@2y0,3:.h 9@40l,O:/tkt_.c:$(OBJDIR)/tkt.h M@BmW,I@BmW,3:.h Z@5PG,K@BvW,G@BvW,3:.h H@B~0,A@Ce7,6:url.h H@C4~,H::$(OBJDIR)/user.hK@CEy,1::H@3Zk,1:hG@CJ0,M@5TF,I@CKG,H@CMV,k@5UG,K@CXG,1::H@CXG,3:.h e@5Vl,H@CdG,I@5Wj,M@Ck0,1::J@Ck0,3:.h X@5Xq,I@CsW,G:h $(SRCDIR)/th.hL@3sV,45l@5ZP,2:d5G@9x0,2:d5k@Cgk,7:md5.c >G@9qG,BT@9py,22A@A5E,wc@CB_,_UpXJ;markdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
92 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
93 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
94 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
95 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
96 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
97 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
98 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
99 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
100 #
101 Zzlib-1.2.7
102 ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
103 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
104 TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
105 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
106 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
107 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
108 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
109 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
110 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
111 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupoergenameathivotverifyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
112 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
113 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
114 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
115 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
116 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
117 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
118 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
119 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
120 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
121 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
122 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
123 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
124 RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IU,a@5Ir,Z@5JV,I@2wG,1::I@BLk,I@BNl,H@BP~,X@5LM,X@5Lw,e@5MW,L@2y0,1::I@2y0,3:.h 9@40l,O:/tkt_.c:$(OBJDIR)/tkt.h M@BmW,I@BmW,3:.h Z@5PG,K@BvW,G@BvW,3:.h H@B~0,A@Ce7,6:url.h H@C4~,H::$(OBJDIR)/user.hK@CEy,1::H@3Zk,1:hG@CJ0,M@5TF,I@CKG,H@CMV,k@5UG,K@CXG,1::H@CXG,3:.h e@5Vl,H@CdG,I@5Wj,M@Ck0,1::J@Ck0,3:.h X@5Xq,I@CsW,G:h $(SRCDIR)/th.hL@3sV,45l@5ZP,2:d5G@9x0,2:d5k@Cgk,7:md5.c >G@9qG,BT@9py,22A@A5E,wc@CB_,_UpXJ; -DBUILD_sn_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
125 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
126 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
127 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
128 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
129 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
130 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
131 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
132 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
133 RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IU,a@5Ir,Z@5JV,I@2wG,1::I@BLk,I@BNl,H@BP~,X@5LM,X@5Lw,e@5MW,L@2y0,1::I@2y0,3:.h 9@40l,O:/tkt_.c:$(OBJDIR)/tkt.h M@BmW,I@BmW,d5QLITE# Provide tyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
134 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
135 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
136 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
137 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
138 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
139 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
140 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
141 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
142 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
143 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
144 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
145 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdattMARKDOW With markdownMARKDOWNMARKDOWN=1
146 RMARKDOWtjson_rkdown suppo own supportMARKDOkdownMARKDOWNMA:.h L_SQLITE=1 -DBUILD_sqlite=1make -markdown supportMARKbisect.h :$(OBJDIR)/branch.h:$(OBJDIR)/browse.hinux/DarQLITE# Promarkdown supportMARKDOW :$(OBJDIR)/cgi.hcheckin.hout.hwn supportMARKDOW Withrwn supportMARKDOW Wrk_.c:h:.h:db.h :.h_.c:.h :$(OBJDIR)/diff.h _.c: :$(OBJDIR)/doc.h json_usermarkdovtamn supportMARKDOW WrtMARKDOW With mark:$(OBJDIR)/glob.h:graph.h gpdatrsspdurlrsse markdo.hocketocket.h:rtifacmarkdown supma:h :$(OBJDIR)/info.hportMARKDOW WithMARKDOW With mark.hportMARKDOW WithMARKDOW Wit.hportMARKDOW WithMARKDOW With markdownMARKDOWNMARKDdiff.hrdir.hportMARKDOW WithMARKDfinfo.hlogin.hportMARKDOW WithMARKDquery.hreport.hagtag.htimeline.hportMARKDOW WithMARKuser.hportMARKDOW With marMARKDOW With markdowiki.h :$(OBJDIR)/login.h :main.h_.c:.h :$(OBJDIR)/md5.h.h:$(OBJDIR)/name.h popen_.c:$(OBJDIR)/popen.h :$(OBJDIR)/pqueue.h tagccWindows/LinumarkI@AW~,2:h J@AaG,1::H@Ac0,2:h X@5Dl,c@5EL,c@5F0,I@Atl,1::G@3TG,2:h Z@5GK,Z@5Gw,I@B5U,J::$(OBJDIR)/skins.h J@B8V,K@5IU,a@5Ir,Z@5JV,I@2wG,1::I@BLk,I@BNl,H@BP~,X@5LM,X@5Lw,e@5MW,L@2y0,1::I@2y0,3:.h 9@40l,O:/tkt_.c:$(OBJDIR)/tkt.h M@BmW,I@BmW,3:.h Z@5PG,K@BvW,G@BvW,3:.h H@B~0,A@Ce7,6:url.h H@C4~,H::$(OBJDIR)/user.hK@CEy,1::H@3Zk,1:hG@CJ0,M@5TF,I@CKG,H@CMV,k@5UG,K@CXG,1::H@CXG,3:.h e@5Vl,H@CdG,I@5Wj,M@Ck0,1::J@Ck0,3:.h X@5Xq,I@CsW,G:h $(SRCDIR)/th.hL@3sV,45l@5ZP,2:d5G@9x0,2:d5k@Cgk,7:md5.c >G@9qG,BT@9py,22A@A5E,wc@CB_,_UpXJ;markdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
147 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
148 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
149 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
150 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
151 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
152 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
153 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
154 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
155 #
156 Zzlib-1.2.7
157 ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
158 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
159 TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
160 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
161 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
162 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
163 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEad5_.c:md5.hsupportMARKDOW WitharkdownMARKDOWNMARKDOWN=1
164 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
165 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
166 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
167 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
168 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
169 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
170 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
171 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
172 #
173 Zzlib-1.2.7
174 ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
175 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
176 TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
177 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
178 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite packagemarkdown supportownMARKDOWNMARKDOMARKDOW the SQLite package to TclTCL_SQLITE
179 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
180 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQ With markdown$(OBJDIR)/tkt.h M@BmW,I@BmW,3MARKDOWMARKDOWNMARKDOW With markdownMARKDmarkdowarkdownarkdown_htmld5arkdowmarkdown_htmld5ergmerge3arkdownmarkdown_htmlmd5merg Provide the SQLimarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
181 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
182 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
183 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
184 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
185 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
186 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
187 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatL_SQLITE
188 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSILzlib-1.x.yzlib source directory.
189 #
190 Zzlib-1.2.7
191 ZLIBzlib-1.2.7son_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
192 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatHTTPSSSL
193 TSSarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
194 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
195 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
196 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
197 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
198 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
199 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
200 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupoergenameathivotverifyvfilenampaton_usermarkdovtajsomarkverifyrkdown supportMARKDOW wikirkdown supportMARKOW Wuserjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
201 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
202 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
203 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
204 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
205 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacportpdate_.c:pdupdatmarkdod5verifykdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
206 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
207 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# Provide the SQLite package to TclTCL_SQLITE
208 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifac$(PREFIX),pdatrsspdurlrsse SQLite package to TclTCL_SQLITE
209 TTCL_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacmarkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
210 RMARKDOWtjson_rkdown supportMARKDOW With markdownMARKDOWNMARKDOWN=1
211 RMARKDOWDjson_json_usermarkdovtajson_userjson_wikiwikitagtatagccWindows/Linux/DarQLITE# ProthD_sqlite=1make -DOWDjson_json_usermarkdovc $(SRCDIR)/thQLITE=1 -DBUILD_th.o
212 e=1make -DCth_langD_sqlite=1make -DlangDOWDjson_json_usermarkdovcite=1make -th_langQLITE=1 -DBUILD_th_langkdown supportMARKDOW With ma_SQLITE=1 -DBUILD_sqliteTCL_SQLITE=1 -DBUILD_sqlite=1make -DCSON_FOSSIL_MODEartifacp
+94 -3
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -36,13 +36,13 @@
3636
LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
3737
LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
3838
3939
SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 /DSQLITE_THREADSAFE=0 /DSQLITE_DEFAULT_FILE_FORMAT=4 /DSQLITE_ENABLE_STAT3 /Dlocaltime=fossil_localtime /DSQLITE_ENABLE_LOCKING_STYLE=0
4040
41
-SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
41
+SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_diff_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
4242
43
-OBJ = $(OX)\add$O $(OX)\allrepo$O $(OX)\attach$O $(OX)\bag$O $(OX)\bisect$O $(OX)\blob$O $(OX)\branch$O $(OX)\browse$O $(OX)\captcha$O $(OX)\cgi$O $(OX)\checkin$O $(OX)\checkout$O $(OX)\clearsign$O $(OX)\clone$O $(OX)\comformat$O $(OX)\configure$O $(OX)\content$O $(OX)\db$O $(OX)\delta$O $(OX)\deltacmd$O $(OX)\descendants$O $(OX)\diff$O $(OX)\diffcmd$O $(OX)\doc$O $(OX)\encode$O $(OX)\event$O $(OX)\export$O $(OX)\file$O $(OX)\finfo$O $(OX)\glob$O $(OX)\graph$O $(OX)\gzip$O $(OX)\http$O $(OX)\http_socket$O $(OX)\http_ssl$O $(OX)\http_transport$O $(OX)\import$O $(OX)\info$O $(OX)\leaf$O $(OX)\login$O $(OX)\main$O $(OX)\manifest$O $(OX)\md5$O $(OX)\merge$O $(OX)\merge3$O $(OX)\name$O $(OX)\path$O $(OX)\pivot$O $(OX)\popen$O $(OX)\pqueue$O $(OX)\printf$O $(OX)\rebuild$O $(OX)\report$O $(OX)\rss$O $(OX)\schema$O $(OX)\search$O $(OX)\setup$O $(OX)\sha1$O $(OX)\shun$O $(OX)\skins$O $(OX)\sqlcmd$O $(OX)\stash$O $(OX)\stat$O $(OX)\style$O $(OX)\sync$O $(OX)\tag$O $(OX)\tar$O $(OX)\th_main$O $(OX)\timeline$O $(OX)\tkt$O $(OX)\tktsetup$O $(OX)\undo$O $(OX)\update$O $(OX)\url$O $(OX)\user$O $(OX)\verify$O $(OX)\vfile$O $(OX)\wiki$O $(OX)\wikiformat$O $(OX)\winhttp$O $(OX)\xfer$O $(OX)\zip$O $(OX)\shell$O $(OX)\sqlite3$O $(OX)\th$O $(OX)\th_lang$O
43
+OBJ = $(OX)\add$O $(OX)\allrepo$O $(OX)\attach$O $(OX)\bag$O $(OX)\bisect$O $(OX)\blob$O $(OX)\branch$O $(OX)\browse$O $(OX)\captcha$O $(OX)\cgi$O $(OX)\checkin$O $(OX)\checkout$O $(OX)\clearsign$O $(OX)\clone$O $(OX)\comformat$O $(OX)\configure$O $(OX)\content$O $(OX)\db$O $(OX)\delta$O $(OX)\deltacmd$O $(OX)\descendants$O $(OX)\diff$O $(OX)\diffcmd$O $(OX)\doc$O $(OX)\encode$O $(OX)\event$O $(OX)\export$O $(OX)\file$O $(OX)\finfo$O $(OX)\glob$O $(OX)\graph$O $(OX)\gzip$O $(OX)\http$O $(OX)\http_socket$O $(OX)\http_ssl$O $(OX)\http_transport$O $(OX)\import$O $(OX)\info$O $(OX)\json$O $(OX)\json_artifact$O $(OX)\json_branch$O $(OX)\json_diff$O $(OX)\json_login$O $(OX)\json_query$O $(OX)\json_report$O $(OX)\json_tag$O $(OX)\json_timeline$O $(OX)\json_user$O $(OX)\json_wiki$O $(OX)\leaf$O $(OX)\login$O $(OX)\main$O $(OX)\manifest$O $(OX)\md5$O $(OX)\merge$O $(OX)\merge3$O $(OX)\name$O $(OX)\path$O $(OX)\pivot$O $(OX)\popen$O $(OX)\pqueue$O $(OX)\printf$O $(OX)\rebuild$O $(OX)\report$O $(OX)\rss$O $(OX)\schema$O $(OX)\search$O $(OX)\setup$O $(OX)\sha1$O $(OX)\shun$O $(OX)\skins$O $(OX)\sqlcmd$O $(OX)\stash$O $(OX)\stat$O $(OX)\style$O $(OX)\sync$O $(OX)\tag$O $(OX)\tar$O $(OX)\th_main$O $(OX)\timeline$O $(OX)\tkt$O $(OX)\tktsetup$O $(OX)\undo$O $(OX)\update$O $(OX)\url$O $(OX)\user$O $(OX)\verify$O $(OX)\vfile$O $(OX)\wiki$O $(OX)\wikiformat$O $(OX)\winhttp$O $(OX)\xfer$O $(OX)\zip$O $(OX)\shell$O $(OX)\sqlite3$O $(OX)\th$O $(OX)\th_lang$O
4444
4545
4646
APPNAME = $(OX)\fossil$(E)
4747
4848
all: $(OX) $(APPNAME)
@@ -88,10 +88,21 @@
8888
echo $(OX)\http_socket.obj >> $@
8989
echo $(OX)\http_ssl.obj >> $@
9090
echo $(OX)\http_transport.obj >> $@
9191
echo $(OX)\import.obj >> $@
9292
echo $(OX)\info.obj >> $@
93
+ echo $(OX)\json.obj >> $@
94
+ echo $(OX)\json_artifact.obj >> $@
95
+ echo $(OX)\json_branch.obj >> $@
96
+ echo $(OX)\json_diff.obj >> $@
97
+ echo $(OX)\json_login.obj >> $@
98
+ echo $(OX)\json_query.obj >> $@
99
+ echo $(OX)\json_report.obj >> $@
100
+ echo $(OX)\json_tag.obj >> $@
101
+ echo $(OX)\json_timeline.obj >> $@
102
+ echo $(OX)\json_user.obj >> $@
103
+ echo $(OX)\json_wiki.obj >> $@
93104
echo $(OX)\leaf.obj >> $@
94105
echo $(OX)\login.obj >> $@
95106
echo $(OX)\main.obj >> $@
96107
echo $(OX)\manifest.obj >> $@
97108
echo $(OX)\md5.obj >> $@
@@ -170,10 +181,12 @@
170181
$(OX)\th_lang$O : $(SRCDIR)\th_lang.c
171182
$(TCC) /Fo$@ -c $**
172183
173184
VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
174185
$** > $@
186
+$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
187
+ cp $(SRCDIR)\cson_amalgamation.h $@
175188
176189
page_index.h: mkindex$E $(SRC)
177190
$** > $@
178191
179192
clean:
@@ -182,10 +195,22 @@
182195
-del headers linkopts
183196
184197
realclean:
185198
-del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
186199
200
+$(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
201
+$(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
202
+$(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
203
+$(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
204
+$(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
205
+$(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
206
+$(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
207
+$(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
208
+$(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
209
+$(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
210
+$(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
211
+
187212
188213
$(OX)\add$O : add_.c add.h
189214
$(TCC) /Fo$@ -c add_.c
190215
191216
add_.c : $(SRCDIR)\add.c
@@ -410,10 +435,76 @@
410435
$(OX)\info$O : info_.c info.h
411436
$(TCC) /Fo$@ -c info_.c
412437
413438
info_.c : $(SRCDIR)\info.c
414439
translate$E $** > $@
440
+
441
+$(OX)\json$O : json_.c json.h
442
+ $(TCC) /Fo$@ -c json_.c
443
+
444
+json_.c : $(SRCDIR)\json.c
445
+ translate$E $** > $@
446
+
447
+$(OX)\json_artifact$O : json_artifact_.c json_artifact.h
448
+ $(TCC) /Fo$@ -c json_artifact_.c
449
+
450
+json_artifact_.c : $(SRCDIR)\json_artifact.c
451
+ translate$E $** > $@
452
+
453
+$(OX)\json_branch$O : json_branch_.c json_branch.h
454
+ $(TCC) /Fo$@ -c json_branch_.c
455
+
456
+json_branch_.c : $(SRCDIR)\json_branch.c
457
+ translate$E $** > $@
458
+
459
+$(OX)\json_diff$O : json_diff_.c json_diff.h
460
+ $(TCC) /Fo$@ -c json_diff_.c
461
+
462
+json_diff_.c : $(SRCDIR)\json_diff.c
463
+ translate$E $** > $@
464
+
465
+$(OX)\json_login$O : json_login_.c json_login.h
466
+ $(TCC) /Fo$@ -c json_login_.c
467
+
468
+json_login_.c : $(SRCDIR)\json_login.c
469
+ translate$E $** > $@
470
+
471
+$(OX)\json_query$O : json_query_.c json_query.h
472
+ $(TCC) /Fo$@ -c json_query_.c
473
+
474
+json_query_.c : $(SRCDIR)\json_query.c
475
+ translate$E $** > $@
476
+
477
+$(OX)\json_report$O : json_report_.c json_report.h
478
+ $(TCC) /Fo$@ -c json_report_.c
479
+
480
+json_report_.c : $(SRCDIR)\json_report.c
481
+ translate$E $** > $@
482
+
483
+$(OX)\json_tag$O : json_tag_.c json_tag.h
484
+ $(TCC) /Fo$@ -c json_tag_.c
485
+
486
+json_tag_.c : $(SRCDIR)\json_tag.c
487
+ translate$E $** > $@
488
+
489
+$(OX)\json_timeline$O : json_timeline_.c json_timeline.h
490
+ $(TCC) /Fo$@ -c json_timeline_.c
491
+
492
+json_timeline_.c : $(SRCDIR)\json_timeline.c
493
+ translate$E $** > $@
494
+
495
+$(OX)\json_user$O : json_user_.c json_user.h
496
+ $(TCC) /Fo$@ -c json_user_.c
497
+
498
+json_user_.c : $(SRCDIR)\json_user.c
499
+ translate$E $** > $@
500
+
501
+$(OX)\json_wiki$O : json_wiki_.c json_wiki.h
502
+ $(TCC) /Fo$@ -c json_wiki_.c
503
+
504
+json_wiki_.c : $(SRCDIR)\json_wiki.c
505
+ translate$E $** > $@
415506
416507
$(OX)\leaf$O : leaf_.c leaf.h
417508
$(TCC) /Fo$@ -c leaf_.c
418509
419510
leaf_.c : $(SRCDIR)\leaf.c
@@ -676,7 +767,7 @@
676767
677768
zip_.c : $(SRCDIR)\zip.c
678769
translate$E $** > $@
679770
680771
headers: makeheaders$E page_index.h VERSION.h
681
- makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h
772
+ makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_diff_.c:json_diff.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
682773
@copy /Y nul: headers
683774
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -36,13 +36,13 @@
36 LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
37 LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
38
39 SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 /DSQLITE_THREADSAFE=0 /DSQLITE_DEFAULT_FILE_FORMAT=4 /DSQLITE_ENABLE_STAT3 /Dlocaltime=fossil_localtime /DSQLITE_ENABLE_LOCKING_STYLE=0
40
41 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
42
43 OBJ = $(OX)\add$O $(OX)\allrepo$O $(OX)\attach$O $(OX)\bag$O $(OX)\bisect$O $(OX)\blob$O $(OX)\branch$O $(OX)\browse$O $(OX)\captcha$O $(OX)\cgi$O $(OX)\checkin$O $(OX)\checkout$O $(OX)\clearsign$O $(OX)\clone$O $(OX)\comformat$O $(OX)\configure$O $(OX)\content$O $(OX)\db$O $(OX)\delta$O $(OX)\deltacmd$O $(OX)\descendants$O $(OX)\diff$O $(OX)\diffcmd$O $(OX)\doc$O $(OX)\encode$O $(OX)\event$O $(OX)\export$O $(OX)\file$O $(OX)\finfo$O $(OX)\glob$O $(OX)\graph$O $(OX)\gzip$O $(OX)\http$O $(OX)\http_socket$O $(OX)\http_ssl$O $(OX)\http_transport$O $(OX)\import$O $(OX)\info$O $(OX)\leaf$O $(OX)\login$O $(OX)\main$O $(OX)\manifest$O $(OX)\md5$O $(OX)\merge$O $(OX)\merge3$O $(OX)\name$O $(OX)\path$O $(OX)\pivot$O $(OX)\popen$O $(OX)\pqueue$O $(OX)\printf$O $(OX)\rebuild$O $(OX)\report$O $(OX)\rss$O $(OX)\schema$O $(OX)\search$O $(OX)\setup$O $(OX)\sha1$O $(OX)\shun$O $(OX)\skins$O $(OX)\sqlcmd$O $(OX)\stash$O $(OX)\stat$O $(OX)\style$O $(OX)\sync$O $(OX)\tag$O $(OX)\tar$O $(OX)\th_main$O $(OX)\timeline$O $(OX)\tkt$O $(OX)\tktsetup$O $(OX)\undo$O $(OX)\update$O $(OX)\url$O $(OX)\user$O $(OX)\verify$O $(OX)\vfile$O $(OX)\wiki$O $(OX)\wikiformat$O $(OX)\winhttp$O $(OX)\xfer$O $(OX)\zip$O $(OX)\shell$O $(OX)\sqlite3$O $(OX)\th$O $(OX)\th_lang$O
44
45
46 APPNAME = $(OX)\fossil$(E)
47
48 all: $(OX) $(APPNAME)
@@ -88,10 +88,21 @@
88 echo $(OX)\http_socket.obj >> $@
89 echo $(OX)\http_ssl.obj >> $@
90 echo $(OX)\http_transport.obj >> $@
91 echo $(OX)\import.obj >> $@
92 echo $(OX)\info.obj >> $@
 
 
 
 
 
 
 
 
 
 
 
93 echo $(OX)\leaf.obj >> $@
94 echo $(OX)\login.obj >> $@
95 echo $(OX)\main.obj >> $@
96 echo $(OX)\manifest.obj >> $@
97 echo $(OX)\md5.obj >> $@
@@ -170,10 +181,12 @@
170 $(OX)\th_lang$O : $(SRCDIR)\th_lang.c
171 $(TCC) /Fo$@ -c $**
172
173 VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
174 $** > $@
 
 
175
176 page_index.h: mkindex$E $(SRC)
177 $** > $@
178
179 clean:
@@ -182,10 +195,22 @@
182 -del headers linkopts
183
184 realclean:
185 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
186
 
 
 
 
 
 
 
 
 
 
 
 
187
188 $(OX)\add$O : add_.c add.h
189 $(TCC) /Fo$@ -c add_.c
190
191 add_.c : $(SRCDIR)\add.c
@@ -410,10 +435,76 @@
410 $(OX)\info$O : info_.c info.h
411 $(TCC) /Fo$@ -c info_.c
412
413 info_.c : $(SRCDIR)\info.c
414 translate$E $** > $@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
416 $(OX)\leaf$O : leaf_.c leaf.h
417 $(TCC) /Fo$@ -c leaf_.c
418
419 leaf_.c : $(SRCDIR)\leaf.c
@@ -676,7 +767,7 @@
676
677 zip_.c : $(SRCDIR)\zip.c
678 translate$E $** > $@
679
680 headers: makeheaders$E page_index.h VERSION.h
681 makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h
682 @copy /Y nul: headers
683
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -36,13 +36,13 @@
36 LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB)
37 LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)
38
39 SQLITE_OPTIONS = /DSQLITE_OMIT_LOAD_EXTENSION=1 /DSQLITE_THREADSAFE=0 /DSQLITE_DEFAULT_FILE_FORMAT=4 /DSQLITE_ENABLE_STAT3 /Dlocaltime=fossil_localtime /DSQLITE_ENABLE_LOCKING_STYLE=0
40
41 SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_diff_.c json_login_.c json_query_.c json_report_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c
42
43 OBJ = $(OX)\add$O $(OX)\allrepo$O $(OX)\attach$O $(OX)\bag$O $(OX)\bisect$O $(OX)\blob$O $(OX)\branch$O $(OX)\browse$O $(OX)\captcha$O $(OX)\cgi$O $(OX)\checkin$O $(OX)\checkout$O $(OX)\clearsign$O $(OX)\clone$O $(OX)\comformat$O $(OX)\configure$O $(OX)\content$O $(OX)\db$O $(OX)\delta$O $(OX)\deltacmd$O $(OX)\descendants$O $(OX)\diff$O $(OX)\diffcmd$O $(OX)\doc$O $(OX)\encode$O $(OX)\event$O $(OX)\export$O $(OX)\file$O $(OX)\finfo$O $(OX)\glob$O $(OX)\graph$O $(OX)\gzip$O $(OX)\http$O $(OX)\http_socket$O $(OX)\http_ssl$O $(OX)\http_transport$O $(OX)\import$O $(OX)\info$O $(OX)\json$O $(OX)\json_artifact$O $(OX)\json_branch$O $(OX)\json_diff$O $(OX)\json_login$O $(OX)\json_query$O $(OX)\json_report$O $(OX)\json_tag$O $(OX)\json_timeline$O $(OX)\json_user$O $(OX)\json_wiki$O $(OX)\leaf$O $(OX)\login$O $(OX)\main$O $(OX)\manifest$O $(OX)\md5$O $(OX)\merge$O $(OX)\merge3$O $(OX)\name$O $(OX)\path$O $(OX)\pivot$O $(OX)\popen$O $(OX)\pqueue$O $(OX)\printf$O $(OX)\rebuild$O $(OX)\report$O $(OX)\rss$O $(OX)\schema$O $(OX)\search$O $(OX)\setup$O $(OX)\sha1$O $(OX)\shun$O $(OX)\skins$O $(OX)\sqlcmd$O $(OX)\stash$O $(OX)\stat$O $(OX)\style$O $(OX)\sync$O $(OX)\tag$O $(OX)\tar$O $(OX)\th_main$O $(OX)\timeline$O $(OX)\tkt$O $(OX)\tktsetup$O $(OX)\undo$O $(OX)\update$O $(OX)\url$O $(OX)\user$O $(OX)\verify$O $(OX)\vfile$O $(OX)\wiki$O $(OX)\wikiformat$O $(OX)\winhttp$O $(OX)\xfer$O $(OX)\zip$O $(OX)\shell$O $(OX)\sqlite3$O $(OX)\th$O $(OX)\th_lang$O
44
45
46 APPNAME = $(OX)\fossil$(E)
47
48 all: $(OX) $(APPNAME)
@@ -88,10 +88,21 @@
88 echo $(OX)\http_socket.obj >> $@
89 echo $(OX)\http_ssl.obj >> $@
90 echo $(OX)\http_transport.obj >> $@
91 echo $(OX)\import.obj >> $@
92 echo $(OX)\info.obj >> $@
93 echo $(OX)\json.obj >> $@
94 echo $(OX)\json_artifact.obj >> $@
95 echo $(OX)\json_branch.obj >> $@
96 echo $(OX)\json_diff.obj >> $@
97 echo $(OX)\json_login.obj >> $@
98 echo $(OX)\json_query.obj >> $@
99 echo $(OX)\json_report.obj >> $@
100 echo $(OX)\json_tag.obj >> $@
101 echo $(OX)\json_timeline.obj >> $@
102 echo $(OX)\json_user.obj >> $@
103 echo $(OX)\json_wiki.obj >> $@
104 echo $(OX)\leaf.obj >> $@
105 echo $(OX)\login.obj >> $@
106 echo $(OX)\main.obj >> $@
107 echo $(OX)\manifest.obj >> $@
108 echo $(OX)\md5.obj >> $@
@@ -170,10 +181,12 @@
181 $(OX)\th_lang$O : $(SRCDIR)\th_lang.c
182 $(TCC) /Fo$@ -c $**
183
184 VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION
185 $** > $@
186 $(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h
187 cp $(SRCDIR)\cson_amalgamation.h $@
188
189 page_index.h: mkindex$E $(SRC)
190 $** > $@
191
192 clean:
@@ -182,10 +195,22 @@
195 -del headers linkopts
196
197 realclean:
198 -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E
199
200 $(OBJDIR)\json$O : $(SRCDIR)\json_detail.h
201 $(OBJDIR)\json_artifact$O : $(SRCDIR)\json_detail.h
202 $(OBJDIR)\json_branch$O : $(SRCDIR)\json_detail.h
203 $(OBJDIR)\json_diff$O : $(SRCDIR)\json_detail.h
204 $(OBJDIR)\json_login$O : $(SRCDIR)\json_detail.h
205 $(OBJDIR)\json_query$O : $(SRCDIR)\json_detail.h
206 $(OBJDIR)\json_report$O : $(SRCDIR)\json_detail.h
207 $(OBJDIR)\json_tag$O : $(SRCDIR)\json_detail.h
208 $(OBJDIR)\json_timeline$O : $(SRCDIR)\json_detail.h
209 $(OBJDIR)\json_user$O : $(SRCDIR)\json_detail.h
210 $(OBJDIR)\json_wiki$O : $(SRCDIR)\json_detail.h
211
212
213 $(OX)\add$O : add_.c add.h
214 $(TCC) /Fo$@ -c add_.c
215
216 add_.c : $(SRCDIR)\add.c
@@ -410,10 +435,76 @@
435 $(OX)\info$O : info_.c info.h
436 $(TCC) /Fo$@ -c info_.c
437
438 info_.c : $(SRCDIR)\info.c
439 translate$E $** > $@
440
441 $(OX)\json$O : json_.c json.h
442 $(TCC) /Fo$@ -c json_.c
443
444 json_.c : $(SRCDIR)\json.c
445 translate$E $** > $@
446
447 $(OX)\json_artifact$O : json_artifact_.c json_artifact.h
448 $(TCC) /Fo$@ -c json_artifact_.c
449
450 json_artifact_.c : $(SRCDIR)\json_artifact.c
451 translate$E $** > $@
452
453 $(OX)\json_branch$O : json_branch_.c json_branch.h
454 $(TCC) /Fo$@ -c json_branch_.c
455
456 json_branch_.c : $(SRCDIR)\json_branch.c
457 translate$E $** > $@
458
459 $(OX)\json_diff$O : json_diff_.c json_diff.h
460 $(TCC) /Fo$@ -c json_diff_.c
461
462 json_diff_.c : $(SRCDIR)\json_diff.c
463 translate$E $** > $@
464
465 $(OX)\json_login$O : json_login_.c json_login.h
466 $(TCC) /Fo$@ -c json_login_.c
467
468 json_login_.c : $(SRCDIR)\json_login.c
469 translate$E $** > $@
470
471 $(OX)\json_query$O : json_query_.c json_query.h
472 $(TCC) /Fo$@ -c json_query_.c
473
474 json_query_.c : $(SRCDIR)\json_query.c
475 translate$E $** > $@
476
477 $(OX)\json_report$O : json_report_.c json_report.h
478 $(TCC) /Fo$@ -c json_report_.c
479
480 json_report_.c : $(SRCDIR)\json_report.c
481 translate$E $** > $@
482
483 $(OX)\json_tag$O : json_tag_.c json_tag.h
484 $(TCC) /Fo$@ -c json_tag_.c
485
486 json_tag_.c : $(SRCDIR)\json_tag.c
487 translate$E $** > $@
488
489 $(OX)\json_timeline$O : json_timeline_.c json_timeline.h
490 $(TCC) /Fo$@ -c json_timeline_.c
491
492 json_timeline_.c : $(SRCDIR)\json_timeline.c
493 translate$E $** > $@
494
495 $(OX)\json_user$O : json_user_.c json_user.h
496 $(TCC) /Fo$@ -c json_user_.c
497
498 json_user_.c : $(SRCDIR)\json_user.c
499 translate$E $** > $@
500
501 $(OX)\json_wiki$O : json_wiki_.c json_wiki.h
502 $(TCC) /Fo$@ -c json_wiki_.c
503
504 json_wiki_.c : $(SRCDIR)\json_wiki.c
505 translate$E $** > $@
506
507 $(OX)\leaf$O : leaf_.c leaf.h
508 $(TCC) /Fo$@ -c leaf_.c
509
510 leaf_.c : $(SRCDIR)\leaf.c
@@ -676,7 +767,7 @@
767
768 zip_.c : $(SRCDIR)\zip.c
769 translate$E $** > $@
770
771 headers: makeheaders$E page_index.h VERSION.h
772 makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_diff_.c:json_diff.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h path_.c:path.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
773 @copy /Y nul: headers
774
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,7 +1,44 @@
11
<title>Change Log</title>
22
3
+<h2>Changes For Version 1.20 (2011-10-21)</h2>
4
+ * Added side-by-side diffs in HTML interface. [0bde74ea1e]
5
+ * Added support for symlinks. (Controlled by "allow-symlinks" setting,
6
+ off by default). [e4f1c1fe95]
7
+ * Fixed CLI annotate to show the proper file version in case there
8
+ are multiple equal versions in history. [e161670939]
9
+ * Timeline now shows tag changes (requires rebuild).[87540ed6e6]
10
+ * Fixed annotate to show "more relevant" versions of lines in
11
+ some cases. [e161670939]
12
+ * New command: ticket history. [98a855c508]
13
+ * Disabled SSLv2 in HTTPS client.[ea1d369d23]
14
+ * Fixed constant prompting regarding previously-saved SSL
15
+ certificates. [636804745b]
16
+ * Other SSL improvements.
17
+ * Added -R REPOFILE support to several more CLI commands. [e080560378]
18
+ * Generated tarballs now have constant timestamps, so they are
19
+ always identical for any given checkin. [e080560378]
20
+ * A number of minor HTML-related tweaks and fixes.
21
+ * Added --args FILENAME global CLI argument to import arbitrary
22
+ CLI arguments from a file (e.g. long file lists). [e080560378]
23
+ * Fixed significant memory leak in annotation of files with long
24
+ histories.[9929bab702]
25
+ * Added warnings when a merge operation overwrites local copies
26
+ (UNDO is available, but previously this condition normally went
27
+ silently unnoticed). [39f979b08c]
28
+ * Improved performance when adding many files. [a369dc7721]
29
+ * Improve merges which contain many file renames. [0b93b0f958]
30
+ * Added protection against timing attacks. [d4a341b49d]
31
+ * Firefox now remembers filled fields when returning to forms. [3fac77d7b0]
32
+ * Added the --stats option to the rebuild command. [f25e5e53c4]
33
+ * RSS feed now passes validation. [ce354d0a9f]
34
+ * Show overridden user when entering commit comment. [ce354d0a9f]
35
+ * Made rebuilding from web interface silent. [ce354d0a9f]
36
+ * Now works on MSVC with repos >2GB. [6092935ff2]
37
+ * A number of code cleanups to resolve warnings from various compilers.
38
+ * Update the built-in SQLite to version 3.7.9 beta.
39
+
340
<h2>Changes For Version 1.19 (2011-09-02)</h2>
441
542
* Added a ./configure script based on autosetup.
643
* Added the "[/help/winsrv | fossil winsrv]" command
744
for creating a Fossil service on windows systems.
845
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,7 +1,44 @@
1 <title>Change Log</title>
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3 <h2>Changes For Version 1.19 (2011-09-02)</h2>
4
5 * Added a ./configure script based on autosetup.
6 * Added the "[/help/winsrv | fossil winsrv]" command
7 for creating a Fossil service on windows systems.
8
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,7 +1,44 @@
1 <title>Change Log</title>
2
3 <h2>Changes For Version 1.20 (2011-10-21)</h2>
4 * Added side-by-side diffs in HTML interface. [0bde74ea1e]
5 * Added support for symlinks. (Controlled by "allow-symlinks" setting,
6 off by default). [e4f1c1fe95]
7 * Fixed CLI annotate to show the proper file version in case there
8 are multiple equal versions in history. [e161670939]
9 * Timeline now shows tag changes (requires rebuild).[87540ed6e6]
10 * Fixed annotate to show "more relevant" versions of lines in
11 some cases. [e161670939]
12 * New command: ticket history. [98a855c508]
13 * Disabled SSLv2 in HTTPS client.[ea1d369d23]
14 * Fixed constant prompting regarding previously-saved SSL
15 certificates. [636804745b]
16 * Other SSL improvements.
17 * Added -R REPOFILE support to several more CLI commands. [e080560378]
18 * Generated tarballs now have constant timestamps, so they are
19 always identical for any given checkin. [e080560378]
20 * A number of minor HTML-related tweaks and fixes.
21 * Added --args FILENAME global CLI argument to import arbitrary
22 CLI arguments from a file (e.g. long file lists). [e080560378]
23 * Fixed significant memory leak in annotation of files with long
24 histories.[9929bab702]
25 * Added warnings when a merge operation overwrites local copies
26 (UNDO is available, but previously this condition normally went
27 silently unnoticed). [39f979b08c]
28 * Improved performance when adding many files. [a369dc7721]
29 * Improve merges which contain many file renames. [0b93b0f958]
30 * Added protection against timing attacks. [d4a341b49d]
31 * Firefox now remembers filled fields when returning to forms. [3fac77d7b0]
32 * Added the --stats option to the rebuild command. [f25e5e53c4]
33 * RSS feed now passes validation. [ce354d0a9f]
34 * Show overridden user when entering commit comment. [ce354d0a9f]
35 * Made rebuilding from web interface silent. [ce354d0a9f]
36 * Now works on MSVC with repos >2GB. [6092935ff2]
37 * A number of code cleanups to resolve warnings from various compilers.
38 * Update the built-in SQLite to version 3.7.9 beta.
39
40 <h2>Changes For Version 1.19 (2011-09-02)</h2>
41
42 * Added a ./configure script based on autosetup.
43 * Added the "[/help/winsrv | fossil winsrv]" command
44 for creating a Fossil service on windows systems.
45
--- www/checkin_names.wiki
+++ www/checkin_names.wiki
@@ -126,23 +126,24 @@
126126
* <i>YYYY-MM-DD HH:MM</i>
127127
* <i>YYYY-MM-DD HH:MM:SS</i>
128128
129129
The space between the day and the year can optionally be
130130
replaced by an uppercase <b>T</b> and the entire timestamp can
131
-optionally be followed by "<b>utc</b>".
131
+optionally be followed by "<b>z</b>" or "<b>Z</b>".
132132
133133
In its default configuration, Fossil interprets and displays all dates
134134
in Universal Coordinated Time (UTC). This tends to work the best for
135135
distributed projects where participants are scattered around the globe.
136136
But there is an option on the Admin/Timeline page of the web-interface to
137
-switch to local time. The "<b>utc</b>" suffix on an timestamp check-in
137
+switch to local time. The "<b>Z</b>" suffix on an timestamp check-in
138138
name is meaningless if Fossil is in the default mode of using UTC for
139139
everything, but if Fossil has been switched to localtime mode, then the
140
-"<b>utc</b>" suffix means to interpret that particular timestamp using
140
+"<b>Z</b>" suffix means to interpret that particular timestamp using
141141
UTC instead localtime.
142142
143
-As an example, consider the homepage for the Fossil website itself:
143
+For an example of how timestamps are useful,
144
+consider the homepage for the Fossil website itself:
144145
145146
<blockquote>
146147
http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki
147148
</blockquote>
148149
149150
--- www/checkin_names.wiki
+++ www/checkin_names.wiki
@@ -126,23 +126,24 @@
126 * <i>YYYY-MM-DD HH:MM</i>
127 * <i>YYYY-MM-DD HH:MM:SS</i>
128
129 The space between the day and the year can optionally be
130 replaced by an uppercase <b>T</b> and the entire timestamp can
131 optionally be followed by "<b>utc</b>".
132
133 In its default configuration, Fossil interprets and displays all dates
134 in Universal Coordinated Time (UTC). This tends to work the best for
135 distributed projects where participants are scattered around the globe.
136 But there is an option on the Admin/Timeline page of the web-interface to
137 switch to local time. The "<b>utc</b>" suffix on an timestamp check-in
138 name is meaningless if Fossil is in the default mode of using UTC for
139 everything, but if Fossil has been switched to localtime mode, then the
140 "<b>utc</b>" suffix means to interpret that particular timestamp using
141 UTC instead localtime.
142
143 As an example, consider the homepage for the Fossil website itself:
 
144
145 <blockquote>
146 http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki
147 </blockquote>
148
149
--- www/checkin_names.wiki
+++ www/checkin_names.wiki
@@ -126,23 +126,24 @@
126 * <i>YYYY-MM-DD HH:MM</i>
127 * <i>YYYY-MM-DD HH:MM:SS</i>
128
129 The space between the day and the year can optionally be
130 replaced by an uppercase <b>T</b> and the entire timestamp can
131 optionally be followed by "<b>z</b>" or "<b>Z</b>".
132
133 In its default configuration, Fossil interprets and displays all dates
134 in Universal Coordinated Time (UTC). This tends to work the best for
135 distributed projects where participants are scattered around the globe.
136 But there is an option on the Admin/Timeline page of the web-interface to
137 switch to local time. The "<b>Z</b>" suffix on an timestamp check-in
138 name is meaningless if Fossil is in the default mode of using UTC for
139 everything, but if Fossil has been switched to localtime mode, then the
140 "<b>Z</b>" suffix means to interpret that particular timestamp using
141 UTC instead localtime.
142
143 For an example of how timestamps are useful,
144 consider the homepage for the Fossil website itself:
145
146 <blockquote>
147 http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki
148 </blockquote>
149
150

Keyboard Shortcuts

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