Fossil SCM
Merge with trunk
Commit
c30eaa88625c9585f6ab53ba928c2c3039918d98
Parent
1349e5ed20bd6ea…
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 @@ | ||
| 9 | 9 | # the following to point from the build directory to the src/ folder. |
| 10 | 10 | # |
| 11 | 11 | SRCDIR = @srcdir@/src |
| 12 | 12 | |
| 13 | 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. | |
| 14 | 18 | # |
| 15 | 19 | # |
| 16 | -OBJDIR = ./bld | |
| 20 | +OBJDIR = bld | |
| 17 | 21 | |
| 18 | 22 | #### C Compiler and options for use in building executables that |
| 19 | 23 | # will run on the platform that is doing the build. This is used |
| 20 | 24 | # to compile code-generator programs as part of the build process. |
| 21 | 25 | # See TCC below for the C compiler for building the finished binary. |
| 22 | 26 |
| --- 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 |
M
VERSION
+1
-1
| --- VERSION | ||
| +++ VERSION | ||
| @@ -1,1 +1,1 @@ | ||
| 1 | -1.19 | |
| 1 | +1.20 | |
| 2 | 2 | |
| 3 | 3 | ADDED ajax/README |
| 4 | 4 | ADDED ajax/cgi-bin/fossil-json.cgi.example |
| 5 | 5 | ADDED ajax/i-test/rhino-shell.js |
| 6 | 6 | ADDED ajax/i-test/rhino-test.js |
| 7 | 7 | ADDED ajax/index.html |
| 8 | 8 | ADDED ajax/js/fossil-ajaj.js |
| 9 | 9 | ADDED ajax/js/json2.js |
| 10 | 10 | ADDED ajax/js/whajaj.js |
| 11 | 11 | 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 |
+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-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(); |
+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/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 |
+1
| --- 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 |
+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/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*/ |
+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 ' '), | |
| 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 ' '), |
| 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 | }()); |
+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/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 |
M
auto.def
+30
| --- auto.def | ||
| +++ auto.def | ||
| @@ -4,15 +4,17 @@ | ||
| 4 | 4 | |
| 5 | 5 | options { |
| 6 | 6 | with-openssl:path|auto|none |
| 7 | 7 | => {Look for openssl in the given path, or auto or none} |
| 8 | 8 | with-zlib:path => {Look for zlib in the given path} |
| 9 | + with-tcl:path => {Enable Tcl integration, with Tcl in the specified path} | |
| 9 | 10 | internal-sqlite=1 => {Don't use the internal sqlite, use the system one} |
| 10 | 11 | static=0 => {Link a static executable} |
| 11 | 12 | lineedit=1 => {Disable line editing} |
| 12 | 13 | fossil-debug=0 => {Build with fossil debugging enabled} |
| 13 | 14 | ipv6=1 => {Disable IPv6 support} |
| 15 | + json=0 => {Build with fossil JSON API enabled} | |
| 14 | 16 | } |
| 15 | 17 | |
| 16 | 18 | # sqlite wants these types if possible |
| 17 | 19 | cc-with {-includes {stdint.h inttypes.h}} { |
| 18 | 20 | cc-check-types uint32_t uint16_t int16_t uint8_t |
| @@ -62,10 +64,14 @@ | ||
| 62 | 64 | } |
| 63 | 65 | |
| 64 | 66 | if {[opt-bool fossil-debug]} { |
| 65 | 67 | define-append EXTRA_CFLAGS -DFOSSIL_DEBUG |
| 66 | 68 | } |
| 69 | + | |
| 70 | +if {[opt-bool json]} { | |
| 71 | + define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON | |
| 72 | +} | |
| 67 | 73 | |
| 68 | 74 | if {[opt-bool static]} { |
| 69 | 75 | # XXX: This will not work on all systems. |
| 70 | 76 | define-append EXTRA_LDFLAGS -static |
| 71 | 77 | } |
| @@ -88,10 +94,34 @@ | ||
| 88 | 94 | define-append EXTRA_LDFLAGS -L$zlibpath |
| 89 | 95 | } |
| 90 | 96 | if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} { |
| 91 | 97 | user-error "zlib not found please install it or specify the location with --with-zlib" |
| 92 | 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 | +} | |
| 93 | 123 | |
| 94 | 124 | # Helper for openssl checking |
| 95 | 125 | proc check-for-openssl {msg {cflags {}}} { |
| 96 | 126 | msg-checking "Checking for $msg..." |
| 97 | 127 | set rc 0 |
| 98 | 128 | |
| 99 | 129 | 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 |
M
auto.def
+30
| --- auto.def | ||
| +++ auto.def | ||
| @@ -4,15 +4,17 @@ | ||
| 4 | 4 | |
| 5 | 5 | options { |
| 6 | 6 | with-openssl:path|auto|none |
| 7 | 7 | => {Look for openssl in the given path, or auto or none} |
| 8 | 8 | with-zlib:path => {Look for zlib in the given path} |
| 9 | + with-tcl:path => {Enable Tcl integration, with Tcl in the specified path} | |
| 9 | 10 | internal-sqlite=1 => {Don't use the internal sqlite, use the system one} |
| 10 | 11 | static=0 => {Link a static executable} |
| 11 | 12 | lineedit=1 => {Disable line editing} |
| 12 | 13 | fossil-debug=0 => {Build with fossil debugging enabled} |
| 13 | 14 | ipv6=1 => {Disable IPv6 support} |
| 15 | + json=0 => {Build with fossil JSON API enabled} | |
| 14 | 16 | } |
| 15 | 17 | |
| 16 | 18 | # sqlite wants these types if possible |
| 17 | 19 | cc-with {-includes {stdint.h inttypes.h}} { |
| 18 | 20 | cc-check-types uint32_t uint16_t int16_t uint8_t |
| @@ -62,10 +64,14 @@ | ||
| 62 | 64 | } |
| 63 | 65 | |
| 64 | 66 | if {[opt-bool fossil-debug]} { |
| 65 | 67 | define-append EXTRA_CFLAGS -DFOSSIL_DEBUG |
| 66 | 68 | } |
| 69 | + | |
| 70 | +if {[opt-bool json]} { | |
| 71 | + define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON | |
| 72 | +} | |
| 67 | 73 | |
| 68 | 74 | if {[opt-bool static]} { |
| 69 | 75 | # XXX: This will not work on all systems. |
| 70 | 76 | define-append EXTRA_LDFLAGS -static |
| 71 | 77 | } |
| @@ -88,10 +94,34 @@ | ||
| 88 | 94 | define-append EXTRA_LDFLAGS -L$zlibpath |
| 89 | 95 | } |
| 90 | 96 | if {![cc-check-includes zlib.h] || ![cc-check-function-in-lib inflateEnd z]} { |
| 91 | 97 | user-error "zlib not found please install it or specify the location with --with-zlib" |
| 92 | 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 | +} | |
| 93 | 123 | |
| 94 | 124 | # Helper for openssl checking |
| 95 | 125 | proc check-for-openssl {msg {cflags {}}} { |
| 96 | 126 | msg-checking "Checking for $msg..." |
| 97 | 127 | set rc 0 |
| 98 | 128 | |
| 99 | 129 | 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 |
+2
| --- 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 @@ | ||
| 260 | 260 | db_end_transaction(0); |
| 261 | 261 | } |
| 262 | 262 | |
| 263 | 263 | /* |
| 264 | 264 | ** COMMAND: rm |
| 265 | -** COMMAND: delete | |
| 265 | +** COMMAND: delete* | |
| 266 | 266 | ** |
| 267 | 267 | ** Usage: %fossil rm FILE1 ?FILE2 ...? |
| 268 | 268 | ** or: %fossil delete FILE1 ?FILE2 ...? |
| 269 | 269 | ** |
| 270 | 270 | ** Remove one or more files or directories from the repository. |
| @@ -473,11 +473,11 @@ | ||
| 473 | 473 | ); |
| 474 | 474 | } |
| 475 | 475 | |
| 476 | 476 | /* |
| 477 | 477 | ** COMMAND: mv |
| 478 | -** COMMAND: rename | |
| 478 | +** COMMAND: rename* | |
| 479 | 479 | ** |
| 480 | 480 | ** Usage: %fossil mv|rename OLDNAME NEWNAME |
| 481 | 481 | ** or: %fossil mv|rename OLDNAME... DIR |
| 482 | 482 | ** |
| 483 | 483 | ** Move or rename one or more files or directories within the repository tree. |
| 484 | 484 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -260,11 +260,11 @@ | |
| 260 | db_end_transaction(0); |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | ** COMMAND: rm |
| 265 | ** COMMAND: delete |
| 266 | ** |
| 267 | ** Usage: %fossil rm FILE1 ?FILE2 ...? |
| 268 | ** or: %fossil delete FILE1 ?FILE2 ...? |
| 269 | ** |
| 270 | ** Remove one or more files or directories from the repository. |
| @@ -473,11 +473,11 @@ | |
| 473 | ); |
| 474 | } |
| 475 | |
| 476 | /* |
| 477 | ** COMMAND: mv |
| 478 | ** COMMAND: rename |
| 479 | ** |
| 480 | ** Usage: %fossil mv|rename OLDNAME NEWNAME |
| 481 | ** or: %fossil mv|rename OLDNAME... DIR |
| 482 | ** |
| 483 | ** Move or rename one or more files or directories within the repository tree. |
| 484 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -260,11 +260,11 @@ | |
| 260 | db_end_transaction(0); |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | ** COMMAND: rm |
| 265 | ** COMMAND: delete* |
| 266 | ** |
| 267 | ** Usage: %fossil rm FILE1 ?FILE2 ...? |
| 268 | ** or: %fossil delete FILE1 ?FILE2 ...? |
| 269 | ** |
| 270 | ** Remove one or more files or directories from the repository. |
| @@ -473,11 +473,11 @@ | |
| 473 | ); |
| 474 | } |
| 475 | |
| 476 | /* |
| 477 | ** COMMAND: mv |
| 478 | ** COMMAND: rename* |
| 479 | ** |
| 480 | ** Usage: %fossil mv|rename OLDNAME NEWNAME |
| 481 | ** or: %fossil mv|rename OLDNAME... DIR |
| 482 | ** |
| 483 | ** Move or rename one or more files or directories within the repository tree. |
| 484 |
+41
-2
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -766,12 +766,24 @@ | ||
| 766 | 766 | int blob_write_to_file(Blob *pBlob, const char *zFilename){ |
| 767 | 767 | FILE *out; |
| 768 | 768 | int wrote; |
| 769 | 769 | |
| 770 | 770 | if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ |
| 771 | - fossil_puts(blob_str(pBlob), 0); | |
| 772 | - return blob_size(pBlob); | |
| 771 | + int n; | |
| 772 | +#if defined(_WIN32) | |
| 773 | + if( _isatty(fileno(stdout)) ){ | |
| 774 | + char *z; | |
| 775 | + z = fossil_utf8_to_console(blob_str(pBlob)); | |
| 776 | + n = strlen(z); | |
| 777 | + fwrite(z, 1, n, stdout); | |
| 778 | + free(z); | |
| 779 | + return n; | |
| 780 | + } | |
| 781 | +#endif | |
| 782 | + n = blob_size(pBlob); | |
| 783 | + fwrite(blob_buffer(pBlob), 1, n, stdout); | |
| 784 | + return n; | |
| 773 | 785 | }else{ |
| 774 | 786 | int i, nName; |
| 775 | 787 | char *zName, zBuf[1000]; |
| 776 | 788 | |
| 777 | 789 | nName = strlen(zFilename); |
| @@ -1039,5 +1051,32 @@ | ||
| 1039 | 1051 | return; |
| 1040 | 1052 | } |
| 1041 | 1053 | } |
| 1042 | 1054 | blob_append(pBlob, zIn, -1); |
| 1043 | 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 | +} | |
| 1044 | 1083 |
| --- 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 @@ | ||
| 178 | 178 | /* Do an autosync push, if requested */ |
| 179 | 179 | if( !isPrivate ) autosync(AUTOSYNC_PUSH); |
| 180 | 180 | } |
| 181 | 181 | |
| 182 | 182 | /* |
| 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. | |
| 184 | 188 | */ |
| 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 ){ | |
| 187 | 191 | db_prepare(pQuery, |
| 188 | 192 | "SELECT value FROM tagxref" |
| 189 | 193 | " WHERE tagid=%d AND value NOT NULL " |
| 190 | 194 | "EXCEPT " |
| 191 | 195 | "SELECT value FROM tagxref" |
| @@ -193,11 +197,11 @@ | ||
| 193 | 197 | " AND rid IN leaf" |
| 194 | 198 | " AND NOT %z" |
| 195 | 199 | " ORDER BY value COLLATE nocase /*sort*/", |
| 196 | 200 | TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") |
| 197 | 201 | ); |
| 198 | - }else if( showAll ){ | |
| 202 | + }else if( which>0 ){ | |
| 199 | 203 | db_prepare(pQuery, |
| 200 | 204 | "SELECT DISTINCT value FROM tagxref" |
| 201 | 205 | " WHERE tagid=%d AND value NOT NULL" |
| 202 | 206 | " AND rid IN leaf" |
| 203 | 207 | " ORDER BY value COLLATE nocase /*sort*/", |
| @@ -260,11 +264,11 @@ | ||
| 260 | 264 | if( g.localOpen ){ |
| 261 | 265 | vid = db_lget_int("checkout", 0); |
| 262 | 266 | zCurrent = db_text(0, "SELECT value FROM tagxref" |
| 263 | 267 | " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH); |
| 264 | 268 | } |
| 265 | - prepareBranchQuery(&q, showAll, showClosed); | |
| 269 | + branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0)); | |
| 266 | 270 | while( db_step(&q)==SQLITE_ROW ){ |
| 267 | 271 | const char *zBr = db_column_text(&q, 0); |
| 268 | 272 | int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0; |
| 269 | 273 | fossil_print("%s%s\n", (isCur ? "* " : " "), zBr); |
| 270 | 274 | } |
| @@ -327,11 +331,11 @@ | ||
| 327 | 331 | @ Closed branches are fixed and do not change (unless they are first |
| 328 | 332 | @ reopened)</li> |
| 329 | 333 | @ </ol> |
| 330 | 334 | style_sidebox_end(); |
| 331 | 335 | |
| 332 | - prepareBranchQuery(&q, showAll, showClosed); | |
| 336 | + branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0)); | |
| 333 | 337 | cnt = 0; |
| 334 | 338 | while( db_step(&q)==SQLITE_ROW ){ |
| 335 | 339 | const char *zBr = db_column_text(&q, 0); |
| 336 | 340 | if( cnt==0 ){ |
| 337 | 341 | if( colorTest ){ |
| 338 | 342 |
| --- 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 |
+1
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -249,10 +249,11 @@ | ||
| 249 | 249 | /* Generate a multi-column table listing the contents of zD[] |
| 250 | 250 | ** directory. |
| 251 | 251 | */ |
| 252 | 252 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); |
| 253 | 253 | cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/"); |
| 254 | + if( mxLen<12 ) mxLen = 12; | |
| 254 | 255 | nCol = 100/mxLen; |
| 255 | 256 | if( nCol<1 ) nCol = 1; |
| 256 | 257 | if( nCol>5 ) nCol = 5; |
| 257 | 258 | nRow = (cnt+nCol-1)/nCol; |
| 258 | 259 | db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/"); |
| 259 | 260 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -249,10 +249,11 @@ | |
| 249 | /* Generate a multi-column table listing the contents of zD[] |
| 250 | ** directory. |
| 251 | */ |
| 252 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); |
| 253 | cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/"); |
| 254 | nCol = 100/mxLen; |
| 255 | if( nCol<1 ) nCol = 1; |
| 256 | if( nCol>5 ) nCol = 5; |
| 257 | nRow = (cnt+nCol-1)/nCol; |
| 258 | db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/"); |
| 259 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -249,10 +249,11 @@ | |
| 249 | /* Generate a multi-column table listing the contents of zD[] |
| 250 | ** directory. |
| 251 | */ |
| 252 | mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); |
| 253 | cnt = db_int(0, "SELECT count(*) FROM localfiles /*scan*/"); |
| 254 | if( mxLen<12 ) mxLen = 12; |
| 255 | nCol = 100/mxLen; |
| 256 | if( nCol<1 ) nCol = 1; |
| 257 | if( nCol>5 ) nCol = 5; |
| 258 | nRow = (cnt+nCol-1)/nCol; |
| 259 | db_prepare(&q, "SELECT x, u FROM localfiles ORDER BY x /*scan*/"); |
| 260 |
+3
-1
| --- src/captcha.c | ||
| +++ src/captcha.c | ||
| @@ -412,12 +412,14 @@ | ||
| 412 | 412 | return x; |
| 413 | 413 | } |
| 414 | 414 | |
| 415 | 415 | /* |
| 416 | 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. | |
| 417 | 419 | */ |
| 418 | -char *captcha_decode(unsigned int seed){ | |
| 420 | +char const *captcha_decode(unsigned int seed){ | |
| 419 | 421 | const char *zSecret; |
| 420 | 422 | const char *z; |
| 421 | 423 | Blob b; |
| 422 | 424 | static char zRes[20]; |
| 423 | 425 | |
| 424 | 426 |
| --- 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 @@ | ||
| 43 | 43 | #endif |
| 44 | 44 | #include <time.h> |
| 45 | 45 | #include <stdio.h> |
| 46 | 46 | #include <stdlib.h> |
| 47 | 47 | #include <unistd.h> |
| 48 | -#if defined (__POCC__) | |
| 49 | -# undef INTERFACE | |
| 50 | -#endif | |
| 51 | 48 | #include "cgi.h" |
| 52 | 49 | |
| 53 | 50 | #if INTERFACE |
| 54 | 51 | /* |
| 55 | 52 | ** Shortcuts for cgi_parameter. P("x") returns the value of query parameter |
| @@ -136,11 +133,11 @@ | ||
| 136 | 133 | } |
| 137 | 134 | |
| 138 | 135 | /* |
| 139 | 136 | ** Return a pointer to the HTTP reply text. |
| 140 | 137 | */ |
| 141 | -char *cgi_extract_content(int *pnAmt){ | |
| 138 | +char *cgi_extract_content(void){ | |
| 142 | 139 | cgi_combine_header_and_body(); |
| 143 | 140 | return blob_buffer(&cgiContent[0]); |
| 144 | 141 | } |
| 145 | 142 | |
| 146 | 143 | /* |
| @@ -509,10 +506,13 @@ | ||
| 509 | 506 | zValue = ""; |
| 510 | 507 | } |
| 511 | 508 | if( fossil_islower(zName[0]) ){ |
| 512 | 509 | cgi_set_parameter_nocopy(zName, zValue); |
| 513 | 510 | } |
| 511 | +#ifdef FOSSIL_ENABLE_JSON | |
| 512 | + json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) ); | |
| 513 | +#endif /* FOSSIL_ENABLE_JSON */ | |
| 514 | 514 | } |
| 515 | 515 | } |
| 516 | 516 | |
| 517 | 517 | /* |
| 518 | 518 | ** *pz is a string that consists of multiple lines of text. This |
| @@ -681,10 +681,88 @@ | ||
| 681 | 681 | } |
| 682 | 682 | } |
| 683 | 683 | } |
| 684 | 684 | } |
| 685 | 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 | + | |
| 686 | 764 | |
| 687 | 765 | /* |
| 688 | 766 | ** Initialize the query parameter database. Information is pulled from |
| 689 | 767 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 690 | 768 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -691,19 +769,32 @@ | ||
| 691 | 769 | */ |
| 692 | 770 | void cgi_init(void){ |
| 693 | 771 | char *z; |
| 694 | 772 | const char *zType; |
| 695 | 773 | int len; |
| 774 | +#ifdef FOSSIL_ENABLE_JSON | |
| 775 | + json_main_bootstrap(); | |
| 776 | +#endif | |
| 777 | + g.isHTTP = 1; | |
| 696 | 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 | + | |
| 697 | 786 | z = (char*)P("QUERY_STRING"); |
| 698 | 787 | if( z ){ |
| 699 | 788 | z = mprintf("%s",z); |
| 700 | 789 | add_param_list(z, '&'); |
| 701 | 790 | } |
| 702 | 791 | |
| 703 | 792 | z = (char*)P("REMOTE_ADDR"); |
| 704 | - if( z ) g.zIpAddr = mprintf("%s", z); | |
| 793 | + if( z ){ | |
| 794 | + g.zIpAddr = mprintf("%s", z); | |
| 795 | + } | |
| 705 | 796 | |
| 706 | 797 | len = atoi(PD("CONTENT_LENGTH", "0")); |
| 707 | 798 | g.zContentType = zType = P("CONTENT_TYPE"); |
| 708 | 799 | if( len>0 && zType ){ |
| 709 | 800 | blob_zero(&g.cgiIn); |
| @@ -723,17 +814,36 @@ | ||
| 723 | 814 | }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){ |
| 724 | 815 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 725 | 816 | }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){ |
| 726 | 817 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 727 | 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 */ | |
| 728 | 843 | } |
| 729 | 844 | |
| 730 | - z = (char*)P("HTTP_COOKIE"); | |
| 731 | - if( z ){ | |
| 732 | - z = mprintf("%s",z); | |
| 733 | - add_param_list(z, ';'); | |
| 734 | - } | |
| 735 | 845 | } |
| 736 | 846 | |
| 737 | 847 | /* |
| 738 | 848 | ** This is the comparison function used to sort the aParamQP[] array of |
| 739 | 849 | ** query parameters and cookies. |
| @@ -943,20 +1053,33 @@ | ||
| 943 | 1053 | ** Panic and die while processing a webpage. |
| 944 | 1054 | */ |
| 945 | 1055 | NORETURN void cgi_panic(const char *zFormat, ...){ |
| 946 | 1056 | va_list ap; |
| 947 | 1057 | 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 | + } | |
| 958 | 1081 | } |
| 959 | 1082 | |
| 960 | 1083 | /* |
| 961 | 1084 | ** Remove the first space-delimited token from a string and return |
| 962 | 1085 | ** a pointer to it. Add a NULL to the string to terminate the token. |
| @@ -993,11 +1116,10 @@ | ||
| 993 | 1116 | char *z, *zToken; |
| 994 | 1117 | int i; |
| 995 | 1118 | struct sockaddr_storage remoteName; |
| 996 | 1119 | socklen_t size = sizeof(remoteName); |
| 997 | 1120 | char zLine[2000]; /* A single line of input. */ |
| 998 | - | |
| 999 | 1121 | g.fullHttpReply = 1; |
| 1000 | 1122 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1001 | 1123 | malformed_request(); |
| 1002 | 1124 | } |
| 1003 | 1125 | zToken = extract_token(zLine, &z); |
| @@ -1058,16 +1180,16 @@ | ||
| 1058 | 1180 | while(*p && *p != ',') p++; |
| 1059 | 1181 | *p = '\0'; |
| 1060 | 1182 | zIpAddr = mprintf( "%s", zVal ); |
| 1061 | 1183 | } |
| 1062 | 1184 | #if 0 |
| 1063 | - else if( fossil_strcmp(zFieldName,"referer:")==0 ){ | |
| 1185 | + }else if( fossil_strcmp(zFieldName,"referer:")==0 ){ | |
| 1064 | 1186 | cgi_setenv("HTTP_REFERER", zVal); |
| 1187 | +#endif | |
| 1065 | 1188 | }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){ |
| 1066 | 1189 | cgi_setenv("HTTP_USER_AGENT", zVal); |
| 1067 | 1190 | } |
| 1068 | -#endif | |
| 1069 | 1191 | } |
| 1070 | 1192 | |
| 1071 | 1193 | if( zIpAddr==0 && |
| 1072 | 1194 | getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName, |
| 1073 | 1195 | &size)>=0 |
| 1074 | 1196 |
| --- 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 @@ | ||
| 43 | 43 | #endif |
| 44 | 44 | #include <time.h> |
| 45 | 45 | #include <stdio.h> |
| 46 | 46 | #include <stdlib.h> |
| 47 | 47 | #include <unistd.h> |
| 48 | -#if defined (__POCC__) | |
| 49 | -# undef INTERFACE | |
| 50 | -#endif | |
| 51 | 48 | #include "cgi.h" |
| 52 | 49 | |
| 53 | 50 | #if INTERFACE |
| 54 | 51 | /* |
| 55 | 52 | ** Shortcuts for cgi_parameter. P("x") returns the value of query parameter |
| @@ -136,11 +133,11 @@ | ||
| 136 | 133 | } |
| 137 | 134 | |
| 138 | 135 | /* |
| 139 | 136 | ** Return a pointer to the HTTP reply text. |
| 140 | 137 | */ |
| 141 | -char *cgi_extract_content(int *pnAmt){ | |
| 138 | +char *cgi_extract_content(void){ | |
| 142 | 139 | cgi_combine_header_and_body(); |
| 143 | 140 | return blob_buffer(&cgiContent[0]); |
| 144 | 141 | } |
| 145 | 142 | |
| 146 | 143 | /* |
| @@ -509,10 +506,13 @@ | ||
| 509 | 506 | zValue = ""; |
| 510 | 507 | } |
| 511 | 508 | if( fossil_islower(zName[0]) ){ |
| 512 | 509 | cgi_set_parameter_nocopy(zName, zValue); |
| 513 | 510 | } |
| 511 | +#ifdef FOSSIL_ENABLE_JSON | |
| 512 | + json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) ); | |
| 513 | +#endif /* FOSSIL_ENABLE_JSON */ | |
| 514 | 514 | } |
| 515 | 515 | } |
| 516 | 516 | |
| 517 | 517 | /* |
| 518 | 518 | ** *pz is a string that consists of multiple lines of text. This |
| @@ -681,10 +681,88 @@ | ||
| 681 | 681 | } |
| 682 | 682 | } |
| 683 | 683 | } |
| 684 | 684 | } |
| 685 | 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 | + | |
| 686 | 764 | |
| 687 | 765 | /* |
| 688 | 766 | ** Initialize the query parameter database. Information is pulled from |
| 689 | 767 | ** the QUERY_STRING environment variable (if it exists), from standard |
| 690 | 768 | ** input if there is POST data, and from HTTP_COOKIE. |
| @@ -691,19 +769,32 @@ | ||
| 691 | 769 | */ |
| 692 | 770 | void cgi_init(void){ |
| 693 | 771 | char *z; |
| 694 | 772 | const char *zType; |
| 695 | 773 | int len; |
| 774 | +#ifdef FOSSIL_ENABLE_JSON | |
| 775 | + json_main_bootstrap(); | |
| 776 | +#endif | |
| 777 | + g.isHTTP = 1; | |
| 696 | 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 | + | |
| 697 | 786 | z = (char*)P("QUERY_STRING"); |
| 698 | 787 | if( z ){ |
| 699 | 788 | z = mprintf("%s",z); |
| 700 | 789 | add_param_list(z, '&'); |
| 701 | 790 | } |
| 702 | 791 | |
| 703 | 792 | z = (char*)P("REMOTE_ADDR"); |
| 704 | - if( z ) g.zIpAddr = mprintf("%s", z); | |
| 793 | + if( z ){ | |
| 794 | + g.zIpAddr = mprintf("%s", z); | |
| 795 | + } | |
| 705 | 796 | |
| 706 | 797 | len = atoi(PD("CONTENT_LENGTH", "0")); |
| 707 | 798 | g.zContentType = zType = P("CONTENT_TYPE"); |
| 708 | 799 | if( len>0 && zType ){ |
| 709 | 800 | blob_zero(&g.cgiIn); |
| @@ -723,17 +814,36 @@ | ||
| 723 | 814 | }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){ |
| 724 | 815 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 725 | 816 | }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){ |
| 726 | 817 | blob_read_from_channel(&g.cgiIn, g.httpIn, len); |
| 727 | 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 */ | |
| 728 | 843 | } |
| 729 | 844 | |
| 730 | - z = (char*)P("HTTP_COOKIE"); | |
| 731 | - if( z ){ | |
| 732 | - z = mprintf("%s",z); | |
| 733 | - add_param_list(z, ';'); | |
| 734 | - } | |
| 735 | 845 | } |
| 736 | 846 | |
| 737 | 847 | /* |
| 738 | 848 | ** This is the comparison function used to sort the aParamQP[] array of |
| 739 | 849 | ** query parameters and cookies. |
| @@ -943,20 +1053,33 @@ | ||
| 943 | 1053 | ** Panic and die while processing a webpage. |
| 944 | 1054 | */ |
| 945 | 1055 | NORETURN void cgi_panic(const char *zFormat, ...){ |
| 946 | 1056 | va_list ap; |
| 947 | 1057 | 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 | + } | |
| 958 | 1081 | } |
| 959 | 1082 | |
| 960 | 1083 | /* |
| 961 | 1084 | ** Remove the first space-delimited token from a string and return |
| 962 | 1085 | ** a pointer to it. Add a NULL to the string to terminate the token. |
| @@ -993,11 +1116,10 @@ | ||
| 993 | 1116 | char *z, *zToken; |
| 994 | 1117 | int i; |
| 995 | 1118 | struct sockaddr_storage remoteName; |
| 996 | 1119 | socklen_t size = sizeof(remoteName); |
| 997 | 1120 | char zLine[2000]; /* A single line of input. */ |
| 998 | - | |
| 999 | 1121 | g.fullHttpReply = 1; |
| 1000 | 1122 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1001 | 1123 | malformed_request(); |
| 1002 | 1124 | } |
| 1003 | 1125 | zToken = extract_token(zLine, &z); |
| @@ -1058,16 +1180,16 @@ | ||
| 1058 | 1180 | while(*p && *p != ',') p++; |
| 1059 | 1181 | *p = '\0'; |
| 1060 | 1182 | zIpAddr = mprintf( "%s", zVal ); |
| 1061 | 1183 | } |
| 1062 | 1184 | #if 0 |
| 1063 | - else if( fossil_strcmp(zFieldName,"referer:")==0 ){ | |
| 1185 | + }else if( fossil_strcmp(zFieldName,"referer:")==0 ){ | |
| 1064 | 1186 | cgi_setenv("HTTP_REFERER", zVal); |
| 1187 | +#endif | |
| 1065 | 1188 | }else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){ |
| 1066 | 1189 | cgi_setenv("HTTP_USER_AGENT", zVal); |
| 1067 | 1190 | } |
| 1068 | -#endif | |
| 1069 | 1191 | } |
| 1070 | 1192 | |
| 1071 | 1193 | if( zIpAddr==0 && |
| 1072 | 1194 | getsockname(fileno(g.httpIn), (struct sockaddr*)&remoteName, |
| 1073 | 1195 | &size)>=0 |
| 1074 | 1196 |
| --- 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 @@ | ||
| 182 | 182 | int vid; |
| 183 | 183 | db_must_be_within_tree(); |
| 184 | 184 | /* 012345678901234 */ |
| 185 | 185 | fossil_print("repository: %s\n", db_lget("repository","")); |
| 186 | 186 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 187 | - fossil_print("server-code: %s\n", db_get("server-code", "")); | |
| 188 | 187 | vid = db_lget_int("checkout", 0); |
| 189 | 188 | if( vid ){ |
| 190 | 189 | show_common_info(vid, "checkout:", 1, 1); |
| 191 | 190 | } |
| 191 | + db_record_repository_filename(0); | |
| 192 | 192 | changes_cmd(); |
| 193 | 193 | } |
| 194 | 194 | |
| 195 | 195 | /* |
| 196 | 196 | ** COMMAND: ls |
| @@ -282,18 +282,16 @@ | ||
| 282 | 282 | Stmt q; |
| 283 | 283 | int n; |
| 284 | 284 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 285 | 285 | int allFlag = find_option("dotfiles",0,0)!=0; |
| 286 | 286 | int cwdRelative = 0; |
| 287 | - int outputManifest; | |
| 288 | 287 | Glob *pIgnore; |
| 289 | 288 | Blob rewrittenPathname; |
| 290 | 289 | const char *zPathname, *zDisplayName; |
| 291 | 290 | |
| 292 | 291 | db_must_be_within_tree(); |
| 293 | 292 | cwdRelative = determine_cwd_relative_option(); |
| 294 | - outputManifest = db_get_boolean("manifest",0); | |
| 295 | 293 | db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); |
| 296 | 294 | n = strlen(g.zLocalRoot); |
| 297 | 295 | blob_init(&path, g.zLocalRoot, n-1); |
| 298 | 296 | if( zIgnoreFlag==0 ){ |
| 299 | 297 | zIgnoreFlag = db_get("ignore-glob", 0); |
| @@ -827,11 +825,11 @@ | ||
| 827 | 825 | blob_reset(&fname); |
| 828 | 826 | } |
| 829 | 827 | } |
| 830 | 828 | |
| 831 | 829 | /* |
| 832 | -** COMMAND: ci | |
| 830 | +** COMMAND: ci* | |
| 833 | 831 | ** COMMAND: commit |
| 834 | 832 | ** |
| 835 | 833 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 836 | 834 | ** |
| 837 | 835 | ** Create a new version containing all of the changes in the current |
| 838 | 836 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -182,15 +182,15 @@ | |
| 182 | int vid; |
| 183 | db_must_be_within_tree(); |
| 184 | /* 012345678901234 */ |
| 185 | fossil_print("repository: %s\n", db_lget("repository","")); |
| 186 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| 187 | fossil_print("server-code: %s\n", db_get("server-code", "")); |
| 188 | vid = db_lget_int("checkout", 0); |
| 189 | if( vid ){ |
| 190 | show_common_info(vid, "checkout:", 1, 1); |
| 191 | } |
| 192 | changes_cmd(); |
| 193 | } |
| 194 | |
| 195 | /* |
| 196 | ** COMMAND: ls |
| @@ -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 @@ | ||
| 161 | 161 | } |
| 162 | 162 | |
| 163 | 163 | } |
| 164 | 164 | |
| 165 | 165 | /* |
| 166 | -** COMMAND: checkout | |
| 167 | -** COMMAND: co | |
| 166 | +** COMMAND: checkout* | |
| 167 | +** COMMAND: co* | |
| 168 | 168 | ** |
| 169 | 169 | ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS? |
| 170 | 170 | ** or: %fossil co ?VERSION | --latest? ?OPTIONS? |
| 171 | 171 | ** |
| 172 | 172 | ** Check out a version specified on the command-line. This command |
| @@ -272,11 +272,11 @@ | ||
| 272 | 272 | } |
| 273 | 273 | } |
| 274 | 274 | } |
| 275 | 275 | |
| 276 | 276 | /* |
| 277 | -** COMMAND: close | |
| 277 | +** COMMAND: close* | |
| 278 | 278 | ** |
| 279 | 279 | ** Usage: %fossil close ?OPTIONS? |
| 280 | 280 | ** |
| 281 | 281 | ** The opposite of "open". Close the current database connection. |
| 282 | 282 | ** Require a -f or --force flag if there are unsaved changed in the |
| 283 | 283 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -161,12 +161,12 @@ | |
| 161 | } |
| 162 | |
| 163 | } |
| 164 | |
| 165 | /* |
| 166 | ** COMMAND: checkout |
| 167 | ** COMMAND: co |
| 168 | ** |
| 169 | ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS? |
| 170 | ** or: %fossil co ?VERSION | --latest? ?OPTIONS? |
| 171 | ** |
| 172 | ** Check out a version specified on the command-line. This command |
| @@ -272,11 +272,11 @@ | |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | /* |
| 277 | ** COMMAND: close |
| 278 | ** |
| 279 | ** Usage: %fossil close ?OPTIONS? |
| 280 | ** |
| 281 | ** The opposite of "open". Close the current database connection. |
| 282 | ** Require a -f or --force flag if there are unsaved changed in the |
| 283 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -161,12 +161,12 @@ | |
| 161 | } |
| 162 | |
| 163 | } |
| 164 | |
| 165 | /* |
| 166 | ** COMMAND: checkout* |
| 167 | ** COMMAND: co* |
| 168 | ** |
| 169 | ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS? |
| 170 | ** or: %fossil co ?VERSION | --latest? ?OPTIONS? |
| 171 | ** |
| 172 | ** Check out a version specified on the command-line. This command |
| @@ -272,11 +272,11 @@ | |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | /* |
| 277 | ** COMMAND: close* |
| 278 | ** |
| 279 | ** Usage: %fossil close ?OPTIONS? |
| 280 | ** |
| 281 | ** The opposite of "open". Close the current database connection. |
| 282 | ** Require a -f or --force flag if there are unsaved changed in the |
| 283 |
+1
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -724,11 +724,11 @@ | ||
| 724 | 724 | blob_reset(&out); |
| 725 | 725 | } |
| 726 | 726 | |
| 727 | 727 | |
| 728 | 728 | /* |
| 729 | -** COMMAND: configuration | |
| 729 | +** COMMAND: configuration* | |
| 730 | 730 | ** |
| 731 | 731 | ** Usage: %fossil configuration METHOD ... ?OPTIONS? |
| 732 | 732 | ** |
| 733 | 733 | ** Where METHOD is one of: export import merge pull push reset. All methods |
| 734 | 734 | ** accept the -R or --repository option to specific a repository. |
| 735 | 735 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -724,11 +724,11 @@ | |
| 724 | blob_reset(&out); |
| 725 | } |
| 726 | |
| 727 | |
| 728 | /* |
| 729 | ** COMMAND: configuration |
| 730 | ** |
| 731 | ** Usage: %fossil configuration METHOD ... ?OPTIONS? |
| 732 | ** |
| 733 | ** Where METHOD is one of: export import merge pull push reset. All methods |
| 734 | ** accept the -R or --repository option to specific a repository. |
| 735 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -724,11 +724,11 @@ | |
| 724 | blob_reset(&out); |
| 725 | } |
| 726 | |
| 727 | |
| 728 | /* |
| 729 | ** COMMAND: configuration* |
| 730 | ** |
| 731 | ** Usage: %fossil configuration METHOD ... ?OPTIONS? |
| 732 | ** |
| 733 | ** Where METHOD is one of: export import merge pull push reset. All methods |
| 734 | ** accept the -R or --repository option to specific a repository. |
| 735 |
+1
-1
| --- src/content.c | ||
| +++ src/content.c | ||
| @@ -303,11 +303,11 @@ | ||
| 303 | 303 | } |
| 304 | 304 | return rc; |
| 305 | 305 | } |
| 306 | 306 | |
| 307 | 307 | /* |
| 308 | -** COMMAND: artifact | |
| 308 | +** COMMAND: artifact* | |
| 309 | 309 | ** |
| 310 | 310 | ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? |
| 311 | 311 | ** |
| 312 | 312 | ** Extract an artifact by its SHA1 hash and write the results on |
| 313 | 313 | ** standard output, or if the optional 4th argument is given, in |
| 314 | 314 | |
| 315 | 315 | ADDED src/cson_amalgamation.c |
| 316 | 316 | 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 |
+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.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 |
M
src/db.c
+64
-7
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -48,40 +48,59 @@ | ||
| 48 | 48 | Blob sql; /* The SQL for this statement */ |
| 49 | 49 | sqlite3_stmt *pStmt; /* The results of sqlite3_prepare() */ |
| 50 | 50 | Stmt *pNext, *pPrev; /* List of all unfinalized statements */ |
| 51 | 51 | int nStep; /* Number of sqlite3_step() calls */ |
| 52 | 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} | |
| 53 | 60 | #endif /* INTERFACE */ |
| 61 | +const struct Stmt empty_Stmt = empty_Stmt_m; | |
| 54 | 62 | |
| 55 | 63 | /* |
| 56 | 64 | ** Call this routine when a database error occurs. |
| 57 | 65 | */ |
| 58 | 66 | static void db_err(const char *zFormat, ...){ |
| 59 | 67 | va_list ap; |
| 60 | 68 | char *z; |
| 69 | + int rc = 1; | |
| 61 | 70 | static const char zRebuildMsg[] = |
| 62 | 71 | "If you have recently updated your fossil executable, you might\n" |
| 63 | 72 | "need to run \"fossil all rebuild\" to bring the repository\n" |
| 64 | 73 | "schemas up to date.\n"; |
| 65 | 74 | va_start(ap, zFormat); |
| 66 | 75 | z = vmprintf(zFormat, ap); |
| 67 | 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 */ | |
| 68 | 86 | if( g.xferPanic ){ |
| 69 | 87 | cgi_reset_content(); |
| 70 | 88 | @ error Database\serror:\s%F(z) |
| 71 | - cgi_reply(); | |
| 89 | + cgi_reply(); | |
| 72 | 90 | } |
| 73 | - if( g.cgiOutput ){ | |
| 91 | + else if( g.cgiOutput ){ | |
| 74 | 92 | g.cgiOutput = 0; |
| 75 | 93 | cgi_printf("<h1>Database Error</h1>\n" |
| 76 | 94 | "<pre>%h</pre><p>%s</p>", z, zRebuildMsg); |
| 77 | 95 | cgi_reply(); |
| 78 | 96 | }else{ |
| 79 | 97 | fprintf(stderr, "%s: %s\n\n%s", fossil_nameofexe(), z, zRebuildMsg); |
| 80 | 98 | } |
| 99 | + free(z); | |
| 81 | 100 | db_force_rollback(); |
| 82 | - fossil_exit(1); | |
| 101 | + fossil_exit(rc); | |
| 83 | 102 | } |
| 84 | 103 | |
| 85 | 104 | static int nBegin = 0; /* Nesting depth of BEGIN */ |
| 86 | 105 | static int doRollback = 0; /* True to force a rollback */ |
| 87 | 106 | static int nCommitHook = 0; /* Number of commit hooks */ |
| @@ -561,11 +580,11 @@ | ||
| 561 | 580 | ** Execute a query. Return the first column of the first row |
| 562 | 581 | ** of the result set as a string. Space to hold the string is |
| 563 | 582 | ** obtained from malloc(). If the result set is empty, return |
| 564 | 583 | ** zDefault instead. |
| 565 | 584 | */ |
| 566 | -char *db_text(char *zDefault, const char *zSql, ...){ | |
| 585 | +char *db_text(char const *zDefault, const char *zSql, ...){ | |
| 567 | 586 | va_list ap; |
| 568 | 587 | Stmt s; |
| 569 | 588 | char *z; |
| 570 | 589 | va_start(ap, zSql); |
| 571 | 590 | db_vprepare(&s, 0, zSql, ap); |
| @@ -863,15 +882,24 @@ | ||
| 863 | 882 | db_err("unable to find the name of a repository database"); |
| 864 | 883 | } |
| 865 | 884 | } |
| 866 | 885 | if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){ |
| 867 | 886 | if( file_access(zDbName, 0) ){ |
| 887 | +#ifdef FOSSIL_ENABLE_JSON | |
| 888 | + g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND; | |
| 889 | +#endif | |
| 868 | 890 | fossil_panic("repository does not exist or" |
| 869 | 891 | " is in an unreadable directory: %s", zDbName); |
| 870 | 892 | }else if( file_access(zDbName, R_OK) ){ |
| 893 | +#ifdef FOSSIL_ENABLE_JSON | |
| 894 | + g.json.resultCode = FSL_JSON_E_DENIED; | |
| 895 | +#endif | |
| 871 | 896 | fossil_panic("read permission denied for repository %s", zDbName); |
| 872 | 897 | }else{ |
| 898 | +#ifdef FOSSIL_ENABLE_JSON | |
| 899 | + g.json.resultCode = FSL_JSON_E_DB_NOT_VALID; | |
| 900 | +#endif | |
| 873 | 901 | fossil_panic("not a valid repository: %s", zDbName); |
| 874 | 902 | } |
| 875 | 903 | } |
| 876 | 904 | db_open_or_attach(zDbName, "repository"); |
| 877 | 905 | g.repositoryOpen = 1; |
| @@ -902,11 +930,11 @@ | ||
| 902 | 930 | } |
| 903 | 931 | if( zRep==0 ){ |
| 904 | 932 | if( db_open_local()==0 ){ |
| 905 | 933 | goto rep_not_found; |
| 906 | 934 | } |
| 907 | - zRep = db_lget("repository", 0); | |
| 935 | + zRep = db_lget("repository", 0)/*leak here*/; | |
| 908 | 936 | if( zRep==0 ){ |
| 909 | 937 | goto rep_not_found; |
| 910 | 938 | } |
| 911 | 939 | } |
| 912 | 940 | db_open_repository(zRep); |
| @@ -914,10 +942,13 @@ | ||
| 914 | 942 | if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema(); |
| 915 | 943 | return; |
| 916 | 944 | } |
| 917 | 945 | rep_not_found: |
| 918 | 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 | |
| 919 | 950 | fossil_fatal("use --repository or -R to specify the repository database"); |
| 920 | 951 | } |
| 921 | 952 | } |
| 922 | 953 | |
| 923 | 954 | /* |
| @@ -944,10 +975,13 @@ | ||
| 944 | 975 | ** Verify that the repository schema is correct. If it is not correct, |
| 945 | 976 | ** issue a fatal error and die. |
| 946 | 977 | */ |
| 947 | 978 | void db_verify_schema(void){ |
| 948 | 979 | if( db_schema_is_outofdate() ){ |
| 980 | +#ifdef FOSSIL_ENABLE_JSON | |
| 981 | + g.json.resultCode = FSL_JSON_E_DB_NEEDS_REBUILD; | |
| 982 | +#endif | |
| 949 | 983 | fossil_warning("incorrect repository schema version"); |
| 950 | 984 | fossil_warning("your repository has schema version \"%s\" " |
| 951 | 985 | "but this binary expects version \"%s\"", |
| 952 | 986 | db_get("aux-schema",0), AUX_SCHEMA); |
| 953 | 987 | fossil_fatal("run \"fossil rebuild\" to fix this problem"); |
| @@ -1164,11 +1198,11 @@ | ||
| 1164 | 1198 | manifest_crosslink(rid, &manifest); |
| 1165 | 1199 | } |
| 1166 | 1200 | } |
| 1167 | 1201 | |
| 1168 | 1202 | /* |
| 1169 | -** COMMAND: new | |
| 1203 | +** COMMAND: new* | |
| 1170 | 1204 | ** COMMAND: init |
| 1171 | 1205 | ** |
| 1172 | 1206 | ** Usage: %fossil new ?OPTIONS? FILENAME |
| 1173 | 1207 | ** Or: %fossil init ?OPTIONS? FILENAME |
| 1174 | 1208 | ** |
| @@ -1621,10 +1655,17 @@ | ||
| 1621 | 1655 | ** of the following form: |
| 1622 | 1656 | ** |
| 1623 | 1657 | ** repo:%s |
| 1624 | 1658 | ** |
| 1625 | 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. | |
| 1626 | 1667 | */ |
| 1627 | 1668 | void db_record_repository_filename(const char *zName){ |
| 1628 | 1669 | Blob full; |
| 1629 | 1670 | if( zName==0 ){ |
| 1630 | 1671 | if( !g.localOpen ) return; |
| @@ -1635,10 +1676,17 @@ | ||
| 1635 | 1676 | db_multi_exec( |
| 1636 | 1677 | "INSERT OR IGNORE INTO global_config(name,value)" |
| 1637 | 1678 | "VALUES('repo:%q',1)", |
| 1638 | 1679 | blob_str(&full) |
| 1639 | 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 | + } | |
| 1640 | 1688 | db_swap_connections(); |
| 1641 | 1689 | blob_reset(&full); |
| 1642 | 1690 | } |
| 1643 | 1691 | |
| 1644 | 1692 | /* |
| @@ -1792,18 +1840,21 @@ | ||
| 1792 | 1840 | { "repo-cksum", 0, 0, 0, "on" }, |
| 1793 | 1841 | { "self-register", 0, 0, 0, "off" }, |
| 1794 | 1842 | { "ssl-ca-location",0, 40, 0, "" }, |
| 1795 | 1843 | { "ssl-identity", 0, 40, 0, "" }, |
| 1796 | 1844 | { "ssh-command", 0, 32, 0, "" }, |
| 1845 | +#ifdef FOSSIL_ENABLE_TCL | |
| 1846 | + { "tcl", 0, 0, 0, "off" }, | |
| 1847 | +#endif | |
| 1797 | 1848 | { "web-browser", 0, 32, 0, "" }, |
| 1798 | 1849 | { "white-foreground", 0, 0, 0, "off" }, |
| 1799 | 1850 | { 0,0,0,0,0 } |
| 1800 | 1851 | }; |
| 1801 | 1852 | |
| 1802 | 1853 | /* |
| 1803 | 1854 | ** COMMAND: settings |
| 1804 | -** COMMAND: unset | |
| 1855 | +** COMMAND: unset* | |
| 1805 | 1856 | ** |
| 1806 | 1857 | ** %fossil settings ?PROPERTY? ?VALUE? ?-global? |
| 1807 | 1858 | ** %fossil unset PROPERTY ?-global? |
| 1808 | 1859 | ** |
| 1809 | 1860 | ** The "settings" command with no arguments lists all properties and their |
| @@ -1945,10 +1996,16 @@ | ||
| 1945 | 1996 | ** authenticate this client, in addition to the normal |
| 1946 | 1997 | ** password authentication. |
| 1947 | 1998 | ** |
| 1948 | 1999 | ** ssh-command Command used to talk to a remote machine with |
| 1949 | 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. | |
| 1950 | 2007 | ** |
| 1951 | 2008 | ** web-browser A shell command used to launch your preferred |
| 1952 | 2009 | ** web browser when given a URL as an argument. |
| 1953 | 2010 | ** Defaults to "start" on windows, "open" on Mac, |
| 1954 | 2011 | ** and "firefox" on Unix. |
| 1955 | 2012 |
| --- 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 |
+4
-3
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -202,11 +202,12 @@ | ||
| 202 | 202 | void compute_direct_ancestors(int rid, int N){ |
| 203 | 203 | Stmt ins; |
| 204 | 204 | Stmt q; |
| 205 | 205 | int gen = 0; |
| 206 | 206 | 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;" | |
| 208 | 209 | "INSERT INTO ancestor VALUES(%d, 0);", rid |
| 209 | 210 | ); |
| 210 | 211 | db_prepare(&ins, "INSERT INTO ancestor VALUES(:rid, :gen)"); |
| 211 | 212 | db_prepare(&q, |
| 212 | 213 | "SELECT pid FROM plink" |
| @@ -262,11 +263,11 @@ | ||
| 262 | 263 | db_finalize(&ins); |
| 263 | 264 | db_finalize(&q); |
| 264 | 265 | } |
| 265 | 266 | |
| 266 | 267 | /* |
| 267 | -** COMMAND: descendants | |
| 268 | +** COMMAND: descendants* | |
| 268 | 269 | ** |
| 269 | 270 | ** Usage: %fossil descendants ?BASELINE-ID? ?OPTIONS? |
| 270 | 271 | ** |
| 271 | 272 | ** Find all leaf descendants of the baseline specified or if the argument |
| 272 | 273 | ** is omitted, of the baseline currently checked out. |
| @@ -297,11 +298,11 @@ | ||
| 297 | 298 | print_timeline(&q, 20, 0); |
| 298 | 299 | db_finalize(&q); |
| 299 | 300 | } |
| 300 | 301 | |
| 301 | 302 | /* |
| 302 | -** COMMAND: leaves | |
| 303 | +** COMMAND: leaves* | |
| 303 | 304 | ** |
| 304 | 305 | ** Usage: %fossil leaves ?OPTIONS? |
| 305 | 306 | ** |
| 306 | 307 | ** Find leaves of all branches. By default show only open leaves. |
| 307 | 308 | ** The --all flag causes all leaves (closed and open) to be shown. |
| 308 | 309 |
| --- 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 @@ | ||
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "diff.h" |
| 23 | 23 | #include <assert.h> |
| 24 | 24 | |
| 25 | 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 | + | |
| 26 | 38 | /* |
| 27 | 39 | ** Maximum length of a line in a text file. (8192) |
| 28 | 40 | */ |
| 29 | 41 | #define LENGTH_MASK_SZ 13 |
| 30 | 42 | #define LENGTH_MASK ((1<<LENGTH_MASK_SZ)-1) |
| @@ -285,10 +297,196 @@ | ||
| 285 | 297 | for(j=0; j<m; j++){ |
| 286 | 298 | appendDiffLine(pOut, " ", &B[b+j]); |
| 287 | 299 | } |
| 288 | 300 | } |
| 289 | 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 | +} | |
| 290 | 488 | |
| 291 | 489 | /* |
| 292 | 490 | ** Compute the optimal longest common subsequence (LCS) using an |
| 293 | 491 | ** exhaustive search. This version of the LCS is only used for |
| 294 | 492 | ** shorter input strings since runtime is O(N*N) where N is the |
| @@ -520,10 +718,30 @@ | ||
| 520 | 718 | p->aEdit[p->nEdit++] = 0; |
| 521 | 719 | p->aEdit[p->nEdit++] = 0; |
| 522 | 720 | p->aEdit[p->nEdit++] = 0; |
| 523 | 721 | } |
| 524 | 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 | +} | |
| 525 | 743 | |
| 526 | 744 | /* |
| 527 | 745 | ** Generate a report of the differences between files pA and pB. |
| 528 | 746 | ** If pOut is not NULL then a unified diff is appended there. It |
| 529 | 747 | ** is assumed that pOut has already been initialized. If pOut is |
| @@ -538,16 +756,20 @@ | ||
| 538 | 756 | ** text "cannot compute difference between binary files". |
| 539 | 757 | */ |
| 540 | 758 | int *text_diff( |
| 541 | 759 | Blob *pA_Blob, /* FROM file */ |
| 542 | 760 | 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 */ | |
| 546 | 763 | ){ |
| 764 | + int ignoreEolWs; /* Ignore whitespace at the end of lines */ | |
| 765 | + int nContext; /* Amount of context to display */ | |
| 547 | 766 | DContext c; |
| 548 | - | |
| 767 | + | |
| 768 | + nContext = diff_context_lines(diffFlags); | |
| 769 | + ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0; | |
| 770 | + | |
| 549 | 771 | /* Prepare the input files */ |
| 550 | 772 | memset(&c, 0, sizeof(c)); |
| 551 | 773 | c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), |
| 552 | 774 | &c.nFrom, ignoreEolWs); |
| 553 | 775 | c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), |
| @@ -563,12 +785,17 @@ | ||
| 563 | 785 | |
| 564 | 786 | /* Compute the difference */ |
| 565 | 787 | diff_all(&c); |
| 566 | 788 | |
| 567 | 789 | 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 | + } | |
| 570 | 797 | free(c.aFrom); |
| 571 | 798 | free(c.aTo); |
| 572 | 799 | free(c.aEdit); |
| 573 | 800 | return 0; |
| 574 | 801 | }else{ |
| @@ -578,10 +805,194 @@ | ||
| 578 | 805 | free(c.aFrom); |
| 579 | 806 | free(c.aTo); |
| 580 | 807 | return c.aEdit; |
| 581 | 808 | } |
| 582 | 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><</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>></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 | + | |
| 583 | 994 | |
| 584 | 995 | /* |
| 585 | 996 | ** COMMAND: test-rawdiff |
| 586 | 997 | */ |
| 587 | 998 | void test_rawdiff_cmd(void){ |
| @@ -592,29 +1003,58 @@ | ||
| 592 | 1003 | if( g.argc<4 ) usage("FILE1 FILE2 ..."); |
| 593 | 1004 | blob_read_from_file(&a, g.argv[2]); |
| 594 | 1005 | for(i=3; i<g.argc; i++){ |
| 595 | 1006 | if( i>3 ) fossil_print("-------------------------------\n"); |
| 596 | 1007 | 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); | |
| 598 | 1009 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| 599 | 1010 | fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]); |
| 600 | 1011 | } |
| 601 | 1012 | /* free(R); */ |
| 602 | 1013 | blob_reset(&b); |
| 603 | 1014 | } |
| 604 | 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 | +} | |
| 605 | 1041 | |
| 606 | 1042 | /* |
| 607 | 1043 | ** COMMAND: test-udiff |
| 1044 | +** | |
| 1045 | +** Print the difference between two files. The usual diff options apply. | |
| 608 | 1046 | */ |
| 609 | 1047 | void test_udiff_cmd(void){ |
| 610 | 1048 | Blob a, b, out; |
| 1049 | + int diffFlag = diff_options(); | |
| 1050 | + | |
| 611 | 1051 | if( g.argc!=4 ) usage("FILE1 FILE2"); |
| 612 | 1052 | blob_read_from_file(&a, g.argv[2]); |
| 613 | 1053 | blob_read_from_file(&b, g.argv[3]); |
| 614 | 1054 | blob_zero(&out); |
| 615 | - text_diff(&a, &b, &out, 3, 0); | |
| 1055 | + text_diff(&a, &b, &out, diffFlag); | |
| 616 | 1056 | blob_write_to_file(&out, "-"); |
| 617 | 1057 | } |
| 618 | 1058 | |
| 619 | 1059 | /************************************************************************** |
| 620 | 1060 | ** The basic difference engine is above. What follows is the annotation |
| @@ -884,10 +1324,11 @@ | ||
| 884 | 1324 | */ |
| 885 | 1325 | void annotate_cmd(void){ |
| 886 | 1326 | int fnid; /* Filename ID */ |
| 887 | 1327 | int fid; /* File instance ID */ |
| 888 | 1328 | int mid; /* Manifest where file was checked in */ |
| 1329 | + int cid; /* Checkout ID */ | |
| 889 | 1330 | Blob treename; /* FILENAME translated to canonical form */ |
| 890 | 1331 | char *zFilename; /* Cannonical filename */ |
| 891 | 1332 | Annotator ann; /* The annotation of the file */ |
| 892 | 1333 | int i; /* Loop counter */ |
| 893 | 1334 | const char *zLimit; /* The value to the --limit option */ |
| @@ -913,11 +1354,20 @@ | ||
| 913 | 1354 | } |
| 914 | 1355 | fid = db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q", zFilename); |
| 915 | 1356 | if( fid==0 ){ |
| 916 | 1357 | fossil_fatal("not part of current checkout: %s", zFilename); |
| 917 | 1358 | } |
| 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); | |
| 919 | 1369 | if( mid==0 ){ |
| 920 | 1370 | fossil_panic("unable to find manifest"); |
| 921 | 1371 | } |
| 922 | 1372 | if( fileVers ) annFlags |= ANN_FILE_VERS; |
| 923 | 1373 | annotate_file(&ann, fnid, mid, 0, iLimit, annFlags); |
| 924 | 1374 |
| --- 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><</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>></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 @@ | ||
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "diffcmd.h" |
| 22 | 22 | #include <assert.h> |
| 23 | 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 | 24 | /* |
| 31 | 25 | ** Output the results of a diff. Output goes to stdout for command-line |
| 32 | 26 | ** or to the CGI/HTTP result buffer for web pages. |
| 33 | 27 | */ |
| 34 | 28 | static void diff_printf(const char *zFormat, ...){ |
| @@ -41,15 +35,38 @@ | ||
| 41 | 35 | } |
| 42 | 36 | va_end(ap); |
| 43 | 37 | } |
| 44 | 38 | |
| 45 | 39 | /* |
| 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. | |
| 47 | 52 | */ |
| 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); | |
| 51 | 68 | } |
| 52 | 69 | |
| 53 | 70 | /* |
| 54 | 71 | ** Show the difference between two files, one in memory and one on disk. |
| 55 | 72 | ** |
| @@ -62,11 +79,11 @@ | ||
| 62 | 79 | void diff_file( |
| 63 | 80 | Blob *pFile1, /* In memory content to compare from */ |
| 64 | 81 | const char *zFile2, /* On disk content to compare to */ |
| 65 | 82 | const char *zName, /* Display name of the file */ |
| 66 | 83 | const char *zDiffCmd, /* Command for comparison */ |
| 67 | - int ignoreEolWs /* Ignore whitespace at end of line */ | |
| 84 | + int diffFlags /* Flags to control the diff */ | |
| 68 | 85 | ){ |
| 69 | 86 | if( zDiffCmd==0 ){ |
| 70 | 87 | Blob out; /* Diff output text */ |
| 71 | 88 | Blob file2; /* Content of zFile2 */ |
| 72 | 89 | const char *zName2; /* Name of zFile2 for display */ |
| @@ -84,13 +101,13 @@ | ||
| 84 | 101 | zName2 = zName; |
| 85 | 102 | } |
| 86 | 103 | |
| 87 | 104 | /* Compute and output the differences */ |
| 88 | 105 | blob_zero(&out); |
| 89 | - text_diff(pFile1, &file2, &out, 5, ignoreEolWs); | |
| 106 | + text_diff(pFile1, &file2, &out, diffFlags); | |
| 90 | 107 | if( blob_size(&out) ){ |
| 91 | - diff_printf("--- %s\n+++ %s\n", zName, zName2); | |
| 108 | + diff_print_filenames(zName, zName2, diffFlags); | |
| 92 | 109 | diff_printf("%s\n", blob_str(&out)); |
| 93 | 110 | } |
| 94 | 111 | |
| 95 | 112 | /* Release memory resources */ |
| 96 | 113 | blob_reset(&file2); |
| @@ -138,18 +155,18 @@ | ||
| 138 | 155 | void diff_file_mem( |
| 139 | 156 | Blob *pFile1, /* In memory content to compare from */ |
| 140 | 157 | Blob *pFile2, /* In memory content to compare to */ |
| 141 | 158 | const char *zName, /* Display name of the file */ |
| 142 | 159 | const char *zDiffCmd, /* Command for comparison */ |
| 143 | - int ignoreEolWs /* Ignore whitespace at end of lines */ | |
| 160 | + int diffFlags /* Diff flags */ | |
| 144 | 161 | ){ |
| 145 | 162 | if( zDiffCmd==0 ){ |
| 146 | 163 | Blob out; /* Diff output text */ |
| 147 | 164 | |
| 148 | 165 | 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); | |
| 151 | 168 | diff_printf("%s\n", blob_str(&out)); |
| 152 | 169 | |
| 153 | 170 | /* Release memory resources */ |
| 154 | 171 | blob_reset(&out); |
| 155 | 172 | }else{ |
| @@ -185,11 +202,11 @@ | ||
| 185 | 202 | ** against the same file on disk. |
| 186 | 203 | */ |
| 187 | 204 | static void diff_one_against_disk( |
| 188 | 205 | const char *zFrom, /* Name of file */ |
| 189 | 206 | const char *zDiffCmd, /* Use this "diff" command */ |
| 190 | - int ignoreEolWs, /* Ignore whitespace changes at end of lines */ | |
| 207 | + int diffFlags, /* Diff control flags */ | |
| 191 | 208 | const char *zFileTreeName |
| 192 | 209 | ){ |
| 193 | 210 | Blob fname; |
| 194 | 211 | Blob content; |
| 195 | 212 | int isLink; |
| @@ -196,11 +213,11 @@ | ||
| 196 | 213 | file_tree_name(zFileTreeName, &fname, 1); |
| 197 | 214 | historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0); |
| 198 | 215 | if( !isLink != !file_wd_islink(zFrom) ){ |
| 199 | 216 | diff_printf("cannot compute difference between symlink and regular file\n"); |
| 200 | 217 | }else{ |
| 201 | - diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, ignoreEolWs); | |
| 218 | + diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags); | |
| 202 | 219 | } |
| 203 | 220 | blob_reset(&content); |
| 204 | 221 | blob_reset(&fname); |
| 205 | 222 | } |
| 206 | 223 | |
| @@ -215,14 +232,12 @@ | ||
| 215 | 232 | int diffFlags /* Flags controlling diff output */ |
| 216 | 233 | ){ |
| 217 | 234 | int vid; |
| 218 | 235 | Blob sql; |
| 219 | 236 | Stmt q; |
| 220 | - int ignoreEolWs; /* Ignore end-of-line whitespace */ | |
| 221 | 237 | int asNewFile; /* Treat non-existant files as empty files */ |
| 222 | 238 | |
| 223 | - ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0; | |
| 224 | 239 | asNewFile = (diffFlags & DIFF_NEWFILE)!=0; |
| 225 | 240 | vid = db_lget_int("checkout", 0); |
| 226 | 241 | vfile_check_signature(vid, 1, 0); |
| 227 | 242 | blob_zero(&sql); |
| 228 | 243 | db_begin_transaction(); |
| @@ -289,22 +304,22 @@ | ||
| 289 | 304 | if( !asNewFile ){ showDiff = 0; } |
| 290 | 305 | } |
| 291 | 306 | if( showDiff ){ |
| 292 | 307 | Blob content; |
| 293 | 308 | 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); | |
| 296 | 311 | diff_printf("cannot compute difference between symlink and regular file\n"); |
| 297 | 312 | continue; |
| 298 | 313 | } |
| 299 | 314 | if( srcid>0 ){ |
| 300 | 315 | content_get(srcid, &content); |
| 301 | 316 | }else{ |
| 302 | 317 | blob_zero(&content); |
| 303 | 318 | } |
| 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); | |
| 306 | 321 | blob_reset(&content); |
| 307 | 322 | } |
| 308 | 323 | free(zToFree); |
| 309 | 324 | } |
| 310 | 325 | db_finalize(&q); |
| @@ -317,11 +332,11 @@ | ||
| 317 | 332 | */ |
| 318 | 333 | static void diff_one_two_versions( |
| 319 | 334 | const char *zFrom, |
| 320 | 335 | const char *zTo, |
| 321 | 336 | const char *zDiffCmd, |
| 322 | - int ignoreEolWs, | |
| 337 | + int diffFlags, | |
| 323 | 338 | const char *zFileTreeName |
| 324 | 339 | ){ |
| 325 | 340 | char *zName; |
| 326 | 341 | Blob fname; |
| 327 | 342 | Blob v1, v2; |
| @@ -329,14 +344,14 @@ | ||
| 329 | 344 | file_tree_name(zFileTreeName, &fname, 1); |
| 330 | 345 | zName = blob_str(&fname); |
| 331 | 346 | historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0); |
| 332 | 347 | historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0); |
| 333 | 348 | if( isLink1 != isLink2 ){ |
| 334 | - diff_printf("--- %s\n+++ %s\n", zName, zName); | |
| 349 | + diff_print_filenames(zName, zName, diffFlags); | |
| 335 | 350 | diff_printf("cannot compute difference between symlink and regular file\n"); |
| 336 | 351 | }else{ |
| 337 | - diff_file_mem(&v1, &v2, zName, zDiffCmd, ignoreEolWs); | |
| 352 | + diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags); | |
| 338 | 353 | } |
| 339 | 354 | blob_reset(&v1); |
| 340 | 355 | blob_reset(&v2); |
| 341 | 356 | blob_reset(&fname); |
| 342 | 357 | } |
| @@ -347,16 +362,16 @@ | ||
| 347 | 362 | */ |
| 348 | 363 | static void diff_manifest_entry( |
| 349 | 364 | struct ManifestFile *pFrom, |
| 350 | 365 | struct ManifestFile *pTo, |
| 351 | 366 | const char *zDiffCmd, |
| 352 | - int ignoreEolWs | |
| 367 | + int diffFlags | |
| 353 | 368 | ){ |
| 354 | 369 | Blob f1, f2; |
| 355 | 370 | int rid; |
| 356 | 371 | const char *zName = pFrom ? pFrom->zName : pTo->zName; |
| 357 | - diff_print_index(zName); | |
| 372 | + diff_print_index(zName, diffFlags); | |
| 358 | 373 | if( pFrom ){ |
| 359 | 374 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 360 | 375 | content_get(rid, &f1); |
| 361 | 376 | }else{ |
| 362 | 377 | blob_zero(&f1); |
| @@ -365,11 +380,11 @@ | ||
| 365 | 380 | rid = uuid_to_rid(pTo->zUuid, 0); |
| 366 | 381 | content_get(rid, &f2); |
| 367 | 382 | }else{ |
| 368 | 383 | blob_zero(&f2); |
| 369 | 384 | } |
| 370 | - diff_file_mem(&f1, &f2, zName, zDiffCmd, ignoreEolWs); | |
| 385 | + diff_file_mem(&f1, &f2, zName, zDiffCmd, diffFlags); | |
| 371 | 386 | blob_reset(&f1); |
| 372 | 387 | blob_reset(&f2); |
| 373 | 388 | } |
| 374 | 389 | |
| 375 | 390 | /* |
| @@ -381,11 +396,10 @@ | ||
| 381 | 396 | const char *zDiffCmd, |
| 382 | 397 | int diffFlags |
| 383 | 398 | ){ |
| 384 | 399 | Manifest *pFrom, *pTo; |
| 385 | 400 | ManifestFile *pFromFile, *pToFile; |
| 386 | - int ignoreEolWs = (diffFlags & DIFF_NOEOLWS)!=0 ? 1 : 0; | |
| 387 | 401 | int asNewFlag = (diffFlags & DIFF_NEWFILE)!=0 ? 1 : 0; |
| 388 | 402 | |
| 389 | 403 | pFrom = manifest_get_by_name(zFrom, 0); |
| 390 | 404 | manifest_file_rewind(pFrom); |
| 391 | 405 | pFromFile = manifest_file_next(pFrom,0); |
| @@ -403,26 +417,26 @@ | ||
| 403 | 417 | cmp = fossil_strcmp(pFromFile->zName, pToFile->zName); |
| 404 | 418 | } |
| 405 | 419 | if( cmp<0 ){ |
| 406 | 420 | diff_printf("DELETED %s\n", pFromFile->zName); |
| 407 | 421 | if( asNewFlag ){ |
| 408 | - diff_manifest_entry(pFromFile, 0, zDiffCmd, ignoreEolWs); | |
| 422 | + diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags); | |
| 409 | 423 | } |
| 410 | 424 | pFromFile = manifest_file_next(pFrom,0); |
| 411 | 425 | }else if( cmp>0 ){ |
| 412 | 426 | diff_printf("ADDED %s\n", pToFile->zName); |
| 413 | 427 | if( asNewFlag ){ |
| 414 | - diff_manifest_entry(0, pToFile, zDiffCmd, ignoreEolWs); | |
| 428 | + diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags); | |
| 415 | 429 | } |
| 416 | 430 | pToFile = manifest_file_next(pTo,0); |
| 417 | 431 | }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ |
| 418 | 432 | /* No changes */ |
| 419 | 433 | pFromFile = manifest_file_next(pFrom,0); |
| 420 | 434 | pToFile = manifest_file_next(pTo,0); |
| 421 | 435 | }else{ |
| 422 | 436 | /* diff_printf("CHANGED %s\n", pFromFile->zName); */ |
| 423 | - diff_manifest_entry(pFromFile, pToFile, zDiffCmd, ignoreEolWs); | |
| 437 | + diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags); | |
| 424 | 438 | pFromFile = manifest_file_next(pFrom,0); |
| 425 | 439 | pToFile = manifest_file_next(pTo,0); |
| 426 | 440 | } |
| 427 | 441 | } |
| 428 | 442 | manifest_destroy(pFrom); |
| @@ -456,14 +470,17 @@ | ||
| 456 | 470 | ** |
| 457 | 471 | ** The "-N" or "--new-file" option causes the complete text of added or |
| 458 | 472 | ** deleted files to be displayed. |
| 459 | 473 | ** |
| 460 | 474 | ** Options: |
| 475 | +** --context|-c N Use N lines of context | |
| 461 | 476 | ** --from|-r VERSION select VERSION as source for the diff |
| 462 | 477 | ** --new-file|-N output complete text of added or deleted files |
| 463 | 478 | ** -i use internal diff logic |
| 464 | 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 | |
| 465 | 482 | */ |
| 466 | 483 | void diff_cmd(void){ |
| 467 | 484 | int isGDiff; /* True for gdiff. False for normal diff */ |
| 468 | 485 | int isInternDiff; /* True for internal diff */ |
| 469 | 486 | int hasNFlag; /* True if -N or --new-file flag is used */ |
| @@ -475,23 +492,23 @@ | ||
| 475 | 492 | |
| 476 | 493 | isGDiff = g.argv[1][0]=='g'; |
| 477 | 494 | isInternDiff = find_option("internal","i",0)!=0; |
| 478 | 495 | zFrom = find_option("from", "r", 1); |
| 479 | 496 | zTo = find_option("to", 0, 1); |
| 497 | + diffFlags = diff_options(); | |
| 480 | 498 | hasNFlag = find_option("new-file","N",0)!=0; |
| 481 | - | |
| 482 | - | |
| 483 | 499 | if( hasNFlag ) diffFlags |= DIFF_NEWFILE; |
| 500 | + | |
| 484 | 501 | if( zTo==0 ){ |
| 485 | 502 | db_must_be_within_tree(); |
| 486 | 503 | verify_all_options(); |
| 487 | 504 | if( !isInternDiff ){ |
| 488 | 505 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 489 | 506 | } |
| 490 | 507 | if( g.argc>=3 ){ |
| 491 | 508 | 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]); | |
| 493 | 510 | } |
| 494 | 511 | }else{ |
| 495 | 512 | diff_all_against_disk(zFrom, zDiffCmd, diffFlags); |
| 496 | 513 | } |
| 497 | 514 | }else if( zFrom==0 ){ |
| @@ -502,11 +519,11 @@ | ||
| 502 | 519 | if( !isInternDiff ){ |
| 503 | 520 | zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0); |
| 504 | 521 | } |
| 505 | 522 | if( g.argc>=3 ){ |
| 506 | 523 | 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]); | |
| 508 | 525 | } |
| 509 | 526 | }else{ |
| 510 | 527 | diff_all_two_versions(zFrom, zTo, zDiffCmd, diffFlags); |
| 511 | 528 | } |
| 512 | 529 | } |
| 513 | 530 |
| --- 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 @@ | ||
| 367 | 367 | login_check_credentials(); |
| 368 | 368 | if( !g.perm.Read ){ login_needed(); return; } |
| 369 | 369 | zName = PD("name", "tip/index.wiki"); |
| 370 | 370 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 371 | 371 | if( zName[i]==0 || i>UUID_SIZE ){ |
| 372 | + zName = "index.html"; | |
| 372 | 373 | goto doc_not_found; |
| 373 | 374 | } |
| 374 | 375 | memcpy(zBaseline, zName, i); |
| 375 | 376 | zBaseline[i] = 0; |
| 376 | 377 | zName += i; |
| 377 | 378 | while( zName[0]=='/' ){ zName++; } |
| 378 | 379 | if( !file_is_simple_pathname(zName) ){ |
| 379 | - goto doc_not_found; | |
| 380 | + int n = strlen(zName); | |
| 381 | + if( n>0 && zName[n-1]=='/' ){ | |
| 382 | + zName = mprintf("%sindex.html", zName); | |
| 383 | + if( !file_is_simple_pathname(zName) ){ | |
| 384 | + goto doc_not_found; | |
| 385 | + } | |
| 386 | + }else{ | |
| 387 | + goto doc_not_found; | |
| 388 | + } | |
| 380 | 389 | } |
| 381 | 390 | if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){ |
| 382 | 391 | sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); |
| 383 | 392 | } |
| 384 | 393 | if( fossil_strcmp(zBaseline,"ckout")==0 ){ |
| @@ -408,10 +417,12 @@ | ||
| 408 | 417 | " fname TEXT, -- filename\n" |
| 409 | 418 | " rid INTEGER, -- artifact ID\n" |
| 410 | 419 | " UNIQUE(vid,fname,rid)\n" |
| 411 | 420 | ")" |
| 412 | 421 | ); |
| 422 | + | |
| 423 | + | |
| 413 | 424 | |
| 414 | 425 | /* Check to see if the documentation file artifact ID is contained |
| 415 | 426 | ** in the baseline cache */ |
| 416 | 427 | rid = db_int(0, "SELECT rid FROM vcache" |
| 417 | 428 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| @@ -499,11 +510,11 @@ | ||
| 499 | 510 | |
| 500 | 511 | doc_not_found: |
| 501 | 512 | /* Jump here when unable to locate the document */ |
| 502 | 513 | db_end_transaction(0); |
| 503 | 514 | style_header("Document Not Found"); |
| 504 | - @ <p>No such document: %h(PD("name","tip/index.wiki"))</p> | |
| 515 | + @ <p>No such document: %h(zName)</p> | |
| 505 | 516 | style_footer(); |
| 506 | 517 | return; |
| 507 | 518 | } |
| 508 | 519 | |
| 509 | 520 | /* |
| 510 | 521 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -367,18 +367,27 @@ | |
| 367 | login_check_credentials(); |
| 368 | if( !g.perm.Read ){ login_needed(); return; } |
| 369 | zName = PD("name", "tip/index.wiki"); |
| 370 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 371 | if( zName[i]==0 || i>UUID_SIZE ){ |
| 372 | goto doc_not_found; |
| 373 | } |
| 374 | memcpy(zBaseline, zName, i); |
| 375 | zBaseline[i] = 0; |
| 376 | zName += i; |
| 377 | while( zName[0]=='/' ){ zName++; } |
| 378 | if( !file_is_simple_pathname(zName) ){ |
| 379 | goto doc_not_found; |
| 380 | } |
| 381 | if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){ |
| 382 | sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); |
| 383 | } |
| 384 | if( fossil_strcmp(zBaseline,"ckout")==0 ){ |
| @@ -408,10 +417,12 @@ | |
| 408 | " fname TEXT, -- filename\n" |
| 409 | " rid INTEGER, -- artifact ID\n" |
| 410 | " UNIQUE(vid,fname,rid)\n" |
| 411 | ")" |
| 412 | ); |
| 413 | |
| 414 | /* Check to see if the documentation file artifact ID is contained |
| 415 | ** in the baseline cache */ |
| 416 | rid = db_int(0, "SELECT rid FROM vcache" |
| 417 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| @@ -499,11 +510,11 @@ | |
| 499 | |
| 500 | doc_not_found: |
| 501 | /* Jump here when unable to locate the document */ |
| 502 | db_end_transaction(0); |
| 503 | style_header("Document Not Found"); |
| 504 | @ <p>No such document: %h(PD("name","tip/index.wiki"))</p> |
| 505 | style_footer(); |
| 506 | return; |
| 507 | } |
| 508 | |
| 509 | /* |
| 510 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -367,18 +367,27 @@ | |
| 367 | login_check_credentials(); |
| 368 | if( !g.perm.Read ){ login_needed(); return; } |
| 369 | zName = PD("name", "tip/index.wiki"); |
| 370 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 371 | if( zName[i]==0 || i>UUID_SIZE ){ |
| 372 | zName = "index.html"; |
| 373 | goto doc_not_found; |
| 374 | } |
| 375 | memcpy(zBaseline, zName, i); |
| 376 | zBaseline[i] = 0; |
| 377 | zName += i; |
| 378 | while( zName[0]=='/' ){ zName++; } |
| 379 | if( !file_is_simple_pathname(zName) ){ |
| 380 | int n = strlen(zName); |
| 381 | if( n>0 && zName[n-1]=='/' ){ |
| 382 | zName = mprintf("%sindex.html", zName); |
| 383 | if( !file_is_simple_pathname(zName) ){ |
| 384 | goto doc_not_found; |
| 385 | } |
| 386 | }else{ |
| 387 | goto doc_not_found; |
| 388 | } |
| 389 | } |
| 390 | if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local()==0 ){ |
| 391 | sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); |
| 392 | } |
| 393 | if( fossil_strcmp(zBaseline,"ckout")==0 ){ |
| @@ -408,10 +417,12 @@ | |
| 417 | " fname TEXT, -- filename\n" |
| 418 | " rid INTEGER, -- artifact ID\n" |
| 419 | " UNIQUE(vid,fname,rid)\n" |
| 420 | ")" |
| 421 | ); |
| 422 | |
| 423 | |
| 424 | |
| 425 | /* Check to see if the documentation file artifact ID is contained |
| 426 | ** in the baseline cache */ |
| 427 | rid = db_int(0, "SELECT rid FROM vcache" |
| 428 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| @@ -499,11 +510,11 @@ | |
| 510 | |
| 511 | doc_not_found: |
| 512 | /* Jump here when unable to locate the document */ |
| 513 | db_end_transaction(0); |
| 514 | style_header("Document Not Found"); |
| 515 | @ <p>No such document: %h(zName)</p> |
| 516 | style_footer(); |
| 517 | return; |
| 518 | } |
| 519 | |
| 520 | /* |
| 521 |
+1
-1
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -287,11 +287,11 @@ | ||
| 287 | 287 | char zShortCkin[20]; |
| 288 | 288 | if( zBr==0 ) zBr = "trunk"; |
| 289 | 289 | if( uBg ){ |
| 290 | 290 | zBgClr = hash_color(zUser); |
| 291 | 291 | }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); | |
| 293 | 293 | } |
| 294 | 294 | gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr, 0); |
| 295 | 295 | if( memcmp(zDate, zPrevDate, 10) ){ |
| 296 | 296 | sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate); |
| 297 | 297 | @ <tr><td> |
| 298 | 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 ? "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 |
+1
-3
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -265,14 +265,12 @@ | ||
| 265 | 265 | void transport_send(Blob *toSend){ |
| 266 | 266 | char *z = blob_buffer(toSend); |
| 267 | 267 | int n = blob_size(toSend); |
| 268 | 268 | transport.nSent += n; |
| 269 | 269 | if( g.urlIsSsh ){ |
| 270 | - int sent; | |
| 271 | - sent = fwrite(z, 1, n, sshOut); | |
| 270 | + fwrite(z, 1, n, sshOut); | |
| 272 | 271 | fflush(sshOut); |
| 273 | - /* printf("sent %d of %d bytes\n", sent, n); fflush(stdout); */ | |
| 274 | 272 | }else if( g.urlIsHttps ){ |
| 275 | 273 | #ifdef FOSSIL_ENABLE_SSL |
| 276 | 274 | int sent; |
| 277 | 275 | while( n>0 ){ |
| 278 | 276 | sent = ssl_send(0, z, n); |
| 279 | 277 |
| --- 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 @@ | ||
| 148 | 148 | db_open_config(0); |
| 149 | 149 | db_record_repository_filename(g.argv[2]); |
| 150 | 150 | db_open_repository(g.argv[2]); |
| 151 | 151 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| 152 | 152 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 153 | - fossil_print("server-code: %s\n", db_get("server-code", "<none>")); | |
| 154 | 153 | return; |
| 155 | 154 | } |
| 156 | 155 | db_find_and_open_repository(0,0); |
| 157 | 156 | if( g.argc==2 ){ |
| 158 | 157 | int vid; |
| @@ -167,11 +166,10 @@ | ||
| 167 | 166 | if( g.zHome ){ |
| 168 | 167 | fossil_print("user-home: %s\n", g.zHome); |
| 169 | 168 | } |
| 170 | 169 | #endif |
| 171 | 170 | fossil_print("project-code: %s\n", db_get("project-code", "")); |
| 172 | - fossil_print("server-code: %s\n", db_get("server-code", "")); | |
| 173 | 171 | vid = g.localOpen ? db_lget_int("checkout", 0) : 0; |
| 174 | 172 | if( vid ){ |
| 175 | 173 | show_common_info(vid, "checkout:", 1, 1); |
| 176 | 174 | } |
| 177 | 175 | }else{ |
| @@ -270,16 +268,46 @@ | ||
| 270 | 268 | content_get(toid, &to); |
| 271 | 269 | }else{ |
| 272 | 270 | blob_zero(&to); |
| 273 | 271 | } |
| 274 | 272 | blob_zero(&out); |
| 275 | - text_diff(&from, &to, &out, 5, 1); | |
| 273 | + text_diff(&from, &to, &out, DIFF_IGNORE_EOLWS | 5); | |
| 276 | 274 | @ %h(blob_str(&out)) |
| 277 | 275 | blob_reset(&from); |
| 278 | 276 | blob_reset(&to); |
| 279 | 277 | blob_reset(&out); |
| 280 | 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 | + | |
| 281 | 309 | |
| 282 | 310 | /* |
| 283 | 311 | ** Write a line of web-page output that shows changes that have occurred |
| 284 | 312 | ** to a file between two check-ins. |
| 285 | 313 | */ |
| @@ -287,10 +315,11 @@ | ||
| 287 | 315 | const char *zName, /* Name of the file that has changed */ |
| 288 | 316 | const char *zOld, /* blob.uuid before change. NULL for added files */ |
| 289 | 317 | const char *zNew, /* blob.uuid after change. NULL for deletes */ |
| 290 | 318 | const char *zOldName, /* Prior name. NULL if no name change. */ |
| 291 | 319 | int showDiff, /* Show edit diffs if true */ |
| 320 | + int sideBySide, /* Show diffs side-by-side */ | |
| 292 | 321 | int mperm /* executable or symlink permission for zNew */ |
| 293 | 322 | ){ |
| 294 | 323 | if( !g.perm.History ){ |
| 295 | 324 | if( zNew==0 ){ |
| 296 | 325 | @ <p>Deleted %h(zName)</p> |
| @@ -303,13 +332,17 @@ | ||
| 303 | 332 | @ for %h(zName)</p> |
| 304 | 333 | }else{ |
| 305 | 334 | @ <p>Changes to %h(zName)</p> |
| 306 | 335 | } |
| 307 | 336 | 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 | + } | |
| 311 | 344 | } |
| 312 | 345 | }else{ |
| 313 | 346 | if( zOld && zNew ){ |
| 314 | 347 | if( fossil_strcmp(zOld, zNew)!=0 ){ |
| 315 | 348 | @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| @@ -329,13 +362,17 @@ | ||
| 329 | 362 | }else{ |
| 330 | 363 | @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> |
| 331 | 364 | @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a> |
| 332 | 365 | } |
| 333 | 366 | 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 | + } | |
| 337 | 374 | }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ |
| 338 | 375 | @ |
| 339 | 376 | @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&v2=%S(zNew)">[diff]</a> |
| 340 | 377 | } |
| 341 | 378 | @ </p> |
| @@ -361,10 +398,11 @@ | ||
| 361 | 398 | void ci_page(void){ |
| 362 | 399 | Stmt q; |
| 363 | 400 | int rid; |
| 364 | 401 | int isLeaf; |
| 365 | 402 | int showDiff; |
| 403 | + int sideBySide; | |
| 366 | 404 | const char *zName; /* Name of the checkin to be displayed */ |
| 367 | 405 | const char *zUuid; /* UUID of zName */ |
| 368 | 406 | const char *zParent; /* UUID of the parent checkin (if any) */ |
| 369 | 407 | |
| 370 | 408 | login_check_credentials(); |
| @@ -390,10 +428,11 @@ | ||
| 390 | 428 | " FROM blob, event" |
| 391 | 429 | " WHERE blob.rid=%d" |
| 392 | 430 | " AND event.objid=%d", |
| 393 | 431 | rid, rid |
| 394 | 432 | ); |
| 433 | + sideBySide = atoi(PD("sbs","1")); | |
| 395 | 434 | if( db_step(&q)==SQLITE_ROW ){ |
| 396 | 435 | const char *zUuid = db_column_text(&q, 0); |
| 397 | 436 | char *zTitle = mprintf("Check-in [%.10s]", zUuid); |
| 398 | 437 | char *zEUser, *zEComment; |
| 399 | 438 | const char *zUser; |
| @@ -467,11 +506,11 @@ | ||
| 467 | 506 | } |
| 468 | 507 | if( !isLeaf ){ |
| 469 | 508 | @ | <a href="%s(g.zTop)/timeline?d=%S(zUuid)">descendants</a> |
| 470 | 509 | } |
| 471 | 510 | if( zParent && !isLeaf ){ |
| 472 | - @ | <a href="%s(g.zTop)/timeline?d=%S(zUuid)&p=%S(zUuid)">both</a> | |
| 511 | + @ | <a href="%s(g.zTop)/timeline?dp=%S(zUuid)">both</a> | |
| 473 | 512 | } |
| 474 | 513 | db_prepare(&q, "SELECT substr(tag.tagname,5) FROM tagxref, tag " |
| 475 | 514 | " WHERE rid=%d AND tagtype>0 " |
| 476 | 515 | " AND tag.tagid=tagxref.tagid " |
| 477 | 516 | " AND +tag.tagname GLOB 'sym-*'", rid); |
| @@ -506,27 +545,49 @@ | ||
| 506 | 545 | } |
| 507 | 546 | db_finalize(&q); |
| 508 | 547 | showTags(rid, ""); |
| 509 | 548 | if( zParent ){ |
| 510 | 549 | @ <div class="section">Changes</div> |
| 550 | + @ <div class="sectionmenu"> | |
| 511 | 551 | showDiff = g.zPath[0]!='c'; |
| 512 | 552 | if( db_get_boolean("show-version-diffs", 0)==0 ){ |
| 513 | 553 | showDiff = !showDiff; |
| 514 | 554 | if( showDiff ){ |
| 515 | - @ <a href="%s(g.zTop)/vinfo/%T(zName)">[hide diffs]</a> | |
| 555 | + @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)"> | |
| 556 | + @ hide diffs</a> | |
| 557 | + if( sideBySide ){ | |
| 558 | + @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=0"> | |
| 559 | + @ unified diffs</a> | |
| 560 | + }else{ | |
| 561 | + @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=1"> | |
| 562 | + @ side-by-side diffs</a> | |
| 563 | + } | |
| 516 | 564 | }else{ |
| 517 | - @ <a href="%s(g.zTop)/ci/%T(zName)">[show diffs]</a> | |
| 565 | + @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=0"> | |
| 566 | + @ show unified diffs</a> | |
| 567 | + @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=1"> | |
| 568 | + @ show side-by-side diffs</a> | |
| 518 | 569 | } |
| 519 | 570 | }else{ |
| 520 | 571 | if( showDiff ){ |
| 521 | - @ <a href="%s(g.zTop)/ci/%T(zName)">[hide diffs]</a> | |
| 572 | + @ <a class="button" href="%s(g.zTop)/ci/%T(zName)">hide diffs</a> | |
| 573 | + if( sideBySide ){ | |
| 574 | + @ <a class="button" href="%s(g.zTop)/info/%T(zName)?sbs=0"> | |
| 575 | + @ unified diffs</a> | |
| 576 | + }else{ | |
| 577 | + @ <a class="button" href="%s(g.zTop)/info/%T(zName)?sbs=1"> | |
| 578 | + @ side-by-side diffs</a> | |
| 579 | + } | |
| 522 | 580 | }else{ |
| 523 | - @ <a href="%s(g.zTop)/vinfo/%T(zName)">[show diffs]</a> | |
| 581 | + @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)?sbs=0"> | |
| 582 | + @ show unified diffs</a> | |
| 583 | + @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)?sbs=1"> | |
| 584 | + @ show side-by-side diffs</a> | |
| 524 | 585 | } |
| 525 | 586 | } |
| 526 | - @ | |
| 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> | |
| 528 | 589 | db_prepare(&q, |
| 529 | 590 | "SELECT name," |
| 530 | 591 | " mperm," |
| 531 | 592 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 532 | 593 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," |
| @@ -540,11 +601,12 @@ | ||
| 540 | 601 | const char *zName = db_column_text(&q,0); |
| 541 | 602 | int mperm = db_column_int(&q, 1); |
| 542 | 603 | const char *zOld = db_column_text(&q,2); |
| 543 | 604 | const char *zNew = db_column_text(&q,3); |
| 544 | 605 | 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); | |
| 546 | 608 | } |
| 547 | 609 | db_finalize(&q); |
| 548 | 610 | } |
| 549 | 611 | style_footer(); |
| 550 | 612 | } |
| @@ -690,17 +752,18 @@ | ||
| 690 | 752 | } |
| 691 | 753 | |
| 692 | 754 | |
| 693 | 755 | /* |
| 694 | 756 | ** WEBPAGE: vdiff |
| 695 | -** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN | |
| 757 | +** URL: /vdiff?from=UUID&to=UUID&detail=BOOLEAN;sbs=BOOLEAN | |
| 696 | 758 | ** |
| 697 | 759 | ** Show all differences between two checkins. |
| 698 | 760 | */ |
| 699 | 761 | void vdiff_page(void){ |
| 700 | 762 | int ridFrom, ridTo; |
| 701 | 763 | int showDetail = 0; |
| 764 | + int sideBySide = 0; | |
| 702 | 765 | Manifest *pFrom, *pTo; |
| 703 | 766 | ManifestFile *pFileFrom, *pFileTo; |
| 704 | 767 | |
| 705 | 768 | login_check_credentials(); |
| 706 | 769 | if( !g.perm.Read ){ login_needed(); return; } |
| @@ -709,10 +772,20 @@ | ||
| 709 | 772 | pFrom = vdiff_parse_manifest("from", &ridFrom); |
| 710 | 773 | if( pFrom==0 ) return; |
| 711 | 774 | pTo = vdiff_parse_manifest("to", &ridTo); |
| 712 | 775 | if( pTo==0 ) return; |
| 713 | 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 | + } | |
| 714 | 787 | style_header("Check-in Differences"); |
| 715 | 788 | @ <h2>Difference From:</h2><blockquote> |
| 716 | 789 | checkin_description(ridFrom); |
| 717 | 790 | @ </blockquote><h2>To:</h2><blockquote> |
| 718 | 791 | checkin_description(ridTo); |
| @@ -731,25 +804,25 @@ | ||
| 731 | 804 | }else{ |
| 732 | 805 | cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); |
| 733 | 806 | } |
| 734 | 807 | if( cmp<0 ){ |
| 735 | 808 | append_file_change_line(pFileFrom->zName, |
| 736 | - pFileFrom->zUuid, 0, 0, 0, 0); | |
| 809 | + pFileFrom->zUuid, 0, 0, 0, 0, 0); | |
| 737 | 810 | pFileFrom = manifest_file_next(pFrom, 0); |
| 738 | 811 | }else if( cmp>0 ){ |
| 739 | 812 | append_file_change_line(pFileTo->zName, |
| 740 | - 0, pFileTo->zUuid, 0, 0, | |
| 813 | + 0, pFileTo->zUuid, 0, 0, 0, | |
| 741 | 814 | manifest_file_mperm(pFileTo)); |
| 742 | 815 | pFileTo = manifest_file_next(pTo, 0); |
| 743 | 816 | }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ |
| 744 | 817 | /* No changes */ |
| 745 | 818 | pFileFrom = manifest_file_next(pFrom, 0); |
| 746 | 819 | pFileTo = manifest_file_next(pTo, 0); |
| 747 | 820 | }else{ |
| 748 | 821 | append_file_change_line(pFileFrom->zName, |
| 749 | 822 | pFileFrom->zUuid, |
| 750 | - pFileTo->zUuid, 0, showDetail, | |
| 823 | + pFileTo->zUuid, 0, showDetail, sideBySide, | |
| 751 | 824 | manifest_file_mperm(pFileTo)); |
| 752 | 825 | pFileFrom = manifest_file_next(pFrom, 0); |
| 753 | 826 | pFileTo = manifest_file_next(pTo, 0); |
| 754 | 827 | } |
| 755 | 828 | } |
| @@ -983,28 +1056,30 @@ | ||
| 983 | 1056 | } |
| 984 | 1057 | |
| 985 | 1058 | |
| 986 | 1059 | /* |
| 987 | 1060 | ** WEBPAGE: fdiff |
| 988 | -** URL: fdiff?v1=UUID&v2=UUID&patch | |
| 1061 | +** URL: fdiff?v1=UUID&v2=UUID&patch&sbs=BOOLEAN | |
| 989 | 1062 | ** |
| 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. | |
| 993 | 1066 | */ |
| 994 | 1067 | void diff_page(void){ |
| 995 | 1068 | int v1, v2; |
| 996 | 1069 | int isPatch; |
| 1070 | + int sideBySide; | |
| 997 | 1071 | Blob c1, c2, diff, *pOut; |
| 998 | 1072 | char *zV1; |
| 999 | 1073 | char *zV2; |
| 1000 | 1074 | |
| 1001 | 1075 | login_check_credentials(); |
| 1002 | 1076 | if( !g.perm.Read ){ login_needed(); return; } |
| 1003 | 1077 | v1 = name_to_rid_www("v1"); |
| 1004 | 1078 | v2 = name_to_rid_www("v2"); |
| 1005 | 1079 | if( v1==0 || v2==0 ) fossil_redirect_home(); |
| 1080 | + sideBySide = atoi(PD("sbs","1")); | |
| 1006 | 1081 | zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1); |
| 1007 | 1082 | zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2); |
| 1008 | 1083 | isPatch = P("patch")!=0; |
| 1009 | 1084 | if( isPatch ){ |
| 1010 | 1085 | pOut = cgi_output_blob(); |
| @@ -1011,28 +1086,44 @@ | ||
| 1011 | 1086 | cgi_set_content_type("text/plain"); |
| 1012 | 1087 | }else{ |
| 1013 | 1088 | blob_zero(&diff); |
| 1014 | 1089 | pOut = &diff; |
| 1015 | 1090 | } |
| 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 | + } | |
| 1021 | 1098 | if( !isPatch ){ |
| 1022 | 1099 | style_header("Diff"); |
| 1023 | 1100 | style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch", |
| 1024 | 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 | + | |
| 1025 | 1112 | @ <h2>Differences From |
| 1026 | 1113 | @ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2> |
| 1027 | 1114 | object_description(v1, 0, 0); |
| 1028 | 1115 | @ <h2>To Artifact <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2> |
| 1029 | 1116 | object_description(v2, 0, 0); |
| 1030 | 1117 | @ <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 | + } | |
| 1034 | 1125 | blob_reset(&diff); |
| 1035 | 1126 | style_footer(); |
| 1036 | 1127 | } |
| 1037 | 1128 | } |
| 1038 | 1129 | |
| @@ -1584,10 +1675,42 @@ | ||
| 1584 | 1675 | @ value="%h(stdClrFound?"":zDefaultColor)" /> |
| 1585 | 1676 | @ </td> |
| 1586 | 1677 | @ </tr> |
| 1587 | 1678 | @ </table> |
| 1588 | 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 | +} | |
| 1589 | 1712 | |
| 1590 | 1713 | /* |
| 1591 | 1714 | ** WEBPAGE: ci_edit |
| 1592 | 1715 | ** URL: ci_edit?r=RID&c=NEWCOMMENT&u=NEWUSER |
| 1593 | 1716 | ** |
| @@ -1661,11 +1784,12 @@ | ||
| 1661 | 1784 | blob_zero(&ctrl); |
| 1662 | 1785 | zNow = date_in_standard_format("now"); |
| 1663 | 1786 | blob_appendf(&ctrl, "D %s\n", zNow); |
| 1664 | 1787 | db_multi_exec("CREATE TEMP TABLE newtags(tag UNIQUE, prefix, value)"); |
| 1665 | 1788 | if( zNewColor[0] |
| 1666 | - && (fPropagateColor!=fNewPropagateColor || fossil_strcmp(zColor,zNewColor)!=0) | |
| 1789 | + && (fPropagateColor!=fNewPropagateColor | |
| 1790 | + || fossil_strcmp(zColor,zNewColor)!=0) | |
| 1667 | 1791 | ){ |
| 1668 | 1792 | char *zPrefix = "+"; |
| 1669 | 1793 | if( fNewPropagateColor ){ |
| 1670 | 1794 | zPrefix = "*"; |
| 1671 | 1795 | } |
| @@ -1673,11 +1797,11 @@ | ||
| 1673 | 1797 | zPrefix, zNewColor); |
| 1674 | 1798 | } |
| 1675 | 1799 | if( zNewColor[0]==0 && zColor[0]!=0 ){ |
| 1676 | 1800 | db_multi_exec("REPLACE INTO newtags VALUES('bgcolor','-',NULL)"); |
| 1677 | 1801 | } |
| 1678 | - if( fossil_strcmp(zComment,zNewComment)!=0 ){ | |
| 1802 | + if( comment_compare(zComment,zNewComment)==0 ){ | |
| 1679 | 1803 | db_multi_exec("REPLACE INTO newtags VALUES('comment','+',%Q)", |
| 1680 | 1804 | zNewComment); |
| 1681 | 1805 | } |
| 1682 | 1806 | if( fossil_strcmp(zDate,zNewDate)!=0 ){ |
| 1683 | 1807 | db_multi_exec("REPLACE INTO newtags VALUES('date','+',%Q)", |
| 1684 | 1808 | |
| 1685 | 1809 | ADDED src/json.c |
| 1686 | 1810 | ADDED src/json_artifact.c |
| 1687 | 1811 | ADDED src/json_branch.c |
| 1688 | 1812 | ADDED src/json_detail.h |
| 1689 | 1813 | ADDED src/json_diff.c |
| 1690 | 1814 | ADDED src/json_login.c |
| 1691 | 1815 | ADDED src/json_query.c |
| 1692 | 1816 | ADDED src/json_report.c |
| 1693 | 1817 | ADDED src/json_tag.c |
| 1694 | 1818 | ADDED src/json_timeline.c |
| 1695 | 1819 | ADDED src/json_user.c |
| 1696 | 1820 | 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 | @ |
| 339 | @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&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)&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 diffs]</a> |
| 516 | }else{ |
| 517 | @ <a href="%s(g.zTop)/ci/%T(zName)">[show diffs]</a> |
| 518 | } |
| 519 | }else{ |
| 520 | if( showDiff ){ |
| 521 | @ <a href="%s(g.zTop)/ci/%T(zName)">[hide diffs]</a> |
| 522 | }else{ |
| 523 | @ <a href="%s(g.zTop)/vinfo/%T(zName)">[show diffs]</a> |
| 524 | } |
| 525 | } |
| 526 | @ |
| 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&to=UUID&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 | @ |
| 376 | @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&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 diffs</a> |
| 557 | if( sideBySide ){ |
| 558 | @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=0"> |
| 559 | @ unified diffs</a> |
| 560 | }else{ |
| 561 | @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=1"> |
| 562 | @ side-by-side diffs</a> |
| 563 | } |
| 564 | }else{ |
| 565 | @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=0"> |
| 566 | @ show unified diffs</a> |
| 567 | @ <a class="button" href="%s(g.zTop)/ci/%T(zName)?sbs=1"> |
| 568 | @ show side-by-side diffs</a> |
| 569 | } |
| 570 | }else{ |
| 571 | if( showDiff ){ |
| 572 | @ <a class="button" href="%s(g.zTop)/ci/%T(zName)">hide diffs</a> |
| 573 | if( sideBySide ){ |
| 574 | @ <a class="button" href="%s(g.zTop)/info/%T(zName)?sbs=0"> |
| 575 | @ unified diffs</a> |
| 576 | }else{ |
| 577 | @ <a class="button" href="%s(g.zTop)/info/%T(zName)?sbs=1"> |
| 578 | @ side-by-side diffs</a> |
| 579 | } |
| 580 | }else{ |
| 581 | @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)?sbs=0"> |
| 582 | @ show unified diffs</a> |
| 583 | @ <a class="button" href="%s(g.zTop)/vinfo/%T(zName)?sbs=1"> |
| 584 | @ show side-by-side 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&to=UUID&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 |
+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_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 : |
+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_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 |
+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_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( |
+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_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" |
+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_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 |
+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_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 |
+1
| --- 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, |
+1
| --- 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 |
+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_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 |
+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_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 |
+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 |
| --- 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 @@ | ||
| 84 | 84 | ** |
| 85 | 85 | ** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX |
| 86 | 86 | ** where the Xs are the first 16 characters of the login-group-code or |
| 87 | 87 | ** of the project-code if we are not a member of any login-group. |
| 88 | 88 | */ |
| 89 | -static char *login_cookie_name(void){ | |
| 89 | +char *login_cookie_name(void){ | |
| 90 | 90 | static char *zCookieName = 0; |
| 91 | 91 | if( zCookieName==0 ){ |
| 92 | 92 | zCookieName = db_text(0, |
| 93 | 93 | "SELECT 'fossil-' || substr(value,1,16)" |
| 94 | 94 | " FROM config" |
| @@ -117,15 +117,20 @@ | ||
| 117 | 117 | ** But some clients are behind firewalls that shift the IP address |
| 118 | 118 | ** with each HTTP request. To allow such (broken) clients to log in, |
| 119 | 119 | ** extract just a prefix of the IP address. |
| 120 | 120 | */ |
| 121 | 121 | 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"); | |
| 123 | 128 | for(i=j=0; zIP[i]; i++){ |
| 124 | 129 | if( zIP[i]=='.' ){ |
| 125 | 130 | j++; |
| 126 | - if( j==2 ) break; | |
| 131 | + if( j==ip_prefix_terms ) break; | |
| 127 | 132 | } |
| 128 | 133 | } |
| 129 | 134 | return mprintf("%.*s", i, zIP); |
| 130 | 135 | } |
| 131 | 136 | |
| @@ -141,24 +146,26 @@ | ||
| 141 | 146 | |
| 142 | 147 | |
| 143 | 148 | /* |
| 144 | 149 | ** Check to see if the anonymous login is valid. If it is valid, return |
| 145 | 150 | ** the userid of the anonymous user. |
| 151 | +** | |
| 152 | +** The zCS parameter is the "captcha seed" used for a specific | |
| 153 | +** anonymous login request. | |
| 146 | 154 | */ |
| 147 | -static int isValidAnonymousLogin( | |
| 155 | +int login_is_valid_anonymous( | |
| 148 | 156 | 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 */ | |
| 150 | 159 | ){ |
| 151 | - const char *zCS; /* The captcha seed value */ | |
| 152 | 160 | const char *zPw; /* The correct password shown in the captcha */ |
| 153 | 161 | int uid; /* The user ID of anonymous */ |
| 154 | 162 | |
| 155 | 163 | 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; | |
| 160 | 167 | zPw = captcha_decode((unsigned int)atoi(zCS)); |
| 161 | 168 | if( fossil_stricmp(zPw, zPassword)!=0 ) return 0; |
| 162 | 169 | uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" |
| 163 | 170 | " AND length(pw)>0 AND length(cap)>0"); |
| 164 | 171 | return uid; |
| @@ -192,10 +199,225 @@ | ||
| 192 | 199 | "INSERT INTO accesslog(uname,ipaddr,success,mtime)" |
| 193 | 200 | "VALUES(%Q,%Q,%d,julianday('now'));", |
| 194 | 201 | zUsername, zIpAddr, bSuccess |
| 195 | 202 | ); |
| 196 | 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 | +} | |
| 197 | 419 | |
| 198 | 420 | /* |
| 199 | 421 | ** WEBPAGE: login |
| 200 | 422 | ** WEBPAGE: logout |
| 201 | 423 | ** WEBPAGE: my |
| @@ -214,23 +436,24 @@ | ||
| 214 | 436 | int anonFlag; |
| 215 | 437 | char *zErrMsg = ""; |
| 216 | 438 | int uid; /* User id loged in user */ |
| 217 | 439 | char *zSha1Pw; |
| 218 | 440 | const char *zIpAddr; /* IP address of requestor */ |
| 219 | - char *zRemoteAddr; /* Abbreviated IP address of requestor */ | |
| 220 | 441 | |
| 221 | 442 | login_check_credentials(); |
| 443 | + sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, | |
| 444 | + constant_time_cmp_function, 0, 0); | |
| 222 | 445 | zUsername = P("u"); |
| 223 | 446 | zPasswd = P("p"); |
| 224 | 447 | anonFlag = P("anon")!=0; |
| 225 | 448 | 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(); | |
| 229 | 450 | redirect_to_g(); |
| 230 | 451 | } |
| 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 | + ){ | |
| 232 | 455 | /* The user requests a password change */ |
| 233 | 456 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| 234 | 457 | if( db_int(1, "SELECT 0 FROM user" |
| 235 | 458 | " WHERE uid=%d" |
| 236 | 459 | " AND (constant_time_cmp(pw,%Q)=0" |
| @@ -273,50 +496,20 @@ | ||
| 273 | 496 | return; |
| 274 | 497 | } |
| 275 | 498 | } |
| 276 | 499 | } |
| 277 | 500 | 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")); | |
| 280 | 502 | 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); | |
| 303 | 504 | record_login_attempt("anonymous", zIpAddr, 1); |
| 304 | 505 | redirect_to_g(); |
| 305 | 506 | } |
| 306 | 507 | if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){ |
| 307 | 508 | /* Attempting to log in as a user other than anonymous. |
| 308 | 509 | */ |
| 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); | |
| 318 | 511 | if( uid<=0 ){ |
| 319 | 512 | sleep(1); |
| 320 | 513 | zErrMsg = |
| 321 | 514 | @ <p><span class="loginError"> |
| 322 | 515 | @ You entered an unknown user or an incorrect password. |
| @@ -329,26 +522,11 @@ | ||
| 329 | 522 | ** HASH/PROJECT/LOGIN |
| 330 | 523 | ** |
| 331 | 524 | ** where HASH is a random hex number, PROJECT is either project |
| 332 | 525 | ** code prefix, and LOGIN is the user name. |
| 333 | 526 | */ |
| 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); | |
| 350 | 528 | redirect_to_g(); |
| 351 | 529 | } |
| 352 | 530 | } |
| 353 | 531 | style_header("Login/Logout"); |
| 354 | 532 | @ %s(zErrMsg) |
| @@ -454,37 +632,10 @@ | ||
| 454 | 632 | @ </form> |
| 455 | 633 | } |
| 456 | 634 | style_footer(); |
| 457 | 635 | } |
| 458 | 636 | |
| 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 | 637 | /* |
| 487 | 638 | ** Attempt to find login credentials for user zLogin on a peer repository |
| 488 | 639 | ** with project code zCode. Transfer those credentials to the local |
| 489 | 640 | ** repository. |
| 490 | 641 | ** |
| @@ -542,12 +693,16 @@ | ||
| 542 | 693 | fossil_free(zOtherRepo); |
| 543 | 694 | return nXfer; |
| 544 | 695 | } |
| 545 | 696 | |
| 546 | 697 | /* |
| 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. | |
| 549 | 704 | */ |
| 550 | 705 | static int login_find_user( |
| 551 | 706 | const char *zLogin, /* User name */ |
| 552 | 707 | const char *zCookie, /* Login cookie value */ |
| 553 | 708 | const char *zRemoteAddr /* Abbreviated IP address for valid login */ |
| @@ -569,16 +724,14 @@ | ||
| 569 | 724 | ); |
| 570 | 725 | return uid; |
| 571 | 726 | } |
| 572 | 727 | |
| 573 | 728 | /* |
| 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. | |
| 580 | 733 | */ |
| 581 | 734 | void login_check_credentials(void){ |
| 582 | 735 | int uid = 0; /* User id */ |
| 583 | 736 | const char *zCookie; /* Text of the login cookie */ |
| 584 | 737 | const char *zIpAddr; /* Raw IP address of the requestor */ |
| @@ -667,11 +820,11 @@ | ||
| 667 | 820 | } |
| 668 | 821 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash); |
| 669 | 822 | } |
| 670 | 823 | |
| 671 | 824 | /* 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. | |
| 673 | 826 | */ |
| 674 | 827 | if( uid==0 ){ |
| 675 | 828 | const char *zRemoteUser = P("REMOTE_USER"); |
| 676 | 829 | if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){ |
| 677 | 830 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q" |
| @@ -717,12 +870,16 @@ | ||
| 717 | 870 | if( fossil_strcmp(g.zLogin,"nobody")==0 ){ |
| 718 | 871 | g.zLogin = 0; |
| 719 | 872 | } |
| 720 | 873 | |
| 721 | 874 | /* Set the capabilities */ |
| 722 | - login_set_capabilities(zCap, 0); | |
| 875 | + login_replace_capabilities(zCap, 0); | |
| 723 | 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 | + } | |
| 724 | 881 | } |
| 725 | 882 | |
| 726 | 883 | /* |
| 727 | 884 | ** Memory of settings |
| 728 | 885 | */ |
| @@ -746,22 +903,25 @@ | ||
| 746 | 903 | login_anon_once = 0; |
| 747 | 904 | } |
| 748 | 905 | } |
| 749 | 906 | |
| 750 | 907 | /* |
| 751 | -** Flags passed into the 2nd argument of login_set_capabilities(). | |
| 908 | +** Flags passed into the 2nd argument of login_set/replace_capabilities(). | |
| 752 | 909 | */ |
| 753 | 910 | #if INTERFACE |
| 754 | 911 | #define LOGIN_IGNORE_U 0x01 /* Ignore "u" */ |
| 755 | 912 | #define LOGIN_IGNORE_V 0x01 /* Ignore "v" */ |
| 756 | 913 | #endif |
| 757 | 914 | |
| 758 | 915 | /* |
| 759 | -** Set the global capability flags based on a capability string. | |
| 916 | +** Adds all capability flags in zCap to g.perm. | |
| 760 | 917 | */ |
| 761 | 918 | void login_set_capabilities(const char *zCap, unsigned flags){ |
| 762 | 919 | int i; |
| 920 | + if(NULL==zCap){ | |
| 921 | + return; | |
| 922 | + } | |
| 763 | 923 | for(i=0; zCap[i]; i++){ |
| 764 | 924 | switch( zCap[i] ){ |
| 765 | 925 | case 's': g.perm.Setup = 1; /* Fall thru into Admin */ |
| 766 | 926 | case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip = |
| 767 | 927 | g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki = |
| @@ -815,10 +975,18 @@ | ||
| 815 | 975 | break; |
| 816 | 976 | } |
| 817 | 977 | } |
| 818 | 978 | } |
| 819 | 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 | +} | |
| 820 | 988 | |
| 821 | 989 | /* |
| 822 | 990 | ** If the current login lacks any of the capabilities listed in |
| 823 | 991 | ** the input, then return 0. If all capabilities are present, then |
| 824 | 992 | ** return 1. |
| @@ -893,14 +1061,24 @@ | ||
| 893 | 1061 | /* |
| 894 | 1062 | ** Call this routine when the credential check fails. It causes |
| 895 | 1063 | ** a redirect to the "login" page. |
| 896 | 1064 | */ |
| 897 | 1065 | 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 | + } | |
| 902 | 1080 | } |
| 903 | 1081 | |
| 904 | 1082 | /* |
| 905 | 1083 | ** Call this routine if the user lacks okHistory permission. If |
| 906 | 1084 | ** the anonymous user has okHistory permission, then paint a mesage |
| 907 | 1085 |
| --- 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 @@ | ||
| 84 | 84 | ** |
| 85 | 85 | ** The login cookie name is always of the form: fossil-XXXXXXXXXXXXXXXX |
| 86 | 86 | ** where the Xs are the first 16 characters of the login-group-code or |
| 87 | 87 | ** of the project-code if we are not a member of any login-group. |
| 88 | 88 | */ |
| 89 | -static char *login_cookie_name(void){ | |
| 89 | +char *login_cookie_name(void){ | |
| 90 | 90 | static char *zCookieName = 0; |
| 91 | 91 | if( zCookieName==0 ){ |
| 92 | 92 | zCookieName = db_text(0, |
| 93 | 93 | "SELECT 'fossil-' || substr(value,1,16)" |
| 94 | 94 | " FROM config" |
| @@ -117,15 +117,20 @@ | ||
| 117 | 117 | ** But some clients are behind firewalls that shift the IP address |
| 118 | 118 | ** with each HTTP request. To allow such (broken) clients to log in, |
| 119 | 119 | ** extract just a prefix of the IP address. |
| 120 | 120 | */ |
| 121 | 121 | 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"); | |
| 123 | 128 | for(i=j=0; zIP[i]; i++){ |
| 124 | 129 | if( zIP[i]=='.' ){ |
| 125 | 130 | j++; |
| 126 | - if( j==2 ) break; | |
| 131 | + if( j==ip_prefix_terms ) break; | |
| 127 | 132 | } |
| 128 | 133 | } |
| 129 | 134 | return mprintf("%.*s", i, zIP); |
| 130 | 135 | } |
| 131 | 136 | |
| @@ -141,24 +146,26 @@ | ||
| 141 | 146 | |
| 142 | 147 | |
| 143 | 148 | /* |
| 144 | 149 | ** Check to see if the anonymous login is valid. If it is valid, return |
| 145 | 150 | ** the userid of the anonymous user. |
| 151 | +** | |
| 152 | +** The zCS parameter is the "captcha seed" used for a specific | |
| 153 | +** anonymous login request. | |
| 146 | 154 | */ |
| 147 | -static int isValidAnonymousLogin( | |
| 155 | +int login_is_valid_anonymous( | |
| 148 | 156 | 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 */ | |
| 150 | 159 | ){ |
| 151 | - const char *zCS; /* The captcha seed value */ | |
| 152 | 160 | const char *zPw; /* The correct password shown in the captcha */ |
| 153 | 161 | int uid; /* The user ID of anonymous */ |
| 154 | 162 | |
| 155 | 163 | 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; | |
| 160 | 167 | zPw = captcha_decode((unsigned int)atoi(zCS)); |
| 161 | 168 | if( fossil_stricmp(zPw, zPassword)!=0 ) return 0; |
| 162 | 169 | uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'" |
| 163 | 170 | " AND length(pw)>0 AND length(cap)>0"); |
| 164 | 171 | return uid; |
| @@ -192,10 +199,225 @@ | ||
| 192 | 199 | "INSERT INTO accesslog(uname,ipaddr,success,mtime)" |
| 193 | 200 | "VALUES(%Q,%Q,%d,julianday('now'));", |
| 194 | 201 | zUsername, zIpAddr, bSuccess |
| 195 | 202 | ); |
| 196 | 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 | +} | |
| 197 | 419 | |
| 198 | 420 | /* |
| 199 | 421 | ** WEBPAGE: login |
| 200 | 422 | ** WEBPAGE: logout |
| 201 | 423 | ** WEBPAGE: my |
| @@ -214,23 +436,24 @@ | ||
| 214 | 436 | int anonFlag; |
| 215 | 437 | char *zErrMsg = ""; |
| 216 | 438 | int uid; /* User id loged in user */ |
| 217 | 439 | char *zSha1Pw; |
| 218 | 440 | const char *zIpAddr; /* IP address of requestor */ |
| 219 | - char *zRemoteAddr; /* Abbreviated IP address of requestor */ | |
| 220 | 441 | |
| 221 | 442 | login_check_credentials(); |
| 443 | + sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, | |
| 444 | + constant_time_cmp_function, 0, 0); | |
| 222 | 445 | zUsername = P("u"); |
| 223 | 446 | zPasswd = P("p"); |
| 224 | 447 | anonFlag = P("anon")!=0; |
| 225 | 448 | 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(); | |
| 229 | 450 | redirect_to_g(); |
| 230 | 451 | } |
| 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 | + ){ | |
| 232 | 455 | /* The user requests a password change */ |
| 233 | 456 | zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0); |
| 234 | 457 | if( db_int(1, "SELECT 0 FROM user" |
| 235 | 458 | " WHERE uid=%d" |
| 236 | 459 | " AND (constant_time_cmp(pw,%Q)=0" |
| @@ -273,50 +496,20 @@ | ||
| 273 | 496 | return; |
| 274 | 497 | } |
| 275 | 498 | } |
| 276 | 499 | } |
| 277 | 500 | 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")); | |
| 280 | 502 | 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); | |
| 303 | 504 | record_login_attempt("anonymous", zIpAddr, 1); |
| 304 | 505 | redirect_to_g(); |
| 305 | 506 | } |
| 306 | 507 | if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){ |
| 307 | 508 | /* Attempting to log in as a user other than anonymous. |
| 308 | 509 | */ |
| 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); | |
| 318 | 511 | if( uid<=0 ){ |
| 319 | 512 | sleep(1); |
| 320 | 513 | zErrMsg = |
| 321 | 514 | @ <p><span class="loginError"> |
| 322 | 515 | @ You entered an unknown user or an incorrect password. |
| @@ -329,26 +522,11 @@ | ||
| 329 | 522 | ** HASH/PROJECT/LOGIN |
| 330 | 523 | ** |
| 331 | 524 | ** where HASH is a random hex number, PROJECT is either project |
| 332 | 525 | ** code prefix, and LOGIN is the user name. |
| 333 | 526 | */ |
| 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); | |
| 350 | 528 | redirect_to_g(); |
| 351 | 529 | } |
| 352 | 530 | } |
| 353 | 531 | style_header("Login/Logout"); |
| 354 | 532 | @ %s(zErrMsg) |
| @@ -454,37 +632,10 @@ | ||
| 454 | 632 | @ </form> |
| 455 | 633 | } |
| 456 | 634 | style_footer(); |
| 457 | 635 | } |
| 458 | 636 | |
| 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 | 637 | /* |
| 487 | 638 | ** Attempt to find login credentials for user zLogin on a peer repository |
| 488 | 639 | ** with project code zCode. Transfer those credentials to the local |
| 489 | 640 | ** repository. |
| 490 | 641 | ** |
| @@ -542,12 +693,16 @@ | ||
| 542 | 693 | fossil_free(zOtherRepo); |
| 543 | 694 | return nXfer; |
| 544 | 695 | } |
| 545 | 696 | |
| 546 | 697 | /* |
| 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. | |
| 549 | 704 | */ |
| 550 | 705 | static int login_find_user( |
| 551 | 706 | const char *zLogin, /* User name */ |
| 552 | 707 | const char *zCookie, /* Login cookie value */ |
| 553 | 708 | const char *zRemoteAddr /* Abbreviated IP address for valid login */ |
| @@ -569,16 +724,14 @@ | ||
| 569 | 724 | ); |
| 570 | 725 | return uid; |
| 571 | 726 | } |
| 572 | 727 | |
| 573 | 728 | /* |
| 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. | |
| 580 | 733 | */ |
| 581 | 734 | void login_check_credentials(void){ |
| 582 | 735 | int uid = 0; /* User id */ |
| 583 | 736 | const char *zCookie; /* Text of the login cookie */ |
| 584 | 737 | const char *zIpAddr; /* Raw IP address of the requestor */ |
| @@ -667,11 +820,11 @@ | ||
| 667 | 820 | } |
| 668 | 821 | sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "%.10s", zHash); |
| 669 | 822 | } |
| 670 | 823 | |
| 671 | 824 | /* 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. | |
| 673 | 826 | */ |
| 674 | 827 | if( uid==0 ){ |
| 675 | 828 | const char *zRemoteUser = P("REMOTE_USER"); |
| 676 | 829 | if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){ |
| 677 | 830 | uid = db_int(0, "SELECT uid FROM user WHERE login=%Q" |
| @@ -717,12 +870,16 @@ | ||
| 717 | 870 | if( fossil_strcmp(g.zLogin,"nobody")==0 ){ |
| 718 | 871 | g.zLogin = 0; |
| 719 | 872 | } |
| 720 | 873 | |
| 721 | 874 | /* Set the capabilities */ |
| 722 | - login_set_capabilities(zCap, 0); | |
| 875 | + login_replace_capabilities(zCap, 0); | |
| 723 | 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 | + } | |
| 724 | 881 | } |
| 725 | 882 | |
| 726 | 883 | /* |
| 727 | 884 | ** Memory of settings |
| 728 | 885 | */ |
| @@ -746,22 +903,25 @@ | ||
| 746 | 903 | login_anon_once = 0; |
| 747 | 904 | } |
| 748 | 905 | } |
| 749 | 906 | |
| 750 | 907 | /* |
| 751 | -** Flags passed into the 2nd argument of login_set_capabilities(). | |
| 908 | +** Flags passed into the 2nd argument of login_set/replace_capabilities(). | |
| 752 | 909 | */ |
| 753 | 910 | #if INTERFACE |
| 754 | 911 | #define LOGIN_IGNORE_U 0x01 /* Ignore "u" */ |
| 755 | 912 | #define LOGIN_IGNORE_V 0x01 /* Ignore "v" */ |
| 756 | 913 | #endif |
| 757 | 914 | |
| 758 | 915 | /* |
| 759 | -** Set the global capability flags based on a capability string. | |
| 916 | +** Adds all capability flags in zCap to g.perm. | |
| 760 | 917 | */ |
| 761 | 918 | void login_set_capabilities(const char *zCap, unsigned flags){ |
| 762 | 919 | int i; |
| 920 | + if(NULL==zCap){ | |
| 921 | + return; | |
| 922 | + } | |
| 763 | 923 | for(i=0; zCap[i]; i++){ |
| 764 | 924 | switch( zCap[i] ){ |
| 765 | 925 | case 's': g.perm.Setup = 1; /* Fall thru into Admin */ |
| 766 | 926 | case 'a': g.perm.Admin = g.perm.RdTkt = g.perm.WrTkt = g.perm.Zip = |
| 767 | 927 | g.perm.RdWiki = g.perm.WrWiki = g.perm.NewWiki = |
| @@ -815,10 +975,18 @@ | ||
| 815 | 975 | break; |
| 816 | 976 | } |
| 817 | 977 | } |
| 818 | 978 | } |
| 819 | 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 | +} | |
| 820 | 988 | |
| 821 | 989 | /* |
| 822 | 990 | ** If the current login lacks any of the capabilities listed in |
| 823 | 991 | ** the input, then return 0. If all capabilities are present, then |
| 824 | 992 | ** return 1. |
| @@ -893,14 +1061,24 @@ | ||
| 893 | 1061 | /* |
| 894 | 1062 | ** Call this routine when the credential check fails. It causes |
| 895 | 1063 | ** a redirect to the "login" page. |
| 896 | 1064 | */ |
| 897 | 1065 | 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 | + } | |
| 902 | 1080 | } |
| 903 | 1081 | |
| 904 | 1082 | /* |
| 905 | 1083 | ** Call this routine if the user lacks okHistory permission. If |
| 906 | 1084 | ** the anonymous user has okHistory permission, then paint a mesage |
| 907 | 1085 |
| --- 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 @@ | ||
| 23 | 23 | #include <string.h> |
| 24 | 24 | #include <time.h> |
| 25 | 25 | #include <fcntl.h> |
| 26 | 26 | #include <sys/types.h> |
| 27 | 27 | #include <sys/stat.h> |
| 28 | - | |
| 28 | +#include <stdlib.h> /* atexit() */ | |
| 29 | 29 | |
| 30 | 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 | |
| 31 | 38 | |
| 32 | 39 | /* |
| 33 | 40 | ** Number of elements in an array |
| 34 | 41 | */ |
| 35 | 42 | #define count(X) (sizeof(X)/sizeof(X[0])) |
| @@ -69,10 +76,23 @@ | ||
| 69 | 76 | char TktFmt; /* t: create new ticket report formats */ |
| 70 | 77 | char RdAddr; /* e: read email addresses or other private data */ |
| 71 | 78 | char Zip; /* z: download zipped artifact via /zip URL */ |
| 72 | 79 | char Private; /* x: can send and receive private content */ |
| 73 | 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 | |
| 74 | 94 | |
| 75 | 95 | /* |
| 76 | 96 | ** All global variables are in this structure. |
| 77 | 97 | */ |
| 78 | 98 | struct Global { |
| @@ -117,10 +137,11 @@ | ||
| 117 | 137 | int xlinkClusterOnly; /* Set when cloning. Only process clusters */ |
| 118 | 138 | int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ |
| 119 | 139 | int *aCommitFile; /* Array of files to be committed */ |
| 120 | 140 | int markPrivate; /* All new artifacts are private if true */ |
| 121 | 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. */ | |
| 122 | 143 | |
| 123 | 144 | int urlIsFile; /* True if a "file:" url */ |
| 124 | 145 | int urlIsHttps; /* True if a "https:" url */ |
| 125 | 146 | int urlIsSsh; /* True if an "ssh:" url */ |
| 126 | 147 | char *urlName; /* Hostname for http: or filename for file: */ |
| @@ -133,11 +154,11 @@ | ||
| 133 | 154 | char *urlPasswd; /* Password for http: */ |
| 134 | 155 | char *urlCanonical; /* Canonical representation of the URL */ |
| 135 | 156 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 136 | 157 | char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ |
| 137 | 158 | int dontKeepUrl; /* Do not persist the URL */ |
| 138 | - | |
| 159 | + | |
| 139 | 160 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 140 | 161 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ |
| 141 | 162 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 142 | 163 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 143 | 164 | int userUid; /* Integer user id */ |
| @@ -147,10 +168,15 @@ | ||
| 147 | 168 | char *zIpAddr; /* The remote IP address */ |
| 148 | 169 | char *zNonce; /* The nonce used for login */ |
| 149 | 170 | |
| 150 | 171 | /* permissions used by the server */ |
| 151 | 172 | struct FossilUserPerms perm; |
| 173 | + | |
| 174 | +#ifdef FOSSIL_ENABLE_TCL | |
| 175 | + /* all Tcl related context necessary for integration */ | |
| 176 | + struct TclContext tcl; | |
| 177 | +#endif | |
| 152 | 178 | |
| 153 | 179 | /* For defense against Cross-site Request Forgery attacks */ |
| 154 | 180 | char zCsrfToken[12]; /* Value of the anti-CSRF token */ |
| 155 | 181 | int okCsrf; /* Anti-CSRF token is present and valid */ |
| 156 | 182 | |
| @@ -168,10 +194,66 @@ | ||
| 168 | 194 | const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */ |
| 169 | 195 | const char **azAuxOpt[MX_AUX]; /* Options of each option() value */ |
| 170 | 196 | int anAuxCols[MX_AUX]; /* Number of columns for option() values */ |
| 171 | 197 | |
| 172 | 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 */ | |
| 173 | 255 | }; |
| 174 | 256 | |
| 175 | 257 | /* |
| 176 | 258 | ** Macro for debugging: |
| 177 | 259 | */ |
| @@ -232,10 +314,25 @@ | ||
| 232 | 314 | *pIndex = m; |
| 233 | 315 | return 0; |
| 234 | 316 | } |
| 235 | 317 | return 1+(cnt>1); |
| 236 | 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 | +} | |
| 237 | 334 | |
| 238 | 335 | /* |
| 239 | 336 | ** Search g.argv for arguments "--args FILENAME". If found, then |
| 240 | 337 | ** (1) remove the two arguments from g.argv |
| 241 | 338 | ** (2) Read the file FILENAME |
| @@ -315,27 +412,52 @@ | ||
| 315 | 412 | int main(int argc, char **argv){ |
| 316 | 413 | const char *zCmdName = "unknown"; |
| 317 | 414 | int idx; |
| 318 | 415 | int rc; |
| 319 | 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 | |
| 320 | 423 | |
| 321 | 424 | sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); |
| 425 | + memset(&g, 0, sizeof(g)); | |
| 322 | 426 | g.now = time(0); |
| 323 | 427 | g.argc = argc; |
| 324 | 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 */ | |
| 325 | 442 | expand_args_option(); |
| 326 | 443 | argc = g.argc; |
| 327 | 444 | argv = g.argv; |
| 328 | 445 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 329 | 446 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 330 | 447 | zCmdName = "cgi"; |
| 448 | + g.isHTTP = 1; | |
| 331 | 449 | }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); | |
| 336 | 457 | }else{ |
| 458 | + g.isHTTP = 0; | |
| 337 | 459 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 338 | 460 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 339 | 461 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| 340 | 462 | g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; |
| 341 | 463 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| @@ -370,15 +492,17 @@ | ||
| 370 | 492 | for(i=0; i<count(aCommand); i++){ |
| 371 | 493 | if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ |
| 372 | 494 | blob_appendf(&couldbe, " %s", aCommand[i].zName); |
| 373 | 495 | } |
| 374 | 496 | } |
| 375 | - fossil_fatal("%s: ambiguous command prefix: %s\n" | |
| 497 | + fossil_print("%s: ambiguous command prefix: %s\n" | |
| 376 | 498 | "%s: could be any of:%s\n" |
| 377 | 499 | "%s: use \"help\" for more information\n", |
| 378 | 500 | argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]); |
| 501 | + fossil_exit(1); | |
| 379 | 502 | } |
| 503 | + atexit( fossil_atexit ); | |
| 380 | 504 | aCommand[idx].xFunc(); |
| 381 | 505 | fossil_exit(0); |
| 382 | 506 | /*NOT_REACHED*/ |
| 383 | 507 | return 0; |
| 384 | 508 | } |
| @@ -414,43 +538,70 @@ | ||
| 414 | 538 | ** routines never return. |
| 415 | 539 | */ |
| 416 | 540 | NORETURN void fossil_panic(const char *zFormat, ...){ |
| 417 | 541 | char *z; |
| 418 | 542 | va_list ap; |
| 543 | + int rc = 1; | |
| 419 | 544 | static int once = 1; |
| 420 | 545 | mainInFatalError = 1; |
| 421 | 546 | va_start(ap, zFormat); |
| 422 | 547 | z = vmprintf(zFormat, ap); |
| 423 | 548 | 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); | |
| 432 | 569 | db_force_rollback(); |
| 433 | - fossil_exit(1); | |
| 570 | + fossil_exit(rc); | |
| 434 | 571 | } |
| 572 | + | |
| 435 | 573 | NORETURN void fossil_fatal(const char *zFormat, ...){ |
| 436 | 574 | char *z; |
| 575 | + int rc = 1; | |
| 437 | 576 | va_list ap; |
| 438 | 577 | mainInFatalError = 1; |
| 439 | 578 | va_start(ap, zFormat); |
| 440 | 579 | z = vmprintf(zFormat, ap); |
| 441 | 580 | 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); | |
| 450 | 601 | db_force_rollback(); |
| 451 | - fossil_exit(1); | |
| 602 | + fossil_exit(rc); | |
| 452 | 603 | } |
| 453 | 604 | |
| 454 | 605 | /* This routine works like fossil_fatal() except that if called |
| 455 | 606 | ** recursively, the recursive call is a no-op. |
| 456 | 607 | ** |
| @@ -461,25 +612,37 @@ | ||
| 461 | 612 | ** be prepared for this routine to return. |
| 462 | 613 | */ |
| 463 | 614 | void fossil_fatal_recursive(const char *zFormat, ...){ |
| 464 | 615 | char *z; |
| 465 | 616 | va_list ap; |
| 617 | + int rc = 1; | |
| 466 | 618 | if( mainInFatalError ) return; |
| 467 | 619 | mainInFatalError = 1; |
| 468 | 620 | va_start(ap, zFormat); |
| 469 | 621 | z = vmprintf(zFormat, ap); |
| 470 | 622 | 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 | + } | |
| 478 | 641 | } |
| 479 | 642 | db_force_rollback(); |
| 480 | - fossil_exit(1); | |
| 643 | + fossil_exit(rc); | |
| 481 | 644 | } |
| 482 | 645 | |
| 483 | 646 | |
| 484 | 647 | /* Print a warning message */ |
| 485 | 648 | void fossil_warning(const char *zFormat, ...){ |
| @@ -486,17 +649,25 @@ | ||
| 486 | 649 | char *z; |
| 487 | 650 | va_list ap; |
| 488 | 651 | va_start(ap, zFormat); |
| 489 | 652 | z = vmprintf(zFormat, ap); |
| 490 | 653 | 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 | + } | |
| 497 | 667 | } |
| 668 | + free(z); | |
| 498 | 669 | } |
| 499 | 670 | |
| 500 | 671 | /* |
| 501 | 672 | ** Malloc and free routines that cannot fail |
| 502 | 673 | */ |
| @@ -695,40 +866,22 @@ | ||
| 695 | 866 | } |
| 696 | 867 | |
| 697 | 868 | /* |
| 698 | 869 | ** List of commands starting with zPrefix, or all commands if zPrefix is NULL. |
| 699 | 870 | */ |
| 700 | -static void cmd_cmd_list(const char *zPrefix){ | |
| 871 | +static void command_list(const char *zPrefix, int cmdMask){ | |
| 701 | 872 | int i, nCmd; |
| 702 | 873 | int nPrefix = zPrefix ? strlen(zPrefix) : 0; |
| 703 | 874 | const char *aCmd[count(aCommand)]; |
| 704 | 875 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 705 | 876 | const char *z = aCommand[i].zName; |
| 706 | - if( memcmp(z,"test",4)==0 ) continue; | |
| 877 | + if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue; | |
| 707 | 878 | if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue; |
| 708 | 879 | aCmd[nCmd++] = aCommand[i].zName; |
| 709 | 880 | } |
| 710 | 881 | multi_column_list(aCmd, nCmd); |
| 711 | 882 | } |
| 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 | 883 | |
| 731 | 884 | /* |
| 732 | 885 | ** COMMAND: test-list-webpage |
| 733 | 886 | ** |
| 734 | 887 | ** List all web pages |
| @@ -739,11 +892,10 @@ | ||
| 739 | 892 | for(i=nCmd=0; i<count(aWebpage); i++){ |
| 740 | 893 | aCmd[nCmd++] = aWebpage[i].zName; |
| 741 | 894 | } |
| 742 | 895 | multi_column_list(aCmd, nCmd); |
| 743 | 896 | } |
| 744 | - | |
| 745 | 897 | |
| 746 | 898 | /* |
| 747 | 899 | ** COMMAND: version |
| 748 | 900 | ** |
| 749 | 901 | ** Usage: %fossil version |
| @@ -758,32 +910,54 @@ | ||
| 758 | 910 | |
| 759 | 911 | /* |
| 760 | 912 | ** COMMAND: help |
| 761 | 913 | ** |
| 762 | 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: | |
| 763 | 919 | ** |
| 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 | |
| 765 | 924 | */ |
| 766 | 925 | void help_cmd(void){ |
| 767 | 926 | int rc, idx; |
| 768 | 927 | const char *z; |
| 769 | 928 | 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); | |
| 773 | 935 | version_cmd(); |
| 774 | 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; | |
| 775 | 949 | } |
| 776 | 950 | rc = name_search(g.argv[2], aCommand, count(aCommand), &idx); |
| 777 | 951 | if( rc==1 ){ |
| 778 | 952 | fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]); |
| 779 | - cmd_cmd_list(0); | |
| 953 | + command_list(0, 0xff); | |
| 780 | 954 | fossil_exit(1); |
| 781 | 955 | }else if( rc==2 ){ |
| 782 | 956 | fossil_print("ambiguous command prefix: %s\nMatching commands:\n", |
| 783 | 957 | g.argv[2]); |
| 784 | - cmd_cmd_list(g.argv[2]); | |
| 958 | + command_list(g.argv[2], 0xff); | |
| 785 | 959 | fossil_exit(1); |
| 786 | 960 | } |
| 787 | 961 | z = aCmdHelp[idx]; |
| 788 | 962 | if( z==0 ){ |
| 789 | 963 | fossil_fatal("no help available for the %s command", |
| @@ -1033,10 +1207,16 @@ | ||
| 1033 | 1207 | |
| 1034 | 1208 | if( szFile<1024 ){ |
| 1035 | 1209 | if( zNotFound ){ |
| 1036 | 1210 | cgi_redirect(zNotFound); |
| 1037 | 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 | |
| 1038 | 1218 | @ <h1>Not Found</h1> |
| 1039 | 1219 | cgi_set_status(404, "not found"); |
| 1040 | 1220 | cgi_reply(); |
| 1041 | 1221 | } |
| 1042 | 1222 | return; |
| @@ -1064,11 +1244,17 @@ | ||
| 1064 | 1244 | zPathInfo = "/xfer"; |
| 1065 | 1245 | } |
| 1066 | 1246 | set_base_url(); |
| 1067 | 1247 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1068 | 1248 | || (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*/; | |
| 1070 | 1256 | }else{ |
| 1071 | 1257 | zPath = mprintf("%s", zPathInfo); |
| 1072 | 1258 | } |
| 1073 | 1259 | |
| 1074 | 1260 | /* Make g.zPath point to the first element of the path. Make |
| @@ -1125,10 +1311,12 @@ | ||
| 1125 | 1311 | break; |
| 1126 | 1312 | } |
| 1127 | 1313 | if( g.zExtra ){ |
| 1128 | 1314 | /* CGI parameters get this treatment elsewhere, but places like getfile |
| 1129 | 1315 | ** will use g.zExtra directly. |
| 1316 | + ** Reminder: the login mechanism uses 'name' differently, and may | |
| 1317 | + ** eventually have a problem/collision with this. | |
| 1130 | 1318 | */ |
| 1131 | 1319 | dehttpize(g.zExtra); |
| 1132 | 1320 | cgi_set_parameter_nocopy("name", g.zExtra); |
| 1133 | 1321 | } |
| 1134 | 1322 | |
| @@ -1135,17 +1323,31 @@ | ||
| 1135 | 1323 | /* Locate the method specified by the path and execute the function |
| 1136 | 1324 | ** that implements that method. |
| 1137 | 1325 | */ |
| 1138 | 1326 | if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) && |
| 1139 | 1327 | 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 | + } | |
| 1143 | 1338 | }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 | + } | |
| 1147 | 1349 | }else{ |
| 1148 | 1350 | aWebpage[idx].xFunc(); |
| 1149 | 1351 | } |
| 1150 | 1352 | |
| 1151 | 1353 | /* Return the result. |
| @@ -1152,11 +1354,11 @@ | ||
| 1152 | 1354 | */ |
| 1153 | 1355 | cgi_reply(); |
| 1154 | 1356 | } |
| 1155 | 1357 | |
| 1156 | 1358 | /* |
| 1157 | -** COMMAND: cgi | |
| 1359 | +** COMMAND: cgi* | |
| 1158 | 1360 | ** |
| 1159 | 1361 | ** Usage: %fossil ?cgi? SCRIPT |
| 1160 | 1362 | ** |
| 1161 | 1363 | ** The SCRIPT argument is the name of a file that is the CGI script |
| 1162 | 1364 | ** that is being run. The command name, "cgi", may be omitted if |
| @@ -1339,11 +1541,11 @@ | ||
| 1339 | 1541 | ** |
| 1340 | 1542 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| 1341 | 1543 | ** |
| 1342 | 1544 | ** The argv==6 form is used by the win32 server only. |
| 1343 | 1545 | ** |
| 1344 | -** COMMAND: http | |
| 1546 | +** COMMAND: http* | |
| 1345 | 1547 | ** |
| 1346 | 1548 | ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https] |
| 1347 | 1549 | ** |
| 1348 | 1550 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 1349 | 1551 | ** is delivered on stdout. This method is used to launch an HTTP request |
| @@ -1440,11 +1642,11 @@ | ||
| 1440 | 1642 | } |
| 1441 | 1643 | #endif |
| 1442 | 1644 | #endif |
| 1443 | 1645 | |
| 1444 | 1646 | /* |
| 1445 | -** COMMAND: server | |
| 1647 | +** COMMAND: server* | |
| 1446 | 1648 | ** COMMAND: ui |
| 1447 | 1649 | ** |
| 1448 | 1650 | ** Usage: %fossil server ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY? |
| 1449 | 1651 | ** Or: %fossil ui ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY? |
| 1450 | 1652 | ** |
| 1451 | 1653 |
| --- 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 @@ | ||
| 23 | 23 | #include <string.h> |
| 24 | 24 | #include <time.h> |
| 25 | 25 | #include <fcntl.h> |
| 26 | 26 | #include <sys/types.h> |
| 27 | 27 | #include <sys/stat.h> |
| 28 | - | |
| 28 | +#include <stdlib.h> /* atexit() */ | |
| 29 | 29 | |
| 30 | 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 | |
| 31 | 38 | |
| 32 | 39 | /* |
| 33 | 40 | ** Number of elements in an array |
| 34 | 41 | */ |
| 35 | 42 | #define count(X) (sizeof(X)/sizeof(X[0])) |
| @@ -69,10 +76,23 @@ | ||
| 69 | 76 | char TktFmt; /* t: create new ticket report formats */ |
| 70 | 77 | char RdAddr; /* e: read email addresses or other private data */ |
| 71 | 78 | char Zip; /* z: download zipped artifact via /zip URL */ |
| 72 | 79 | char Private; /* x: can send and receive private content */ |
| 73 | 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 | |
| 74 | 94 | |
| 75 | 95 | /* |
| 76 | 96 | ** All global variables are in this structure. |
| 77 | 97 | */ |
| 78 | 98 | struct Global { |
| @@ -117,10 +137,11 @@ | ||
| 117 | 137 | int xlinkClusterOnly; /* Set when cloning. Only process clusters */ |
| 118 | 138 | int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ |
| 119 | 139 | int *aCommitFile; /* Array of files to be committed */ |
| 120 | 140 | int markPrivate; /* All new artifacts are private if true */ |
| 121 | 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. */ | |
| 122 | 143 | |
| 123 | 144 | int urlIsFile; /* True if a "file:" url */ |
| 124 | 145 | int urlIsHttps; /* True if a "https:" url */ |
| 125 | 146 | int urlIsSsh; /* True if an "ssh:" url */ |
| 126 | 147 | char *urlName; /* Hostname for http: or filename for file: */ |
| @@ -133,11 +154,11 @@ | ||
| 133 | 154 | char *urlPasswd; /* Password for http: */ |
| 134 | 155 | char *urlCanonical; /* Canonical representation of the URL */ |
| 135 | 156 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 136 | 157 | char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ |
| 137 | 158 | int dontKeepUrl; /* Do not persist the URL */ |
| 138 | - | |
| 159 | + | |
| 139 | 160 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 140 | 161 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ |
| 141 | 162 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 142 | 163 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 143 | 164 | int userUid; /* Integer user id */ |
| @@ -147,10 +168,15 @@ | ||
| 147 | 168 | char *zIpAddr; /* The remote IP address */ |
| 148 | 169 | char *zNonce; /* The nonce used for login */ |
| 149 | 170 | |
| 150 | 171 | /* permissions used by the server */ |
| 151 | 172 | struct FossilUserPerms perm; |
| 173 | + | |
| 174 | +#ifdef FOSSIL_ENABLE_TCL | |
| 175 | + /* all Tcl related context necessary for integration */ | |
| 176 | + struct TclContext tcl; | |
| 177 | +#endif | |
| 152 | 178 | |
| 153 | 179 | /* For defense against Cross-site Request Forgery attacks */ |
| 154 | 180 | char zCsrfToken[12]; /* Value of the anti-CSRF token */ |
| 155 | 181 | int okCsrf; /* Anti-CSRF token is present and valid */ |
| 156 | 182 | |
| @@ -168,10 +194,66 @@ | ||
| 168 | 194 | const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */ |
| 169 | 195 | const char **azAuxOpt[MX_AUX]; /* Options of each option() value */ |
| 170 | 196 | int anAuxCols[MX_AUX]; /* Number of columns for option() values */ |
| 171 | 197 | |
| 172 | 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 */ | |
| 173 | 255 | }; |
| 174 | 256 | |
| 175 | 257 | /* |
| 176 | 258 | ** Macro for debugging: |
| 177 | 259 | */ |
| @@ -232,10 +314,25 @@ | ||
| 232 | 314 | *pIndex = m; |
| 233 | 315 | return 0; |
| 234 | 316 | } |
| 235 | 317 | return 1+(cnt>1); |
| 236 | 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 | +} | |
| 237 | 334 | |
| 238 | 335 | /* |
| 239 | 336 | ** Search g.argv for arguments "--args FILENAME". If found, then |
| 240 | 337 | ** (1) remove the two arguments from g.argv |
| 241 | 338 | ** (2) Read the file FILENAME |
| @@ -315,27 +412,52 @@ | ||
| 315 | 412 | int main(int argc, char **argv){ |
| 316 | 413 | const char *zCmdName = "unknown"; |
| 317 | 414 | int idx; |
| 318 | 415 | int rc; |
| 319 | 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 | |
| 320 | 423 | |
| 321 | 424 | sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); |
| 425 | + memset(&g, 0, sizeof(g)); | |
| 322 | 426 | g.now = time(0); |
| 323 | 427 | g.argc = argc; |
| 324 | 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 */ | |
| 325 | 442 | expand_args_option(); |
| 326 | 443 | argc = g.argc; |
| 327 | 444 | argv = g.argv; |
| 328 | 445 | for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]); |
| 329 | 446 | if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){ |
| 330 | 447 | zCmdName = "cgi"; |
| 448 | + g.isHTTP = 1; | |
| 331 | 449 | }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); | |
| 336 | 457 | }else{ |
| 458 | + g.isHTTP = 0; | |
| 337 | 459 | g.fQuiet = find_option("quiet", 0, 0)!=0; |
| 338 | 460 | g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; |
| 339 | 461 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| 340 | 462 | g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; |
| 341 | 463 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| @@ -370,15 +492,17 @@ | ||
| 370 | 492 | for(i=0; i<count(aCommand); i++){ |
| 371 | 493 | if( memcmp(zCmdName, aCommand[i].zName, n)==0 ){ |
| 372 | 494 | blob_appendf(&couldbe, " %s", aCommand[i].zName); |
| 373 | 495 | } |
| 374 | 496 | } |
| 375 | - fossil_fatal("%s: ambiguous command prefix: %s\n" | |
| 497 | + fossil_print("%s: ambiguous command prefix: %s\n" | |
| 376 | 498 | "%s: could be any of:%s\n" |
| 377 | 499 | "%s: use \"help\" for more information\n", |
| 378 | 500 | argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]); |
| 501 | + fossil_exit(1); | |
| 379 | 502 | } |
| 503 | + atexit( fossil_atexit ); | |
| 380 | 504 | aCommand[idx].xFunc(); |
| 381 | 505 | fossil_exit(0); |
| 382 | 506 | /*NOT_REACHED*/ |
| 383 | 507 | return 0; |
| 384 | 508 | } |
| @@ -414,43 +538,70 @@ | ||
| 414 | 538 | ** routines never return. |
| 415 | 539 | */ |
| 416 | 540 | NORETURN void fossil_panic(const char *zFormat, ...){ |
| 417 | 541 | char *z; |
| 418 | 542 | va_list ap; |
| 543 | + int rc = 1; | |
| 419 | 544 | static int once = 1; |
| 420 | 545 | mainInFatalError = 1; |
| 421 | 546 | va_start(ap, zFormat); |
| 422 | 547 | z = vmprintf(zFormat, ap); |
| 423 | 548 | 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); | |
| 432 | 569 | db_force_rollback(); |
| 433 | - fossil_exit(1); | |
| 570 | + fossil_exit(rc); | |
| 434 | 571 | } |
| 572 | + | |
| 435 | 573 | NORETURN void fossil_fatal(const char *zFormat, ...){ |
| 436 | 574 | char *z; |
| 575 | + int rc = 1; | |
| 437 | 576 | va_list ap; |
| 438 | 577 | mainInFatalError = 1; |
| 439 | 578 | va_start(ap, zFormat); |
| 440 | 579 | z = vmprintf(zFormat, ap); |
| 441 | 580 | 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); | |
| 450 | 601 | db_force_rollback(); |
| 451 | - fossil_exit(1); | |
| 602 | + fossil_exit(rc); | |
| 452 | 603 | } |
| 453 | 604 | |
| 454 | 605 | /* This routine works like fossil_fatal() except that if called |
| 455 | 606 | ** recursively, the recursive call is a no-op. |
| 456 | 607 | ** |
| @@ -461,25 +612,37 @@ | ||
| 461 | 612 | ** be prepared for this routine to return. |
| 462 | 613 | */ |
| 463 | 614 | void fossil_fatal_recursive(const char *zFormat, ...){ |
| 464 | 615 | char *z; |
| 465 | 616 | va_list ap; |
| 617 | + int rc = 1; | |
| 466 | 618 | if( mainInFatalError ) return; |
| 467 | 619 | mainInFatalError = 1; |
| 468 | 620 | va_start(ap, zFormat); |
| 469 | 621 | z = vmprintf(zFormat, ap); |
| 470 | 622 | 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 | + } | |
| 478 | 641 | } |
| 479 | 642 | db_force_rollback(); |
| 480 | - fossil_exit(1); | |
| 643 | + fossil_exit(rc); | |
| 481 | 644 | } |
| 482 | 645 | |
| 483 | 646 | |
| 484 | 647 | /* Print a warning message */ |
| 485 | 648 | void fossil_warning(const char *zFormat, ...){ |
| @@ -486,17 +649,25 @@ | ||
| 486 | 649 | char *z; |
| 487 | 650 | va_list ap; |
| 488 | 651 | va_start(ap, zFormat); |
| 489 | 652 | z = vmprintf(zFormat, ap); |
| 490 | 653 | 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 | + } | |
| 497 | 667 | } |
| 668 | + free(z); | |
| 498 | 669 | } |
| 499 | 670 | |
| 500 | 671 | /* |
| 501 | 672 | ** Malloc and free routines that cannot fail |
| 502 | 673 | */ |
| @@ -695,40 +866,22 @@ | ||
| 695 | 866 | } |
| 696 | 867 | |
| 697 | 868 | /* |
| 698 | 869 | ** List of commands starting with zPrefix, or all commands if zPrefix is NULL. |
| 699 | 870 | */ |
| 700 | -static void cmd_cmd_list(const char *zPrefix){ | |
| 871 | +static void command_list(const char *zPrefix, int cmdMask){ | |
| 701 | 872 | int i, nCmd; |
| 702 | 873 | int nPrefix = zPrefix ? strlen(zPrefix) : 0; |
| 703 | 874 | const char *aCmd[count(aCommand)]; |
| 704 | 875 | for(i=nCmd=0; i<count(aCommand); i++){ |
| 705 | 876 | const char *z = aCommand[i].zName; |
| 706 | - if( memcmp(z,"test",4)==0 ) continue; | |
| 877 | + if( (aCommand[i].cmdFlags & cmdMask)==0 ) continue; | |
| 707 | 878 | if( zPrefix && memcmp(zPrefix, z, nPrefix)!=0 ) continue; |
| 708 | 879 | aCmd[nCmd++] = aCommand[i].zName; |
| 709 | 880 | } |
| 710 | 881 | multi_column_list(aCmd, nCmd); |
| 711 | 882 | } |
| 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 | 883 | |
| 731 | 884 | /* |
| 732 | 885 | ** COMMAND: test-list-webpage |
| 733 | 886 | ** |
| 734 | 887 | ** List all web pages |
| @@ -739,11 +892,10 @@ | ||
| 739 | 892 | for(i=nCmd=0; i<count(aWebpage); i++){ |
| 740 | 893 | aCmd[nCmd++] = aWebpage[i].zName; |
| 741 | 894 | } |
| 742 | 895 | multi_column_list(aCmd, nCmd); |
| 743 | 896 | } |
| 744 | - | |
| 745 | 897 | |
| 746 | 898 | /* |
| 747 | 899 | ** COMMAND: version |
| 748 | 900 | ** |
| 749 | 901 | ** Usage: %fossil version |
| @@ -758,32 +910,54 @@ | ||
| 758 | 910 | |
| 759 | 911 | /* |
| 760 | 912 | ** COMMAND: help |
| 761 | 913 | ** |
| 762 | 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: | |
| 763 | 919 | ** |
| 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 | |
| 765 | 924 | */ |
| 766 | 925 | void help_cmd(void){ |
| 767 | 926 | int rc, idx; |
| 768 | 927 | const char *z; |
| 769 | 928 | 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); | |
| 773 | 935 | version_cmd(); |
| 774 | 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; | |
| 775 | 949 | } |
| 776 | 950 | rc = name_search(g.argv[2], aCommand, count(aCommand), &idx); |
| 777 | 951 | if( rc==1 ){ |
| 778 | 952 | fossil_print("unknown command: %s\nAvailable commands:\n", g.argv[2]); |
| 779 | - cmd_cmd_list(0); | |
| 953 | + command_list(0, 0xff); | |
| 780 | 954 | fossil_exit(1); |
| 781 | 955 | }else if( rc==2 ){ |
| 782 | 956 | fossil_print("ambiguous command prefix: %s\nMatching commands:\n", |
| 783 | 957 | g.argv[2]); |
| 784 | - cmd_cmd_list(g.argv[2]); | |
| 958 | + command_list(g.argv[2], 0xff); | |
| 785 | 959 | fossil_exit(1); |
| 786 | 960 | } |
| 787 | 961 | z = aCmdHelp[idx]; |
| 788 | 962 | if( z==0 ){ |
| 789 | 963 | fossil_fatal("no help available for the %s command", |
| @@ -1033,10 +1207,16 @@ | ||
| 1033 | 1207 | |
| 1034 | 1208 | if( szFile<1024 ){ |
| 1035 | 1209 | if( zNotFound ){ |
| 1036 | 1210 | cgi_redirect(zNotFound); |
| 1037 | 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 | |
| 1038 | 1218 | @ <h1>Not Found</h1> |
| 1039 | 1219 | cgi_set_status(404, "not found"); |
| 1040 | 1220 | cgi_reply(); |
| 1041 | 1221 | } |
| 1042 | 1222 | return; |
| @@ -1064,11 +1244,17 @@ | ||
| 1064 | 1244 | zPathInfo = "/xfer"; |
| 1065 | 1245 | } |
| 1066 | 1246 | set_base_url(); |
| 1067 | 1247 | if( zPathInfo==0 || zPathInfo[0]==0 |
| 1068 | 1248 | || (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*/; | |
| 1070 | 1256 | }else{ |
| 1071 | 1257 | zPath = mprintf("%s", zPathInfo); |
| 1072 | 1258 | } |
| 1073 | 1259 | |
| 1074 | 1260 | /* Make g.zPath point to the first element of the path. Make |
| @@ -1125,10 +1311,12 @@ | ||
| 1125 | 1311 | break; |
| 1126 | 1312 | } |
| 1127 | 1313 | if( g.zExtra ){ |
| 1128 | 1314 | /* CGI parameters get this treatment elsewhere, but places like getfile |
| 1129 | 1315 | ** will use g.zExtra directly. |
| 1316 | + ** Reminder: the login mechanism uses 'name' differently, and may | |
| 1317 | + ** eventually have a problem/collision with this. | |
| 1130 | 1318 | */ |
| 1131 | 1319 | dehttpize(g.zExtra); |
| 1132 | 1320 | cgi_set_parameter_nocopy("name", g.zExtra); |
| 1133 | 1321 | } |
| 1134 | 1322 | |
| @@ -1135,17 +1323,31 @@ | ||
| 1135 | 1323 | /* Locate the method specified by the path and execute the function |
| 1136 | 1324 | ** that implements that method. |
| 1137 | 1325 | */ |
| 1138 | 1326 | if( name_search(g.zPath, aWebpage, count(aWebpage), &idx) && |
| 1139 | 1327 | 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 | + } | |
| 1143 | 1338 | }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 | + } | |
| 1147 | 1349 | }else{ |
| 1148 | 1350 | aWebpage[idx].xFunc(); |
| 1149 | 1351 | } |
| 1150 | 1352 | |
| 1151 | 1353 | /* Return the result. |
| @@ -1152,11 +1354,11 @@ | ||
| 1152 | 1354 | */ |
| 1153 | 1355 | cgi_reply(); |
| 1154 | 1356 | } |
| 1155 | 1357 | |
| 1156 | 1358 | /* |
| 1157 | -** COMMAND: cgi | |
| 1359 | +** COMMAND: cgi* | |
| 1158 | 1360 | ** |
| 1159 | 1361 | ** Usage: %fossil ?cgi? SCRIPT |
| 1160 | 1362 | ** |
| 1161 | 1363 | ** The SCRIPT argument is the name of a file that is the CGI script |
| 1162 | 1364 | ** that is being run. The command name, "cgi", may be omitted if |
| @@ -1339,11 +1541,11 @@ | ||
| 1339 | 1541 | ** |
| 1340 | 1542 | ** fossil http REPOSITORY INFILE OUTFILE IPADDR |
| 1341 | 1543 | ** |
| 1342 | 1544 | ** The argv==6 form is used by the win32 server only. |
| 1343 | 1545 | ** |
| 1344 | -** COMMAND: http | |
| 1546 | +** COMMAND: http* | |
| 1345 | 1547 | ** |
| 1346 | 1548 | ** Usage: %fossil http REPOSITORY [--notfound URL] [--host HOSTNAME] [--https] |
| 1347 | 1549 | ** |
| 1348 | 1550 | ** Handle a single HTTP request appearing on stdin. The resulting webpage |
| 1349 | 1551 | ** is delivered on stdout. This method is used to launch an HTTP request |
| @@ -1440,11 +1642,11 @@ | ||
| 1440 | 1642 | } |
| 1441 | 1643 | #endif |
| 1442 | 1644 | #endif |
| 1443 | 1645 | |
| 1444 | 1646 | /* |
| 1445 | -** COMMAND: server | |
| 1647 | +** COMMAND: server* | |
| 1446 | 1648 | ** COMMAND: ui |
| 1447 | 1649 | ** |
| 1448 | 1650 | ** Usage: %fossil server ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY? |
| 1449 | 1651 | ** Or: %fossil ui ?-P|--port TCPPORT? -?-R|--root ROOT? ?REPOSITORY? |
| 1450 | 1652 | ** |
| 1451 | 1653 |
| --- 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 @@ | ||
| 47 | 47 | $(SRCDIR)/http_socket.c \ |
| 48 | 48 | $(SRCDIR)/http_ssl.c \ |
| 49 | 49 | $(SRCDIR)/http_transport.c \ |
| 50 | 50 | $(SRCDIR)/import.c \ |
| 51 | 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 \ | |
| 52 | 63 | $(SRCDIR)/leaf.c \ |
| 53 | 64 | $(SRCDIR)/login.c \ |
| 54 | 65 | $(SRCDIR)/main.c \ |
| 55 | 66 | $(SRCDIR)/manifest.c \ |
| 56 | 67 | $(SRCDIR)/md5.c \ |
| @@ -131,10 +142,21 @@ | ||
| 131 | 142 | $(OBJDIR)/http_socket_.c \ |
| 132 | 143 | $(OBJDIR)/http_ssl_.c \ |
| 133 | 144 | $(OBJDIR)/http_transport_.c \ |
| 134 | 145 | $(OBJDIR)/import_.c \ |
| 135 | 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 \ | |
| 136 | 158 | $(OBJDIR)/leaf_.c \ |
| 137 | 159 | $(OBJDIR)/login_.c \ |
| 138 | 160 | $(OBJDIR)/main_.c \ |
| 139 | 161 | $(OBJDIR)/manifest_.c \ |
| 140 | 162 | $(OBJDIR)/md5_.c \ |
| @@ -215,10 +237,21 @@ | ||
| 215 | 237 | $(OBJDIR)/http_socket.o \ |
| 216 | 238 | $(OBJDIR)/http_ssl.o \ |
| 217 | 239 | $(OBJDIR)/http_transport.o \ |
| 218 | 240 | $(OBJDIR)/import.o \ |
| 219 | 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 \ | |
| 220 | 253 | $(OBJDIR)/leaf.o \ |
| 221 | 254 | $(OBJDIR)/login.o \ |
| 222 | 255 | $(OBJDIR)/main.o \ |
| 223 | 256 | $(OBJDIR)/manifest.o \ |
| 224 | 257 | $(OBJDIR)/md5.o \ |
| @@ -287,12 +320,12 @@ | ||
| 287 | 320 | $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c |
| 288 | 321 | |
| 289 | 322 | # WARNING. DANGER. Running the testsuite modifies the repository the |
| 290 | 323 | # build is done from, i.e. the checkout belongs to. Do not sync/push |
| 291 | 324 | # 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) | |
| 294 | 327 | |
| 295 | 328 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion |
| 296 | 329 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| 297 | 330 | |
| 298 | 331 | # The USE_SYSTEM_SQLITE variable may be undefined, set to 0, or set |
| @@ -301,11 +334,15 @@ | ||
| 301 | 334 | # using -lsqlite3. |
| 302 | 335 | SQLITE3_OBJ.1 = |
| 303 | 336 | SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o |
| 304 | 337 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) |
| 305 | 338 | |
| 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 | |
| 307 | 344 | |
| 308 | 345 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) |
| 309 | 346 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) |
| 310 | 347 | |
| 311 | 348 | # This rule prevents make from using its default rules to try build |
| @@ -319,13 +356,14 @@ | ||
| 319 | 356 | |
| 320 | 357 | |
| 321 | 358 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex |
| 322 | 359 | $(OBJDIR)/mkindex $(TRANS_SRC) >$@ |
| 323 | 360 | $(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 | |
| 325 | 362 | touch $(OBJDIR)/headers |
| 326 | 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 | |
| 327 | 365 | Makefile: |
| 328 | 366 | $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate |
| 329 | 367 | $(OBJDIR)/translate $(SRCDIR)/add.c >$(OBJDIR)/add_.c |
| 330 | 368 | |
| 331 | 369 | $(OBJDIR)/add.o: $(OBJDIR)/add_.c $(OBJDIR)/add.h $(SRCDIR)/config.h |
| @@ -589,10 +627,87 @@ | ||
| 589 | 627 | |
| 590 | 628 | $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h |
| 591 | 629 | $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c |
| 592 | 630 | |
| 593 | 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 | |
| 594 | 709 | $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate |
| 595 | 710 | $(OBJDIR)/translate $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c |
| 596 | 711 | |
| 597 | 712 | $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h |
| 598 | 713 | $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c |
| @@ -909,5 +1024,12 @@ | ||
| 909 | 1024 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o |
| 910 | 1025 | |
| 911 | 1026 | $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c |
| 912 | 1027 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o |
| 913 | 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 | + | |
| 914 | 1036 |
| --- 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 |
+5
-1
| --- src/makeheaders.c | ||
| +++ src/makeheaders.c | ||
| @@ -327,13 +327,17 @@ | ||
| 327 | 327 | |
| 328 | 328 | /* |
| 329 | 329 | ** The following text line appears at the top of every file generated |
| 330 | 330 | ** by this program. By recognizing this line, the program can be sure |
| 331 | 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. | |
| 332 | 335 | */ |
| 333 | 336 | 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"; | |
| 335 | 339 | #define nTopLine (sizeof(zTopLine)-1) |
| 336 | 340 | |
| 337 | 341 | /* |
| 338 | 342 | ** The name of the file currently being parsed. |
| 339 | 343 | */ |
| 340 | 344 |
| --- 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 @@ | ||
| 53 | 53 | http |
| 54 | 54 | http_socket |
| 55 | 55 | http_transport |
| 56 | 56 | import |
| 57 | 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 | |
| 58 | 69 | leaf |
| 59 | 70 | login |
| 60 | 71 | main |
| 61 | 72 | manifest |
| 62 | 73 | md5 |
| @@ -182,12 +193,12 @@ | ||
| 182 | 193 | $(BCC) -o $(OBJDIR)/mkversion $(SRCDIR)/mkversion.c |
| 183 | 194 | |
| 184 | 195 | # WARNING. DANGER. Running the testsuite modifies the repository the |
| 185 | 196 | # build is done from, i.e. the checkout belongs to. Do not sync/push |
| 186 | 197 | # 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) | |
| 189 | 200 | |
| 190 | 201 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion |
| 191 | 202 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ |
| 192 | 203 | $(SRCDIR)/../manifest \ |
| 193 | 204 | $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| @@ -198,15 +209,21 @@ | ||
| 198 | 209 | # using -lsqlite3. |
| 199 | 210 | SQLITE3_OBJ.1 = |
| 200 | 211 | SQLITE3_OBJ.0 = $(OBJDIR)/sqlite3.o |
| 201 | 212 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) |
| 202 | 213 | |
| 214 | +TCL_OBJ.1 = | |
| 215 | +TCL_OBJ.0 = $(OBJDIR)/th_tcl.o | |
| 216 | +TCL_OBJ. = $(TCL_OBJ.0) | |
| 217 | + | |
| 203 | 218 | EXTRAOBJ = \ |
| 204 | 219 | $(SQLITE3_OBJ.$(USE_SYSTEM_SQLITE)) \ |
| 205 | 220 | $(OBJDIR)/shell.o \ |
| 206 | 221 | $(OBJDIR)/th.o \ |
| 207 | - $(OBJDIR)/th_lang.o | |
| 222 | + $(OBJDIR)/th_lang.o \ | |
| 223 | + $(TCL_OBJ.$(FOSSIL_ENABLE_TCL)) \ | |
| 224 | + $(OBJDIR)/cson_amalgamation.o | |
| 208 | 225 | |
| 209 | 226 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) |
| 210 | 227 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) |
| 211 | 228 | |
| 212 | 229 | # This rule prevents make from using its default rules to try build |
| @@ -225,17 +242,19 @@ | ||
| 225 | 242 | append mhargs " \$(OBJDIR)/${s}_.c:\$(OBJDIR)/$s.h" |
| 226 | 243 | set extra_h($s) {} |
| 227 | 244 | } |
| 228 | 245 | append mhargs " \$(SRCDIR)/sqlite3.h" |
| 229 | 246 | append mhargs " \$(SRCDIR)/th.h" |
| 247 | +#append mhargs " \$(SRCDIR)/cson_amalgamation.h" | |
| 230 | 248 | append mhargs " \$(OBJDIR)/VERSION.h" |
| 231 | 249 | writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" |
| 232 | 250 | writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >$@" |
| 233 | 251 | writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h" |
| 234 | 252 | writeln "\t\$(OBJDIR)/makeheaders $mhargs" |
| 235 | 253 | writeln "\ttouch \$(OBJDIR)/headers" |
| 236 | 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" | |
| 237 | 256 | writeln "Makefile:" |
| 238 | 257 | set extra_h(main) \$(OBJDIR)/page_index.h |
| 239 | 258 | |
| 240 | 259 | foreach s [lsort $src] { |
| 241 | 260 | writeln "\$(OBJDIR)/${s}_.c:\t\$(SRCDIR)/$s.c \$(OBJDIR)/translate" |
| @@ -264,10 +283,19 @@ | ||
| 264 | 283 | writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" |
| 265 | 284 | writeln "\t\$(XTCC) -I\$(SRCDIR) -c \$(SRCDIR)/th.c -o \$(OBJDIR)/th.o\n" |
| 266 | 285 | |
| 267 | 286 | writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" |
| 268 | 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 | +} | |
| 269 | 297 | |
| 270 | 298 | close $output_file |
| 271 | 299 | # |
| 272 | 300 | # End of the main.mk output |
| 273 | 301 | ############################################################################## |
| @@ -412,11 +440,12 @@ | ||
| 412 | 440 | |
| 413 | 441 | EXTRAOBJ = \ |
| 414 | 442 | $(OBJDIR)/sqlite3.o \ |
| 415 | 443 | $(OBJDIR)/shell.o \ |
| 416 | 444 | $(OBJDIR)/th.o \ |
| 417 | - $(OBJDIR)/th_lang.o | |
| 445 | + $(OBJDIR)/th_lang.o \ | |
| 446 | + $(OBJDIR)/cson_amalgamation.o | |
| 418 | 447 | |
| 419 | 448 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/icon.o |
| 420 | 449 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/icon.o |
| 421 | 450 | |
| 422 | 451 | # This rule prevents make from using its default rules to try build |
| @@ -466,10 +495,15 @@ | ||
| 466 | 495 | |
| 467 | 496 | writeln "\$(OBJDIR)/sqlite3.o:\t\$(SRCDIR)/sqlite3.c" |
| 468 | 497 | set opt $SQLITE_OPTIONS |
| 469 | 498 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/sqlite3.c -o \$(OBJDIR)/sqlite3.o\n" |
| 470 | 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 | + | |
| 471 | 505 | writeln "\$(OBJDIR)/shell.o:\t\$(SRCDIR)/shell.c \$(SRCDIR)/sqlite3.h" |
| 472 | 506 | set opt {-Dmain=sqlite3_shell} |
| 473 | 507 | append opt " -DSQLITE_OMIT_LOAD_EXTENSION=1" |
| 474 | 508 | writeln "\t\$(XTCC) $opt -c \$(SRCDIR)/shell.c -o \$(OBJDIR)/shell.o\n" |
| 475 | 509 | |
| @@ -577,10 +611,13 @@ | ||
| 577 | 611 | $(OBJDIR)\th$O : $(SRCDIR)\th.c |
| 578 | 612 | $(TCC) -o$@ -c $** |
| 579 | 613 | |
| 580 | 614 | $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c |
| 581 | 615 | $(TCC) -o$@ -c $** |
| 616 | + | |
| 617 | +$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 618 | + cp $@ $@ | |
| 582 | 619 | |
| 583 | 620 | VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION |
| 584 | 621 | +$** > $@ |
| 585 | 622 | |
| 586 | 623 | page_index.h: mkindex$E $(SRC) |
| @@ -590,10 +627,23 @@ | ||
| 590 | 627 | -del $(OBJDIR)\*.obj |
| 591 | 628 | -del *.obj *_.c *.h *.map |
| 592 | 629 | |
| 593 | 630 | realclean: |
| 594 | 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 | + | |
| 595 | 645 | |
| 596 | 646 | } |
| 597 | 647 | foreach s [lsort $src] { |
| 598 | 648 | writeln "\$(OBJDIR)\\$s\$O : ${s}_.c ${s}.h" |
| 599 | 649 | writeln "\t\$(TCC) -o\$@ -c ${s}_.c\n" |
| @@ -603,11 +653,11 @@ | ||
| 603 | 653 | |
| 604 | 654 | writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\t +makeheaders\$E " |
| 605 | 655 | foreach s [lsort $src] { |
| 606 | 656 | writeln -nonewline "${s}_.c:$s.h " |
| 607 | 657 | } |
| 608 | -writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h" | |
| 658 | +writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h" | |
| 609 | 659 | writeln "\t@copy /Y nul: headers" |
| 610 | 660 | |
| 611 | 661 | close $output_file |
| 612 | 662 | # |
| 613 | 663 | # End of the win/Makefile.dmc output |
| @@ -717,10 +767,12 @@ | ||
| 717 | 767 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 718 | 768 | $(TCC) /Fo$@ -c $** |
| 719 | 769 | |
| 720 | 770 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 721 | 771 | $** > $@ |
| 772 | +$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 773 | + cp $(SRCDIR)\cson_amalgamation.h $@ | |
| 722 | 774 | |
| 723 | 775 | page_index.h: mkindex$E $(SRC) |
| 724 | 776 | $** > $@ |
| 725 | 777 | |
| 726 | 778 | clean: |
| @@ -729,10 +781,22 @@ | ||
| 729 | 781 | -del headers linkopts |
| 730 | 782 | |
| 731 | 783 | realclean: |
| 732 | 784 | -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E |
| 733 | 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 | + | |
| 734 | 798 | } |
| 735 | 799 | foreach s [lsort $src] { |
| 736 | 800 | writeln "\$(OX)\\$s\$O : ${s}_.c ${s}.h" |
| 737 | 801 | writeln "\t\$(TCC) /Fo\$@ -c ${s}_.c\n" |
| 738 | 802 | writeln "${s}_.c : \$(SRCDIR)\\$s.c" |
| @@ -741,11 +805,11 @@ | ||
| 741 | 805 | |
| 742 | 806 | writeln -nonewline "headers: makeheaders\$E page_index.h VERSION.h\n\tmakeheaders\$E " |
| 743 | 807 | foreach s [lsort $src] { |
| 744 | 808 | writeln -nonewline "${s}_.c:$s.h " |
| 745 | 809 | } |
| 746 | -writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h" | |
| 810 | +writeln "\$(SRCDIR)\\sqlite3.h \$(SRCDIR)\\th.h VERSION.h \$(SRCDIR)\\cson_amalgamation.h" | |
| 747 | 811 | writeln "\t@copy /Y nul: headers" |
| 748 | 812 | |
| 749 | 813 | |
| 750 | 814 | close $output_file |
| 751 | 815 | # |
| 752 | 816 |
| --- 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 @@ | ||
| 1334 | 1334 | } |
| 1335 | 1335 | } |
| 1336 | 1336 | } |
| 1337 | 1337 | if( pParent->zBaseline && pChild->zBaseline ){ |
| 1338 | 1338 | /* Both parent and child are delta manifests. Look for files that |
| 1339 | - ** are marked as deleted in the parent but which reappear in the child | |
| 1340 | - ** and show such files as being added in the child. */ | |
| 1339 | + ** are deleted or modified in the parent but which reappear or revert | |
| 1340 | + ** to baseline in the child and show such files as being added or changed | |
| 1341 | + ** in the child. */ | |
| 1341 | 1342 | for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ |
| 1342 | - if( pParentFile->zUuid ) continue; | |
| 1343 | - pChildFile = manifest_file_seek(pChild, pParentFile->zName); | |
| 1344 | - if( pChildFile ){ | |
| 1345 | - add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1346 | - isPublic, manifest_file_mperm(pChildFile)); | |
| 1343 | + if( pParentFile->zUuid ){ | |
| 1344 | + pChildFile = manifest_file_seek_base(pChild, pParentFile->zName); | |
| 1345 | + if( pChildFile==0 ){ | |
| 1346 | + /* The child file reverts to baseline. Show this as a change */ | |
| 1347 | + pChildFile = manifest_file_seek(pChild, pParentFile->zName); | |
| 1348 | + if( pChildFile ){ | |
| 1349 | + add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, | |
| 1350 | + pChildFile->zName, 0, isPublic, | |
| 1351 | + manifest_file_mperm(pChildFile)); | |
| 1352 | + } | |
| 1353 | + } | |
| 1354 | + }else{ | |
| 1355 | + pChildFile = manifest_file_seek(pChild, pParentFile->zName); | |
| 1356 | + if( pChildFile ){ | |
| 1357 | + /* File resurrected in the child after having been deleted in | |
| 1358 | + ** the parent. Show this as an added file. */ | |
| 1359 | + add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1360 | + isPublic, manifest_file_mperm(pChildFile)); | |
| 1361 | + } | |
| 1347 | 1362 | } |
| 1348 | 1363 | } |
| 1349 | 1364 | }else if( pChild->zBaseline==0 ){ |
| 1350 | 1365 | /* pChild is a baseline. Look for files that are present in pParent |
| 1351 | 1366 | ** but are missing from pChild and mark them as having been deleted. */ |
| @@ -1819,10 +1834,48 @@ | ||
| 1819 | 1834 | "VALUES('t',%.17g,%d,%Q,%Q)", |
| 1820 | 1835 | p->rDate, rid, p->zUser, zComment |
| 1821 | 1836 | ); |
| 1822 | 1837 | free(zComment); |
| 1823 | 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); | |
| 1824 | 1877 | } |
| 1825 | 1878 | db_end_transaction(0); |
| 1826 | 1879 | if( p->type==CFTYPE_MANIFEST ){ |
| 1827 | 1880 | manifest_cache_insert(p); |
| 1828 | 1881 | }else{ |
| 1829 | 1882 |
| --- 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 @@ | ||
| 421 | 421 | return 0; |
| 422 | 422 | } |
| 423 | 423 | |
| 424 | 424 | |
| 425 | 425 | /* |
| 426 | -** COMMAND: test-md5sum | |
| 426 | +** COMMAND: md5sum* | |
| 427 | +** Usage: %fossil md5sum FILES.... | |
| 427 | 428 | ** |
| 428 | 429 | ** Compute an MD5 checksum of all files named on the command-line. |
| 429 | -** If an file is named "-" then take its content from standard input. | |
| 430 | +** If a file is named "-" then content is read from standard input. | |
| 430 | 431 | */ |
| 431 | 432 | void md5sum_test(void){ |
| 432 | 433 | int i; |
| 433 | 434 | Blob in; |
| 434 | 435 | Blob cksum; |
| 435 | 436 |
| --- src/md5.c | |
| +++ src/md5.c | |
| @@ -421,14 +421,15 @@ | |
| 421 | return 0; |
| 422 | } |
| 423 | |
| 424 | |
| 425 | /* |
| 426 | ** COMMAND: test-md5sum |
| 427 | ** |
| 428 | ** Compute an MD5 checksum of all files named on the command-line. |
| 429 | ** If an file is named "-" then take its content from standard input. |
| 430 | */ |
| 431 | void md5sum_test(void){ |
| 432 | int i; |
| 433 | Blob in; |
| 434 | Blob cksum; |
| 435 |
| --- src/md5.c | |
| +++ src/md5.c | |
| @@ -421,14 +421,15 @@ | |
| 421 | return 0; |
| 422 | } |
| 423 | |
| 424 | |
| 425 | /* |
| 426 | ** COMMAND: md5sum* |
| 427 | ** Usage: %fossil md5sum FILES.... |
| 428 | ** |
| 429 | ** Compute an MD5 checksum of all files named on the command-line. |
| 430 | ** If a file is named "-" then content is read from standard input. |
| 431 | */ |
| 432 | void md5sum_test(void){ |
| 433 | int i; |
| 434 | Blob in; |
| 435 | Blob cksum; |
| 436 |
+18
-4
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -73,10 +73,11 @@ | ||
| 73 | 73 | int debugFlag; /* True if --debug is present */ |
| 74 | 74 | int nChng; /* Number of file name changes */ |
| 75 | 75 | int *aChng; /* An array of file name changes */ |
| 76 | 76 | int i; /* Loop counter */ |
| 77 | 77 | int nConflict = 0; /* Number of conflicts seen */ |
| 78 | + int nOverwrite = 0; /* Number of unmanaged files overwritten */ | |
| 78 | 79 | int caseSensitive; /* True for case-sensitive filenames */ |
| 79 | 80 | Stmt q; |
| 80 | 81 | |
| 81 | 82 | |
| 82 | 83 | /* Notation: |
| @@ -319,19 +320,27 @@ | ||
| 319 | 320 | while( db_step(&q)==SQLITE_ROW ){ |
| 320 | 321 | int idm = db_column_int(&q, 0); |
| 321 | 322 | int rowid = db_column_int(&q, 1); |
| 322 | 323 | int idv; |
| 323 | 324 | const char *zName; |
| 325 | + char *zFullName; | |
| 324 | 326 | db_multi_exec( |
| 325 | 327 | "INSERT INTO vfile(vid,chnged,deleted,rid,mrid,isexe,islink,pathname)" |
| 326 | 328 | " SELECT %d,3,0,rid,mrid,isexe,islink,pathname FROM vfile WHERE id=%d", |
| 327 | 329 | vid, idm |
| 328 | 330 | ); |
| 329 | 331 | idv = db_last_insert_rowid(); |
| 330 | 332 | db_multi_exec("UPDATE fv SET idv=%d WHERE rowid=%d", idv, rowid); |
| 331 | 333 | 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); | |
| 333 | 342 | if( !nochangeFlag ){ |
| 334 | 343 | undo_save(zName); |
| 335 | 344 | vfile_to_disk(0, idm, 0, 0); |
| 336 | 345 | } |
| 337 | 346 | } |
| @@ -495,13 +504,18 @@ | ||
| 495 | 504 | db_finalize(&q); |
| 496 | 505 | |
| 497 | 506 | |
| 498 | 507 | /* Report on conflicts |
| 499 | 508 | */ |
| 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 | + } | |
| 503 | 517 | } |
| 504 | 518 | |
| 505 | 519 | /* |
| 506 | 520 | ** Clean up the mid and pid VFILE entries. Then commit the changes. |
| 507 | 521 | */ |
| 508 | 522 |
| --- 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 @@ | ||
| 27 | 27 | #define DEBUG(X) |
| 28 | 28 | #define ISDEBUG 0 |
| 29 | 29 | #endif |
| 30 | 30 | |
| 31 | 31 | /* 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 | |
| 33 | 35 | |
| 34 | 36 | /* |
| 35 | 37 | ** Compare N lines of text from pV1 and pV2. If the lines |
| 36 | 38 | ** are the same, return true. Return false if one or more of the N |
| 37 | 39 | ** lines are different. |
| @@ -171,12 +173,12 @@ | ||
| 171 | 173 | ** is the number of lines of text to copy directly from the pivot, |
| 172 | 174 | ** the second integer is the number of lines of text to omit from the |
| 173 | 175 | ** pivot, and the third integer is the number of lines of text that are |
| 174 | 176 | ** inserted. The edit array ends with a triple of 0,0,0. |
| 175 | 177 | */ |
| 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); | |
| 178 | 180 | if( aC1==0 || aC2==0 ){ |
| 179 | 181 | free(aC1); |
| 180 | 182 | free(aC2); |
| 181 | 183 | return -1; |
| 182 | 184 | } |
| 183 | 185 |
| --- 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 @@ | ||
| 36 | 36 | ** We also scan for comments lines of this form: |
| 37 | 37 | ** |
| 38 | 38 | ** COMMAND: cmdname |
| 39 | 39 | ** |
| 40 | 40 | ** These entries build a constant table used to map command names into |
| 41 | -** functions. | |
| 41 | +** functions. If cmdname ends with "*" then the command is a second-tier | |
| 42 | +** command that is not displayed by the "fossil help" command. The | |
| 43 | +** final "*" is not considered to be part of the command name. | |
| 42 | 44 | ** |
| 43 | 45 | ** Comment text following COMMAND: through the end of the comment is |
| 44 | 46 | ** understood to be help text for the command specified. This help |
| 45 | 47 | ** text is accumulated and a table containing the text for each command |
| 46 | 48 | ** is generated. That table is used implement the "fossil help" command |
| @@ -59,10 +61,11 @@ | ||
| 59 | 61 | /* |
| 60 | 62 | ** Each entry looks like this: |
| 61 | 63 | */ |
| 62 | 64 | typedef struct Entry { |
| 63 | 65 | int eType; |
| 66 | + char *zIf; | |
| 64 | 67 | char *zFunc; |
| 65 | 68 | char *zPath; |
| 66 | 69 | char *zHelp; |
| 67 | 70 | } Entry; |
| 68 | 71 | |
| @@ -85,10 +88,15 @@ | ||
| 85 | 88 | ** Current help message accumulator |
| 86 | 89 | */ |
| 87 | 90 | char zHelp[MX_HELP]; |
| 88 | 91 | int nHelp; |
| 89 | 92 | |
| 93 | +/* | |
| 94 | +** Most recently encountered #if | |
| 95 | +*/ | |
| 96 | +char zIf[200]; | |
| 97 | + | |
| 90 | 98 | /* |
| 91 | 99 | ** How many entries are used |
| 92 | 100 | */ |
| 93 | 101 | int nUsed; |
| 94 | 102 | int nFixed; |
| @@ -133,10 +141,30 @@ | ||
| 133 | 141 | aEntry[nUsed].eType = eType; |
| 134 | 142 | aEntry[nUsed].zPath = string_dup(&zLine[i], j); |
| 135 | 143 | aEntry[nUsed].zFunc = 0; |
| 136 | 144 | nUsed++; |
| 137 | 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 | +} | |
| 138 | 166 | |
| 139 | 167 | /* |
| 140 | 168 | ** Scan a line for a function that implements a web page or command. |
| 141 | 169 | */ |
| 142 | 170 | void scan_for_func(char *zLine){ |
| @@ -177,10 +205,11 @@ | ||
| 177 | 205 | z = string_dup(&zHelp[k], nHelp-k); |
| 178 | 206 | }else{ |
| 179 | 207 | z = 0; |
| 180 | 208 | } |
| 181 | 209 | for(k=nFixed; k<nUsed; k++){ |
| 210 | + aEntry[k].zIf = zIf[0] ? string_dup(zIf, -1) : 0; | |
| 182 | 211 | aEntry[k].zFunc = string_dup(&zLine[i], j); |
| 183 | 212 | aEntry[k].zHelp = z; |
| 184 | 213 | } |
| 185 | 214 | i+=j; |
| 186 | 215 | while( isspace(zLine[i]) ){ i++; } |
| @@ -217,41 +246,68 @@ | ||
| 217 | 246 | int i; |
| 218 | 247 | int nType0; |
| 219 | 248 | |
| 220 | 249 | qsort(aEntry, nFixed, sizeof(aEntry[0]), e_compare); |
| 221 | 250 | for(i=0; i<nFixed; i++){ |
| 251 | + if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); | |
| 222 | 252 | printf("extern void %s(void);\n", aEntry[i].zFunc); |
| 253 | + if( aEntry[i].zIf ) printf("#endif\n"); | |
| 223 | 254 | } |
| 224 | 255 | printf( |
| 225 | 256 | "typedef struct NameMap NameMap;\n" |
| 226 | 257 | "struct NameMap {\n" |
| 227 | 258 | " const char *zName;\n" |
| 228 | 259 | " void (*xFunc)(void);\n" |
| 260 | + " char cmdFlags;\n" | |
| 229 | 261 | "};\n" |
| 262 | + "#define CMDFLAG_1ST_TIER 0x01\n" | |
| 263 | + "#define CMDFLAG_2ND_TIER 0x02\n" | |
| 264 | + "#define CMDFLAG_TEST 0x04\n" | |
| 230 | 265 | "static const NameMap aWebpage[] = {\n" |
| 231 | 266 | ); |
| 232 | 267 | 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)), "" | |
| 236 | 276 | ); |
| 277 | + if( aEntry[i].zIf ) printf("#endif\n"); | |
| 237 | 278 | } |
| 238 | 279 | printf("};\n"); |
| 239 | 280 | nType0 = i; |
| 240 | 281 | printf( |
| 241 | 282 | "static const NameMap aCommand[] = {\n" |
| 242 | 283 | ); |
| 243 | 284 | 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 | |
| 247 | 301 | ); |
| 302 | + if( aEntry[i].zIf ) printf("#endif\n"); | |
| 248 | 303 | } |
| 249 | 304 | printf("};\n"); |
| 250 | 305 | for(i=nType0; i<nFixed; i++){ |
| 251 | 306 | char *z = aEntry[i].zHelp; |
| 252 | 307 | if( z && z[0] ){ |
| 308 | + if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); | |
| 253 | 309 | printf("static const char zHelp_%s[] = \n", aEntry[i].zFunc); |
| 254 | 310 | printf(" \""); |
| 255 | 311 | while( *z ){ |
| 256 | 312 | if( *z=='\n' ){ |
| 257 | 313 | printf("\\n\"\n \""); |
| @@ -261,22 +317,25 @@ | ||
| 261 | 317 | putchar(*z); |
| 262 | 318 | } |
| 263 | 319 | z++; |
| 264 | 320 | } |
| 265 | 321 | printf("\";\n"); |
| 322 | + if( aEntry[i].zIf ) printf("#endif\n"); | |
| 266 | 323 | aEntry[i].zHelp[0] = 0; |
| 267 | 324 | } |
| 268 | 325 | } |
| 269 | 326 | printf( |
| 270 | 327 | "static const char * const aCmdHelp[] = {\n" |
| 271 | 328 | ); |
| 272 | 329 | for(i=nType0; i<nFixed; i++){ |
| 330 | + if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); | |
| 273 | 331 | if( aEntry[i].zHelp==0 ){ |
| 274 | 332 | printf(" 0,\n"); |
| 275 | 333 | }else{ |
| 276 | 334 | printf(" zHelp_%s,\n", aEntry[i].zFunc); |
| 277 | 335 | } |
| 336 | + if( aEntry[i].zIf ) printf("#endif\n"); | |
| 278 | 337 | } |
| 279 | 338 | printf("};\n"); |
| 280 | 339 | } |
| 281 | 340 | |
| 282 | 341 | /* |
| @@ -290,10 +349,11 @@ | ||
| 290 | 349 | return; |
| 291 | 350 | } |
| 292 | 351 | nLine = 0; |
| 293 | 352 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 294 | 353 | nLine++; |
| 354 | + scan_for_if(zLine); | |
| 295 | 355 | scan_for_label("WEBPAGE:",zLine,0); |
| 296 | 356 | scan_for_label("COMMAND:",zLine,1); |
| 297 | 357 | scan_for_func(zLine); |
| 298 | 358 | } |
| 299 | 359 | fclose(in); |
| 300 | 360 |
| --- 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 @@ | ||
| 22 | 22 | ** not necessarily in canonical form. |
| 23 | 23 | */ |
| 24 | 24 | #include "config.h" |
| 25 | 25 | #include "name.h" |
| 26 | 26 | #include <assert.h> |
| 27 | + | |
| 28 | +/* | |
| 29 | +** Return TRUE if the string begins with something that looks roughly | |
| 30 | +** like an ISO date/time string. The SQLite date/time functions will | |
| 31 | +** have the final say-so about whether or not the date/time string is | |
| 32 | +** well-formed. | |
| 33 | +*/ | |
| 34 | +static int is_date(const char *z){ | |
| 35 | + if( !fossil_isdigit(z[0]) ) return 0; | |
| 36 | + if( !fossil_isdigit(z[1]) ) return 0; | |
| 37 | + if( !fossil_isdigit(z[2]) ) return 0; | |
| 38 | + if( !fossil_isdigit(z[3]) ) return 0; | |
| 39 | + if( z[4]!='-') return 0; | |
| 40 | + if( !fossil_isdigit(z[5]) ) return 0; | |
| 41 | + if( !fossil_isdigit(z[6]) ) return 0; | |
| 42 | + if( z[7]!='-') return 0; | |
| 43 | + if( !fossil_isdigit(z[8]) ) return 0; | |
| 44 | + if( !fossil_isdigit(z[9]) ) return 0; | |
| 45 | + return 1; | |
| 46 | +} | |
| 47 | + | |
| 48 | +/* | |
| 49 | +** Convert a symbolic name into a RID. Acceptable forms: | |
| 50 | +** | |
| 51 | +** * SHA1 hash | |
| 52 | +** * SHA1 hash prefix of at least 4 characters | |
| 53 | +** * Symbolic Name | |
| 54 | +** * "tag:" + symbolic name | |
| 55 | +** * Date or date-time | |
| 56 | +** * "date:" + Date or date-time | |
| 57 | +** * symbolic-name ":" date-time | |
| 58 | +** * "tip" | |
| 59 | +** | |
| 60 | +** The following additional forms are available in local checkouts: | |
| 61 | +** | |
| 62 | +** * "current" | |
| 63 | +** * "prev" or "previous" | |
| 64 | +** * "next" | |
| 65 | +** | |
| 66 | +** Return the RID of the matching artifact. Or return 0 if the name does not | |
| 67 | +** match any known object. Or return -1 if the name is ambiguious. | |
| 68 | +** | |
| 69 | +** The zType parameter specifies the type of artifact: ci, t, w, e, g. | |
| 70 | +** If zType is NULL or "" or "*" then any type of artifact will serve. | |
| 71 | +** zType is "ci" in most use cases since we are usually searching for | |
| 72 | +** a check-in. | |
| 73 | +*/ | |
| 74 | +static int symbolic_name_to_rid(const char *zTag, const char *zType){ | |
| 75 | + int vid; | |
| 76 | + int rid = 0; | |
| 77 | + int nTag; | |
| 78 | + int i; | |
| 79 | + | |
| 80 | + if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 81 | + if( zTag==0 || zTag[0]==0 ) return 0; | |
| 82 | + | |
| 83 | + /* special keyword: "tip" */ | |
| 84 | + if( fossil_strcmp(zTag, "tip")==0 && (zType[0]=='*' || zType[0]=='c') ){ | |
| 85 | + rid = db_int(0, | |
| 86 | + "SELECT objid" | |
| 87 | + " FROM event" | |
| 88 | + " WHERE type='ci'" | |
| 89 | + " ORDER BY event.mtime DESC" | |
| 90 | + ); | |
| 91 | + if( rid ) return rid; | |
| 92 | + } | |
| 93 | + | |
| 94 | + /* special keywords: "prev", "previous", "current", and "next" */ | |
| 95 | + if( g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ | |
| 96 | + if( fossil_strcmp(zTag, "current")==0 ){ | |
| 97 | + rid = vid; | |
| 98 | + }else if( fossil_strcmp(zTag, "prev")==0 | |
| 99 | + || fossil_strcmp(zTag, "previous")==0 ){ | |
| 100 | + rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", vid); | |
| 101 | + }else if( fossil_strcmp(zTag, "next")==0 ){ | |
| 102 | + rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d" | |
| 103 | + " ORDER BY isprim DESC, mtime DESC", vid); | |
| 104 | + } | |
| 105 | + if( rid ) return rid; | |
| 106 | + } | |
| 107 | + | |
| 108 | + /* Date and times */ | |
| 109 | + if( memcmp(zTag, "date:", 5)==0 ){ | |
| 110 | + rid = db_int(0, | |
| 111 | + "SELECT objid FROM event" | |
| 112 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 113 | + " ORDER BY mtime DESC LIMIT 1", | |
| 114 | + &zTag[5], zType); | |
| 115 | + return rid; | |
| 116 | + } | |
| 117 | + if( is_date(zTag) ){ | |
| 118 | + rid = db_int(0, | |
| 119 | + "SELECT objid FROM event" | |
| 120 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 121 | + " ORDER BY mtime DESC LIMIT 1", | |
| 122 | + zTag, zType); | |
| 123 | + if( rid) return rid; | |
| 124 | + } | |
| 125 | + | |
| 126 | + /* Deprecated date & time formats: "local:" + date-time and | |
| 127 | + ** "utc:" + date-time */ | |
| 128 | + if( memcmp(zTag, "local:", 6)==0 ){ | |
| 129 | + rid = db_int(0, | |
| 130 | + "SELECT objid FROM event" | |
| 131 | + " WHERE mtime<=julianday(%Q) AND type GLOB '%q'" | |
| 132 | + " ORDER BY mtime DESC LIMIT 1", | |
| 133 | + &zTag[6], zType); | |
| 134 | + return rid; | |
| 135 | + } | |
| 136 | + if( memcmp(zTag, "utc:", 4)==0 ){ | |
| 137 | + rid = db_int(0, | |
| 138 | + "SELECT objid FROM event" | |
| 139 | + " WHERE mtime<=julianday('%qz') AND type GLOB '%q'" | |
| 140 | + " ORDER BY mtime DESC LIMIT 1", | |
| 141 | + &zTag[4], zType); | |
| 142 | + return rid; | |
| 143 | + } | |
| 144 | + | |
| 145 | + /* "tag:" + symbolic-name */ | |
| 146 | + if( memcmp(zTag, "tag:", 4)==0 ){ | |
| 147 | + rid = db_int(0, | |
| 148 | + "SELECT event.objid" | |
| 149 | + " FROM tag, tagxref, event" | |
| 150 | + " WHERE tag.tagname='sym-%q' " | |
| 151 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 152 | + " AND event.objid=tagxref.rid " | |
| 153 | + " AND event.type GLOB '%q'" | |
| 154 | + " ORDER BY event.mtime DESC /*sort*/", | |
| 155 | + &zTag[4], zType | |
| 156 | + ); | |
| 157 | + return rid; | |
| 158 | + } | |
| 159 | + | |
| 160 | + /* symbolic-name ":" date-time */ | |
| 161 | + nTag = strlen(zTag); | |
| 162 | + for(i=0; i<nTag-10 && zTag[i]!=':'; i++){} | |
| 163 | + if( zTag[i]==':' && is_date(&zTag[i+1]) ){ | |
| 164 | + char *zDate = mprintf("%s", &zTag[i+1]); | |
| 165 | + char *zTagBase = mprintf("%.*s", i, zTag); | |
| 166 | + int nDate = strlen(zDate); | |
| 167 | + if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ | |
| 168 | + zDate[nDate-3] = 'z'; | |
| 169 | + zDate[nDate-2] = 0; | |
| 170 | + } | |
| 171 | + rid = db_int(0, | |
| 172 | + "SELECT event.objid" | |
| 173 | + " FROM tag, tagxref, event" | |
| 174 | + " WHERE tag.tagname='sym-%q' " | |
| 175 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 176 | + " AND event.objid=tagxref.rid " | |
| 177 | + " AND event.mtime<=julianday(%Q)" | |
| 178 | + " AND event.type GLOB '%q'" | |
| 179 | + " ORDER BY event.mtime DESC /*sort*/ ", | |
| 180 | + zTagBase, zDate, zType | |
| 181 | + ); | |
| 182 | + return rid; | |
| 183 | + } | |
| 184 | + | |
| 185 | + /* SHA1 hash or prefix */ | |
| 186 | + if( nTag>=4 && nTag<=UUID_SIZE && validate16(zTag, nTag) ){ | |
| 187 | + Stmt q; | |
| 188 | + char zUuid[UUID_SIZE+1]; | |
| 189 | + memcpy(zUuid, zTag, nTag+1); | |
| 190 | + canonical16(zUuid, nTag); | |
| 191 | + rid = 0; | |
| 192 | + if( zType[0]=='*' ){ | |
| 193 | + db_prepare(&q, "SELECT rid FROM blob WHERE uuid GLOB '%s*'", zUuid); | |
| 194 | + }else{ | |
| 195 | + db_prepare(&q, | |
| 196 | + "SELECT blob.rid" | |
| 197 | + " FROM blob, event" | |
| 198 | + " WHERE blob.uuid GLOB '%s*'" | |
| 199 | + " AND event.objid=blob.rid" | |
| 200 | + " AND event.type GLOB '%q'", | |
| 201 | + zUuid, zType | |
| 202 | + ); | |
| 203 | + } | |
| 204 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 205 | + rid = db_column_int(&q, 0); | |
| 206 | + if( db_step(&q)==SQLITE_ROW ) rid = -1; | |
| 207 | + } | |
| 208 | + db_finalize(&q); | |
| 209 | + if( rid ) return rid; | |
| 210 | + } | |
| 211 | + | |
| 212 | + /* Symbolic name */ | |
| 213 | + rid = db_int(0, | |
| 214 | + "SELECT event.objid" | |
| 215 | + " FROM tag, tagxref, event" | |
| 216 | + " WHERE tag.tagname='sym-%q' " | |
| 217 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 218 | + " AND event.objid=tagxref.rid " | |
| 219 | + " AND event.type GLOB '%q'" | |
| 220 | + " ORDER BY event.mtime DESC /*sort*/ ", | |
| 221 | + zTag, zType | |
| 222 | + ); | |
| 223 | + if( rid>0 ) return rid; | |
| 224 | + | |
| 225 | + /* Undocumented: numeric tags get translated directly into the RID */ | |
| 226 | + for(i=0; fossil_isdigit(zTag[i]); i++){} | |
| 227 | + if( zTag[i]==0 ){ | |
| 228 | + rid = db_int(0, | |
| 229 | + "SELECT event.objid" | |
| 230 | + " FROM event" | |
| 231 | + " WHERE event.objid=%s" | |
| 232 | + " AND event.type GLOB '%q'", zTag, zType); | |
| 233 | + } | |
| 234 | + return rid; | |
| 235 | +} | |
| 236 | + | |
| 27 | 237 | |
| 28 | 238 | /* |
| 29 | 239 | ** This routine takes a user-entered UUID which might be in mixed |
| 30 | 240 | ** case and might only be a prefix of the full UUID and converts it |
| 31 | 241 | ** into the full-length UUID in canonical form. |
| @@ -41,228 +251,23 @@ | ||
| 41 | 251 | ** |
| 42 | 252 | ** Return 0 on success. Return 1 if the name cannot be resolved. |
| 43 | 253 | ** Return 2 name is ambiguous. |
| 44 | 254 | */ |
| 45 | 255 | int name_to_uuid(Blob *pName, int iErrPriority, const char *zType){ |
| 46 | - int rc; | |
| 47 | - int sz; | |
| 48 | - sz = blob_size(pName); | |
| 49 | - if( sz>UUID_SIZE || sz<4 || !validate16(blob_buffer(pName), sz) ){ | |
| 50 | - char *zUuid; | |
| 51 | - const char *zName = blob_str(pName); | |
| 52 | - if( memcmp(zName, "tag:", 4)==0 ){ | |
| 53 | - zName += 4; | |
| 54 | - zUuid = tag_to_uuid(zName, zType); | |
| 55 | - }else{ | |
| 56 | - zUuid = tag_to_uuid(zName, zType); | |
| 57 | - if( zUuid==0 ){ | |
| 58 | - zUuid = date_to_uuid(zName, zType); | |
| 59 | - } | |
| 60 | - } | |
| 61 | - if( zUuid ){ | |
| 62 | - blob_reset(pName); | |
| 63 | - blob_append(pName, zUuid, -1); | |
| 64 | - free(zUuid); | |
| 65 | - return 0; | |
| 66 | - } | |
| 67 | - fossil_error(iErrPriority, "not a valid object name: %s", zName); | |
| 68 | - return 1; | |
| 69 | - } | |
| 70 | - blob_materialize(pName); | |
| 71 | - canonical16(blob_buffer(pName), sz); | |
| 72 | - if( sz==UUID_SIZE ){ | |
| 73 | - rc = db_int(1, "SELECT 0 FROM blob WHERE uuid=%B", pName); | |
| 74 | - if( rc ){ | |
| 75 | - fossil_error(iErrPriority, "no such artifact: %b", pName); | |
| 76 | - blob_reset(pName); | |
| 77 | - } | |
| 78 | - }else if( sz<UUID_SIZE && sz>=4 ){ | |
| 79 | - Stmt q; | |
| 80 | - db_prepare(&q, "SELECT uuid FROM blob WHERE uuid GLOB '%b*'", pName); | |
| 81 | - if( db_step(&q)!=SQLITE_ROW ){ | |
| 82 | - char *zUuid; | |
| 83 | - db_finalize(&q); | |
| 84 | - zUuid = tag_to_uuid(blob_str(pName), "*"); | |
| 85 | - if( zUuid ){ | |
| 86 | - blob_reset(pName); | |
| 87 | - blob_append(pName, zUuid, -1); | |
| 88 | - free(zUuid); | |
| 89 | - return 0; | |
| 90 | - } | |
| 91 | - fossil_error(iErrPriority, "no artifacts match the prefix \"%b\"", pName); | |
| 92 | - return 1; | |
| 93 | - } | |
| 94 | - blob_reset(pName); | |
| 95 | - blob_append(pName, db_column_text(&q, 0), db_column_bytes(&q, 0)); | |
| 96 | - if( db_step(&q)==SQLITE_ROW ){ | |
| 97 | - fossil_error(iErrPriority, | |
| 98 | - "multiple artifacts match" | |
| 99 | - ); | |
| 100 | - blob_reset(pName); | |
| 101 | - db_finalize(&q); | |
| 102 | - return 2; | |
| 103 | - } | |
| 104 | - db_finalize(&q); | |
| 105 | - rc = 0; | |
| 106 | - }else{ | |
| 107 | - rc = 0; | |
| 108 | - } | |
| 109 | - return rc; | |
| 110 | -} | |
| 111 | - | |
| 112 | -/* | |
| 113 | -** Return TRUE if the string begins with an ISO8601 date: YYYY-MM-DD. | |
| 114 | -*/ | |
| 115 | -static int is_date(const char *z){ | |
| 116 | - if( !fossil_isdigit(z[0]) ) return 0; | |
| 117 | - if( !fossil_isdigit(z[1]) ) return 0; | |
| 118 | - if( !fossil_isdigit(z[2]) ) return 0; | |
| 119 | - if( !fossil_isdigit(z[3]) ) return 0; | |
| 120 | - if( z[4]!='-') return 0; | |
| 121 | - if( !fossil_isdigit(z[5]) ) return 0; | |
| 122 | - if( !fossil_isdigit(z[6]) ) return 0; | |
| 123 | - if( z[7]!='-') return 0; | |
| 124 | - if( !fossil_isdigit(z[8]) ) return 0; | |
| 125 | - if( !fossil_isdigit(z[9]) ) return 0; | |
| 126 | - return 1; | |
| 127 | -} | |
| 128 | - | |
| 129 | -/* | |
| 130 | -** Convert a symbolic tag name into the UUID of a check-in that contains | |
| 131 | -** that tag. If the tag appears on multiple check-ins, return the UUID | |
| 132 | -** of the most recent check-in with the tag. | |
| 133 | -** | |
| 134 | -** If the input string is of the form: | |
| 135 | -** | |
| 136 | -** tag:date | |
| 137 | -** | |
| 138 | -** Then return the UUID of the oldest check-in with that tag that is | |
| 139 | -** not older than 'date'. | |
| 140 | -** | |
| 141 | -** An input of "tip" returns the most recent check-in. | |
| 142 | -** | |
| 143 | -** Memory to hold the returned string comes from malloc() and needs to | |
| 144 | -** be freed by the caller. | |
| 145 | -*/ | |
| 146 | -char *tag_to_uuid(const char *zTag, const char *zType){ | |
| 147 | - int vid; | |
| 148 | - char *zUuid; | |
| 149 | - | |
| 150 | - if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 151 | - zUuid = db_text(0, | |
| 152 | - "SELECT blob.uuid" | |
| 153 | - " FROM tag, tagxref, event, blob" | |
| 154 | - " WHERE tag.tagname='sym-%q' " | |
| 155 | - " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 156 | - " AND event.objid=tagxref.rid " | |
| 157 | - " AND blob.rid=event.objid " | |
| 158 | - " AND event.type GLOB '%q'" | |
| 159 | - " ORDER BY event.mtime DESC /*sort*/", | |
| 160 | - zTag, zType | |
| 161 | - ); | |
| 162 | - if( zUuid==0 ){ | |
| 163 | - int nTag = strlen(zTag); | |
| 164 | - int i; | |
| 165 | - for(i=0; i<nTag-10; i++){ | |
| 166 | - if( zTag[i]==':' && is_date(&zTag[i+1]) ){ | |
| 167 | - char *zDate = mprintf("%s", &zTag[i+1]); | |
| 168 | - char *zTagBase = mprintf("%.*s", i, zTag); | |
| 169 | - int nDate = strlen(zDate); | |
| 170 | - int useUtc = 0; | |
| 171 | - if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ | |
| 172 | - nDate -= 3; | |
| 173 | - zDate[nDate] = 0; | |
| 174 | - useUtc = 1; | |
| 175 | - } | |
| 176 | - zUuid = db_text(0, | |
| 177 | - "SELECT blob.uuid" | |
| 178 | - " FROM tag, tagxref, event, blob" | |
| 179 | - " WHERE tag.tagname='sym-%q' " | |
| 180 | - " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " | |
| 181 | - " AND event.objid=tagxref.rid " | |
| 182 | - " AND blob.rid=event.objid " | |
| 183 | - " AND event.mtime<=julianday(%Q %s)" | |
| 184 | - " AND event.type GLOB '%q'" | |
| 185 | - " ORDER BY event.mtime DESC /*sort*/ ", | |
| 186 | - zTagBase, zDate, (useUtc ? "" : ",'utc'"), zType | |
| 187 | - ); | |
| 188 | - break; | |
| 189 | - } | |
| 190 | - } | |
| 191 | - if( zUuid==0 && fossil_strcmp(zTag, "tip")==0 ){ | |
| 192 | - zUuid = db_text(0, | |
| 193 | - "SELECT blob.uuid" | |
| 194 | - " FROM event, blob" | |
| 195 | - " WHERE event.type='ci'" | |
| 196 | - " AND blob.rid=event.objid" | |
| 197 | - " ORDER BY event.mtime DESC" | |
| 198 | - ); | |
| 199 | - } | |
| 200 | - if( zUuid==0 && g.localOpen && (vid=db_lget_int("checkout",0))!=0 ){ | |
| 201 | - if( fossil_strcmp(zTag, "current")==0 ){ | |
| 202 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); | |
| 203 | - }else if( fossil_strcmp(zTag, "prev")==0 | |
| 204 | - || fossil_strcmp(zTag, "previous")==0 ){ | |
| 205 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" | |
| 206 | - "(SELECT pid FROM plink WHERE cid=%d AND isprim)", | |
| 207 | - vid); | |
| 208 | - }else if( fossil_strcmp(zTag, "next")==0 ){ | |
| 209 | - zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=" | |
| 210 | - "(SELECT cid FROM plink WHERE pid=%d" | |
| 211 | - " ORDER BY isprim DESC, mtime DESC)", | |
| 212 | - vid); | |
| 213 | - } | |
| 214 | - } | |
| 215 | - } | |
| 216 | - return zUuid; | |
| 217 | -} | |
| 218 | - | |
| 219 | -/* | |
| 220 | -** Convert a date/time string into a UUID. | |
| 221 | -** | |
| 222 | -** Input forms accepted: | |
| 223 | -** | |
| 224 | -** date:DATE | |
| 225 | -** local:DATE | |
| 226 | -** utc:DATE | |
| 227 | -** | |
| 228 | -** The DATE is interpreted as localtime unless the "utc:" prefix is used | |
| 229 | -** or a "utc" string appears at the end of the DATE string. | |
| 230 | -*/ | |
| 231 | -char *date_to_uuid(const char *zDate, const char *zType){ | |
| 232 | - int useUtc = 0; | |
| 233 | - int n; | |
| 234 | - char *zCopy = 0; | |
| 235 | - char *zUuid; | |
| 236 | - | |
| 237 | - if( memcmp(zDate, "date:", 5)==0 ){ | |
| 238 | - zDate += 5; | |
| 239 | - }else if( memcmp(zDate, "local:", 6)==0 ){ | |
| 240 | - zDate += 6; | |
| 241 | - }else if( memcmp(zDate, "utc:", 4)==0 ){ | |
| 242 | - zDate += 4; | |
| 243 | - useUtc = 1; | |
| 244 | - } | |
| 245 | - n = strlen(zDate); | |
| 246 | - if( n<10 || !is_date(zDate) ) return 0; | |
| 247 | - if( n>4 && sqlite3_strnicmp(&zDate[n-3], "utc", 3)==0 ){ | |
| 248 | - zCopy = mprintf("%s", zDate); | |
| 249 | - zCopy[n-3] = 0; | |
| 250 | - zDate = zCopy; | |
| 251 | - n -= 3; | |
| 252 | - useUtc = 1; | |
| 253 | - } | |
| 254 | - if( zType==0 || zType[0]==0 ) zType = "*"; | |
| 255 | - zUuid = db_text(0, | |
| 256 | - "SELECT (SELECT uuid FROM blob WHERE rid=event.objid)" | |
| 257 | - " FROM event" | |
| 258 | - " WHERE mtime<=julianday(%Q %s) AND type GLOB '%q'" | |
| 259 | - " ORDER BY mtime DESC LIMIT 1", | |
| 260 | - zDate, useUtc ? "" : ",'utc'", zType | |
| 261 | - ); | |
| 262 | - free(zCopy); | |
| 263 | - return zUuid; | |
| 256 | + char *zName = blob_str(pName); | |
| 257 | + int rid = symbolic_name_to_rid(zName, zType); | |
| 258 | + if( rid<0 ){ | |
| 259 | + fossil_error(iErrPriority, "ambiguous name: %s", zName); | |
| 260 | + return 2; | |
| 261 | + }else if( rid==0 ){ | |
| 262 | + fossil_error(iErrPriority, "not found: %s", zName); | |
| 263 | + return 1; | |
| 264 | + }else{ | |
| 265 | + blob_reset(pName); | |
| 266 | + db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid); | |
| 267 | + return 0; | |
| 268 | + } | |
| 264 | 269 | } |
| 265 | 270 | |
| 266 | 271 | /* |
| 267 | 272 | ** COMMAND: test-name-to-id |
| 268 | 273 | ** |
| @@ -284,41 +289,38 @@ | ||
| 284 | 289 | blob_reset(&name); |
| 285 | 290 | } |
| 286 | 291 | } |
| 287 | 292 | |
| 288 | 293 | /* |
| 289 | -** Convert a name to a rid. If the name is a small integer value then | |
| 290 | -** just use atoi() to do the conversion. If the name contains alphabetic | |
| 291 | -** characters or is not an existing rid, then use name_to_uuid then | |
| 292 | -** convert the uuid to a rid. | |
| 294 | +** Convert a name to a rid. If the name can be any of the various forms | |
| 295 | +** accepted: | |
| 296 | +** | |
| 297 | +** * SHA1 hash or prefix thereof | |
| 298 | +** * symbolic name | |
| 299 | +** * date | |
| 300 | +** * label:date | |
| 301 | +** * prev, previous | |
| 302 | +** * next | |
| 303 | +** * tip | |
| 293 | 304 | ** |
| 294 | 305 | ** This routine is used by command-line routines to resolve command-line inputs |
| 295 | 306 | ** into a rid. |
| 296 | 307 | */ |
| 297 | 308 | int name_to_typed_rid(const char *zName, const char *zType){ |
| 298 | - int i; | |
| 299 | 309 | int rid; |
| 300 | - Blob name; | |
| 301 | 310 | |
| 302 | 311 | if( zName==0 || zName[0]==0 ) return 0; |
| 303 | - blob_init(&name, zName, -1); | |
| 304 | - if( name_to_uuid(&name, -1, zType) ){ | |
| 305 | - blob_reset(&name); | |
| 306 | - for(i=0; zName[i] && fossil_isdigit(zName[i]); i++){} | |
| 307 | - if( zName[i]==0 ){ | |
| 308 | - rid = atoi(zName); | |
| 309 | - if( db_exists("SELECT 1 FROM blob WHERE rid=%d", rid) ){ | |
| 310 | - return rid; | |
| 311 | - } | |
| 312 | - } | |
| 313 | - fossil_error(1, "no such artifact: %s", zName); | |
| 312 | + rid = symbolic_name_to_rid(zName, zType); | |
| 313 | + if( rid<0 ){ | |
| 314 | + fossil_error(1, "ambiguous name: %s", zName); | |
| 315 | + return 0; | |
| 316 | + }else if( rid==0 ){ | |
| 317 | + fossil_error(1, "not found: %s", zName); | |
| 314 | 318 | return 0; |
| 315 | 319 | }else{ |
| 316 | - rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%B", &name); | |
| 317 | - blob_reset(&name); | |
| 320 | + return rid; | |
| 318 | 321 | } |
| 319 | - return rid; | |
| 320 | 322 | } |
| 321 | 323 | int name_to_rid(const char *zName){ |
| 322 | 324 | return name_to_typed_rid(zName, "*"); |
| 323 | 325 | } |
| 324 | 326 | |
| @@ -362,32 +364,104 @@ | ||
| 362 | 364 | ** rid. If the CGI parameter is missing or is not a valid artifact tag, |
| 363 | 365 | ** return 0. If the CGI parameter is ambiguous, redirect to a page that |
| 364 | 366 | ** shows all possibilities and do not return. |
| 365 | 367 | */ |
| 366 | 368 | int name_to_rid_www(const char *zParamName){ |
| 367 | - int i, rc; | |
| 368 | 369 | int rid; |
| 369 | 370 | const char *zName = P(zParamName); |
| 370 | - Blob name; | |
| 371 | - | |
| 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 | + } | |
| 393 | 467 | } |
| 394 | 468 |
| --- 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 @@ | ||
| 545 | 545 | buf[0] = '%'; |
| 546 | 546 | bufpt = buf; |
| 547 | 547 | length = 1; |
| 548 | 548 | break; |
| 549 | 549 | case etCHARX: |
| 550 | - c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt); | |
| 550 | + c = buf[0] = va_arg(ap,int); | |
| 551 | 551 | if( precision>=0 ){ |
| 552 | 552 | for(idx=1; idx<precision; idx++) buf[idx] = c; |
| 553 | 553 | length = precision; |
| 554 | 554 | }else{ |
| 555 | 555 | length =1; |
| 556 | 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] = (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 @@ | ||
| 370 | 370 | " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))" |
| 371 | 371 | ); |
| 372 | 372 | db_multi_exec( |
| 373 | 373 | "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" |
| 374 | 374 | ); |
| 375 | - totalSize = db_int(0, "SELECT count(*) FROM blob"); | |
| 375 | + | |
| 376 | + /* The following should be count(*) instead of max(rid). max(rid) is | |
| 377 | + ** an adequate approximation, however, and is much faster for large | |
| 378 | + ** repositories. */ | |
| 379 | + totalSize = db_int(0, "SELECT max(rid) FROM blob"); | |
| 376 | 380 | incrSize = totalSize/100; |
| 377 | 381 | totalSize += incrSize*2; |
| 378 | 382 | db_prepare(&s, |
| 379 | 383 | "SELECT rid, size FROM blob /*scan*/" |
| 380 | 384 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| @@ -483,11 +487,11 @@ | ||
| 483 | 487 | |
| 484 | 488 | db_end_transaction(0); |
| 485 | 489 | } |
| 486 | 490 | |
| 487 | 491 | /* |
| 488 | -** COMMAND: rebuild | |
| 492 | +** COMMAND: rebuild | |
| 489 | 493 | ** |
| 490 | 494 | ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS? |
| 491 | 495 | ** |
| 492 | 496 | ** Reconstruct the named repository database from the core |
| 493 | 497 | ** records. Run this command after updating the fossil |
| @@ -716,11 +720,11 @@ | ||
| 716 | 720 | db_finalize(&q); |
| 717 | 721 | } |
| 718 | 722 | } |
| 719 | 723 | |
| 720 | 724 | /* |
| 721 | -** COMMAND: scrub | |
| 725 | +** COMMAND: scrub* | |
| 722 | 726 | ** %fossil scrub ?OPTIONS? ?REPOSITORY? |
| 723 | 727 | ** |
| 724 | 728 | ** The command removes sensitive information (such as passwords) from a |
| 725 | 729 | ** repository so that the respository can be sent to an untrusted reader. |
| 726 | 730 | ** |
| @@ -850,11 +854,11 @@ | ||
| 850 | 854 | } |
| 851 | 855 | fossil_mbcs_free(zMbcsPath); |
| 852 | 856 | } |
| 853 | 857 | |
| 854 | 858 | /* |
| 855 | -** COMMAND: reconstruct | |
| 859 | +** COMMAND: reconstruct* | |
| 856 | 860 | ** |
| 857 | 861 | ** Usage: %fossil reconstruct FILENAME DIRECTORY |
| 858 | 862 | ** |
| 859 | 863 | ** This command studies the artifacts (files) in DIRECTORY and |
| 860 | 864 | ** reconstructs the fossil record from them. It places the new |
| @@ -911,11 +915,11 @@ | ||
| 911 | 915 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 912 | 916 | fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 913 | 917 | } |
| 914 | 918 | |
| 915 | 919 | /* |
| 916 | -** COMMAND: deconstruct | |
| 920 | +** COMMAND: deconstruct* | |
| 917 | 921 | ** |
| 918 | 922 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 919 | 923 | ** |
| 920 | 924 | ** |
| 921 | 925 | ** This command exports all artifacts of a given repository and |
| 922 | 926 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -370,11 +370,15 @@ | |
| 370 | " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))" |
| 371 | ); |
| 372 | db_multi_exec( |
| 373 | "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" |
| 374 | ); |
| 375 | totalSize = db_int(0, "SELECT count(*) FROM blob"); |
| 376 | incrSize = totalSize/100; |
| 377 | totalSize += incrSize*2; |
| 378 | db_prepare(&s, |
| 379 | "SELECT rid, size FROM blob /*scan*/" |
| 380 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| @@ -483,11 +487,11 @@ | |
| 483 | |
| 484 | db_end_transaction(0); |
| 485 | } |
| 486 | |
| 487 | /* |
| 488 | ** COMMAND: rebuild |
| 489 | ** |
| 490 | ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS? |
| 491 | ** |
| 492 | ** Reconstruct the named repository database from the core |
| 493 | ** records. Run this command after updating the fossil |
| @@ -716,11 +720,11 @@ | |
| 716 | db_finalize(&q); |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | /* |
| 721 | ** COMMAND: scrub |
| 722 | ** %fossil scrub ?OPTIONS? ?REPOSITORY? |
| 723 | ** |
| 724 | ** The command removes sensitive information (such as passwords) from a |
| 725 | ** repository so that the respository can be sent to an untrusted reader. |
| 726 | ** |
| @@ -850,11 +854,11 @@ | |
| 850 | } |
| 851 | fossil_mbcs_free(zMbcsPath); |
| 852 | } |
| 853 | |
| 854 | /* |
| 855 | ** COMMAND: reconstruct |
| 856 | ** |
| 857 | ** Usage: %fossil reconstruct FILENAME DIRECTORY |
| 858 | ** |
| 859 | ** This command studies the artifacts (files) in DIRECTORY and |
| 860 | ** reconstructs the fossil record from them. It places the new |
| @@ -911,11 +915,11 @@ | |
| 911 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 912 | fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 913 | } |
| 914 | |
| 915 | /* |
| 916 | ** COMMAND: deconstruct |
| 917 | ** |
| 918 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 919 | ** |
| 920 | ** |
| 921 | ** This command exports all artifacts of a given repository and |
| 922 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -370,11 +370,15 @@ | |
| 370 | " WHERE rid IN (SELECT rid FROM shun JOIN blob USING(uuid))" |
| 371 | ); |
| 372 | db_multi_exec( |
| 373 | "DELETE FROM config WHERE name IN ('remote-code', 'remote-maxid')" |
| 374 | ); |
| 375 | |
| 376 | /* The following should be count(*) instead of max(rid). max(rid) is |
| 377 | ** an adequate approximation, however, and is much faster for large |
| 378 | ** repositories. */ |
| 379 | totalSize = db_int(0, "SELECT max(rid) FROM blob"); |
| 380 | incrSize = totalSize/100; |
| 381 | totalSize += incrSize*2; |
| 382 | db_prepare(&s, |
| 383 | "SELECT rid, size FROM blob /*scan*/" |
| 384 | " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| @@ -483,11 +487,11 @@ | |
| 487 | |
| 488 | db_end_transaction(0); |
| 489 | } |
| 490 | |
| 491 | /* |
| 492 | ** COMMAND: rebuild |
| 493 | ** |
| 494 | ** Usage: %fossil rebuild ?REPOSITORY? ?OPTIONS? |
| 495 | ** |
| 496 | ** Reconstruct the named repository database from the core |
| 497 | ** records. Run this command after updating the fossil |
| @@ -716,11 +720,11 @@ | |
| 720 | db_finalize(&q); |
| 721 | } |
| 722 | } |
| 723 | |
| 724 | /* |
| 725 | ** COMMAND: scrub* |
| 726 | ** %fossil scrub ?OPTIONS? ?REPOSITORY? |
| 727 | ** |
| 728 | ** The command removes sensitive information (such as passwords) from a |
| 729 | ** repository so that the respository can be sent to an untrusted reader. |
| 730 | ** |
| @@ -850,11 +854,11 @@ | |
| 854 | } |
| 855 | fossil_mbcs_free(zMbcsPath); |
| 856 | } |
| 857 | |
| 858 | /* |
| 859 | ** COMMAND: reconstruct* |
| 860 | ** |
| 861 | ** Usage: %fossil reconstruct FILENAME DIRECTORY |
| 862 | ** |
| 863 | ** This command studies the artifacts (files) in DIRECTORY and |
| 864 | ** reconstructs the fossil record from them. It places the new |
| @@ -911,11 +915,11 @@ | |
| 915 | zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); |
| 916 | fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); |
| 917 | } |
| 918 | |
| 919 | /* |
| 920 | ** COMMAND: deconstruct* |
| 921 | ** |
| 922 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 923 | ** |
| 924 | ** |
| 925 | ** This command exports all artifacts of a given repository and |
| 926 |
+7
-19
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -19,10 +19,13 @@ | ||
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include <time.h> |
| 22 | 22 | #include "report.h" |
| 23 | 23 | #include <assert.h> |
| 24 | +#ifdef FOSSIL_ENABLE_JSON | |
| 25 | +# include "cson_amalgamation.h" | |
| 26 | +#endif | |
| 24 | 27 | |
| 25 | 28 | /* Forward references to static routines */ |
| 26 | 29 | static void report_format_hints(void); |
| 27 | 30 | |
| 28 | 31 | /* |
| @@ -630,17 +633,11 @@ | ||
| 630 | 633 | char **azName /* Names of the columns */ |
| 631 | 634 | ){ |
| 632 | 635 | struct GenerateHTML *pState = (struct GenerateHTML*)pUser; |
| 633 | 636 | int i; |
| 634 | 637 | const char *zTid; /* Ticket UUID. (value of column named '#') */ |
| 635 | - int rn; /* Report number */ | |
| 636 | 638 | 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 | 639 | |
| 643 | 640 | /* Do initialization |
| 644 | 641 | */ |
| 645 | 642 | if( pState->nCount==0 ){ |
| 646 | 643 | /* Turn off the authorizer. It is no longer doing anything since the |
| @@ -719,11 +716,10 @@ | ||
| 719 | 716 | */ |
| 720 | 717 | zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0; |
| 721 | 718 | if( zBg==0 ) zBg = "white"; |
| 722 | 719 | @ <tr style="background-color:%h(zBg)"> |
| 723 | 720 | zTid = 0; |
| 724 | - zPage[0] = 0; | |
| 725 | 721 | for(i=0; i<nArg; i++){ |
| 726 | 722 | char *zData; |
| 727 | 723 | if( i==pState->iBg ) continue; |
| 728 | 724 | zData = azArg[i]; |
| 729 | 725 | if( zData==0 ) zData = ""; |
| @@ -1108,41 +1104,32 @@ | ||
| 1108 | 1104 | const char *zFilter, |
| 1109 | 1105 | tTktShowEncoding enc |
| 1110 | 1106 | ){ |
| 1111 | 1107 | Stmt q; |
| 1112 | 1108 | char *zSql; |
| 1113 | - const char *zTitle; | |
| 1114 | - const char *zOwner; | |
| 1115 | - const char *zClrKey; | |
| 1116 | 1109 | char *zErr1 = 0; |
| 1117 | 1110 | char *zErr2 = 0; |
| 1118 | 1111 | int count = 0; |
| 1119 | 1112 | int rn; |
| 1120 | 1113 | |
| 1121 | 1114 | if (!zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){ |
| 1122 | - zTitle = zFullTicketRptTitle; | |
| 1123 | 1115 | zSql = "SELECT * FROM ticket"; |
| 1124 | - zOwner = g.zLogin; | |
| 1125 | - zClrKey = ""; | |
| 1126 | 1116 | }else{ |
| 1127 | 1117 | rn = atoi(zRep); |
| 1128 | 1118 | if( rn ){ |
| 1129 | 1119 | db_prepare(&q, |
| 1130 | - "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE rn=%d", rn); | |
| 1120 | + "SELECT sqlcode FROM reportfmt WHERE rn=%d", rn); | |
| 1131 | 1121 | }else{ |
| 1132 | 1122 | db_prepare(&q, |
| 1133 | - "SELECT title, sqlcode, owner, cols FROM reportfmt WHERE title='%s'", zRep); | |
| 1123 | + "SELECT sqlcode FROM reportfmt WHERE title=%Q", zRep); | |
| 1134 | 1124 | } |
| 1135 | 1125 | if( db_step(&q)!=SQLITE_ROW ){ |
| 1136 | 1126 | db_finalize(&q); |
| 1137 | 1127 | rpt_list_reports(); |
| 1138 | 1128 | fossil_fatal("unknown report format(%s)!",zRep); |
| 1139 | 1129 | } |
| 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); | |
| 1144 | 1131 | db_finalize(&q); |
| 1145 | 1132 | } |
| 1146 | 1133 | if( zFilter ){ |
| 1147 | 1134 | zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter); |
| 1148 | 1135 | } |
| @@ -1154,5 +1141,6 @@ | ||
| 1154 | 1141 | report_unrestrict_sql(); |
| 1155 | 1142 | if( zFilter ){ |
| 1156 | 1143 | free(zSql); |
| 1157 | 1144 | } |
| 1158 | 1145 | } |
| 1146 | + | |
| 1159 | 1147 |
| --- 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 @@ | ||
| 251 | 251 | @ CREATE TABLE leaf(rid INTEGER PRIMARY KEY); |
| 252 | 252 | @ |
| 253 | 253 | @ -- Events used to generate a timeline |
| 254 | 254 | @ -- |
| 255 | 255 | @ CREATE TABLE event( |
| 256 | -@ type TEXT, -- Type of event: 'ci', 'w', 'e', 't' | |
| 256 | +@ type TEXT, -- Type of event: 'ci', 'w', 'e', 't', 'g' | |
| 257 | 257 | @ mtime DATETIME, -- Time of occurrence. Julian day. |
| 258 | 258 | @ objid INTEGER PRIMARY KEY, -- Associated record ID |
| 259 | 259 | @ tagid INTEGER, -- Associated ticket or wiki name tag |
| 260 | 260 | @ uid INTEGER REFERENCES user, -- User who caused the event |
| 261 | 261 | @ bgcolor TEXT, -- Color set by 'bgcolor' property |
| 262 | 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' |
| 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 @@ | ||
| 165 | 165 | } |
| 166 | 166 | |
| 167 | 167 | /* |
| 168 | 168 | ** Testing the search function. |
| 169 | 169 | ** |
| 170 | -** COMMAND: search | |
| 170 | +** COMMAND: search* | |
| 171 | 171 | ** %fossil search pattern... |
| 172 | 172 | ** |
| 173 | 173 | ** Search for timeline entries matching the pattern. |
| 174 | 174 | */ |
| 175 | 175 | void search_cmd(void){ |
| 176 | 176 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -165,11 +165,11 @@ | |
| 165 | } |
| 166 | |
| 167 | /* |
| 168 | ** Testing the search function. |
| 169 | ** |
| 170 | ** COMMAND: search |
| 171 | ** %fossil search pattern... |
| 172 | ** |
| 173 | ** Search for timeline entries matching the pattern. |
| 174 | */ |
| 175 | void search_cmd(void){ |
| 176 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -165,11 +165,11 @@ | |
| 165 | } |
| 166 | |
| 167 | /* |
| 168 | ** Testing the search function. |
| 169 | ** |
| 170 | ** COMMAND: search* |
| 171 | ** %fossil search pattern... |
| 172 | ** |
| 173 | ** Search for timeline entries matching the pattern. |
| 174 | */ |
| 175 | void search_cmd(void){ |
| 176 |
+19
-1
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -250,11 +250,11 @@ | ||
| 250 | 250 | char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap; |
| 251 | 251 | char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae; |
| 252 | 252 | char *oat, *oau, *oav, *oab, *oax, *oaz; |
| 253 | 253 | const char *zGroup; |
| 254 | 254 | const char *zOldLogin; |
| 255 | - const char *inherit[128]; | |
| 255 | + char *inherit[128]; | |
| 256 | 256 | int doWrite; |
| 257 | 257 | int uid; |
| 258 | 258 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 259 | 259 | /* user doing the editing is ADMIN. Disallow editing */ |
| 260 | 260 | |
| @@ -864,10 +864,17 @@ | ||
| 864 | 864 | "remote_user_ok", "remote_user_ok", 0); |
| 865 | 865 | @ <p>When enabled, if the REMOTE_USER environment variable is set to the |
| 866 | 866 | @ login name of a valid user and no other login credentials are available, |
| 867 | 867 | @ then the REMOTE_USER is accepted as an authenticated user. |
| 868 | 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> | |
| 869 | 876 | @ |
| 870 | 877 | @ <hr /> |
| 871 | 878 | entry_attribute("Login expiration time", 6, "cookie-expire", "cex", "8766"); |
| 872 | 879 | @ <p>The number of hours for which a login is valid. This must be a |
| 873 | 880 | @ positive number. The default is 8760 hours which is approximately equal |
| @@ -879,10 +886,21 @@ | ||
| 879 | 886 | @ <p>Fossil tries to limit out-bound sync, clone, and pull packets |
| 880 | 887 | @ to this many bytes, uncompressed. If the client requires more data |
| 881 | 888 | @ than this, then the client will issue multiple HTTP requests. |
| 882 | 889 | @ Values below 1 million are not recommended. 5 million is a |
| 883 | 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> | |
| 884 | 902 | |
| 885 | 903 | @ <hr /> |
| 886 | 904 | onoff_attribute("Allow users to register themselves", |
| 887 | 905 | "self-register", "selfregister", 0); |
| 888 | 906 | @ <p>Allow users to register themselves through the HTTP UI. |
| 889 | 907 |
| --- 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 @@ | ||
| 437 | 437 | sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1, |
| 438 | 438 | fossil_free); |
| 439 | 439 | } |
| 440 | 440 | |
| 441 | 441 | /* |
| 442 | -** COMMAND: sha1sum | |
| 442 | +** COMMAND: sha1sum* | |
| 443 | 443 | ** %fossil sha1sum FILE... |
| 444 | 444 | ** |
| 445 | 445 | ** Compute an SHA1 checksum of all files named on the command-line. |
| 446 | 446 | ** If an file is named "-" then take its content from standard input. |
| 447 | 447 | */ |
| 448 | 448 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -437,11 +437,11 @@ | |
| 437 | sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1, |
| 438 | fossil_free); |
| 439 | } |
| 440 | |
| 441 | /* |
| 442 | ** COMMAND: sha1sum |
| 443 | ** %fossil sha1sum FILE... |
| 444 | ** |
| 445 | ** Compute an SHA1 checksum of all files named on the command-line. |
| 446 | ** If an file is named "-" then take its content from standard input. |
| 447 | */ |
| 448 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -437,11 +437,11 @@ | |
| 437 | sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1, |
| 438 | fossil_free); |
| 439 | } |
| 440 | |
| 441 | /* |
| 442 | ** COMMAND: sha1sum* |
| 443 | ** %fossil sha1sum FILE... |
| 444 | ** |
| 445 | ** Compute an SHA1 checksum of all files named on the command-line. |
| 446 | ** If an file is named "-" then take its content from standard input. |
| 447 | */ |
| 448 |
+278
-68
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -86,29 +86,30 @@ | ||
| 86 | 86 | @ text-align: center; |
| 87 | 87 | @ letter-spacing: 1px; |
| 88 | 88 | @ background-color: #404040; |
| 89 | 89 | @ color: white; |
| 90 | 90 | @ } |
| 91 | -@ | |
| 91 | +@ | |
| 92 | 92 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 93 | -@ div.submenu { | |
| 93 | +@ div.submenu, div.sectionmenu { | |
| 94 | 94 | @ padding: 3px 10px 3px 0px; |
| 95 | 95 | @ font-size: 0.9em; |
| 96 | 96 | @ text-align: center; |
| 97 | 97 | @ background-color: #606060; |
| 98 | 98 | @ color: white; |
| 99 | 99 | @ } |
| 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 { | |
| 101 | 102 | @ padding: 3px 10px 3px 10px; |
| 102 | 103 | @ color: white; |
| 103 | 104 | @ text-decoration: none; |
| 104 | 105 | @ } |
| 105 | -@ div.mainmenu a:hover, div.submenu a:hover { | |
| 106 | +@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 106 | 107 | @ color: #404040; |
| 107 | 108 | @ background-color: white; |
| 108 | 109 | @ } |
| 109 | -@ | |
| 110 | +@ | |
| 110 | 111 | @ /* All page content from the bottom of the menu or submenu down to |
| 111 | 112 | @ ** the footer */ |
| 112 | 113 | @ div.content { |
| 113 | 114 | @ padding: 0ex 0ex 0ex 0ex; |
| 114 | 115 | @ } |
| @@ -152,10 +153,55 @@ | ||
| 152 | 153 | @ /* The label/value pairs on (for example) the vinfo page */ |
| 153 | 154 | @ table.label-value th { |
| 154 | 155 | @ vertical-align: top; |
| 155 | 156 | @ text-align: right; |
| 156 | 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); | |
| 157 | 203 | @ }'); |
| 158 | 204 | @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> |
| 159 | 205 | @ <head> |
| 160 | 206 | @ <title>$<project_name>: $<title></title> |
| 161 | 207 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| @@ -163,49 +209,47 @@ | ||
| 163 | 209 | @ <link rel="stylesheet" href="$home/style.css?blackwhite" type="text/css" |
| 164 | 210 | @ media="screen"> |
| 165 | 211 | @ </head> |
| 166 | 212 | @ <body> |
| 167 | 213 | @ <div class="header"> |
| 168 | -@ <div class="logo"> | |
| 169 | -@ <img src="$home/logo" alt="logo"> | |
| 170 | -@ </div> | |
| 171 | 214 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 172 | 215 | @ <div class="status"><nobr><th1> |
| 173 | 216 | @ if {[info exists login]} { |
| 174 | 217 | @ puts "Logged in as $login" |
| 175 | 218 | @ } else { |
| 176 | 219 | @ puts "Not logged in" |
| 177 | 220 | @ } |
| 178 | 221 | @ </th1></nobr></div> |
| 179 | 222 | @ </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" | |
| 182 | 226 | @ if {[anycap jor]} { |
| 183 | -@ html "<a href=''$home/timeline''>Timeline</a> " | |
| 227 | +@ html "<a href=''$home/timeline''>Timeline</a>\n" | |
| 184 | 228 | @ } |
| 185 | 229 | @ if {[hascap oh]} { |
| 186 | -@ html "<a href=''$home/dir?ci=tip''>Files</a> " | |
| 230 | +@ html "<a href=''$home/dir?ci=tip''>Files</a>\n" | |
| 187 | 231 | @ } |
| 188 | 232 | @ 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" | |
| 191 | 235 | @ } |
| 192 | 236 | @ if {[hascap r]} { |
| 193 | -@ html "<a href=''$home/reportlist''>Tickets</a> " | |
| 237 | +@ html "<a href=''$home/reportlist''>Tickets</a>\n" | |
| 194 | 238 | @ } |
| 195 | 239 | @ if {[hascap j]} { |
| 196 | -@ html "<a href=''$home/wiki''>Wiki</a> " | |
| 240 | +@ html "<a href=''$home/wiki''>Wiki</a>\n" | |
| 197 | 241 | @ } |
| 198 | 242 | @ if {[hascap s]} { |
| 199 | -@ html "<a href=''$home/setup''>Admin</a> " | |
| 243 | +@ html "<a href=''$home/setup''>Admin</a>\n" | |
| 200 | 244 | @ } elseif {[hascap a]} { |
| 201 | -@ html "<a href=''$home/setup_ulist''>Users</a> " | |
| 245 | +@ html "<a href=''$home/setup_ulist''>Users</a>\n" | |
| 202 | 246 | @ } |
| 203 | 247 | @ if {[info exists login]} { |
| 204 | -@ html "<a href=''$home/login''>Logout</a> " | |
| 248 | +@ html "<a href=''$home/login''>Logout</a>\n" | |
| 205 | 249 | @ } else { |
| 206 | -@ html "<a href=''$home/login''>Login</a> " | |
| 250 | +@ html "<a href=''$home/login''>Login</a>\n" | |
| 207 | 251 | @ } |
| 208 | 252 | @ </th1></div> |
| 209 | 253 | @ '); |
| 210 | 254 | @ REPLACE INTO config(name,mtime,value) |
| 211 | 255 | @ VALUES('footer',now(),'<div class="footer"> |
| @@ -279,23 +323,24 @@ | ||
| 279 | 323 | @ background-color: #a09048; |
| 280 | 324 | @ color: black; |
| 281 | 325 | @ } |
| 282 | 326 | @ |
| 283 | 327 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 284 | -@ div.submenu { | |
| 328 | +@ div.submenu, div.sectionmenu { | |
| 285 | 329 | @ padding: 3px 10px 3px 0px; |
| 286 | 330 | @ font-size: 0.9em; |
| 287 | 331 | @ text-align: center; |
| 288 | 332 | @ background-color: #c0af58; |
| 289 | 333 | @ color: white; |
| 290 | 334 | @ } |
| 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 { | |
| 292 | 337 | @ padding: 3px 10px 3px 10px; |
| 293 | 338 | @ color: white; |
| 294 | 339 | @ text-decoration: none; |
| 295 | 340 | @ } |
| 296 | -@ div.mainmenu a:hover, div.submenu a:hover { | |
| 341 | +@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 297 | 342 | @ color: #a09048; |
| 298 | 343 | @ background-color: white; |
| 299 | 344 | @ } |
| 300 | 345 | @ |
| 301 | 346 | @ /* All page content from the bottom of the menu or submenu down to |
| @@ -356,11 +401,54 @@ | ||
| 356 | 401 | @ table.label-value th { |
| 357 | 402 | @ vertical-align: top; |
| 358 | 403 | @ text-align: right; |
| 359 | 404 | @ padding: 0.2ex 2ex; |
| 360 | 405 | @ } |
| 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 | +@ }'); | |
| 362 | 450 | @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> |
| 363 | 451 | @ <head> |
| 364 | 452 | @ <title>$<project_name>: $<title></title> |
| 365 | 453 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 366 | 454 | @ href="$home/timeline.rss"> |
| @@ -378,37 +466,38 @@ | ||
| 378 | 466 | @ } else { |
| 379 | 467 | @ puts "Not logged in" |
| 380 | 468 | @ } |
| 381 | 469 | @ </th1></nobr></div> |
| 382 | 470 | @ </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" | |
| 385 | 474 | @ if {[anycap jor]} { |
| 386 | -@ html "<a href=''$home/timeline''>Timeline</a> " | |
| 475 | +@ html "<a href=''$home/timeline''>Timeline</a>\n" | |
| 387 | 476 | @ } |
| 388 | 477 | @ if {[hascap oh]} { |
| 389 | -@ html "<a href=''$home/dir?ci=tip''>Files</a> " | |
| 478 | +@ html "<a href=''$home/dir?ci=tip''>Files</a>\n" | |
| 390 | 479 | @ } |
| 391 | 480 | @ 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" | |
| 394 | 483 | @ } |
| 395 | 484 | @ if {[hascap r]} { |
| 396 | -@ html "<a href=''$home/reportlist''>Tickets</a> " | |
| 485 | +@ html "<a href=''$home/reportlist''>Tickets</a>\n" | |
| 397 | 486 | @ } |
| 398 | 487 | @ if {[hascap j]} { |
| 399 | -@ html "<a href=''$home/wiki''>Wiki</a> " | |
| 488 | +@ html "<a href=''$home/wiki''>Wiki</a>\n" | |
| 400 | 489 | @ } |
| 401 | 490 | @ if {[hascap s]} { |
| 402 | -@ html "<a href=''$home/setup''>Admin</a> " | |
| 491 | +@ html "<a href=''$home/setup''>Admin</a>\n" | |
| 403 | 492 | @ } elseif {[hascap a]} { |
| 404 | -@ html "<a href=''$home/setup_ulist''>Users</a> " | |
| 493 | +@ html "<a href=''$home/setup_ulist''>Users</a>\n" | |
| 405 | 494 | @ } |
| 406 | 495 | @ if {[info exists login]} { |
| 407 | -@ html "<a href=''$home/login''>Logout</a> " | |
| 496 | +@ html "<a href=''$home/login''>Logout</a>\n" | |
| 408 | 497 | @ } else { |
| 409 | -@ html "<a href=''$home/login''>Login</a> " | |
| 498 | +@ html "<a href=''$home/login''>Login</a>\n" | |
| 410 | 499 | @ } |
| 411 | 500 | @ </th1></div> |
| 412 | 501 | @ '); |
| 413 | 502 | @ REPLACE INTO config(name,mtime,value) |
| 414 | 503 | @ VALUES('footer',now(),'<div class="footer"> |
| @@ -517,25 +606,26 @@ | ||
| 517 | 606 | @ #container { |
| 518 | 607 | @ padding-left: 9em; |
| 519 | 608 | @ } |
| 520 | 609 | @ |
| 521 | 610 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 522 | -@ div.submenu { | |
| 611 | +@ div.submenu, div.sectionmenu { | |
| 523 | 612 | @ padding: 3px 10px 3px 10px; |
| 524 | 613 | @ font-size: 0.9em; |
| 525 | 614 | @ text-align: center; |
| 526 | 615 | @ border:1px solid #999; |
| 527 | 616 | @ border-width:1px 0px; |
| 528 | 617 | @ background-color: #eee; |
| 529 | 618 | @ color: #333; |
| 530 | 619 | @ } |
| 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 { | |
| 532 | 622 | @ padding: 3px 10px 3px 10px; |
| 533 | 623 | @ color: #333; |
| 534 | 624 | @ text-decoration: none; |
| 535 | 625 | @ } |
| 536 | -@ div.submenu a:hover { | |
| 626 | +@ div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 537 | 627 | @ color: #eee; |
| 538 | 628 | @ background-color: #333; |
| 539 | 629 | @ } |
| 540 | 630 | @ |
| 541 | 631 | @ /* All page content from the bottom of the menu or submenu down to |
| @@ -590,10 +680,56 @@ | ||
| 590 | 680 | @ /* The label/value pairs on (for example) the ci page */ |
| 591 | 681 | @ table.label-value th { |
| 592 | 682 | @ vertical-align: top; |
| 593 | 683 | @ text-align: right; |
| 594 | 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; | |
| 595 | 731 | @ }'); |
| 596 | 732 | @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> |
| 597 | 733 | @ <head> |
| 598 | 734 | @ <title>$<project_name>: $<title></title> |
| 599 | 735 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| @@ -614,37 +750,38 @@ | ||
| 614 | 750 | @ } else { |
| 615 | 751 | @ puts "Not logged in" |
| 616 | 752 | @ } |
| 617 | 753 | @ </th1></nobr></div> |
| 618 | 754 | @ </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" | |
| 621 | 758 | @ if {[anycap jor]} { |
| 622 | -@ html "<li><a href=''$home/timeline''>Timeline</a></li>" | |
| 759 | +@ html "<a href=''$home/timeline''>Timeline</a>\n" | |
| 623 | 760 | @ } |
| 624 | 761 | @ 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" | |
| 626 | 763 | @ } |
| 627 | 764 | @ 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" | |
| 630 | 767 | @ } |
| 631 | 768 | @ if {[hascap r]} { |
| 632 | -@ html "<li><a href=''$home/reportlist''>Tickets</a></li>" | |
| 769 | +@ html "<a href=''$home/reportlist''>Tickets</a>\n" | |
| 633 | 770 | @ } |
| 634 | 771 | @ if {[hascap j]} { |
| 635 | -@ html "<li><a href=''$home/wiki''>Wiki</a></li>" | |
| 772 | +@ html "<a href=''$home/wiki''>Wiki</a>\n" | |
| 636 | 773 | @ } |
| 637 | 774 | @ if {[hascap s]} { |
| 638 | -@ html "<li><a href=''$home/setup''>Admin</a></li>" | |
| 775 | +@ html "<a href=''$home/setup''>Admin</a>\n" | |
| 639 | 776 | @ } elseif {[hascap a]} { |
| 640 | -@ html "<li><a href=''$home/setup_ulist''>Users</a></li>" | |
| 777 | +@ html "<a href=''$home/setup_ulist''>Users</a>\n" | |
| 641 | 778 | @ } |
| 642 | 779 | @ if {[info exists login]} { |
| 643 | -@ html "<li><a href=''$home/login''>Logout</a></li>" | |
| 780 | +@ html "<a href=''$home/login''>Logout</a>\n" | |
| 644 | 781 | @ } else { |
| 645 | -@ html "<li><a href=''$home/login''>Login</a></li>" | |
| 782 | +@ html "<a href=''$home/login''>Login</a>\n" | |
| 646 | 783 | @ } |
| 647 | 784 | @ </th1></ul></div> |
| 648 | 785 | @ <div id="container"> |
| 649 | 786 | @ '); |
| 650 | 787 | @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div> |
| @@ -725,12 +862,13 @@ | ||
| 725 | 862 | @ -webkit-border-top-left-radius: 5px; |
| 726 | 863 | @ -border-top-right-radius: 5px; |
| 727 | 864 | @ -border-top-left-radius: 5px; |
| 728 | 865 | @ border-top-left-radius: 5px; |
| 729 | 866 | @ 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; | |
| 732 | 870 | @ background-color: #446979; |
| 733 | 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))); |
| 734 | 872 | @ background: -moz-linear-gradient(center bottom,rgb(51,81,94) 2%, rgb(85,129,149) 76%); |
| 735 | 873 | @ -webkit-box-shadow: 0px 3px 4px #333333; |
| 736 | 874 | @ -moz-box-shadow: 0px 3px 4px #333333; |
| @@ -753,11 +891,12 @@ | ||
| 753 | 891 | @ div.mainmenu a, div.mainmenu a:visited { |
| 754 | 892 | @ padding: 3px 10px 3px 10px; |
| 755 | 893 | @ color: white; |
| 756 | 894 | @ text-decoration: none; |
| 757 | 895 | @ } |
| 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 { | |
| 759 | 898 | @ padding: 2px 8px; |
| 760 | 899 | @ color: #000; |
| 761 | 900 | @ font-family: Arial; |
| 762 | 901 | @ text-decoration: none; |
| 763 | 902 | @ margin:auto; |
| @@ -775,11 +914,11 @@ | ||
| 775 | 914 | @ div.mainmenu a:hover { |
| 776 | 915 | @ color: #000; |
| 777 | 916 | @ background-color: white; |
| 778 | 917 | @ } |
| 779 | 918 | @ |
| 780 | -@ div.submenu a:hover { | |
| 919 | +@ div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 781 | 920 | @ background: -webkit-gradient(linear,left bottom, left top, color-stop(0, rgb(214,214,214)), color-stop(0.75, rgb(184,184,184))); |
| 782 | 921 | @ background: -moz-linear-gradient(center bottom, rgb(214,214,214) 0%, rgb(184,184,184) 75%); |
| 783 | 922 | @ background-color: #c0c0c0 ; |
| 784 | 923 | @ } |
| 785 | 924 | @ |
| @@ -885,10 +1024,77 @@ | ||
| 885 | 1024 | @ padding: 3px 5px; |
| 886 | 1025 | @ } |
| 887 | 1026 | @ |
| 888 | 1027 | @ textarea { |
| 889 | 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); */ | |
| 890 | 1096 | @ }'); |
| 891 | 1097 | @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> |
| 892 | 1098 | @ <head> |
| 893 | 1099 | @ <title>$<project_name>: $<title></title> |
| 894 | 1100 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| @@ -909,39 +1115,40 @@ | ||
| 909 | 1115 | @ } else { |
| 910 | 1116 | @ puts "Not logged in" |
| 911 | 1117 | @ } |
| 912 | 1118 | @ </th1></nobr></div> |
| 913 | 1119 | @ </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" | |
| 916 | 1123 | @ if {[anycap jor]} { |
| 917 | -@ html "<a href=''$home/timeline''>Timeline</a>" | |
| 1124 | +@ html "<a href=''$home/timeline''>Timeline</a>\n" | |
| 918 | 1125 | @ } |
| 919 | 1126 | @ if {[hascap oh]} { |
| 920 | -@ html "<a href=''$home/dir?ci=tip''>Files</a>" | |
| 1127 | +@ html "<a href=''$home/dir?ci=tip''>Files</a>\n" | |
| 921 | 1128 | @ } |
| 922 | 1129 | @ 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" | |
| 925 | 1132 | @ } |
| 926 | 1133 | @ if {[hascap r]} { |
| 927 | -@ html "<a href=''$home/reportlist''>Tickets</a>" | |
| 1134 | +@ html "<a href=''$home/reportlist''>Tickets</a>\n" | |
| 928 | 1135 | @ } |
| 929 | 1136 | @ if {[hascap j]} { |
| 930 | -@ html "<a href=''$home/wiki''>Wiki</a>" | |
| 1137 | +@ html "<a href=''$home/wiki''>Wiki</a>\n" | |
| 931 | 1138 | @ } |
| 932 | 1139 | @ if {[hascap s]} { |
| 933 | -@ html "<a href=''$home/setup''>Admin</a>" | |
| 1140 | +@ html "<a href=''$home/setup''>Admin</a>\n" | |
| 934 | 1141 | @ } elseif {[hascap a]} { |
| 935 | -@ html "<a href=''$home/setup_ulist''>Users</a>" | |
| 1142 | +@ html "<a href=''$home/setup_ulist''>Users</a>\n" | |
| 936 | 1143 | @ } |
| 937 | 1144 | @ if {[info exists login]} { |
| 938 | -@ html "<a href=''$home/login''>Logout</a>" | |
| 1145 | +@ html "<a href=''$home/login''>Logout</a>\n" | |
| 939 | 1146 | @ } else { |
| 940 | -@ html "<a href=''$home/login''>Login</a>" | |
| 1147 | +@ html "<a href=''$home/login''>Login</a>\n" | |
| 941 | 1148 | @ } |
| 942 | -@ </th1></ul></div> | |
| 1149 | +@ </th1></div> | |
| 943 | 1150 | @ <div id="container"> |
| 944 | 1151 | @ '); |
| 945 | 1152 | @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div> |
| 946 | 1153 | @ <div class="footer"> |
| 947 | 1154 | @ Fossil version $manifest_version $manifest_date |
| @@ -1100,10 +1307,13 @@ | ||
| 1100 | 1307 | db_multi_exec("%s", zCurrent); |
| 1101 | 1308 | } |
| 1102 | 1309 | } |
| 1103 | 1310 | |
| 1104 | 1311 | style_header("Skins"); |
| 1312 | + if( zErr ){ | |
| 1313 | + @ <p><font color="red">%h(zErr)</font></p> | |
| 1314 | + } | |
| 1105 | 1315 | @ <p>A "skin" is a combination of |
| 1106 | 1316 | @ <a href="setup_editcss">CSS</a>, |
| 1107 | 1317 | @ <a href="setup_header">Header</a>, |
| 1108 | 1318 | @ <a href="setup_footer">Footer</a>, and |
| 1109 | 1319 | @ <a href="setup_logo">Logo</a> that determines the look and feel |
| 1110 | 1320 |
| --- 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 @@ | ||
| 314 | 314 | #endif |
| 315 | 315 | #ifdef HAVE_INTTYPES_H |
| 316 | 316 | #include <inttypes.h> |
| 317 | 317 | #endif |
| 318 | 318 | |
| 319 | -/* | |
| 320 | -** The number of samples of an index that SQLite takes in order to | |
| 321 | -** construct a histogram of the table content when running ANALYZE | |
| 322 | -** and with SQLITE_ENABLE_STAT2 | |
| 323 | -*/ | |
| 324 | -#define SQLITE_INDEX_SAMPLES 10 | |
| 325 | - | |
| 326 | 319 | /* |
| 327 | 320 | ** The following macros are used to cast pointers to integers and |
| 328 | 321 | ** integers to pointers. The way you do this varies from one compiler |
| 329 | 322 | ** to the next, so we have developed the following set of #if statements |
| 330 | 323 | ** to generate appropriate macros for a wide range of compilers. |
| @@ -656,11 +649,11 @@ | ||
| 656 | 649 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 657 | 650 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 658 | 651 | */ |
| 659 | 652 | #define SQLITE_VERSION "3.7.9" |
| 660 | 653 | #define SQLITE_VERSION_NUMBER 3007009 |
| 661 | -#define SQLITE_SOURCE_ID "2011-10-15 00:16:30 39408702a989f907261c298bf0947f3e68bd10fe" | |
| 654 | +#define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf" | |
| 662 | 655 | |
| 663 | 656 | /* |
| 664 | 657 | ** CAPI3REF: Run-Time Library Version Numbers |
| 665 | 658 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 666 | 659 | ** |
| @@ -1951,12 +1944,12 @@ | ||
| 1951 | 1944 | ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or |
| 1952 | 1945 | ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory |
| 1953 | 1946 | ** allocator is engaged to handle all of SQLites memory allocation needs. |
| 1954 | 1947 | ** The first pointer (the memory pointer) must be aligned to an 8-byte |
| 1955 | 1948 | ** 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> | |
| 1958 | 1951 | ** |
| 1959 | 1952 | ** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt> |
| 1960 | 1953 | ** <dd> ^(This option takes a single argument which is a pointer to an |
| 1961 | 1954 | ** instance of the [sqlite3_mutex_methods] structure. The argument specifies |
| 1962 | 1955 | ** alternative low-level mutex routines to be used in place |
| @@ -8792,10 +8785,11 @@ | ||
| 8792 | 8785 | SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); |
| 8793 | 8786 | SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); |
| 8794 | 8787 | SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); |
| 8795 | 8788 | SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); |
| 8796 | 8789 | SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); |
| 8790 | +SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); | |
| 8797 | 8791 | |
| 8798 | 8792 | /* Functions used to truncate the database file. */ |
| 8799 | 8793 | SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); |
| 8800 | 8794 | |
| 8801 | 8795 | #if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) |
| @@ -10189,12 +10183,13 @@ | ||
| 10189 | 10183 | IndexSample *aSample; /* Samples of the left-most key */ |
| 10190 | 10184 | #endif |
| 10191 | 10185 | }; |
| 10192 | 10186 | |
| 10193 | 10187 | /* |
| 10194 | -** Each sample stored in the sqlite_stat2 table is represented in memory | |
| 10195 | -** using a structure of this type. | |
| 10188 | +** Each sample stored in the sqlite_stat3 table is represented in memory | |
| 10189 | +** using a structure of this type. See documentation at the top of the | |
| 10190 | +** analyze.c source file for additional information. | |
| 10196 | 10191 | */ |
| 10197 | 10192 | struct IndexSample { |
| 10198 | 10193 | union { |
| 10199 | 10194 | char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ |
| 10200 | 10195 | double r; /* Value if eType is SQLITE_FLOAT */ |
| @@ -12293,13 +12288,10 @@ | ||
| 12293 | 12288 | "ENABLE_OVERSIZE_CELL_CHECK", |
| 12294 | 12289 | #endif |
| 12295 | 12290 | #ifdef SQLITE_ENABLE_RTREE |
| 12296 | 12291 | "ENABLE_RTREE", |
| 12297 | 12292 | #endif |
| 12298 | -#ifdef SQLITE_ENABLE_STAT2 | |
| 12299 | - "ENABLE_STAT2", | |
| 12300 | -#endif | |
| 12301 | 12293 | #ifdef SQLITE_ENABLE_STAT3 |
| 12302 | 12294 | "ENABLE_STAT3", |
| 12303 | 12295 | #endif |
| 12304 | 12296 | #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY |
| 12305 | 12297 | "ENABLE_UNLOCK_NOTIFY", |
| @@ -12996,10 +12988,11 @@ | ||
| 12996 | 12988 | SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); |
| 12997 | 12989 | SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int); |
| 12998 | 12990 | SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*); |
| 12999 | 12991 | SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *); |
| 13000 | 12992 | SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem); |
| 12993 | +SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p); | |
| 13001 | 12994 | |
| 13002 | 12995 | #ifdef SQLITE_OMIT_MERGE_SORT |
| 13003 | 12996 | # define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK |
| 13004 | 12997 | # define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK |
| 13005 | 12998 | # define sqlite3VdbeSorterClose(Y,Z) |
| @@ -20969,11 +20962,11 @@ | ||
| 20969 | 20962 | }else if( *z=='+' ){ |
| 20970 | 20963 | z+=incr; |
| 20971 | 20964 | } |
| 20972 | 20965 | /* copy digits to exponent */ |
| 20973 | 20966 | while( z<zEnd && sqlite3Isdigit(*z) ){ |
| 20974 | - e = e*10 + (*z - '0'); | |
| 20967 | + e = e<10000 ? (e*10 + (*z - '0')) : 10000; | |
| 20975 | 20968 | z+=incr; |
| 20976 | 20969 | eValid = 1; |
| 20977 | 20970 | } |
| 20978 | 20971 | } |
| 20979 | 20972 | |
| @@ -21020,10 +21013,16 @@ | ||
| 21020 | 21013 | result /= 1.0e+308; |
| 21021 | 21014 | }else{ |
| 21022 | 21015 | result = s * scale; |
| 21023 | 21016 | result *= 1.0e+308; |
| 21024 | 21017 | } |
| 21018 | + }else if( e>=342 ){ | |
| 21019 | + if( esign<0 ){ | |
| 21020 | + result = 0.0*s; | |
| 21021 | + }else{ | |
| 21022 | + result = 1e308*1e308*s; /* Infinity */ | |
| 21023 | + } | |
| 21025 | 21024 | }else{ |
| 21026 | 21025 | /* 1.0e+22 is the largest power of 10 than can be |
| 21027 | 21026 | ** represented exactly. */ |
| 21028 | 21027 | while( e%22 ) { scale *= 1.0e+1; e -= 1; } |
| 21029 | 21028 | while( e>0 ) { scale *= 1.0e+22; e -= 22; } |
| @@ -29477,17 +29476,17 @@ | ||
| 29477 | 29476 | ** "<path to db>-journal" |
| 29478 | 29477 | ** "<path to db>-wal" |
| 29479 | 29478 | ** "<path to db>-journalNN" |
| 29480 | 29479 | ** "<path to db>-walNN" |
| 29481 | 29480 | ** |
| 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 | |
| 29483 | 29482 | ** used by the test_multiplex.c module. |
| 29484 | 29483 | */ |
| 29485 | 29484 | nDb = sqlite3Strlen30(zPath) - 1; |
| 29486 | 29485 | #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; | |
| 29489 | 29488 | #else |
| 29490 | 29489 | while( zPath[nDb]!='-' ){ |
| 29491 | 29490 | assert( nDb>0 ); |
| 29492 | 29491 | assert( zPath[nDb]!='\n' ); |
| 29493 | 29492 | nDb--; |
| @@ -44157,10 +44156,17 @@ | ||
| 44157 | 44156 | pPager->pWal = 0; |
| 44158 | 44157 | } |
| 44159 | 44158 | } |
| 44160 | 44159 | return rc; |
| 44161 | 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 | +} | |
| 44162 | 44168 | |
| 44163 | 44169 | #ifdef SQLITE_HAS_CODEC |
| 44164 | 44170 | /* |
| 44165 | 44171 | ** This function is called by the wal module when writing page content |
| 44166 | 44172 | ** into the log file. |
| @@ -57000,10 +57006,12 @@ | ||
| 57000 | 57006 | sqlite3_backup_step(&b, 0x7FFFFFFF); |
| 57001 | 57007 | assert( b.rc!=SQLITE_OK ); |
| 57002 | 57008 | rc = sqlite3_backup_finish(&b); |
| 57003 | 57009 | if( rc==SQLITE_OK ){ |
| 57004 | 57010 | pTo->pBt->pageSizeFixed = 0; |
| 57011 | + }else{ | |
| 57012 | + sqlite3PagerClearCache(sqlite3BtreePager(b.pDest)); | |
| 57005 | 57013 | } |
| 57006 | 57014 | |
| 57007 | 57015 | assert( sqlite3BtreeIsInTrans(pTo)==0 ); |
| 57008 | 57016 | sqlite3BtreeLeave(pFrom); |
| 57009 | 57017 | sqlite3BtreeLeave(pTo); |
| @@ -60474,10 +60482,34 @@ | ||
| 60474 | 60482 | ** in p->rc. This routine sets that result back to SQLITE_OK. |
| 60475 | 60483 | */ |
| 60476 | 60484 | SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){ |
| 60477 | 60485 | p->rc = SQLITE_OK; |
| 60478 | 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 | +} | |
| 60479 | 60511 | |
| 60480 | 60512 | /* |
| 60481 | 60513 | ** Clean up a VDBE after execution but do not delete the VDBE just yet. |
| 60482 | 60514 | ** Write any error messages into *pzErrMsg. Return the result code. |
| 60483 | 60515 | ** |
| @@ -60502,22 +60534,13 @@ | ||
| 60502 | 60534 | ** and error message from the VDBE into the main database structure. But |
| 60503 | 60535 | ** if the VDBE has just been set to run but has not actually executed any |
| 60504 | 60536 | ** instructions yet, leave the main database error information unchanged. |
| 60505 | 60537 | */ |
| 60506 | 60538 | 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; | |
| 60519 | 60542 | if( p->runOnlyOnce ) p->expired = 1; |
| 60520 | 60543 | }else if( p->rc && p->expired ){ |
| 60521 | 60544 | /* The expired flag was set on the VDBE before the first call |
| 60522 | 60545 | ** to sqlite3_step(). For consistency (since sqlite3_step() was |
| 60523 | 60546 | ** called), set the database error in this case as well. |
| @@ -61859,11 +61882,11 @@ | ||
| 61859 | 61882 | if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ |
| 61860 | 61883 | /* If this statement was prepared using sqlite3_prepare_v2(), and an |
| 61861 | 61884 | ** error has occured, then return the error code in p->rc to the |
| 61862 | 61885 | ** caller. Set the error code in the database handle to the same value. |
| 61863 | 61886 | */ |
| 61864 | - rc = db->errCode = p->rc; | |
| 61887 | + rc = sqlite3VdbeTransferError(p); | |
| 61865 | 61888 | } |
| 61866 | 61889 | return (rc&db->errMask); |
| 61867 | 61890 | } |
| 61868 | 61891 | |
| 61869 | 61892 | /* |
| @@ -67759,13 +67782,12 @@ | ||
| 67759 | 67782 | |
| 67760 | 67783 | assert( pOp->p1>=0 && pOp->p1<p->nCursor ); |
| 67761 | 67784 | u.bn.pC = p->apCsr[pOp->p1]; |
| 67762 | 67785 | assert( u.bn.pC!=0 ); |
| 67763 | 67786 | 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) ){ | |
| 67767 | 67789 | rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res); |
| 67768 | 67790 | } |
| 67769 | 67791 | u.bn.pC->nullRow = (u8)u.bn.res; |
| 67770 | 67792 | u.bn.pC->deferredMoveto = 0; |
| 67771 | 67793 | u.bn.pC->rowidIsValid = 0; |
| @@ -68962,11 +68984,11 @@ | ||
| 68962 | 68984 | |
| 68963 | 68985 | /* Do not allow a transition to journal_mode=WAL for a database |
| 68964 | 68986 | ** in temporary storage or if the VFS does not support shared memory |
| 68965 | 68987 | */ |
| 68966 | 68988 | if( u.ch.eNew==PAGER_JOURNALMODE_WAL |
| 68967 | - && (u.ch.zFilename[0]==0 /* Temp file */ | |
| 68989 | + && (sqlite3Strlen30(u.ch.zFilename)==0 /* Temp file */ | |
| 68968 | 68990 | || !sqlite3PagerWalSupported(u.ch.pPager)) /* No shared-memory support */ |
| 68969 | 68991 | ){ |
| 68970 | 68992 | u.ch.eNew = u.ch.eOld; |
| 68971 | 68993 | } |
| 68972 | 68994 | |
| @@ -69397,14 +69419,19 @@ | ||
| 69397 | 69419 | u.co.pName = &aMem[pOp->p1]; |
| 69398 | 69420 | assert( u.co.pVtab->pModule->xRename ); |
| 69399 | 69421 | assert( memIsValid(u.co.pName) ); |
| 69400 | 69422 | REGISTER_TRACE(pOp->p1, u.co.pName); |
| 69401 | 69423 | 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 | + } | |
| 69406 | 69433 | break; |
| 69407 | 69434 | } |
| 69408 | 69435 | #endif |
| 69409 | 69436 | |
| 69410 | 69437 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| @@ -71758,10 +71785,28 @@ | ||
| 71758 | 71785 | ExprSetProperty(pExpr, EP_Static); |
| 71759 | 71786 | sqlite3ExprDelete(db, pExpr); |
| 71760 | 71787 | memcpy(pExpr, pDup, sizeof(*pExpr)); |
| 71761 | 71788 | sqlite3DbFree(db, pDup); |
| 71762 | 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 | + | |
| 71763 | 71808 | |
| 71764 | 71809 | /* |
| 71765 | 71810 | ** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up |
| 71766 | 71811 | ** that name in the set of source tables in pSrcList and make the pExpr |
| 71767 | 71812 | ** expression node refer back to that source column. The following changes |
| @@ -71850,38 +71895,25 @@ | ||
| 71850 | 71895 | pSchema = pTab->pSchema; |
| 71851 | 71896 | pMatch = pItem; |
| 71852 | 71897 | } |
| 71853 | 71898 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ |
| 71854 | 71899 | 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 | + } | |
| 71856 | 71908 | cnt++; |
| 71857 | 71909 | pExpr->iTable = pItem->iCursor; |
| 71858 | 71910 | pExpr->pTab = pTab; |
| 71859 | 71911 | pMatch = pItem; |
| 71860 | 71912 | pSchema = pTab->pSchema; |
| 71861 | 71913 | /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ |
| 71862 | 71914 | 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 | 71915 | break; |
| 71884 | 71916 | } |
| 71885 | 71917 | } |
| 71886 | 71918 | } |
| 71887 | 71919 | } |
| @@ -77594,20 +77626,20 @@ | ||
| 77594 | 77626 | #ifndef SQLITE_OMIT_ANALYZE |
| 77595 | 77627 | |
| 77596 | 77628 | /* |
| 77597 | 77629 | ** This routine generates code that opens the sqlite_stat1 table for |
| 77598 | 77630 | ** 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 | |
| 77600 | 77632 | ** opened for writing using cursor (iStatCur+1) |
| 77601 | 77633 | ** |
| 77602 | 77634 | ** 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. | |
| 77605 | 77637 | ** |
| 77606 | 77638 | ** Argument zWhere may be a pointer to a buffer containing a table name, |
| 77607 | 77639 | ** 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 | |
| 77609 | 77641 | ** with the named table are deleted. If zWhere==0, then code is generated |
| 77610 | 77642 | ** to delete all stat table entries. |
| 77611 | 77643 | */ |
| 77612 | 77644 | static void openStatTable( |
| 77613 | 77645 | Parse *pParse, /* Parsing context */ |
| @@ -81388,31 +81420,28 @@ | ||
| 81388 | 81420 | } |
| 81389 | 81421 | #endif |
| 81390 | 81422 | } |
| 81391 | 81423 | |
| 81392 | 81424 | /* |
| 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)) | |
| 81394 | 81426 | ** after a DROP INDEX or DROP TABLE command. |
| 81395 | 81427 | */ |
| 81396 | 81428 | static void sqlite3ClearStatTables( |
| 81397 | 81429 | Parse *pParse, /* The parsing context */ |
| 81398 | 81430 | int iDb, /* The database number */ |
| 81399 | 81431 | const char *zType, /* "idx" or "tbl" */ |
| 81400 | 81432 | const char *zName /* Name of index or table */ |
| 81401 | 81433 | ){ |
| 81402 | - static const char *azStatTab[] = { | |
| 81403 | - "sqlite_stat1", | |
| 81404 | - "sqlite_stat2", | |
| 81405 | - "sqlite_stat3", | |
| 81406 | - }; | |
| 81407 | 81434 | int i; |
| 81408 | 81435 | 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) ){ | |
| 81411 | 81440 | sqlite3NestedParse(pParse, |
| 81412 | 81441 | "DELETE FROM %Q.%s WHERE %s=%Q", |
| 81413 | - zDbName, azStatTab[i], zType, zName | |
| 81442 | + zDbName, zTab, zType, zName | |
| 81414 | 81443 | ); |
| 81415 | 81444 | } |
| 81416 | 81445 | } |
| 81417 | 81446 | } |
| 81418 | 81447 | |
| @@ -89234,12 +89263,14 @@ | ||
| 89234 | 89263 | int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); |
| 89235 | 89264 | int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); |
| 89236 | 89265 | int (*busy_timeout)(sqlite3*,int ms); |
| 89237 | 89266 | int (*changes)(sqlite3*); |
| 89238 | 89267 | 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*)); | |
| 89241 | 89272 | const void * (*column_blob)(sqlite3_stmt*,int iCol); |
| 89242 | 89273 | int (*column_bytes)(sqlite3_stmt*,int iCol); |
| 89243 | 89274 | int (*column_bytes16)(sqlite3_stmt*,int iCol); |
| 89244 | 89275 | int (*column_count)(sqlite3_stmt*pStmt); |
| 89245 | 89276 | const char * (*column_database_name)(sqlite3_stmt*,int); |
| @@ -89260,14 +89291,22 @@ | ||
| 89260 | 89291 | int (*column_type)(sqlite3_stmt*,int iCol); |
| 89261 | 89292 | sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); |
| 89262 | 89293 | void * (*commit_hook)(sqlite3*,int(*)(void*),void*); |
| 89263 | 89294 | int (*complete)(const char*sql); |
| 89264 | 89295 | 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*)); | |
| 89269 | 89308 | int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); |
| 89270 | 89309 | int (*data_count)(sqlite3_stmt*pStmt); |
| 89271 | 89310 | sqlite3 * (*db_handle)(sqlite3_stmt*); |
| 89272 | 89311 | int (*declare_vtab)(sqlite3*,const char*); |
| 89273 | 89312 | int (*enable_shared_cache)(int); |
| @@ -89308,20 +89347,23 @@ | ||
| 89308 | 89347 | void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89309 | 89348 | void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89310 | 89349 | void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); |
| 89311 | 89350 | void (*result_value)(sqlite3_context*,sqlite3_value*); |
| 89312 | 89351 | 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*); | |
| 89314 | 89354 | void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); |
| 89315 | 89355 | char * (*snprintf)(int,char*,const char*,...); |
| 89316 | 89356 | 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*); | |
| 89318 | 89359 | void (*thread_cleanup)(void); |
| 89319 | 89360 | int (*total_changes)(sqlite3*); |
| 89320 | 89361 | void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); |
| 89321 | 89362 | 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*); | |
| 89323 | 89365 | void * (*user_data)(sqlite3_context*); |
| 89324 | 89366 | const void * (*value_blob)(sqlite3_value*); |
| 89325 | 89367 | int (*value_bytes)(sqlite3_value*); |
| 89326 | 89368 | int (*value_bytes16)(sqlite3_value*); |
| 89327 | 89369 | double (*value_double)(sqlite3_value*); |
| @@ -89339,19 +89381,23 @@ | ||
| 89339 | 89381 | /* Added by 3.3.13 */ |
| 89340 | 89382 | int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); |
| 89341 | 89383 | int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); |
| 89342 | 89384 | int (*clear_bindings)(sqlite3_stmt*); |
| 89343 | 89385 | /* 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 *)); | |
| 89345 | 89388 | /* Added by 3.5.0 */ |
| 89346 | 89389 | int (*bind_zeroblob)(sqlite3_stmt*,int,int); |
| 89347 | 89390 | int (*blob_bytes)(sqlite3_blob*); |
| 89348 | 89391 | 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**); | |
| 89350 | 89394 | int (*blob_read)(sqlite3_blob*,void*,int,int); |
| 89351 | 89395 | 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*)); | |
| 89353 | 89399 | int (*file_control)(sqlite3*,const char*,int,void*); |
| 89354 | 89400 | sqlite3_int64 (*memory_highwater)(int); |
| 89355 | 89401 | sqlite3_int64 (*memory_used)(void); |
| 89356 | 89402 | sqlite3_mutex *(*mutex_alloc)(int); |
| 89357 | 89403 | void (*mutex_enter)(sqlite3_mutex*); |
| @@ -89383,11 +89429,15 @@ | ||
| 89383 | 89429 | int (*backup_pagecount)(sqlite3_backup*); |
| 89384 | 89430 | int (*backup_remaining)(sqlite3_backup*); |
| 89385 | 89431 | int (*backup_step)(sqlite3_backup*,int); |
| 89386 | 89432 | const char *(*compileoption_get)(int); |
| 89387 | 89433 | 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*)); | |
| 89389 | 89439 | int (*db_config)(sqlite3*,int,...); |
| 89390 | 89440 | sqlite3_mutex *(*db_mutex)(sqlite3*); |
| 89391 | 89441 | int (*db_status)(sqlite3*,int,int*,int*,int); |
| 89392 | 89442 | int (*extended_errcode)(sqlite3*); |
| 89393 | 89443 | void (*log)(int,const char*,...); |
| @@ -100469,11 +100519,11 @@ | ||
| 100469 | 100519 | if( db->aVTrans ){ |
| 100470 | 100520 | int i; |
| 100471 | 100521 | for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){ |
| 100472 | 100522 | VTable *pVTab = db->aVTrans[i]; |
| 100473 | 100523 | const sqlite3_module *pMod = pVTab->pMod->pModule; |
| 100474 | - if( pMod->iVersion>=2 ){ | |
| 100524 | + if( pVTab->pVtab && pMod->iVersion>=2 ){ | |
| 100475 | 100525 | int (*xMethod)(sqlite3_vtab *, int); |
| 100476 | 100526 | switch( op ){ |
| 100477 | 100527 | case SAVEPOINT_BEGIN: |
| 100478 | 100528 | xMethod = pMod->xSavepoint; |
| 100479 | 100529 | pVTab->iSavepoint = iSavepoint+1; |
| @@ -100484,11 +100534,11 @@ | ||
| 100484 | 100534 | default: |
| 100485 | 100535 | xMethod = pMod->xRelease; |
| 100486 | 100536 | break; |
| 100487 | 100537 | } |
| 100488 | 100538 | if( xMethod && pVTab->iSavepoint>iSavepoint ){ |
| 100489 | - rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint); | |
| 100539 | + rc = xMethod(pVTab->pVtab, iSavepoint); | |
| 100490 | 100540 | } |
| 100491 | 100541 | } |
| 100492 | 100542 | } |
| 100493 | 100543 | } |
| 100494 | 100544 | return rc; |
| @@ -101351,11 +101401,11 @@ | ||
| 101351 | 101401 | int iCol = pRight->iColumn; |
| 101352 | 101402 | pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE); |
| 101353 | 101403 | if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ |
| 101354 | 101404 | z = (char *)sqlite3_value_text(pVal); |
| 101355 | 101405 | } |
| 101356 | - sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-31526-56213 */ | |
| 101406 | + sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); | |
| 101357 | 101407 | assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); |
| 101358 | 101408 | }else if( op==TK_STRING ){ |
| 101359 | 101409 | z = pRight->u.zToken; |
| 101360 | 101410 | } |
| 101361 | 101411 | if( z ){ |
| @@ -101369,11 +101419,11 @@ | ||
| 101369 | 101419 | pPrefix = sqlite3Expr(db, TK_STRING, z); |
| 101370 | 101420 | if( pPrefix ) pPrefix->u.zToken[cnt] = 0; |
| 101371 | 101421 | *ppPrefix = pPrefix; |
| 101372 | 101422 | if( op==TK_VARIABLE ){ |
| 101373 | 101423 | Vdbe *v = pParse->pVdbe; |
| 101374 | - sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-31526-56213 */ | |
| 101424 | + sqlite3VdbeSetVarmask(v, pRight->iColumn); | |
| 101375 | 101425 | if( *pisComplete && pRight->u.zToken[1] ){ |
| 101376 | 101426 | /* If the rhs of the LIKE expression is a variable, and the current |
| 101377 | 101427 | ** value of the variable means there is no need to invoke the LIKE |
| 101378 | 101428 | ** function, then no OP_Variable will be added to the program. |
| 101379 | 101429 | ** This causes problems for the sqlite3_bind_parameter_name() |
| @@ -102501,10 +102551,11 @@ | ||
| 102501 | 102551 | tempWC.pParse = pWC->pParse; |
| 102502 | 102552 | tempWC.pMaskSet = pWC->pMaskSet; |
| 102503 | 102553 | tempWC.pOuter = pWC; |
| 102504 | 102554 | tempWC.op = TK_AND; |
| 102505 | 102555 | tempWC.a = pOrTerm; |
| 102556 | + tempWC.wctrlFlags = 0; | |
| 102506 | 102557 | tempWC.nTerm = 1; |
| 102507 | 102558 | bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost); |
| 102508 | 102559 | }else{ |
| 102509 | 102560 | continue; |
| 102510 | 102561 | } |
| @@ -103282,11 +103333,11 @@ | ||
| 103282 | 103333 | ){ |
| 103283 | 103334 | if( pExpr->op==TK_VARIABLE |
| 103284 | 103335 | || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE) |
| 103285 | 103336 | ){ |
| 103286 | 103337 | int iVar = pExpr->iColumn; |
| 103287 | - sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-31526-56213 */ | |
| 103338 | + sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); | |
| 103288 | 103339 | *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); |
| 103289 | 103340 | return SQLITE_OK; |
| 103290 | 103341 | } |
| 103291 | 103342 | return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); |
| 103292 | 103343 | } |
| @@ -103812,11 +103863,11 @@ | ||
| 103812 | 103863 | ** a table or index. The actual times can vary, with the size of |
| 103813 | 103864 | ** records being an important factor. Both moves and searches are |
| 103814 | 103865 | ** slower with larger records, presumably because fewer records fit |
| 103815 | 103866 | ** on one page and hence more pages have to be fetched. |
| 103816 | 103867 | ** |
| 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 | |
| 103818 | 103869 | ** not give us data on the relative sizes of table and index records. |
| 103819 | 103870 | ** So this computation assumes table records are about twice as big |
| 103820 | 103871 | ** as index records |
| 103821 | 103872 | */ |
| 103822 | 103873 | if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ |
| @@ -114528,10 +114579,11 @@ | ||
| 114528 | 114579 | const char *zDb; /* logical database name */ |
| 114529 | 114580 | const char *zName; /* virtual table name */ |
| 114530 | 114581 | int nColumn; /* number of named columns in virtual table */ |
| 114531 | 114582 | char **azColumn; /* column names. malloced */ |
| 114532 | 114583 | sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ |
| 114584 | + char *zContentTbl; /* content=xxx option, or NULL */ | |
| 114533 | 114585 | |
| 114534 | 114586 | /* Precompiled statements used by the implementation. Each of these |
| 114535 | 114587 | ** statements is run and reset within a single virtual table API call. |
| 114536 | 114588 | */ |
| 114537 | 114589 | sqlite3_stmt *aStmt[27]; |
| @@ -114568,11 +114620,11 @@ | ||
| 114568 | 114620 | } *aIndex; |
| 114569 | 114621 | int nMaxPendingData; /* Max pending data before flush to disk */ |
| 114570 | 114622 | int nPendingData; /* Current bytes of pending data */ |
| 114571 | 114623 | sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */ |
| 114572 | 114624 | |
| 114573 | -#if defined(SQLITE_DEBUG) | |
| 114625 | +#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) | |
| 114574 | 114626 | /* State variables used for validating that the transaction control |
| 114575 | 114627 | ** methods of the virtual table are called at appropriate times. These |
| 114576 | 114628 | ** values do not contribution to the FTS computation; they are used for |
| 114577 | 114629 | ** verifying the SQLite core. |
| 114578 | 114630 | */ |
| @@ -114653,10 +114705,11 @@ | ||
| 114653 | 114705 | */ |
| 114654 | 114706 | struct Fts3PhraseToken { |
| 114655 | 114707 | char *z; /* Text of the token */ |
| 114656 | 114708 | int n; /* Number of bytes in buffer z */ |
| 114657 | 114709 | int isPrefix; /* True if token ends with a "*" character */ |
| 114710 | + int bFirst; /* True if token must appear at position 0 */ | |
| 114658 | 114711 | |
| 114659 | 114712 | /* Variables above this point are populated when the expression is |
| 114660 | 114713 | ** parsed (by code in fts3_expr.c). Below this point the variables are |
| 114661 | 114714 | ** used when evaluating the expression. */ |
| 114662 | 114715 | Fts3DeferredToken *pDeferred; /* Deferred token object for this token */ |
| @@ -114771,10 +114824,11 @@ | ||
| 114771 | 114824 | #define FTS3_SEGMENT_REQUIRE_POS 0x00000001 |
| 114772 | 114825 | #define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002 |
| 114773 | 114826 | #define FTS3_SEGMENT_COLUMN_FILTER 0x00000004 |
| 114774 | 114827 | #define FTS3_SEGMENT_PREFIX 0x00000008 |
| 114775 | 114828 | #define FTS3_SEGMENT_SCAN 0x00000010 |
| 114829 | +#define FTS3_SEGMENT_FIRST 0x00000020 | |
| 114776 | 114830 | |
| 114777 | 114831 | /* Type passed as 4th argument to SegmentReaderIterate() */ |
| 114778 | 114832 | struct Fts3SegFilter { |
| 114779 | 114833 | const char *zTerm; |
| 114780 | 114834 | int nTerm; |
| @@ -114810,12 +114864,12 @@ | ||
| 114810 | 114864 | SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); |
| 114811 | 114865 | SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *); |
| 114812 | 114866 | SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64); |
| 114813 | 114867 | SQLITE_PRIVATE void sqlite3Fts3Dequote(char *); |
| 114814 | 114868 | SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*); |
| 114815 | - | |
| 114816 | 114869 | SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *); |
| 114870 | +SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *); | |
| 114817 | 114871 | |
| 114818 | 114872 | /* fts3_tokenizer.c */ |
| 114819 | 114873 | SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *); |
| 114820 | 114874 | SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *); |
| 114821 | 114875 | SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *, |
| @@ -114830,11 +114884,11 @@ | ||
| 114830 | 114884 | ); |
| 114831 | 114885 | SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *); |
| 114832 | 114886 | |
| 114833 | 114887 | /* fts3_expr.c */ |
| 114834 | 114888 | SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *, |
| 114835 | - char **, int, int, const char *, int, Fts3Expr ** | |
| 114889 | + char **, int, int, int, const char *, int, Fts3Expr ** | |
| 114836 | 114890 | ); |
| 114837 | 114891 | SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *); |
| 114838 | 114892 | #ifdef SQLITE_TEST |
| 114839 | 114893 | SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db); |
| 114840 | 114894 | SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db); |
| @@ -115031,10 +115085,11 @@ | ||
| 115031 | 115085 | sqlite3_finalize(p->aStmt[i]); |
| 115032 | 115086 | } |
| 115033 | 115087 | sqlite3_free(p->zSegmentsTbl); |
| 115034 | 115088 | sqlite3_free(p->zReadExprlist); |
| 115035 | 115089 | sqlite3_free(p->zWriteExprlist); |
| 115090 | + sqlite3_free(p->zContentTbl); | |
| 115036 | 115091 | |
| 115037 | 115092 | /* Invoke the tokenizer destructor to free the tokenizer. */ |
| 115038 | 115093 | p->pTokenizer->pModule->xDestroy(p->pTokenizer); |
| 115039 | 115094 | |
| 115040 | 115095 | sqlite3_free(p); |
| @@ -115070,20 +115125,23 @@ | ||
| 115070 | 115125 | |
| 115071 | 115126 | /* |
| 115072 | 115127 | ** The xDestroy() virtual table method. |
| 115073 | 115128 | */ |
| 115074 | 115129 | static int fts3DestroyMethod(sqlite3_vtab *pVtab){ |
| 115075 | - int rc = SQLITE_OK; /* Return code */ | |
| 115076 | 115130 | 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 */ | |
| 115078 | 115134 | |
| 115079 | 115135 | /* 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); | |
| 115085 | 115143 | |
| 115086 | 115144 | /* If everything has worked, invoke fts3DisconnectMethod() to free the |
| 115087 | 115145 | ** memory associated with the Fts3Table structure and return SQLITE_OK. |
| 115088 | 115146 | ** Otherwise, return an SQLite error code. |
| 115089 | 115147 | */ |
| @@ -115141,27 +115199,31 @@ | ||
| 115141 | 115199 | ** %_stat tables required by FTS4. |
| 115142 | 115200 | */ |
| 115143 | 115201 | static int fts3CreateTables(Fts3Table *p){ |
| 115144 | 115202 | int rc = SQLITE_OK; /* Return code */ |
| 115145 | 115203 | int i; /* Iterator variable */ |
| 115146 | - char *zContentCols; /* Columns of %_content table */ | |
| 115147 | 115204 | sqlite3 *db = p->db; /* The database connection */ |
| 115148 | 115205 | |
| 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 | + | |
| 115163 | 115225 | /* Create other tables */ |
| 115164 | 115226 | fts3DbExec(&rc, db, |
| 115165 | 115227 | "CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);", |
| 115166 | 115228 | p->zDb, p->zName |
| 115167 | 115229 | ); |
| @@ -115308,12 +115370,12 @@ | ||
| 115308 | 115370 | } |
| 115309 | 115371 | return zRet; |
| 115310 | 115372 | } |
| 115311 | 115373 | |
| 115312 | 115374 | /* |
| 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: | |
| 115315 | 115377 | ** |
| 115316 | 115378 | ** SELECT <list of expressions> FROM %_content AS x ... |
| 115317 | 115379 | ** |
| 115318 | 115380 | ** to return the docid, followed by each column of text data in order |
| 115319 | 115381 | ** from left to write. If parameter zFunc is not NULL, then instead of |
| @@ -115320,11 +115382,11 @@ | ||
| 115320 | 115382 | ** being returned directly each column of text data is passed to an SQL |
| 115321 | 115383 | ** function named zFunc first. For example, if zFunc is "unzip" and the |
| 115322 | 115384 | ** table has the three user-defined columns "a", "b", and "c", the following |
| 115323 | 115385 | ** string is returned: |
| 115324 | 115386 | ** |
| 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" | |
| 115326 | 115388 | ** |
| 115327 | 115389 | ** The pointer returned points to a buffer allocated by sqlite3_malloc(). It |
| 115328 | 115390 | ** is the responsibility of the caller to eventually free it. |
| 115329 | 115391 | ** |
| 115330 | 115392 | ** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and |
| @@ -115336,20 +115398,32 @@ | ||
| 115336 | 115398 | char *zRet = 0; |
| 115337 | 115399 | char *zFree = 0; |
| 115338 | 115400 | char *zFunction; |
| 115339 | 115401 | int i; |
| 115340 | 115402 | |
| 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); | |
| 115343 | 115414 | }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 | + } | |
| 115345 | 115419 | } |
| 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 | + ); | |
| 115351 | 115425 | return zRet; |
| 115352 | 115426 | } |
| 115353 | 115427 | |
| 115354 | 115428 | /* |
| 115355 | 115429 | ** Return a list of N comma separated question marks, where N is the number |
| @@ -115468,10 +115542,95 @@ | ||
| 115468 | 115542 | } |
| 115469 | 115543 | } |
| 115470 | 115544 | |
| 115471 | 115545 | return SQLITE_OK; |
| 115472 | 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 | +} | |
| 115473 | 115632 | |
| 115474 | 115633 | /* |
| 115475 | 115634 | ** This function is the implementation of both the xConnect and xCreate |
| 115476 | 115635 | ** methods of the FTS3 virtual table. |
| 115477 | 115636 | ** |
| @@ -115513,10 +115672,11 @@ | ||
| 115513 | 115672 | int bNoDocsize = 0; /* True to omit %_docsize table */ |
| 115514 | 115673 | int bDescIdx = 0; /* True to store descending indexes */ |
| 115515 | 115674 | char *zPrefix = 0; /* Prefix parameter value (or NULL) */ |
| 115516 | 115675 | char *zCompress = 0; /* compress=? parameter (or NULL) */ |
| 115517 | 115676 | char *zUncompress = 0; /* uncompress=? parameter (or NULL) */ |
| 115677 | + char *zContent = 0; /* content=? parameter (or NULL) */ | |
| 115518 | 115678 | |
| 115519 | 115679 | assert( strlen(argv[0])==4 ); |
| 115520 | 115680 | assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4) |
| 115521 | 115681 | || (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4) |
| 115522 | 115682 | ); |
| @@ -115556,17 +115716,17 @@ | ||
| 115556 | 115716 | /* Check if it is an FTS4 special argument. */ |
| 115557 | 115717 | else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){ |
| 115558 | 115718 | struct Fts4Option { |
| 115559 | 115719 | const char *zOpt; |
| 115560 | 115720 | int nOpt; |
| 115561 | - char **pzVar; | |
| 115562 | 115721 | } 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 */ | |
| 115568 | 115728 | }; |
| 115569 | 115729 | |
| 115570 | 115730 | int iOpt; |
| 115571 | 115731 | if( !zVal ){ |
| 115572 | 115732 | rc = SQLITE_NOMEM; |
| @@ -115608,17 +115768,24 @@ | ||
| 115608 | 115768 | zVal = 0; |
| 115609 | 115769 | break; |
| 115610 | 115770 | |
| 115611 | 115771 | case 4: /* ORDER */ |
| 115612 | 115772 | 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)) | |
| 115614 | 115774 | ){ |
| 115615 | 115775 | *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal); |
| 115616 | 115776 | rc = SQLITE_ERROR; |
| 115617 | 115777 | } |
| 115618 | 115778 | bDescIdx = (zVal[0]=='d' || zVal[0]=='D'); |
| 115619 | 115779 | break; |
| 115780 | + | |
| 115781 | + default: /* CONTENT */ | |
| 115782 | + assert( iOpt==5 ); | |
| 115783 | + sqlite3_free(zUncompress); | |
| 115784 | + zContent = zVal; | |
| 115785 | + zVal = 0; | |
| 115786 | + break; | |
| 115620 | 115787 | } |
| 115621 | 115788 | } |
| 115622 | 115789 | sqlite3_free(zVal); |
| 115623 | 115790 | } |
| 115624 | 115791 | } |
| @@ -115627,10 +115794,30 @@ | ||
| 115627 | 115794 | else { |
| 115628 | 115795 | nString += (int)(strlen(z) + 1); |
| 115629 | 115796 | aCol[nCol++] = z; |
| 115630 | 115797 | } |
| 115631 | 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 | + } | |
| 115632 | 115819 | if( rc!=SQLITE_OK ) goto fts3_init_out; |
| 115633 | 115820 | |
| 115634 | 115821 | if( nCol==0 ){ |
| 115635 | 115822 | assert( nString==0 ); |
| 115636 | 115823 | aCol[0] = "content"; |
| @@ -115671,10 +115858,12 @@ | ||
| 115671 | 115858 | p->pTokenizer = pTokenizer; |
| 115672 | 115859 | p->nMaxPendingData = FTS3_MAX_PENDING_DATA; |
| 115673 | 115860 | p->bHasDocsize = (isFts4 && bNoDocsize==0); |
| 115674 | 115861 | p->bHasStat = isFts4; |
| 115675 | 115862 | p->bDescIdx = bDescIdx; |
| 115863 | + p->zContentTbl = zContent; | |
| 115864 | + zContent = 0; | |
| 115676 | 115865 | TESTONLY( p->inTransaction = -1 ); |
| 115677 | 115866 | TESTONLY( p->mxSavepoint = -1 ); |
| 115678 | 115867 | |
| 115679 | 115868 | p->aIndex = (struct Fts3Index *)&p->azColumn[nCol]; |
| 115680 | 115869 | memcpy(p->aIndex, aIndex, sizeof(struct Fts3Index) * nIndex); |
| @@ -115732,10 +115921,11 @@ | ||
| 115732 | 115921 | fts3_init_out: |
| 115733 | 115922 | sqlite3_free(zPrefix); |
| 115734 | 115923 | sqlite3_free(aIndex); |
| 115735 | 115924 | sqlite3_free(zCompress); |
| 115736 | 115925 | sqlite3_free(zUncompress); |
| 115926 | + sqlite3_free(zContent); | |
| 115737 | 115927 | sqlite3_free((void *)aCol); |
| 115738 | 115928 | if( rc!=SQLITE_OK ){ |
| 115739 | 115929 | if( p ){ |
| 115740 | 115930 | fts3DisconnectMethod((sqlite3_vtab *)p); |
| 115741 | 115931 | }else if( pTokenizer ){ |
| @@ -115882,40 +116072,69 @@ | ||
| 115882 | 116072 | sqlite3_free(pCsr->aMatchinfo); |
| 115883 | 116073 | assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); |
| 115884 | 116074 | sqlite3_free(pCsr); |
| 115885 | 116075 | return SQLITE_OK; |
| 115886 | 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 | +} | |
| 115887 | 116102 | |
| 115888 | 116103 | /* |
| 115889 | 116104 | ** Position the pCsr->pStmt statement so that it is on the row |
| 115890 | 116105 | ** of the %_content table that contains the last match. Return |
| 115891 | 116106 | ** SQLITE_OK on success. |
| 115892 | 116107 | */ |
| 115893 | 116108 | static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ |
| 116109 | + int rc = SQLITE_OK; | |
| 115894 | 116110 | 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; | |
| 115917 | 116136 | } |
| 115918 | 116137 | |
| 115919 | 116138 | /* |
| 115920 | 116139 | ** This function is used to process a single interior node when searching |
| 115921 | 116140 | ** a b-tree for a term or term prefix. The node data is passed to this |
| @@ -116351,20 +116570,20 @@ | ||
| 116351 | 116570 | int isSaveLeft, /* Save the left position */ |
| 116352 | 116571 | int isExact, /* If *pp1 is exactly nTokens before *pp2 */ |
| 116353 | 116572 | char **pp1, /* IN/OUT: Left input list */ |
| 116354 | 116573 | char **pp2 /* IN/OUT: Right input list */ |
| 116355 | 116574 | ){ |
| 116356 | - char *p = (pp ? *pp : 0); | |
| 116575 | + char *p = *pp; | |
| 116357 | 116576 | char *p1 = *pp1; |
| 116358 | 116577 | char *p2 = *pp2; |
| 116359 | 116578 | int iCol1 = 0; |
| 116360 | 116579 | int iCol2 = 0; |
| 116361 | 116580 | |
| 116362 | 116581 | /* Never set both isSaveLeft and isExact for the same invocation. */ |
| 116363 | 116582 | assert( isSaveLeft==0 || isExact==0 ); |
| 116364 | 116583 | |
| 116365 | - assert( *p1!=0 && *p2!=0 ); | |
| 116584 | + assert( p!=0 && *p1!=0 && *p2!=0 ); | |
| 116366 | 116585 | if( *p1==POS_COLUMN ){ |
| 116367 | 116586 | p1++; |
| 116368 | 116587 | p1 += sqlite3Fts3GetVarint32(p1, &iCol1); |
| 116369 | 116588 | } |
| 116370 | 116589 | if( *p2==POS_COLUMN ){ |
| @@ -116377,11 +116596,11 @@ | ||
| 116377 | 116596 | char *pSave = p; |
| 116378 | 116597 | sqlite3_int64 iPrev = 0; |
| 116379 | 116598 | sqlite3_int64 iPos1 = 0; |
| 116380 | 116599 | sqlite3_int64 iPos2 = 0; |
| 116381 | 116600 | |
| 116382 | - if( pp && iCol1 ){ | |
| 116601 | + if( iCol1 ){ | |
| 116383 | 116602 | *p++ = POS_COLUMN; |
| 116384 | 116603 | p += sqlite3Fts3PutVarint(p, iCol1); |
| 116385 | 116604 | } |
| 116386 | 116605 | |
| 116387 | 116606 | assert( *p1!=POS_END && *p1!=POS_COLUMN ); |
| @@ -116392,20 +116611,14 @@ | ||
| 116392 | 116611 | while( 1 ){ |
| 116393 | 116612 | if( iPos2==iPos1+nToken |
| 116394 | 116613 | || (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken) |
| 116395 | 116614 | ){ |
| 116396 | 116615 | 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 | 116616 | iSave = isSaveLeft ? iPos1 : iPos2; |
| 116405 | 116617 | fts3PutDeltaVarint(&p, &iPrev, iSave+2); iPrev -= 2; |
| 116406 | 116618 | pSave = 0; |
| 116619 | + assert( p ); | |
| 116407 | 116620 | } |
| 116408 | 116621 | if( (!isSaveLeft && iPos2<=(iPos1+nToken)) || iPos2<=iPos1 ){ |
| 116409 | 116622 | if( (*p2&0xFE)==0 ) break; |
| 116410 | 116623 | fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; |
| 116411 | 116624 | }else{ |
| @@ -116450,11 +116663,11 @@ | ||
| 116450 | 116663 | |
| 116451 | 116664 | fts3PoslistCopy(0, &p2); |
| 116452 | 116665 | fts3PoslistCopy(0, &p1); |
| 116453 | 116666 | *pp1 = p1; |
| 116454 | 116667 | *pp2 = p2; |
| 116455 | - if( !pp || *pp==p ){ | |
| 116668 | + if( *pp==p ){ | |
| 116456 | 116669 | return 0; |
| 116457 | 116670 | } |
| 116458 | 116671 | *p++ = 0x00; |
| 116459 | 116672 | *pp = p; |
| 116460 | 116673 | return 1; |
| @@ -116752,10 +116965,60 @@ | ||
| 116752 | 116965 | } |
| 116753 | 116966 | } |
| 116754 | 116967 | |
| 116755 | 116968 | *pnRight = p - aOut; |
| 116756 | 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 | +} | |
| 116757 | 117020 | |
| 116758 | 117021 | |
| 116759 | 117022 | /* |
| 116760 | 117023 | ** Merge all doclists in the TermSelect.aaOutput[] array into a single |
| 116761 | 117024 | ** doclist stored in TermSelect.aaOutput[0]. If successful, delete all |
| @@ -117109,10 +117372,11 @@ | ||
| 117109 | 117372 | pSegcsr = pTok->pSegcsr; |
| 117110 | 117373 | memset(&tsc, 0, sizeof(TermSelect)); |
| 117111 | 117374 | |
| 117112 | 117375 | filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | FTS3_SEGMENT_REQUIRE_POS |
| 117113 | 117376 | | (pTok->isPrefix ? FTS3_SEGMENT_PREFIX : 0) |
| 117377 | + | (pTok->bFirst ? FTS3_SEGMENT_FIRST : 0) | |
| 117114 | 117378 | | (iColumn<p->nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0); |
| 117115 | 117379 | filter.iCol = iColumn; |
| 117116 | 117380 | filter.zTerm = pTok->z; |
| 117117 | 117381 | filter.nTerm = pTok->n; |
| 117118 | 117382 | |
| @@ -117249,12 +117513,12 @@ | ||
| 117249 | 117513 | |
| 117250 | 117514 | if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ |
| 117251 | 117515 | return SQLITE_NOMEM; |
| 117252 | 117516 | } |
| 117253 | 117517 | |
| 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 | |
| 117256 | 117520 | ); |
| 117257 | 117521 | if( rc!=SQLITE_OK ){ |
| 117258 | 117522 | if( rc==SQLITE_ERROR ){ |
| 117259 | 117523 | static const char *zErr = "malformed MATCH expression: [%s]"; |
| 117260 | 117524 | p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery); |
| @@ -117277,26 +117541,27 @@ | ||
| 117277 | 117541 | ** statement loops through all rows of the %_content table. For a |
| 117278 | 117542 | ** full-text query or docid lookup, the statement retrieves a single |
| 117279 | 117543 | ** row by docid. |
| 117280 | 117544 | */ |
| 117281 | 117545 | 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; | |
| 117298 | 117563 | |
| 117299 | 117564 | return fts3NextMethod(pCursor); |
| 117300 | 117565 | } |
| 117301 | 117566 | |
| 117302 | 117567 | /* |
| @@ -117345,11 +117610,11 @@ | ||
| 117345 | 117610 | ** Return a blob which is a pointer to the cursor. |
| 117346 | 117611 | */ |
| 117347 | 117612 | sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT); |
| 117348 | 117613 | }else{ |
| 117349 | 117614 | rc = fts3CursorSeek(0, pCsr); |
| 117350 | - if( rc==SQLITE_OK ){ | |
| 117615 | + if( rc==SQLITE_OK && sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){ | |
| 117351 | 117616 | sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1)); |
| 117352 | 117617 | } |
| 117353 | 117618 | } |
| 117354 | 117619 | |
| 117355 | 117620 | assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); |
| @@ -117638,19 +117903,26 @@ | ||
| 117638 | 117903 | ){ |
| 117639 | 117904 | Fts3Table *p = (Fts3Table *)pVtab; |
| 117640 | 117905 | sqlite3 *db = p->db; /* Database connection */ |
| 117641 | 117906 | int rc; /* Return Code */ |
| 117642 | 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 ); | |
| 117643 | 117915 | 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 | + ); | |
| 117646 | 117922 | } |
| 117647 | 117923 | |
| 117648 | - fts3DbExec(&rc, db, | |
| 117649 | - "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", | |
| 117650 | - p->zDb, p->zName, zName | |
| 117651 | - ); | |
| 117652 | 117924 | if( p->bHasDocsize ){ |
| 117653 | 117925 | fts3DbExec(&rc, db, |
| 117654 | 117926 | "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';", |
| 117655 | 117927 | p->zDb, p->zName, zName |
| 117656 | 117928 | ); |
| @@ -118005,25 +118277,24 @@ | ||
| 118005 | 118277 | ** |
| 118006 | 118278 | ** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code. |
| 118007 | 118279 | */ |
| 118008 | 118280 | static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){ |
| 118009 | 118281 | int iToken; /* Used to iterate through phrase tokens */ |
| 118010 | - int rc = SQLITE_OK; /* Return code */ | |
| 118011 | 118282 | char *aPoslist = 0; /* Position list for deferred tokens */ |
| 118012 | 118283 | int nPoslist = 0; /* Number of bytes in aPoslist */ |
| 118013 | 118284 | int iPrev = -1; /* Token number of previous deferred token */ |
| 118014 | 118285 | |
| 118015 | 118286 | assert( pPhrase->doclist.bFreeList==0 ); |
| 118016 | 118287 | |
| 118017 | - for(iToken=0; rc==SQLITE_OK && iToken<pPhrase->nToken; iToken++){ | |
| 118288 | + for(iToken=0; iToken<pPhrase->nToken; iToken++){ | |
| 118018 | 118289 | Fts3PhraseToken *pToken = &pPhrase->aToken[iToken]; |
| 118019 | 118290 | Fts3DeferredToken *pDeferred = pToken->pDeferred; |
| 118020 | 118291 | |
| 118021 | 118292 | if( pDeferred ){ |
| 118022 | 118293 | char *pList; |
| 118023 | 118294 | int nList; |
| 118024 | - rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList); | |
| 118295 | + int rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList); | |
| 118025 | 118296 | if( rc!=SQLITE_OK ) return rc; |
| 118026 | 118297 | |
| 118027 | 118298 | if( pList==0 ){ |
| 118028 | 118299 | sqlite3_free(aPoslist); |
| 118029 | 118300 | pPhrase->doclist.pList = 0; |
| @@ -118120,10 +118391,11 @@ | ||
| 118120 | 118391 | if( pCsr->bDesc==pTab->bDescIdx |
| 118121 | 118392 | && bOptOk==1 |
| 118122 | 118393 | && p->nToken==1 |
| 118123 | 118394 | && pFirst->pSegcsr |
| 118124 | 118395 | && pFirst->pSegcsr->bLookup |
| 118396 | + && pFirst->bFirst==0 | |
| 118125 | 118397 | ){ |
| 118126 | 118398 | /* Use the incremental approach. */ |
| 118127 | 118399 | int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn); |
| 118128 | 118400 | rc = sqlite3Fts3MsrIncrStart( |
| 118129 | 118401 | pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n); |
| @@ -118349,11 +118621,11 @@ | ||
| 118349 | 118621 | Fts3Expr *pExpr, /* Expression to consider */ |
| 118350 | 118622 | Fts3TokenAndCost **ppTC, /* Write new entries to *(*ppTC)++ */ |
| 118351 | 118623 | Fts3Expr ***ppOr, /* Write new OR root to *(*ppOr)++ */ |
| 118352 | 118624 | int *pRc /* IN/OUT: Error code */ |
| 118353 | 118625 | ){ |
| 118354 | - if( *pRc==SQLITE_OK && pExpr ){ | |
| 118626 | + if( *pRc==SQLITE_OK ){ | |
| 118355 | 118627 | if( pExpr->eType==FTSQUERY_PHRASE ){ |
| 118356 | 118628 | Fts3Phrase *pPhrase = pExpr->pPhrase; |
| 118357 | 118629 | int i; |
| 118358 | 118630 | for(i=0; *pRc==SQLITE_OK && i<pPhrase->nToken; i++){ |
| 118359 | 118631 | Fts3TokenAndCost *pTC = (*ppTC)++; |
| @@ -118363,10 +118635,15 @@ | ||
| 118363 | 118635 | pTC->pToken = &pPhrase->aToken[i]; |
| 118364 | 118636 | pTC->iCol = pPhrase->iColumn; |
| 118365 | 118637 | *pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl); |
| 118366 | 118638 | } |
| 118367 | 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 ); | |
| 118368 | 118645 | if( pExpr->eType==FTSQUERY_OR ){ |
| 118369 | 118646 | pRoot = pExpr->pLeft; |
| 118370 | 118647 | **ppOr = pRoot; |
| 118371 | 118648 | (*ppOr)++; |
| 118372 | 118649 | } |
| @@ -118466,10 +118743,19 @@ | ||
| 118466 | 118743 | int nOvfl = 0; /* Total overflow pages used by doclists */ |
| 118467 | 118744 | int nToken = 0; /* Total number of tokens in cluster */ |
| 118468 | 118745 | |
| 118469 | 118746 | int nMinEst = 0; /* The minimum count for any phrase so far. */ |
| 118470 | 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 | + } | |
| 118471 | 118757 | |
| 118472 | 118758 | /* Count the tokens in this AND/NEAR cluster. If none of the doclists |
| 118473 | 118759 | ** associated with the tokens spill onto overflow pages, or if there is |
| 118474 | 118760 | ** only 1 token, exit early. No tokens to defer in this case. */ |
| 118475 | 118761 | for(ii=0; ii<nTC; ii++){ |
| @@ -118529,11 +118815,15 @@ | ||
| 118529 | 118815 | Fts3PhraseToken *pToken = pTC->pToken; |
| 118530 | 118816 | rc = sqlite3Fts3DeferToken(pCsr, pToken, pTC->iCol); |
| 118531 | 118817 | fts3SegReaderCursorFree(pToken->pSegcsr); |
| 118532 | 118818 | pToken->pSegcsr = 0; |
| 118533 | 118819 | }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 | + | |
| 118535 | 118825 | if( ii==0 || pTC->pPhrase->nToken>1 ){ |
| 118536 | 118826 | /* Either this is the cheapest token in the entire query, or it is |
| 118537 | 118827 | ** part of a multi-token phrase. Either way, the entire doclist will |
| 118538 | 118828 | ** (eventually) be loaded into memory. It may as well be now. */ |
| 118539 | 118829 | Fts3PhraseToken *pToken = pTC->pToken; |
| @@ -119990,10 +120280,11 @@ | ||
| 119990 | 120280 | */ |
| 119991 | 120281 | typedef struct ParseContext ParseContext; |
| 119992 | 120282 | struct ParseContext { |
| 119993 | 120283 | sqlite3_tokenizer *pTokenizer; /* Tokenizer module */ |
| 119994 | 120284 | const char **azCol; /* Array of column names for fts3 table */ |
| 120285 | + int bFts4; /* True to allow FTS4-only syntax */ | |
| 119995 | 120286 | int nCol; /* Number of entries in azCol[] */ |
| 119996 | 120287 | int iDefaultCol; /* Default column to query */ |
| 119997 | 120288 | int isNot; /* True if getNextNode() sees a unary - */ |
| 119998 | 120289 | sqlite3_context *pCtx; /* Write error message here */ |
| 119999 | 120290 | int nNest; /* Number of nested brackets */ |
| @@ -120077,13 +120368,25 @@ | ||
| 120077 | 120368 | |
| 120078 | 120369 | if( iEnd<n && z[iEnd]=='*' ){ |
| 120079 | 120370 | pRet->pPhrase->aToken[0].isPrefix = 1; |
| 120080 | 120371 | iEnd++; |
| 120081 | 120372 | } |
| 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 | + } | |
| 120084 | 120386 | } |
| 120387 | + | |
| 120085 | 120388 | } |
| 120086 | 120389 | nConsumed = iEnd; |
| 120087 | 120390 | } |
| 120088 | 120391 | |
| 120089 | 120392 | pModule->xClose(pCursor); |
| @@ -120178,10 +120481,11 @@ | ||
| 120178 | 120481 | memcpy(&zTemp[nTemp], zByte, nByte); |
| 120179 | 120482 | nTemp += nByte; |
| 120180 | 120483 | |
| 120181 | 120484 | pToken->n = nByte; |
| 120182 | 120485 | pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*'); |
| 120486 | + pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^'); | |
| 120183 | 120487 | nToken = ii+1; |
| 120184 | 120488 | } |
| 120185 | 120489 | } |
| 120186 | 120490 | |
| 120187 | 120491 | pModule->xClose(pCursor); |
| @@ -120629,10 +120933,11 @@ | ||
| 120629 | 120933 | ** match any table column. |
| 120630 | 120934 | */ |
| 120631 | 120935 | SQLITE_PRIVATE int sqlite3Fts3ExprParse( |
| 120632 | 120936 | sqlite3_tokenizer *pTokenizer, /* Tokenizer module */ |
| 120633 | 120937 | char **azCol, /* Array of column names for fts3 table */ |
| 120938 | + int bFts4, /* True to allow FTS4-only syntax */ | |
| 120634 | 120939 | int nCol, /* Number of entries in azCol[] */ |
| 120635 | 120940 | int iDefaultCol, /* Default column to query */ |
| 120636 | 120941 | const char *z, int n, /* Text of MATCH query */ |
| 120637 | 120942 | Fts3Expr **ppExpr /* OUT: Parsed query structure */ |
| 120638 | 120943 | ){ |
| @@ -120642,10 +120947,11 @@ | ||
| 120642 | 120947 | sParse.pTokenizer = pTokenizer; |
| 120643 | 120948 | sParse.azCol = (const char **)azCol; |
| 120644 | 120949 | sParse.nCol = nCol; |
| 120645 | 120950 | sParse.iDefaultCol = iDefaultCol; |
| 120646 | 120951 | sParse.nNest = 0; |
| 120952 | + sParse.bFts4 = bFts4; | |
| 120647 | 120953 | if( z==0 ){ |
| 120648 | 120954 | *ppExpr = 0; |
| 120649 | 120955 | return SQLITE_OK; |
| 120650 | 120956 | } |
| 120651 | 120957 | if( n<0 ){ |
| @@ -120831,11 +121137,11 @@ | ||
| 120831 | 121137 | for(ii=0; ii<nCol; ii++){ |
| 120832 | 121138 | azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]); |
| 120833 | 121139 | } |
| 120834 | 121140 | |
| 120835 | 121141 | rc = sqlite3Fts3ExprParse( |
| 120836 | - pTokenizer, azCol, nCol, nCol, zExpr, nExpr, &pExpr | |
| 121142 | + pTokenizer, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr | |
| 120837 | 121143 | ); |
| 120838 | 121144 | if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ |
| 120839 | 121145 | sqlite3_result_error(context, "Error parsing expression", -1); |
| 120840 | 121146 | }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ |
| 120841 | 121147 | sqlite3_result_error_nomem(context); |
| @@ -122878,11 +123184,11 @@ | ||
| 122878 | 123184 | /* 2 */ "DELETE FROM %Q.'%q_content'", |
| 122879 | 123185 | /* 3 */ "DELETE FROM %Q.'%q_segments'", |
| 122880 | 123186 | /* 4 */ "DELETE FROM %Q.'%q_segdir'", |
| 122881 | 123187 | /* 5 */ "DELETE FROM %Q.'%q_docsize'", |
| 122882 | 123188 | /* 6 */ "DELETE FROM %Q.'%q_stat'", |
| 122883 | -/* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?", | |
| 123189 | +/* 7 */ "SELECT %s WHERE rowid=?", | |
| 122884 | 123190 | /* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1", |
| 122885 | 123191 | /* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)", |
| 122886 | 123192 | /* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)", |
| 122887 | 123193 | /* 11 */ "INSERT INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)", |
| 122888 | 123194 | |
| @@ -122920,11 +123226,11 @@ | ||
| 122920 | 123226 | if( !pStmt ){ |
| 122921 | 123227 | char *zSql; |
| 122922 | 123228 | if( eStmt==SQL_CONTENT_INSERT ){ |
| 122923 | 123229 | zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); |
| 122924 | 123230 | }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); | |
| 122926 | 123232 | }else{ |
| 122927 | 123233 | zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); |
| 122928 | 123234 | } |
| 122929 | 123235 | if( !zSql ){ |
| 122930 | 123236 | rc = SQLITE_NOMEM; |
| @@ -123031,21 +123337,28 @@ | ||
| 123031 | 123337 | ** We try to avoid this because if FTS3 returns any error when committing |
| 123032 | 123338 | ** a transaction, the whole transaction will be rolled back. And this is |
| 123033 | 123339 | ** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can |
| 123034 | 123340 | ** still happen if the user reads data directly from the %_segments or |
| 123035 | 123341 | ** %_segdir tables instead of going through FTS3 though. |
| 123342 | +** | |
| 123343 | +** This reasoning does not apply to a content=xxx table. | |
| 123036 | 123344 | */ |
| 123037 | 123345 | SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){ |
| 123038 | 123346 | int rc; /* Return code */ |
| 123039 | 123347 | sqlite3_stmt *pStmt; /* Statement used to obtain lock */ |
| 123040 | 123348 | |
| 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; | |
| 123046 | 123358 | } |
| 123359 | + | |
| 123047 | 123360 | return rc; |
| 123048 | 123361 | } |
| 123049 | 123362 | |
| 123050 | 123363 | /* |
| 123051 | 123364 | ** Set *ppStmt to a statement handle that may be used to iterate through |
| @@ -123401,10 +123714,22 @@ | ||
| 123401 | 123714 | sqlite3_value **apVal, /* Array of values to insert */ |
| 123402 | 123715 | sqlite3_int64 *piDocid /* OUT: Docid for row just inserted */ |
| 123403 | 123716 | ){ |
| 123404 | 123717 | int rc; /* Return code */ |
| 123405 | 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 | + } | |
| 123406 | 123731 | |
| 123407 | 123732 | /* Locate the statement handle used to insert data into the %_content |
| 123408 | 123733 | ** table. The SQL for this statement is: |
| 123409 | 123734 | ** |
| 123410 | 123735 | ** INSERT INTO %_content VALUES(?, ?, ?, ...) |
| @@ -123452,18 +123777,20 @@ | ||
| 123452 | 123777 | |
| 123453 | 123778 | /* |
| 123454 | 123779 | ** Remove all data from the FTS3 table. Clear the hash table containing |
| 123455 | 123780 | ** pending terms. |
| 123456 | 123781 | */ |
| 123457 | -static int fts3DeleteAll(Fts3Table *p){ | |
| 123782 | +static int fts3DeleteAll(Fts3Table *p, int bContent){ | |
| 123458 | 123783 | int rc = SQLITE_OK; /* Return code */ |
| 123459 | 123784 | |
| 123460 | 123785 | /* Discard the contents of the pending-terms hash table. */ |
| 123461 | 123786 | sqlite3Fts3PendingTermsClear(p); |
| 123462 | 123787 | |
| 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); | |
| 123465 | 123792 | fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0); |
| 123466 | 123793 | fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); |
| 123467 | 123794 | if( p->bHasDocsize ){ |
| 123468 | 123795 | fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0); |
| 123469 | 123796 | } |
| @@ -124747,16 +125074,22 @@ | ||
| 124747 | 125074 | ** error occurs, an SQLite error code is returned. |
| 124748 | 125075 | */ |
| 124749 | 125076 | static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){ |
| 124750 | 125077 | sqlite3_stmt *pStmt; |
| 124751 | 125078 | 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); | |
| 124756 | 125090 | } |
| 124757 | - rc = sqlite3_reset(pStmt); | |
| 124758 | 125091 | } |
| 124759 | 125092 | return rc; |
| 124760 | 125093 | } |
| 124761 | 125094 | |
| 124762 | 125095 | /* |
| @@ -125104,10 +125437,11 @@ | ||
| 125104 | 125437 | int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY); |
| 125105 | 125438 | int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS); |
| 125106 | 125439 | int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER); |
| 125107 | 125440 | int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX); |
| 125108 | 125441 | int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN); |
| 125442 | + int isFirst = (pCsr->pFilter->flags & FTS3_SEGMENT_FIRST); | |
| 125109 | 125443 | |
| 125110 | 125444 | Fts3SegReader **apSegment = pCsr->apSegment; |
| 125111 | 125445 | int nSegment = pCsr->nSegment; |
| 125112 | 125446 | Fts3SegFilter *pFilter = pCsr->pFilter; |
| 125113 | 125447 | int (*xCmp)(Fts3SegReader *, Fts3SegReader *) = ( |
| @@ -125163,10 +125497,11 @@ | ||
| 125163 | 125497 | } |
| 125164 | 125498 | |
| 125165 | 125499 | assert( isIgnoreEmpty || (isRequirePos && !isColFilter) ); |
| 125166 | 125500 | if( nMerge==1 |
| 125167 | 125501 | && !isIgnoreEmpty |
| 125502 | + && !isFirst | |
| 125168 | 125503 | && (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0) |
| 125169 | 125504 | ){ |
| 125170 | 125505 | pCsr->nDoclist = apSegment[0]->nDoclist; |
| 125171 | 125506 | if( fts3SegReaderIsPending(apSegment[0]) ){ |
| 125172 | 125507 | rc = fts3MsrBufferData(pCsr, apSegment[0]->aDoclist, pCsr->nDoclist); |
| @@ -125228,16 +125563,28 @@ | ||
| 125228 | 125563 | if( !aNew ){ |
| 125229 | 125564 | return SQLITE_NOMEM; |
| 125230 | 125565 | } |
| 125231 | 125566 | pCsr->aBuffer = aNew; |
| 125232 | 125567 | } |
| 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 | + } | |
| 125239 | 125586 | } |
| 125240 | 125587 | } |
| 125241 | 125588 | |
| 125242 | 125589 | fts3SegReaderSort(apSegment, nMerge, j, xCmp); |
| 125243 | 125590 | } |
| @@ -125409,13 +125756,13 @@ | ||
| 125409 | 125756 | ** Insert the sizes (in tokens) for each column of the document |
| 125410 | 125757 | ** with docid equal to p->iPrevDocid. The sizes are encoded as |
| 125411 | 125758 | ** a blob of varints. |
| 125412 | 125759 | */ |
| 125413 | 125760 | 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 */ | |
| 125417 | 125764 | ){ |
| 125418 | 125765 | char *pBlob; /* The BLOB encoding of the document size */ |
| 125419 | 125766 | int nBlob; /* Number of bytes in the BLOB */ |
| 125420 | 125767 | sqlite3_stmt *pStmt; /* Statement used to insert the encoding */ |
| 125421 | 125768 | int rc; /* Result code from subfunctions */ |
| @@ -125532,10 +125879,90 @@ | ||
| 125532 | 125879 | sqlite3Fts3SegmentsClose(p); |
| 125533 | 125880 | sqlite3Fts3PendingTermsClear(p); |
| 125534 | 125881 | |
| 125535 | 125882 | return (rc==SQLITE_OK && bReturnDone && bSeenDone) ? SQLITE_DONE : rc; |
| 125536 | 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 | +} | |
| 125537 | 125964 | |
| 125538 | 125965 | /* |
| 125539 | 125966 | ** Handle a 'special' INSERT of the form: |
| 125540 | 125967 | ** |
| 125541 | 125968 | ** "INSERT INTO tbl(tbl) VALUES(<expr>)" |
| @@ -125550,10 +125977,12 @@ | ||
| 125550 | 125977 | |
| 125551 | 125978 | if( !zVal ){ |
| 125552 | 125979 | return SQLITE_NOMEM; |
| 125553 | 125980 | }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ |
| 125554 | 125981 | rc = fts3DoOptimize(p, 0); |
| 125982 | + }else if( nVal==7 && 0==sqlite3_strnicmp(zVal, "rebuild", 7) ){ | |
| 125983 | + rc = fts3DoRebuild(p); | |
| 125555 | 125984 | #ifdef SQLITE_TEST |
| 125556 | 125985 | }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ |
| 125557 | 125986 | p->nNodeSize = atoi(&zVal[9]); |
| 125558 | 125987 | rc = SQLITE_OK; |
| 125559 | 125988 | }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ |
| @@ -125630,10 +126059,11 @@ | ||
| 125630 | 126059 | pTC->pTokenizer = pT; |
| 125631 | 126060 | rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos); |
| 125632 | 126061 | for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){ |
| 125633 | 126062 | Fts3PhraseToken *pPT = pDef->pToken; |
| 125634 | 126063 | if( (pDef->iCol>=p->nColumn || pDef->iCol==i) |
| 126064 | + && (pPT->bFirst==0 || iPos==0) | |
| 125635 | 126065 | && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken)) |
| 125636 | 126066 | && (0==memcmp(zToken, pPT->z, pPT->n)) |
| 125637 | 126067 | ){ |
| 125638 | 126068 | fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc); |
| 125639 | 126069 | } |
| @@ -125721,18 +126151,22 @@ | ||
| 125721 | 126151 | if( rc==SQLITE_OK ){ |
| 125722 | 126152 | if( isEmpty ){ |
| 125723 | 126153 | /* Deleting this row means the whole table is empty. In this case |
| 125724 | 126154 | ** delete the contents of all three tables and throw away any |
| 125725 | 126155 | ** data in the pendingTerms hash table. */ |
| 125726 | - rc = fts3DeleteAll(p); | |
| 126156 | + rc = fts3DeleteAll(p, 1); | |
| 125727 | 126157 | *pnDoc = *pnDoc - 1; |
| 125728 | 126158 | }else{ |
| 125729 | 126159 | sqlite3_int64 iRemove = sqlite3_value_int64(pRowid); |
| 125730 | 126160 | rc = fts3PendingTermsDocid(p, iRemove); |
| 125731 | 126161 | 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 | + } | |
| 125734 | 126168 | if( p->bHasDocsize ){ |
| 125735 | 126169 | fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid); |
| 125736 | 126170 | } |
| 125737 | 126171 | } |
| 125738 | 126172 | } |
| @@ -125788,11 +126222,11 @@ | ||
| 125788 | 126222 | ** should be deleted from the database before inserting the new row. Or, |
| 125789 | 126223 | ** if the on-conflict mode is other than REPLACE, then this method must |
| 125790 | 126224 | ** detect the conflict and return SQLITE_CONSTRAINT before beginning to |
| 125791 | 126225 | ** modify the database file. |
| 125792 | 126226 | */ |
| 125793 | - if( nArg>1 ){ | |
| 126227 | + if( nArg>1 && p->zContentTbl==0 ){ | |
| 125794 | 126228 | /* Find the value object that holds the new rowid value. */ |
| 125795 | 126229 | sqlite3_value *pNewRowid = apVal[3+p->nColumn]; |
| 125796 | 126230 | if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){ |
| 125797 | 126231 | pNewRowid = apVal[1]; |
| 125798 | 126232 | } |
| @@ -125839,11 +126273,13 @@ | ||
| 125839 | 126273 | |
| 125840 | 126274 | /* If this is an INSERT or UPDATE operation, insert the new record. */ |
| 125841 | 126275 | if( nArg>1 && rc==SQLITE_OK ){ |
| 125842 | 126276 | if( bInsertDone==0 ){ |
| 125843 | 126277 | 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 | + } | |
| 125845 | 126281 | } |
| 125846 | 126282 | if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){ |
| 125847 | 126283 | rc = fts3PendingTermsDocid(p, *pRowid); |
| 125848 | 126284 | } |
| 125849 | 126285 | if( rc==SQLITE_OK ){ |
| @@ -126259,10 +126695,11 @@ | ||
| 126259 | 126695 | pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol); |
| 126260 | 126696 | if( pCsr ){ |
| 126261 | 126697 | int iFirst = 0; |
| 126262 | 126698 | pPhrase->pList = pCsr; |
| 126263 | 126699 | fts3GetDeltaPosition(&pCsr, &iFirst); |
| 126700 | + assert( iFirst>=0 ); | |
| 126264 | 126701 | pPhrase->pHead = pCsr; |
| 126265 | 126702 | pPhrase->pTail = pCsr; |
| 126266 | 126703 | pPhrase->iHead = iFirst; |
| 126267 | 126704 | pPhrase->iTail = iFirst; |
| 126268 | 126705 | }else{ |
| @@ -127300,11 +127737,11 @@ | ||
| 127300 | 127737 | } |
| 127301 | 127738 | } |
| 127302 | 127739 | |
| 127303 | 127740 | if( !pTerm ){ |
| 127304 | 127741 | /* All offsets for this column have been gathered. */ |
| 127305 | - break; | |
| 127742 | + rc = SQLITE_DONE; | |
| 127306 | 127743 | }else{ |
| 127307 | 127744 | assert( iCurrent<=iMinPos ); |
| 127308 | 127745 | if( 0==(0xFE&*pTerm->pList) ){ |
| 127309 | 127746 | pTerm->pList = 0; |
| 127310 | 127747 | }else{ |
| @@ -127317,11 +127754,11 @@ | ||
| 127317 | 127754 | char aBuffer[64]; |
| 127318 | 127755 | sqlite3_snprintf(sizeof(aBuffer), aBuffer, |
| 127319 | 127756 | "%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart |
| 127320 | 127757 | ); |
| 127321 | 127758 | rc = fts3StringAppend(&res, aBuffer, -1); |
| 127322 | - }else if( rc==SQLITE_DONE ){ | |
| 127759 | + }else if( rc==SQLITE_DONE && pTab->zContentTbl==0 ){ | |
| 127323 | 127760 | rc = FTS_CORRUPT_VTAB; |
| 127324 | 127761 | } |
| 127325 | 127762 | } |
| 127326 | 127763 | } |
| 127327 | 127764 | if( rc==SQLITE_DONE ){ |
| 127328 | 127765 |
| --- 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 @@ | ||
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | 110 | #define SQLITE_VERSION "3.7.9" |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3007009 |
| 112 | -#define SQLITE_SOURCE_ID "2011-10-15 00:16:30 39408702a989f907261c298bf0947f3e68bd10fe" | |
| 112 | +#define SQLITE_SOURCE_ID "2011-10-29 19:25:08 5b82ec6fbbd2f4195ad06dd911de3817373ad5bf" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| @@ -1402,12 +1402,12 @@ | ||
| 1402 | 1402 | ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or |
| 1403 | 1403 | ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory |
| 1404 | 1404 | ** allocator is engaged to handle all of SQLites memory allocation needs. |
| 1405 | 1405 | ** The first pointer (the memory pointer) must be aligned to an 8-byte |
| 1406 | 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> | |
| 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 | 1409 | ** |
| 1410 | 1410 | ** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt> |
| 1411 | 1411 | ** <dd> ^(This option takes a single argument which is a pointer to an |
| 1412 | 1412 | ** instance of the [sqlite3_mutex_methods] structure. The argument specifies |
| 1413 | 1413 | ** alternative low-level mutex routines to be used in place |
| 1414 | 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-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 @@ | ||
| 170 | 170 | int i; |
| 171 | 171 | for(i=3; i<g.argc; i++){ |
| 172 | 172 | stash_add_file_or_dir(stashid, vid, g.argv[i]); |
| 173 | 173 | } |
| 174 | 174 | }else{ |
| 175 | - stash_add_file_or_dir(stashid, vid, "."); | |
| 175 | + stash_add_file_or_dir(stashid, vid, g.zLocalRoot); | |
| 176 | 176 | } |
| 177 | 177 | return stashid; |
| 178 | 178 | } |
| 179 | 179 | |
| 180 | 180 | /* |
| @@ -258,19 +258,20 @@ | ||
| 258 | 258 | file_delete(zOPath); |
| 259 | 259 | } |
| 260 | 260 | } |
| 261 | 261 | db_finalize(&q); |
| 262 | 262 | if( nConflict ){ |
| 263 | - fossil_print("WARNING: %d merge conflicts - see messages above for details.\n", | |
| 264 | - nConflict); | |
| 263 | + fossil_print( | |
| 264 | + "WARNING: %d merge conflicts - see messages above for details.\n", | |
| 265 | + nConflict); | |
| 265 | 266 | } |
| 266 | 267 | } |
| 267 | 268 | |
| 268 | 269 | /* |
| 269 | 270 | ** Show the diffs associate with a single stash. |
| 270 | 271 | */ |
| 271 | -static void stash_diff(int stashid, const char *zDiffCmd){ | |
| 272 | +static void stash_diff(int stashid, const char *zDiffCmd, int diffFlags){ | |
| 272 | 273 | Stmt q; |
| 273 | 274 | Blob empty; |
| 274 | 275 | blob_zero(&empty); |
| 275 | 276 | db_prepare(&q, |
| 276 | 277 | "SELECT rid, isRemoved, isExec, isLink, origname, newname, delta" |
| @@ -286,21 +287,21 @@ | ||
| 286 | 287 | char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig); |
| 287 | 288 | Blob delta; |
| 288 | 289 | if( rid==0 ){ |
| 289 | 290 | db_ephemeral_blob(&q, 6, &delta); |
| 290 | 291 | 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); | |
| 293 | 294 | }else if( isRemoved ){ |
| 294 | 295 | fossil_print("DELETE %s\n", zOrig); |
| 295 | 296 | if( file_wd_islink(zOPath) ){ |
| 296 | 297 | blob_read_link(&delta, zOPath); |
| 297 | 298 | }else{ |
| 298 | 299 | blob_read_from_file(&delta, zOPath); |
| 299 | 300 | } |
| 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); | |
| 302 | 303 | }else{ |
| 303 | 304 | Blob a, b, disk; |
| 304 | 305 | int isOrigLink = file_wd_islink(zOPath); |
| 305 | 306 | db_ephemeral_blob(&q, 6, &delta); |
| 306 | 307 | if( isOrigLink ){ |
| @@ -308,17 +309,17 @@ | ||
| 308 | 309 | }else{ |
| 309 | 310 | blob_read_from_file(&disk, zOPath); |
| 310 | 311 | } |
| 311 | 312 | fossil_print("CHANGED %s\n", zNew); |
| 312 | 313 | 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); | |
| 315 | 316 | printf("cannot compute difference between symlink and regular file\n"); |
| 316 | 317 | }else{ |
| 317 | 318 | content_get(rid, &a); |
| 318 | 319 | blob_delta_apply(&a, &delta, &b); |
| 319 | - diff_file_mem(&disk, &b, zNew, zDiffCmd, 0); | |
| 320 | + diff_file_mem(&disk, &b, zNew, zDiffCmd, diffFlags); | |
| 320 | 321 | blob_reset(&a); |
| 321 | 322 | blob_reset(&b); |
| 322 | 323 | } |
| 323 | 324 | blob_reset(&disk); |
| 324 | 325 | } |
| @@ -363,35 +364,32 @@ | ||
| 363 | 364 | ** |
| 364 | 365 | ** Usage: %fossil stash SUBCOMMAND ARGS... |
| 365 | 366 | ** |
| 366 | 367 | ** fossil stash |
| 367 | 368 | ** fossil stash save ?-m COMMENT? ?FILES...? |
| 369 | +** fossil stash snapshot ?-m COMMENT? ?FILES...? | |
| 368 | 370 | ** |
| 369 | 371 | ** Save the current changes in the working tree as a new stash. |
| 370 | 372 | ** Then revert the changes back to the last check-in. If FILES |
| 371 | 373 | ** are listed, then only stash and revert the named files. The |
| 372 | 374 | ** "save" verb can be omitted if and only if there are no other |
| 373 | -** arguments. | |
| 375 | +** arguments. The "snapshot" verb works the same as "save" but | |
| 376 | +** omits the revert, keeping the check-out unchanged. | |
| 374 | 377 | ** |
| 375 | -** fossil stash list | |
| 376 | -** fossil stash ls | |
| 378 | +** fossil stash list ?--detail? | |
| 379 | +** fossil stash ls ?-l? | |
| 377 | 380 | ** |
| 378 | -** List all changes sets currently stashed. | |
| 381 | +** List all changes sets currently stashed. Show information about | |
| 382 | +** individual files in each changeset if --detail or -l is used. | |
| 379 | 383 | ** |
| 380 | 384 | ** fossil stash pop |
| 381 | -** | |
| 382 | -** Apply the most recently create stash to the current working | |
| 383 | -** check-out. Then delete that stash. This is equivalent to | |
| 384 | -** doing an "apply" and a "drop" against the most recent stash. | |
| 385 | -** This command is undoable. | |
| 386 | -** | |
| 387 | 385 | ** fossil stash apply ?STASHID? |
| 388 | 386 | ** |
| 389 | -** Apply the identified stash to the current working check-out. | |
| 390 | -** If no STASHID is specified, use the most recent stash. Unlike | |
| 391 | -** the "pop" command, the stash is retained so that it can be used | |
| 392 | -** again. This command is undoable. | |
| 387 | +** Apply STASHID or the most recently create stash to the current | |
| 388 | +** working check-out. The "pop" command deletes that changeset from | |
| 389 | +** the stash after applying it but the "apply" command retains the | |
| 390 | +** changeset. | |
| 393 | 391 | ** |
| 394 | 392 | ** fossil stash goto ?STASHID? |
| 395 | 393 | ** |
| 396 | 394 | ** Update to the baseline checkout for STASHID then apply the |
| 397 | 395 | ** changes of STASHID. Keep STASHID so that it can be reused |
| @@ -401,15 +399,10 @@ | ||
| 401 | 399 | ** fossil stash rm ?STASHID? ?--all? |
| 402 | 400 | ** |
| 403 | 401 | ** Forget everything about STASHID. Forget the whole stash if the |
| 404 | 402 | ** --all flag is used. Individual drops are undoable but --all is not. |
| 405 | 403 | ** |
| 406 | -** fossil stash snapshot ?-m COMMENT? ?FILES...? | |
| 407 | -** | |
| 408 | -** Save the current changes in the working tree as a new stash | |
| 409 | -** but, unlike "save", do not revert those changes. | |
| 410 | -** | |
| 411 | 404 | ** fossil stash diff ?STASHID? |
| 412 | 405 | ** fossil stash gdiff ?STASHID? |
| 413 | 406 | ** |
| 414 | 407 | ** Show diffs of the current working directory and what that |
| 415 | 408 | ** directory would be if STASHID were applied. |
| @@ -455,40 +448,71 @@ | ||
| 455 | 448 | }else |
| 456 | 449 | if( memcmp(zCmd, "snapshot", nCmd)==0 ){ |
| 457 | 450 | stash_create(); |
| 458 | 451 | }else |
| 459 | 452 | if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){ |
| 460 | - Stmt q; | |
| 453 | + Stmt q, q2; | |
| 461 | 454 | int n = 0; |
| 455 | + int fDetail = find_option("detail","l",0)!=0; | |
| 462 | 456 | verify_all_options(); |
| 463 | 457 | db_prepare(&q, |
| 464 | 458 | "SELECT stashid, (SELECT uuid FROM blob WHERE rid=vid)," |
| 465 | 459 | " comment, datetime(ctime) FROM stash" |
| 466 | 460 | " ORDER BY ctime DESC" |
| 467 | 461 | ); |
| 462 | + if( fDetail ){ | |
| 463 | + db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname" | |
| 464 | + " FROM stashfile WHERE stashid=$id"); | |
| 465 | + } | |
| 468 | 466 | while( db_step(&q)==SQLITE_ROW ){ |
| 467 | + int stashid = db_column_int(&q, 0); | |
| 469 | 468 | const char *zCom; |
| 470 | 469 | n++; |
| 471 | 470 | fossil_print("%5d: [%.14s] on %s\n", |
| 472 | - db_column_int(&q, 0), | |
| 471 | + stashid, | |
| 473 | 472 | db_column_text(&q, 1), |
| 474 | 473 | db_column_text(&q, 3) |
| 475 | 474 | ); |
| 476 | 475 | zCom = db_column_text(&q, 2); |
| 477 | 476 | if( zCom && zCom[0] ){ |
| 478 | 477 | fossil_print(" "); |
| 479 | 478 | comment_print(zCom, 7, 79); |
| 480 | 479 | } |
| 480 | + if( fDetail ){ | |
| 481 | + db_bind_int(&q2, "$id", stashid); | |
| 482 | + while( db_step(&q2)==SQLITE_ROW ){ | |
| 483 | + int isAdded = db_column_int(&q2, 0); | |
| 484 | + int isRemoved = db_column_int(&q2, 1); | |
| 485 | + const char *zOrig = db_column_text(&q2, 2); | |
| 486 | + const char *zNew = db_column_text(&q2, 3); | |
| 487 | + if( isAdded ){ | |
| 488 | + fossil_print(" ADD %s\n", zNew); | |
| 489 | + }else if( isRemoved ){ | |
| 490 | + fossil_print(" REMOVE %s\n", zOrig); | |
| 491 | + }else if( fossil_strcmp(zOrig,zNew)!=0 ){ | |
| 492 | + fossil_print(" RENAME %s -> %s\n", zOrig, zNew); | |
| 493 | + }else{ | |
| 494 | + fossil_print(" EDIT %s\n", zOrig); | |
| 495 | + } | |
| 496 | + } | |
| 497 | + db_reset(&q2); | |
| 498 | + } | |
| 481 | 499 | } |
| 482 | 500 | db_finalize(&q); |
| 501 | + if( fDetail ) db_finalize(&q2); | |
| 483 | 502 | if( n==0 ) fossil_print("empty stash\n"); |
| 484 | 503 | }else |
| 485 | 504 | if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){ |
| 486 | 505 | int allFlag = find_option("all", 0, 0)!=0; |
| 487 | - if( g.argc>4 ) usage("apply STASHID"); | |
| 506 | + if( g.argc>4 ) usage("drop STASHID"); | |
| 488 | 507 | if( allFlag ){ |
| 489 | - db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;"); | |
| 508 | + Blob ans; | |
| 509 | + blob_zero(&ans); | |
| 510 | + prompt_user("This action is not undoable. Continue (y/N)? ", &ans); | |
| 511 | + if( blob_str(&ans)[0]=='y' ){ | |
| 512 | + db_multi_exec("DELETE FROM stash; DELETE FROM stashfile;"); | |
| 513 | + } | |
| 490 | 514 | }else{ |
| 491 | 515 | stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); |
| 492 | 516 | undo_begin(); |
| 493 | 517 | undo_save_stash(stashid); |
| 494 | 518 | stash_drop(stashid); |
| @@ -526,20 +550,28 @@ | ||
| 526 | 550 | stashid); |
| 527 | 551 | undo_finish(); |
| 528 | 552 | }else |
| 529 | 553 | if( memcmp(zCmd, "diff", nCmd)==0 ){ |
| 530 | 554 | const char *zDiffCmd = db_get("diff-command", 0); |
| 555 | + int diffFlags = diff_options(); | |
| 531 | 556 | if( g.argc>4 ) usage("diff STASHID"); |
| 532 | 557 | stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0); |
| 533 | - stash_diff(stashid, zDiffCmd); | |
| 558 | + stash_diff(stashid, zDiffCmd, diffFlags); | |
| 534 | 559 | }else |
| 535 | 560 | if( memcmp(zCmd, "gdiff", nCmd)==0 ){ |
| 536 | 561 | const char *zDiffCmd = db_get("gdiff-command", 0); |
| 562 | + int diffFlags = diff_options(); | |
| 537 | 563 | if( g.argc>4 ) usage("diff STASHID"); |
| 538 | 564 | 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(); | |
| 540 | 572 | }else |
| 541 | 573 | { |
| 542 | 574 | usage("SUBCOMMAND ARGS..."); |
| 543 | 575 | } |
| 544 | 576 | db_end_transaction(0); |
| 545 | 577 | } |
| 546 | 578 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -170,11 +170,11 @@ | |
| 170 | int i; |
| 171 | for(i=3; i<g.argc; i++){ |
| 172 | stash_add_file_or_dir(stashid, vid, g.argv[i]); |
| 173 | } |
| 174 | }else{ |
| 175 | stash_add_file_or_dir(stashid, vid, "."); |
| 176 | } |
| 177 | return stashid; |
| 178 | } |
| 179 | |
| 180 | /* |
| @@ -258,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 @@ | ||
| 210 | 210 | @ } else { |
| 211 | 211 | @ puts "Not logged in" |
| 212 | 212 | @ } |
| 213 | 213 | @ </th1></div> |
| 214 | 214 | @ </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" | |
| 217 | 218 | @ if {[anycap jor]} { |
| 218 | -@ html "<a href='$home/timeline'>Timeline</a> " | |
| 219 | +@ html "<a href='$home/timeline'>Timeline</a>\n" | |
| 219 | 220 | @ } |
| 220 | 221 | @ if {[hascap oh]} { |
| 221 | -@ html "<a href='$home/dir?ci=tip'>Files</a> " | |
| 222 | +@ html "<a href='$home/dir?ci=tip'>Files</a>\n" | |
| 222 | 223 | @ } |
| 223 | 224 | @ 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" | |
| 226 | 227 | @ } |
| 227 | 228 | @ if {[hascap r]} { |
| 228 | -@ html "<a href='$home/reportlist'>Tickets</a> " | |
| 229 | +@ html "<a href='$home/reportlist'>Tickets</a>\n" | |
| 229 | 230 | @ } |
| 230 | 231 | @ if {[hascap j]} { |
| 231 | -@ html "<a href='$home/wiki'>Wiki</a> " | |
| 232 | +@ html "<a href='$home/wiki'>Wiki</a>\n" | |
| 232 | 233 | @ } |
| 233 | 234 | @ if {[hascap s]} { |
| 234 | -@ html "<a href='$home/setup'>Admin</a> " | |
| 235 | +@ html "<a href='$home/setup'>Admin</a>\n" | |
| 235 | 236 | @ } elseif {[hascap a]} { |
| 236 | -@ html "<a href='$home/setup_ulist'>Users</a> " | |
| 237 | +@ html "<a href='$home/setup_ulist'>Users</a>\n" | |
| 237 | 238 | @ } |
| 238 | 239 | @ if {[info exists login]} { |
| 239 | -@ html "<a href='$home/login'>Logout</a> " | |
| 240 | +@ html "<a href='$home/login'>Logout</a>\n" | |
| 240 | 241 | @ } else { |
| 241 | -@ html "<a href='$home/login'>Login</a> " | |
| 242 | +@ html "<a href='$home/login'>Login</a>\n" | |
| 242 | 243 | @ } |
| 243 | 244 | @ </th1></div> |
| 244 | 245 | ; |
| 245 | 246 | |
| 246 | 247 | /* |
| @@ -320,23 +321,24 @@ | ||
| 320 | 321 | @ background-color: #558195; |
| 321 | 322 | @ color: white; |
| 322 | 323 | @ } |
| 323 | 324 | @ |
| 324 | 325 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 325 | -@ div.submenu { | |
| 326 | +@ div.submenu, div.sectionmenu { | |
| 326 | 327 | @ padding: 3px 10px 3px 0px; |
| 327 | 328 | @ font-size: 0.9em; |
| 328 | 329 | @ text-align: center; |
| 329 | 330 | @ background-color: #456878; |
| 330 | 331 | @ color: white; |
| 331 | 332 | @ } |
| 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 { | |
| 333 | 335 | @ padding: 3px 10px 3px 10px; |
| 334 | 336 | @ color: white; |
| 335 | 337 | @ text-decoration: none; |
| 336 | 338 | @ } |
| 337 | -@ div.mainmenu a:hover, div.submenu a:hover { | |
| 339 | +@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 338 | 340 | @ color: #558195; |
| 339 | 341 | @ background-color: white; |
| 340 | 342 | @ } |
| 341 | 343 | @ |
| 342 | 344 | @ /* All page content from the bottom of the menu or submenu down to |
| @@ -396,10 +398,69 @@ | ||
| 396 | 398 | @ table.label-value th { |
| 397 | 399 | @ vertical-align: top; |
| 398 | 400 | @ text-align: right; |
| 399 | 401 | @ padding: 0.2ex 2ex; |
| 400 | 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 | +@ } | |
| 401 | 462 | @ |
| 402 | 463 | ; |
| 403 | 464 | |
| 404 | 465 | |
| 405 | 466 | /* The following table contains bits of default CSS that must |
| @@ -470,11 +531,11 @@ | ||
| 470 | 531 | @ vertical-align: top; |
| 471 | 532 | @ text-align: right; |
| 472 | 533 | }, |
| 473 | 534 | { "td.timelineGraph", |
| 474 | 535 | "the format for the grap placeholder cells in timelines", |
| 475 | - @ width: 20; | |
| 536 | + @ width: 20px; | |
| 476 | 537 | @ text-align: left; |
| 477 | 538 | @ vertical-align: top; |
| 478 | 539 | }, |
| 479 | 540 | { "a.tagLink", |
| 480 | 541 | "the format for the tag links", |
| @@ -564,11 +625,11 @@ | ||
| 564 | 625 | @ vertical-align: top |
| 565 | 626 | }, |
| 566 | 627 | { "table.usetupUserList", |
| 567 | 628 | "format for the user list table on the user setup page", |
| 568 | 629 | @ outline-style: double; |
| 569 | - @ outline-width: 1; | |
| 630 | + @ outline-width: 1px; | |
| 570 | 631 | @ padding: 10px; |
| 571 | 632 | }, |
| 572 | 633 | { "th.usetupListUser", |
| 573 | 634 | "format for table header user in user list on user setup page", |
| 574 | 635 | @ text-align: right; |
| @@ -688,17 +749,17 @@ | ||
| 688 | 749 | @ border-color: #000000; |
| 689 | 750 | @ border-style: solid; |
| 690 | 751 | }, |
| 691 | 752 | { "input.checkinUserColor", |
| 692 | 753 | "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. */ | |
| 700 | 761 | }, |
| 701 | 762 | { "div.endContent", |
| 702 | 763 | "format for end of content area, to be used to clear page flow(sidebox on branch,..", |
| 703 | 764 | @ clear: both; |
| 704 | 765 | }, |
| @@ -718,11 +779,11 @@ | ||
| 718 | 779 | }, |
| 719 | 780 | { "span.thTrace", |
| 720 | 781 | "format for th script trace messages", |
| 721 | 782 | @ color: red; |
| 722 | 783 | }, |
| 723 | - { "p:reportError", | |
| 784 | + { "p.reportError", | |
| 724 | 785 | "format for report configuration errors", |
| 725 | 786 | @ color: red; |
| 726 | 787 | @ font-weight: bold; |
| 727 | 788 | }, |
| 728 | 789 | { "blockquote.reportError", |
| 729 | 790 |
| --- 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 @@ | ||
| 523 | 523 | blob_reset(&filename); |
| 524 | 524 | tar_finish(pTar); |
| 525 | 525 | } |
| 526 | 526 | |
| 527 | 527 | /* |
| 528 | -** COMMAND: tarball | |
| 528 | +** COMMAND: tarball* | |
| 529 | 529 | ** |
| 530 | 530 | ** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 531 | 531 | ** |
| 532 | 532 | ** Generate a compressed tarball for a specified version. If the --name |
| 533 | 533 | ** option is used, its argument becomes the name of the top-level directory |
| 534 | 534 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -523,11 +523,11 @@ | |
| 523 | blob_reset(&filename); |
| 524 | tar_finish(pTar); |
| 525 | } |
| 526 | |
| 527 | /* |
| 528 | ** COMMAND: tarball |
| 529 | ** |
| 530 | ** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 531 | ** |
| 532 | ** Generate a compressed tarball for a specified version. If the --name |
| 533 | ** option is used, its argument becomes the name of the top-level directory |
| 534 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -523,11 +523,11 @@ | |
| 523 | blob_reset(&filename); |
| 524 | tar_finish(pTar); |
| 525 | } |
| 526 | |
| 527 | /* |
| 528 | ** COMMAND: tarball* |
| 529 | ** |
| 530 | ** Usage: %fossil tarball VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 531 | ** |
| 532 | ** Generate a compressed tarball for a specified version. If the --name |
| 533 | ** option is used, its argument becomes the name of the top-level directory |
| 534 |
M
src/th.h
+1
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -154,10 +154,11 @@ | ||
| 154 | 154 | */ |
| 155 | 155 | int th_register_language(Th_Interp *interp); /* th_lang.c */ |
| 156 | 156 | int th_register_sqlite(Th_Interp *interp); /* th_sqlite.c */ |
| 157 | 157 | int th_register_vfs(Th_Interp *interp); /* th_vfs.c */ |
| 158 | 158 | int th_register_testvfs(Th_Interp *interp); /* th_testvfs.c */ |
| 159 | +int th_register_tcl(Th_Interp *interp, void *pContext); /* th_tcl.c */ | |
| 159 | 160 | |
| 160 | 161 | /* |
| 161 | 162 | ** General purpose hash table from th_lang.c. |
| 162 | 163 | */ |
| 163 | 164 | typedef struct Th_Hash Th_Hash; |
| 164 | 165 |
| --- 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 @@ | ||
| 1054 | 1054 | {"return", return_command, 0}, |
| 1055 | 1055 | {"break", simple_command, (void *)TH_BREAK}, |
| 1056 | 1056 | {"continue", simple_command, (void *)TH_CONTINUE}, |
| 1057 | 1057 | {"error", simple_command, (void *)TH_ERROR}, |
| 1058 | 1058 | |
| 1059 | - {0, 0} | |
| 1059 | + {0, 0, 0} | |
| 1060 | 1060 | }; |
| 1061 | 1061 | int i; |
| 1062 | 1062 | |
| 1063 | 1063 | /* Add the language commands. */ |
| 1064 | 1064 | 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; | |
| 1066 | 1068 | Th_CreateCommand(interp, aCommand[i].zName, aCommand[i].xProc, ctx, 0); |
| 1067 | 1069 | } |
| 1068 | 1070 | |
| 1069 | 1071 | return TH_OK; |
| 1070 | 1072 | } |
| 1071 | 1073 |
| --- 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 @@ | ||
| 93 | 93 | } |
| 94 | 94 | if( g.cgiOutput ){ |
| 95 | 95 | cgi_append_content(z, n); |
| 96 | 96 | }else{ |
| 97 | 97 | fwrite(z, 1, n, stdout); |
| 98 | + fflush(stdout); | |
| 98 | 99 | } |
| 99 | 100 | if( encode ) free((char*)z); |
| 100 | 101 | } |
| 101 | 102 | } |
| 102 | 103 | |
| @@ -334,10 +335,39 @@ | ||
| 334 | 335 | if( n<iMin ) n = iMin; |
| 335 | 336 | if( n>iMax ) n = iMax; |
| 336 | 337 | Th_SetResultInt(interp, n); |
| 337 | 338 | return TH_OK; |
| 338 | 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 | +} | |
| 339 | 369 | |
| 340 | 370 | /* |
| 341 | 371 | ** Make sure the interpreter has been initialized. Initialize it if |
| 342 | 372 | ** it has not been already. |
| 343 | 373 | ** |
| @@ -357,16 +387,24 @@ | ||
| 357 | 387 | {"htmlize", htmlizeCmd, 0}, |
| 358 | 388 | {"date", dateCmd, 0}, |
| 359 | 389 | {"html", putsCmd, 0}, |
| 360 | 390 | {"puts", putsCmd, (void*)1}, |
| 361 | 391 | {"wiki", wikiCmd, 0}, |
| 392 | + {"repository", repositoryCmd, 0}, | |
| 393 | + {0, 0, 0} | |
| 362 | 394 | }; |
| 363 | 395 | if( g.interp==0 ){ |
| 364 | 396 | int i; |
| 365 | 397 | g.interp = Th_CreateInterp(&vtab); |
| 366 | 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 | |
| 367 | 404 | for(i=0; i<sizeof(aCommand)/sizeof(aCommand[0]); i++){ |
| 405 | + if ( !aCommand[i].zName || !aCommand[i].xProc ) continue; | |
| 368 | 406 | Th_CreateCommand(g.interp, aCommand[i].zName, aCommand[i].xProc, |
| 369 | 407 | aCommand[i].pContext, 0); |
| 370 | 408 | } |
| 371 | 409 | } |
| 372 | 410 | } |
| @@ -482,25 +520,27 @@ | ||
| 482 | 520 | Th_FossilInit(); |
| 483 | 521 | while( z[i] ){ |
| 484 | 522 | if( z[i]=='$' && (n = validVarName(&z[i+1]))>0 ){ |
| 485 | 523 | const char *zVar; |
| 486 | 524 | int nVar; |
| 525 | + int encode = 1; | |
| 487 | 526 | sendText(z, i, 0); |
| 488 | 527 | if( z[i+1]=='<' ){ |
| 489 | - /* Variables of the form $<aaa> */ | |
| 528 | + /* Variables of the form $<aaa> are html escaped */ | |
| 490 | 529 | zVar = &z[i+2]; |
| 491 | 530 | nVar = n-2; |
| 492 | 531 | }else{ |
| 493 | - /* Variables of the form $aaa */ | |
| 532 | + /* Variables of the form $aaa are output raw */ | |
| 494 | 533 | zVar = &z[i+1]; |
| 495 | 534 | nVar = n; |
| 535 | + encode = 0; | |
| 496 | 536 | } |
| 497 | 537 | rc = Th_GetVar(g.interp, (char*)zVar, nVar); |
| 498 | 538 | z += i+1+n; |
| 499 | 539 | i = 0; |
| 500 | 540 | zResult = (char*)Th_GetResult(g.interp, &n); |
| 501 | - sendText((char*)zResult, n, n>nVar); | |
| 541 | + sendText((char*)zResult, n, encode); | |
| 502 | 542 | }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ |
| 503 | 543 | sendText(z, i, 0); |
| 504 | 544 | z += i+5; |
| 505 | 545 | for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} |
| 506 | 546 | rc = Th_Eval(g.interp, 0, (const char*)z, i); |
| @@ -529,9 +569,10 @@ | ||
| 529 | 569 | void test_th_render(void){ |
| 530 | 570 | Blob in; |
| 531 | 571 | if( g.argc<3 ){ |
| 532 | 572 | usage("FILE"); |
| 533 | 573 | } |
| 574 | + db_open_config(0); /* Needed for "tcl" setting. */ | |
| 534 | 575 | blob_zero(&in); |
| 535 | 576 | blob_read_from_file(&in, g.argv[2]); |
| 536 | 577 | Th_Render(blob_str(&in)); |
| 537 | 578 | } |
| 538 | 579 | |
| 539 | 580 | 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 |
+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} |
| --- 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 @@ | ||
| 20 | 20 | */ |
| 21 | 21 | #include <string.h> |
| 22 | 22 | #include <time.h> |
| 23 | 23 | #include "config.h" |
| 24 | 24 | #include "timeline.h" |
| 25 | +#ifdef FOSSIL_ENABLE_JSON | |
| 26 | +# include "cson_amalgamation.h" | |
| 27 | +#endif | |
| 25 | 28 | |
| 26 | 29 | /* |
| 27 | 30 | ** Shorten a UUID so that is the minimum length needed to contain |
| 28 | 31 | ** at least one digit in the range 'a'..'f'. The minimum length is 10. |
| 29 | 32 | */ |
| @@ -179,11 +182,11 @@ | ||
| 179 | 182 | ** 2. Date/Time |
| 180 | 183 | ** 3. Comment string |
| 181 | 184 | ** 4. User |
| 182 | 185 | ** 5. True if is a leaf |
| 183 | 186 | ** 6. background color |
| 184 | -** 7. type ("ci", "w", "t", "e", "div") | |
| 187 | +** 7. type ("ci", "w", "t", "e", "g", "div") | |
| 185 | 188 | ** 8. list of symbolic tags. |
| 186 | 189 | ** 9. tagid for ticket or wiki or event |
| 187 | 190 | ** 10. Short comment to user for repeated tickets and wiki |
| 188 | 191 | */ |
| 189 | 192 | void www_print_timeline( |
| @@ -316,10 +319,13 @@ | ||
| 316 | 319 | @</td> |
| 317 | 320 | if( zBgClr && zBgClr[0] ){ |
| 318 | 321 | @ <td class="timelineTableCell" style="background-color: %h(zBgClr);"> |
| 319 | 322 | }else{ |
| 320 | 323 | @ <td class="timelineTableCell"> |
| 324 | + } | |
| 325 | + if( pGraph && zType[0]!='c' ){ | |
| 326 | + @ • | |
| 321 | 327 | } |
| 322 | 328 | if( zType[0]=='c' ){ |
| 323 | 329 | hyperlink_to_uuid(zUuid); |
| 324 | 330 | if( isLeaf ){ |
| 325 | 331 | if( db_exists("SELECT 1 FROM tagxref" |
| @@ -765,24 +771,24 @@ | ||
| 765 | 771 | */ |
| 766 | 772 | const char *timeline_query_for_www(void){ |
| 767 | 773 | static char *zBase = 0; |
| 768 | 774 | static const char zBaseSql[] = |
| 769 | 775 | @ SELECT |
| 770 | - @ blob.rid, | |
| 771 | - @ uuid, | |
| 776 | + @ blob.rid AS blobRid, | |
| 777 | + @ uuid AS uuid, | |
| 772 | 778 | @ 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, | |
| 778 | 784 | @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref |
| 779 | 785 | @ 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 | |
| 784 | 790 | @ FROM event JOIN blob |
| 785 | 791 | @ WHERE blob.rid=event.objid |
| 786 | 792 | ; |
| 787 | 793 | if( zBase==0 ){ |
| 788 | 794 | zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH); |
| @@ -840,23 +846,24 @@ | ||
| 840 | 846 | ** |
| 841 | 847 | ** a=TIMESTAMP after this date |
| 842 | 848 | ** b=TIMESTAMP before this date. |
| 843 | 849 | ** c=TIMESTAMP "circa" this date. |
| 844 | 850 | ** 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 | |
| 847 | 854 | ** t=TAGID show only check-ins with the given tagid |
| 848 | 855 | ** r=TAGID show check-ins related to tagid |
| 849 | 856 | ** u=USER only if belonging to this user |
| 850 | 857 | ** y=TYPE 'ci', 'w', 't', 'e' |
| 851 | 858 | ** s=TEXT string search (comment and brief) |
| 852 | 859 | ** ng Suppress the graph if present |
| 853 | 860 | ** nd Suppress "divider" lines |
| 854 | 861 | ** 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 | |
| 858 | 865 | ** nomerge ... avoid merge links on the path |
| 859 | 866 | ** brbg Background color from branch name |
| 860 | 867 | ** ubg Background color from user |
| 861 | 868 | ** |
| 862 | 869 | ** p= and d= can appear individually or together. If either p= or d= |
| @@ -892,15 +899,23 @@ | ||
| 892 | 899 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| 893 | 900 | int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */ |
| 894 | 901 | int noMerge = P("nomerge")!=0; /* Do not follow merge links */ |
| 895 | 902 | int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ |
| 896 | 903 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 904 | + int pd_rid; | |
| 897 | 905 | |
| 898 | 906 | /* To view the timeline, must have permission to read project data. |
| 899 | 907 | */ |
| 908 | + pd_rid = name_to_typed_rid(P("dp"),"ci"); | |
| 909 | + if( pd_rid ){ | |
| 910 | + p_rid = d_rid = pd_rid; | |
| 911 | + } | |
| 900 | 912 | 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 | + } | |
| 902 | 917 | if( zTagName && g.perm.Read ){ |
| 903 | 918 | tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTagName); |
| 904 | 919 | zThisTag = zTagName; |
| 905 | 920 | }else if( zBrName && g.perm.Read ){ |
| 906 | 921 | tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",zBrName); |
| @@ -988,14 +1003,12 @@ | ||
| 988 | 1003 | blob_appendf(&sql, " AND event.objid IN ok"); |
| 989 | 1004 | nd = 0; |
| 990 | 1005 | if( d_rid ){ |
| 991 | 1006 | compute_descendants(d_rid, nEntry+1); |
| 992 | 1007 | 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"); | |
| 997 | 1010 | if( useDividers ) timeline_add_dividers(0, d_rid); |
| 998 | 1011 | db_multi_exec("DELETE FROM ok"); |
| 999 | 1012 | } |
| 1000 | 1013 | if( p_rid ){ |
| 1001 | 1014 | compute_ancestors(p_rid, nEntry+1); |
| @@ -1075,19 +1088,20 @@ | ||
| 1075 | 1088 | } |
| 1076 | 1089 | if( (zType[0]=='w' && !g.perm.RdWiki) |
| 1077 | 1090 | || (zType[0]=='t' && !g.perm.RdTkt) |
| 1078 | 1091 | || (zType[0]=='e' && !g.perm.RdWiki) |
| 1079 | 1092 | || (zType[0]=='c' && !g.perm.Read) |
| 1093 | + || (zType[0]=='g' && !g.perm.Read) | |
| 1080 | 1094 | ){ |
| 1081 | 1095 | zType = "all"; |
| 1082 | 1096 | } |
| 1083 | 1097 | if( zType[0]=='a' ){ |
| 1084 | 1098 | if( !g.perm.Read || !g.perm.RdWiki || !g.perm.RdTkt ){ |
| 1085 | 1099 | char cSep = '('; |
| 1086 | 1100 | blob_appendf(&sql, " AND event.type IN "); |
| 1087 | 1101 | if( g.perm.Read ){ |
| 1088 | - blob_appendf(&sql, "%c'ci'", cSep); | |
| 1102 | + blob_appendf(&sql, "%c'ci','g'", cSep); | |
| 1089 | 1103 | cSep = ','; |
| 1090 | 1104 | } |
| 1091 | 1105 | if( g.perm.RdWiki ){ |
| 1092 | 1106 | blob_appendf(&sql, "%c'w','e'", cSep); |
| 1093 | 1107 | cSep = ','; |
| @@ -1107,10 +1121,12 @@ | ||
| 1107 | 1121 | zEType = "wiki edit"; |
| 1108 | 1122 | }else if( zType[0]=='t' ){ |
| 1109 | 1123 | zEType = "ticket change"; |
| 1110 | 1124 | }else if( zType[0]=='e' ){ |
| 1111 | 1125 | zEType = "event"; |
| 1126 | + }else if( zType[0]=='g' ){ | |
| 1127 | + zEType = "tag"; | |
| 1112 | 1128 | } |
| 1113 | 1129 | } |
| 1114 | 1130 | if( zUser ){ |
| 1115 | 1131 | blob_appendf(&sql, " AND (event.user=%Q OR event.euser=%Q)", |
| 1116 | 1132 | zUser, zUser); |
| @@ -1226,10 +1242,13 @@ | ||
| 1226 | 1242 | timeline_submenu(&url, "Tickets Only", "y", "t", 0); |
| 1227 | 1243 | } |
| 1228 | 1244 | if( zType[0]!='e' && g.perm.RdWiki ){ |
| 1229 | 1245 | timeline_submenu(&url, "Events Only", "y", "e", 0); |
| 1230 | 1246 | } |
| 1247 | + if( zType[0]!='g' && g.perm.Read ){ | |
| 1248 | + timeline_submenu(&url, "Tags Only", "y", "g", 0); | |
| 1249 | + } | |
| 1231 | 1250 | } |
| 1232 | 1251 | if( nEntry>20 ){ |
| 1233 | 1252 | timeline_submenu(&url, "20 Entries", "n", "20", 0); |
| 1234 | 1253 | } |
| 1235 | 1254 | if( nEntry<200 ){ |
| @@ -1365,24 +1384,24 @@ | ||
| 1365 | 1384 | ** a timeline query for display on a TTY. |
| 1366 | 1385 | */ |
| 1367 | 1386 | const char *timeline_query_for_tty(void){ |
| 1368 | 1387 | static const char zBaseSql[] = |
| 1369 | 1388 | @ SELECT |
| 1370 | - @ blob.rid, | |
| 1389 | + @ blob.rid AS rid, | |
| 1371 | 1390 | @ uuid, |
| 1372 | - @ datetime(event.mtime,'localtime'), | |
| 1391 | + @ datetime(event.mtime,'localtime') AS mDateTime, | |
| 1373 | 1392 | @ coalesce(ecomment,comment) |
| 1374 | 1393 | @ || ' (user: ' || coalesce(euser,user,'?') |
| 1375 | 1394 | @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end |
| 1376 | 1395 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 1377 | 1396 | @ FROM tag, tagxref |
| 1378 | 1397 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| 1379 | 1398 | @ 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 | |
| 1384 | 1403 | @ FROM event, blob |
| 1385 | 1404 | @ WHERE blob.rid=event.objid |
| 1386 | 1405 | ; |
| 1387 | 1406 | return zBaseSql; |
| 1388 | 1407 | } |
| @@ -1425,12 +1444,12 @@ | ||
| 1425 | 1444 | ** |
| 1426 | 1445 | ** w = wiki commits only |
| 1427 | 1446 | ** ci = file commits only |
| 1428 | 1447 | ** t = tickets only |
| 1429 | 1448 | ** |
| 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. | |
| 1432 | 1451 | ** |
| 1433 | 1452 | */ |
| 1434 | 1453 | void timeline_cmd(void){ |
| 1435 | 1454 | Stmt q; |
| 1436 | 1455 | int n, k; |
| @@ -1524,11 +1543,10 @@ | ||
| 1524 | 1543 | blob_appendf(&sql, " AND blob.rid IN ok"); |
| 1525 | 1544 | } |
| 1526 | 1545 | if( zType && (zType[0]!='a') ){ |
| 1527 | 1546 | blob_appendf(&sql, " AND event.type=%Q ", zType); |
| 1528 | 1547 | } |
| 1529 | - | |
| 1530 | 1548 | blob_appendf(&sql, " ORDER BY event.mtime DESC"); |
| 1531 | 1549 | db_prepare(&q, blob_str(&sql)); |
| 1532 | 1550 | blob_reset(&sql); |
| 1533 | 1551 | print_timeline(&q, n, showfilesFlag); |
| 1534 | 1552 | db_finalize(&q); |
| 1535 | 1553 |
| --- 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 | @ • |
| 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 @@ | ||
| 170 | 170 | */ |
| 171 | 171 | int ticket_insert(const Manifest *p, int createFlag, int rid){ |
| 172 | 172 | Blob sql; |
| 173 | 173 | Stmt q; |
| 174 | 174 | int i; |
| 175 | - const char *zSep; | |
| 176 | 175 | int rc = 0; |
| 177 | 176 | |
| 178 | 177 | getAllTicketFields(); |
| 179 | 178 | if( createFlag ){ |
| 180 | 179 | db_multi_exec("INSERT OR IGNORE INTO ticket(tkt_uuid, tkt_mtime) " |
| @@ -181,11 +180,10 @@ | ||
| 181 | 180 | "VALUES(%Q, 0)", p->zTicketUuid); |
| 182 | 181 | rc = db_changes(); |
| 183 | 182 | } |
| 184 | 183 | blob_zero(&sql); |
| 185 | 184 | blob_appendf(&sql, "UPDATE OR REPLACE ticket SET tkt_mtime=:mtime"); |
| 186 | - zSep = "SET"; | |
| 187 | 185 | for(i=0; i<p->nField; i++){ |
| 188 | 186 | const char *zName = p->aField[i].zName; |
| 189 | 187 | if( zName[0]=='+' ){ |
| 190 | 188 | zName++; |
| 191 | 189 | if( fieldId(zName)<0 ) continue; |
| @@ -839,11 +837,11 @@ | ||
| 839 | 837 | } |
| 840 | 838 | @ </ol> |
| 841 | 839 | } |
| 842 | 840 | |
| 843 | 841 | /* |
| 844 | -** COMMAND: ticket | |
| 842 | +** COMMAND: ticket* | |
| 845 | 843 | ** Usage: %fossil ticket SUBCOMMAND ... |
| 846 | 844 | ** |
| 847 | 845 | ** Run various subcommands to control tickets |
| 848 | 846 | ** |
| 849 | 847 | ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? |
| @@ -855,10 +853,11 @@ | ||
| 855 | 853 | ** |
| 856 | 854 | ** Run the ticket report, identified by the report format title |
| 857 | 855 | ** used in the gui. The data is written as flat file on stdout, |
| 858 | 856 | ** using "," as separator. The separator "," can be changed using |
| 859 | 857 | ** the -l or --limit option. |
| 858 | +** | |
| 860 | 859 | ** If TICKETFILTER is given on the commandline, the query is |
| 861 | 860 | ** limited with a new WHERE-condition. |
| 862 | 861 | ** example: Report lists a column # with the uuid |
| 863 | 862 | ** TICKETFILTER may be [#]='uuuuuuuuu' |
| 864 | 863 | ** example: Report only lists rows with status not open |
| @@ -865,11 +864,11 @@ | ||
| 865 | 864 | ** TICKETFILTER: status != 'open' |
| 866 | 865 | ** If the option -q|--quote is used, the tickets are encoded by |
| 867 | 866 | ** quoting special chars(space -> \\s, tab -> \\t, newline -> \\n, |
| 868 | 867 | ** cr -> \\r, formfeed -> \\f, vtab -> \\v, nul -> \\0, \\ -> \\\\). |
| 869 | 868 | ** 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. | |
| 871 | 870 | ** |
| 872 | 871 | ** Instead of the report title its possible to use the report |
| 873 | 872 | ** number. Using the special report number 0 list all columns, |
| 874 | 873 | ** defined in the ticket table. |
| 875 | 874 | ** |
| @@ -959,22 +958,19 @@ | ||
| 959 | 958 | usage("show REPORTNR"); |
| 960 | 959 | }else{ |
| 961 | 960 | const char *zRep = 0; |
| 962 | 961 | const char *zSep = 0; |
| 963 | 962 | const char *zFilterUuid = 0; |
| 964 | - | |
| 965 | 963 | zSep = find_option("limit","l",1); |
| 966 | 964 | zRep = g.argv[3]; |
| 967 | 965 | if( !strcmp(zRep,"0") ){ |
| 968 | 966 | zRep = 0; |
| 969 | 967 | } |
| 970 | 968 | if( g.argc>4 ){ |
| 971 | 969 | zFilterUuid = g.argv[4]; |
| 972 | 970 | } |
| 973 | - | |
| 974 | 971 | rptshow( zRep, zSep, zFilterUuid, tktEncoding ); |
| 975 | - | |
| 976 | 972 | } |
| 977 | 973 | }else{ |
| 978 | 974 | /* add a new ticket or update an existing ticket */ |
| 979 | 975 | enum { set,add,history,err } eCmd = err; |
| 980 | 976 | int i = 0; |
| 981 | 977 |
| --- 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 |
+10
| --- src/translate.c | ||
| +++ src/translate.c | ||
| @@ -160,15 +160,25 @@ | ||
| 160 | 160 | } |
| 161 | 161 | } |
| 162 | 162 | |
| 163 | 163 | int main(int argc, char **argv){ |
| 164 | 164 | if( argc==2 ){ |
| 165 | + char *arg; | |
| 165 | 166 | FILE *in = fopen(argv[1], "r"); |
| 166 | 167 | if( in==0 ){ |
| 167 | 168 | fprintf(stderr,"can not open %s\n", argv[1]); |
| 168 | 169 | exit(1); |
| 169 | 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"); | |
| 170 | 180 | trans(in, stdout); |
| 171 | 181 | fclose(in); |
| 172 | 182 | }else{ |
| 173 | 183 | trans(stdin, stdout); |
| 174 | 184 | } |
| 175 | 185 |
| --- 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 @@ | ||
| 348 | 348 | undo_all_filesystem(0); |
| 349 | 349 | } |
| 350 | 350 | |
| 351 | 351 | /* |
| 352 | 352 | ** COMMAND: undo |
| 353 | -** COMMAND: redo | |
| 353 | +** COMMAND: redo* | |
| 354 | 354 | ** |
| 355 | 355 | ** Usage: %fossil undo ?--explain? ?FILENAME...? |
| 356 | 356 | ** or: %fossil redo ?--explain? ?FILENAME...? |
| 357 | 357 | ** |
| 358 | 358 | ** Undo the changes to the working checkout caused by the most recent |
| 359 | 359 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -348,11 +348,11 @@ | |
| 348 | undo_all_filesystem(0); |
| 349 | } |
| 350 | |
| 351 | /* |
| 352 | ** COMMAND: undo |
| 353 | ** COMMAND: redo |
| 354 | ** |
| 355 | ** Usage: %fossil undo ?--explain? ?FILENAME...? |
| 356 | ** or: %fossil redo ?--explain? ?FILENAME...? |
| 357 | ** |
| 358 | ** Undo the changes to the working checkout caused by the most recent |
| 359 |
| --- src/undo.c | |
| +++ src/undo.c | |
| @@ -348,11 +348,11 @@ | |
| 348 | undo_all_filesystem(0); |
| 349 | } |
| 350 | |
| 351 | /* |
| 352 | ** COMMAND: undo |
| 353 | ** COMMAND: redo* |
| 354 | ** |
| 355 | ** Usage: %fossil undo ?--explain? ?FILENAME...? |
| 356 | ** or: %fossil redo ?--explain? ?FILENAME...? |
| 357 | ** |
| 358 | ** Undo the changes to the working checkout caused by the most recent |
| 359 |
+19
-8
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -96,10 +96,11 @@ | ||
| 96 | 96 | int debugFlag; /* --debug option */ |
| 97 | 97 | int nChng; /* Number of file renames */ |
| 98 | 98 | int *aChng; /* Array of file renames */ |
| 99 | 99 | int i; /* Loop counter */ |
| 100 | 100 | int nConflict = 0; /* Number of merge conflicts */ |
| 101 | + int nOverwrite = 0; /* Number of unmanaged files overwritten */ | |
| 101 | 102 | Stmt mtimeXfer; /* Statment to transfer mtimes */ |
| 102 | 103 | |
| 103 | 104 | if( !internalUpdate ){ |
| 104 | 105 | undo_capture_command_line(); |
| 105 | 106 | url_proxy_options(); |
| @@ -359,11 +360,16 @@ | ||
| 359 | 360 | */ |
| 360 | 361 | fossil_print("CONFLICT %s\n", zName); |
| 361 | 362 | nConflict++; |
| 362 | 363 | }else if( idt>0 && idv==0 ){ |
| 363 | 364 | /* 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 | + } | |
| 365 | 371 | undo_save(zName); |
| 366 | 372 | if( !nochangeFlag ) vfile_to_disk(0, idt, 0, 0); |
| 367 | 373 | }else if( idt>0 && idv>0 && ridt!=ridv && chnged==0 ){ |
| 368 | 374 | /* The file is unedited. Change it to the target version */ |
| 369 | 375 | undo_save(zName); |
| @@ -449,17 +455,22 @@ | ||
| 449 | 455 | fossil_print("--------------\n"); |
| 450 | 456 | show_common_info(tid, "updated-to:", 1, 0); |
| 451 | 457 | |
| 452 | 458 | /* Report on conflicts |
| 453 | 459 | */ |
| 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); | |
| 461 | 472 | } |
| 462 | 473 | } |
| 463 | 474 | |
| 464 | 475 | /* |
| 465 | 476 | ** Clean up the mid and pid VFILE entries. Then commit the changes. |
| 466 | 477 |
| --- 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 @@ | ||
| 142 | 142 | } |
| 143 | 143 | } |
| 144 | 144 | |
| 145 | 145 | |
| 146 | 146 | /* |
| 147 | -** COMMAND: user | |
| 147 | +** COMMAND: user* | |
| 148 | 148 | ** |
| 149 | 149 | ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE? |
| 150 | 150 | ** |
| 151 | 151 | ** Run various subcommands on users of the open repository or of |
| 152 | 152 | ** the repository identified by the -R or --repository option. |
| 153 | 153 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -142,11 +142,11 @@ | |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | |
| 146 | /* |
| 147 | ** COMMAND: user |
| 148 | ** |
| 149 | ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE? |
| 150 | ** |
| 151 | ** Run various subcommands on users of the open repository or of |
| 152 | ** the repository identified by the -R or --repository option. |
| 153 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -142,11 +142,11 @@ | |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | |
| 146 | /* |
| 147 | ** COMMAND: user* |
| 148 | ** |
| 149 | ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE? |
| 150 | ** |
| 151 | ** Run various subcommands on users of the open repository or of |
| 152 | ** the repository identified by the -R or --repository option. |
| 153 |
+26
-9
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -621,19 +621,36 @@ | ||
| 621 | 621 | blob_zero(&w2); |
| 622 | 622 | if( rid2 && (pW2 = manifest_get(rid2, CFTYPE_WIKI))!=0 ){ |
| 623 | 623 | blob_init(&w2, pW2->zWiki, -1); |
| 624 | 624 | } |
| 625 | 625 | blob_zero(&d); |
| 626 | - text_diff(&w2, &w1, &d, 5, 1); | |
| 626 | + text_diff(&w2, &w1, &d, 5 | DIFF_IGNORE_EOLWS); | |
| 627 | 627 | @ <pre> |
| 628 | 628 | @ %h(blob_str(&d)) |
| 629 | 629 | @ </pre> |
| 630 | 630 | manifest_destroy(pW1); |
| 631 | 631 | manifest_destroy(pW2); |
| 632 | 632 | style_footer(); |
| 633 | 633 | } |
| 634 | 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 | +} | |
| 635 | 652 | /* |
| 636 | 653 | ** WEBPAGE: wcontent |
| 637 | 654 | ** |
| 638 | 655 | ** all=1 Show deleted pages |
| 639 | 656 | ** |
| @@ -650,17 +667,11 @@ | ||
| 650 | 667 | style_submenu_element("Active", "Only Active Pages", "%s/wcontent", g.zTop); |
| 651 | 668 | }else{ |
| 652 | 669 | style_submenu_element("All", "All", "%s/wcontent?all=1", g.zTop); |
| 653 | 670 | } |
| 654 | 671 | @ <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); | |
| 662 | 673 | while( db_step(&q)==SQLITE_ROW ){ |
| 663 | 674 | const char *zName = db_column_text(&q, 0); |
| 664 | 675 | int size = db_column_int(&q, 1); |
| 665 | 676 | if( size>0 ){ |
| 666 | 677 | @ <li><a href="%s(g.zTop)/wiki?name=%T(zName)">%h(zName)</a></li> |
| @@ -790,13 +801,19 @@ | ||
| 790 | 801 | " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q'" |
| 791 | 802 | " ORDER BY x.mtime DESC LIMIT 1", |
| 792 | 803 | zPageName |
| 793 | 804 | ); |
| 794 | 805 | if( rid==0 && !isNew ){ |
| 806 | +#ifdef FOSSIL_ENABLE_JSON | |
| 807 | + g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; | |
| 808 | +#endif | |
| 795 | 809 | fossil_fatal("no such wiki page: %s", zPageName); |
| 796 | 810 | } |
| 797 | 811 | if( rid!=0 && isNew ){ |
| 812 | +#ifdef FOSSIL_ENABLE_JSON | |
| 813 | + g.json.resultCode = FSL_JSON_E_RESOURCE_ALREADY_EXISTS; | |
| 814 | +#endif | |
| 798 | 815 | fossil_fatal("wiki page %s already exists", zPageName); |
| 799 | 816 | } |
| 800 | 817 | |
| 801 | 818 | blob_zero(&wiki); |
| 802 | 819 | zDate = date_in_standard_format("now"); |
| @@ -826,11 +843,11 @@ | ||
| 826 | 843 | db_end_transaction(0); |
| 827 | 844 | return 1; |
| 828 | 845 | } |
| 829 | 846 | |
| 830 | 847 | /* |
| 831 | -** COMMAND: wiki | |
| 848 | +** COMMAND: wiki* | |
| 832 | 849 | ** |
| 833 | 850 | ** Usage: %fossil wiki (export|create|commit|list) WikiName |
| 834 | 851 | ** |
| 835 | 852 | ** Run various subcommands to work with wiki entries. |
| 836 | 853 | ** |
| 837 | 854 |
| --- 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 |
+1
-3
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -1708,20 +1708,18 @@ | ||
| 1708 | 1708 | /* Enter <verbatim> processing. With verbatim enabled, all other |
| 1709 | 1709 | ** markup other than the corresponding end-tag with the same ID is |
| 1710 | 1710 | ** ignored. |
| 1711 | 1711 | */ |
| 1712 | 1712 | if( markup.iCode==MARKUP_VERBATIM ){ |
| 1713 | - int vAttrIdx, vAttrDidAppend=0; | |
| 1713 | + int vAttrIdx; | |
| 1714 | 1714 | renderer.zVerbatimId = 0; |
| 1715 | 1715 | renderer.inVerbatim = 1; |
| 1716 | 1716 | renderer.preVerbState = renderer.state; |
| 1717 | 1717 | renderer.state &= ~ALLOW_WIKI; |
| 1718 | 1718 | for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){ |
| 1719 | 1719 | if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){ |
| 1720 | 1720 | renderer.zVerbatimId = markup.aAttr[0].zValue; |
| 1721 | - }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){ | |
| 1722 | - vAttrDidAppend=1; | |
| 1723 | 1721 | } |
| 1724 | 1722 | } |
| 1725 | 1723 | renderer.wantAutoParagraph = 0; |
| 1726 | 1724 | } |
| 1727 | 1725 | |
| 1728 | 1726 |
| --- 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 @@ | ||
| 432 | 432 | } |
| 433 | 433 | return 0; |
| 434 | 434 | } |
| 435 | 435 | |
| 436 | 436 | /* |
| 437 | -** COMMAND: winsrv | |
| 437 | +** COMMAND: winsrv* | |
| 438 | 438 | ** Usage: fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS? |
| 439 | 439 | ** |
| 440 | 440 | ** Where METHOD is one of: create delete show start stop. |
| 441 | 441 | ** |
| 442 | 442 | ** The winsrv command manages Fossil as a Windows service. This allows |
| @@ -848,15 +848,6 @@ | ||
| 848 | 848 | " create delete show start stop"); |
| 849 | 849 | } |
| 850 | 850 | return; |
| 851 | 851 | } |
| 852 | 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 | 853 | #endif /* _WIN32 -- This code is for win32 only */ |
| 863 | 854 |
| --- 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 @@ | ||
| 637 | 637 | Stmt q; |
| 638 | 638 | int nUncl; |
| 639 | 639 | int nRow = 0; |
| 640 | 640 | int rid; |
| 641 | 641 | |
| 642 | +#if 0 | |
| 642 | 643 | /* We should not ever get any private artifacts in the unclustered table. |
| 643 | 644 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 644 | 645 | db_multi_exec( |
| 645 | 646 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| 646 | 647 | ); |
| 648 | +#endif | |
| 647 | 649 | |
| 648 | 650 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 649 | 651 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 650 | 652 | " WHERE rid=unclustered.rid)"); |
| 651 | 653 | if( nUncl>=100 ){ |
| @@ -1198,20 +1200,19 @@ | ||
| 1198 | 1200 | ** |
| 1199 | 1201 | ** gdb fossil |
| 1200 | 1202 | ** r test-xfer out.txt |
| 1201 | 1203 | */ |
| 1202 | 1204 | void cmd_test_xfer(void){ |
| 1203 | - int notUsed; | |
| 1204 | 1205 | db_find_and_open_repository(0,0); |
| 1205 | 1206 | if( g.argc!=2 && g.argc!=3 ){ |
| 1206 | 1207 | usage("?MESSAGEFILE?"); |
| 1207 | 1208 | } |
| 1208 | 1209 | blob_zero(&g.cgiIn); |
| 1209 | 1210 | blob_read_from_file(&g.cgiIn, g.argc==2 ? "-" : g.argv[2]); |
| 1210 | 1211 | disableLogin = 1; |
| 1211 | 1212 | page_xfer(); |
| 1212 | - fossil_print("%s\n", cgi_extract_content(¬Used)); | |
| 1213 | + fossil_print("%s\n", cgi_extract_content()); | |
| 1213 | 1214 | } |
| 1214 | 1215 | |
| 1215 | 1216 | /* |
| 1216 | 1217 | ** Format strings for progress reporting. |
| 1217 | 1218 | */ |
| @@ -1238,11 +1239,10 @@ | ||
| 1238 | 1239 | int go = 1; /* Loop until zero */ |
| 1239 | 1240 | int nCardSent = 0; /* Number of cards sent */ |
| 1240 | 1241 | int nCardRcvd = 0; /* Number of cards received */ |
| 1241 | 1242 | int nCycle = 0; /* Number of round trips to the server */ |
| 1242 | 1243 | int size; /* Size of a config value */ |
| 1243 | - int nFileSend = 0; | |
| 1244 | 1244 | int origConfigRcvMask; /* Original value of configRcvMask */ |
| 1245 | 1245 | int nFileRecv; /* Number of files received */ |
| 1246 | 1246 | int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ |
| 1247 | 1247 | const char *zCookie; /* Server cookie */ |
| 1248 | 1248 | i64 nSent, nRcvd; /* Bytes sent and received (after compression) */ |
| @@ -1380,11 +1380,10 @@ | ||
| 1380 | 1380 | zRandomness = db_text(0, "SELECT hex(randomblob(20))"); |
| 1381 | 1381 | blob_appendf(&send, "# %s\n", zRandomness); |
| 1382 | 1382 | free(zRandomness); |
| 1383 | 1383 | |
| 1384 | 1384 | /* Exchange messages with the server */ |
| 1385 | - nFileSend = xfer.nFileSent + xfer.nDeltaSent; | |
| 1386 | 1385 | fossil_print(zValueFormat, "Sent:", |
| 1387 | 1386 | blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, |
| 1388 | 1387 | xfer.nFileSent, xfer.nDeltaSent); |
| 1389 | 1388 | nCardSent = 0; |
| 1390 | 1389 | nCardRcvd = 0; |
| 1391 | 1390 |
| --- 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(¬Used)); |
| 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 @@ | ||
| 377 | 377 | blob_reset(&filename); |
| 378 | 378 | zip_close(pZip); |
| 379 | 379 | } |
| 380 | 380 | |
| 381 | 381 | /* |
| 382 | -** COMMAND: zip | |
| 382 | +** COMMAND: zip* | |
| 383 | 383 | ** |
| 384 | 384 | ** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 385 | 385 | ** |
| 386 | 386 | ** Generate a ZIP archive for a specified version. If the --name option is |
| 387 | 387 | ** used, it argument becomes the name of the top-level directory in the |
| 388 | 388 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -377,11 +377,11 @@ | |
| 377 | blob_reset(&filename); |
| 378 | zip_close(pZip); |
| 379 | } |
| 380 | |
| 381 | /* |
| 382 | ** COMMAND: zip |
| 383 | ** |
| 384 | ** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 385 | ** |
| 386 | ** Generate a ZIP archive for a specified version. If the --name option is |
| 387 | ** used, it argument becomes the name of the top-level directory in the |
| 388 |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -377,11 +377,11 @@ | |
| 377 | blob_reset(&filename); |
| 378 | zip_close(pZip); |
| 379 | } |
| 380 | |
| 381 | /* |
| 382 | ** COMMAND: zip* |
| 383 | ** |
| 384 | ** Usage: %fossil zip VERSION OUTPUTFILE [--name DIRECTORYNAME] |
| 385 | ** |
| 386 | ** Generate a ZIP archive for a specified version. If the --name option is |
| 387 | ** used, it argument becomes the name of the top-level directory in the |
| 388 |
+5
-1
| --- test/merge5.test | ||
| +++ test/merge5.test | ||
| @@ -38,14 +38,18 @@ | ||
| 38 | 38 | } |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | 41 | catch {exec $::fossilexe info} res |
| 42 | 42 | puts res=$res |
| 43 | -if {![regexp {not within an open checkout} $res]} { | |
| 43 | +if {![regexp {use --repository} $res]} { | |
| 44 | 44 | puts stderr "Cannot run this test within an open checkout" |
| 45 | 45 | return |
| 46 | 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] | |
| 47 | 51 | |
| 48 | 52 | # Construct a test repository |
| 49 | 53 | # |
| 50 | 54 | exec sqlite3 m5.fossil <$testdir/${testfile}_repo.sql |
| 51 | 55 | fossil rebuild m5.fossil |
| 52 | 56 |
| --- 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 @@ | ||
| 7 | 7 | puts res=$res |
| 8 | 8 | if {![regexp {use --repository} $res]} { |
| 9 | 9 | puts stderr "Cannot run this test within an open checkout" |
| 10 | 10 | return |
| 11 | 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 | + | |
| 12 | 18 | |
| 13 | 19 | ###################################### |
| 14 | 20 | # Test 1 # |
| 15 | 21 | # Reported: Ticket [554f44ee74e3d] # |
| 16 | 22 | ###################################### |
| 17 | 23 | |
| 18 | 24 | ADDED test/th1-tcl.test |
| 19 | 25 | ADDED test/th1-tcl1.txt |
| 20 | 26 | ADDED test/th1-tcl2.txt |
| 21 | 27 | ADDED test/th1-tcl3.txt |
| 22 | 28 | ADDED test/th1-tcl4.txt |
| 23 | 29 | ADDED test/th1-tcl5.txt |
| 24 | 30 | ADDED test/th1-tcl6.txt |
| 25 | 31 | ADDED test/th1-tcl7.txt |
| 26 | 32 | 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 |
+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-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 |
+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-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 |
+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-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 | # |
+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-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 |
+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-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 |
+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-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 | # |
+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-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> |
+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-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> |
+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 |
| --- 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 @@ | ||
| 22 | 22 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) |
| 23 | 23 | LIBS = $(DMDIR)\extra\lib\ zlib wsock32 |
| 24 | 24 | |
| 25 | 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 | 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 | |
| 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 | 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 | |
| 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 | 30 | |
| 31 | 31 | |
| 32 | 32 | RC=$(DMDIR)\bin\rcc |
| 33 | 33 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 34 | 34 | |
| @@ -42,11 +42,11 @@ | ||
| 42 | 42 | |
| 43 | 43 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 44 | 44 | $(RC) $(RCFLAGS) -o$@ $** |
| 45 | 45 | |
| 46 | 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 > $@ | |
| 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 | 48 | +echo fossil >> $@ |
| 49 | 49 | +echo fossil >> $@ |
| 50 | 50 | +echo $(LIBS) >> $@ |
| 51 | 51 | +echo. >> $@ |
| 52 | 52 | +echo fossil >> $@ |
| @@ -72,10 +72,13 @@ | ||
| 72 | 72 | $(OBJDIR)\th$O : $(SRCDIR)\th.c |
| 73 | 73 | $(TCC) -o$@ -c $** |
| 74 | 74 | |
| 75 | 75 | $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c |
| 76 | 76 | $(TCC) -o$@ -c $** |
| 77 | + | |
| 78 | +$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 79 | + cp $@ $@ | |
| 77 | 80 | |
| 78 | 81 | VERSION.h : version$E $B\manifest.uuid $B\manifest $B\VERSION |
| 79 | 82 | +$** > $@ |
| 80 | 83 | |
| 81 | 84 | page_index.h: mkindex$E $(SRC) |
| @@ -85,10 +88,23 @@ | ||
| 85 | 88 | -del $(OBJDIR)\*.obj |
| 86 | 89 | -del *.obj *_.c *.h *.map |
| 87 | 90 | |
| 88 | 91 | realclean: |
| 89 | 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 | + | |
| 90 | 106 | |
| 91 | 107 | |
| 92 | 108 | $(OBJDIR)\add$O : add_.c add.h |
| 93 | 109 | $(TCC) -o$@ -c add_.c |
| 94 | 110 | |
| @@ -314,10 +330,76 @@ | ||
| 314 | 330 | $(OBJDIR)\info$O : info_.c info.h |
| 315 | 331 | $(TCC) -o$@ -c info_.c |
| 316 | 332 | |
| 317 | 333 | info_.c : $(SRCDIR)\info.c |
| 318 | 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 $** > $@ | |
| 319 | 401 | |
| 320 | 402 | $(OBJDIR)\leaf$O : leaf_.c leaf.h |
| 321 | 403 | $(TCC) -o$@ -c leaf_.c |
| 322 | 404 | |
| 323 | 405 | leaf_.c : $(SRCDIR)\leaf.c |
| @@ -580,7 +662,7 @@ | ||
| 580 | 662 | |
| 581 | 663 | zip_.c : $(SRCDIR)\zip.c |
| 582 | 664 | +translate$E $** > $@ |
| 583 | 665 | |
| 584 | 666 | 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 | |
| 586 | 668 | @copy /Y nul: headers |
| 587 | 669 |
| --- 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 @@ | ||
| 110 | 110 | $(SRCDIR)/http_socket.c \ |
| 111 | 111 | $(SRCDIR)/http_ssl.c \ |
| 112 | 112 | $(SRCDIR)/http_transport.c \ |
| 113 | 113 | $(SRCDIR)/import.c \ |
| 114 | 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 \ | |
| 115 | 126 | $(SRCDIR)/leaf.c \ |
| 116 | 127 | $(SRCDIR)/login.c \ |
| 117 | 128 | $(SRCDIR)/main.c \ |
| 118 | 129 | $(SRCDIR)/manifest.c \ |
| 119 | 130 | $(SRCDIR)/md5.c \ |
| @@ -194,10 +205,21 @@ | ||
| 194 | 205 | $(OBJDIR)/http_socket_.c \ |
| 195 | 206 | $(OBJDIR)/http_ssl_.c \ |
| 196 | 207 | $(OBJDIR)/http_transport_.c \ |
| 197 | 208 | $(OBJDIR)/import_.c \ |
| 198 | 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 \ | |
| 199 | 221 | $(OBJDIR)/leaf_.c \ |
| 200 | 222 | $(OBJDIR)/login_.c \ |
| 201 | 223 | $(OBJDIR)/main_.c \ |
| 202 | 224 | $(OBJDIR)/manifest_.c \ |
| 203 | 225 | $(OBJDIR)/md5_.c \ |
| @@ -278,10 +300,21 @@ | ||
| 278 | 300 | $(OBJDIR)/http_socket.o \ |
| 279 | 301 | $(OBJDIR)/http_ssl.o \ |
| 280 | 302 | $(OBJDIR)/http_transport.o \ |
| 281 | 303 | $(OBJDIR)/import.o \ |
| 282 | 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 \ | |
| 283 | 316 | $(OBJDIR)/leaf.o \ |
| 284 | 317 | $(OBJDIR)/login.o \ |
| 285 | 318 | $(OBJDIR)/main.o \ |
| 286 | 319 | $(OBJDIR)/manifest.o \ |
| 287 | 320 | $(OBJDIR)/md5.o \ |
| @@ -363,11 +396,11 @@ | ||
| 363 | 396 | $(TCLSH) test/tester.tcl $(APPNAME) |
| 364 | 397 | |
| 365 | 398 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(VERSION) |
| 366 | 399 | $(VERSION) $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| 367 | 400 | |
| 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 | |
| 369 | 402 | |
| 370 | 403 | $(APPNAME): $(OBJDIR)/headers $(OBJ) $(EXTRAOBJ) $(OBJDIR)/icon.o |
| 371 | 404 | $(TCC) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB) $(OBJDIR)/icon.o |
| 372 | 405 | |
| 373 | 406 | # This rule prevents make from using its default rules to try build |
| @@ -388,11 +421,11 @@ | ||
| 388 | 421 | |
| 389 | 422 | |
| 390 | 423 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex |
| 391 | 424 | $(MKINDEX) $(TRANS_SRC) >$@ |
| 392 | 425 | $(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 | |
| 394 | 427 | echo Done >$(OBJDIR)/headers |
| 395 | 428 | |
| 396 | 429 | $(OBJDIR)/headers: Makefile |
| 397 | 430 | Makefile: |
| 398 | 431 | $(OBJDIR)/add_.c: $(SRCDIR)/add.c $(OBJDIR)/translate |
| @@ -659,10 +692,87 @@ | ||
| 659 | 692 | |
| 660 | 693 | $(OBJDIR)/info.o: $(OBJDIR)/info_.c $(OBJDIR)/info.h $(SRCDIR)/config.h |
| 661 | 694 | $(XTCC) -o $(OBJDIR)/info.o -c $(OBJDIR)/info_.c |
| 662 | 695 | |
| 663 | 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 | |
| 664 | 774 | $(OBJDIR)/leaf_.c: $(SRCDIR)/leaf.c $(OBJDIR)/translate |
| 665 | 775 | $(TRANSLATE) $(SRCDIR)/leaf.c >$(OBJDIR)/leaf_.c |
| 666 | 776 | |
| 667 | 777 | $(OBJDIR)/leaf.o: $(OBJDIR)/leaf_.c $(OBJDIR)/leaf.h $(SRCDIR)/config.h |
| 668 | 778 | $(XTCC) -o $(OBJDIR)/leaf.o -c $(OBJDIR)/leaf_.c |
| @@ -970,14 +1080,18 @@ | ||
| 970 | 1080 | |
| 971 | 1081 | zip.h: $(OBJDIR)/headers |
| 972 | 1082 | $(OBJDIR)/sqlite3.o: $(SRCDIR)/sqlite3.c |
| 973 | 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 |
| 974 | 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 | |
| 975 | 1089 | $(OBJDIR)/shell.o: $(SRCDIR)/shell.c $(SRCDIR)/sqlite3.h |
| 976 | 1090 | $(XTCC) -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -c $(SRCDIR)/shell.c -o $(OBJDIR)/shell.o |
| 977 | 1091 | |
| 978 | 1092 | $(OBJDIR)/th.o: $(SRCDIR)/th.c |
| 979 | 1093 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th.c -o $(OBJDIR)/th.o |
| 980 | 1094 | |
| 981 | 1095 | $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c |
| 982 | 1096 | $(XTCC) -I$(SRCDIR) -c $(SRCDIR)/th_lang.c -o $(OBJDIR)/th_lang.o |
| 983 | 1097 | |
| 984 | 1098 | |
| 985 | 1099 | 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 @@ | ||
| 36 | 36 | LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) |
| 37 | 37 | LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR) |
| 38 | 38 | |
| 39 | 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 | 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 | |
| 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 | 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 | |
| 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 | 44 | |
| 45 | 45 | |
| 46 | 46 | APPNAME = $(OX)\fossil$(E) |
| 47 | 47 | |
| 48 | 48 | all: $(OX) $(APPNAME) |
| @@ -88,10 +88,21 @@ | ||
| 88 | 88 | echo $(OX)\http_socket.obj >> $@ |
| 89 | 89 | echo $(OX)\http_ssl.obj >> $@ |
| 90 | 90 | echo $(OX)\http_transport.obj >> $@ |
| 91 | 91 | echo $(OX)\import.obj >> $@ |
| 92 | 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 >> $@ | |
| 93 | 104 | echo $(OX)\leaf.obj >> $@ |
| 94 | 105 | echo $(OX)\login.obj >> $@ |
| 95 | 106 | echo $(OX)\main.obj >> $@ |
| 96 | 107 | echo $(OX)\manifest.obj >> $@ |
| 97 | 108 | echo $(OX)\md5.obj >> $@ |
| @@ -170,10 +181,12 @@ | ||
| 170 | 181 | $(OX)\th_lang$O : $(SRCDIR)\th_lang.c |
| 171 | 182 | $(TCC) /Fo$@ -c $** |
| 172 | 183 | |
| 173 | 184 | VERSION.h : mkversion$E $B\manifest.uuid $B\manifest $B\VERSION |
| 174 | 185 | $** > $@ |
| 186 | +$(OBJDIR)\cson_amalgamation.h : $(SRCDIR)\cson_amalgamation.h | |
| 187 | + cp $(SRCDIR)\cson_amalgamation.h $@ | |
| 175 | 188 | |
| 176 | 189 | page_index.h: mkindex$E $(SRC) |
| 177 | 190 | $** > $@ |
| 178 | 191 | |
| 179 | 192 | clean: |
| @@ -182,10 +195,22 @@ | ||
| 182 | 195 | -del headers linkopts |
| 183 | 196 | |
| 184 | 197 | realclean: |
| 185 | 198 | -del $(APPNAME) translate$E mkindex$E makeheaders$E mkversion$E |
| 186 | 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 | + | |
| 187 | 212 | |
| 188 | 213 | $(OX)\add$O : add_.c add.h |
| 189 | 214 | $(TCC) /Fo$@ -c add_.c |
| 190 | 215 | |
| 191 | 216 | add_.c : $(SRCDIR)\add.c |
| @@ -410,10 +435,76 @@ | ||
| 410 | 435 | $(OX)\info$O : info_.c info.h |
| 411 | 436 | $(TCC) /Fo$@ -c info_.c |
| 412 | 437 | |
| 413 | 438 | info_.c : $(SRCDIR)\info.c |
| 414 | 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 $** > $@ | |
| 415 | 506 | |
| 416 | 507 | $(OX)\leaf$O : leaf_.c leaf.h |
| 417 | 508 | $(TCC) /Fo$@ -c leaf_.c |
| 418 | 509 | |
| 419 | 510 | leaf_.c : $(SRCDIR)\leaf.c |
| @@ -676,7 +767,7 @@ | ||
| 676 | 767 | |
| 677 | 768 | zip_.c : $(SRCDIR)\zip.c |
| 678 | 769 | translate$E $** > $@ |
| 679 | 770 | |
| 680 | 771 | 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 | |
| 682 | 773 | @copy /Y nul: headers |
| 683 | 774 |
| --- 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 |
+37
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,7 +1,44 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 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 | + | |
| 3 | 40 | <h2>Changes For Version 1.19 (2011-09-02)</h2> |
| 4 | 41 | |
| 5 | 42 | * Added a ./configure script based on autosetup. |
| 6 | 43 | * Added the "[/help/winsrv | fossil winsrv]" command |
| 7 | 44 | for creating a Fossil service on windows systems. |
| 8 | 45 |
| --- 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 |
+5
-4
| --- www/checkin_names.wiki | ||
| +++ www/checkin_names.wiki | ||
| @@ -126,23 +126,24 @@ | ||
| 126 | 126 | * <i>YYYY-MM-DD HH:MM</i> |
| 127 | 127 | * <i>YYYY-MM-DD HH:MM:SS</i> |
| 128 | 128 | |
| 129 | 129 | The space between the day and the year can optionally be |
| 130 | 130 | replaced by an uppercase <b>T</b> and the entire timestamp can |
| 131 | -optionally be followed by "<b>utc</b>". | |
| 131 | +optionally be followed by "<b>z</b>" or "<b>Z</b>". | |
| 132 | 132 | |
| 133 | 133 | In its default configuration, Fossil interprets and displays all dates |
| 134 | 134 | in Universal Coordinated Time (UTC). This tends to work the best for |
| 135 | 135 | distributed projects where participants are scattered around the globe. |
| 136 | 136 | But there is an option on the Admin/Timeline page of the web-interface to |
| 137 | -switch to local time. The "<b>utc</b>" suffix on an timestamp check-in | |
| 137 | +switch to local time. The "<b>Z</b>" suffix on an timestamp check-in | |
| 138 | 138 | name is meaningless if Fossil is in the default mode of using UTC for |
| 139 | 139 | everything, but if Fossil has been switched to localtime mode, then the |
| 140 | -"<b>utc</b>" suffix means to interpret that particular timestamp using | |
| 140 | +"<b>Z</b>" suffix means to interpret that particular timestamp using | |
| 141 | 141 | UTC instead localtime. |
| 142 | 142 | |
| 143 | -As an example, consider the homepage for the Fossil website itself: | |
| 143 | +For an example of how timestamps are useful, | |
| 144 | +consider the homepage for the Fossil website itself: | |
| 144 | 145 | |
| 145 | 146 | <blockquote> |
| 146 | 147 | http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki |
| 147 | 148 | </blockquote> |
| 148 | 149 | |
| 149 | 150 |
| --- www/checkin_names.wiki | |
| +++ www/checkin_names.wiki | |
| @@ -126,23 +126,24 @@ | |
| 126 | * <i>YYYY-MM-DD HH:MM</i> |
| 127 | * <i>YYYY-MM-DD HH:MM:SS</i> |
| 128 | |
| 129 | The space between the day and the year can optionally be |
| 130 | replaced by an uppercase <b>T</b> and the entire timestamp can |
| 131 | optionally be followed by "<b>utc</b>". |
| 132 | |
| 133 | In its default configuration, Fossil interprets and displays all dates |
| 134 | in Universal Coordinated Time (UTC). This tends to work the best for |
| 135 | distributed projects where participants are scattered around the globe. |
| 136 | But there is an option on the Admin/Timeline page of the web-interface to |
| 137 | switch to local time. The "<b>utc</b>" suffix on an timestamp check-in |
| 138 | name is meaningless if Fossil is in the default mode of using UTC for |
| 139 | everything, but if Fossil has been switched to localtime mode, then the |
| 140 | "<b>utc</b>" suffix means to interpret that particular timestamp using |
| 141 | UTC instead localtime. |
| 142 | |
| 143 | As an example, consider the homepage for the Fossil website itself: |
| 144 | |
| 145 | <blockquote> |
| 146 | http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki |
| 147 | </blockquote> |
| 148 | |
| 149 |
| --- www/checkin_names.wiki | |
| +++ www/checkin_names.wiki | |
| @@ -126,23 +126,24 @@ | |
| 126 | * <i>YYYY-MM-DD HH:MM</i> |
| 127 | * <i>YYYY-MM-DD HH:MM:SS</i> |
| 128 | |
| 129 | The space between the day and the year can optionally be |
| 130 | replaced by an uppercase <b>T</b> and the entire timestamp can |
| 131 | optionally be followed by "<b>z</b>" or "<b>Z</b>". |
| 132 | |
| 133 | In its default configuration, Fossil interprets and displays all dates |
| 134 | in Universal Coordinated Time (UTC). This tends to work the best for |
| 135 | distributed projects where participants are scattered around the globe. |
| 136 | But there is an option on the Admin/Timeline page of the web-interface to |
| 137 | switch to local time. The "<b>Z</b>" suffix on an timestamp check-in |
| 138 | name is meaningless if Fossil is in the default mode of using UTC for |
| 139 | everything, but if Fossil has been switched to localtime mode, then the |
| 140 | "<b>Z</b>" suffix means to interpret that particular timestamp using |
| 141 | UTC instead localtime. |
| 142 | |
| 143 | For an example of how timestamps are useful, |
| 144 | consider the homepage for the Fossil website itself: |
| 145 | |
| 146 | <blockquote> |
| 147 | http://www.fossil-scm.org/fossil/doc/<b>trunk</b>/www/index.wiki |
| 148 | </blockquote> |
| 149 | |
| 150 |