Check-in [7124f09f07]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
SHA1 Hash:7124f09f0770f64948f18d1866b37aa52e8048dd
Date: 2012-03-10 13:17:50
User: ashish
Comment:Merge latest changes from trunk.
Tags And Properties
Changes

Changes to Makefile.in

38 # 38 # 39 TCLSH = tclsh 39 TCLSH = tclsh 40 40 41 LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ 41 LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ 42 TCC += @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H 42 TCC += @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H 43 INSTALLDIR = $(DESTDIR)@prefix@/bin 43 INSTALLDIR = $(DESTDIR)@prefix@/bin 44 USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ 44 USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ > 45 FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@ 45 46 46 include $(SRCDIR)/main.mk 47 include $(SRCDIR)/main.mk 47 48 48 distclean: clean 49 distclean: clean 49 rm -f autoconfig.h config.log Makefile 50 rm -f autoconfig.h config.log Makefile

Changes to VERSION

1 1.21 | 1 1.22

Changes to ajax/i-test/rhino-test.js

1 var TestApp = { 1 var TestApp = { 2 serverUrl: 2 serverUrl: 3 'http://localhost:8080' 3 'http://localhost:8080' 4 //'http://fjson/cgi-bin/fossil-json.cgi' 4 //'http://fjson/cgi-bin/fossil-json.cgi' 5 //'http://192.168.1.62:8080' 5 //'http://192.168.1.62:8080' 6 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi' 6 //'http://fossil.wanderinghorse.net/repos/fossil-json-java/index.cgi' 7 , 7 , 8 verbose:false, | 8 verbose:true, 9 fossilBinary:'fossil', 9 fossilBinary:'fossil', 10 wiki:{} 10 wiki:{} 11 }; 11 }; 12 (function bootstrap() { 12 (function bootstrap() { 13 var srcdir = '../js/'; 13 var srcdir = '../js/'; 14 var includes = [srcdir+'json2.js', 14 var includes = [srcdir+'json2.js', 15 srcdir+'whajaj.js', 15 srcdir+'whajaj.js', ................................................................................................................................................................................ 169 assertResponseOK(rs); 169 assertResponseOK(rs); 170 assert(rs.payload.name == TestApp.wiki.list[0], "Fetched page name matches e 170 assert(rs.payload.name == TestApp.wiki.list[0], "Fetched page name matches e 171 print("Got first wiki page: "+WhAjaj.stringify(rs.payload)); 171 print("Got first wiki page: "+WhAjaj.stringify(rs.payload)); 172 172 173 } 173 } 174 testAnonWiki.description = 'Fetch wiki list as anonymous user.'; 174 testAnonWiki.description = 'Fetch wiki list as anonymous user.'; 175 175 > 176 function testFetchCheckinArtifact(){ > 177 var art = '18dd383e5e7684ece'; > 178 var rs; > 179 TestApp.fossil.sendCommand('/json/artifact',{ > 180 'name': art > 181 }, > 182 { > 183 onResponse:function(resp,req){ > 184 rs = resp; > 185 } > 186 }); > 187 assertResponseOK(rs); > 188 assert(3 == rs.payload.artifact.parents.length, 'Got 3 parent artifacts.'); > 189 } > 190 testFetchCheckinArtifact.description = '/json/artifact/CHECKIN'; > 191 176 function testAnonLogout(){ 192 function testAnonLogout(){ 177 var rs; 193 var rs; 178 TestApp.fossil.logout({ 194 TestApp.fossil.logout({ 179 onResponse:function(resp,req){ 195 onResponse:function(resp,req){ 180 rs = resp; 196 rs = resp; 181 } 197 } 182 }); 198 }); ................................................................................................................................................................................ 197 var args = [TestApp.fossilBinary, 'json', '--json-input', '-']; 213 var args = [TestApp.fossilBinary, 'json', '--json-input', '-']; 198 var p = java.lang.Runtime.getRuntime().exec(args); 214 var p = java.lang.Runtime.getRuntime().exec(args); 199 var outs = p.getOutputStream(); 215 var outs = p.getOutputStream(); 200 var osr = new java.io.OutputStreamWriter(outs); 216 var osr = new java.io.OutputStreamWriter(outs); 201 var osb = new java.io.BufferedWriter(osr); 217 var osb = new java.io.BufferedWriter(osr); 202 var json = JSON.stringify(req); 218 var json = JSON.stringify(req); 203 osb.write(json,0, json.length); 219 osb.write(json,0, json.length); 204 //osb.flush(); < 205 osb.close(); 220 osb.close(); > 221 req = json = outs = osr = osb = undefined; 206 var ins = p.getInputStream(); 222 var ins = p.getInputStream(); 207 var isr = new java.io.InputStreamReader(ins); 223 var isr = new java.io.InputStreamReader(ins); 208 var br = new java.io.BufferedReader(isr); 224 var br = new java.io.BufferedReader(isr); 209 var line; 225 var line; 210 226 211 while( null !== (line=br.readLine())){ 227 while( null !== (line=br.readLine())){ 212 print(line); 228 print(line); 213 } 229 } 214 //outs.close(); | 230 br.close(); > 231 isr.close(); 215 ins.close(); 232 ins.close(); > 233 p.waitFor(); 216 } 234 } 217 testExternalProcess.description = 'Run fossil as external process.'; 235 testExternalProcess.description = 'Run fossil as external process.'; 218 236 219 function testExternalProcessHandler(){ 237 function testExternalProcessHandler(){ 220 var aj = TestApp.fossil.ajaj; 238 var aj = TestApp.fossil.ajaj; 221 var oldImpl = aj.sendImpl; 239 var oldImpl = aj.sendImpl; 222 aj.sendImpl = FossilAjaj.rhinoLocalBinarySendImpl; 240 aj.sendImpl = FossilAjaj.rhinoLocalBinarySendImpl; ................................................................................................................................................................................ 235 253 236 (function runAllTests(){ 254 (function runAllTests(){ 237 var testList = [ 255 var testList = [ 238 testHAI, 256 testHAI, 239 testIAmNobody, 257 testIAmNobody, 240 testAnonymousLogin, 258 testAnonymousLogin, 241 testAnonWiki, 259 testAnonWiki, > 260 testFetchCheckinArtifact, 242 testAnonLogout, 261 testAnonLogout, 243 //testExternalProcess, | 262 testExternalProcess, 244 testExternalProcessHandler 263 testExternalProcessHandler 245 ]; 264 ]; 246 var i, f; 265 var i, f; 247 for( i = 0; i < testList.length; ++i ){ 266 for( i = 0; i < testList.length; ++i ){ 248 f = testList[i]; 267 f = testList[i]; 249 try{ 268 try{ 250 print("Running test #"+(i+1)+": "+(f.description || "no description. 269 print("Running test #"+(i+1)+": "+(f.description || "no description.

Changes to ajax/index.html

231 <br/> 231 <br/> 232 232 233 <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/ 233 <input type='button' value='branch/list' onclick='TheApp.cgi.sendCommand("/json/ 234 <input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/ 234 <input type='button' value='timeline/ci' onclick='TheApp.cgi.sendCommand("/json/ 235 <input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/jso 235 <input type='button' value='timeline/wiki' onclick='TheApp.cgi.sendCommand("/jso 236 <input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/j 236 <input type='button' value='timeline/ticket' onclick='TheApp.cgi.sendCommand("/j 237 <input type='button' value='timeline/branch' onclick='TheApp.cgi.sendCommand("/j 237 <input type='button' value='timeline/branch' onclick='TheApp.cgi.sendCommand("/j > 238 > 239 <br/> > 240 238 <input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wi 241 <input type='button' value='wiki/list' onclick='TheApp.cgi.sendCommand("/json/wi > 242 <input type='button' value='wiki/list verbose' onclick='TheApp.cgi.sendCommand(" 239 <input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/j 243 <input type='button' value='wiki/get Fossil' onclick='TheApp.cgi.sendCommand("/j 240 <input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/j 244 <input type='button' value='wiki/get/Fossil' onclick='TheApp.cgi.sendCommand("/j > 245 <input type='button' value='wiki/diff' onclick='TheApp.cgi.sendCommand("/json/wi 241 246 242 <br/> 247 <br/> 243 248 244 <input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/us 249 <input type='button' value='user/list' onclick='TheApp.cgi.sendCommand("/json/us 245 <input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/use 250 <input type='button' value='user/get' onclick='TheApp.cgi.sendCommand("/json/use 246 <input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag 251 <input type='button' value='tag/list' onclick='TheApp.cgi.sendCommand("/json/tag 247 <input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/jso 252 <input type='button' value='tag/list/json' onclick='TheApp.cgi.sendCommand("/jso ................................................................................................................................................................................ 265 <input type='button' value='report list' 270 <input type='button' value='report list' 266 onclick='TheApp.cgi.sendCommand("/json/report/list")' /> 271 onclick='TheApp.cgi.sendCommand("/json/report/list")' /> 267 <input type='button' value='report get' 272 <input type='button' value='report get' 268 onclick='TheApp.cgi.sendCommand("/json/report/get",2)' /> 273 onclick='TheApp.cgi.sendCommand("/json/report/get",2)' /> 269 274 270 <input type='button' value='report run' 275 <input type='button' value='report run' 271 onclick='TheApp.cgi.sendCommand("/json/report/run",{"report":2,"format":"o"} 276 onclick='TheApp.cgi.sendCommand("/json/report/run",{"report":2,"format":"o"} > 277 > 278 <input type='button' value='config/get' onclick='TheApp.cgi.sendCommand("/json/c 272 279 273 <!-- not yet ready... 280 <!-- not yet ready... 274 <input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json 281 <input type='button' value='artifact/XYZ' onclick='TheApp.cgi.sendCommand("/json 275 --> 282 --> 276 283 277 <!-- 284 <!-- 278 <input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' /> 285 <input type='button' value='get whiki' onclick='TheApp.cgi.getPages("whiki")' />

Changes to ajax/js/whajaj.js

107 if( 0 === arguments.length ) { 107 if( 0 === arguments.length ) { 108 if( ('undefined' === typeof window) || 108 if( ('undefined' === typeof window) || 109 !window.location || 109 !window.location || 110 !window.location.search ) return false; 110 !window.location.search ) return false; 111 else str = (''+window.location.search).substring(1); 111 else str = (''+window.location.search).substring(1); 112 } 112 } 113 if( ! str ) return false; 113 if( ! str ) return false; > 114 str = (''+str).split(/#/,2)[0]; // remove #... to avoid it being added as pa 114 var args = {}; 115 var args = {}; 115 var sp = str.split(/&+/); 116 var sp = str.split(/&+/); 116 var rx = /^([^=]+)(=(.+))?/; 117 var rx = /^([^=]+)(=(.+))?/; 117 var i, m; 118 var i, m; 118 for( i in sp ) { 119 for( i in sp ) { 119 m = rx.exec( sp[i] ); 120 m = rx.exec( sp[i] ); 120 if( ! m ) continue; 121 if( ! m ) continue;

Changes to auto.def

164 if {$dir eq ""} { 164 if {$dir eq ""} { 165 set msg "system ssl" 165 set msg "system ssl" 166 set cflags "" 166 set cflags "" 167 set ldflags "" 167 set ldflags "" 168 } else { 168 } else { 169 set msg "ssl in $dir" 169 set msg "ssl in $dir" 170 set cflags "-I$dir/include" 170 set cflags "-I$dir/include" 171 set ldflags "-L$dir/include" | 171 set ldflags "-L$dir/lib" 172 } 172 } 173 if {[check-for-openssl $msg "$cflags $ldflags"]} { 173 if {[check-for-openssl $msg "$cflags $ldflags"]} { 174 incr found 174 incr found 175 break 175 break 176 } 176 } 177 } 177 } 178 } 178 }

Changes to autosetup/README.autosetup

1 This is autosetup v0.6.2. See http://msteveb.github.com/autosetup/ | 1 This is autosetup v0.6.4. See http://msteveb.github.com/autosetup/

Changes to autosetup/autosetup

1 #!/bin/sh 1 #!/bin/sh 2 # Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/ 2 # Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/ 3 # All rights reserved 3 # All rights reserved 4 # vim:se syntax=tcl: 4 # vim:se syntax=tcl: 5 # \ 5 # \ 6 dir=`dirname "$0"`; exec "`$dir/find-tclsh`" "$0" "$@" 6 dir=`dirname "$0"`; exec "`$dir/find-tclsh`" "$0" "$@" 7 7 8 set autosetup(version) 0.6.2 | 8 set autosetup(version) 0.6.4 9 9 10 # Can be set to 1 to debug early-init problems 10 # Can be set to 1 to debug early-init problems 11 set autosetup(debug) 0 11 set autosetup(debug) 0 12 12 13 ################################################################## 13 ################################################################## 14 # 14 # 15 # Main flow of control, option handling 15 # Main flow of control, option handling ................................................................................................................................................................................ 514 } 514 } 515 getenv $name $default 515 getenv $name $default 516 } 516 } 517 517 518 # @env-is-set name 518 # @env-is-set name 519 # 519 # 520 # Returns 1 if the $name was specified on the command line or in the environment 520 # Returns 1 if the $name was specified on the command line or in the environment > 521 # Note that an empty environment variable is not considered to be set. 521 # 522 # 522 proc env-is-set {name} { 523 proc env-is-set {name} { 523 if {[dict exists $::autosetup(cmdline) $name]} { 524 if {[dict exists $::autosetup(cmdline) $name]} { 524 return 1 525 return 1 525 } 526 } 526 info exists ::env($name) | 527 if {[getenv $name ""] ne ""} { > 528 return 1 > 529 } > 530 return 0 527 } 531 } 528 532 529 # @readfile filename ?default=""? 533 # @readfile filename ?default=""? 530 # 534 # 531 # Return the contents of the file, without the trailing newline. 535 # Return the contents of the file, without the trailing newline. 532 # If the doesn't exist or can't be read, returns $default. 536 # If the doesn't exist or can't be read, returns $default. 533 # 537 # ................................................................................................................................................................................ 1290 } else { 1294 } else { 1291 puts "I don't see configure, so I will create it." 1295 puts "I don't see configure, so I will create it." 1292 } 1296 } 1293 if {$create_configure} { 1297 if {$create_configure} { 1294 if {!$::autosetup(installed)} { 1298 if {!$::autosetup(installed)} { 1295 user-notice "Warning: Initialising from the development 1299 user-notice "Warning: Initialising from the development 1296 1300 1297 writefile configure "#!/bin/sh\nWRAPPER=\"\$0\" exec $:: | 1301 writefile configure "#!/bin/sh\nWRAPPER=\"\$0\"; export 1298 } else { 1302 } else { 1299 writefile configure \ 1303 writefile configure \ 1300 {#!/bin/sh 1304 {#!/bin/sh 1301 dir="`dirname "$0"`/autosetup" 1305 dir="`dirname "$0"`/autosetup" 1302 WRAPPER="$0" exec "`$dir/find-tclsh`" "$dir/autosetup" "$@" | 1306 WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/autosetup" "$@" 1303 } 1307 } 1304 } 1308 } 1305 catch {exec chmod 755 configure} 1309 catch {exec chmod 755 configure} 1306 } 1310 } 1307 if {![file exists auto.def]} { 1311 if {![file exists auto.def]} { 1308 puts "I don't see auto.def, so I will create a default one." 1312 puts "I don't see auto.def, so I will create a default one." 1309 writefile auto.def {# Initial auto.def created by 'autosetup --i 1313 writefile auto.def {# Initial auto.def created by 'autosetup --i ................................................................................................................................................................................ 1539 } 1543 } 1540 } elseif {$autosetup(iswin)} { 1544 } elseif {$autosetup(iswin)} { 1541 # On Windows, backslash convert all environment variables 1545 # On Windows, backslash convert all environment variables 1542 # (Assume that Tcl does this for us) 1546 # (Assume that Tcl does this for us) 1543 proc getenv {name args} { 1547 proc getenv {name args} { 1544 string map {\\ /} [env $name {*}$args] 1548 string map {\\ /} [env $name {*}$args] 1545 } 1549 } 1546 # Jim uses system() for exec under mingw, so < 1547 # we need to fetch the output ourselves < 1548 proc exec-with-stderr {args} { < 1549 set tmpfile auto[format %04x [rand 10000]].tmp < 1550 set rc [catch [list exec {*}$args >$tmpfile 2>&1] result < 1551 set result [readfile $tmpfile] < 1552 file delete $tmpfile < 1553 return -code $rc $result < 1554 } < 1555 } else { 1550 } else { 1556 # Jim on unix is simple 1551 # Jim on unix is simple 1557 alias getenv env 1552 alias getenv env 1558 } 1553 } 1559 1554 1560 # In case 'file normalize' doesn't exist 1555 # In case 'file normalize' doesn't exist 1561 # 1556 #

Added autosetup/cc-db.tcl

> 1 # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ > 2 # All rights reserved > 3 > 4 # @synopsis: > 5 # > 6 # The 'cc-db' module provides a knowledge based of system idiosyncracies > 7 # In general, this module can always be included > 8 > 9 use cc > 10 > 11 module-options {} > 12 > 13 # openbsd needs sys/types.h to detect some system headers > 14 cc-include-needs sys/socket.h sys/types.h > 15 cc-include-needs netinet/in.h sys/types.h

Changes to autosetup/cc-shared.tcl

5 # 5 # 6 # The 'cc-shared' module provides support for shared libraries and shared object 6 # The 'cc-shared' module provides support for shared libraries and shared object 7 # It defines the following variables: 7 # It defines the following variables: 8 # 8 # 9 ## SH_CFLAGS Flags to use compiling sources destined for a shared librar 9 ## SH_CFLAGS Flags to use compiling sources destined for a shared librar 10 ## SH_LDFLAGS Flags to use linking a shared library 10 ## SH_LDFLAGS Flags to use linking a shared library 11 ## SHOBJ_CFLAGS Flags to use compiling sources destined for a shared object 11 ## SHOBJ_CFLAGS Flags to use compiling sources destined for a shared object 12 ## SHOBJ_LDFLAGS Flags to use linking a shared object | 12 ## SHOBJ_LDFLAGS Flags to use linking a shared object, undefined symbols all > 13 ## SHOBJ_LDFLAGS_R - as above, but all symbols must be resolved 13 ## SH_LINKFLAGS Flags to use linking an executable which will load shared o 14 ## SH_LINKFLAGS Flags to use linking an executable which will load shared o 14 ## LD_LIBRARY_PATH Environment variable which specifies path to shared librari 15 ## LD_LIBRARY_PATH Environment variable which specifies path to shared librari 15 16 16 module-options {} 17 module-options {} 17 18 18 foreach i {SH_LINKFLAGS SH_CFLAGS SH_LDFLAGS SHOBJ_CFLAGS SHOBJ_LDFLAGS} { 19 foreach i {SH_LINKFLAGS SH_CFLAGS SH_LDFLAGS SHOBJ_CFLAGS SHOBJ_LDFLAGS} { 19 define $i "" 20 define $i "" ................................................................................................................................................................................ 23 24 24 switch -glob -- [get-define host] { 25 switch -glob -- [get-define host] { 25 *-*-darwin* { 26 *-*-darwin* { 26 define SH_CFLAGS -dynamic 27 define SH_CFLAGS -dynamic 27 define SH_LDFLAGS "-dynamiclib" 28 define SH_LDFLAGS "-dynamiclib" 28 define SHOBJ_CFLAGS "-dynamic -fno-common" 29 define SHOBJ_CFLAGS "-dynamic -fno-common" 29 define SHOBJ_LDFLAGS "-bundle -undefined dynamic_lookup" 30 define SHOBJ_LDFLAGS "-bundle -undefined dynamic_lookup" > 31 define SHOBJ_LDFLAGS_R "-bundle" 30 define LD_LIBRARY_PATH DYLD_LIBRARY_PATH 32 define LD_LIBRARY_PATH DYLD_LIBRARY_PATH 31 } 33 } 32 *-*-ming* { 34 *-*-ming* { 33 define SH_LDFLAGS -shared 35 define SH_LDFLAGS -shared 34 define SHOBJ_LDFLAGS -shared 36 define SHOBJ_LDFLAGS -shared > 37 define SHOBJ_LDFLAGS_R -shared 35 } 38 } 36 *-*-cygwin { 39 *-*-cygwin { 37 define SH_LDFLAGS -shared 40 define SH_LDFLAGS -shared 38 define SHOBJ_LDFLAGS -shared 41 define SHOBJ_LDFLAGS -shared 39 } 42 } 40 *-*-solaris* { 43 *-*-solaris* { 41 # XXX: These haven't been fully tested. 44 # XXX: These haven't been fully tested. ................................................................................................................................................................................ 48 # XXX: These haven't been tested 51 # XXX: These haven't been tested 49 define SH_LINKFLAGS -Wl,+s 52 define SH_LINKFLAGS -Wl,+s 50 define SH_CFLAGS +z 53 define SH_CFLAGS +z 51 define SHOBJ_CFLAGS "+O3 +z" 54 define SHOBJ_CFLAGS "+O3 +z" 52 define SHOBJ_LDFLAGS -b 55 define SHOBJ_LDFLAGS -b 53 define LD_LIBRARY_PATH SHLIB_PATH 56 define LD_LIBRARY_PATH SHLIB_PATH 54 } 57 } > 58 sparc* { > 59 # sparc has a very small GOT table limit, so use -fPIC > 60 define SH_LINKFLAGS -rdynamic > 61 define SH_CFLAGS -fPIC > 62 define SH_LDFLAGS -shared > 63 define SHOBJ_CFLAGS -fPIC > 64 define SHOBJ_LDFLAGS -shared > 65 } 55 * { 66 * { 56 # Generic Unix settings 67 # Generic Unix settings 57 define SH_LINKFLAGS -rdynamic 68 define SH_LINKFLAGS -rdynamic 58 define SH_CFLAGS -fpic 69 define SH_CFLAGS -fpic 59 define SH_LDFLAGS -shared 70 define SH_LDFLAGS -shared 60 define SHOBJ_CFLAGS -fpic 71 define SHOBJ_CFLAGS -fpic 61 define SHOBJ_LDFLAGS "-shared -nostartfiles" | 72 define SHOBJ_LDFLAGS -shared 62 } 73 } 63 } 74 } > 75 > 76 if {![is-defined SHOBJ_LDFLAGS_R]} { > 77 define SHOBJ_LDFLAGS_R [get-define SHOBJ_LDFLAGS] > 78 }

Changes to autosetup/cc.tcl

109 } 109 } 110 110 111 # @cc-check-includes includes ... 111 # @cc-check-includes includes ... 112 # 112 # 113 # Checks that the given include files can be used 113 # Checks that the given include files can be used 114 proc cc-check-includes {args} { 114 proc cc-check-includes {args} { 115 cc-check-some-feature $args { 115 cc-check-some-feature $args { > 116 set with {} > 117 if {[dict exists $::autosetup(cc-include-deps) $each]} { > 118 set deps [dict keys [dict get $::autosetup(cc-include-de > 119 msg-quiet cc-check-includes $deps > 120 foreach i $deps { > 121 if {[have-feature $i]} { > 122 lappend with $i > 123 } > 124 } > 125 } > 126 if {[llength $with]} { > 127 cc-with [list -includes $with] { 116 cctest -includes $each | 128 cctest -includes $each 117 } | 129 } > 130 } else { > 131 cctest -includes $each 118 } | 132 } > 133 } > 134 } > 135 > 136 # @cc-include-needs include required > 137 # > 138 # Ensures that when checking for 'include', a check is first > 139 # made for 'required', and if found, it is #included > 140 proc cc-include-needs {file depfile} { > 141 dict set ::autosetup(cc-include-deps) $file $depfile 1 > 142 } 119 143 120 # @cc-check-types type ... 144 # @cc-check-types type ... 121 # 145 # 122 # Checks that the types exist. 146 # Checks that the types exist. 123 proc cc-check-types {args} { 147 proc cc-check-types {args} { 124 cc-check-some-feature $args { 148 cc-check-some-feature $args { 125 cctest_type $each 149 cctest_type $each ................................................................................................................................................................................ 370 } elseif {[llength $args] > 1} { 394 } elseif {[llength $args] > 1} { 371 autosetup-error "usage: cc-with settings ?script?" 395 autosetup-error "usage: cc-with settings ?script?" 372 } else { 396 } else { 373 set save [cc-add-settings $settings] 397 set save [cc-add-settings $settings] 374 set rc [catch {uplevel 1 [lindex $args 0]} result info] 398 set rc [catch {uplevel 1 [lindex $args 0]} result info] 375 cc-store-settings $save 399 cc-store-settings $save 376 if {$rc != 0} { 400 if {$rc != 0} { 377 return $result -code [dict get $info -code] | 401 return -code [dict get $info -code] $result 378 } 402 } 379 return $result 403 return $result 380 } 404 } 381 } 405 } 382 406 383 # @cctest ?settings? 407 # @cctest ?settings? 384 # 408 # ................................................................................................................................................................................ 644 user-error "Could not find a C compiler. Tried: [join $try ", "]" 668 user-error "Could not find a C compiler. Tried: [join $try ", "]" 645 } 669 } 646 670 647 define CCACHE [find-an-executable [get-env CCACHE ccache]] 671 define CCACHE [find-an-executable [get-env CCACHE ccache]] 648 672 649 # Initial cctest settings 673 # Initial cctest settings 650 cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} 674 cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} > 675 set autosetup(cc-include-deps) {} 651 676 652 msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS] 677 msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS] 653 if {[get-define CXX] ne "false"} { 678 if {[get-define CXX] ne "false"} { 654 msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-def 679 msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-def 655 } 680 } 656 msg-result "Build C compiler...[get-define CC_FOR_BUILD]" 681 msg-result "Build C compiler...[get-define CC_FOR_BUILD]" 657 682 658 if {![cc-check-includes stdlib.h]} { 683 if {![cc-check-includes stdlib.h]} { 659 user-error "Compiler does not work. See config.log" 684 user-error "Compiler does not work. See config.log" 660 } 685 }

Changes to autosetup/find-tclsh

1 #!/bin/sh 1 #!/bin/sh 2 # Looks for a suitable tclsh or jimsh in the PATH 2 # Looks for a suitable tclsh or jimsh in the PATH 3 # If not found, builds a bootstrap jimsh from source 3 # If not found, builds a bootstrap jimsh from source 4 d=`dirname "$0"` 4 d=`dirname "$0"` > 5 { "$d/jimsh0" "$d/test-tclsh"; } 2>/dev/null && exit 0 5 PATH="$PATH:$d" | 6 PATH="$PATH:$d"; export PATH 6 for tclsh in jimsh tclsh tclsh8.5 tclsh8.6 jimsh0; do | 7 for tclsh in jimsh tclsh tclsh8.5 tclsh8.6; do 7 { $tclsh "$d/test-tclsh"; } 2>/dev/null && exit 0 8 { $tclsh "$d/test-tclsh"; } 2>/dev/null && exit 0 8 done 9 done 9 echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" 10 echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" 10 for cc in ${CC_FOR_BUILD:-cc} gcc; do 11 for cc in ${CC_FOR_BUILD:-cc} gcc; do 11 { $cc -o "$d/jimsh0" "$d/jimsh0.c"; } 2>/dev/null || continue 12 { $cc -o "$d/jimsh0" "$d/jimsh0.c"; } 2>/dev/null || continue 12 "$d/jimsh0" "$d/test-tclsh" && exit 0 13 "$d/jimsh0" "$d/test-tclsh" && exit 0 13 done 14 done 14 echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." 15 echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." 15 echo false 16 echo false

Changes to autosetup/jimsh0.c

More than 10,000 changes

Changes to autosetup/system.tcl

224 define prefix $prefix 224 define prefix $prefix 225 define builddir $autosetup(builddir) 225 define builddir $autosetup(builddir) 226 define srcdir $autosetup(srcdir) 226 define srcdir $autosetup(srcdir) 227 # Allow this to come from the environment 227 # Allow this to come from the environment 228 define top_srcdir [get-env top_srcdir [get-define srcdir]] 228 define top_srcdir [get-env top_srcdir [get-define srcdir]] 229 229 230 # autoconf supports all of these 230 # autoconf supports all of these 231 define exec_prefix [opt-val exec-prefix [get-env exec-prefix \${prefix}]] | 231 set exec_prefix [opt-val exec-prefix $prefix] > 232 define exec_prefix $exec_prefix 232 foreach {name defpath} { 233 foreach {name defpath} { 233 bindir \${exec_prefix}/bin | 234 bindir /bin 234 sbindir \${exec_prefix}/sbin | 235 sbindir /sbin 235 libexecdir \${exec_prefix}/libexec | 236 libexecdir /libexec 236 libdir \${exec_prefix}/lib | 237 libdir /lib > 238 } { > 239 define $name [opt-val $name $exec_prefix$defpath] > 240 } > 241 foreach {name defpath} { 237 datadir \${prefix}/share | 242 datadir /share 238 sysconfdir \${prefix}/etc | 243 sysconfdir /etc 239 sharedstatedir \${prefix}/com | 244 sharedstatedir /com 240 localstatedir \${prefix}/var | 245 localstatedir /var 241 infodir \${prefix}/share/info | 246 infodir /share/info 242 mandir \${prefix}/share/man | 247 mandir /share/man 243 includedir \${prefix}/include | 248 includedir /include 244 } { 249 } { 245 define $name [opt-val $name [get-env $name $defpath]] | 250 define $name [opt-val $name $prefix$defpath] 246 } 251 } 247 252 248 define SHELL [get-env SHELL [find-an-executable sh bash ksh]] 253 define SHELL [get-env SHELL [find-an-executable sh bash ksh]] 249 254 250 # Windows vs. non-Windows 255 # Windows vs. non-Windows 251 switch -glob -- [get-define host] { 256 switch -glob -- [get-define host] { 252 *-*-ming* - *-*-cygwin { 257 *-*-ming* - *-*-cygwin {

Changes to autosetup/test-tclsh

1 # A small Tcl script to verify that the chosen 1 # A small Tcl script to verify that the chosen 2 # interpreter works. Sometimes we might e.g. pick up 2 # interpreter works. Sometimes we might e.g. pick up 3 # an interpreter for a different arch. 3 # an interpreter for a different arch. 4 # Outputs the full path to the interpreter 4 # Outputs the full path to the interpreter 5 5 6 if {[catch {info version} version] == 0} { 6 if {[catch {info version} version] == 0} { 7 # This is Jim Tcl 7 # This is Jim Tcl 8 if {$version >= 0.70} { | 8 if {$version >= 0.72} { 9 # Ensure that regexp works 9 # Ensure that regexp works 10 regexp (a.*?) a 10 regexp (a.*?) a 11 < 12 # Older versions of jimsh may return a relative path for [info n < 13 puts [file join [pwd] [info nameofexecutable]] | 11 puts [info nameofexecutable] 14 exit 0 12 exit 0 15 } 13 } 16 } elseif {[catch {info tclversion} version] == 0} { 14 } elseif {[catch {info tclversion} version] == 0} { 17 if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} { 15 if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} { 18 puts [info nameofexecutable] 16 puts [info nameofexecutable] 19 exit 0 17 exit 0 20 } 18 } 21 } 19 } 22 exit 1 20 exit 1

Changes to configure

1 #!/bin/sh 1 #!/bin/sh 2 dir="`dirname "$0"`/autosetup" 2 dir="`dirname "$0"`/autosetup" 3 WRAPPER="$0" exec "`$dir/find-tclsh`" "$dir/autosetup" "$@" | 3 WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/autosetup" "$@"

Changes to debian/makedeb.sh

1 #!/bin/bash 1 #!/bin/bash 2 # A quick hack to generate a Debian package of fossil. i took most of this 2 # A quick hack to generate a Debian package of fossil. i took most of this 3 # from Martin Krafft's "The Debian System" book. 3 # from Martin Krafft's "The Debian System" book. 4 4 5 DEB_REV=${1-1} # .deb package build/revision number. 5 DEB_REV=${1-1} # .deb package build/revision number. 6 PACKAGE_DEBNAME=fossil-scm | 6 PACKAGE_DEBNAME=fossil 7 THISDIR=${PWD} 7 THISDIR=${PWD} 8 8 9 if uname -a | grep -i nexenta &>/dev/null; then 9 if uname -a | grep -i nexenta &>/dev/null; then 10 # Assume NexentaOS/GnuSolaris: 10 # Assume NexentaOS/GnuSolaris: 11 DEB_PLATFORM=nexenta < 12 DEB_ARCH_NAME=solaris-i386 11 DEB_ARCH_NAME=solaris-i386 13 DEB_ARCH_PKGDEPENDS="sunwcsl" # for -lsocket 12 DEB_ARCH_PKGDEPENDS="sunwcsl" # for -lsocket 14 else 13 else 15 DEB_PLATFORM=${DEB_PLATFORM-ubuntu-gutsy} < 16 DEB_ARCH_NAME=i386 | 14 DEB_ARCH_NAME=$(dpkg --print-architecture) 17 fi 15 fi 18 16 19 SRCDIR=$(cd ..; pwd) 17 SRCDIR=$(cd ..; pwd) 20 test -e ${SRCDIR}/fossil || { 18 test -e ${SRCDIR}/fossil || { 21 echo "This script must be run from a BUILT copy of the source tree." 19 echo "This script must be run from a BUILT copy of the source tree." 22 exit 1 20 exit 1 23 } 21 } ................................................................................................................................................................................ 39 37 40 38 41 rm -fr DEBIAN 39 rm -fr DEBIAN 42 mkdir DEBIAN 40 mkdir DEBIAN 43 41 44 PACKAGE_VERSION=$(date +%Y.%m.%d) 42 PACKAGE_VERSION=$(date +%Y.%m.%d) 45 PACKAGE_DEB_VERSION=${PACKAGE_VERSION}-${DEB_REV} 43 PACKAGE_DEB_VERSION=${PACKAGE_VERSION}-${DEB_REV} 46 DEBFILE=${THISDIR}/${PACKAGE_DEBNAME}-${PACKAGE_DEB_VERSION}-dev-${DEB_ARCH_NAME | 44 DEBFILE=${THISDIR}/${PACKAGE_DEBNAME}-${PACKAGE_DEB_VERSION}-dev-${DEB_ARCH_NAME 47 PACKAGE_TIME=$(/bin/date) 45 PACKAGE_TIME=$(/bin/date) 48 46 49 rm -f ${DEBFILE} 47 rm -f ${DEBFILE} 50 echo "Creating .deb package [${DEBFILE}]..." 48 echo "Creating .deb package [${DEBFILE}]..." 51 49 52 echo "Generating md5 sums..." 50 echo "Generating md5 sums..." 53 find ${DEBLOCALPREFIX} -type f -exec md5sum {} \; > DEBIAN/md5sums 51 find ${DEBLOCALPREFIX} -type f -exec md5sum {} \; > DEBIAN/md5sums 54 52 55 true && { 53 true && { 56 echo "Generating Debian-specific files..." 54 echo "Generating Debian-specific files..." 57 COPYRIGHT=${DEBLOCALPREFIX}/share/doc/${PACKAGE_DEBNAME}/copyright 55 COPYRIGHT=${DEBLOCALPREFIX}/share/doc/${PACKAGE_DEBNAME}/copyright 58 cat <<EOF > ${COPYRIGHT} 56 cat <<EOF > ${COPYRIGHT} 59 This package was created by stephan beal <stephan@s11n.net> | 57 This package was created by fossil-scm <fossil-dev@lists.fossil-scm.org> 60 on ${PACKAGE_TIME}. 58 on ${PACKAGE_TIME}. 61 59 62 The original sources for fossil can be downloaded for free from: 60 The original sources for fossil can be downloaded for free from: 63 61 64 http://www.fossil-scm.org/ 62 http://www.fossil-scm.org/ 65 63 66 fossil is released under the terms of the GNU General Public License. 64 fossil is released under the terms of the GNU General Public License. ................................................................................................................................................................................ 72 CHANGELOG=${DEBLOCALPREFIX}/share/doc/${PACKAGE_DEBNAME}/changelog.gz 70 CHANGELOG=${DEBLOCALPREFIX}/share/doc/${PACKAGE_DEBNAME}/changelog.gz 73 cat <<EOF | gzip -c > ${CHANGELOG} 71 cat <<EOF | gzip -c > ${CHANGELOG} 74 ${PACKAGE_DEBNAME} ${PACKAGE_DEB_VERSION}; urgency=low 72 ${PACKAGE_DEBNAME} ${PACKAGE_DEB_VERSION}; urgency=low 75 73 76 This release has no changes over the core source distribution. It has 74 This release has no changes over the core source distribution. It has 77 simply been Debianized. 75 simply been Debianized. 78 76 79 Packaged by stephan beal <stephan@s11n.net> on | 77 Packaged by fossil-dev <fossil-dev@lists.fossil-scm.org> on 80 ${PACKAGE_TIME}. 78 ${PACKAGE_TIME}. 81 79 82 EOF 80 EOF 83 81 84 } 82 } 85 83 86 84 87 true && { 85 true && { 88 CONTROL=DEBIAN/control 86 CONTROL=DEBIAN/control 89 echo "Generating ${CONTROL}..." 87 echo "Generating ${CONTROL}..." 90 cat <<EOF > ${CONTROL} 88 cat <<EOF > ${CONTROL} 91 Package: ${PACKAGE_DEBNAME} 89 Package: ${PACKAGE_DEBNAME} 92 Section: devel | 90 Section: vcs 93 Priority: optional 91 Priority: optional 94 Maintainer: stephan beal <stephan@s11n.net> | 92 Maintainer: fossil-dev <fossil-dev@lists.fossil-scm.org> 95 Architecture: ${DEB_ARCH_NAME} 93 Architecture: ${DEB_ARCH_NAME} 96 Depends: libc6-dev ${DEB_ARCH_PKGDEPENDS+, }${DEB_ARCH_PKGDEPENDS} | 94 Depends: libc6 ${DEB_ARCH_PKGDEPENDS+, }${DEB_ARCH_PKGDEPENDS} 97 Version: ${PACKAGE_DEB_VERSION} 95 Version: ${PACKAGE_DEB_VERSION} 98 Description: Fossil is a unique SCM (Software Configuration Management) system. 96 Description: Fossil is a unique SCM (Software Configuration Management) system. 99 This package contains the Fossil binary for *buntu/Debian systems. 97 This package contains the Fossil binary for *buntu/Debian systems. 100 Fossil is a unique SCM program which supports distributed source control 98 Fossil is a unique SCM program which supports distributed source control 101 management using local repositories, access over HTTP CGI, or using the 99 management using local repositories, access over HTTP CGI, or using the 102 built-in HTTP server. It has a built-in wiki, file browsing, etc. 100 built-in HTTP server. It has a built-in wiki, file browsing, etc. 103 Fossil home page: http://fossil-scm.org 101 Fossil home page: http://fossil-scm.org

Changes to src/add.c

42 ** its associated journals 42 ** its associated journals 43 */ 43 */ 44 static const char *azName[] = { 44 static const char *azName[] = { 45 "_FOSSIL_", 45 "_FOSSIL_", 46 "_FOSSIL_-journal", 46 "_FOSSIL_-journal", 47 "_FOSSIL_-wal", 47 "_FOSSIL_-wal", 48 "_FOSSIL_-shm", 48 "_FOSSIL_-shm", > 49 ".fslckout", > 50 ".fslckout-journal", > 51 ".fslckout-wal", > 52 ".fslckout-shm", > 53 > 54 /* The use of ".fos" as the name of the checkout database is > 55 ** deprecated. Use ".fslckout" instead. At some point, the following > 56 ** entries should be removed. 2012-02-04 */ 49 ".fos", 57 ".fos", 50 ".fos-journal", 58 ".fos-journal", 51 ".fos-wal", 59 ".fos-wal", 52 ".fos-shm", 60 ".fos-shm", 53 }; 61 }; 54 62 55 /* Names of auxiliary files generated by SQLite when the "manifest" 63 /* Names of auxiliary files generated by SQLite when the "manifest" ................................................................................................................................................................................ 497 ** Move or rename one or more files or directories within the repository tree. 505 ** Move or rename one or more files or directories within the repository tree. 498 ** You can either rename a file or directory or move it to another subdirectory. 506 ** You can either rename a file or directory or move it to another subdirectory. 499 ** 507 ** 500 ** This command does NOT rename or move the files on disk. This command merely 508 ** This command does NOT rename or move the files on disk. This command merely 501 ** records the fact that filenames have changed so that appropriate notations 509 ** records the fact that filenames have changed so that appropriate notations 502 ** can be made at the next commit/checkin. 510 ** can be made at the next commit/checkin. 503 ** 511 ** 504 ** | 512 ** See also: changes, status 505 ** SUMMARY: fossil mv|rename OLDNAME NEWNAME < 506 ** or: fossil mv|rename OLDNAME... DIR < 507 */ 513 */ 508 void mv_cmd(void){ 514 void mv_cmd(void){ 509 int i; 515 int i; 510 int vid; 516 int vid; 511 char *zDest; 517 char *zDest; 512 Blob dest; 518 Blob dest; 513 Stmt q; 519 Stmt q;

Changes to src/branch.c

144 prompt_user("unable to sign manifest. continue (y/N)? ", &ans); 144 prompt_user("unable to sign manifest. continue (y/N)? ", &ans); 145 if( blob_str(&ans)[0]!='y' ){ 145 if( blob_str(&ans)[0]!='y' ){ 146 db_end_transaction(1); 146 db_end_transaction(1); 147 fossil_exit(1); 147 fossil_exit(1); 148 } 148 } 149 } 149 } 150 150 151 brid = content_put(&branch); | 151 brid = content_put_ex(&branch, 0, 0, 0, isPrivate); 152 if( brid==0 ){ 152 if( brid==0 ){ 153 fossil_panic("trouble committing manifest: %s", g.zErrMsg); 153 fossil_panic("trouble committing manifest: %s", g.zErrMsg); 154 } 154 } 155 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); 155 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid); 156 if( manifest_crosslink(brid, &branch)==0 ){ 156 if( manifest_crosslink(brid, &branch)==0 ){ 157 fossil_panic("unable to install new manifest"); 157 fossil_panic("unable to install new manifest"); 158 } 158 }

Changes to src/cgi.c

913 } 913 } 914 914 915 /* If no match is found and the name begins with an upper-case 915 /* If no match is found and the name begins with an upper-case 916 ** letter, then check to see if there is an environment variable 916 ** letter, then check to see if there is an environment variable 917 ** with the given name. 917 ** with the given name. 918 */ 918 */ 919 if( fossil_isupper(zName[0]) ){ 919 if( fossil_isupper(zName[0]) ){ 920 const char *zValue = getenv(zName); | 920 const char *zValue = fossil_getenv(zName); 921 if( zValue ){ 921 if( zValue ){ 922 cgi_set_parameter_nocopy(zName, zValue); 922 cgi_set_parameter_nocopy(zName, zValue); 923 CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue)); 923 CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue)); 924 return zValue; 924 return zValue; 925 } 925 } 926 } 926 } 927 CGIDEBUG(("no-match [%s]\n", zName)); 927 CGIDEBUG(("no-match [%s]\n", zName));

Changes to src/checkin.c

178 ** 178 ** 179 ** See also: changes, extra, ls 179 ** See also: changes, extra, ls 180 */ 180 */ 181 void status_cmd(void){ 181 void status_cmd(void){ 182 int vid; 182 int vid; 183 db_must_be_within_tree(); 183 db_must_be_within_tree(); 184 /* 012345678901234 */ 184 /* 012345678901234 */ 185 fossil_print("repository: %s\n", db_lget("repository","")); | 185 fossil_print("repository: %s\n", db_repository_filename()); 186 fossil_print("local-root: %s\n", g.zLocalRoot); 186 fossil_print("local-root: %s\n", g.zLocalRoot); 187 vid = db_lget_int("checkout", 0); 187 vid = db_lget_int("checkout", 0); 188 if( vid ){ 188 if( vid ){ 189 show_common_info(vid, "checkout:", 1, 1); 189 show_common_info(vid, "checkout:", 1, 1); 190 } 190 } 191 db_record_repository_filename(0); 191 db_record_repository_filename(0); 192 changes_cmd(); 192 changes_cmd(); ................................................................................................................................................................................ 454 "# repositories.\n" 454 "# repositories.\n" 455 "#\n", -1 455 "#\n", -1 456 ); 456 ); 457 } 457 } 458 status_report(&text, "# ", 1, 0); 458 status_report(&text, "# ", 1, 0); 459 zEditor = db_get("editor", 0); 459 zEditor = db_get("editor", 0); 460 if( zEditor==0 ){ 460 if( zEditor==0 ){ 461 zEditor = getenv("VISUAL"); | 461 zEditor = fossil_getenv("VISUAL"); 462 } 462 } 463 if( zEditor==0 ){ 463 if( zEditor==0 ){ 464 zEditor = getenv("EDITOR"); | 464 zEditor = fossil_getenv("EDITOR"); 465 } 465 } 466 if( zEditor==0 ){ 466 if( zEditor==0 ){ 467 blob_append(&text, 467 blob_append(&text, 468 "#\n" 468 "#\n" 469 "# Since no default text editor is set using EDITOR or VISUAL\n" 469 "# Since no default text editor is set using EDITOR or VISUAL\n" 470 "# environment variables or the \"fossil set editor\" command,\n" 470 "# environment variables or the \"fossil set editor\" command,\n" 471 "# and because no check-in comment was specified using the \"-m\"\n" 471 "# and because no check-in comment was specified using the \"-m\"\n"

Changes to src/clone.c

17 ** 17 ** 18 ** This file contains code used to clone a repository 18 ** This file contains code used to clone a repository 19 */ 19 */ 20 #include "config.h" 20 #include "config.h" 21 #include "clone.h" 21 #include "clone.h" 22 #include <assert.h> 22 #include <assert.h> 23 23 > 24 /* > 25 ** If there are public BLOBs that deltas from private BLOBs, then > 26 ** undeltify the public BLOBs so that the private BLOBs may be safely > 27 ** deleted. > 28 */ > 29 void fix_private_blob_dependencies(int showWarning){ > 30 Bag toUndelta; > 31 Stmt q; > 32 int rid; > 33 > 34 /* Careful: We are about to delete all BLOB entries that are private. > 35 ** So make sure that any no public BLOBs are deltas from a private BLOB. > 36 ** Otherwise after the deletion, we won't be able to recreate the public > 37 ** BLOBs. > 38 */ > 39 db_prepare(&q, > 40 "SELECT " > 41 " rid, (SELECT uuid FROM blob WHERE rid=delta.rid)," > 42 " srcid, (SELECT uuid FROM blob WHERE rid=delta.srcid)" > 43 " FROM delta" > 44 " WHERE srcid in private AND rid NOT IN private" > 45 ); > 46 bag_init(&toUndelta); > 47 while( db_step(&q)==SQLITE_ROW ){ > 48 int rid = db_column_int(&q, 0); > 49 const char *zId = db_column_text(&q, 1); > 50 int srcid = db_column_int(&q, 2); > 51 const char *zSrc = db_column_text(&q, 3); > 52 if( showWarning ){ > 53 fossil_warning( > 54 "public artifact %S (%d) is a delta from private artifact %S (%d)", > 55 zId, rid, zSrc, srcid > 56 ); > 57 } > 58 bag_insert(&toUndelta, rid); > 59 } > 60 db_finalize(&q); > 61 while( (rid = bag_first(&toUndelta))>0 ){ > 62 content_undelta(rid); > 63 bag_remove(&toUndelta, rid); > 64 } > 65 bag_clear(&toUndelta); > 66 } > 67 > 68 /* > 69 ** Delete all private content from a repository. > 70 */ > 71 void delete_private_content(void){ > 72 fix_private_blob_dependencies(1); > 73 db_multi_exec( > 74 "DELETE FROM blob WHERE rid IN private;" > 75 "DELETE FROM delta wHERE rid IN private;" > 76 "DELETE FROM private;" > 77 ); > 78 } 24 79 25 80 26 /* 81 /* 27 ** COMMAND: clone 82 ** COMMAND: clone 28 ** 83 ** 29 ** Usage: %fossil clone ?OPTIONS? URL FILENAME 84 ** Usage: %fossil clone ?OPTIONS? URL FILENAME 30 ** 85 ** ................................................................................................................................................................................ 70 db_multi_exec( 125 db_multi_exec( 71 "REPLACE INTO config(name,value,mtime)" 126 "REPLACE INTO config(name,value,mtime)" 72 " VALUES('server-code', lower(hex(randomblob(20))),now());" 127 " VALUES('server-code', lower(hex(randomblob(20))),now());" 73 "REPLACE INTO config(name,value,mtime)" 128 "REPLACE INTO config(name,value,mtime)" 74 " VALUES('last-sync-url', '%q',now());", 129 " VALUES('last-sync-url', '%q',now());", 75 g.urlCanonical 130 g.urlCanonical 76 ); 131 ); 77 db_multi_exec( | 132 if( !bPrivate ) delete_private_content(); 78 "DELETE FROM blob WHERE rid IN private;" < 79 "DELETE FROM delta wHERE rid IN private;" < 80 "DELETE FROM private;" < 81 ); < 82 shun_artifacts(); 133 shun_artifacts(); 83 db_create_default_users(1, zDefaultUser); 134 db_create_default_users(1, zDefaultUser); 84 if( zDefaultUser ){ 135 if( zDefaultUser ){ 85 g.zLogin = zDefaultUser; 136 g.zLogin = zDefaultUser; 86 }else{ 137 }else{ 87 g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'"); 138 g.zLogin = db_text(0, "SELECT login FROM user WHERE cap LIKE '%%s%%'"); 88 } 139 }

Changes to src/configure.c

31 */ 31 */ 32 #define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */ 32 #define CONFIGSET_SKIN 0x000001 /* WWW interface appearance */ 33 #define CONFIGSET_TKT 0x000002 /* Ticket configuration */ 33 #define CONFIGSET_TKT 0x000002 /* Ticket configuration */ 34 #define CONFIGSET_PROJ 0x000004 /* Project name */ 34 #define CONFIGSET_PROJ 0x000004 /* Project name */ 35 #define CONFIGSET_SHUN 0x000008 /* Shun settings */ 35 #define CONFIGSET_SHUN 0x000008 /* Shun settings */ 36 #define CONFIGSET_USER 0x000010 /* The USER table */ 36 #define CONFIGSET_USER 0x000010 /* The USER table */ 37 #define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */ 37 #define CONFIGSET_ADDR 0x000020 /* The CONCEALED table */ > 38 #define CONFIGSET_XFER 0x000040 /* Transfer configuration */ 38 39 39 #define CONFIGSET_ALL 0x0000ff /* Everything */ 40 #define CONFIGSET_ALL 0x0000ff /* Everything */ 40 41 41 #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ 42 #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ 42 #define CONFIGSET_OLDFORMAT 0x200000 /* Use the legacy format */ 43 #define CONFIGSET_OLDFORMAT 0x200000 /* Use the legacy format */ 43 44 44 #endif /* INTERFACE */ 45 #endif /* INTERFACE */ ................................................................................................................................................................................ 53 } aGroupName[] = { 54 } aGroupName[] = { 54 { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" }, 55 { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" }, 55 { "/project", CONFIGSET_PROJ, "Project name and description" }, 56 { "/project", CONFIGSET_PROJ, "Project name and description" }, 56 { "/skin", CONFIGSET_SKIN, "Web interface apparance settings" }, 57 { "/skin", CONFIGSET_SKIN, "Web interface apparance settings" }, 57 { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" }, 58 { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" }, 58 { "/ticket", CONFIGSET_TKT, "Ticket setup", }, 59 { "/ticket", CONFIGSET_TKT, "Ticket setup", }, 59 { "/user", CONFIGSET_USER, "Users and privilege settings" }, 60 { "/user", CONFIGSET_USER, "Users and privilege settings" }, > 61 { "/xfer", CONFIGSET_XFER, "Transfer setup", }, 60 { "/all", CONFIGSET_ALL, "All of the above" }, 62 { "/all", CONFIGSET_ALL, "All of the above" }, 61 }; 63 }; 62 64 63 65 64 /* 66 /* 65 ** The following is a list of settings that we are willing to 67 ** The following is a list of settings that we are willing to 66 ** transfer. 68 ** transfer. ................................................................................................................................................................................ 83 { "manifest", CONFIGSET_PROJ }, 85 { "manifest", CONFIGSET_PROJ }, 84 { "ignore-glob", CONFIGSET_PROJ }, 86 { "ignore-glob", CONFIGSET_PROJ }, 85 { "crnl-glob", CONFIGSET_PROJ }, 87 { "crnl-glob", CONFIGSET_PROJ }, 86 { "empty-dirs", CONFIGSET_PROJ }, 88 { "empty-dirs", CONFIGSET_PROJ }, 87 { "allow-symlinks", CONFIGSET_PROJ }, 89 { "allow-symlinks", CONFIGSET_PROJ }, 88 { "index-page", CONFIGSET_SKIN }, 90 { "index-page", CONFIGSET_SKIN }, 89 #ifdef FOSSIL_ENABLE_TCL 91 #ifdef FOSSIL_ENABLE_TCL 90 { "tcl", CONFIGSET_SKIN|CONFIGSET_TKT }, | 92 { "tcl", CONFIGSET_SKIN|CONFIGSET_TKT|CONFIGSET_XFER }, 91 #endif 93 #endif 92 { "timeline-block-markup", CONFIGSET_SKIN }, 94 { "timeline-block-markup", CONFIGSET_SKIN }, 93 { "timeline-max-comment", CONFIGSET_SKIN }, 95 { "timeline-max-comment", CONFIGSET_SKIN }, 94 { "ticket-table", CONFIGSET_TKT }, 96 { "ticket-table", CONFIGSET_TKT }, 95 { "ticket-common", CONFIGSET_TKT }, 97 { "ticket-common", CONFIGSET_TKT }, > 98 { "ticket-change", CONFIGSET_TKT }, 96 { "ticket-newpage", CONFIGSET_TKT }, 99 { "ticket-newpage", CONFIGSET_TKT }, 97 { "ticket-viewpage", CONFIGSET_TKT }, 100 { "ticket-viewpage", CONFIGSET_TKT }, 98 { "ticket-editpage", CONFIGSET_TKT }, 101 { "ticket-editpage", CONFIGSET_TKT }, 99 { "ticket-reportlist", CONFIGSET_TKT }, 102 { "ticket-reportlist", CONFIGSET_TKT }, 100 { "ticket-report-template", CONFIGSET_TKT }, 103 { "ticket-report-template", CONFIGSET_TKT }, 101 { "ticket-key-template", CONFIGSET_TKT }, 104 { "ticket-key-template", CONFIGSET_TKT }, 102 { "ticket-title-expr", CONFIGSET_TKT }, 105 { "ticket-title-expr", CONFIGSET_TKT }, 103 { "ticket-closed-expr", CONFIGSET_TKT }, 106 { "ticket-closed-expr", CONFIGSET_TKT }, 104 { "@reportfmt", CONFIGSET_TKT }, 107 { "@reportfmt", CONFIGSET_TKT }, 105 { "@user", CONFIGSET_USER }, 108 { "@user", CONFIGSET_USER }, 106 { "@concealed", CONFIGSET_ADDR }, 109 { "@concealed", CONFIGSET_ADDR }, 107 { "@shun", CONFIGSET_SHUN }, 110 { "@shun", CONFIGSET_SHUN }, > 111 { "xfer-common-script", CONFIGSET_XFER }, > 112 { "xfer-push-script", CONFIGSET_XFER }, 108 }; 113 }; 109 static int iConfig = 0; 114 static int iConfig = 0; 110 115 111 /* 116 /* 112 ** Return name of first configuration property matching the given mask. 117 ** Return name of first configuration property matching the given mask. 113 */ 118 */ 114 const char *configure_first_name(int iMask){ 119 const char *configure_first_name(int iMask){ ................................................................................................................................................................................ 778 ** 783 ** 779 ** Synchronize configuration changes in the local repository with 784 ** Synchronize configuration changes in the local repository with 780 ** the remote repository at URL. 785 ** the remote repository at URL. 781 ** 786 ** 782 ** Options: 787 ** Options: 783 ** -R|--repository FILE Extract info from repository FILE 788 ** -R|--repository FILE Extract info from repository FILE 784 ** 789 ** 785 ** See also: set | 790 ** See also: settings, unset 786 */ 791 */ 787 void configuration_cmd(void){ 792 void configuration_cmd(void){ 788 int n; 793 int n; 789 const char *zMethod; 794 const char *zMethod; 790 if( g.argc<3 ){ 795 if( g.argc<3 ){ 791 usage("export|import|merge|pull|reset ..."); 796 usage("export|import|merge|pull|reset ..."); 792 } 797 }

Changes to src/content.c

311 ** 311 ** 312 ** Extract an artifact by its SHA1 hash and write the results on 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 313 ** standard output, or if the optional 4th argument is given, in 314 ** the named output file. 314 ** the named output file. 315 ** 315 ** 316 ** Options: 316 ** Options: 317 ** -R|--repository FILE Extract artifacts from repository FILE 317 ** -R|--repository FILE Extract artifacts from repository FILE > 318 ** > 319 ** See also: finfo 318 */ 320 */ 319 void artifact_cmd(void){ 321 void artifact_cmd(void){ 320 int rid; 322 int rid; 321 Blob content; 323 Blob content; 322 const char *zFile; 324 const char *zFile; 323 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); 325 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); 324 if( g.argc!=4 && g.argc!=3 ) usage("ARTIFACT-ID ?FILENAME? ?OPTIONS?"); 326 if( g.argc!=4 && g.argc!=3 ) usage("ARTIFACT-ID ?FILENAME? ?OPTIONS?"); 325 zFile = g.argc==4 ? g.argv[3] : "-"; 327 zFile = g.argc==4 ? g.argv[3] : "-"; 326 rid = name_to_rid(g.argv[2]); 328 rid = name_to_rid(g.argv[2]); > 329 if( rid==0 ){ > 330 fossil_fatal("%s",g.zErrMsg); > 331 } 327 content_get(rid, &content); 332 content_get(rid, &content); 328 blob_write_to_file(&content, zFile); 333 blob_write_to_file(&content, zFile); 329 } 334 } 330 335 331 /* 336 /* 332 ** COMMAND: test-content-rawget 337 ** COMMAND: test-content-rawget 333 ** 338 **

Changes to src/cson_amalgamation.c

1425 1425 1426 1426 1427 /** 1427 /** 1428 Type IDs corresponding to JavaScript/JSON types. 1428 Type IDs corresponding to JavaScript/JSON types. 1429 */ 1429 */ 1430 enum cson_type_id { 1430 enum cson_type_id { 1431 /** 1431 /** 1432 The special "null" value constant. | 1432 The special "undefined" value constant. 1433 1433 1434 Its value must be 0 for internal reasons. 1434 Its value must be 0 for internal reasons. 1435 */ 1435 */ 1436 CSON_TYPE_UNDEF = 0, 1436 CSON_TYPE_UNDEF = 0, 1437 /** 1437 /** 1438 The special "null" value constant. 1438 The special "null" value constant. 1439 */ 1439 */ ................................................................................................................................................................................ 1595 static const cson_value cson_value_bool_empty = { &cson_value_api_bool, NULL, 0 1595 static const cson_value cson_value_bool_empty = { &cson_value_api_bool, NULL, 0 1596 static const cson_value cson_value_integer_empty = { &cson_value_api_integer, NU 1596 static const cson_value cson_value_integer_empty = { &cson_value_api_integer, NU 1597 static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL 1597 static const cson_value cson_value_double_empty = { &cson_value_api_double, NULL 1598 static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL 1598 static const cson_value cson_value_string_empty = { &cson_value_api_string, NULL 1599 static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 1599 static const cson_value cson_value_array_empty = { &cson_value_api_array, NULL, 1600 static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL 1600 static const cson_value cson_value_object_empty = { &cson_value_api_object, NULL 1601 1601 > 1602 /** > 1603 Strings are allocated as an instances of this class with N+1 > 1604 trailing bytes, where N is the length of the string being > 1605 allocated. To convert a cson_string to c-string we simply increment > 1606 the cson_string pointer. To do the opposite we use (cstr - > 1607 sizeof(cson_string)). Zero-length strings are a special case > 1608 handled by a couple of the cson_string functions. > 1609 */ 1602 struct cson_string 1610 struct cson_string 1603 { 1611 { 1604 unsigned int length; 1612 unsigned int length; 1605 }; 1613 }; 1606 #define cson_string_empty_m {0/*length*/} 1614 #define cson_string_empty_m {0/*length*/} 1607 static const cson_string cson_string_empty = cson_string_empty_m; 1615 static const cson_string cson_string_empty = cson_string_empty_m; 1608 1616 1609 1617 1610 1618 1611 #define CSON_CAST(T,V) ((T*)((V)->value)) 1619 #define CSON_CAST(T,V) ((T*)((V)->value)) 1612 #define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)) 1620 #define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)) > 1621 > 1622 #if CSON_VOID_PTR_IS_BIG > 1623 # define CSON_INT(V) ((cson_int_t*)(&((V)->value))) > 1624 #else 1613 #define CSON_INT(V) ((cson_int_t*)(V)->value) | 1625 # define CSON_INT(V) ((cson_int_t*)(V)->value) > 1626 #endif > 1627 1614 #define CSON_DBL(V) CSON_CAST(cson_double_t,(V)) 1628 #define CSON_DBL(V) CSON_CAST(cson_double_t,(V)) 1615 #define CSON_STR(V) CSON_CAST(cson_string,(V)) 1629 #define CSON_STR(V) CSON_CAST(cson_string,(V)) 1616 #define CSON_OBJ(V) CSON_CAST(cson_object,(V)) 1630 #define CSON_OBJ(V) CSON_CAST(cson_object,(V)) 1617 #define CSON_ARRAY(V) CSON_CAST(cson_array,(V)) 1631 #define CSON_ARRAY(V) CSON_CAST(cson_array,(V)) 1618 1632 1619 /** 1633 /** 1620 1634 ................................................................................................................................................................................ 1666 { &cson_value_api_undef, NULL, 0 }, /* UNDEF */ 1680 { &cson_value_api_undef, NULL, 0 }, /* UNDEF */ 1667 { &cson_value_api_null, NULL, 0 }, /* NULL */ 1681 { &cson_value_api_null, NULL, 0 }, /* NULL */ 1668 { &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */ 1682 { &cson_value_api_bool, &CSON_EMPTY_HOLDER.trueValue, 0 }, /* TRUE */ 1669 { &cson_value_api_bool, NULL, 0 }, /* FALSE */ 1683 { &cson_value_api_bool, NULL, 0 }, /* FALSE */ 1670 { &cson_value_api_integer, NULL, 0 }, /* INT_0 */ 1684 { &cson_value_api_integer, NULL, 0 }, /* INT_0 */ 1671 { &cson_value_api_double, NULL, 0 }, /* DBL_0 */ 1685 { &cson_value_api_double, NULL, 0 }, /* DBL_0 */ 1672 { &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */ 1686 { &cson_value_api_string, &CSON_EMPTY_HOLDER.stringValue, 0 }, /* STR_EMPTY */ 1673 { 0, NULL, 0 } | 1687 { NULL, NULL, 0 } 1674 }; 1688 }; 1675 1689 1676 1690 1677 /** 1691 /** 1678 Returns non-0 (true) if m is one of our special 1692 Returns non-0 (true) if m is one of our special 1679 "built-in" values, e.g. from CSON_SPECIAL_VALUES and some 1693 "built-in" values, e.g. from CSON_SPECIAL_VALUES and some 1680 "empty" values. 1694 "empty" values. ................................................................................................................................................................................ 1914 /** 1928 /** 1915 Fetches v's string value as a non-const string. 1929 Fetches v's string value as a non-const string. 1916 1930 1917 cson_strings are supposed to be immutable, but this form provides 1931 cson_strings are supposed to be immutable, but this form provides 1918 access to the immutable bits, which are v->length bytes long. A 1932 access to the immutable bits, which are v->length bytes long. A 1919 length-0 string is returned as NULL from here, as opposed to 1933 length-0 string is returned as NULL from here, as opposed to 1920 "". (This is a side-effect of the string allocation mechanism.) 1934 "". (This is a side-effect of the string allocation mechanism.) 1921 Returns NULL if !v. | 1935 Returns NULL if !v or if v is the internal empty-string singleton. 1922 */ 1936 */ 1923 static char * cson_string_str(cson_string *v) 1937 static char * cson_string_str(cson_string *v) 1924 { 1938 { 1925 /* 1939 /* 1926 See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thr 1940 See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thr 1927 */ 1941 */ 1928 #if 1 1942 #if 1 ................................................................................................................................................................................ 1946 { 1960 { 1947 /* 1961 /* 1948 See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thr 1962 See http://groups.google.com/group/comp.lang.c.moderated/browse_thread/thr 1949 */ 1963 */ 1950 #if 1 1964 #if 1 1951 if( ! v ) return NULL; 1965 if( ! v ) return NULL; 1952 else if( v == &CSON_EMPTY_HOLDER.stringValue ) return ""; 1966 else if( v == &CSON_EMPTY_HOLDER.stringValue ) return ""; > 1967 else { > 1968 assert((0 < v->length) && "How do we have a non-singleton empty string?" 1953 else return (char *)((unsigned char *)(v+1)); | 1969 return (char const *)((unsigned char const *)(v+1)); > 1970 } 1954 #else 1971 #else 1955 return (NULL == v) 1972 return (NULL == v) 1956 ? NULL 1973 ? NULL 1957 : (v->length 1974 : (v->length 1958 ? (char const *) ((unsigned char const *)(v+1)) 1975 ? (char const *) ((unsigned char const *)(v+1)) 1959 : ""); 1976 : ""); 1960 #endif 1977 #endif ................................................................................................................................................................................ 2026 cson_value * key; 2043 cson_value * key; 2027 cson_value * value; 2044 cson_value * value; 2028 }; 2045 }; 2029 #define cson_kvp_empty_m {NULL,NULL} 2046 #define cson_kvp_empty_m {NULL,NULL} 2030 static const cson_kvp cson_kvp_empty = cson_kvp_empty_m; 2047 static const cson_kvp cson_kvp_empty = cson_kvp_empty_m; 2031 2048 2032 /** @def CSON_OBJECT_PROPS_SORT 2049 /** @def CSON_OBJECT_PROPS_SORT > 2050 > 2051 Don't use this - it has not been updated to account for internal > 2052 changes in cson_object. 2033 2053 2034 If CSON_OBJECT_PROPS_SORT is set to a true value then 2054 If CSON_OBJECT_PROPS_SORT is set to a true value then 2035 qsort() and bsearch() are used to sort (upon insertion) 2055 qsort() and bsearch() are used to sort (upon insertion) 2036 and search cson_object::kvp property lists. This costs us 2056 and search cson_object::kvp property lists. This costs us 2037 a re-sort on each insertion but searching is O(log n) 2057 a re-sort on each insertion but searching is O(log n) 2038 average/worst case (and O(1) best-case). 2058 average/worst case (and O(1) best-case). 2039 2059 ................................................................................................................................................................................ 2198 # define LIST_T cson_kvp_list 2218 # define LIST_T cson_kvp_list 2199 # define VALUE_T cson_kvp * 2219 # define VALUE_T cson_kvp * 2200 # define VALUE_T_IS_PTR 1 2220 # define VALUE_T_IS_PTR 1 2201 #else 2221 #else 2202 #endif 2222 #endif 2203 2223 2204 /** 2224 /** 2205 Allocates a new value of the specified type ownership of it to the | 2225 Allocates a new value of the specified type. Ownership is 2206 caller. It must eventually be destroyed, by the caller or its | 2226 transfered to the caller, who must eventually free it by passing it 2207 owning container, by passing it to cson_value_free() or transfering < 2208 ownership to a container. | 2227 to cson_value_free() or transfering ownership to a container. 2209 2228 2210 extra is only valid for type CSON_TYPE_STRING, and must be the length 2229 extra is only valid for type CSON_TYPE_STRING, and must be the length 2211 of the string to allocate + 1 byte (for the NUL). 2230 of the string to allocate + 1 byte (for the NUL). 2212 2231 2213 The returned value->api member will be set appropriately and 2232 The returned value->api member will be set appropriately and 2214 val->value will be set to point to the memory allocated to hold the 2233 val->value will be set to point to the memory allocated to hold the 2215 native value type. Use the internal CSON_CAST() family of macros to 2234 native value type. Use the internal CSON_CAST() family of macros to 2216 convert them. | 2235 convert the cson_values to their corresponding native > 2236 representation. 2217 2237 2218 Returns NULL on allocation error. 2238 Returns NULL on allocation error. 2219 2239 2220 @see cson_value_new_array() 2240 @see cson_value_new_array() 2221 @see cson_value_new_object() 2241 @see cson_value_new_object() 2222 @see cson_value_new_string() 2242 @see cson_value_new_string() 2223 @see cson_value_new_integer() 2243 @see cson_value_new_integer() 2224 @see cson_value_new_double() 2244 @see cson_value_new_double() 2225 @see cson_value_new_bool() 2245 @see cson_value_new_bool() 2226 @see cson_value_free() 2246 @see cson_value_free() 2227 */ 2247 */ 2228 static cson_value * cson_value_new(cson_type_id t, size_t extra); | 2248 static cson_value * cson_value_new(cson_type_id t, size_t extra) 2229 < 2230 cson_value * cson_value_new(cson_type_id t, size_t extra) < 2231 { 2249 { 2232 static const size_t vsz = sizeof(cson_value); 2250 static const size_t vsz = sizeof(cson_value); 2233 const size_t sz = vsz + extra; 2251 const size_t sz = vsz + extra; 2234 size_t tx = 0; 2252 size_t tx = 0; 2235 cson_value def = cson_value_undef; 2253 cson_value def = cson_value_undef; 2236 cson_value * v = NULL; 2254 cson_value * v = NULL; 2237 char const * reason = "cson_value_new"; 2255 char const * reason = "cson_value_new"; ................................................................................................................................................................................ 2248 def = cson_value_double_empty; 2266 def = cson_value_double_empty; 2249 tx = sizeof(cson_double_t); 2267 tx = sizeof(cson_double_t); 2250 reason = "cson_value:double"; 2268 reason = "cson_value:double"; 2251 break; 2269 break; 2252 case CSON_TYPE_INTEGER: 2270 case CSON_TYPE_INTEGER: 2253 assert( 0 == extra ); 2271 assert( 0 == extra ); 2254 def = cson_value_integer_empty; 2272 def = cson_value_integer_empty; > 2273 #if !CSON_VOID_PTR_IS_BIG 2255 tx = sizeof(cson_int_t); 2274 tx = sizeof(cson_int_t); > 2275 #endif 2256 reason = "cson_value:int"; 2276 reason = "cson_value:int"; 2257 break; 2277 break; 2258 case CSON_TYPE_STRING: 2278 case CSON_TYPE_STRING: 2259 assert( 0 != extra ); 2279 assert( 0 != extra ); 2260 def = cson_value_string_empty; 2280 def = cson_value_string_empty; 2261 tx = sizeof(cson_string); 2281 tx = sizeof(cson_string); 2262 reason = "cson_value:string"; 2282 reason = "cson_value:string"; ................................................................................................................................................................................ 2279 memset(v+1, 0, tx + extra); 2299 memset(v+1, 0, tx + extra); 2280 v->value = (void *)(v+1); 2300 v->value = (void *)(v+1); 2281 } 2301 } 2282 } 2302 } 2283 return v; 2303 return v; 2284 } 2304 } 2285 2305 2286 < 2287 void cson_value_free(cson_value *v) 2306 void cson_value_free(cson_value *v) 2288 { 2307 { 2289 cson_refcount_decr( v ); 2308 cson_refcount_decr( v ); 2290 } 2309 } 2291 2310 2292 #if 0 /* we might actually want this later on. */ 2311 #if 0 /* we might actually want this later on. */ 2293 /** Returns true if v is not NULL and has the given type ID. */ 2312 /** Returns true if v is not NULL and has the given type ID. */ ................................................................................................................................................................................ 2302 { 2321 { 2303 return (v && v->api) ? v->api->typeID : CSON_TYPE_UNDEF; 2322 return (v && v->api) ? v->api->typeID : CSON_TYPE_UNDEF; 2304 } 2323 } 2305 #endif 2324 #endif 2306 2325 2307 char cson_value_is_undef( cson_value const * v ) 2326 char cson_value_is_undef( cson_value const * v ) 2308 { 2327 { 2309 /** < 2310 This special-case impl is needed because the underlying < 2311 (generic) list operations do not know how to populate < 2312 new entries < 2313 */ < 2314 return ( !v || !v->api || (v->api==&cson_value_api_undef)) 2328 return ( !v || !v->api || (v->api==&cson_value_api_undef)) 2315 ? 1 : 0; 2329 ? 1 : 0; 2316 } 2330 } 2317 #define ISA(T,TID) char cson_value_is_##T( cson_value const * v ) { \ 2331 #define ISA(T,TID) char cson_value_is_##T( cson_value const * v ) { \ 2318 /*return (v && v->api) ? cson_value_is_a(v,CSON_TYPE_##TID) : 0;*/ \ 2332 /*return (v && v->api) ? cson_value_is_a(v,CSON_TYPE_##TID) : 0;*/ \ 2319 return (v && (v->api == &cson_value_api_##T)) ? 1 : 0; \ 2333 return (v && (v->api == &cson_value_api_##T)) ? 1 : 0; \ 2320 } static const char bogusPlaceHolderForEmacsIndention##TID = CSON_TYPE_##TID 2334 } static const char bogusPlaceHolderForEmacsIndention##TID = CSON_TYPE_##TID ................................................................................................................................................................................ 2648 i = (cson_int_t)d; 2662 i = (cson_int_t)d; 2649 break; 2663 break; 2650 } 2664 } 2651 case CSON_TYPE_STRING: 2665 case CSON_TYPE_STRING: 2652 case CSON_TYPE_ARRAY: 2666 case CSON_TYPE_ARRAY: 2653 case CSON_TYPE_OBJECT: 2667 case CSON_TYPE_OBJECT: 2654 default: 2668 default: > 2669 rc = cson_rc.TypeError; 2655 break; | 2670 break; 2656 } 2671 } 2657 if(v) *v = i; | 2672 if(!rc && v) *v = i; 2658 return rc; 2673 return rc; 2659 } 2674 } 2660 } 2675 } 2661 2676 2662 cson_int_t cson_value_get_integer( cson_value const * val ) 2677 cson_int_t cson_value_get_integer( cson_value const * val ) 2663 { 2678 { 2664 cson_int_t i = 0; 2679 cson_int_t i = 0; ................................................................................................................................................................................ 2863 2878 2864 cson_value * cson_value_new_integer( cson_int_t v ) 2879 cson_value * cson_value_new_integer( cson_int_t v ) 2865 { 2880 { 2866 if( 0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_INT_0]; 2881 if( 0 == v ) return &CSON_SPECIAL_VALUES[CSON_VAL_INT_0]; 2867 else 2882 else 2868 { 2883 { 2869 cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0); 2884 cson_value * c = cson_value_new(CSON_TYPE_INTEGER,0); 2870 | 2885 #if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG > 2886 assert( sizeof(cson_int_t) <= sizeof(void *) ); > 2887 #endif 2871 if( c ) 2888 if( c ) 2872 { 2889 { 2873 *CSON_INT(c) = v; 2890 *CSON_INT(c) = v; 2874 } 2891 } 2875 return c; 2892 return c; 2876 } 2893 } 2877 } 2894 } ................................................................................................................................................................................ 3142 cson_value_free( kvp->value ); 3159 cson_value_free( kvp->value ); 3143 cson_refcount_incr( v ); 3160 cson_refcount_incr( v ); 3144 kvp->value = v; 3161 kvp->value = v; 3145 } 3162 } 3146 return 0; 3163 return 0; 3147 } 3164 } 3148 if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) 3165 if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1)) 3149 { | 3166 { /* reserve space */ 3150 unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; 3167 unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6; 3151 if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) 3168 if( n > cson_kvp_list_reserve( &obj->kvp, n ) ) 3152 { 3169 { 3153 return cson_rc.AllocError; 3170 return cson_rc.AllocError; 3154 } 3171 } 3155 } 3172 } 3156 { /* insert new item... */ 3173 { /* insert new item... */ ................................................................................................................................................................................ 3281 kvp = cson_kvp_alloc(); 3298 kvp = cson_kvp_alloc(); 3282 if( ! kvp ) 3299 if( ! kvp ) 3283 { 3300 { 3284 cson_value_free(val); 3301 cson_value_free(val); 3285 return cson_rc.AllocError; 3302 return cson_rc.AllocError; 3286 } 3303 } 3287 kvp->key = cson_string_value(p->ckey)/*transfer ownership*/; 3304 kvp->key = cson_string_value(p->ckey)/*transfer ownership*/; > 3305 assert(0 == kvp->key->refcount); > 3306 cson_refcount_incr(kvp->key); 3288 p->ckey = NULL; 3307 p->ckey = NULL; 3289 kvp->value = val; 3308 kvp->value = val; 3290 cson_refcount_incr( val ); 3309 cson_refcount_incr( val ); 3291 rc = cson_kvp_list_append( &obj->kvp, kvp ); 3310 rc = cson_kvp_list_append( &obj->kvp, kvp ); 3292 if( 0 != rc ) 3311 if( 0 != rc ) 3293 { 3312 { 3294 cson_kvp_free( kvp ); 3313 cson_kvp_free( kvp ); ................................................................................................................................................................................ 3361 case JSON_T_OBJECT_BEGIN: { 3380 case JSON_T_OBJECT_BEGIN: { 3362 cson_value * obja = (JSON_T_ARRAY_BEGIN == type) 3381 cson_value * obja = (JSON_T_ARRAY_BEGIN == type) 3363 ? cson_value_new_array() 3382 ? cson_value_new_array() 3364 : cson_value_new_object(); 3383 : cson_value_new_object(); 3365 if( ! obja ) 3384 if( ! obja ) 3366 { 3385 { 3367 p->errNo = cson_rc.AllocError; 3386 p->errNo = cson_rc.AllocError; 3368 return 0; | 3387 break; 3369 } 3388 } 3370 if( 0 != rc ) break; 3389 if( 0 != rc ) break; 3371 if( ! p->root ) 3390 if( ! p->root ) 3372 { 3391 { 3373 p->root = p->node = obja; 3392 p->root = p->node = obja; 3374 rc = cson_array_append( &p->stack, obja ); 3393 rc = cson_array_append( &p->stack, obja ); 3375 if( 0 != rc ) 3394 if( 0 != rc ) ................................................................................................................................................................................ 3384 ; 3403 ; 3385 ++p->totalValueCount; 3404 ++p->totalValueCount; 3386 } 3405 } 3387 } 3406 } 3388 else 3407 else 3389 { 3408 { 3390 rc = cson_array_append( &p->stack, obja ); 3409 rc = cson_array_append( &p->stack, obja ); > 3410 if(rc) cson_value_free( obja ); > 3411 else > 3412 { 3391 if( 0 == rc ) rc = cson_parser_push_value( p, obja ); | 3413 rc = cson_parser_push_value( p, obja ); 3392 if( 0 == rc ) p->node = obja; | 3414 if( 0 == rc ) p->node = obja; > 3415 } 3393 } 3416 } 3394 break; 3417 break; 3395 } 3418 } 3396 case JSON_T_ARRAY_END: 3419 case JSON_T_ARRAY_END: 3397 case JSON_T_OBJECT_END: { 3420 case JSON_T_OBJECT_END: { 3398 if( 0 == p->stack.list.count ) 3421 if( 0 == p->stack.list.count ) 3399 { 3422 { ................................................................................................................................................................................ 3731 int c; 3754 int c; 3732 READ_UTF8(z, zTerm, c); 3755 READ_UTF8(z, zTerm, c); 3733 *pzNext = z; 3756 *pzNext = z; 3734 return c; 3757 return c; 3735 } 3758 } 3736 #undef READ_UTF8 3759 #undef READ_UTF8 3737 3760 3738 #if defined(_WIN32) | 3761 #ifdef _MSC_VER > 3762 # if _MSC_VER >= 1400 /* Visual Studio 2005 and up */ 3739 # pragma warning( pop ) | 3763 # pragma warning( pop ) > 3764 # endif 3740 #endif 3765 #endif 3741 3766 3742 unsigned int cson_string_length_utf8( cson_string const * str ) 3767 unsigned int cson_string_length_utf8( cson_string const * str ) 3743 { 3768 { 3744 if( ! str ) return 0; 3769 if( ! str ) return 0; 3745 else 3770 else 3746 { 3771 { ................................................................................................................................................................................ 4576 cson_value * cson_object_get_sub2( cson_object const * obj, char const * path ) 4601 cson_value * cson_object_get_sub2( cson_object const * obj, char const * path ) 4577 { 4602 { 4578 cson_value * v = NULL; 4603 cson_value * v = NULL; 4579 cson_object_fetch_sub2( obj, &v, path ); 4604 cson_object_fetch_sub2( obj, &v, path ); 4580 return v; 4605 return v; 4581 } 4606 } 4582 4607 > 4608 > 4609 /** > 4610 If v is-a Object or Array then this function returns a deep > 4611 clone, otherwise it returns v. In either case, the refcount > 4612 of the returned value is increased by 1 by this call. > 4613 */ > 4614 static cson_value * cson_value_clone_ref( cson_value * v ) > 4615 { > 4616 cson_value * rc = NULL; > 4617 #define TRY_SHARING 1 > 4618 #if TRY_SHARING > 4619 if(!v ) return rc; > 4620 else if( cson_value_is_object(v) > 4621 || cson_value_is_array(v)) > 4622 { > 4623 rc = cson_value_clone( v ); > 4624 } > 4625 else > 4626 { > 4627 rc = v; > 4628 } > 4629 #else > 4630 rc = cson_value_clone(v); > 4631 #endif > 4632 #undef TRY_SHARING > 4633 cson_value_add_reference(rc); > 4634 return rc; > 4635 } > 4636 4583 static cson_value * cson_value_clone_array( cson_value const * orig ) 4637 static cson_value * cson_value_clone_array( cson_value const * orig ) 4584 { 4638 { 4585 unsigned int i = 0; 4639 unsigned int i = 0; 4586 cson_array const * asrc = cson_value_get_array( orig ); 4640 cson_array const * asrc = cson_value_get_array( orig ); 4587 unsigned int alen = cson_array_length_get( asrc ); 4641 unsigned int alen = cson_array_length_get( asrc ); 4588 cson_value * destV = NULL; 4642 cson_value * destV = NULL; 4589 cson_array * destA = NULL; 4643 cson_array * destA = NULL; ................................................................................................................................................................................ 4598 return NULL; 4652 return NULL; 4599 } 4653 } 4600 for( ; i < alen; ++i ) 4654 for( ; i < alen; ++i ) 4601 { 4655 { 4602 cson_value * ch = cson_array_get( asrc, i ); 4656 cson_value * ch = cson_array_get( asrc, i ); 4603 if( NULL != ch ) 4657 if( NULL != ch ) 4604 { 4658 { 4605 cson_value * cl = cson_value_clone( ch ); | 4659 cson_value * cl = cson_value_clone_ref( ch ); 4606 if( NULL == cl ) 4660 if( NULL == cl ) 4607 { 4661 { 4608 cson_value_free( destV ); 4662 cson_value_free( destV ); 4609 return NULL; 4663 return NULL; 4610 } 4664 } 4611 if( 0 != cson_array_set( destA, i, cl ) ) 4665 if( 0 != cson_array_set( destA, i, cl ) ) 4612 { 4666 { 4613 cson_value_free( cl ); 4667 cson_value_free( cl ); 4614 cson_value_free( destV ); 4668 cson_value_free( destV ); 4615 return NULL; 4669 return NULL; 4616 } 4670 } > 4671 cson_value_free(cl)/*remove our artificial reference */; 4617 } 4672 } 4618 } 4673 } 4619 return destV; 4674 return destV; 4620 } 4675 } 4621 | 4676 4622 static cson_value * cson_value_clone_object( cson_value const * orig ) 4677 static cson_value * cson_value_clone_object( cson_value const * orig ) 4623 { 4678 { 4624 cson_object const * src = cson_value_get_object( orig ); 4679 cson_object const * src = cson_value_get_object( orig ); 4625 cson_value * destV = NULL; 4680 cson_value * destV = NULL; 4626 cson_object * dest = NULL; 4681 cson_object * dest = NULL; 4627 cson_kvp const * kvp = NULL; 4682 cson_kvp const * kvp = NULL; 4628 cson_object_iterator iter = cson_object_iterator_empty; 4683 cson_object_iterator iter = cson_object_iterator_empty; ................................................................................................................................................................................ 4637 assert( dest ); 4692 assert( dest ); 4638 if( src->kvp.count > cson_kvp_list_reserve( &dest->kvp, src->kvp.count ) ){ 4693 if( src->kvp.count > cson_kvp_list_reserve( &dest->kvp, src->kvp.count ) ){ 4639 cson_value_free( destV ); 4694 cson_value_free( destV ); 4640 return NULL; 4695 return NULL; 4641 } 4696 } 4642 while( (kvp = cson_object_iter_next( &iter )) ) 4697 while( (kvp = cson_object_iter_next( &iter )) ) 4643 { 4698 { 4644 /* < 4645 FIXME: refcount the keys! We first need a setter which takes < 4646 a cson_string or cson_value key type. < 4647 */ < 4648 cson_value * key = NULL; 4699 cson_value * key = NULL; 4649 cson_value * val = NULL; 4700 cson_value * val = NULL; > 4701 assert( kvp->key && (kvp->key->refcount>0) ); 4650 key = cson_value_clone(kvp->key); | 4702 key = cson_value_clone_ref(kvp->key); 4651 val = key ? cson_value_clone( kvp->value ) : NULL; | 4703 val = key ? cson_value_clone_ref(kvp->value) : NULL; 4652 if( ! key || !val ){ 4704 if( ! key || !val ){ 4653 cson_value_free(key); < 4654 cson_value_free(val); < 4655 cson_value_free(destV); < 4656 return NULL; < > 4705 goto error; 4657 } 4706 } 4658 assert( CSON_STR(key) ); 4707 assert( CSON_STR(key) ); 4659 if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) ) 4708 if( 0 != cson_object_set_s( dest, CSON_STR(key), val ) ) 4660 { 4709 { > 4710 goto error; > 4711 } > 4712 /* remove our references */ 4661 cson_value_free(key); | 4713 cson_value_free(key); 4662 cson_value_free(val); | 4714 cson_value_free(val); > 4715 continue; > 4716 error: > 4717 cson_value_free(key); > 4718 cson_value_free(val); 4663 cson_value_free(destV); | 4719 cson_value_free(destV); 4664 return NULL; | 4720 destV = NULL; 4665 } < > 4721 break; 4666 } 4722 } 4667 return destV; 4723 return destV; 4668 } 4724 } 4669 4725 4670 cson_value * cson_value_clone( cson_value const * orig ) 4726 cson_value * cson_value_clone( cson_value const * orig ) 4671 { 4727 { 4672 if( NULL == orig ) return NULL; 4728 if( NULL == orig ) return NULL; ................................................................................................................................................................................ 4731 if(x) cson_value_free(cson_object_value(x)); 4787 if(x) cson_value_free(cson_object_value(x)); 4732 } 4788 } 4733 void cson_free_array(cson_array *x) 4789 void cson_free_array(cson_array *x) 4734 { 4790 { 4735 if(x) cson_value_free(cson_array_value(x)); 4791 if(x) cson_value_free(cson_array_value(x)); 4736 } 4792 } 4737 4793 4738 void cson_free_string(cson_string const *x) | 4794 void cson_free_string(cson_string *x) 4739 { 4795 { 4740 if(x) cson_value_free(cson_string_value(x)); 4796 if(x) cson_value_free(cson_string_value(x)); 4741 } 4797 } 4742 void cson_free_value(cson_value *x) 4798 void cson_free_value(cson_value *x) 4743 { 4799 { 4744 cson_value_free(x); | 4800 if(x) cson_value_free(x); 4745 } 4801 } 4746 4802 4747 4803 4748 #if 0 4804 #if 0 4749 /* i'm not happy with this... */ 4805 /* i'm not happy with this... */ 4750 char * cson_pod_to_string( cson_value const * orig ) 4806 char * cson_pod_to_string( cson_value const * orig ) 4751 { 4807 { ................................................................................................................................................................................ 4905 case CSON_TYPE_NULL: 4961 case CSON_TYPE_NULL: 4906 case CSON_TYPE_BOOL: 4962 case CSON_TYPE_BOOL: 4907 assert( 0 && "Should have been caught by is-builtin check!" ); 4963 assert( 0 && "Should have been caught by is-builtin check!" ); 4908 break; 4964 break; 4909 default: 4965 default: 4910 assert(0 && "Invalid typeID!"); 4966 assert(0 && "Invalid typeID!"); 4911 return 0; 4967 return 0; 4912 | 4968 #undef RCCHECK 4913 } 4969 } 4914 return rc; 4970 return rc; 4915 } 4971 } 4916 } 4972 } 4917 4973 4918 int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){ 4974 int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){ 4919 cson_object_iterator iter = cson_object_iterator_empty; 4975 cson_object_iterator iter = cson_object_iterator_empty; ................................................................................................................................................................................ 4952 else continue; 5008 else continue; 4953 } 5009 } 4954 return 0; 5010 return 0; 4955 } 5011 } 4956 5012 4957 static cson_value * cson_guess_arg_type(char const *arg){ 5013 static cson_value * cson_guess_arg_type(char const *arg){ 4958 char * end = NULL; 5014 char * end = NULL; 4959 if(('0'<=*arg) && ('9'>=*arg)){ | 5015 if(!arg || !*arg) return cson_value_null(); > 5016 else if(('0'>*arg) || ('9'<*arg)){ 4960 goto do_string; 5017 goto do_string; 4961 } 5018 } 4962 { < > 5019 else{ /* try numbers... */ 4963 long const val = strtol(arg, &end, 10); 5020 long const val = strtol(arg, &end, 10); 4964 if(!*end){ 5021 if(!*end){ 4965 return cson_value_new_integer( (cson_int_t)val); 5022 return cson_value_new_integer( (cson_int_t)val); 4966 } 5023 } > 5024 else if( '.' != *end ) { > 5025 goto do_string; 4967 } | 5026 } 4968 { < > 5027 else { 4969 double const val = strtod(arg, &end); | 5028 double const val = strtod(arg, &end); 4970 if(!*end){ | 5029 if(!*end){ 4971 return cson_value_new_double(val); | 5030 return cson_value_new_double(val); 4972 } | 5031 } 4973 } | 5032 } 4974 | 5033 } 4975 < 4976 do_string: 5034 do_string: 4977 return cson_value_new_string(arg, strlen(arg)); 5035 return cson_value_new_string(arg, strlen(arg)); 4978 } 5036 } 4979 5037 4980 5038 4981 int cson_parse_argv_flags( int argc, char const * const * argv, 5039 int cson_parse_argv_flags( int argc, char const * const * argv, 4982 cson_object ** tgt, unsigned int * count ){ 5040 cson_object ** tgt, unsigned int * count ){ ................................................................................................................................................................................ 5003 break; 5061 break; 5004 } 5062 } 5005 if(!*pos){ /** --key */ 5063 if(!*pos){ /** --key */ 5006 v = cson_value_true(); 5064 v = cson_value_true(); 5007 }else{ /** --key=...*/ 5065 }else{ /** --key=...*/ 5008 assert('=' == *pos); 5066 assert('=' == *pos); 5009 ++pos /*skip '='*/; 5067 ++pos /*skip '='*/; 5010 v = *pos < 5011 ? cson_guess_arg_type(pos) | 5068 v = cson_guess_arg_type(pos); 5012 : cson_value_null(); < 5013 } 5069 } 5014 if(0 != (rc=cson_object_set_s(o, k, v))){ 5070 if(0 != (rc=cson_object_set_s(o, k, v))){ 5015 cson_free_string(k); 5071 cson_free_string(k); 5016 cson_value_free(v); 5072 cson_value_free(v); 5017 break; 5073 break; 5018 } 5074 } 5019 else if(count) ++*count; 5075 else if(count) ++*count; ................................................................................................................................................................................ 5502 #define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; } 5558 #define RETURN(RC) { if(rootV) cson_value_free(rootV); return RC; } 5503 if( ! tgt || !st ) return cson_rc.ArgError; 5559 if( ! tgt || !st ) return cson_rc.ArgError; 5504 else 5560 else 5505 { 5561 { 5506 cson_value * rootV = NULL; 5562 cson_value * rootV = NULL; 5507 cson_object * root = NULL; 5563 cson_object * root = NULL; 5508 cson_value * aryV = NULL; 5564 cson_value * aryV = NULL; 5509 cson_array * ary = NULL; < 5510 cson_value * rowsV = NULL; 5565 cson_value * rowsV = NULL; 5511 cson_array * rows = NULL; 5566 cson_array * rows = NULL; 5512 int rc = 0; 5567 int rc = 0; 5513 int const colCount = sqlite3_column_count(st); 5568 int const colCount = sqlite3_column_count(st); 5514 if( colCount <= 0 ) return cson_rc.ArgError; 5569 if( colCount <= 0 ) return cson_rc.ArgError; 5515 rootV = cson_value_new_object(); 5570 rootV = cson_value_new_object(); 5516 if( ! rootV ) return cson_rc.AllocError; 5571 if( ! rootV ) return cson_rc.AllocError; ................................................................................................................................................................................ 5524 rc = cson_object_set( root, "columns", aryV ); 5579 rc = cson_object_set( root, "columns", aryV ); 5525 if( rc ) 5580 if( rc ) 5526 { 5581 { 5527 cson_value_free( aryV ); 5582 cson_value_free( aryV ); 5528 RETURN(rc); 5583 RETURN(rc); 5529 } 5584 } 5530 aryV = NULL; 5585 aryV = NULL; 5531 ary = NULL; < 5532 rowsV = cson_value_new_array(); 5586 rowsV = cson_value_new_array(); 5533 if( ! rowsV ) RETURN(cson_rc.AllocError); 5587 if( ! rowsV ) RETURN(cson_rc.AllocError); 5534 rc = cson_object_set( root, "rows", rowsV ); 5588 rc = cson_object_set( root, "rows", rowsV ); 5535 if( 0 != rc ) 5589 if( 0 != rc ) 5536 { 5590 { 5537 cson_value_free( rowsV ); 5591 cson_value_free( rowsV ); 5538 RETURN(rc); 5592 RETURN(rc);

Changes to src/cson_amalgamation.h

72 #define CSON_DOUBLE_T_SFMT "Lf" 72 #define CSON_DOUBLE_T_SFMT "Lf" 73 #define CSON_DOUBLE_T_PFMT "Lf" 73 #define CSON_DOUBLE_T_PFMT "Lf" 74 #else 74 #else 75 typedef double cson_double_t; 75 typedef double cson_double_t; 76 #define CSON_DOUBLE_T_SFMT "f" 76 #define CSON_DOUBLE_T_SFMT "f" 77 #define CSON_DOUBLE_T_PFMT "f" 77 #define CSON_DOUBLE_T_PFMT "f" 78 #endif 78 #endif > 79 > 80 /** @def CSON_VOID_PTR_IS_BIG > 81 > 82 ONLY define this to a true value if you know that > 83 > 84 (sizeof(cson_int_t) <= sizeof(void*)) > 85 > 86 If that is the case, cson does not need to dynamically > 87 allocate integers. However, enabling this may cause > 88 compilation warnings in 32-bit builds even though the code > 89 being warned about cannot ever be called. To get around such > 90 warnings, when building on a 64-bit environment you can define > 91 this to 1 to get "big" integer support. HOWEVER, all clients must > 92 also use the same value for this macro. If i knew a halfway reliable > 93 way to determine this automatically at preprocessor-time, i would > 94 automate this. We might be able to do halfway reliably by looking > 95 for a large INT_MAX value? > 96 */ > 97 #if !defined(CSON_VOID_PTR_IS_BIG) > 98 > 99 /* Largely taken from http://predef.sourceforge.net/prearch.html > 100 > 101 See also: http://poshlib.hookatooka.com/poshlib/trac.cgi/browser/posh.h > 102 */ > 103 # if defined(_WIN64) || defined(__LP64__)/*gcc*/ \ > 104 || defined(_M_X64) || defined(__amd64__) || defined(__amd64) \ > 105 || defined(__x86_64__) || defined(__x86_64) \ > 106 || defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64_ > 107 || defined(_M_IA64) \ > 108 || defined(__sparc_v9__) || defined(__sparcv9) || defined(_ADDR64) \ > 109 || defined(__64BIT__) > 110 # define CSON_VOID_PTR_IS_BIG 1 > 111 # else > 112 # define CSON_VOID_PTR_IS_BIG 0 > 113 # endif > 114 #endif 79 115 80 /** @def CSON_INT_T_SFMT 116 /** @def CSON_INT_T_SFMT 81 117 82 scanf()-compatible format token for cson_int_t. 118 scanf()-compatible format token for cson_int_t. 83 */ 119 */ 84 120 85 /** @def CSON_INT_T_PFMT 121 /** @def CSON_INT_T_PFMT ................................................................................................................................................................................ 926 cson_string * cson_value_get_string( cson_value const * val ); 962 cson_string * cson_value_get_string( cson_value const * val ); 927 963 928 /** 964 /** 929 Returns a pointer to the NULL-terminated string bytes of str. 965 Returns a pointer to the NULL-terminated string bytes of str. 930 The bytes are owned by string and will be invalided when it 966 The bytes are owned by string and will be invalided when it 931 is cleaned up. 967 is cleaned up. 932 968 933 If str is NULL then NULL is returned. | 969 If str is NULL then NULL is returned. If the string has a length > 970 of 0 then "" is returned. 934 971 935 @see cson_string_length_bytes() 972 @see cson_string_length_bytes() 936 @see cson_value_get_string() 973 @see cson_value_get_string() 937 */ 974 */ 938 char const * cson_string_cstr( cson_string const * str ); 975 char const * cson_string_cstr( cson_string const * str ); 939 976 940 /** 977 /** ................................................................................................................................................................................ 1259 Equivalent to cson_value_free(cson_array_value(x)). 1296 Equivalent to cson_value_free(cson_array_value(x)). 1260 */ 1297 */ 1261 void cson_free_array(cson_array *x); 1298 void cson_free_array(cson_array *x); 1262 1299 1263 /** 1300 /** 1264 Equivalent to cson_value_free(cson_string_value(x)). 1301 Equivalent to cson_value_free(cson_string_value(x)). 1265 */ 1302 */ 1266 void cson_free_string(cson_string const *x); | 1303 void cson_free_string(cson_string *x); 1267 1304 1268 1305 1269 /** 1306 /** 1270 Allocates a new "array" value and transfers ownership of it to the 1307 Allocates a new "array" value and transfers ownership of it to the 1271 caller. It must eventually be destroyed, by the caller or its 1308 caller. It must eventually be destroyed, by the caller or its 1272 owning container, by passing it to cson_value_free(). 1309 owning container, by passing it to cson_value_free(). 1273 1310 ................................................................................................................................................................................ 2035 int cson_value_refcount_set( cson_value * v, unsigned short rc ); 2072 int cson_value_refcount_set( cson_value * v, unsigned short rc ); 2036 #endif 2073 #endif 2037 2074 2038 /** 2075 /** 2039 Deeply copies a JSON value, be it an object/array or a "plain" 2076 Deeply copies a JSON value, be it an object/array or a "plain" 2040 value (e.g. number/string/boolean). If cv is not NULL then this 2077 value (e.g. number/string/boolean). If cv is not NULL then this 2041 function makes a deep clone of it and returns that clone. Ownership 2078 function makes a deep clone of it and returns that clone. Ownership 2042 of the clone is transfered to the caller, who must eventually free | 2079 of the clone is identical t transfered to the caller, who must 2043 the value using cson_value_free() or add it to a container | 2080 eventually free the value using cson_value_free() or add it to a 2044 object/array to transfer ownership to the container. The returned | 2081 container object/array to transfer ownership to the container. The 2045 object will be of the same logical type as orig. | 2082 returned object will be of the same logical type as orig. 2046 2083 2047 ACHTUNG: if orig contains any cyclic references at any depth level 2084 ACHTUNG: if orig contains any cyclic references at any depth level 2048 this function will endlessly recurse. (Having _any_ cyclic 2085 this function will endlessly recurse. (Having _any_ cyclic 2049 references violates this library's requirements.) 2086 references violates this library's requirements.) 2050 2087 2051 Returns NULL if orig is NULL or if cloning fails. Assuming that 2088 Returns NULL if orig is NULL or if cloning fails. Assuming that 2052 orig is in a valid state, the only "likely" error case is that an 2089 orig is in a valid state, the only "likely" error case is that an 2053 allocation fails while constructing the clone. In other words, if 2090 allocation fails while constructing the clone. In other words, if 2054 cloning fails due to something other than an allocation error then 2091 cloning fails due to something other than an allocation error then 2055 either orig is in an invalid state or there is a bug. 2092 either orig is in an invalid state or there is a bug. > 2093 > 2094 When this function clones Objects or Arrays it shares any immutable > 2095 values (including object keys) between the parent and the > 2096 clone. Mutable values (Objects and Arrays) are copied, however. > 2097 For example, if we clone: > 2098 > 2099 @code > 2100 { a: 1, b: 2, c:["hi"] } > 2101 @endcode > 2102 > 2103 The cloned object and the array "c" would be a new Object/Array > 2104 instances but the object keys (a,b,b) and the values of (a,b), as > 2105 well as the string value within the "c" array, would be shared > 2106 between the original and the clone. The "c" array itself would be > 2107 deeply cloned, such that future changes to the clone are not > 2108 visible to the parent, and vice versa, but immutable values within > 2109 the array are shared (in this case the string "hi"). The > 2110 justification for this heuristic is that immutable values can never > 2111 be changed, so there is no harm in sharing them across > 2112 clones. Additionally, such types can never contribute to cycles in > 2113 a JSON tree, so they are safe to share this way. Objects and > 2114 Arrays, on the other hand, can be modified later and can contribute > 2115 to cycles, and thus the clone needs to be an independent instance. > 2116 Note, however, that if this function directly passed a > 2117 non-Object/Array, that value is deeply cloned. The sharing > 2118 behaviour only applies when traversing Objects/Arrays. 2056 */ 2119 */ 2057 cson_value * cson_value_clone( cson_value const * orig ); 2120 cson_value * cson_value_clone( cson_value const * orig ); 2058 2121 2059 /** 2122 /** 2060 Returns the value handle associated with s. The handle itself owns 2123 Returns the value handle associated with s. The handle itself owns 2061 s, and ownership of the handle is not changed by calling this 2124 s, and ownership of the handle is not changed by calling this 2062 function. If the returned handle is part of a container, calling 2125 function. If the returned handle is part of a container, calling 2063 cson_value_free() on the returned handle invoked undefined 2126 cson_value_free() on the returned handle invoked undefined 2064 behaviour (quite possibly downstream when the container tries to 2127 behaviour (quite possibly downstream when the container tries to 2065 use it). 2128 use it). 2066 2129 2067 This function only returns NULL if s. is NULL. | 2130 This function only returns NULL if s is NULL. The length of the > 2131 returned string is cson_string_length_bytes(). 2068 */ 2132 */ 2069 cson_value * cson_string_value(cson_string const * s); 2133 cson_value * cson_string_value(cson_string const * s); 2070 /** 2134 /** 2071 The Object form of cson_string_value(). See that function 2135 The Object form of cson_string_value(). See that function 2072 for full details. 2136 for full details. 2073 */ 2137 */ 2074 cson_value * cson_object_value(cson_object const * s); 2138 cson_value * cson_object_value(cson_object const * s); ................................................................................................................................................................................ 2077 The Array form of cson_string_value(). See that function 2141 The Array form of cson_string_value(). See that function 2078 for full details. 2142 for full details. 2079 */ 2143 */ 2080 cson_value * cson_array_value(cson_array const * s); 2144 cson_value * cson_array_value(cson_array const * s); 2081 2145 2082 2146 2083 /** 2147 /** 2084 Calculates the in-memory-allocated size of v, recursively if it is | 2148 Calculates the approximate in-memory-allocated size of v, 2085 a container type, with the following caveats and limitations: | 2149 recursively if it is a container type, with the following caveats > 2150 and limitations: 2086 2151 2087 If a given value is reference counted and multiple times within a | 2152 If a given value is reference counted then it is only and multiple 2088 traversed container, each reference is counted at full cost. We | 2153 times within a traversed container, each reference is counted at 2089 have no what of knowing if a given reference has been visited | 2154 full cost. We have no way of knowing if a given reference has been 2090 already and whether it should or should not be counted, so we | 2155 visited already and whether it should or should not be counted, so 2091 pessimistically count them even though the _might_ not really count | 2156 we pessimistically count them even though the _might_ not really 2092 for the given object tree (it depends on where the other open | 2157 count for the given object tree (it depends on where the other open 2093 references live). 2158 references live). 2094 2159 2095 This function returns 0 if any of the following are true: 2160 This function returns 0 if any of the following are true: 2096 2161 2097 - v is NULL 2162 - v is NULL 2098 2163 2099 - v is one of the special singleton values (null, bools, empty 2164 - v is one of the special singleton values (null, bools, empty

Changes to src/db.c

97 fprintf(stderr, "%s: %s\n\n%s", fossil_nameofexe(), z, zRebuildMsg); 97 fprintf(stderr, "%s: %s\n\n%s", fossil_nameofexe(), z, zRebuildMsg); 98 } 98 } 99 free(z); 99 free(z); 100 db_force_rollback(); 100 db_force_rollback(); 101 fossil_exit(rc); 101 fossil_exit(rc); 102 } 102 } 103 103 > 104 /* > 105 ** All static variable that a used by only this file are gathered into > 106 ** the following structure. > 107 */ > 108 static struct DbLocalData { 104 static int nBegin = 0; /* Nesting depth of BEGIN */ | 109 int nBegin; /* Nesting depth of BEGIN */ 105 static int doRollback = 0; /* True to force a rollback */ | 110 int doRollback; /* True to force a rollback */ 106 static int nCommitHook = 0; /* Number of commit hooks */ | 111 int nCommitHook; /* Number of commit hooks */ > 112 Stmt *pAllStmt; /* List of all unfinalized statements */ > 113 int nPrepare; /* Number of calls to sqlite3_prepare() */ > 114 int nDeleteOnFail; /* Number of entries in azDeleteOnFail[] */ 107 static struct sCommitHook { | 115 struct sCommitHook { 108 int (*xHook)(void); /* Functions to call at db_end_transaction() */ | 116 int (*xHook)(void); /* Functions to call at db_end_transaction() */ 109 int sequence; /* Call functions in sequence order */ | 117 int sequence; /* Call functions in sequence order */ 110 } aHook[5]; | 118 } aHook[5]; 111 static Stmt *pAllStmt = 0; /* List of all unfinalized statements */ < 112 static int nPrepare = 0; /* Number of calls to sqlite3_prepare() */ < 113 static int nDeleteOnFail = 0; /* Number of entries in azDeleteOnFail[] */ < 114 static char *azDeleteOnFail[3]; /* Files to delete on a failure */ | 119 char *azDeleteOnFail[3]; /* Files to delete on a failure */ 115 < > 120 } db = {0, 0, 0, 0, 0, 0, }; 116 121 117 /* 122 /* 118 ** Arrange for the given file to be deleted on a failure. 123 ** Arrange for the given file to be deleted on a failure. 119 */ 124 */ 120 void db_delete_on_failure(const char *zFilename){ 125 void db_delete_on_failure(const char *zFilename){ 121 assert( nDeleteOnFail<count(azDeleteOnFail) ); | 126 assert( db.nDeleteOnFail<count(db.azDeleteOnFail) ); 122 azDeleteOnFail[nDeleteOnFail++] = fossil_strdup(zFilename); | 127 db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename); 123 } 128 } 124 129 125 /* 130 /* 126 ** This routine is called by the SQLite commit-hook mechanism 131 ** This routine is called by the SQLite commit-hook mechanism 127 ** just prior to each commit. All this routine does is verify 132 ** just prior to each commit. All this routine does is verify 128 ** that nBegin really is zero. That insures that transactions 133 ** that nBegin really is zero. That insures that transactions 129 ** cannot commit by any means other than by calling db_end_transaction() 134 ** cannot commit by any means other than by calling db_end_transaction() 130 ** below. 135 ** below. 131 ** 136 ** 132 ** This is just a safety and sanity check. 137 ** This is just a safety and sanity check. 133 */ 138 */ 134 static int db_verify_at_commit(void *notUsed){ 139 static int db_verify_at_commit(void *notUsed){ 135 if( nBegin ){ | 140 if( db.nBegin ){ 136 fossil_panic("illegal commit attempt"); 141 fossil_panic("illegal commit attempt"); 137 return 1; 142 return 1; 138 } 143 } 139 return 0; 144 return 0; 140 } 145 } 141 146 142 /* 147 /* 143 ** Begin and end a nested transaction 148 ** Begin and end a nested transaction 144 */ 149 */ 145 void db_begin_transaction(void){ 150 void db_begin_transaction(void){ 146 if( nBegin==0 ){ | 151 if( db.nBegin==0 ){ 147 db_multi_exec("BEGIN"); 152 db_multi_exec("BEGIN"); 148 sqlite3_commit_hook(g.db, db_verify_at_commit, 0); 153 sqlite3_commit_hook(g.db, db_verify_at_commit, 0); 149 } 154 } 150 nBegin++; | 155 db.nBegin++; 151 } 156 } 152 void db_end_transaction(int rollbackFlag){ 157 void db_end_transaction(int rollbackFlag){ 153 if( g.db==0 ) return; 158 if( g.db==0 ) return; 154 if( nBegin<=0 ) return; | 159 if( db.nBegin<=0 ) return; 155 if( rollbackFlag ) doRollback = 1; | 160 if( rollbackFlag ) db.doRollback = 1; 156 nBegin--; | 161 db.nBegin--; 157 if( nBegin==0 ){ | 162 if( db.nBegin==0 ){ 158 int i; 163 int i; 159 if( doRollback==0 ) leaf_do_pending_checks(); | 164 if( db.doRollback==0 ) leaf_do_pending_checks(); 160 for(i=0; doRollback==0 && i<nCommitHook; i++){ | 165 for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){ 161 doRollback |= aHook[i].xHook(); | 166 db.doRollback |= db.aHook[i].xHook(); 162 } 167 } 163 while( pAllStmt ){ | 168 while( db.pAllStmt ){ 164 db_finalize(pAllStmt); | 169 db_finalize(db.pAllStmt); 165 } 170 } 166 db_multi_exec(doRollback ? "ROLLBACK" : "COMMIT"); | 171 db_multi_exec(db.doRollback ? "ROLLBACK" : "COMMIT"); 167 doRollback = 0; | 172 db.doRollback = 0; 168 } 173 } 169 } 174 } 170 175 171 /* 176 /* 172 ** Force a rollback and shutdown the database 177 ** Force a rollback and shutdown the database 173 */ 178 */ 174 void db_force_rollback(void){ 179 void db_force_rollback(void){ ................................................................................................................................................................................ 177 sqlite3_stmt *pStmt = 0; 182 sqlite3_stmt *pStmt = 0; 178 if( busy || g.db==0 ) return; 183 if( busy || g.db==0 ) return; 179 busy = 1; 184 busy = 1; 180 undo_rollback(); 185 undo_rollback(); 181 while( (pStmt = sqlite3_next_stmt(g.db,pStmt))!=0 ){ 186 while( (pStmt = sqlite3_next_stmt(g.db,pStmt))!=0 ){ 182 sqlite3_reset(pStmt); 187 sqlite3_reset(pStmt); 183 } 188 } 184 while( pAllStmt ){ | 189 while( db.pAllStmt ){ 185 db_finalize(pAllStmt); | 190 db_finalize(db.pAllStmt); 186 } 191 } 187 if( nBegin ){ | 192 if( db.nBegin ){ 188 sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); 193 sqlite3_exec(g.db, "ROLLBACK", 0, 0, 0); 189 nBegin = 0; | 194 db.nBegin = 0; 190 } 195 } 191 busy = 0; 196 busy = 0; 192 db_close(0); 197 db_close(0); 193 for(i=0; i<nDeleteOnFail; i++){ | 198 for(i=0; i<db.nDeleteOnFail; i++){ 194 file_delete(azDeleteOnFail[i]); | 199 file_delete(db.azDeleteOnFail[i]); 195 } 200 } 196 } 201 } 197 202 198 /* 203 /* 199 ** Install a commit hook. Hooks are installed in sequence order. 204 ** Install a commit hook. Hooks are installed in sequence order. 200 ** It is an error to install the same commit hook more than once. 205 ** It is an error to install the same commit hook more than once. 201 ** 206 ** ................................................................................................................................................................................ 203 ** each commit operation. If any commit hook returns non-zero, 208 ** each commit operation. If any commit hook returns non-zero, 204 ** the subsequence commit hooks are omitted and the transaction 209 ** the subsequence commit hooks are omitted and the transaction 205 ** rolls back rather than commit. It is the responsibility of the 210 ** rolls back rather than commit. It is the responsibility of the 206 ** hooks themselves to issue any error messages. 211 ** hooks themselves to issue any error messages. 207 */ 212 */ 208 void db_commit_hook(int (*x)(void), int sequence){ 213 void db_commit_hook(int (*x)(void), int sequence){ 209 int i; 214 int i; 210 assert( nCommitHook < sizeof(aHook)/sizeof(aHook[1]) ); | 215 assert( db.nCommitHook < sizeof(db.aHook)/sizeof(db.aHook[1]) ); 211 for(i=0; i<nCommitHook; i++){ | 216 for(i=0; i<db.nCommitHook; i++){ 212 assert( x!=aHook[i].xHook ); | 217 assert( x!=db.aHook[i].xHook ); 213 if( aHook[i].sequence>sequence ){ | 218 if( db.aHook[i].sequence>sequence ){ 214 int s = sequence; 219 int s = sequence; 215 int (*xS)(void) = x; 220 int (*xS)(void) = x; 216 sequence = aHook[i].sequence; | 221 sequence = db.aHook[i].sequence; 217 x = aHook[i].xHook; | 222 x = db.aHook[i].xHook; 218 aHook[i].sequence = s; | 223 db.aHook[i].sequence = s; 219 aHook[i].xHook = xS; | 224 db.aHook[i].xHook = xS; 220 } 225 } 221 } 226 } 222 aHook[nCommitHook].sequence = sequence; | 227 db.aHook[db.nCommitHook].sequence = sequence; 223 aHook[nCommitHook].xHook = x; | 228 db.aHook[db.nCommitHook].xHook = x; 224 nCommitHook++; | 229 db.nCommitHook++; 225 } 230 } 226 231 227 /* 232 /* 228 ** Prepare a Stmt. Assume that the Stmt is previously uninitialized. 233 ** Prepare a Stmt. Assume that the Stmt is previously uninitialized. 229 ** If the input string contains multiple SQL statements, only the first 234 ** If the input string contains multiple SQL statements, only the first 230 ** one is processed. All statements beyond the first are silently ignored. 235 ** one is processed. All statements beyond the first are silently ignored. 231 */ 236 */ ................................................................................................................................................................................ 232 int db_vprepare(Stmt *pStmt, int errOk, const char *zFormat, va_list ap){ 237 int db_vprepare(Stmt *pStmt, int errOk, const char *zFormat, va_list ap){ 233 int rc; 238 int rc; 234 char *zSql; 239 char *zSql; 235 blob_zero(&pStmt->sql); 240 blob_zero(&pStmt->sql); 236 blob_vappendf(&pStmt->sql, zFormat, ap); 241 blob_vappendf(&pStmt->sql, zFormat, ap); 237 va_end(ap); 242 va_end(ap); 238 zSql = blob_str(&pStmt->sql); 243 zSql = blob_str(&pStmt->sql); 239 nPrepare++; | 244 db.nPrepare++; 240 rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt->pStmt, 0); 245 rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt->pStmt, 0); 241 if( rc!=0 && !errOk ){ 246 if( rc!=0 && !errOk ){ 242 db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); 247 db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); 243 } 248 } 244 pStmt->pNext = pStmt->pPrev = 0; 249 pStmt->pNext = pStmt->pPrev = 0; 245 pStmt->nStep = 0; 250 pStmt->nStep = 0; 246 return rc; 251 return rc; ................................................................................................................................................................................ 263 } 268 } 264 int db_static_prepare(Stmt *pStmt, const char *zFormat, ...){ 269 int db_static_prepare(Stmt *pStmt, const char *zFormat, ...){ 265 int rc = SQLITE_OK; 270 int rc = SQLITE_OK; 266 if( blob_size(&pStmt->sql)==0 ){ 271 if( blob_size(&pStmt->sql)==0 ){ 267 va_list ap; 272 va_list ap; 268 va_start(ap, zFormat); 273 va_start(ap, zFormat); 269 rc = db_vprepare(pStmt, 0, zFormat, ap); 274 rc = db_vprepare(pStmt, 0, zFormat, ap); 270 pStmt->pNext = pAllStmt; | 275 pStmt->pNext = db.pAllStmt; 271 pStmt->pPrev = 0; 276 pStmt->pPrev = 0; 272 if( pAllStmt ) pAllStmt->pPrev = pStmt; | 277 if( db.pAllStmt ) db.pAllStmt->pPrev = pStmt; 273 pAllStmt = pStmt; | 278 db.pAllStmt = pStmt; 274 va_end(ap); 279 va_end(ap); 275 } 280 } 276 return rc; 281 return rc; 277 } 282 } 278 283 279 /* 284 /* 280 ** Return the index of a bind parameter 285 ** Return the index of a bind parameter ................................................................................................................................................................................ 370 db_check_result(rc); 375 db_check_result(rc); 371 pStmt->pStmt = 0; 376 pStmt->pStmt = 0; 372 if( pStmt->pNext ){ 377 if( pStmt->pNext ){ 373 pStmt->pNext->pPrev = pStmt->pPrev; 378 pStmt->pNext->pPrev = pStmt->pPrev; 374 } 379 } 375 if( pStmt->pPrev ){ 380 if( pStmt->pPrev ){ 376 pStmt->pPrev->pNext = pStmt->pNext; 381 pStmt->pPrev->pNext = pStmt->pNext; 377 }else if( pAllStmt==pStmt ){ | 382 }else if( db.pAllStmt==pStmt ){ 378 pAllStmt = pStmt->pNext; | 383 db.pAllStmt = pStmt->pNext; 379 } 384 } 380 pStmt->pNext = 0; 385 pStmt->pNext = 0; 381 pStmt->pPrev = 0; 386 pStmt->pPrev = 0; 382 return rc; 387 return rc; 383 } 388 } 384 389 385 /* 390 /* ................................................................................................................................................................................ 654 ** connection. An error results in process abort. 659 ** connection. An error results in process abort. 655 */ 660 */ 656 static sqlite3 *openDatabase(const char *zDbName){ 661 static sqlite3 *openDatabase(const char *zDbName){ 657 int rc; 662 int rc; 658 const char *zVfs; 663 const char *zVfs; 659 sqlite3 *db; 664 sqlite3 *db; 660 665 661 zVfs = getenv("FOSSIL_VFS"); | 666 zVfs = fossil_getenv("FOSSIL_VFS"); 662 rc = sqlite3_open_v2( 667 rc = sqlite3_open_v2( 663 zDbName, &db, 668 zDbName, &db, 664 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 669 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 665 zVfs 670 zVfs 666 ); 671 ); 667 if( rc!=SQLITE_OK ){ 672 if( rc!=SQLITE_OK ){ 668 db_err(sqlite3_errmsg(db)); 673 db_err(sqlite3_errmsg(db)); ................................................................................................................................................................................ 702 ** case, invoke this routine with useAttach as 1. 707 ** case, invoke this routine with useAttach as 1. 703 */ 708 */ 704 void db_open_config(int useAttach){ 709 void db_open_config(int useAttach){ 705 char *zDbName; 710 char *zDbName; 706 const char *zHome; 711 const char *zHome; 707 if( g.configOpen ) return; 712 if( g.configOpen ) return; 708 #if defined(_WIN32) 713 #if defined(_WIN32) 709 zHome = getenv("LOCALAPPDATA"); | 714 zHome = fossil_getenv("LOCALAPPDATA"); 710 if( zHome==0 ){ 715 if( zHome==0 ){ 711 zHome = getenv("APPDATA"); | 716 zHome = fossil_getenv("APPDATA"); 712 if( zHome==0 ){ 717 if( zHome==0 ){ 713 char *zDrive = getenv("HOMEDRIVE"); | 718 char *zDrive = fossil_getenv("HOMEDRIVE"); 714 zHome = getenv("HOMEPATH"); | 719 zHome = fossil_getenv("HOMEPATH"); 715 if( zDrive && zHome ) zHome = mprintf("%s%s", zDrive, zHome); 720 if( zDrive && zHome ) zHome = mprintf("%s%s", zDrive, zHome); 716 } 721 } 717 } 722 } 718 if( zHome==0 ){ 723 if( zHome==0 ){ 719 fossil_fatal("cannot locate home directory - " 724 fossil_fatal("cannot locate home directory - " 720 "please set the LOCALAPPDATA or APPDATA or HOMEPATH " 725 "please set the LOCALAPPDATA or APPDATA or HOMEPATH " 721 "environment variables"); 726 "environment variables"); 722 } 727 } 723 zHome = fossil_mbcs_to_utf8(zHome); < 724 #else 728 #else 725 zHome = getenv("HOME"); | 729 zHome = fossil_getenv("HOME"); 726 if( zHome==0 ){ 730 if( zHome==0 ){ 727 fossil_fatal("cannot locate home directory - " 731 fossil_fatal("cannot locate home directory - " 728 "please set the HOME environment variable"); 732 "please set the HOME environment variable"); 729 } 733 } 730 #endif 734 #endif 731 if( file_isdir(zHome)!=1 ){ 735 if( file_isdir(zHome)!=1 ){ 732 fossil_fatal("invalid home directory: %s", zHome); 736 fossil_fatal("invalid home directory: %s", zHome); ................................................................................................................................................................................ 783 static int isValidLocalDb(const char *zDbName){ 787 static int isValidLocalDb(const char *zDbName){ 784 i64 lsize; 788 i64 lsize; 785 789 786 if( file_access(zDbName, F_OK) ) return 0; 790 if( file_access(zDbName, F_OK) ) return 0; 787 lsize = file_size(zDbName); 791 lsize = file_size(zDbName); 788 if( lsize%1024!=0 || lsize<4096 ) return 0; 792 if( lsize%1024!=0 || lsize<4096 ) return 0; 789 db_open_or_attach(zDbName, "localdb"); 793 db_open_or_attach(zDbName, "localdb"); 790 g.localOpen = 1; < 791 db_open_config(0); < 792 db_open_repository(0); < 793 794 794 /* If the "isexe" column is missing from the vfile table, then 795 /* If the "isexe" column is missing from the vfile table, then 795 ** add it now. This code added on 2010-03-06. After all users have 796 ** add it now. This code added on 2010-03-06. After all users have 796 ** upgraded, this code can be safely deleted. 797 ** upgraded, this code can be safely deleted. 797 */ 798 */ 798 if( !db_local_column_exists("vfile", "isexe") ) | 799 if( !db_local_column_exists("vfile", "isexe") ){ 799 db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0"); 800 db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0"); > 801 } 800 802 801 /* If "islink"/"isLink" columns are missing from tables, then 803 /* If "islink"/"isLink" columns are missing from tables, then 802 ** add them now. This code added on 2011-01-17 and 2011-08-27. 804 ** add them now. This code added on 2011-01-17 and 2011-08-27. 803 ** After all users have upgraded, this code can be safely deleted. 805 ** After all users have upgraded, this code can be safely deleted. 804 */ 806 */ 805 if( !db_local_column_exists("vfile", "islink") ) | 807 if( !db_local_column_exists("vfile", "islink") ){ 806 db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0"); 808 db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0"); 807 | 809 } > 810 808 if( !db_local_column_exists("stashfile", "isLink") && 811 if( !db_local_column_exists("stashfile", "isLink") && 809 db_local_table_exists("stashfile") ) | 812 db_local_table_exists("stashfile") ){ 810 db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOLEAN DEFAULT 0"); 813 db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOLEAN DEFAULT 0"); 811 | 814 } > 815 812 if( !db_local_column_exists("undo", "isLink") && 816 if( !db_local_column_exists("undo", "isLink") && 813 db_local_table_exists("undo") ) | 817 db_local_table_exists("undo") ){ 814 db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0"); 818 db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0"); 815 | 819 } > 820 816 if( !db_local_column_exists("undo_vfile", "islink") && 821 if( !db_local_column_exists("undo_vfile", "islink") && 817 db_local_table_exists("undo_vfile") ) | 822 db_local_table_exists("undo_vfile") ){ 818 db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOLEAN DEFAULT 0"); 823 db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOLEAN DEFAULT 0"); 819 | 824 } 820 return 1; 825 return 1; 821 } 826 } 822 827 823 /* 828 /* 824 ** Locate the root directory of the local repository tree. The root 829 ** Locate the root directory of the local repository tree. The root 825 ** directory is found by searching for a file named "_FOSSIL_" or ".fos" | 830 ** directory is found by searching for a file named "_FOSSIL_" or ".fslckout" 826 ** that contains a valid repository database. 831 ** that contains a valid repository database. > 832 ** > 833 ** For legacy, also look for ".fos". The use of ".fos" is deprecated > 834 ** since "fos" has negative connotations in Hungarian, we are told. 827 ** 835 ** 828 ** If no valid _FOSSIL_ or .fos file is found, we move up one level and 836 ** If no valid _FOSSIL_ or .fos file is found, we move up one level and 829 ** try again. Once the file is found, the g.zLocalRoot variable is set 837 ** try again. Once the file is found, the g.zLocalRoot variable is set 830 ** to the root of the repository tree and this routine returns 1. If 838 ** to the root of the repository tree and this routine returns 1. If 831 ** no database is found, then this routine return 0. 839 ** no database is found, then this routine return 0. 832 ** 840 ** 833 ** This routine always opens the user database regardless of whether or 841 ** This routine always opens the user database regardless of whether or 834 ** not the repository database is found. If the _FOSSIL_ or .fos file 842 ** not the repository database is found. If the _FOSSIL_ or .fos file 835 ** is found, it is attached to the open database connection too. 843 ** is found, it is attached to the open database connection too. 836 */ 844 */ 837 int db_open_local(void){ 845 int db_open_local(void){ 838 int i, n; 846 int i, n; 839 char zPwd[2000]; 847 char zPwd[2000]; 840 static const char *aDbName[] = { "/_FOSSIL_", "/.fos" }; | 848 static const char *aDbName[] = { "/_FOSSIL_", "/.fslckout", "/.fos" }; 841 849 842 if( g.localOpen) return 1; 850 if( g.localOpen) return 1; 843 file_getcwd(zPwd, sizeof(zPwd)-20); 851 file_getcwd(zPwd, sizeof(zPwd)-20); 844 n = strlen(zPwd); 852 n = strlen(zPwd); 845 if( n==1 && zPwd[0]=='/' ) zPwd[0] = '.'; 853 if( n==1 && zPwd[0]=='/' ) zPwd[0] = '.'; 846 while( n>0 ){ 854 while( n>0 ){ 847 if( file_access(zPwd, W_OK) ) break; 855 if( file_access(zPwd, W_OK) ) break; ................................................................................................................................................................................ 851 /* Found a valid checkout database file */ 859 /* Found a valid checkout database file */ 852 zPwd[n] = 0; 860 zPwd[n] = 0; 853 while( n>1 && zPwd[n-1]=='/' ){ 861 while( n>1 && zPwd[n-1]=='/' ){ 854 n--; 862 n--; 855 zPwd[n] = 0; 863 zPwd[n] = 0; 856 } 864 } 857 g.zLocalRoot = mprintf("%s/", zPwd); 865 g.zLocalRoot = mprintf("%s/", zPwd); > 866 g.localOpen = 1; > 867 db_open_config(0); > 868 db_open_repository(0); 858 return 1; 869 return 1; 859 } 870 } 860 } 871 } 861 n--; 872 n--; 862 while( n>0 && zPwd[n]!='/' ){ n--; } 873 while( n>0 && zPwd[n]!='/' ){ n--; } 863 while( n>0 && zPwd[n-1]=='/' ){ n--; } 874 while( n>0 && zPwd[n-1]=='/' ){ n--; } 864 zPwd[n] = 0; 875 zPwd[n] = 0; 865 } 876 } 866 877 867 /* A checkout database file could not be found */ 878 /* A checkout database file could not be found */ 868 return 0; 879 return 0; 869 } 880 } > 881 > 882 /* > 883 ** Get the full pathname to the repository database file. The > 884 ** local database (the _FOSSIL_ or .fslckout database) must have already > 885 ** been opened before this routine is called. > 886 */ > 887 const char *db_repository_filename(void){ > 888 static char *zRepo = 0; > 889 assert( g.localOpen ); > 890 assert( g.zLocalRoot ); > 891 if( zRepo==0 ){ > 892 zRepo = db_lget("repository", 0); > 893 if( zRepo && !file_is_absolute_path(zRepo) ){ > 894 zRepo = mprintf("%s%s", g.zLocalRoot, zRepo); > 895 } > 896 } > 897 return zRepo; > 898 } 870 899 871 /* 900 /* 872 ** Open the repository database given by zDbName. If zDbName==NULL then 901 ** Open the repository database given by zDbName. If zDbName==NULL then 873 ** get the name from the already open local database. 902 ** get the name from the already open local database. 874 */ 903 */ 875 void db_open_repository(const char *zDbName){ 904 void db_open_repository(const char *zDbName){ 876 if( g.repositoryOpen ) return; 905 if( g.repositoryOpen ) return; 877 if( zDbName==0 ){ 906 if( zDbName==0 ){ 878 if( g.localOpen ){ 907 if( g.localOpen ){ 879 zDbName = db_lget("repository", 0); | 908 zDbName = db_repository_filename(); 880 } 909 } 881 if( zDbName==0 ){ 910 if( zDbName==0 ){ 882 db_err("unable to find the name of a repository database"); 911 db_err("unable to find the name of a repository database"); 883 } 912 } 884 } 913 } 885 if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){ 914 if( file_access(zDbName, R_OK) || file_size(zDbName)<1024 ){ 886 if( file_access(zDbName, 0) ){ 915 if( file_access(zDbName, 0) ){ ................................................................................................................................................................................ 928 if( zRep==0 && nArgUsed && g.argc==nArgUsed+1 ){ 957 if( zRep==0 && nArgUsed && g.argc==nArgUsed+1 ){ 929 zRep = g.argv[nArgUsed]; 958 zRep = g.argv[nArgUsed]; 930 } 959 } 931 if( zRep==0 ){ 960 if( zRep==0 ){ 932 if( db_open_local()==0 ){ 961 if( db_open_local()==0 ){ 933 goto rep_not_found; 962 goto rep_not_found; 934 } 963 } 935 zRep = db_lget("repository", 0)/*leak here*/; | 964 zRep = db_repository_filename(); 936 if( zRep==0 ){ 965 if( zRep==0 ){ 937 goto rep_not_found; 966 goto rep_not_found; 938 } 967 } 939 } 968 } 940 db_open_repository(zRep); 969 db_open_repository(zRep); 941 if( g.repositoryOpen ){ 970 if( g.repositoryOpen ){ 942 if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema(); 971 if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema(); ................................................................................................................................................................................ 1059 fprintf(stderr, "-- MEMORY_USED %10d %10d\n", cur, hiwtr); 1088 fprintf(stderr, "-- MEMORY_USED %10d %10d\n", cur, hiwtr); 1060 sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &cur, &hiwtr, 0); 1089 sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &cur, &hiwtr, 0); 1061 fprintf(stderr, "-- MALLOC_SIZE %10d\n", hiwtr); 1090 fprintf(stderr, "-- MALLOC_SIZE %10d\n", hiwtr); 1062 sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &cur, &hiwtr, 0); 1091 sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &cur, &hiwtr, 0); 1063 fprintf(stderr, "-- MALLOC_COUNT %10d %10d\n", cur, hiwtr); 1092 fprintf(stderr, "-- MALLOC_COUNT %10d %10d\n", cur, hiwtr); 1064 sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0); 1093 sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0); 1065 fprintf(stderr, "-- PCACHE_OVFLOW %10d %10d\n", cur, hiwtr); 1094 fprintf(stderr, "-- PCACHE_OVFLOW %10d %10d\n", cur, hiwtr); 1066 fprintf(stderr, "-- prepared statements %10d\n", nPrepare); | 1095 fprintf(stderr, "-- prepared statements %10d\n", db.nPrepare); 1067 } 1096 } 1068 while( pAllStmt ){ | 1097 while( db.pAllStmt ){ 1069 db_finalize(pAllStmt); | 1098 db_finalize(db.pAllStmt); 1070 } 1099 } 1071 db_end_transaction(1); 1100 db_end_transaction(1); 1072 pStmt = 0; 1101 pStmt = 0; 1073 if( reportErrors ){ 1102 if( reportErrors ){ 1074 while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){ 1103 while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){ 1075 fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt)); 1104 fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt)); 1076 } 1105 } ................................................................................................................................................................................ 1111 void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ 1140 void db_create_default_users(int setupUserOnly, const char *zDefaultUser){ 1112 const char *zUser = zDefaultUser; 1141 const char *zUser = zDefaultUser; 1113 if( zUser==0 ){ 1142 if( zUser==0 ){ 1114 zUser = db_get("default-user", 0); 1143 zUser = db_get("default-user", 0); 1115 } 1144 } 1116 if( zUser==0 ){ 1145 if( zUser==0 ){ 1117 #if defined(_WIN32) 1146 #if defined(_WIN32) 1118 zUser = getenv("USERNAME"); | 1147 zUser = fossil_getenv("USERNAME"); 1119 #else 1148 #else 1120 zUser = getenv("USER"); | 1149 zUser = fossil_getenv("USER"); 1121 #endif 1150 #endif 1122 } 1151 } 1123 if( zUser==0 ){ 1152 if( zUser==0 ){ 1124 zUser = "root"; 1153 zUser = "root"; 1125 } 1154 } 1126 db_multi_exec( 1155 db_multi_exec( 1127 "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser 1156 "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser ................................................................................................................................................................................ 1665 ** 1694 ** 1666 ** Where %s is the checkout root. The value is the repository file. 1695 ** Where %s is the checkout root. The value is the repository file. 1667 */ 1696 */ 1668 void db_record_repository_filename(const char *zName){ 1697 void db_record_repository_filename(const char *zName){ 1669 Blob full; 1698 Blob full; 1670 if( zName==0 ){ 1699 if( zName==0 ){ 1671 if( !g.localOpen ) return; 1700 if( !g.localOpen ) return; 1672 zName = db_lget("repository", 0); | 1701 zName = db_repository_filename(); 1673 } 1702 } 1674 file_canonical_name(zName, &full); 1703 file_canonical_name(zName, &full); 1675 db_swap_connections(); 1704 db_swap_connections(); 1676 db_multi_exec( 1705 db_multi_exec( 1677 "INSERT OR IGNORE INTO global_config(name,value)" 1706 "INSERT OR IGNORE INTO global_config(name,value)" 1678 "VALUES('repo:%q',1)", 1707 "VALUES('repo:%q',1)", 1679 blob_str(&full) 1708 blob_str(&full) ................................................................................................................................................................................ 1723 fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); 1752 fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); 1724 } 1753 } 1725 file_canonical_name(g.argv[2], &path); 1754 file_canonical_name(g.argv[2], &path); 1726 db_open_repository(blob_str(&path)); 1755 db_open_repository(blob_str(&path)); 1727 db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); 1756 db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); 1728 db_delete_on_failure("./_FOSSIL_"); 1757 db_delete_on_failure("./_FOSSIL_"); 1729 db_open_local(); 1758 db_open_local(); 1730 db_lset("repository", blob_str(&path)); | 1759 db_lset("repository", g.argv[2]); 1731 db_record_repository_filename(blob_str(&path)); 1760 db_record_repository_filename(blob_str(&path)); 1732 vid = db_int(0, "SELECT pid FROM plink y" 1761 vid = db_int(0, "SELECT pid FROM plink y" 1733 " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)"); 1762 " WHERE NOT EXISTS(SELECT 1 FROM plink x WHERE x.cid=y.pid)"); 1734 if( vid==0 ){ 1763 if( vid==0 ){ 1735 db_lset_int("checkout", 1); 1764 db_lset_int("checkout", 1); 1736 }else{ 1765 }else{ 1737 char **oldArgv = g.argv; 1766 char **oldArgv = g.argv; ................................................................................................................................................................................ 1850 { 0,0,0,0,0 } 1879 { 0,0,0,0,0 } 1851 }; 1880 }; 1852 1881 1853 /* 1882 /* 1854 ** COMMAND: settings 1883 ** COMMAND: settings 1855 ** COMMAND: unset* 1884 ** COMMAND: unset* 1856 ** 1885 ** 1857 ** %fossil settings ?PROPERTY? ?VALUE? ?-global? | 1886 ** %fossil settings ?PROPERTY? ?VALUE? ?OPTIONS? 1858 ** %fossil unset PROPERTY ?-global? | 1887 ** %fossil unset PROPERTY ?OPTIONS? 1859 ** 1888 ** 1860 ** The "settings" command with no arguments lists all properties and their 1889 ** The "settings" command with no arguments lists all properties and their 1861 ** values. With just a property name it shows the value of that property. 1890 ** values. With just a property name it shows the value of that property. 1862 ** With a value argument it changes the property for the current repository. 1891 ** With a value argument it changes the property for the current repository. 1863 ** 1892 ** 1864 ** Settings marked as versionable are overridden by the contents of the 1893 ** Settings marked as versionable are overridden by the contents of the 1865 ** file named .fossil-settings/PROPERTY in the checked out files, if that 1894 ** file named .fossil-settings/PROPERTY in the checked out files, if that ................................................................................................................................................................................ 2005 ** Tcl interpreter will be able to evaluate TH1 expressions 2034 ** Tcl interpreter will be able to evaluate TH1 expressions 2006 ** and scripts. Default: off. 2035 ** and scripts. Default: off. 2007 ** 2036 ** 2008 ** web-browser A shell command used to launch your preferred 2037 ** web-browser A shell command used to launch your preferred 2009 ** web browser when given a URL as an argument. 2038 ** web browser when given a URL as an argument. 2010 ** Defaults to "start" on windows, "open" on Mac, 2039 ** Defaults to "start" on windows, "open" on Mac, 2011 ** and "firefox" on Unix. 2040 ** and "firefox" on Unix. > 2041 ** > 2042 ** Options: > 2043 ** --global set or unset the given property globally instead of > 2044 ** setting or unsetting it for the open repository only. > 2045 ** > 2046 ** See also: configuration 2012 */ 2047 */ 2013 void setting_cmd(void){ 2048 void setting_cmd(void){ 2014 int i; 2049 int i; 2015 int globalFlag = find_option("global","g",0)!=0; 2050 int globalFlag = find_option("global","g",0)!=0; 2016 int unsetFlag = g.argv[1][0]=='u'; 2051 int unsetFlag = g.argv[1][0]=='u'; 2017 db_open_config(1); 2052 db_open_config(1); 2018 if( !globalFlag ){ 2053 if( !globalFlag ){

Changes to src/diff.c

23 #include <assert.h> 23 #include <assert.h> 24 24 25 25 26 #if INTERFACE 26 #if INTERFACE 27 /* 27 /* 28 ** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions: 28 ** Allowed flag parameters to the text_diff() and html_sbsdiff() funtions: 29 */ 29 */ 30 #define DIFF_CONTEXT_MASK 0x0000fff /* Lines of context. Default if 0 */ | 30 #define DIFF_CONTEXT_MASK 0x0000ffff /* Lines of context. Default if 0 */ 31 #define DIFF_WIDTH_MASK 0x00ff000 /* side-by-side column width */ | 31 #define DIFF_WIDTH_MASK 0x00ff0000 /* side-by-side column width */ 32 #define DIFF_IGNORE_EOLWS 0x0100000 /* Ignore end-of-line whitespace */ | 32 #define DIFF_IGNORE_EOLWS 0x01000000 /* Ignore end-of-line whitespace */ 33 #define DIFF_SIDEBYSIDE 0x0200000 /* Generate a side-by-side diff */ | 33 #define DIFF_SIDEBYSIDE 0x02000000 /* Generate a side-by-side diff */ 34 #define DIFF_NEWFILE 0x0400000 /* Missing files are as empty files */ | 34 #define DIFF_NEWFILE 0x04000000 /* Missing files are as empty files */ > 35 #define DIFF_BRIEF 0x08000000 /* Show filenames only */ > 36 #define DIFF_INLINE 0x00000000 /* Inline (not side-by-side) diff */ > 37 #define DIFF_HTML 0x10000000 /* Render for HTML */ > 38 #define DIFF_LINENO 0x20000000 /* Show line numbers in context diff */ > 39 #define DIFF_NOOPT 0x40000000 /* Suppress optimizations for debug */ > 40 #define DIFF_INVERT 0x80000000 /* Invert the diff for debug */ 35 41 36 #endif /* INTERFACE */ 42 #endif /* INTERFACE */ 37 43 38 /* 44 /* 39 ** Maximum length of a line in a text file. (8192) 45 ** Maximum length of a line in a text file. (8192) 40 */ 46 */ 41 #define LENGTH_MASK_SZ 13 47 #define LENGTH_MASK_SZ 13 ................................................................................................................................................................................ 57 /* an array of DLine elements services two purposes. The fields 63 /* an array of DLine elements services two purposes. The fields 58 ** above are one per line of input text. But each entry is also 64 ** above are one per line of input text. But each entry is also 59 ** a bucket in a hash table, as follows: */ 65 ** a bucket in a hash table, as follows: */ 60 unsigned int iHash; /* 1+(first entry in the hash chain) */ 66 unsigned int iHash; /* 1+(first entry in the hash chain) */ 61 }; 67 }; 62 68 63 /* 69 /* > 70 ** Length of a dline > 71 */ > 72 #define LENGTH(X) ((X)->h & LENGTH_MASK) > 73 > 74 /* 64 ** A context for running a diff. | 75 ** A context for running a raw diff. > 76 ** > 77 ** The aEdit[] array describes the raw diff. Each triple of integers in > 78 ** aEdit[] means: > 79 ** > 80 ** (1) COPY: Number of lines aFrom and aTo have in common > 81 ** (2) DELETE: Number of lines found only in aFrom > 82 ** (3) INSERT: Number of lines found only in aTo > 83 ** > 84 ** The triples repeat until all lines of both aFrom and aTo are accounted > 85 ** for. 65 */ 86 */ 66 typedef struct DContext DContext; 87 typedef struct DContext DContext; 67 struct DContext { 88 struct DContext { 68 int *aEdit; /* Array of copy/delete/insert triples */ 89 int *aEdit; /* Array of copy/delete/insert triples */ 69 int nEdit; /* Number of integers (3x num of triples) in aEdit[] */ 90 int nEdit; /* Number of integers (3x num of triples) in aEdit[] */ 70 int nEditAlloc; /* Space allocated for aEdit[] */ 91 int nEditAlloc; /* Space allocated for aEdit[] */ 71 DLine *aFrom; /* File on left side of the diff */ 92 DLine *aFrom; /* File on left side of the diff */ ................................................................................................................................................................................ 142 ** Return true if two DLine elements are identical. 163 ** Return true if two DLine elements are identical. 143 */ 164 */ 144 static int same_dline(DLine *pA, DLine *pB){ 165 static int same_dline(DLine *pA, DLine *pB){ 145 return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0; 166 return pA->h==pB->h && memcmp(pA->z,pB->z,pA->h & LENGTH_MASK)==0; 146 } 167 } 147 168 148 /* 169 /* 149 ** Append a single line of "diff" output to pOut. | 170 ** Append a single line of context-diff output to pOut. 150 */ 171 */ 151 static void appendDiffLine(Blob *pOut, char *zPrefix, DLine *pLine){ | 172 static void appendDiffLine( > 173 Blob *pOut, /* Where to write the line of output */ > 174 char cPrefix, /* One of " ", "+", or "-" */ > 175 DLine *pLine, /* The line to be output */ > 176 int html /* True if generating HTML. False for plain text */ > 177 ){ 152 blob_append(pOut, zPrefix, 1); | 178 blob_append(pOut, &cPrefix, 1); > 179 if( html ){ > 180 char *zHtml; > 181 if( cPrefix=='+' ){ > 182 blob_append(pOut, "<span class=\"diffadd\">", -1); > 183 }else if( cPrefix=='-' ){ > 184 blob_append(pOut, "<span class=\"diffrm\">", -1); > 185 } 153 blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK); | 186 zHtml = htmlize(pLine->z, (pLine->h & LENGTH_MASK)); 154 blob_append(pOut, "\n", 1); | 187 blob_append(pOut, zHtml, -1); > 188 fossil_free(zHtml); > 189 if( cPrefix!=' ' ){ > 190 blob_append(pOut, "</span>", -1); 155 } | 191 } > 192 }else{ > 193 blob_append(pOut, pLine->z, pLine->h & LENGTH_MASK); 156 | 194 } 157 /* < 158 ** Expand the size of aEdit[] array to hold nEdit elements. < 159 */ < 160 static void expandEdit(DContext *p, int nEdit){ < 161 p->aEdit = fossil_realloc(p->aEdit, nEdit*sizeof(int)); < 162 p->nEditAlloc = nEdit; < > 195 blob_append(pOut, "\n", 1); 163 } 196 } 164 197 165 /* 198 /* 166 ** Append a new COPY/DELETE/INSERT triple. < > 199 ** Add two line numbers to the beginning of an output line for a context > 200 ** diff. One or of the other of the two numbers might be zero, which means > 201 ** to leave that number field blank. The "html" parameter means to format > 202 ** the output for HTML. 167 */ 203 */ 168 static void appendTriple(DContext *p, int nCopy, int nDel, int nIns){ < 169 /* printf("APPEND %d/%d/%d\n", nCopy, nDel, nIns); */ < 170 if( p->nEdit>=3 ){ < 171 if( p->aEdit[p->nEdit-1]==0 ){ < 172 if( p->aEdit[p->nEdit-2]==0 ){ < 173 p->aEdit[p->nEdit-3] += nCopy; < 174 p->aEdit[p->nEdit-2] += nDel; < 175 p->aEdit[p->nEdit-1] += nIns; < 176 return; < 177 } < 178 if( nCopy==0 ){ < 179 p->aEdit[p->nEdit-2] += nDel; < 180 p->aEdit[p->nEdit-1] += nIns; < 181 return; < 182 } < 183 } < > 204 static void appendDiffLineno(Blob *pOut, int lnA, int lnB, int html){ 184 if( nCopy==0 && nDel==0 ){ | 205 if( html ) blob_append(pOut, "<span class=\"diffln\">", -1); 185 p->aEdit[p->nEdit-1] += nIns; | 206 if( lnA>0 ){ 186 return; | 207 blob_appendf(pOut, "%6d ", lnA); 187 } | 208 }else{ > 209 blob_append(pOut, " ", 7); 188 } | 210 } 189 if( p->nEdit+3>p->nEditAlloc ){ < 190 expandEdit(p, p->nEdit*2 + 15); | 211 if( lnB>0 ){ 191 if( p->aEdit==0 ) return; | 212 blob_appendf(pOut, "%6d ", lnB); > 213 }else{ > 214 blob_append(pOut, " ", 8); 192 } 215 } 193 p->aEdit[p->nEdit++] = nCopy; < 194 p->aEdit[p->nEdit++] = nDel; < 195 p->aEdit[p->nEdit++] = nIns; | 216 if( html ) blob_append(pOut, "</span>", -1); 196 } 217 } 197 218 198 219 199 /* 220 /* 200 ** Given a diff context in which the aEdit[] array has been filled 221 ** Given a diff context in which the aEdit[] array has been filled 201 ** in, compute a context diff into pOut. 222 ** in, compute a context diff into pOut. 202 */ 223 */ 203 static void contextDiff(DContext *p, Blob *pOut, int nContext){ | 224 static void contextDiff( > 225 DContext *p, /* The difference */ > 226 Blob *pOut, /* Output a context diff to here */ > 227 int nContext, /* Number of lines of context */ > 228 int showLn, /* Show line numbers */ > 229 int html /* Render as HTML */ > 230 ){ 204 DLine *A; /* Left side of the diff */ 231 DLine *A; /* Left side of the diff */ 205 DLine *B; /* Right side of the diff */ 232 DLine *B; /* Right side of the diff */ 206 int a = 0; /* Index of next line in A[] */ 233 int a = 0; /* Index of next line in A[] */ 207 int b = 0; /* Index of next line in B[] */ 234 int b = 0; /* Index of next line in B[] */ 208 int *R; /* Array of COPY/DELETE/INSERT triples */ 235 int *R; /* Array of COPY/DELETE/INSERT triples */ 209 int r; /* Index into R[] */ 236 int r; /* Index into R[] */ 210 int nr; /* Number of COPY/DELETE/INSERT triples to process */ 237 int nr; /* Number of COPY/DELETE/INSERT triples to process */ 211 int mxr; /* Maximum value for r */ 238 int mxr; /* Maximum value for r */ 212 int na, nb; /* Number of lines shown from A and B */ 239 int na, nb; /* Number of lines shown from A and B */ 213 int i, j; /* Loop counters */ 240 int i, j; /* Loop counters */ 214 int m; /* Number of lines to output */ 241 int m; /* Number of lines to output */ 215 int skip; /* Number of lines to skip */ 242 int skip; /* Number of lines to skip */ > 243 int nChunk = 0; /* Number of diff chunks seen so far */ 216 244 217 A = p->aFrom; 245 A = p->aFrom; 218 B = p->aTo; 246 B = p->aTo; 219 R = p->aEdit; 247 R = p->aEdit; 220 mxr = p->nEdit; 248 mxr = p->nEdit; 221 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } 249 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } 222 for(r=0; r<mxr; r += 3*nr){ 250 for(r=0; r<mxr; r += 3*nr){ ................................................................................................................................................................................ 245 na += R[r+nr*3]; 273 na += R[r+nr*3]; 246 nb += R[r+nr*3]; 274 nb += R[r+nr*3]; 247 } 275 } 248 for(i=1; i<nr; i++){ 276 for(i=1; i<nr; i++){ 249 na += R[r+i*3]; 277 na += R[r+i*3]; 250 nb += R[r+i*3]; 278 nb += R[r+i*3]; 251 } 279 } > 280 > 281 /* Show the header for this block, or if we are doing a modified > 282 ** context diff that contains line numbers, show the separate from > 283 ** the previous block. > 284 */ > 285 nChunk++; > 286 if( showLn ){ > 287 if( r==0 ){ > 288 /* Do not show a top divider */ > 289 }else if( html ){ > 290 blob_appendf(pOut, "<span class=\"diffhr\">%.80c</span>\n", '.'); > 291 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk); > 292 }else{ > 293 blob_appendf(pOut, "%.80c\n", '.'); > 294 } > 295 }else{ > 296 if( html ) blob_appendf(pOut, "<span class=\"diffln\">"); 252 /* | 297 /* 253 * If the patch changes an empty file or results in an empty file, | 298 * If the patch changes an empty file or results in an empty file, 254 * the block header must use 0,0 as position indicator and not 1,0. | 299 * the block header must use 0,0 as position indicator and not 1,0. 255 * Otherwise, patch would be confused and may reject the diff. | 300 * Otherwise, patch would be confused and may reject the diff. 256 */ | 301 */ 257 blob_appendf(pOut,"@@ -%d,%d +%d,%d @@\n", | 302 blob_appendf(pOut,"@@ -%d,%d +%d,%d @@", 258 na ? a+skip+1 : 0, na, | 303 na ? a+skip+1 : 0, na, 259 nb ? b+skip+1 : 0, nb); | 304 nb ? b+skip+1 : 0, nb); > 305 if( html ) blob_appendf(pOut, "</span>"); > 306 blob_append(pOut, "\n", 1); > 307 } 260 308 261 /* Show the initial common area */ 309 /* Show the initial common area */ 262 a += skip; 310 a += skip; 263 b += skip; 311 b += skip; 264 m = R[r] - skip; 312 m = R[r] - skip; 265 for(j=0; j<m; j++){ 313 for(j=0; j<m; j++){ > 314 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html); 266 appendDiffLine(pOut, " ", &A[a+j]); | 315 appendDiffLine(pOut, ' ', &A[a+j], html); 267 } 316 } 268 a += m; 317 a += m; 269 b += m; 318 b += m; 270 319 271 /* Show the differences */ 320 /* Show the differences */ 272 for(i=0; i<nr; i++){ 321 for(i=0; i<nr; i++){ 273 m = R[r+i*3+1]; 322 m = R[r+i*3+1]; 274 for(j=0; j<m; j++){ 323 for(j=0; j<m; j++){ > 324 if( showLn ) appendDiffLineno(pOut, a+j+1, 0, html); 275 appendDiffLine(pOut, "-", &A[a+j]); | 325 appendDiffLine(pOut, '-', &A[a+j], html); 276 } 326 } 277 a += m; 327 a += m; 278 m = R[r+i*3+2]; 328 m = R[r+i*3+2]; 279 for(j=0; j<m; j++){ 329 for(j=0; j<m; j++){ > 330 if( showLn ) appendDiffLineno(pOut, 0, b+j+1, html); 280 appendDiffLine(pOut, "+", &B[b+j]); | 331 appendDiffLine(pOut, '+', &B[b+j], html); 281 } 332 } 282 b += m; 333 b += m; 283 if( i<nr-1 ){ 334 if( i<nr-1 ){ 284 m = R[r+i*3+3]; 335 m = R[r+i*3+3]; 285 for(j=0; j<m; j++){ 336 for(j=0; j<m; j++){ > 337 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html); 286 appendDiffLine(pOut, " ", &B[b+j]); | 338 appendDiffLine(pOut, ' ', &B[b+j], html); 287 } 339 } 288 b += m; 340 b += m; 289 a += m; 341 a += m; 290 } 342 } 291 } 343 } 292 344 293 /* Show the final common area */ 345 /* Show the final common area */ 294 assert( nr==i ); 346 assert( nr==i ); 295 m = R[r+nr*3]; 347 m = R[r+nr*3]; 296 if( m>nContext ) m = nContext; 348 if( m>nContext ) m = nContext; 297 for(j=0; j<m; j++){ 349 for(j=0; j<m; j++){ > 350 if( showLn ) appendDiffLineno(pOut, a+j+1, b+j+1, html); 298 appendDiffLine(pOut, " ", &B[b+j]); | 351 appendDiffLine(pOut, ' ', &B[b+j], html); 299 } 352 } 300 } 353 } 301 } 354 } 302 355 303 /* 356 /* 304 ** Write a 6-digit line number into the buffer z[]. z[] is guaranteed to | 357 ** Status of a single output line 305 ** have space for at least 7 characters. < 306 */ 358 */ > 359 typedef struct SbsLine SbsLine; 307 static void sbsWriteLineno(char *z, int ln){ | 360 struct SbsLine { 308 sqlite3_snprintf(7, z, "%6d", ln+1); | 361 char *zLine; /* The output line under construction */ 309 z[6] = ' '; | 362 int n; /* Index of next unused slot in the zLine[] */ > 363 int width; /* Maximum width of a column in the output */ > 364 unsigned char escHtml; /* True to escape html characters */ > 365 int iStart; /* Write zStart prior to character iStart */ > 366 const char *zStart; /* A <span> tag */ > 367 int iEnd; /* Write </span> prior to character iEnd */ > 368 int iStart2; /* Write zStart2 prior to character iStart2 */ > 369 const char *zStart2; /* A <span> tag */ > 370 int iEnd2; /* Write </span> prior to character iEnd2 */ 310 } | 371 }; 311 372 312 /* 373 /* > 374 ** Flags for sbsWriteText() > 375 */ > 376 #define SBS_NEWLINE 0x0001 /* End with \n\000 */ > 377 #define SBS_PAD 0x0002 /* Pad output to width spaces */ > 378 > 379 /* 313 ** Write up to width characters of pLine into z[]. Translate tabs into 380 ** 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. < > 381 ** spaces. Add a newline if SBS_NEWLINE is set. Translate HTML characters > 382 ** if SBS_HTML is set. Pad the rendering out width bytes if SBS_PAD is set. 316 */ 383 */ 317 static int sbsWriteText(char *z, DLine *pLine, int width, int trunc){ < > 384 static void sbsWriteText(SbsLine *p, DLine *pLine, unsigned flags){ 318 int n = pLine->h & LENGTH_MASK; 385 int n = pLine->h & LENGTH_MASK; 319 int i, j; < > 386 int i; /* Number of input characters consumed */ > 387 int j; /* Number of output characters generated */ > 388 int k; /* Cursor position */ > 389 int needEndSpan = 0; 320 const char *zIn = pLine->z; 390 const char *zIn = pLine->z; 321 for(i=j=0; i<n && j<width; i++){ < > 391 char *z = &p->zLine[p->n]; > 392 int w = p->width; > 393 for(i=j=k=0; k<w && i<n; i++, k++){ 322 char c = zIn[i]; 394 char c = zIn[i]; > 395 if( p->escHtml ){ > 396 if( i==p->iStart ){ > 397 int x = strlen(p->zStart); > 398 memcpy(z+j, p->zStart, x); > 399 j += x; > 400 needEndSpan = 1; > 401 if( p->iStart2 ){ > 402 p->iStart = p->iStart2; > 403 p->zStart = p->zStart2; > 404 p->iStart2 = 0; > 405 } > 406 }else if( i==p->iEnd ){ > 407 memcpy(z+j, "</span>", 7); > 408 j += 7; > 409 needEndSpan = 0; > 410 if( p->iEnd2 ){ > 411 p->iEnd = p->iEnd2; > 412 p->iEnd2 = 0; > 413 } > 414 } > 415 } 323 if( c=='\t' ){ 416 if( c=='\t' ){ 324 z[j++] = ' '; 417 z[j++] = ' '; 325 while( (j&7)!=0 && j<width ) z[j++] = ' '; < > 418 while( (k&7)!=7 && k<w ){ z[j++] = ' '; k++; } 326 }else if( c=='\r' || c=='\f' ){ 419 }else if( c=='\r' || c=='\f' ){ 327 z[j++] = ' '; 420 z[j++] = ' '; > 421 }else if( c=='<' && p->escHtml ){ > 422 memcpy(&z[j], "&lt;", 4); > 423 j += 4; > 424 }else if( c=='&' && p->escHtml ){ > 425 memcpy(&z[j], "&amp;", 5); > 426 j += 5; > 427 }else if( c=='>' && p->escHtml ){ > 428 memcpy(&z[j], "&gt;", 4); > 429 j += 4; 328 }else{ 430 }else{ 329 z[j++] = c; 431 z[j++] = c; 330 } 432 } 331 } 433 } 332 if( trunc ){ | 434 if( needEndSpan ){ > 435 memcpy(&z[j], "</span>", 7); > 436 j += 7; > 437 } > 438 if( (flags & SBS_PAD)!=0 ){ > 439 while( k<w ){ k++; z[j++] = ' '; } > 440 } > 441 if( flags & SBS_NEWLINE ){ 333 z[j++] = '\n'; 442 z[j++] = '\n'; > 443 } > 444 p->n += j; > 445 } > 446 > 447 /* > 448 ** Append a string to an SbSLine with coding, interpretation, or padding. > 449 */ > 450 static void sbsWrite(SbsLine *p, const char *zIn, int nIn){ > 451 memcpy(p->zLine+p->n, zIn, nIn); > 452 p->n += nIn; > 453 } > 454 > 455 /* > 456 ** Append n spaces to the string. > 457 */ > 458 static void sbsWriteSpace(SbsLine *p, int n){ > 459 while( n-- ) p->zLine[p->n++] = ' '; > 460 } > 461 > 462 /* > 463 ** Append a string to the output only if we are rendering HTML. > 464 */ > 465 static void sbsWriteHtml(SbsLine *p, const char *zIn){ > 466 if( p->escHtml ) sbsWrite(p, zIn, strlen(zIn)); > 467 } > 468 > 469 /* > 470 ** Write a 6-digit line number followed by a single space onto the line. > 471 */ > 472 static void sbsWriteLineno(SbsLine *p, int ln){ > 473 sbsWriteHtml(p, "<span class=\"diffln\">"); > 474 sqlite3_snprintf(7, &p->zLine[p->n], "%5d ", ln+1); > 475 p->n += 6; > 476 sbsWriteHtml(p, "</span>"); > 477 p->zLine[p->n++] = ' '; > 478 } > 479 > 480 /* > 481 ** The two text segments zLeft and zRight are known to be different on > 482 ** both ends, but they might have a common segment in the middle. If > 483 ** they do not have a common segment, return 0. If they do have a large > 484 ** common segment, return 1 and before doing so set: > 485 ** > 486 ** aLCS[0] = start of the common segment in zLeft > 487 ** aLCS[1] = end of the common segment in zLeft > 488 ** aLCS[2] = start of the common segment in zLeft > 489 ** aLCS[3] = end of the common segment in zLeft > 490 ** > 491 ** This computation is for display purposes only and does not have to be > 492 ** optimal or exact. > 493 */ > 494 static int textLCS( > 495 const char *zLeft, int nA, /* String on the left */ > 496 const char *zRight, int nB, /* String on the right */ > 497 int *aLCS /* Identify bounds of LCS here */ > 498 ){ > 499 const unsigned char *zA = (const unsigned char*)zLeft; /* left string */ > 500 const unsigned char *zB = (const unsigned char*)zRight; /* right string */ > 501 int nt; /* Number of target points */ > 502 int ti[3]; /* Index for start of each 4-byte target */ > 503 unsigned int target[3]; /* 4-byte alignment targets */ > 504 unsigned int probe; /* probe to compare against target */ > 505 int iAS, iAE, iBS, iBE; /* Range of common segment */ > 506 int i, j; /* Loop counters */ > 507 int rc = 0; /* Result code. 1 for success */ > 508 > 509 if( nA<6 || nB<6 ) return 0; > 510 memset(aLCS, 0, sizeof(int)*4); > 511 ti[0] = i = nB/2-2; > 512 target[0] = (zB[i]<<24) | (zB[i+1]<<16) | (zB[i+2]<<8) | zB[i+3]; 334 z[j] = 0; | 513 probe = 0; > 514 if( nB<16 ){ > 515 nt = 1; > 516 }else{ > 517 ti[1] = i = nB/4-2; > 518 target[1] = (zB[i]<<24) | (zB[i+1]<<16) | (zB[i+2]<<8) | zB[i+3]; > 519 ti[2] = i = (nB*3)/4-2; > 520 target[2] = (zB[i]<<24) | (zB[i+1]<<16) | (zB[i+2]<<8) | zB[i+3]; > 521 nt = 3; 335 } 522 } > 523 probe = (zA[0]<<16) | (zA[1]<<8) | zA[2]; > 524 for(i=3; i<nA; i++){ > 525 probe = (probe<<8) | zA[i]; > 526 for(j=0; j<nt; j++){ > 527 if( probe==target[j] ){ > 528 iAS = i-3; > 529 iAE = i+1; > 530 iBS = ti[j]; > 531 iBE = ti[j]+4; > 532 while( iAE<nA && iBE<nB && zA[iAE]==zB[iBE] ){ iAE++; iBE++; } > 533 while( iAS>0 && iBS>0 && zA[iAS-1]==zB[iBS-1] ){ iAS--; iBS--; } > 534 if( iAE-iAS > aLCS[1] - aLCS[0] ){ > 535 aLCS[0] = iAS; > 536 aLCS[1] = iAE; > 537 aLCS[2] = iBS; > 538 aLCS[3] = iBE; > 539 rc = 1; > 540 } > 541 } > 542 } > 543 } 336 return j; | 544 return rc; 337 } 545 } 338 546 > 547 /* > 548 ** Write out lines that have been edited. Adjust the highlight to cover > 549 ** only those parts of the line that actually changed. > 550 */ > 551 static void sbsWriteLineChange( > 552 SbsLine *p, /* The SBS output line */ > 553 DLine *pLeft, /* Left line of the change */ > 554 int lnLeft, /* Line number for the left line */ > 555 DLine *pRight, /* Right line of the change */ > 556 int lnRight /* Line number of the right line */ > 557 ){ > 558 int nLeft; /* Length of left line in bytes */ > 559 int nRight; /* Length of right line in bytes */ > 560 int nPrefix; /* Length of common prefix */ > 561 int nSuffix; /* Length of common suffix */ > 562 const char *zLeft; /* Text of the left line */ > 563 const char *zRight; /* Text of the right line */ > 564 int nLeftDiff; /* nLeft - nPrefix - nSuffix */ > 565 int nRightDiff; /* nRight - nPrefix - nSuffix */ > 566 int aLCS[4]; /* Bounds of common middle segment */ > 567 static const char zClassRm[] = "<span class=\"diffrm\">"; > 568 static const char zClassAdd[] = "<span class=\"diffadd\">"; > 569 static const char zClassChng[] = "<span class=\"diffchng\">"; 339 570 > 571 nLeft = pLeft->h & LENGTH_MASK; > 572 zLeft = pLeft->z; > 573 nRight = pRight->h & LENGTH_MASK; > 574 zRight = pRight->z; > 575 > 576 nPrefix = 0; > 577 while( nPrefix<nLeft && nPrefix<nRight && zLeft[nPrefix]==zRight[nPrefix] ){ > 578 nPrefix++; > 579 } > 580 nSuffix = 0; > 581 if( nPrefix<nLeft && nPrefix<nRight ){ > 582 while( nSuffix<nLeft && nSuffix<nRight > 583 && zLeft[nLeft-nSuffix-1]==zRight[nRight-nSuffix-1] ){ > 584 nSuffix++; > 585 } > 586 if( nSuffix==nLeft || nSuffix==nRight ) nPrefix = 0; > 587 } > 588 if( nPrefix+nSuffix > nLeft ) nSuffix = nLeft - nPrefix; > 589 if( nPrefix+nSuffix > nRight ) nSuffix = nRight - nPrefix; > 590 > 591 /* A single chunk of text inserted on the right */ > 592 if( nPrefix+nSuffix==nLeft ){ > 593 sbsWriteLineno(p, lnLeft); > 594 p->iStart2 = p->iEnd2 = 0; > 595 p->iStart = p->iEnd = -1; > 596 sbsWriteText(p, pLeft, SBS_PAD); > 597 sbsWrite(p, " | ", 3); > 598 sbsWriteLineno(p, lnRight); > 599 p->iStart = nPrefix; > 600 p->iEnd = nRight - nSuffix; > 601 p->zStart = zClassAdd; > 602 sbsWriteText(p, pRight, SBS_NEWLINE); > 603 return; > 604 } > 605 > 606 /* A single chunk of text deleted from the left */ > 607 if( nPrefix+nSuffix==nRight ){ > 608 /* Text deleted from the left */ > 609 sbsWriteLineno(p, lnLeft); > 610 p->iStart2 = p->iEnd2 = 0; > 611 p->iStart = nPrefix; > 612 p->iEnd = nLeft - nSuffix; > 613 p->zStart = zClassRm; > 614 sbsWriteText(p, pLeft, SBS_PAD); > 615 sbsWrite(p, " | ", 3); > 616 sbsWriteLineno(p, lnRight); > 617 p->iStart = p->iEnd = -1; > 618 sbsWriteText(p, pRight, SBS_NEWLINE); > 619 return; > 620 } > 621 > 622 /* At this point we know that there is a chunk of text that has > 623 ** changed between the left and the right. Check to see if there > 624 ** is a large unchanged section in the middle of that changed block. > 625 */ > 626 nLeftDiff = nLeft - nSuffix - nPrefix; > 627 nRightDiff = nRight - nSuffix - nPrefix; > 628 if( p->escHtml > 629 && nLeftDiff >= 6 > 630 && nRightDiff >= 6 > 631 && textLCS(&zLeft[nPrefix], nLeftDiff, &zRight[nPrefix], nRightDiff, aLCS) > 632 ){ > 633 sbsWriteLineno(p, lnLeft); > 634 p->iStart = nPrefix; > 635 p->iEnd = nPrefix + aLCS[0]; > 636 p->zStart = aLCS[2]==0 ? zClassRm : zClassChng; > 637 p->iStart2 = nPrefix + aLCS[1]; > 638 p->iEnd2 = nLeft - nSuffix; > 639 p->zStart2 = aLCS[3]==nRightDiff ? zClassRm : zClassChng; > 640 if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0; > 641 if( p->iStart==p->iEnd ){ > 642 p->iStart = p->iStart2; > 643 p->iEnd = p->iEnd2; > 644 p->zStart = p->zStart2; > 645 p->iStart2 = 0; > 646 p->iEnd2 = 0; > 647 } > 648 if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1; > 649 sbsWriteText(p, pLeft, SBS_PAD); > 650 sbsWrite(p, " | ", 3); > 651 sbsWriteLineno(p, lnRight); > 652 p->iStart = nPrefix; > 653 p->iEnd = nPrefix + aLCS[2]; > 654 p->zStart = aLCS[0]==0 ? zClassAdd : zClassChng; > 655 p->iStart2 = nPrefix + aLCS[3]; > 656 p->iEnd2 = nRight - nSuffix; > 657 p->zStart2 = aLCS[1]==nLeftDiff ? zClassAdd : zClassChng; > 658 if( p->iStart2==p->iEnd2 ) p->iStart2 = p->iEnd2 = 0; > 659 if( p->iStart==p->iEnd ){ > 660 p->iStart = p->iStart2; > 661 p->iEnd = p->iEnd2; > 662 p->zStart = p->zStart2; > 663 p->iStart2 = 0; > 664 p->iEnd2 = 0; > 665 } > 666 if( p->iStart==p->iEnd ) p->iStart = p->iEnd = -1; > 667 sbsWriteText(p, pRight, SBS_NEWLINE); > 668 return; > 669 } > 670 > 671 /* If all else fails, show a single big change between left and right */ > 672 sbsWriteLineno(p, lnLeft); > 673 p->iStart2 = p->iEnd2 = 0; > 674 p->iStart = nPrefix; > 675 p->iEnd = nLeft - nSuffix; > 676 p->zStart = zClassChng; > 677 sbsWriteText(p, pLeft, SBS_PAD); > 678 sbsWrite(p, " | ", 3); > 679 sbsWriteLineno(p, lnRight); > 680 p->iEnd = nRight - nSuffix; > 681 sbsWriteText(p, pRight, SBS_NEWLINE); > 682 } > 683 340 /* 684 /* > 685 ** Minimum of two values > 686 */ > 687 static int minInt(int a, int b){ return a<b ? a : b; } > 688 > 689 /* > 690 ** Return the number between 0 and 100 that is smaller the closer pA and > 691 ** pB match. Return 0 for a perfect match. Return 100 if pA and pB are > 692 ** completely different. > 693 ** > 694 ** The current algorithm is as follows: > 695 ** > 696 ** (1) Remove leading and trailing whitespace. > 697 ** (2) Truncate both strings to at most 250 characters > 698 ** (3) Find the length of the longest common subsequence > 699 ** (4) Longer common subsequences yield lower scores. > 700 */ > 701 static int match_dline(DLine *pA, DLine *pB){ > 702 const char *zA; /* Left string */ > 703 const char *zB; /* right string */ > 704 int nA; /* Bytes in zA[] */ > 705 int nB; /* Bytes in zB[] */ > 706 int avg; /* Average length of A and B */ > 707 int i, j, k; /* Loop counters */ > 708 int best = 0; /* Longest match found so far */ > 709 int score; /* Final score. 0..100 */ > 710 unsigned char c; /* Character being examined */ > 711 unsigned char aFirst[256]; /* aFirst[X] = index in zB[] of first char X */ > 712 unsigned char aNext[252]; /* aNext[i] = index in zB[] of next zB[i] char */ > 713 > 714 zA = pA->z; > 715 zB = pB->z; > 716 nA = pA->h & LENGTH_MASK; > 717 nB = pB->h & LENGTH_MASK; > 718 while( nA>0 && fossil_isspace(zA[0]) ){ nA--; zA++; } > 719 while( nA>0 && fossil_isspace(zA[nA-1]) ){ nA--; } > 720 while( nB>0 && fossil_isspace(zB[0]) ){ nB--; zB++; } > 721 while( nB>0 && fossil_isspace(zB[nB-1]) ){ nB--; } > 722 if( nA>250 ) nA = 250; > 723 if( nB>250 ) nB = 250; > 724 avg = (nA+nB)/2; > 725 if( avg==0 ) return 0; > 726 if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0; > 727 memset(aFirst, 0, sizeof(aFirst)); > 728 zA--; zB--; /* Make both zA[] and zB[] 1-indexed */ > 729 for(i=nB; i>0; i--){ > 730 c = (unsigned char)zB[i]; > 731 aNext[i] = aFirst[c]; > 732 aFirst[c] = i; > 733 } > 734 best = 0; > 735 for(i=1; i<=nA-best; i++){ > 736 c = (unsigned char)zA[i]; > 737 for(j=aFirst[c]; j>0 && j<nB-best; j = aNext[j]){ > 738 int limit = minInt(nA-i, nB-j); > 739 for(k=1; k<=limit && zA[k+i]==zB[k+j]; k++){} > 740 if( k>best ) best = k; > 741 } > 742 } > 743 score = (best>avg) ? 0 : (avg - best)*100/avg; > 744 > 745 #if 0 > 746 fprintf(stderr, "A: [%.*s]\nB: [%.*s]\nbest=%d avg=%d score=%d\n", > 747 nA, zA+1, nB, zB+1, best, avg, score); > 748 #endif > 749 > 750 /* Return the result */ > 751 return score; > 752 } > 753 > 754 /* > 755 ** There is a change block in which nLeft lines of text on the left are > 756 ** converted into nRight lines of text on the right. This routine computes > 757 ** how the lines on the left line up with the lines on the right. > 758 ** > 759 ** The return value is a buffer of unsigned characters, obtained from > 760 ** fossil_malloc(). (The caller needs to free the return value using > 761 ** fossil_free().) Entries in the returned array have values as follows: > 762 ** > 763 ** 1. Delete the next line of pLeft. > 764 ** 2. The next line of pLeft changes into the next line of pRight. > 765 ** 3. Insert the next line of pRight. > 766 ** > 767 ** The length of the returned array will be just large enough to cause > 768 ** all elements of pLeft and pRight to be consumed. > 769 ** > 770 ** Algorithm: Wagner's minimum edit-distance algorithm, modified by > 771 ** adding a cost to each match based on how well the two rows match > 772 ** each other. Insertion and deletion costs are 50. Match costs > 773 ** are between 0 and 100 where 0 is a perfect match 100 is a complete > 774 ** mismatch. > 775 */ > 776 static unsigned char *sbsAlignment( > 777 DLine *aLeft, int nLeft, /* Text on the left */ > 778 DLine *aRight, int nRight /* Text on the right */ > 779 ){ > 780 int i, j, k; /* Loop counters */ > 781 int *a; /* One row of the Wagner matrix */ > 782 int *pToFree; /* Space that needs to be freed */ > 783 unsigned char *aM; /* Wagner result matrix */ > 784 int aBuf[100]; /* Stack space for a[] if nRight not to big */ > 785 > 786 aM = fossil_malloc( (nLeft+1)*(nRight+1) ); > 787 if( nLeft==0 ){ > 788 memset(aM, 3, nRight); > 789 return aM; > 790 } > 791 if( nRight==0 ){ > 792 memset(aM, 1, nLeft); > 793 return aM; > 794 } > 795 if( nRight < (sizeof(aBuf)/sizeof(aBuf[0]))-1 ){ > 796 pToFree = 0; > 797 a = aBuf; > 798 }else{ > 799 a = pToFree = fossil_malloc( sizeof(a[0])*(nRight+1) ); > 800 } > 801 > 802 /* Compute the best alignment */ > 803 for(i=0; i<=nRight; i++){ > 804 aM[i] = 3; > 805 a[i] = i*50; > 806 } > 807 aM[0] = 0; > 808 for(j=1; j<=nLeft; j++){ > 809 int p = a[0]; > 810 a[0] = p+50; > 811 aM[j*(nRight+1)] = 1; > 812 for(i=1; i<=nRight; i++){ > 813 int m = a[i-1]+50; > 814 int d = 3; > 815 if( m>a[i]+50 ){ > 816 m = a[i]+50; > 817 d = 1; > 818 } > 819 if( m>p ){ > 820 int score = match_dline(&aLeft[j-1], &aRight[i-1]); > 821 if( (score<66 || (i<j+1 && i>j-1)) && m>p+score ){ > 822 m = p+score; > 823 d = 2; > 824 } > 825 } > 826 p = a[i]; > 827 a[i] = m; > 828 aM[j*(nRight+1)+i] = d; > 829 } > 830 } > 831 > 832 /* Compute the lowest-cost path back through the matrix */ > 833 i = nRight; > 834 j = nLeft; > 835 k = (nRight+1)*(nLeft+1)-1; > 836 while( i+j>0 ){ > 837 unsigned char c = aM[k--]; > 838 if( c==2 ){ > 839 assert( i>0 && j>0 ); > 840 i--; > 841 j--; > 842 }else if( c==3 ){ > 843 assert( i>0 ); > 844 i--; > 845 }else{ > 846 assert( j>0 ); > 847 j--; > 848 } > 849 aM[k] = aM[j*(nRight+1)+i]; > 850 } > 851 k++; > 852 i = (nRight+1)*(nLeft+1) - k; > 853 memmove(aM, &aM[k], i); > 854 > 855 /* Return the result */ > 856 fossil_free(pToFree); > 857 return aM; > 858 } > 859 > 860 /* 341 ** Given a diff context in which the aEdit[] array has been filled 861 ** Given a diff context in which the aEdit[] array has been filled 342 ** in, compute a side-by-side diff into pOut. 862 ** in, compute a side-by-side diff into pOut. 343 */ 863 */ 344 static void sbsDiff(DContext *p, Blob *pOut, int nContext, int width){ | 864 static void sbsDiff( > 865 DContext *p, /* The computed diff */ > 866 Blob *pOut, /* Write the results here */ > 867 int nContext, /* Number of lines of context around each change */ > 868 int width, /* Width of each column of output */ > 869 int escHtml /* True to generate HTML output */ > 870 ){ 345 DLine *A; /* Left side of the diff */ 871 DLine *A; /* Left side of the diff */ 346 DLine *B; /* Right side of the diff */ 872 DLine *B; /* Right side of the diff */ 347 int a = 0; /* Index of next line in A[] */ 873 int a = 0; /* Index of next line in A[] */ 348 int b = 0; /* Index of next line in B[] */ 874 int b = 0; /* Index of next line in B[] */ 349 int *R; /* Array of COPY/DELETE/INSERT triples */ 875 int *R; /* Array of COPY/DELETE/INSERT triples */ 350 int r; /* Index into R[] */ 876 int r; /* Index into R[] */ 351 int nr; /* Number of COPY/DELETE/INSERT triples to process */ 877 int nr; /* Number of COPY/DELETE/INSERT triples to process */ 352 int mxr; /* Maximum value for r */ 878 int mxr; /* Maximum value for r */ 353 int na, nb; /* Number of lines shown from A and B */ 879 int na, nb; /* Number of lines shown from A and B */ 354 int i, j; /* Loop counters */ 880 int i, j; /* Loop counters */ 355 int m, ma, mb;/* Number of lines to output */ 881 int m, ma, mb;/* Number of lines to output */ 356 int skip; /* Number of lines to skip */ 882 int skip; /* Number of lines to skip */ 357 int mxLine; /* Length of a line of text */ | 883 int nChunk = 0; /* Number of chunks of diff output seen so far */ 358 char *zLine; /* A line of text being formatted */ | 884 SbsLine s; /* Output line buffer */ 359 int len; /* Length of an output line */ < 360 885 361 mxLine = width*2 + 2*7 + 3 + 1; < 362 zLine = fossil_malloc( mxLine + 1 ); | 886 s.zLine = fossil_malloc( 10*width + 200 ); 363 if( zLine==0 ) return; | 887 if( s.zLine==0 ) return; > 888 s.width = width; > 889 s.escHtml = escHtml; 364 zLine[mxLine] = 0; | 890 s.iStart = -1; > 891 s.iEnd = -1; 365 A = p->aFrom; 892 A = p->aFrom; 366 B = p->aTo; 893 B = p->aTo; 367 R = p->aEdit; 894 R = p->aEdit; 368 mxr = p->nEdit; 895 mxr = p->nEdit; 369 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } 896 while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; } 370 for(r=0; r<mxr; r += 3*nr){ 897 for(r=0; r<mxr; r += 3*nr){ 371 /* Figure out how many triples to show in a single block */ 898 /* Figure out how many triples to show in a single block */ ................................................................................................................................................................................ 393 na += R[r+nr*3]; 920 na += R[r+nr*3]; 394 nb += R[r+nr*3]; 921 nb += R[r+nr*3]; 395 } 922 } 396 for(i=1; i<nr; i++){ 923 for(i=1; i<nr; i++){ 397 na += R[r+i*3]; 924 na += R[r+i*3]; 398 nb += R[r+i*3]; 925 nb += R[r+i*3]; 399 } 926 } 400 /* < > 927 401 * If the patch changes an empty file or results in an empty file, | 928 /* Draw the separator between blocks */ 402 * the block header must use 0,0 as position indicator and not 1,0. | 929 if( r>0 ){ 403 * Otherwise, patch would be confused and may reject the diff. | 930 if( escHtml ){ 404 */ | 931 blob_appendf(pOut, "<span class=\"diffhr\">%.*c</span>\n", > 932 width*2+16, '.'); > 933 }else{ 405 if( r>0 ) blob_appendf(pOut,"%.*c\n", width*2+16, '.'); | 934 blob_appendf(pOut, "%.*c\n", width*2+16, '.'); > 935 } > 936 } > 937 nChunk++; > 938 if( escHtml ){ > 939 blob_appendf(pOut, "<a name=\"chunk%d\"></a>\n", nChunk); > 940 } 406 941 407 /* Show the initial common area */ 942 /* Show the initial common area */ 408 a += skip; 943 a += skip; 409 b += skip; 944 b += skip; 410 m = R[r] - skip; 945 m = R[r] - skip; 411 for(j=0; j<m; j++){ 946 for(j=0; j<m; j++){ 412 memset(zLine, ' ', mxLine); | 947 s.n = 0; 413 sbsWriteLineno(zLine, a+j); | 948 sbsWriteLineno(&s, a+j); 414 sbsWriteText(&zLine[7], &A[a+j], width, 0); | 949 s.iStart = s.iEnd = -1; 415 sbsWriteLineno(&zLine[width+10], b+j); | 950 sbsWriteText(&s, &A[a+j], SBS_PAD); 416 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); | 951 sbsWrite(&s, " ", 3); 417 blob_append(pOut, zLine, len+width+17); | 952 sbsWriteLineno(&s, b+j); 418 } | 953 sbsWriteText(&s, &B[b+j], SBS_NEWLINE); 419 a += m; | 954 blob_append(pOut, s.zLine, s.n); 420 b += m; | 955 } 421 | 956 a += m; 422 /* Show the differences */ | 957 b += m; 423 for(i=0; i<nr; i++){ | 958 424 ma = R[r+i*3+1]; | 959 /* Show the differences */ 425 mb = R[r+i*3+2]; | 960 for(i=0; i<nr; i++){ 426 m = ma<mb ? ma : mb; | 961 unsigned char *alignment; 427 for(j=0; j<m; j++){ | 962 ma = R[r+i*3+1]; /* Lines on left but not on right */ 428 memset(zLine, ' ', mxLine); | 963 mb = R[r+i*3+2]; /* Lines on right but not on left */ 429 sbsWriteLineno(zLine, a+j); | 964 alignment = sbsAlignment(&A[a], ma, &B[b], mb); 430 sbsWriteText(&zLine[7], &A[a+j], width, 0); | 965 for(j=0; ma+mb>0; j++){ 431 zLine[width+8] = '|'; | 966 if( alignment[j]==1 ){ 432 sbsWriteLineno(&zLine[width+10], b+j); | 967 s.n = 0; 433 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); | 968 sbsWriteLineno(&s, a); 434 blob_append(pOut, zLine, len+width+17); | 969 s.iStart = 0; 435 } | 970 s.zStart = "<span class=\"diffrm\">"; 436 a += m; | 971 s.iEnd = s.width; 437 b += m; | 972 sbsWriteText(&s, &A[a], SBS_PAD); 438 ma -= m; | 973 sbsWrite(&s, " <\n", 3); 439 mb -= m; | 974 blob_append(pOut, s.zLine, s.n); 440 for(j=0; j<ma; j++){ | 975 assert( ma>0 ); 441 memset(zLine, ' ', width+7); | 976 ma--; 442 sbsWriteLineno(zLine, a+j); | 977 a++; 443 sbsWriteText(&zLine[7], &A[a+j], width, 0); | 978 }else if( alignment[j]==2 ){ 444 zLine[width+8] = '<'; | 979 s.n = 0; 445 zLine[width+9] = '\n'; | 980 sbsWriteLineChange(&s, &A[a], a, &B[b], b); 446 zLine[width+10] = 0; | 981 blob_append(pOut, s.zLine, s.n); 447 blob_append(pOut, zLine, width+10); | 982 assert( ma>0 && mb>0 ); 448 } | 983 ma--; 449 a += ma; | 984 mb--; 450 for(j=0; j<mb; j++){ | 985 a++; 451 memset(zLine, ' ', mxLine); | 986 b++; 452 zLine[width+8] = '>'; | 987 }else{ 453 sbsWriteLineno(&zLine[width+10], b+j); | 988 s.n = 0; 454 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); | 989 sbsWriteSpace(&s, width + 7); 455 blob_append(pOut, zLine, len+width+17); | 990 sbsWrite(&s, " > ", 3); 456 } | 991 sbsWriteLineno(&s, b); 457 b += mb; | 992 s.iStart = 0; 458 if( i<nr-1 ){ | 993 s.zStart = "<span class=\"diffadd\">"; 459 m = R[r+i*3+3]; | 994 s.iEnd = s.width; 460 for(j=0; j<m; j++){ | 995 sbsWriteText(&s, &B[b], SBS_NEWLINE); 461 memset(zLine, ' ', mxLine); | 996 blob_append(pOut, s.zLine, s.n); 462 sbsWriteLineno(zLine, a+j); | 997 assert( mb>0 ); 463 sbsWriteText(&zLine[7], &A[a+j], width, 0); | 998 mb--; 464 sbsWriteLineno(&zLine[width+10], b+j); | 999 b++; 465 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); | 1000 } 466 blob_append(pOut, zLine, len+width+17); | 1001 } > 1002 fossil_free(alignment); > 1003 if( i<nr-1 ){ > 1004 m = R[r+i*3+3]; > 1005 for(j=0; j<m; j++){ > 1006 s.n = 0; > 1007 sbsWriteLineno(&s, a+j); > 1008 s.iStart = s.iEnd = -1; > 1009 sbsWriteText(&s, &A[a+j], SBS_PAD); > 1010 sbsWrite(&s, " ", 3); > 1011 sbsWriteLineno(&s, b+j); > 1012 sbsWriteText(&s, &B[b+j], SBS_NEWLINE); > 1013 blob_append(pOut, s.zLine, s.n); 467 } 1014 } 468 b += m; 1015 b += m; 469 a += m; 1016 a += m; 470 } 1017 } 471 } 1018 } 472 1019 473 /* Show the final common area */ 1020 /* Show the final common area */ 474 assert( nr==i ); 1021 assert( nr==i ); 475 m = R[r+nr*3]; 1022 m = R[r+nr*3]; 476 if( m>nContext ) m = nContext; 1023 if( m>nContext ) m = nContext; 477 for(j=0; j<m; j++){ 1024 for(j=0; j<m; j++){ 478 memset(zLine, ' ', mxLine); | 1025 s.n = 0; 479 sbsWriteLineno(zLine, a+j); | 1026 sbsWriteLineno(&s, a+j); 480 sbsWriteText(&zLine[7], &A[a+j], width, 0); | 1027 s.iStart = s.iEnd = -1; > 1028 sbsWriteText(&s, &A[a+j], SBS_PAD); > 1029 sbsWrite(&s, " ", 3); 481 sbsWriteLineno(&zLine[width+10], b+j); | 1030 sbsWriteLineno(&s, b+j); 482 len = sbsWriteText(&zLine[width+17], &B[b+j], width, 1); < > 1031 sbsWriteText(&s, &B[b+j], SBS_NEWLINE); 483 blob_append(pOut, zLine, len+width+17); | 1032 blob_append(pOut, s.zLine, s.n); 484 } 1033 } 485 } 1034 } 486 free(zLine); | 1035 free(s.zLine); 487 } 1036 } 488 1037 489 /* 1038 /* 490 ** Compute the optimal longest common subsequence (LCS) using an 1039 ** Compute the optimal longest common subsequence (LCS) using an 491 ** exhaustive search. This version of the LCS is only used for 1040 ** 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 1041 ** shorter input strings since runtime is O(N*N) where N is the 493 ** input string length. 1042 ** input string length. ................................................................................................................................................................................ 550 DContext *p, /* Two files being compared */ 1099 DContext *p, /* Two files being compared */ 551 int iS1, int iE1, /* Range of lines in p->aFrom[] */ 1100 int iS1, int iE1, /* Range of lines in p->aFrom[] */ 552 int iS2, int iE2, /* Range of lines in p->aTo[] */ 1101 int iS2, int iE2, /* Range of lines in p->aTo[] */ 553 int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ 1102 int *piSX, int *piEX, /* Write p->aFrom[] common segment here */ 554 int *piSY, int *piEY /* Write p->aTo[] common segment here */ 1103 int *piSY, int *piEY /* Write p->aTo[] common segment here */ 555 ){ 1104 ){ 556 double bestScore = -1e30; /* Best score seen so far */ 1105 double bestScore = -1e30; /* Best score seen so far */ 557 int i, j; /* Loop counters */ | 1106 int i, j, k; /* Loop counters */ > 1107 int n; /* Loop limit */ > 1108 DLine *pA, *pB; /* Pointers to lines */ 558 int iSX, iSY, iEX, iEY; /* Current match */ 1109 int iSX, iSY, iEX, iEY; /* Current match */ 559 double score; /* Current score */ 1110 double score; /* Current score */ 560 int skew; /* How lopsided is the match */ 1111 int skew; /* How lopsided is the match */ 561 int dist; /* Distance of match from center */ 1112 int dist; /* Distance of match from center */ 562 int mid; /* Center of the span */ 1113 int mid; /* Center of the span */ 563 int iSXb, iSYb, iEXb, iEYb; /* Best match so far */ 1114 int iSXb, iSYb, iEXb, iEYb; /* Best match so far */ 564 int iSXp, iSYp, iEXp, iEYp; /* Previous match */ 1115 int iSXp, iSYp, iEXp, iEYp; /* Previous match */ ................................................................................................................................................................................ 583 } 1134 } 584 if( j==0 ) continue; 1135 if( j==0 ) continue; 585 assert( i>=iSXb && i>=iSXp ); 1136 assert( i>=iSXb && i>=iSXp ); 586 if( i<iEXb && j>=iSYb && j<iEYb ) continue; 1137 if( i<iEXb && j>=iSYb && j<iEYb ) continue; 587 if( i<iEXp && j>=iSYp && j<iEYp ) continue; 1138 if( i<iEXp && j>=iSYp && j<iEYp ) continue; 588 iSX = i; 1139 iSX = i; 589 iSY = j-1; 1140 iSY = j-1; 590 while( iSX>iS1 && iSY>iS2 && same_dline(&p->aFrom[iSX-1],&p->aTo[iSY-1]) ){ | 1141 pA = &p->aFrom[iSX-1]; > 1142 pB = &p->aTo[iSY-1]; > 1143 n = minInt(iSX-iS1, iSY-iS2); > 1144 for(k=0; k<n && same_dline(pA,pB); k++, pA--, pB--){} 591 iSX--; | 1145 iSX -= k; 592 iSY--; | 1146 iSY -= k; 593 } < 594 iEX = i+1; 1147 iEX = i+1; 595 iEY = j; 1148 iEY = j; 596 while( iEX<iE1 && iEY<iE2 && same_dline(&p->aFrom[iEX],&p->aTo[iEY]) ){ < > 1149 pA = &p->aFrom[iEX]; > 1150 pB = &p->aTo[iEY]; > 1151 n = minInt(iE1-iEX, iE2-iEY); > 1152 for(k=0; k<n && same_dline(pA,pB); k++, pA++, pB++){} 597 iEX++; | 1153 iEX += k; 598 iEY++; | 1154 iEY += k; 599 } < 600 skew = (iSX-iS1) - (iSY-iS2); 1155 skew = (iSX-iS1) - (iSY-iS2); 601 if( skew<0 ) skew = -skew; 1156 if( skew<0 ) skew = -skew; 602 dist = (iSX+iEX)/2 - mid; 1157 dist = (iSX+iEX)/2 - mid; 603 if( dist<0 ) dist = -dist; 1158 if( dist<0 ) dist = -dist; 604 score = (iEX - iSX) - 0.05*skew - 0.05*dist; 1159 score = (iEX - iSX) - 0.05*skew - 0.05*dist; 605 if( score>bestScore ){ 1160 if( score>bestScore ){ 606 bestScore = score; 1161 bestScore = score; ................................................................................................................................................................................ 624 *piSY = iSYb; 1179 *piSY = iSYb; 625 *piEX = iEXb; 1180 *piEX = iEXb; 626 *piEY = iEYb; 1181 *piEY = iEYb; 627 } 1182 } 628 /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n", 1183 /* printf("LCS(%d..%d/%d..%d) = %d..%d/%d..%d\n", 629 iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */ 1184 iS1, iE1, iS2, iE2, *piSX, *piEX, *piSY, *piEY); */ 630 } 1185 } > 1186 > 1187 /* > 1188 ** Expand the size of aEdit[] array to hold at least nEdit elements. > 1189 */ > 1190 static void expandEdit(DContext *p, int nEdit){ > 1191 p->aEdit = fossil_realloc(p->aEdit, nEdit*sizeof(int)); > 1192 p->nEditAlloc = nEdit; > 1193 } > 1194 > 1195 /* > 1196 ** Append a new COPY/DELETE/INSERT triple. > 1197 */ > 1198 static void appendTriple(DContext *p, int nCopy, int nDel, int nIns){ > 1199 /* printf("APPEND %d/%d/%d\n", nCopy, nDel, nIns); */ > 1200 if( p->nEdit>=3 ){ > 1201 if( p->aEdit[p->nEdit-1]==0 ){ > 1202 if( p->aEdit[p->nEdit-2]==0 ){ > 1203 p->aEdit[p->nEdit-3] += nCopy; > 1204 p->aEdit[p->nEdit-2] += nDel; > 1205 p->aEdit[p->nEdit-1] += nIns; > 1206 return; > 1207 } > 1208 if( nCopy==0 ){ > 1209 p->aEdit[p->nEdit-2] += nDel; > 1210 p->aEdit[p->nEdit-1] += nIns; > 1211 return; > 1212 } > 1213 } > 1214 if( nCopy==0 && nDel==0 ){ > 1215 p->aEdit[p->nEdit-1] += nIns; > 1216 return; > 1217 } > 1218 } > 1219 if( p->nEdit+3>p->nEditAlloc ){ > 1220 expandEdit(p, p->nEdit*2 + 15); > 1221 if( p->aEdit==0 ) return; > 1222 } > 1223 p->aEdit[p->nEdit++] = nCopy; > 1224 p->aEdit[p->nEdit++] = nDel; > 1225 p->aEdit[p->nEdit++] = nIns; > 1226 } 631 1227 632 /* 1228 /* 633 ** Do a single step in the difference. Compute a sequence of 1229 ** Do a single step in the difference. Compute a sequence of 634 ** copy/delete/insert steps that will convert lines iS1 through iE1-1 of 1230 ** copy/delete/insert steps that will convert lines iS1 through iE1-1 of 635 ** the input into lines iS2 through iE2-1 of the output and write 1231 ** the input into lines iS2 through iE2-1 of the output and write 636 ** that sequence into the difference context. 1232 ** that sequence into the difference context. 637 ** 1233 ** ................................................................................................................................................................................ 657 return; 1253 return; 658 } 1254 } 659 1255 660 /* Find the longest matching segment between the two sequences */ 1256 /* Find the longest matching segment between the two sequences */ 661 longestCommonSequence(p, iS1, iE1, iS2, iE2, &iSX, &iEX, &iSY, &iEY); 1257 longestCommonSequence(p, iS1, iE1, iS2, iE2, &iSX, &iEX, &iSY, &iEY); 662 1258 663 if( iEX>iSX ){ 1259 if( iEX>iSX ){ 664 /* A common segement has been found. | 1260 /* A common segment has been found. 665 ** Recursively diff either side of the matching segment */ 1261 ** Recursively diff either side of the matching segment */ 666 diff_step(p, iS1, iSX, iS2, iSY); 1262 diff_step(p, iS1, iSX, iS2, iSY); 667 if( iEX>iSX ){ 1263 if( iEX>iSX ){ 668 appendTriple(p, iEX - iSX, 0, 0); 1264 appendTriple(p, iEX - iSX, 0, 0); 669 } 1265 } 670 diff_step(p, iEX, iE1, iEY, iE2); 1266 diff_step(p, iEX, iE1, iEY, iE2); 671 }else{ 1267 }else{ ................................................................................................................................................................................ 716 expandEdit(p, p->nEdit+3); 1312 expandEdit(p, p->nEdit+3); 717 if( p->aEdit ){ 1313 if( p->aEdit ){ 718 p->aEdit[p->nEdit++] = 0; 1314 p->aEdit[p->nEdit++] = 0; 719 p->aEdit[p->nEdit++] = 0; 1315 p->aEdit[p->nEdit++] = 0; 720 p->aEdit[p->nEdit++] = 0; 1316 p->aEdit[p->nEdit++] = 0; 721 } 1317 } 722 } 1318 } > 1319 > 1320 /* > 1321 ** Attempt to shift insertion or deletion blocks so that they begin and > 1322 ** end on lines that are pure whitespace. In other words, try to transform > 1323 ** this: > 1324 ** > 1325 ** int func1(int x){ > 1326 ** return x*10; > 1327 ** +} > 1328 ** + > 1329 ** +int func2(int x){ > 1330 ** + return x*20; > 1331 ** } > 1332 ** > 1333 ** int func3(int x){ > 1334 ** return x/5; > 1335 ** } > 1336 ** > 1337 ** Into one of these: > 1338 ** > 1339 ** int func1(int x){ int func1(int x){ > 1340 ** return x*10; return x*10; > 1341 ** } } > 1342 ** + > 1343 ** +int func2(int x){ +int func2(int x){ > 1344 ** + return x*20; + return x*20; > 1345 ** +} +} > 1346 ** + > 1347 ** int func3(int x){ int func3(int x){ > 1348 ** return x/5; return x/5; > 1349 ** } } > 1350 */ > 1351 static void diff_optimize(DContext *p){ > 1352 int r; /* Index of current triple */ > 1353 int lnFrom; /* Line number in p->aFrom */ > 1354 int lnTo; /* Line number in p->aTo */ > 1355 int cpy, del, ins; > 1356 > 1357 lnFrom = lnTo = 0; > 1358 for(r=0; r<p->nEdit; r += 3){ > 1359 cpy = p->aEdit[r]; > 1360 del = p->aEdit[r+1]; > 1361 ins = p->aEdit[r+2]; > 1362 lnFrom += cpy; > 1363 lnTo += cpy; > 1364 > 1365 /* Shift insertions toward the beginning of the file */ > 1366 while( cpy>0 && del==0 && ins>0 ){ > 1367 DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of insert */ > 1368 DLine *pBtm = &p->aTo[lnTo+ins-1]; /* Last line inserted */ > 1369 if( same_dline(pTop, pBtm)==0 ) break; > 1370 if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break; > 1371 lnFrom--; > 1372 lnTo--; > 1373 p->aEdit[r]--; > 1374 p->aEdit[r+3]++; > 1375 cpy--; > 1376 } > 1377 > 1378 /* Shift insertions toward the end of the file */ > 1379 while( r+3<p->nEdit && p->aEdit[r+3]>0 && del==0 && ins>0 ){ > 1380 DLine *pTop = &p->aTo[lnTo]; /* First line inserted */ > 1381 DLine *pBtm = &p->aTo[lnTo+ins]; /* First line past end of insert */ > 1382 if( same_dline(pTop, pBtm)==0 ) break; > 1383 if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop+1)+LENGTH(pBtm) ) break; > 1384 lnFrom++; > 1385 lnTo++; > 1386 p->aEdit[r]++; > 1387 p->aEdit[r+3]--; > 1388 cpy++; > 1389 } > 1390 > 1391 /* Shift deletions toward the beginning of the file */ > 1392 while( cpy>0 && del>0 && ins==0 ){ > 1393 DLine *pTop = &p->aFrom[lnFrom-1]; /* Line before start of delete */ > 1394 DLine *pBtm = &p->aFrom[lnFrom+del-1]; /* Last line deleted */ > 1395 if( same_dline(pTop, pBtm)==0 ) break; > 1396 if( LENGTH(pTop+1)+LENGTH(pBtm)<=LENGTH(pTop)+LENGTH(pBtm-1) ) break; > 1397 lnFrom--; > 1398 lnTo--; > 1399 p->aEdit[r]--; > 1400 p->aEdit[r+3]++; > 1401 cpy--; > 1402 } > 1403 > 1404 /* Shift deletions toward the end of the file */ > 1405 while( r+3<p->nEdit && p->aEdit[r+3]>0 && del>0 && ins==0 ){ > 1406 DLine *pTop = &p->aFrom[lnFrom]; /* First line deleted */ > 1407 DLine *pBtm = &p->aFrom[lnFrom+del]; /* First line past end of delete */ > 1408 if( same_dline(pTop, pBtm)==0 ) break; > 1409 if( LENGTH(pTop)+LENGTH(pBtm-1)<=LENGTH(pTop)+LENGTH(pBtm) ) break; > 1410 lnFrom++; > 1411 lnTo++; > 1412 p->aEdit[r]++; > 1413 p->aEdit[r+3]--; > 1414 cpy++; > 1415 } > 1416 > 1417 lnFrom += del; > 1418 lnTo += ins; > 1419 } > 1420 } 723 1421 724 /* 1422 /* 725 ** Extract the number of lines of context from diffFlags. Supply an 1423 ** Extract the number of lines of context from diffFlags. Supply an 726 ** appropriate default if no context width is specified. 1424 ** appropriate default if no context width is specified. 727 */ 1425 */ 728 int diff_context_lines(int diffFlags){ 1426 int diff_context_lines(int diffFlags){ 729 int n = diffFlags & DIFF_CONTEXT_MASK; 1427 int n = diffFlags & DIFF_CONTEXT_MASK; ................................................................................................................................................................................ 761 Blob *pOut, /* Write diff here if not NULL */ 1459 Blob *pOut, /* Write diff here if not NULL */ 762 int diffFlags /* DIFF_* flags defined above */ 1460 int diffFlags /* DIFF_* flags defined above */ 763 ){ 1461 ){ 764 int ignoreEolWs; /* Ignore whitespace at the end of lines */ 1462 int ignoreEolWs; /* Ignore whitespace at the end of lines */ 765 int nContext; /* Amount of context to display */ 1463 int nContext; /* Amount of context to display */ 766 DContext c; 1464 DContext c; 767 1465 > 1466 if( diffFlags & DIFF_INVERT ){ > 1467 Blob *pTemp = pA_Blob; > 1468 pA_Blob = pB_Blob; > 1469 pB_Blob = pTemp; > 1470 } 768 nContext = diff_context_lines(diffFlags); 1471 nContext = diff_context_lines(diffFlags); 769 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0; 1472 ignoreEolWs = (diffFlags & DIFF_IGNORE_EOLWS)!=0; 770 1473 771 /* Prepare the input files */ 1474 /* Prepare the input files */ 772 memset(&c, 0, sizeof(c)); 1475 memset(&c, 0, sizeof(c)); 773 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), 1476 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), 774 &c.nFrom, ignoreEolWs); 1477 &c.nFrom, ignoreEolWs); ................................................................................................................................................................................ 781 blob_appendf(pOut, "cannot compute difference between binary files\n"); 1484 blob_appendf(pOut, "cannot compute difference between binary files\n"); 782 } 1485 } 783 return 0; 1486 return 0; 784 } 1487 } 785 1488 786 /* Compute the difference */ 1489 /* Compute the difference */ 787 diff_all(&c); 1490 diff_all(&c); > 1491 if( (diffFlags & DIFF_NOOPT)==0 ) diff_optimize(&c); 788 1492 789 if( pOut ){ 1493 if( pOut ){ 790 /* Compute a context or side-by-side diff into pOut */ 1494 /* Compute a context or side-by-side diff into pOut */ > 1495 int escHtml = (diffFlags & DIFF_HTML)!=0; 791 if( diffFlags & DIFF_SIDEBYSIDE ){ 1496 if( diffFlags & DIFF_SIDEBYSIDE ){ 792 int width = diff_width(diffFlags); 1497 int width = diff_width(diffFlags); 793 sbsDiff(&c, pOut, nContext, width); | 1498 sbsDiff(&c, pOut, nContext, width, escHtml); 794 }else{ 1499 }else{ > 1500 int showLn = (diffFlags & DIFF_LINENO)!=0; 795 contextDiff(&c, pOut, nContext); | 1501 contextDiff(&c, pOut, nContext, showLn, escHtml); 796 } 1502 } 797 free(c.aFrom); 1503 free(c.aFrom); 798 free(c.aTo); 1504 free(c.aTo); 799 free(c.aEdit); 1505 free(c.aEdit); 800 return 0; 1506 return 0; 801 }else{ 1507 }else{ 802 /* If a context diff is not requested, then return the 1508 /* If a context diff is not requested, then return the ................................................................................................................................................................................ 804 */ 1510 */ 805 free(c.aFrom); 1511 free(c.aFrom); 806 free(c.aTo); 1512 free(c.aTo); 807 return c.aEdit; 1513 return c.aEdit; 808 } 1514 } 809 } 1515 } 810 1516 811 /* 1517 /* 812 ** Copy a line with a limit. Used for side-by-side diffs to enforce a maximum < 813 ** line length limit. < 814 */ < 815 static char *copylimline(char *out, DLine *dl, int lim){ < 816 int len; < 817 len = dl->h & LENGTH_MASK; < 818 if( lim && len > lim ){ < 819 memcpy(out, dl->z, lim-3); < 820 memcpy(&out[lim-3], "...", 4); < 821 }else{ < 822 memcpy(out, dl->z, len); < 823 out[len] = '\0'; < 824 } < 825 return out; < 826 } < 827 < 828 /* < 829 ** Output table body of a side-by-side diff. Prior to the call, the caller < 830 ** should have output: < 831 ** <table class="sbsdiff"> < 832 ** <tr><th colspan="2" class="diffhdr">Old title</th><th/> < 833 ** <th colspan="2" class="diffhdr">New title</th></tr> < 834 ** < 835 ** And after the call, it should output: < 836 ** </table> < 837 ** < 838 ** Some good reference diffs in the fossil repository for testing: < 839 ** /vdiff?from=080d27a&to=4b0f813&detail=1 < 840 ** /vdiff?from=636804745b&to=c1d78e0556&detail=1 < 841 ** /vdiff?from=c0b6c28d29&to=25169506b7&detail=1 < 842 ** /vdiff?from=e3d022dffa&to=48bcfbd47b&detail=1 < 843 */ < 844 int html_sbsdiff( < 845 Blob *pA_Blob, /* FROM file */ < 846 Blob *pB_Blob, /* TO file */ < 847 int nContext, /* Amount of context to unified diff */ < 848 int ignoreEolWs /* Ignore whitespace at the end of lines */ < 849 ){ < 850 DContext c; < 851 int i; < 852 int iFrom, iTo; < 853 char *linebuf; < 854 int collim=0; /* Currently not settable; allows a column limit for diffs */ < 855 int allowExp=0; /* Currently not settable; (dis)allow expansion of rows */ < 856 < 857 /* Prepare the input files */ < 858 memset(&c, 0, sizeof(c)); < 859 c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob), < 860 &c.nFrom, ignoreEolWs); < 861 c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob), < 862 &c.nTo, ignoreEolWs); < 863 if( c.aFrom==0 || c.aTo==0 ){ < 864 free(c.aFrom); < 865 free(c.aTo); < 866 /* Note: This would be generated within a table. */ < 867 @ <p class="generalError" style="white-space: nowrap">cannot compute < 868 @ difference between binary files</p> < 869 return 0; < 870 } < 871 < 872 collim = collim < 4 ? 0 : collim; < 873 < 874 /* Compute the difference */ < 875 diff_all(&c); < 876 < 877 linebuf = fossil_malloc(LENGTH_MASK+1); < 878 if( !linebuf ){ < 879 free(c.aFrom); < 880 free(c.aTo); < 881 free(c.aEdit); < 882 return 0; < 883 } < 884 < 885 iFrom=iTo=0; < 886 i=0; < 887 while( i<c.nEdit ){ < 888 int j; < 889 /* Copied lines */ < 890 for( j=0; j<c.aEdit[i]; j++){ < 891 /* Hide lines which are copied and are further away from block boundaries < 892 ** than nContext lines. For each block with hidden lines, show a row < 893 ** notifying the user about the hidden rows. < 894 */ < 895 if( j<nContext || j>c.aEdit[i]-nContext-1 ){ < 896 @ <tr> < 897 }else if( j==nContext && j<c.aEdit[i]-nContext-1 ){ < 898 @ <tr> < 899 @ <td class="meta" colspan="5" style="white-space: nowrap;"> < 900 @ %d(c.aEdit[i]-2*nContext) hidden lines</td> < 901 @ </tr> < 902 if( !allowExp ) < 903 continue; < 904 @ <tr style="display:none;"> < 905 }else{ < 906 if( !allowExp ) < 907 continue; < 908 @ <tr style="display:none;"> < 909 } < 910 < 911 copylimline(linebuf, &c.aFrom[iFrom+j], collim); < 912 @ <td class="lineno">%d(iFrom+j+1)</td> < 913 @ <td class="srcline">%h(linebuf)</td> < 914 < 915 @ <td> </td> < 916 < 917 copylimline(linebuf, &c.aTo[iTo+j], collim); < 918 @ <td class="lineno">%d(iTo+j+1)</td> < 919 @ <td class="srcline">%h(linebuf)</td> < 920 < 921 @ </tr> < 922 } < 923 iFrom+=c.aEdit[i]; < 924 iTo+=c.aEdit[i]; < 925 < 926 if( c.aEdit[i+1]!=0 && c.aEdit[i+2]!=0 ){ < 927 int lim; < 928 lim = c.aEdit[i+1] > c.aEdit[i+2] ? c.aEdit[i+1] : c.aEdit[i+2]; < 929 < 930 /* Assume changed lines */ < 931 for( j=0; j<lim; j++ ){ < 932 @ <tr> < 933 < 934 if( j<c.aEdit[i+1] ){ < 935 copylimline(linebuf, &c.aFrom[iFrom+j], collim); < 936 @ <td class="changed lineno">%d(iFrom+j+1)</td> < 937 @ <td class="changed srcline">%h(linebuf)</td> < 938 }else{ < 939 @ <td colspan="2" class="changedvoid"/> < 940 } < 941 < 942 @ <td class="changed">|</td> < 943 < 944 if( j<c.aEdit[i+2] ){ < 945 copylimline(linebuf, &c.aTo[iTo+j], collim); < 946 @ <td class="changed lineno">%d(iTo+j+1)</td> < 947 @ <td class="changed srcline">%h(linebuf)</td> < 948 }else{ < 949 @ <td colspan="2" class="changedvoid"/> < 950 } < 951 < 952 @ </tr> < 953 } < 954 iFrom+=c.aEdit[i+1]; < 955 iTo+=c.aEdit[i+2]; < 956 }else{ < 957 < 958 /* Process deleted lines */ < 959 for( j=0; j<c.aEdit[i+1]; j++ ){ < 960 @ <tr> < 961 < 962 copylimline(linebuf, &c.aFrom[iFrom+j], collim); < 963 @ <td class="removed lineno">%d(iFrom+j+1)</td> < 964 @ <td class="removed srcline">%h(linebuf)</td> < 965 @ <td>&lt;</td> < 966 @ <td colspan="2" class="removedvoid"/> < 967 @ </tr> < 968 } < 969 iFrom+=c.aEdit[i+1]; < 970 < 971 /* Process inserted lines */ < 972 for( j=0; j<c.aEdit[i+2]; j++ ){ < 973 @ <tr> < 974 @ <td colspan="2" class="addedvoid"/> < 975 @ <td>&gt;</td> < 976 copylimline(linebuf, &c.aTo[iTo+j], collim); < 977 @ <td class="added lineno">%d(iTo+j+1)</td> < 978 @ <td class="added srcline">%h(linebuf)</td> < 979 @ </tr> < 980 } < 981 iTo+=c.aEdit[i+2]; < 982 } < 983 < 984 i+=3; < 985 } < 986 < 987 free(linebuf); < 988 free(c.aFrom); < 989 free(c.aTo); < 990 free(c.aEdit); < 991 return 1; < 992 } < 993 < 994 < 995 /* < 996 ** COMMAND: test-rawdiff < 997 */ < 998 void test_rawdiff_cmd(void){ < 999 Blob a, b; < 1000 int r; < 1001 int i; < 1002 int *R; < 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 1518 ** Process diff-related command-line options and return an appropriate 1019 ** "diffFlags" integer. 1519 ** "diffFlags" integer. 1020 ** 1520 ** 1021 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE < > 1521 ** --brief Show filenames only DIFF_BRIEF 1022 ** --context|-c N N lines of context. DIFF_CONTEXT_MASK 1522 ** --context|-c N N lines of context. DIFF_CONTEXT_MASK > 1523 ** --html Format for HTML DIFF_HTML > 1524 ** --invert Invert the diff DIFF_INVERT > 1525 ** --linenum|-n Show line numbers DIFF_LINENO > 1526 ** --noopt Disable optimization DIFF_NOOPT > 1527 ** --side-by-side|-y Side-by-side diff. DIFF_SIDEBYSIDE 1023 ** --width|-W N N character lines. DIFF_WIDTH_MASK 1528 ** --width|-W N N character lines. DIFF_WIDTH_MASK 1024 */ 1529 */ 1025 int diff_options(void){ 1530 int diff_options(void){ 1026 int diffFlags = 0; 1531 int diffFlags = 0; 1027 const char *z; 1532 const char *z; 1028 int f; 1533 int f; 1029 if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE; 1534 if( find_option("side-by-side","y",0)!=0 ) diffFlags |= DIFF_SIDEBYSIDE; ................................................................................................................................................................................ 1032 diffFlags |= f; 1537 diffFlags |= f; 1033 } 1538 } 1034 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){ 1539 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){ 1035 f *= DIFF_CONTEXT_MASK+1; 1540 f *= DIFF_CONTEXT_MASK+1; 1036 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK; 1541 if( f > DIFF_WIDTH_MASK ) f = DIFF_CONTEXT_MASK; 1037 diffFlags |= f; 1542 diffFlags |= f; 1038 } 1543 } > 1544 if( find_option("html",0,0)!=0 ) diffFlags |= DIFF_HTML; > 1545 if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO; > 1546 if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT; > 1547 if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT; > 1548 if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF; 1039 return diffFlags; 1549 return diffFlags; 1040 } 1550 } > 1551 > 1552 /* > 1553 ** COMMAND: test-rawdiff > 1554 */ > 1555 void test_rawdiff_cmd(void){ > 1556 Blob a, b; > 1557 int r; > 1558 int i; > 1559 int *R; > 1560 int diffFlags = diff_options(); > 1561 if( g.argc<4 ) usage("FILE1 FILE2 ..."); > 1562 blob_read_from_file(&a, g.argv[2]); > 1563 for(i=3; i<g.argc; i++){ > 1564 if( i>3 ) fossil_print("-------------------------------\n"); > 1565 blob_read_from_file(&b, g.argv[i]); > 1566 R = text_diff(&a, &b, 0, diffFlags); > 1567 for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ > 1568 fossil_print(" copy %4d delete %4d insert %4d\n", R[r], R[r+1], R[r+2]); > 1569 } > 1570 /* free(R); */ > 1571 blob_reset(&b); > 1572 } > 1573 } 1041 1574 1042 /* 1575 /* 1043 ** COMMAND: test-udiff 1576 ** COMMAND: test-udiff 1044 ** 1577 ** 1045 ** Print the difference between two files. The usual diff options apply. 1578 ** Print the difference between two files. The usual diff options apply. 1046 */ 1579 */ 1047 void test_udiff_cmd(void){ 1580 void test_udiff_cmd(void){ ................................................................................................................................................................................ 1241 while( db_step(&q)==SQLITE_ROW ){ 1774 while( db_step(&q)==SQLITE_ROW ){ 1242 int pid = db_column_int(&q, 0); 1775 int pid = db_column_int(&q, 0); 1243 const char *zUuid = db_column_text(&q, 1); 1776 const char *zUuid = db_column_text(&q, 1); 1244 const char *zDate = db_column_text(&q, 2); 1777 const char *zDate = db_column_text(&q, 2); 1245 const char *zUser = db_column_text(&q, 3); 1778 const char *zUser = db_column_text(&q, 3); 1246 if( webLabel ){ 1779 if( webLabel ){ 1247 zLabel = mprintf( 1780 zLabel = mprintf( 1248 "<a href='%s/info/%s' target='infowindow'>%.10s</a> %s %9.9s", | 1781 "<a href='%s/info/%s' target='infowindow'>%.10s</a> %s %13.13s", 1249 g.zTop, zUuid, zUuid, zDate, zUser 1782 g.zTop, zUuid, zUuid, zDate, zUser 1250 ); 1783 ); 1251 }else{ 1784 }else{ 1252 zLabel = mprintf("%.10s %s %9.9s", zUuid, zDate, zUser); | 1785 zLabel = mprintf("%.10s %s %13.13s", zUuid, zDate, zUser); 1253 } 1786 } 1254 p->nVers++; 1787 p->nVers++; 1255 p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) ); 1788 p->azVers = fossil_realloc(p->azVers, p->nVers*sizeof(p->azVers[0]) ); 1256 p->azVers[p->nVers-1] = zLabel; 1789 p->azVers[p->nVers-1] = zLabel; 1257 content_get(pid, &step); 1790 content_get(pid, &step); 1258 annotation_step(p, &step, zLabel); 1791 annotation_step(p, &step, zLabel); 1259 blob_reset(&step); 1792 blob_reset(&step); ................................................................................................................................................................................ 1317 ** Output the text of a file with markings to show when each line of 1850 ** Output the text of a file with markings to show when each line of 1318 ** the file was last modified. 1851 ** the file was last modified. 1319 ** 1852 ** 1320 ** Options: 1853 ** Options: 1321 ** --limit N Only look backwards in time by N versions 1854 ** --limit N Only look backwards in time by N versions 1322 ** --log List all versions analyzed 1855 ** --log List all versions analyzed 1323 ** --filevers Show file version numbers rather than check-in versions 1856 ** --filevers Show file version numbers rather than check-in versions > 1857 ** > 1858 ** See also: info, finfo, timeline 1324 */ 1859 */ 1325 void annotate_cmd(void){ 1860 void annotate_cmd(void){ 1326 int fnid; /* Filename ID */ 1861 int fnid; /* Filename ID */ 1327 int fid; /* File instance ID */ 1862 int fid; /* File instance ID */ 1328 int mid; /* Manifest where file was checked in */ 1863 int mid; /* Manifest where file was checked in */ 1329 int cid; /* Checkout ID */ 1864 int cid; /* Checkout ID */ 1330 Blob treename; /* FILENAME translated to canonical form */ 1865 Blob treename; /* FILENAME translated to canonical form */

Changes to src/diffcmd.c

17 ** 17 ** 18 ** This file contains code used to implement the "diff" command 18 ** This file contains code used to implement the "diff" command 19 */ 19 */ 20 #include "config.h" 20 #include "config.h" 21 #include "diffcmd.h" 21 #include "diffcmd.h" 22 #include <assert.h> 22 #include <assert.h> 23 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, ...){ < 29 va_list ap; < 30 va_start(ap, zFormat); < 31 if( g.cgiOutput ){ < 32 cgi_vprintf(zFormat, ap); < 33 }else{ < 34 vprintf(zFormat, ap); < 35 } < 36 va_end(ap); < 37 } < 38 < 39 /* 24 /* 40 ** Print the "Index:" message that patches wants to see at the top of a diff. 25 ** Print the "Index:" message that patches wants to see at the top of a diff. 41 */ 26 */ 42 void diff_print_index(const char *zFile, int diffFlags){ 27 void diff_print_index(const char *zFile, int diffFlags){ 43 if( (diffFlags & DIFF_SIDEBYSIDE)==0 ){ | 28 if( (diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF))==0 ){ 44 char *z = mprintf("Index: %s\n%.66c\n", zFile, '='); 29 char *z = mprintf("Index: %s\n%.66c\n", zFile, '='); 45 diff_printf("%s", z); | 30 fossil_print("%s", z); 46 fossil_free(z); 31 fossil_free(z); 47 } 32 } 48 } 33 } 49 34 50 /* 35 /* 51 ** Print the +++/--- filename lines for a diff operation. 36 ** Print the +++/--- filename lines for a diff operation. 52 */ 37 */ 53 void diff_print_filenames(const char *zLeft, const char *zRight, int diffFlags){ 38 void diff_print_filenames(const char *zLeft, const char *zRight, int diffFlags){ 54 char *z = 0; 39 char *z = 0; > 40 if( diffFlags & DIFF_BRIEF ){ > 41 /* no-op */ 55 if( diffFlags & DIFF_SIDEBYSIDE ){ | 42 }else if( diffFlags & DIFF_SIDEBYSIDE ){ 56 int w = diff_width(diffFlags); 43 int w = diff_width(diffFlags); 57 int n1 = strlen(zLeft); 44 int n1 = strlen(zLeft); 58 int x; 45 int x; 59 if( n1>w*2 ) n1 = w*2; 46 if( n1>w*2 ) n1 = w*2; 60 x = w*2+17 - (n1+2); 47 x = w*2+17 - (n1+2); 61 z = mprintf("%.*c %.*s %.*c\n", 48 z = mprintf("%.*c %.*s %.*c\n", 62 x/2, '=', n1, zLeft, (x+1)/2, '='); 49 x/2, '=', n1, zLeft, (x+1)/2, '='); 63 }else{ 50 }else{ 64 z = mprintf("--- %s\n+++ %s\n", zLeft, zRight); 51 z = mprintf("--- %s\n+++ %s\n", zLeft, zRight); 65 } 52 } 66 diff_printf("%s", z); | 53 fossil_print("%s", z); 67 fossil_free(z); 54 fossil_free(z); 68 } 55 } 69 56 70 /* 57 /* 71 ** Show the difference between two files, one in memory and one on disk. 58 ** Show the difference between two files, one in memory and one on disk. 72 ** 59 ** 73 ** The difference is the set of edits needed to transform pFile1 into 60 ** The difference is the set of edits needed to transform pFile1 into ................................................................................................................................................................................ 98 }else{ 85 }else{ 99 blob_read_from_file(&file2, zFile2); 86 blob_read_from_file(&file2, zFile2); 100 } 87 } 101 zName2 = zName; 88 zName2 = zName; 102 } 89 } 103 90 104 /* Compute and output the differences */ 91 /* Compute and output the differences */ > 92 if( diffFlags & DIFF_BRIEF ){ > 93 if( blob_compare(pFile1, &file2) ){ > 94 fossil_print("CHANGED %s\n", zName); > 95 } > 96 }else{ 105 blob_zero(&out); | 97 blob_zero(&out); 106 text_diff(pFile1, &file2, &out, diffFlags); | 98 text_diff(pFile1, &file2, &out, diffFlags); 107 if( blob_size(&out) ){ | 99 if( blob_size(&out) ){ 108 diff_print_filenames(zName, zName2, diffFlags); | 100 diff_print_filenames(zName, zName2, diffFlags); 109 diff_printf("%s\n", blob_str(&out)); | 101 fossil_print("%s\n", blob_str(&out)); > 102 } > 103 blob_reset(&out); 110 } 104 } 111 105 112 /* Release memory resources */ 106 /* Release memory resources */ 113 blob_reset(&file2); 107 blob_reset(&file2); 114 blob_reset(&out); < 115 }else{ 108 }else{ 116 int cnt = 0; 109 int cnt = 0; 117 Blob nameFile1; /* Name of temporary file to old pFile1 content */ 110 Blob nameFile1; /* Name of temporary file to old pFile1 content */ 118 Blob cmd; /* Text of command to run */ 111 Blob cmd; /* Text of command to run */ 119 112 120 /* Construct a temporary file to hold pFile1 based on the name of 113 /* Construct a temporary file to hold pFile1 based on the name of 121 ** zFile2 */ 114 ** zFile2 */ ................................................................................................................................................................................ 155 void diff_file_mem( 148 void diff_file_mem( 156 Blob *pFile1, /* In memory content to compare from */ 149 Blob *pFile1, /* In memory content to compare from */ 157 Blob *pFile2, /* In memory content to compare to */ 150 Blob *pFile2, /* In memory content to compare to */ 158 const char *zName, /* Display name of the file */ 151 const char *zName, /* Display name of the file */ 159 const char *zDiffCmd, /* Command for comparison */ 152 const char *zDiffCmd, /* Command for comparison */ 160 int diffFlags /* Diff flags */ 153 int diffFlags /* Diff flags */ 161 ){ 154 ){ > 155 if( diffFlags & DIFF_BRIEF ) return; 162 if( zDiffCmd==0 ){ 156 if( zDiffCmd==0 ){ 163 Blob out; /* Diff output text */ 157 Blob out; /* Diff output text */ 164 158 165 blob_zero(&out); 159 blob_zero(&out); 166 text_diff(pFile1, pFile2, &out, diffFlags); 160 text_diff(pFile1, pFile2, &out, diffFlags); 167 diff_print_filenames(zName, zName, diffFlags); 161 diff_print_filenames(zName, zName, diffFlags); 168 diff_printf("%s\n", blob_str(&out)); | 162 fossil_print("%s\n", blob_str(&out)); 169 163 170 /* Release memory resources */ 164 /* Release memory resources */ 171 blob_reset(&out); 165 blob_reset(&out); 172 }else{ 166 }else{ 173 Blob cmd; 167 Blob cmd; 174 char zTemp1[300]; 168 char zTemp1[300]; 175 char zTemp2[300]; 169 char zTemp2[300]; ................................................................................................................................................................................ 209 ){ 203 ){ 210 Blob fname; 204 Blob fname; 211 Blob content; 205 Blob content; 212 int isLink; 206 int isLink; 213 file_tree_name(zFileTreeName, &fname, 1); 207 file_tree_name(zFileTreeName, &fname, 1); 214 historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0); 208 historical_version_of_file(zFrom, blob_str(&fname), &content, &isLink, 0, 0); 215 if( !isLink != !file_wd_islink(zFrom) ){ 209 if( !isLink != !file_wd_islink(zFrom) ){ 216 diff_printf("cannot compute difference between symlink and regular file\n"); | 210 fossil_print("cannot compute difference between " > 211 "symlink and regular file\n"); 217 }else{ 212 }else{ 218 diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags); 213 diff_file(&content, zFileTreeName, zFileTreeName, zDiffCmd, diffFlags); 219 } 214 } 220 blob_reset(&content); 215 blob_reset(&content); 221 blob_reset(&fname); 216 blob_reset(&fname); 222 } 217 } 223 218 ................................................................................................................................................................................ 285 int isNew = db_column_int(&q,3); 280 int isNew = db_column_int(&q,3); 286 int srcid = db_column_int(&q, 4); 281 int srcid = db_column_int(&q, 4); 287 int isLink = db_column_int(&q, 5); 282 int isLink = db_column_int(&q, 5); 288 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); 283 char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); 289 char *zToFree = zFullName; 284 char *zToFree = zFullName; 290 int showDiff = 1; 285 int showDiff = 1; 291 if( isDeleted ){ 286 if( isDeleted ){ 292 diff_printf("DELETED %s\n", zPathname); | 287 fossil_print("DELETED %s\n", zPathname); 293 if( !asNewFile ){ showDiff = 0; zFullName = "/dev/null"; } 288 if( !asNewFile ){ showDiff = 0; zFullName = "/dev/null"; } 294 }else if( file_access(zFullName, 0) ){ 289 }else if( file_access(zFullName, 0) ){ 295 diff_printf("MISSING %s\n", zPathname); | 290 fossil_print("MISSING %s\n", zPathname); 296 if( !asNewFile ){ showDiff = 0; } 291 if( !asNewFile ){ showDiff = 0; } 297 }else if( isNew ){ 292 }else if( isNew ){ 298 diff_printf("ADDED %s\n", zPathname); | 293 fossil_print("ADDED %s\n", zPathname); 299 srcid = 0; 294 srcid = 0; 300 if( !asNewFile ){ showDiff = 0; } 295 if( !asNewFile ){ showDiff = 0; } 301 }else if( isChnged==3 ){ 296 }else if( isChnged==3 ){ 302 diff_printf("ADDED_BY_MERGE %s\n", zPathname); | 297 fossil_print("ADDED_BY_MERGE %s\n", zPathname); 303 srcid = 0; 298 srcid = 0; 304 if( !asNewFile ){ showDiff = 0; } 299 if( !asNewFile ){ showDiff = 0; } 305 } 300 } 306 if( showDiff ){ 301 if( showDiff ){ 307 Blob content; 302 Blob content; 308 if( !isLink != !file_wd_islink(zFullName) ){ 303 if( !isLink != !file_wd_islink(zFullName) ){ 309 diff_print_index(zPathname, diffFlags); 304 diff_print_index(zPathname, diffFlags); 310 diff_print_filenames(zPathname, zPathname, diffFlags); 305 diff_print_filenames(zPathname, zPathname, diffFlags); 311 diff_printf("cannot compute difference between symlink and regular file\ | 306 fossil_print("cannot compute difference between " > 307 "symlink and regular file\n"); 312 continue; 308 continue; 313 } 309 } 314 if( srcid>0 ){ 310 if( srcid>0 ){ 315 content_get(srcid, &content); 311 content_get(srcid, &content); 316 }else{ 312 }else{ 317 blob_zero(&content); 313 blob_zero(&content); 318 } 314 } ................................................................................................................................................................................ 337 int diffFlags, 333 int diffFlags, 338 const char *zFileTreeName 334 const char *zFileTreeName 339 ){ 335 ){ 340 char *zName; 336 char *zName; 341 Blob fname; 337 Blob fname; 342 Blob v1, v2; 338 Blob v1, v2; 343 int isLink1, isLink2; 339 int isLink1, isLink2; > 340 if( diffFlags & DIFF_BRIEF ) return; 344 file_tree_name(zFileTreeName, &fname, 1); 341 file_tree_name(zFileTreeName, &fname, 1); 345 zName = blob_str(&fname); 342 zName = blob_str(&fname); 346 historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0); 343 historical_version_of_file(zFrom, zName, &v1, &isLink1, 0, 0); 347 historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0); 344 historical_version_of_file(zTo, zName, &v2, &isLink2, 0, 0); 348 if( isLink1 != isLink2 ){ 345 if( isLink1 != isLink2 ){ 349 diff_print_filenames(zName, zName, diffFlags); 346 diff_print_filenames(zName, zName, diffFlags); > 347 fossil_print("cannot compute difference " 350 diff_printf("cannot compute difference between symlink and regular file\n"); | 348 " between symlink and regular file\n"); 351 }else{ 349 }else{ 352 diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags); 350 diff_file_mem(&v1, &v2, zName, zDiffCmd, diffFlags); 353 } 351 } 354 blob_reset(&v1); 352 blob_reset(&v1); 355 blob_reset(&v2); 353 blob_reset(&v2); 356 blob_reset(&fname); 354 blob_reset(&fname); 357 } 355 } ................................................................................................................................................................................ 365 struct ManifestFile *pTo, 363 struct ManifestFile *pTo, 366 const char *zDiffCmd, 364 const char *zDiffCmd, 367 int diffFlags 365 int diffFlags 368 ){ 366 ){ 369 Blob f1, f2; 367 Blob f1, f2; 370 int rid; 368 int rid; 371 const char *zName = pFrom ? pFrom->zName : pTo->zName; 369 const char *zName = pFrom ? pFrom->zName : pTo->zName; > 370 if( diffFlags & DIFF_BRIEF ) return; 372 diff_print_index(zName, diffFlags); 371 diff_print_index(zName, diffFlags); 373 if( pFrom ){ 372 if( pFrom ){ 374 rid = uuid_to_rid(pFrom->zUuid, 0); 373 rid = uuid_to_rid(pFrom->zUuid, 0); 375 content_get(rid, &f1); 374 content_get(rid, &f1); 376 }else{ 375 }else{ 377 blob_zero(&f1); 376 blob_zero(&f1); 378 } 377 } ................................................................................................................................................................................ 413 cmp = +1; 412 cmp = +1; 414 }else if( pToFile==0 ){ 413 }else if( pToFile==0 ){ 415 cmp = -1; 414 cmp = -1; 416 }else{ 415 }else{ 417 cmp = fossil_strcmp(pFromFile->zName, pToFile->zName); 416 cmp = fossil_strcmp(pFromFile->zName, pToFile->zName); 418 } 417 } 419 if( cmp<0 ){ 418 if( cmp<0 ){ 420 diff_printf("DELETED %s\n", pFromFile->zName); | 419 fossil_print("DELETED %s\n", pFromFile->zName); 421 if( asNewFlag ){ 420 if( asNewFlag ){ 422 diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags); 421 diff_manifest_entry(pFromFile, 0, zDiffCmd, diffFlags); 423 } 422 } 424 pFromFile = manifest_file_next(pFrom,0); 423 pFromFile = manifest_file_next(pFrom,0); 425 }else if( cmp>0 ){ 424 }else if( cmp>0 ){ 426 diff_printf("ADDED %s\n", pToFile->zName); | 425 fossil_print("ADDED %s\n", pToFile->zName); 427 if( asNewFlag ){ 426 if( asNewFlag ){ 428 diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags); 427 diff_manifest_entry(0, pToFile, zDiffCmd, diffFlags); 429 } 428 } 430 pToFile = manifest_file_next(pTo,0); 429 pToFile = manifest_file_next(pTo,0); 431 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ 430 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){ 432 /* No changes */ 431 /* No changes */ 433 pFromFile = manifest_file_next(pFrom,0); 432 pFromFile = manifest_file_next(pFrom,0); 434 pToFile = manifest_file_next(pTo,0); 433 pToFile = manifest_file_next(pTo,0); 435 }else{ 434 }else{ > 435 if( diffFlags & DIFF_BRIEF ){ 436 /* diff_printf("CHANGED %s\n", pFromFile->zName); */ | 436 fossil_print("CHANGED %s\n", pFromFile->zName); > 437 }else{ 437 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags); | 438 diff_manifest_entry(pFromFile, pToFile, zDiffCmd, diffFlags); > 439 } 438 pFromFile = manifest_file_next(pFrom,0); 440 pFromFile = manifest_file_next(pFrom,0); 439 pToFile = manifest_file_next(pTo,0); 441 pToFile = manifest_file_next(pTo,0); 440 } 442 } 441 } 443 } 442 manifest_destroy(pFrom); 444 manifest_destroy(pFrom); 443 manifest_destroy(pTo); 445 manifest_destroy(pTo); 444 } 446 } ................................................................................................................................................................................ 468 ** the "setting" command. If no external diff program is configured, then 470 ** the "setting" command. If no external diff program is configured, then 469 ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". 471 ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". 470 ** 472 ** 471 ** The "-N" or "--new-file" option causes the complete text of added or 473 ** The "-N" or "--new-file" option causes the complete text of added or 472 ** deleted files to be displayed. 474 ** deleted files to be displayed. 473 ** 475 ** 474 ** Options: 476 ** Options: > 477 ** --brief Show filenames only 475 ** --context|-c N Use N lines of context 478 ** --context|-c N Use N lines of context 476 ** --from|-r VERSION select VERSION as source for the diff 479 ** --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 480 ** -i use internal diff logic > 481 ** --new-file|-N output complete text of added or deleted files 479 ** --to VERSION select VERSION as target for the diff 482 ** --to VERSION select VERSION as target for the diff 480 ** --side-by-side|-y side-by-side diff 483 ** --side-by-side|-y side-by-side diff 481 ** --width|-W N Width of lines in side-by-side diff 484 ** --width|-W N Width of lines in side-by-side diff 482 */ 485 */ 483 void diff_cmd(void){ 486 void diff_cmd(void){ 484 int isGDiff; /* True for gdiff. False for normal diff */ 487 int isGDiff; /* True for gdiff. False for normal diff */ 485 int isInternDiff; /* True for internal diff */ 488 int isInternDiff; /* True for internal diff */

Changes to src/file.c

579 }else{ 579 }else{ 580 fossil_fatal("cannot find current working directory; %s", 580 fossil_fatal("cannot find current working directory; %s", 581 strerror(errno)); 581 strerror(errno)); 582 } 582 } 583 } 583 } 584 #endif 584 #endif 585 } 585 } > 586 > 587 /* > 588 ** Return true if zPath is an absolute pathname. Return false > 589 ** if it is relative. > 590 */ > 591 int file_is_absolute_path(const char *zPath){ > 592 if( zPath[0]=='/' > 593 #if defined(_WIN32) > 594 || zPath[0]=='\\' > 595 || (strlen(zPath)>3 && zPath[1]==':' > 596 && (zPath[2]=='\\' || zPath[2]=='/')) > 597 #endif > 598 ){ > 599 return 1; > 600 }else{ > 601 return 0; > 602 } > 603 } 586 604 587 /* 605 /* 588 ** Compute a canonical pathname for a file or directory. 606 ** Compute a canonical pathname for a file or directory. 589 ** Make the name absolute if it is relative. 607 ** Make the name absolute if it is relative. 590 ** Remove redundant / characters 608 ** Remove redundant / characters 591 ** Remove all /./ path elements. 609 ** Remove all /./ path elements. 592 ** Convert /A/../ to just / 610 ** Convert /A/../ to just / 593 */ 611 */ 594 void file_canonical_name(const char *zOrigName, Blob *pOut){ 612 void file_canonical_name(const char *zOrigName, Blob *pOut){ 595 if( zOrigName[0]=='/' | 613 if( file_is_absolute_path(zOrigName) ){ 596 #if defined(_WIN32) < 597 || zOrigName[0]=='\\' < 598 || (strlen(zOrigName)>3 && zOrigName[1]==':' < 599 && (zOrigName[2]=='\\' || zOrigName[2]=='/')) < 600 #endif < 601 ){ < 602 blob_set(pOut, zOrigName); 614 blob_set(pOut, zOrigName); 603 blob_materialize(pOut); 615 blob_materialize(pOut); 604 }else{ 616 }else{ 605 char zPwd[2000]; 617 char zPwd[2000]; 606 file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); 618 file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); 607 blob_zero(pOut); 619 blob_zero(pOut); 608 blob_appendf(pOut, "%//%/", zPwd, zOrigName); 620 blob_appendf(pOut, "%//%/", zPwd, zOrigName); ................................................................................................................................................................................ 947 #ifdef _WIN32 959 #ifdef _WIN32 948 extern char *sqlite3_win32_utf8_to_mbcs(const char*); 960 extern char *sqlite3_win32_utf8_to_mbcs(const char*); 949 return sqlite3_win32_utf8_to_mbcs(zUtf8); 961 return sqlite3_win32_utf8_to_mbcs(zUtf8); 950 #else 962 #else 951 return (char*)zUtf8; /* No-op on unix */ 963 return (char*)zUtf8; /* No-op on unix */ 952 #endif 964 #endif 953 } 965 } > 966 > 967 /* > 968 ** Return the value of an environment variable as UTF8. > 969 */ > 970 char *fossil_getenv(const char *zName){ > 971 char *zValue = getenv(zName); > 972 #ifdef _WIN32 > 973 if( zValue ) zValue = fossil_mbcs_to_utf8(zValue); > 974 #endif > 975 return zValue; > 976 } 954 977 955 /* 978 /* 956 ** Translate UTF8 to MBCS for display on the console. Return a pointer to the 979 ** Translate UTF8 to MBCS for display on the console. Return a pointer to the 957 ** translated text.. Call fossil_mbcs_free() to deallocate any memory 980 ** translated text.. Call fossil_mbcs_free() to deallocate any memory 958 ** used to store the returned pointer when done. 981 ** used to store the returned pointer when done. 959 */ 982 */ 960 char *fossil_utf8_to_console(const char *zUtf8){ 983 char *fossil_utf8_to_console(const char *zUtf8){

Changes to src/finfo.c

48 ** -p select print mode 48 ** -p select print mode 49 ** --revision|-r R print the given revision (or ckout, if none is given) 49 ** --revision|-r R print the given revision (or ckout, if none is given) 50 ** to stdout (only in print mode) 50 ** to stdout (only in print mode) 51 ** -s select status mode (print a status indicator for FILE) 51 ** -s select status mode (print a status indicator for FILE) 52 ** --case-sensitive B Enable or disable case-sensitive filenames. B is a 52 ** --case-sensitive B Enable or disable case-sensitive filenames. B is a 53 ** boolean: "yes", "no", "true", "false", etc. 53 ** boolean: "yes", "no", "true", "false", etc. 54 ** 54 ** 55 ** See also: descendants, info, leaves | 55 ** See also: artifact, descendants, info, leaves 56 */ 56 */ 57 void finfo_cmd(void){ 57 void finfo_cmd(void){ 58 capture_case_sensitive_option(); 58 capture_case_sensitive_option(); 59 db_must_be_within_tree(); 59 db_must_be_within_tree(); 60 if (find_option("status","s",0)) { 60 if (find_option("status","s",0)) { 61 Stmt q; 61 Stmt q; 62 Blob line; 62 Blob line;

Changes to src/http.c

172 */ 172 */ 173 if( g.fHttpTrace ){ 173 if( g.fHttpTrace ){ 174 static int traceCnt = 0; 174 static int traceCnt = 0; 175 char *zOutFile; 175 char *zOutFile; 176 FILE *out; 176 FILE *out; 177 traceCnt++; 177 traceCnt++; 178 zOutFile = mprintf("http-request-%d.txt", traceCnt); 178 zOutFile = mprintf("http-request-%d.txt", traceCnt); 179 out = fopen(zOutFile, "w"); | 179 out = fopen(zOutFile, "wb"); 180 if( out ){ 180 if( out ){ 181 fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), out); 181 fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), out); 182 fwrite(blob_buffer(&payload), 1, blob_size(&payload), out); 182 fwrite(blob_buffer(&payload), 1, blob_size(&payload), out); 183 fclose(out); 183 fclose(out); 184 } 184 } 185 free(zOutFile); 185 free(zOutFile); 186 zOutFile = mprintf("http-reply-%d.txt", traceCnt); 186 zOutFile = mprintf("http-reply-%d.txt", traceCnt); 187 out = fopen(zOutFile, "w"); | 187 out = fopen(zOutFile, "wb"); 188 transport_log(out); 188 transport_log(out); 189 free(zOutFile); 189 free(zOutFile); 190 } 190 } 191 191 192 /* 192 /* 193 ** Send the request to the server. 193 ** Send the request to the server. 194 */ 194 */ ................................................................................................................................................................................ 251 "application/x-fossil-uncompressed", -1)==0 ){ 251 "application/x-fossil-uncompressed", -1)==0 ){ 252 isCompressed = 0; 252 isCompressed = 0; 253 }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){ 253 }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){ 254 isError = 1; 254 isError = 1; 255 } 255 } 256 } 256 } 257 } 257 } > 258 if( iLength<0 ){ > 259 fossil_fatal("server did not reply"); > 260 goto write_err; > 261 } 258 if( rc!=200 ){ 262 if( rc!=200 ){ 259 fossil_warning("\"location:\" missing from 302 redirect reply"); 263 fossil_warning("\"location:\" missing from 302 redirect reply"); 260 goto write_err; 264 goto write_err; 261 } 265 } 262 266 263 /* 267 /* 264 ** Extract the reply payload that follows the header 268 ** Extract the reply payload that follows the header 265 */ 269 */ 266 if( iLength<0 ){ < 267 fossil_fatal("server did not reply"); < 268 goto write_err; < 269 } < 270 blob_zero(pReply); 270 blob_zero(pReply); 271 blob_resize(pReply, iLength); 271 blob_resize(pReply, iLength); 272 iLength = transport_receive(blob_buffer(pReply), iLength); 272 iLength = transport_receive(blob_buffer(pReply), iLength); 273 blob_resize(pReply, iLength); 273 blob_resize(pReply, iLength); 274 if( isError ){ 274 if( isError ){ 275 char *z; 275 char *z; 276 int i, j; 276 int i, j;

Changes to src/info.c

137 ** 137 ** 138 ** Use the "finfo" command to get information about a specific 138 ** Use the "finfo" command to get information about a specific 139 ** file in a checkout. 139 ** file in a checkout. 140 ** 140 ** 141 ** Options: 141 ** Options: 142 ** 142 ** 143 ** -R|--repository FILE Extract info from repository FILE 143 ** -R|--repository FILE Extract info from repository FILE > 144 ** > 145 ** See also: annotate, artifact, finfo, timeline 144 */ 146 */ 145 void info_cmd(void){ 147 void info_cmd(void){ 146 i64 fsize; 148 i64 fsize; 147 if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){ 149 if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){ 148 db_open_config(0); 150 db_open_config(0); 149 db_record_repository_filename(g.argv[2]); 151 db_record_repository_filename(g.argv[2]); 150 db_open_repository(g.argv[2]); 152 db_open_repository(g.argv[2]); ................................................................................................................................................................................ 155 db_find_and_open_repository(0,0); 157 db_find_and_open_repository(0,0); 156 if( g.argc==2 ){ 158 if( g.argc==2 ){ 157 int vid; 159 int vid; 158 /* 012345678901234 */ 160 /* 012345678901234 */ 159 db_record_repository_filename(0); 161 db_record_repository_filename(0); 160 fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); 162 fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); 161 if( g.localOpen ){ 163 if( g.localOpen ){ 162 fossil_print("repository: %s\n", db_lget("repository", "")); | 164 fossil_print("repository: %s\n", db_repository_filename()); 163 fossil_print("local-root: %s\n", g.zLocalRoot); 165 fossil_print("local-root: %s\n", g.zLocalRoot); 164 } 166 } 165 #if defined(_WIN32) 167 #if defined(_WIN32) 166 if( g.zHome ){ 168 if( g.zHome ){ 167 fossil_print("user-home: %s\n", g.zHome); 169 fossil_print("user-home: %s\n", g.zHome); 168 } 170 } 169 #endif 171 #endif ................................................................................................................................................................................ 249 } 251 } 250 } 252 } 251 253 252 254 253 /* 255 /* 254 ** Append the difference between two RIDs to the output 256 ** Append the difference between two RIDs to the output 255 */ 257 */ 256 static void append_diff(const char *zFrom, const char *zTo){ | 258 static void append_diff(const char *zFrom, const char *zTo, int diffFlags){ 257 int fromid; 259 int fromid; 258 int toid; 260 int toid; 259 Blob from, to, out; 261 Blob from, to, out; 260 if( zFrom ){ 262 if( zFrom ){ 261 fromid = uuid_to_rid(zFrom, 0); 263 fromid = uuid_to_rid(zFrom, 0); 262 content_get(fromid, &from); 264 content_get(fromid, &from); 263 }else{ 265 }else{ ................................................................................................................................................................................ 266 if( zTo ){ 268 if( zTo ){ 267 toid = uuid_to_rid(zTo, 0); 269 toid = uuid_to_rid(zTo, 0); 268 content_get(toid, &to); 270 content_get(toid, &to); 269 }else{ 271 }else{ 270 blob_zero(&to); 272 blob_zero(&to); 271 } 273 } 272 blob_zero(&out); 274 blob_zero(&out); > 275 if( diffFlags & DIFF_SIDEBYSIDE ){ 273 text_diff(&from, &to, &out, DIFF_IGNORE_EOLWS | 5); | 276 text_diff(&from, &to, &out, diffFlags | DIFF_HTML); > 277 @ <div class="sbsdiff"> 274 @ %h(blob_str(&out)) | 278 @ %s(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); < > 279 @ </div> 291 }else{ 280 }else{ 292 blob_zero(&from); < > 281 text_diff(&from, &to, &out, diffFlags | DIFF_LINENO | DIFF_HTML); > 282 @ <div class="udiff"> > 283 @ %s(blob_str(&out)) > 284 @ </div> 293 } 285 } 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); 286 blob_reset(&from); 306 blob_reset(&to); 287 blob_reset(&to); > 288 blob_reset(&out); 307 } 289 } 308 290 309 291 310 /* 292 /* 311 ** Write a line of web-page output that shows changes that have occurred 293 ** Write a line of web-page output that shows changes that have occurred 312 ** to a file between two check-ins. 294 ** to a file between two check-ins. 313 */ 295 */ 314 static void append_file_change_line( 296 static void append_file_change_line( 315 const char *zName, /* Name of the file that has changed */ 297 const char *zName, /* Name of the file that has changed */ 316 const char *zOld, /* blob.uuid before change. NULL for added files */ 298 const char *zOld, /* blob.uuid before change. NULL for added files */ 317 const char *zNew, /* blob.uuid after change. NULL for deletes */ 299 const char *zNew, /* blob.uuid after change. NULL for deletes */ 318 const char *zOldName, /* Prior name. NULL if no name change. */ 300 const char *zOldName, /* Prior name. NULL if no name change. */ 319 int showDiff, /* Show edit diffs if true */ | 301 int diffFlags, /* Flags for text_diff(). Zero to omit diffs */ 320 int sideBySide, /* Show diffs side-by-side */ < 321 int mperm /* executable or symlink permission for zNew */ 302 int mperm /* executable or symlink permission for zNew */ 322 ){ 303 ){ 323 if( !g.perm.History ){ 304 if( !g.perm.History ){ 324 if( zNew==0 ){ 305 if( zNew==0 ){ 325 @ <p>Deleted %h(zName)</p> 306 @ <p>Deleted %h(zName)</p> 326 }else if( zOld==0 ){ 307 }else if( zOld==0 ){ 327 @ <p>Added %h(zName)</p> 308 @ <p>Added %h(zName)</p> ................................................................................................................................................................................ 329 @ <p>Name change from %h(zOldName) to %h(zName) 310 @ <p>Name change from %h(zOldName) to %h(zName) 330 }else if( fossil_strcmp(zNew, zOld)==0 ){ 311 }else if( fossil_strcmp(zNew, zOld)==0 ){ 331 @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") 312 @ <p>Execute permission %s(( mperm==PERM_EXE )?"set":"cleared") 332 @ for %h(zName)</p> 313 @ for %h(zName)</p> 333 }else{ 314 }else{ 334 @ <p>Changes to %h(zName)</p> 315 @ <p>Changes to %h(zName)</p> 335 } 316 } 336 if( showDiff ){ | 317 if( diffFlags ){ 337 if( sideBySide ){ | 318 @ <pre style="white-space:pre;"> 338 generate_sbsdiff(zOld, zNew); < 339 }else{ < 340 @ <blockquote><pre> < 341 append_diff(zOld, zNew); | 319 append_diff(zOld, zNew, diffFlags); 342 @ </pre></blockquote> | 320 @ </pre> 343 } < 344 } 321 } 345 }else{ 322 }else{ 346 if( zOld && zNew ){ 323 if( zOld && zNew ){ 347 if( fossil_strcmp(zOld, zNew)!=0 ){ 324 if( fossil_strcmp(zOld, zNew)!=0 ){ 348 @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> 325 @ <p>Modified <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> 349 @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> 326 @ from <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> 350 @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> 327 @ to <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)].</a> ................................................................................................................................................................................ 359 }else if( zOld ){ 336 }else if( zOld ){ 360 @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> 337 @ <p>Deleted <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> 361 @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> 338 @ version <a href="%s(g.zTop)/artifact/%s(zOld)">[%S(zOld)]</a> 362 }else{ 339 }else{ 363 @ <p>Added <a href="%s(g.zTop)/finfo?name=%T(zName)">%h(zName)</a> 340 @ <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> 341 @ version <a href="%s(g.zTop)/artifact/%s(zNew)">[%S(zNew)]</a> 365 } 342 } 366 if( showDiff ){ | 343 if( diffFlags ){ 367 if( sideBySide ){ | 344 @ <pre style="white-space:pre;"> 368 generate_sbsdiff(zOld, zNew); < 369 }else{ < 370 @ <blockquote><pre> < 371 append_diff(zOld, zNew); | 345 append_diff(zOld, zNew, diffFlags); 372 @ </pre></blockquote> | 346 @ </pre> 373 } < 374 }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ 347 }else if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){ 375 @ &nbsp;&nbsp; 348 @ &nbsp;&nbsp; 376 @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a> 349 @ <a href="%s(g.zTop)/fdiff?v1=%S(zOld)&amp;v2=%S(zNew)">[diff]</a> 377 } 350 } 378 @ </p> 351 @ </p> 379 } 352 } 380 } 353 } > 354 > 355 /* > 356 ** Construct an appropriate diffFlag for text_diff() based on query > 357 ** parameters and the to boolean arguments. > 358 */ > 359 int construct_diff_flags(int showDiff, int sideBySide){ > 360 int diffFlags; > 361 if( showDiff==0 ){ > 362 diffFlags = 0; /* Zero means do not show any diff */ > 363 }else{ > 364 int x; > 365 if( sideBySide ){ > 366 diffFlags = DIFF_SIDEBYSIDE | DIFF_IGNORE_EOLWS; > 367 > 368 /* "dw" query parameter determines width of each column */ > 369 x = atoi(PD("dw","80"))*(DIFF_CONTEXT_MASK+1); > 370 if( x<0 || x>DIFF_WIDTH_MASK ) x = DIFF_WIDTH_MASK; > 371 diffFlags += x; > 372 }else{ > 373 diffFlags = DIFF_INLINE | DIFF_IGNORE_EOLWS; > 374 } > 375 > 376 /* "dc" query parameter determines lines of context */ > 377 x = atoi(PD("dc","7")); > 378 if( x<0 || x>DIFF_CONTEXT_MASK ) x = DIFF_CONTEXT_MASK; > 379 diffFlags += x; > 380 > 381 /* The "noopt" parameter disables diff optimization */ > 382 if( PD("noopt",0)!=0 ) diffFlags |= DIFF_NOOPT; > 383 } > 384 return diffFlags; > 385 } 381 386 382 387 383 /* 388 /* 384 ** WEBPAGE: vinfo 389 ** WEBPAGE: vinfo 385 ** WEBPAGE: ci 390 ** WEBPAGE: ci 386 ** URL: /ci?name=RID|ARTIFACTID 391 ** URL: /ci?name=RID|ARTIFACTID 387 ** 392 ** ................................................................................................................................................................................ 395 ** shown, without diffs. This behavior is inverted if the 400 ** shown, without diffs. This behavior is inverted if the 396 ** "show-version-diffs" setting is turned on. 401 ** "show-version-diffs" setting is turned on. 397 */ 402 */ 398 void ci_page(void){ 403 void ci_page(void){ 399 Stmt q; 404 Stmt q; 400 int rid; 405 int rid; 401 int isLeaf; 406 int isLeaf; 402 int showDiff; | 407 int showDiff; /* True to show diffs */ 403 int sideBySide; | 408 int sideBySide; /* True for side-by-side diffs */ > 409 int diffFlags; /* Flag parameter for text_diff() */ 404 const char *zName; /* Name of the checkin to be displayed */ 410 const char *zName; /* Name of the checkin to be displayed */ 405 const char *zUuid; /* UUID of zName */ 411 const char *zUuid; /* UUID of zName */ 406 const char *zParent; /* UUID of the parent checkin (if any) */ 412 const char *zParent; /* UUID of the parent checkin (if any) */ 407 413 408 login_check_credentials(); 414 login_check_credentials(); 409 if( !g.perm.Read ){ login_needed(); return; } 415 if( !g.perm.Read ){ login_needed(); return; } 410 zName = P("name"); 416 zName = P("name"); ................................................................................................................................................................................ 593 " (SELECT uuid FROM blob WHERE rid=mlink.fid)," 599 " (SELECT uuid FROM blob WHERE rid=mlink.fid)," 594 " (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)" 600 " (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)" 595 " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" 601 " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" 596 " WHERE mlink.mid=%d" 602 " WHERE mlink.mid=%d" 597 " ORDER BY name /*sort*/", 603 " ORDER BY name /*sort*/", 598 rid 604 rid 599 ); 605 ); > 606 diffFlags = construct_diff_flags(showDiff, sideBySide); 600 while( db_step(&q)==SQLITE_ROW ){ 607 while( db_step(&q)==SQLITE_ROW ){ 601 const char *zName = db_column_text(&q,0); 608 const char *zName = db_column_text(&q,0); 602 int mperm = db_column_int(&q, 1); 609 int mperm = db_column_int(&q, 1); 603 const char *zOld = db_column_text(&q,2); 610 const char *zOld = db_column_text(&q,2); 604 const char *zNew = db_column_text(&q,3); 611 const char *zNew = db_column_text(&q,3); 605 const char *zOldName = db_column_text(&q, 4); 612 const char *zOldName = db_column_text(&q, 4); 606 append_file_change_line(zName, zOld, zNew, zOldName, showDiff, | 613 append_file_change_line(zName, zOld, zNew, zOldName, diffFlags, mperm); 607 sideBySide, mperm); < 608 } 614 } 609 db_finalize(&q); 615 db_finalize(&q); 610 } 616 } 611 style_footer(); 617 style_footer(); 612 } 618 } 613 619 614 /* 620 /* ................................................................................................................................................................................ 758 ** 764 ** 759 ** Show all differences between two checkins. 765 ** Show all differences between two checkins. 760 */ 766 */ 761 void vdiff_page(void){ 767 void vdiff_page(void){ 762 int ridFrom, ridTo; 768 int ridFrom, ridTo; 763 int showDetail = 0; 769 int showDetail = 0; 764 int sideBySide = 0; 770 int sideBySide = 0; > 771 int diffFlags = 0; 765 Manifest *pFrom, *pTo; 772 Manifest *pFrom, *pTo; 766 ManifestFile *pFileFrom, *pFileTo; 773 ManifestFile *pFileFrom, *pFileTo; 767 774 768 login_check_credentials(); 775 login_check_credentials(); 769 if( !g.perm.Read ){ login_needed(); return; } 776 if( !g.perm.Read ){ login_needed(); return; } 770 login_anonymous_available(); 777 login_anonymous_available(); 771 778 772 pFrom = vdiff_parse_manifest("from", &ridFrom); 779 pFrom = vdiff_parse_manifest("from", &ridFrom); 773 if( pFrom==0 ) return; 780 if( pFrom==0 ) return; 774 pTo = vdiff_parse_manifest("to", &ridTo); 781 pTo = vdiff_parse_manifest("to", &ridTo); 775 if( pTo==0 ) return; 782 if( pTo==0 ) return; 776 showDetail = atoi(PD("detail","0")); < 777 sideBySide = atoi(PD("sbs","1")); 783 sideBySide = atoi(PD("sbs","1")); > 784 showDetail = atoi(PD("detail","0")); > 785 if( !showDetail && sideBySide ) showDetail = 1; 778 if( !sideBySide ){ 786 if( !sideBySide ){ 779 style_submenu_element("Side-by-side Diff", "sbsdiff", 787 style_submenu_element("Side-by-side Diff", "sbsdiff", 780 "%s/vdiff?from=%T&to=%T&detail=%d&sbs=1", 788 "%s/vdiff?from=%T&to=%T&detail=%d&sbs=1", 781 g.zTop, P("from"), P("to"), showDetail); 789 g.zTop, P("from"), P("to"), showDetail); 782 }else{ 790 }else{ 783 style_submenu_element("Unified Diff", "udiff", 791 style_submenu_element("Unified Diff", "udiff", 784 "%s/vdiff?from=%T&to=%T&detail=%d&sbs=0", 792 "%s/vdiff?from=%T&to=%T&detail=%d&sbs=0", ................................................................................................................................................................................ 791 checkin_description(ridTo); 799 checkin_description(ridTo); 792 @ </blockquote><hr /><p> 800 @ </blockquote><hr /><p> 793 801 794 manifest_file_rewind(pFrom); 802 manifest_file_rewind(pFrom); 795 pFileFrom = manifest_file_next(pFrom, 0); 803 pFileFrom = manifest_file_next(pFrom, 0); 796 manifest_file_rewind(pTo); 804 manifest_file_rewind(pTo); 797 pFileTo = manifest_file_next(pTo, 0); 805 pFileTo = manifest_file_next(pTo, 0); > 806 diffFlags = construct_diff_flags(showDetail, sideBySide); 798 while( pFileFrom || pFileTo ){ 807 while( pFileFrom || pFileTo ){ 799 int cmp; 808 int cmp; 800 if( pFileFrom==0 ){ 809 if( pFileFrom==0 ){ 801 cmp = +1; 810 cmp = +1; 802 }else if( pFileTo==0 ){ 811 }else if( pFileTo==0 ){ 803 cmp = -1; 812 cmp = -1; 804 }else{ 813 }else{ 805 cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); 814 cmp = fossil_strcmp(pFileFrom->zName, pFileTo->zName); 806 } 815 } 807 if( cmp<0 ){ 816 if( cmp<0 ){ 808 append_file_change_line(pFileFrom->zName, 817 append_file_change_line(pFileFrom->zName, 809 pFileFrom->zUuid, 0, 0, 0, 0, 0); | 818 pFileFrom->zUuid, 0, 0, 0, 0); 810 pFileFrom = manifest_file_next(pFrom, 0); 819 pFileFrom = manifest_file_next(pFrom, 0); 811 }else if( cmp>0 ){ 820 }else if( cmp>0 ){ 812 append_file_change_line(pFileTo->zName, 821 append_file_change_line(pFileTo->zName, 813 0, pFileTo->zUuid, 0, 0, 0, | 822 0, pFileTo->zUuid, 0, 0, 814 manifest_file_mperm(pFileTo)); 823 manifest_file_mperm(pFileTo)); 815 pFileTo = manifest_file_next(pTo, 0); 824 pFileTo = manifest_file_next(pTo, 0); 816 }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ 825 }else if( fossil_strcmp(pFileFrom->zUuid, pFileTo->zUuid)==0 ){ 817 /* No changes */ 826 /* No changes */ 818 pFileFrom = manifest_file_next(pFrom, 0); 827 pFileFrom = manifest_file_next(pFrom, 0); 819 pFileTo = manifest_file_next(pTo, 0); 828 pFileTo = manifest_file_next(pTo, 0); 820 }else{ 829 }else{ 821 append_file_change_line(pFileFrom->zName, 830 append_file_change_line(pFileFrom->zName, 822 pFileFrom->zUuid, 831 pFileFrom->zUuid, 823 pFileTo->zUuid, 0, showDetail, sideBySide, | 832 pFileTo->zUuid, 0, diffFlags, 824 manifest_file_mperm(pFileTo)); 833 manifest_file_mperm(pFileTo)); 825 pFileFrom = manifest_file_next(pFrom, 0); 834 pFileFrom = manifest_file_next(pFrom, 0); 826 pFileTo = manifest_file_next(pTo, 0); 835 pFileTo = manifest_file_next(pTo, 0); 827 } 836 } 828 } 837 } 829 manifest_destroy(pFrom); 838 manifest_destroy(pFrom); 830 manifest_destroy(pTo); 839 manifest_destroy(pTo); ................................................................................................................................................................................ 1067 void diff_page(void){ 1076 void diff_page(void){ 1068 int v1, v2; 1077 int v1, v2; 1069 int isPatch; 1078 int isPatch; 1070 int sideBySide; 1079 int sideBySide; 1071 Blob c1, c2, diff, *pOut; 1080 Blob c1, c2, diff, *pOut; 1072 char *zV1; 1081 char *zV1; 1073 char *zV2; 1082 char *zV2; > 1083 int diffFlags; > 1084 const char *zStyle = "sbsdiff"; 1074 1085 1075 login_check_credentials(); 1086 login_check_credentials(); 1076 if( !g.perm.Read ){ login_needed(); return; } 1087 if( !g.perm.Read ){ login_needed(); return; } 1077 v1 = name_to_rid_www("v1"); 1088 v1 = name_to_rid_www("v1"); 1078 v2 = name_to_rid_www("v2"); 1089 v2 = name_to_rid_www("v2"); 1079 if( v1==0 || v2==0 ) fossil_redirect_home(); 1090 if( v1==0 || v2==0 ) fossil_redirect_home(); 1080 sideBySide = atoi(PD("sbs","1")); 1091 sideBySide = atoi(PD("sbs","1")); 1081 zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1); 1092 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); 1093 zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2); 1083 isPatch = P("patch")!=0; 1094 isPatch = P("patch")!=0; 1084 if( isPatch ){ 1095 if( isPatch ){ 1085 pOut = cgi_output_blob(); 1096 pOut = cgi_output_blob(); 1086 cgi_set_content_type("text/plain"); 1097 cgi_set_content_type("text/plain"); > 1098 diffFlags = 4; 1087 }else{ 1099 }else{ 1088 blob_zero(&diff); 1100 blob_zero(&diff); 1089 pOut = &diff; 1101 pOut = &diff; > 1102 diffFlags = construct_diff_flags(1, sideBySide); > 1103 if( sideBySide ){ > 1104 zStyle = "sbsdiff"; > 1105 }else{ > 1106 diffFlags |= DIFF_LINENO; > 1107 zStyle = "udiff"; 1090 } | 1108 } 1091 if( !sideBySide || isPatch ){ < > 1109 } 1092 content_get(v1, &c1); | 1110 content_get(v1, &c1); 1093 content_get(v2, &c2); | 1111 content_get(v2, &c2); 1094 text_diff(&c1, &c2, pOut, 4 | 0); | 1112 text_diff(&c1, &c2, pOut, diffFlags | DIFF_HTML ); 1095 blob_reset(&c1); | 1113 blob_reset(&c1); 1096 blob_reset(&c2); | 1114 blob_reset(&c2); 1097 } < 1098 if( !isPatch ){ 1115 if( !isPatch ){ 1099 style_header("Diff"); 1116 style_header("Diff"); 1100 style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch", 1117 style_submenu_element("Patch", "Patch", "%s/fdiff?v1=%T&v2=%T&patch", 1101 g.zTop, P("v1"), P("v2")); 1118 g.zTop, P("v1"), P("v2")); 1102 if( !sideBySide ){ 1119 if( !sideBySide ){ 1103 style_submenu_element("Side-by-side Diff", "sbsdiff", 1120 style_submenu_element("Side-by-side Diff", "sbsdiff", 1104 "%s/fdiff?v1=%T&v2=%T&sbs=1", 1121 "%s/fdiff?v1=%T&v2=%T&sbs=1", ................................................................................................................................................................................ 1105 g.zTop, P("v1"), P("v2")); 1122 g.zTop, P("v1"), P("v2")); 1106 }else{ 1123 }else{ 1107 style_submenu_element("Unified Diff", "udiff", 1124 style_submenu_element("Unified Diff", "udiff", 1108 "%s/fdiff?v1=%T&v2=%T&sbs=0", 1125 "%s/fdiff?v1=%T&v2=%T&sbs=0", 1109 g.zTop, P("v1"), P("v2")); 1126 g.zTop, P("v1"), P("v2")); 1110 } 1127 } 1111 1128 > 1129 if( P("smhdr")!=0 ){ > 1130 @ <h2>Differences From Artifact > 1131 @ <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a> To > 1132 @ <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>.</h2> > 1133 }else{ 1112 @ <h2>Differences From | 1134 @ <h2>Differences From 1113 @ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2> | 1135 @ Artifact <a href="%s(g.zTop)/artifact/%S(zV1)">[%S(zV1)]</a>:</h2> 1114 object_description(v1, 0, 0); | 1136 object_description(v1, 0, 0); > 1137 @ <h2>To Artifact 1115 @ <h2>To Artifact <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2> | 1138 @ <a href="%s(g.zTop)/artifact/%S(zV2)">[%S(zV2)]</a>:</h2> 1116 object_description(v2, 0, 0); | 1139 object_description(v2, 0, 0); > 1140 } 1117 @ <hr /> 1141 @ <hr /> 1118 if( sideBySide ){ < 1119 generate_sbsdiff(zV1, zV2); < 1120 }else{ < 1121 @ <blockquote><pre> < > 1142 @ <div class="%s(zStyle)"> 1122 @ %h(blob_str(&diff)) | 1143 @ %s(blob_str(&diff)) 1123 @ </pre></blockquote> < 1124 } < > 1144 @ </div> 1125 blob_reset(&diff); 1145 blob_reset(&diff); 1126 style_footer(); 1146 style_footer(); 1127 } 1147 } 1128 } 1148 } 1129 1149 1130 /* 1150 /* 1131 ** WEBPAGE: raw 1151 ** WEBPAGE: raw

Changes to src/json.c

50 "payload" /* payload */, 50 "payload" /* payload */, 51 "requestId" /*requestId*/, 51 "requestId" /*requestId*/, 52 "resultCode" /*resultCode*/, 52 "resultCode" /*resultCode*/, 53 "resultText" /*resultText*/, 53 "resultText" /*resultText*/, 54 "timestamp" /*timestamp*/ 54 "timestamp" /*timestamp*/ 55 }; 55 }; 56 56 57 /* < 58 ** Internal helpers to manipulate a byte array as a bitset. The B < 59 ** argument must be-a array at least (BIT/8+1) bytes long. < 60 ** The BIT argument is the bit number to query/set/clear/toggle. < 61 */ < 62 #define BITSET_BYTEFOR(B,BIT) ((B)[ BIT / 8 ]) < 63 #define BITSET_SET(B,BIT) ((BITSET_BYTEFOR(B,BIT) |= (0x01 << (BIT%8))),0x01) < 64 #define BITSET_UNSET(B,BIT) ((BITSET_BYTEFOR(B,BIT) &= ~(0x01 << (BIT%8))),0x00) < 65 #define BITSET_GET(B,BIT) ((BITSET_BYTEFOR(B,BIT) & (0x01 << (BIT%8))) ? 0x01 : < 66 #define BITSET_TOGGLE(B,BIT) (BITSET_GET(B,BIT) ? (BITSET_UNSET(B,BIT)) : (BITSE < 67 < 68 57 69 /* Timer code taken from sqlite3's shell.c, modified slightly. 58 /* Timer code taken from sqlite3's shell.c, modified slightly. 70 FIXME: move the timer into the fossil core API so that we can 59 FIXME: move the timer into the fossil core API so that we can 71 start the timer early on in the app init phase. Right now we're 60 start the timer early on in the app init phase. Right now we're 72 just timing the json ops themselves. 61 just timing the json ops themselves. 73 */ 62 */ 74 #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__ 63 #if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__ ................................................................................................................................................................................ 93 82 94 /* 83 /* 95 ** Print the timing results. 84 ** Print the timing results. 96 */ 85 */ 97 static double endTimer(void){ 86 static double endTimer(void){ 98 struct rusage sEnd; 87 struct rusage sEnd; 99 getrusage(RUSAGE_SELF, &sEnd); 88 getrusage(RUSAGE_SELF, &sEnd); 100 return timeDiff(&sBegin.ru_utime, &sEnd.ru_utime) < 101 + timeDiff(&sBegin.ru_stime, &sEnd.ru_stime); < 102 #if 0 89 #if 0 103 printf("CPU Time: user %f sys %f\n", 90 printf("CPU Time: user %f sys %f\n", 104 timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), 91 timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), 105 timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); 92 timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); 106 #endif 93 #endif > 94 return timeDiff(&sBegin.ru_utime, &sEnd.ru_utime) > 95 + timeDiff(&sBegin.ru_stime, &sEnd.ru_stime); 107 } 96 } 108 97 109 #define BEGIN_TIMER beginTimer() 98 #define BEGIN_TIMER beginTimer() 110 #define END_TIMER endTimer() 99 #define END_TIMER endTimer() 111 #define HAS_TIMER 1 100 #define HAS_TIMER 1 112 101 113 #elif (defined(_WIN32) || defined(WIN32)) 102 #elif (defined(_WIN32) || defined(WIN32)) ................................................................................................................................................................................ 171 static double endTimer(void){ 160 static double endTimer(void){ 172 if(getProcessTimesAddr){ 161 if(getProcessTimesAddr){ 173 FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; 162 FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; 174 getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd 163 getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd 175 return timeDiff(&ftUserBegin, &ftUserEnd) + 164 return timeDiff(&ftUserBegin, &ftUserEnd) + 176 timeDiff(&ftKernelBegin, &ftKernelEnd); 165 timeDiff(&ftKernelBegin, &ftKernelEnd); 177 } 166 } > 167 return 0.0; 178 } 168 } 179 169 180 #define BEGIN_TIMER beginTimer() 170 #define BEGIN_TIMER beginTimer() 181 #define END_TIMER endTimer() 171 #define END_TIMER endTimer() 182 #define HAS_TIMER hasTimer() 172 #define HAS_TIMER hasTimer() 183 173 184 #else 174 #else ................................................................................................................................................................................ 209 static char const * json_err_cstr( int errCode ){ 199 static char const * json_err_cstr( int errCode ){ 210 switch( errCode ){ 200 switch( errCode ){ 211 case 0: return "Success"; 201 case 0: return "Success"; 212 #define C(X,V) case FSL_JSON_E_ ## X: return V 202 #define C(X,V) case FSL_JSON_E_ ## X: return V 213 203 214 C(GENERIC,"Generic error"); 204 C(GENERIC,"Generic error"); 215 C(INVALID_REQUEST,"Invalid request"); 205 C(INVALID_REQUEST,"Invalid request"); 216 C(UNKNOWN_COMMAND,"Unknown Command"); | 206 C(UNKNOWN_COMMAND,"Unknown command or subcommand"); 217 C(UNKNOWN,"Unknown error"); 207 C(UNKNOWN,"Unknown error"); 218 C(TIMEOUT,"Timeout reached"); 208 C(TIMEOUT,"Timeout reached"); 219 C(ASSERT,"Assertion failed"); 209 C(ASSERT,"Assertion failed"); 220 C(ALLOC,"Resource allocation failed"); 210 C(ALLOC,"Resource allocation failed"); 221 C(NYI,"Not yet implemented"); 211 C(NYI,"Not yet implemented"); 222 C(PANIC,"x"); 212 C(PANIC,"x"); 223 C(MANIFEST_READ_FAILED,"Reading artifact manifest failed"); 213 C(MANIFEST_READ_FAILED,"Reading artifact manifest failed"); ................................................................................................................................................................................ 433 }else{ 423 }else{ 434 char const * cv = PD(zKey,NULL); 424 char const * cv = PD(zKey,NULL); 435 if(!cv && !g.isHTTP){ 425 if(!cv && !g.isHTTP){ 436 /* reminder to self: in CLI mode i'd like to try 426 /* reminder to self: in CLI mode i'd like to try 437 find_option(zKey,NULL,XYZ) here, but we don't have a sane 427 find_option(zKey,NULL,XYZ) here, but we don't have a sane 438 default for the XYZ param here. 428 default for the XYZ param here. 439 */ 429 */ 440 cv = getenv(zKey); | 430 cv = fossil_getenv(zKey); 441 } 431 } 442 if(cv){/*transform it to JSON for later use.*/ 432 if(cv){/*transform it to JSON for later use.*/ 443 /* use sscanf() to figure out if it's an int, 433 /* use sscanf() to figure out if it's an int, 444 and transform it to JSON int if it is. 434 and transform it to JSON int if it is. 445 435 446 FIXME: use strtol(), since it has more accurate 436 FIXME: use strtol(), since it has more accurate 447 error handling. 437 error handling. ................................................................................................................................................................................ 498 } 488 } 499 489 500 490 501 /* 491 /* 502 ** Wrapper around json_getenv() which tries to evaluate a payload/env 492 ** Wrapper around json_getenv() which tries to evaluate a payload/env 503 ** value as a boolean. Uses mostly the same logic as 493 ** value as a boolean. Uses mostly the same logic as 504 ** json_getenv_int(), with the addition that string values which 494 ** json_getenv_int(), with the addition that string values which 505 ** either start with a digit 1..9 or the letters [tT] are considered | 495 ** either start with a digit 1..9 or the letters [tTyY] are considered 506 ** to be true. If this function cannot find a matching key/value then 496 ** to be true. If this function cannot find a matching key/value then 507 ** dflt is returned. e.g. if it finds the key but the value is-a 497 ** dflt is returned. e.g. if it finds the key but the value is-a 508 ** Object then dftl is returned. 498 ** Object then dftl is returned. 509 ** 499 ** 510 ** If an entry is found, this function guarantees that it will return 500 ** If an entry is found, this function guarantees that it will return 511 ** either 0 or 1, as opposed to "0 or non-zero", so that clients can 501 ** either 0 or 1, as opposed to "0 or non-zero", so that clients can 512 ** pass a different value as dflt. Thus they can use, e.g. -1 to know 502 ** pass a different value as dflt. Thus they can use, e.g. -1 to know ................................................................................................................................................................................ 520 }else if( cson_value_is_number(v) ){ 510 }else if( cson_value_is_number(v) ){ 521 return cson_value_get_integer(v) ? 1 : 0; 511 return cson_value_get_integer(v) ? 1 : 0; 522 }else if( cson_value_is_string(v) ){ 512 }else if( cson_value_is_string(v) ){ 523 char const * sv = cson_string_cstr(cson_value_get_string(v)); 513 char const * sv = cson_string_cstr(cson_value_get_string(v)); 524 if(!*sv || ('0'==*sv)){ 514 if(!*sv || ('0'==*sv)){ 525 return 0; 515 return 0; 526 }else{ 516 }else{ > 517 return ((('1'<=*sv) && ('9'>=*sv)) 527 return (('t'==*sv) || ('T'==*sv) | 518 || ('t'==*sv) || ('T'==*sv) 528 || (('1'<=*sv) && ('9'>=*sv))) < > 519 || ('y'==*sv) || ('Y'==*sv) > 520 ) 529 ? 1 : 0; 521 ? 1 : 0; 530 } 522 } 531 }else if( cson_value_is_bool(v) ){ 523 }else if( cson_value_is_bool(v) ){ 532 return cson_value_get_bool(v) ? 1 : 0; 524 return cson_value_get_bool(v) ? 1 : 0; 533 }else if( cson_value_is_null(v) ){ 525 }else if( cson_value_is_null(v) ){ 534 return 0; 526 return 0; 535 }else{ 527 }else{ ................................................................................................................................................................................ 549 } 541 } 550 542 551 /* 543 /* 552 ** An extended form of find_option() which tries to look up a combo 544 ** An extended form of find_option() which tries to look up a combo 553 ** GET/POST/CLI argument. 545 ** GET/POST/CLI argument. 554 ** 546 ** 555 ** zKey must be the GET/POST parameter key. zCLILong must be the "long 547 ** zKey must be the GET/POST parameter key. zCLILong must be the "long 556 ** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or | 548 s** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or 557 ** the "short form" CLI flag (if NULL, no short form is used). 549 ** the "short form" CLI flag (if NULL, no short form is used). 558 ** 550 ** 559 ** If argPos is >=0 and no other match is found, 551 ** If argPos is >=0 and no other match is found, 560 ** json_command_arg(argPos) is also checked. 552 ** json_command_arg(argPos) is also checked. 561 ** 553 ** 562 ** On error (no match found) NULL is returned. 554 ** On error (no match found) NULL is returned. 563 ** 555 ** ................................................................................................................................................................................ 579 } 571 } 580 if(!rc && (argPos>=0)){ 572 if(!rc && (argPos>=0)){ 581 rc = json_command_arg((unsigned char)argPos); 573 rc = json_command_arg((unsigned char)argPos); 582 } 574 } 583 return rc; 575 return rc; 584 } 576 } 585 577 > 578 /* > 579 ** Short-hand form of json_find_option_cstr2(zKey,zCLILong,zCLIShort,-1). > 580 */ 586 char const * json_find_option_cstr(char const * zKey, 581 char const * json_find_option_cstr(char const * zKey, 587 char const * zCLILong, 582 char const * zCLILong, 588 char const * zCLIShort){ 583 char const * zCLIShort){ 589 return json_find_option_cstr2(zKey, zCLIShort, zCLIShort, -1); | 584 return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1); 590 } 585 } 591 586 592 /* 587 /* 593 ** The boolean equivalent of json_find_option_cstr(). 588 ** The boolean equivalent of json_find_option_cstr(). 594 ** If the option is not found, dftl is returned. 589 ** If the option is not found, dftl is returned. 595 */ 590 */ 596 char json_find_option_bool(char const * zKey, 591 char json_find_option_bool(char const * zKey, ................................................................................................................................................................................ 607 if((-1==rc) && fossil_has_json()){ 602 if((-1==rc) && fossil_has_json()){ 608 rc = json_getenv_bool(zKey,-1); 603 rc = json_getenv_bool(zKey,-1); 609 } 604 } 610 return (-1==rc) ? dflt : rc; 605 return (-1==rc) ? dflt : rc; 611 } 606 } 612 607 613 /* 608 /* 614 ** The integer equivalent of json_find_option_cstr(). | 609 ** The integer equivalent of json_find_option_cstr2(). 615 ** If the option is not found, dftl is returned. 610 ** If the option is not found, dftl is returned. 616 */ 611 */ 617 int json_find_option_int(char const * zKey, 612 int json_find_option_int(char const * zKey, 618 char const * zCLILong, 613 char const * zCLILong, 619 char const * zCLIShort, 614 char const * zCLIShort, 620 int dflt ){ 615 int dflt ){ 621 enum { Magic = -947 }; | 616 enum { Magic = -1947854832 }; 622 int rc = Magic; 617 int rc = Magic; 623 if(!g.isHTTP){ 618 if(!g.isHTTP){ 624 /* FIXME: use strtol() for better error/dflt handling. */ 619 /* FIXME: use strtol() for better error/dflt handling. */ 625 char const * opt = find_option(zCLILong ? zCLILong : zKey, 620 char const * opt = find_option(zCLILong ? zCLILong : zKey, 626 zCLIShort, 1); 621 zCLIShort, 1); 627 if(NULL!=opt){ 622 if(NULL!=opt){ 628 rc = atoi(opt); 623 rc = atoi(opt); ................................................................................................................................................................................ 918 char separator, 913 char separator, 919 char doDeHttp, 914 char doDeHttp, 920 cson_array * target ){ 915 cson_array * target ){ 921 char const * p = zStr /* current byte */; 916 char const * p = zStr /* current byte */; 922 char const * head /* current start-of-token */; 917 char const * head /* current start-of-token */; 923 unsigned int len = 0 /* current token's length */; 918 unsigned int len = 0 /* current token's length */; 924 int rc = 0 /* return code (number of added elements)*/; 919 int rc = 0 /* return code (number of added elements)*/; 925 char skipWs = fossil_isspace(separator) ? 0 : 1; < 926 assert( zStr && target ); 920 assert( zStr && target ); 927 while( fossil_isspace(*p) ){ 921 while( fossil_isspace(*p) ){ 928 ++p; 922 ++p; 929 } 923 } 930 head = p; 924 head = p; 931 for( ; ; ++p){ 925 for( ; ; ++p){ 932 if( !*p || (separator == *p) ){ 926 if( !*p || (separator == *p) ){ ................................................................................................................................................................................ 985 /* 979 /* 986 ** Wrapper around json_string_split(), taking the same first 3 980 ** Wrapper around json_string_split(), taking the same first 3 987 ** parameters as this function, but returns the results as a JSON 981 ** parameters as this function, but returns the results as a JSON 988 ** Array (if splitting produced tokens) or NULL (if splitting failed 982 ** Array (if splitting produced tokens) or NULL (if splitting failed 989 ** in any way or produced no tokens). 983 ** in any way or produced no tokens). 990 ** 984 ** 991 ** The returned value is owned by the caller. If not NULL then it 985 ** The returned value is owned by the caller. If not NULL then it 992 ** _will_ have a JSON type of Array or Null. | 986 ** _will_ have a JSON type of Array. 993 */ 987 */ 994 cson_value * json_string_split2( char const * zStr, 988 cson_value * json_string_split2( char const * zStr, 995 char separator, 989 char separator, 996 char doDeHttp ){ 990 char doDeHttp ){ 997 cson_value * v = cson_value_new_array(); 991 cson_value * v = cson_value_new_array(); 998 cson_array * a = cson_value_get_array(v); 992 cson_array * a = cson_value_get_array(v); 999 int rc = json_string_split( zStr, separator, doDeHttp, a ); 993 int rc = json_string_split( zStr, separator, doDeHttp, a ); ................................................................................................................................................................................ 1021 ** before they do any work. 1015 ** before they do any work. 1022 ** 1016 ** 1023 ** This must only be called once, or an assertion may be triggered. 1017 ** This must only be called once, or an assertion may be triggered. 1024 */ 1018 */ 1025 static void json_mode_bootstrap(){ 1019 static void json_mode_bootstrap(){ 1026 static char once = 0 /* guard against multiple runs */; 1020 static char once = 0 /* guard against multiple runs */; 1027 char const * zPath = P("PATH_INFO"); 1021 char const * zPath = P("PATH_INFO"); 1028 cson_value * pathSplit = NULL; < 1029 assert( (0==once) && "json_mode_bootstrap() called too many times!"); 1022 assert( (0==once) && "json_mode_bootstrap() called too many times!"); 1030 if( once ){ 1023 if( once ){ 1031 return; 1024 return; 1032 }else{ 1025 }else{ 1033 once = 1; 1026 once = 1; 1034 } 1027 } 1035 g.json.isJsonMode = 1; 1028 g.json.isJsonMode = 1; ................................................................................................................................................................................ 1157 } 1150 } 1158 if(!g.isHTTP){ 1151 if(!g.isHTTP){ 1159 g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/ 1152 g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/ 1160 } 1153 } 1161 1154 1162 {/* set up JSON output formatting options. */ 1155 {/* set up JSON output formatting options. */ 1163 int indent = -1; 1156 int indent = -1; 1164 char const * indentStr = NULL; < 1165 indent = json_find_option_int("indent",NULL,"I",-1); 1157 indent = json_find_option_int("indent",NULL,"I",-1); 1166 g.json.outOpt.indentation = (0>indent) 1158 g.json.outOpt.indentation = (0>indent) 1167 ? (g.isHTTP ? 0 : 1) 1159 ? (g.isHTTP ? 0 : 1) 1168 : (unsigned char)indent; 1160 : (unsigned char)indent; 1169 g.json.outOpt.addNewline = g.isHTTP 1161 g.json.outOpt.addNewline = g.isHTTP 1170 ? 0 1162 ? 0 1171 : (g.json.jsonp ? 0 : 1); 1163 : (g.json.jsonp ? 0 : 1); ................................................................................................................................................................................ 1174 if( g.isHTTP ){ 1166 if( g.isHTTP ){ 1175 json_auth_token()/* will copy our auth token, if any, to fossil's 1167 json_auth_token()/* will copy our auth token, if any, to fossil's 1176 core, which we need before we call 1168 core, which we need before we call 1177 login_check_credentials(). */; 1169 login_check_credentials(). */; 1178 login_check_credentials()/* populates g.perm */; 1170 login_check_credentials()/* populates g.perm */; 1179 } 1171 } 1180 else{ 1172 else{ > 1173 /* FIXME: we need an option which allows us to skip this. At least > 1174 one known command (/json/version) does not need an opened > 1175 repo. The problem here is we cannot know which functions need > 1176 it from here (because command dispatching hasn't yet happened) > 1177 and all other commands rely on the repo being opened before > 1178 they are called. A textbook example of lack of foresight :/. > 1179 */ 1181 db_find_and_open_repository(OPEN_ANY_SCHEMA,0); 1180 db_find_and_open_repository(OPEN_ANY_SCHEMA,0); 1182 } 1181 } 1183 } 1182 } 1184 1183 1185 /* 1184 /* 1186 ** Returns the ndx'th item in the "command path", where index 0 is the 1185 ** Returns the ndx'th item in the "command path", where index 0 is the 1187 ** position of the "json" part of the path. Returns NULL if ndx is out 1186 ** position of the "json" part of the path. Returns NULL if ndx is out ................................................................................................................................................................................ 1299 return code; 1298 return code; 1300 } 1299 } 1301 } 1300 } 1302 1301 1303 /* 1302 /* 1304 ** Convenience routine which converts a Julian time value into a Unix 1303 ** Convenience routine which converts a Julian time value into a Unix 1305 ** Epoch timestamp. Requires the db, so this cannot be used before the 1304 ** Epoch timestamp. Requires the db, so this cannot be used before the 1306 ** repo is opened (will trigger a fatal error in db_xxx()). | 1305 ** repo is opened (will trigger a fatal error in db_xxx()). The returned > 1306 ** value is owned by the caller. 1307 */ 1307 */ 1308 cson_value * json_julian_to_timestamp(double j){ 1308 cson_value * json_julian_to_timestamp(double j){ 1309 return cson_value_new_integer((cson_int_t) 1309 return cson_value_new_integer((cson_int_t) 1310 db_int64(0,"SELECT cast(strftime('%%s',%lf) as int)",j) 1310 db_int64(0,"SELECT cast(strftime('%%s',%lf) as int)",j) 1311 ); 1311 ); 1312 } 1312 } 1313 1313 1314 /* 1314 /* 1315 ** Returns a timestamp value. 1315 ** Returns a timestamp value. 1316 */ 1316 */ 1317 cson_int_t json_timestamp(){ 1317 cson_int_t json_timestamp(){ 1318 return (cson_int_t)time(0); 1318 return (cson_int_t)time(0); 1319 } 1319 } > 1320 1320 /* 1321 /* 1321 ** Returns a new JSON value (owned by the caller) representing 1322 ** Returns a new JSON value (owned by the caller) representing 1322 ** a timestamp. If timeVal is < 0 then time(0) is used to fetch 1323 ** a timestamp. If timeVal is < 0 then time(0) is used to fetch 1323 ** the time, else timeVal is used as-is | 1324 ** the time, else timeVal is used as-is. The returned value is > 1325 ** owned by the caller. 1324 */ 1326 */ 1325 cson_value * json_new_timestamp(cson_int_t timeVal){ 1327 cson_value * json_new_timestamp(cson_int_t timeVal){ 1326 return cson_value_new_integer((timeVal<0) ? (cson_int_t)time(0) : timeVal); 1328 return cson_value_new_integer((timeVal<0) ? (cson_int_t)time(0) : timeVal); 1327 } 1329 } 1328 1330 1329 /* 1331 /* 1330 ** Internal helper for json_create_response(). Appends the first 1332 ** Internal helper for json_create_response(). Appends the first ................................................................................................................................................................................ 1334 */ 1336 */ 1335 static cson_value * json_response_command_path(){ 1337 static cson_value * json_response_command_path(){ 1336 if(!g.json.cmd.a){ 1338 if(!g.json.cmd.a){ 1337 return NULL; 1339 return NULL; 1338 }else{ 1340 }else{ 1339 cson_value * rc = NULL; 1341 cson_value * rc = NULL; 1340 Blob path = empty_blob; 1342 Blob path = empty_blob; 1341 char const * part; < 1342 unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.c 1343 unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.c 1343 unsigned int i = 1; 1344 unsigned int i = 1; 1344 for( ; i < aLen; ++i ){ 1345 for( ; i < aLen; ++i ){ 1345 char const * part = cson_string_cstr(cson_value_get_string(cson_array_get( 1346 char const * part = cson_string_cstr(cson_value_get_string(cson_array_get( 1346 if(!part){ 1347 if(!part){ 1347 fossil_warning("Iterating further than expected in %s.", 1348 fossil_warning("Iterating further than expected in %s.", 1348 __FILE__); 1349 __FILE__); ................................................................................................................................................................................ 1467 /* 1468 /* 1468 ** Creates a new Fossil/JSON response envelope skeleton. It is owned 1469 ** Creates a new Fossil/JSON response envelope skeleton. It is owned 1469 ** by the caller, who must eventually free it using cson_value_free(), 1470 ** by the caller, who must eventually free it using cson_value_free(), 1470 ** or add it to a cson container to transfer ownership. Returns NULL 1471 ** or add it to a cson container to transfer ownership. Returns NULL 1471 ** on error. 1472 ** on error. 1472 ** 1473 ** 1473 ** If payload is not NULL and resultCode is 0 then it is set as the 1474 ** If payload is not NULL and resultCode is 0 then it is set as the 1474 ** "payload" property of the returned object. If resultCode is | 1475 ** "payload" property of the returned object. If resultCode is 0 then > 1476 ** it defaults to g.json.resultCode. If resultCode is (or defaults to) 1475 ** non-zero and payload is not NULL then this function calls 1477 ** non-zero and payload is not NULL then this function calls 1476 ** cson_value_free(payload) and does not insert the payload into the 1478 ** cson_value_free(payload) and does not insert the payload into the 1477 ** response. In either case, onwership of payload is transfered to (or 1479 ** response. In either case, onwership of payload is transfered to (or 1478 ** shared with, if the caller holds a reference) this function. 1480 ** shared with, if the caller holds a reference) this function. 1479 ** 1481 ** 1480 ** pMsg is an optional message string property (resultText) of the 1482 ** pMsg is an optional message string property (resultText) of the 1481 ** response. If resultCode is non-0 and pMsg is NULL then 1483 ** response. If resultCode is non-0 and pMsg is NULL then 1482 ** json_err_cstr() is used to get the error string. The caller may 1484 ** json_err_cstr() is used to get the error string. The caller may 1483 ** provide his own or may use an empty string to suppress the 1485 ** provide his own or may use an empty string to suppress the 1484 ** resultText property. 1486 ** resultText property. 1485 ** 1487 ** 1486 */ 1488 */ 1487 cson_value * json_create_response( int resultCode, | 1489 static cson_value * json_create_response( int resultCode, 1488 char const * pMsg, | 1490 char const * pMsg, 1489 cson_value * payload){ | 1491 cson_value * payload){ 1490 cson_value * v = NULL; 1492 cson_value * v = NULL; 1491 cson_value * tmp = NULL; 1493 cson_value * tmp = NULL; 1492 cson_object * o = NULL; 1494 cson_object * o = NULL; 1493 int rc; 1495 int rc; 1494 resultCode = json_dumbdown_rc(resultCode); | 1496 resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode); 1495 o = cson_new_object(); 1497 o = cson_new_object(); 1496 v = cson_object_value(o); 1498 v = cson_object_value(o); 1497 if( ! o ) return NULL; 1499 if( ! o ) return NULL; 1498 #define SET(K) if(!tmp) goto cleanup; \ 1500 #define SET(K) if(!tmp) goto cleanup; \ 1499 rc = cson_object_set( o, K, tmp ); \ 1501 rc = cson_object_set( o, K, tmp ); \ 1500 if(rc) do{\ 1502 if(rc) do{\ 1501 cson_value_free(tmp); \ 1503 cson_value_free(tmp); \ 1502 tmp = NULL; \ 1504 tmp = NULL; \ 1503 goto cleanup; \ 1505 goto cleanup; \ 1504 }while(0) 1506 }while(0) 1505 1507 > 1508 1506 tmp = cson_value_new_string(MANIFEST_UUID,strlen(MANIFEST_UUID)); | 1509 tmp = json_new_string(MANIFEST_UUID); 1507 SET("fossil"); 1510 SET("fossil"); 1508 1511 1509 tmp = json_new_timestamp(-1); 1512 tmp = json_new_timestamp(-1); 1510 SET(FossilJsonKeys.timestamp); 1513 SET(FossilJsonKeys.timestamp); 1511 1514 1512 if( 0 != resultCode ){ 1515 if( 0 != resultCode ){ 1513 if( ! pMsg ){ 1516 if( ! pMsg ){ ................................................................................................................................................................................ 1606 ** the cgi_xxx() family of functions, this function inherits any 1609 ** the cgi_xxx() family of functions, this function inherits any 1607 ** compression which fossil does for its output. 1610 ** compression which fossil does for its output. 1608 ** 1611 ** 1609 ** If alsoOutput is true AND g.isHTTP then cgi_reply() is called to 1612 ** If alsoOutput is true AND g.isHTTP then cgi_reply() is called to 1610 ** flush the output (and headers). Generally only do this if you are 1613 ** flush the output (and headers). Generally only do this if you are 1611 ** about to call exit(). 1614 ** about to call exit(). 1612 ** 1615 ** 1613 ** !g.isHTTP then alsoOutput is ignored and all output is sent to | 1616 ** If !g.isHTTP then alsoOutput is ignored and all output is sent to 1614 ** stdout immediately. 1617 ** stdout immediately. 1615 ** 1618 ** 1616 ** For generating the resultText property: if msg is not NULL then it 1619 ** For generating the resultText property: if msg is not NULL then it 1617 ** is used as-is. If it is NULL then g.zErrMsg is checked, and if that 1620 ** is used as-is. If it is NULL then g.zErrMsg is checked, and if that 1618 ** is NULL then json_err_cstr(code) is used. 1621 ** is NULL then json_err_cstr(code) is used. 1619 */ 1622 */ 1620 void json_err( int code, char const * msg, char alsoOutput ){ 1623 void json_err( int code, char const * msg, char alsoOutput ){ ................................................................................................................................................................................ 1700 if(!a){ 1703 if(!a){ 1701 a = cson_new_array(); 1704 a = cson_new_array(); 1702 assert(NULL!=a); 1705 assert(NULL!=a); 1703 } 1706 } 1704 if(!colNames){ 1707 if(!colNames){ 1705 colNamesV = cson_sqlite3_column_names(pStmt->pStmt); 1708 colNamesV = cson_sqlite3_column_names(pStmt->pStmt); 1706 assert(NULL != colNamesV); 1709 assert(NULL != colNamesV); 1707 cson_value_add_reference(colNamesV); | 1710 cson_value_add_reference(colNamesV)/*avoids an ownership problem*/; 1708 colNames = cson_value_get_array(colNamesV); 1711 colNames = cson_value_get_array(colNamesV); 1709 assert(NULL != colNames); 1712 assert(NULL != colNames); 1710 } 1713 } 1711 row = cson_sqlite3_row_to_object2(pStmt->pStmt, colNames); 1714 row = cson_sqlite3_row_to_object2(pStmt->pStmt, colNames); 1712 if(!row && !warnMsg){ 1715 if(!row && !warnMsg){ 1713 warnMsg = "Could not convert at least one result row to JSON."; 1716 warnMsg = "Could not convert at least one result row to JSON."; 1714 continue; 1717 continue; ................................................................................................................................................................................ 1755 ** Executes the given SQL and runs it through 1758 ** Executes the given SQL and runs it through 1756 ** json_stmt_to_array_of_obj(), returning the result of that 1759 ** json_stmt_to_array_of_obj(), returning the result of that 1757 ** function. If resetBlob is true then blob_reset(pSql) is called 1760 ** function. If resetBlob is true then blob_reset(pSql) is called 1758 ** after preparing the query. 1761 ** after preparing the query. 1759 ** 1762 ** 1760 ** pTgt has the same semantics as described for 1763 ** pTgt has the same semantics as described for 1761 ** json_stmt_to_array_of_obj(). 1764 ** json_stmt_to_array_of_obj(). > 1765 ** > 1766 ** FIXME: change this to take a (char const *) instead of a blob, > 1767 ** to simplify the trivial use-cases (which don't need a Blob). 1762 */ 1768 */ 1763 cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt, 1769 cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt, 1764 char resetBlob){ 1770 char resetBlob){ 1765 Stmt q = empty_Stmt; 1771 Stmt q = empty_Stmt; 1766 cson_value * pay = NULL; 1772 cson_value * pay = NULL; 1767 assert( blob_size(pSql) > 0 ); 1773 assert( blob_size(pSql) > 0 ); 1768 db_prepare(&q, "%s", blob_str(pSql)); 1774 db_prepare(&q, "%s", blob_str(pSql)); ................................................................................................................................................................................ 1773 db_finalize(&q); 1779 db_finalize(&q); 1774 return pay; 1780 return pay; 1775 1781 1776 } 1782 } 1777 1783 1778 /* 1784 /* 1779 ** If the given COMMIT rid has any tags associated with it, this 1785 ** If the given COMMIT rid has any tags associated with it, this 1780 ** function returns a JSON Array containing the tag names, else it | 1786 ** function returns a JSON Array containing the tag names (owned by 1781 ** returns NULL. | 1787 ** the caller), else it returns NULL. 1782 ** 1788 ** 1783 ** See info_tags_of_checkin() for more details (this is simply a JSON 1789 ** See info_tags_of_checkin() for more details (this is simply a JSON 1784 ** wrapper for that function). 1790 ** wrapper for that function). > 1791 ** > 1792 ** If there are no tags then this function returns NULL, not an empty > 1793 ** Array. 1785 */ 1794 */ 1786 cson_value * json_tags_for_checkin_rid(int rid, char propagatingOnly){ 1795 cson_value * json_tags_for_checkin_rid(int rid, char propagatingOnly){ 1787 cson_value * v = NULL; 1796 cson_value * v = NULL; 1788 char * tags = info_tags_of_checkin(rid, propagatingOnly); 1797 char * tags = info_tags_of_checkin(rid, propagatingOnly); 1789 if(tags){ 1798 if(tags){ 1790 if(*tags){ 1799 if(*tags){ 1791 v = json_string_split2(tags,',',0); 1800 v = json_string_split2(tags,',',0); ................................................................................................................................................................................ 1817 cson_array * list = cson_value_get_array(listV); 1826 cson_array * list = cson_value_get_array(listV); 1818 cson_value * objV = NULL; 1827 cson_value * objV = NULL; 1819 cson_object * obj = NULL; 1828 cson_object * obj = NULL; 1820 cson_string * kRC; 1829 cson_string * kRC; 1821 cson_string * kSymbol; 1830 cson_string * kSymbol; 1822 cson_string * kNumber; 1831 cson_string * kNumber; 1823 cson_string * kDesc; 1832 cson_string * kDesc; 1824 int rc = cson_array_reserve( list, 35 ); | 1833 cson_array_reserve( list, 35 ); 1825 if(rc){ < 1826 assert( 0 && "Allocation error."); < 1827 exit(1); < 1828 } < 1829 kRC = cson_new_string("resultCode",10); 1834 kRC = cson_new_string("resultCode",10); 1830 kSymbol = cson_new_string("cSymbol",7); 1835 kSymbol = cson_new_string("cSymbol",7); 1831 kNumber = cson_new_string("number",6); 1836 kNumber = cson_new_string("number",6); 1832 kDesc = cson_new_string("description",11); 1837 kDesc = cson_new_string("description",11); 1833 #define C(K) objV = cson_value_new_object(); obj = cson_value_get_object(objV); 1838 #define C(K) objV = cson_value_new_object(); obj = cson_value_get_object(objV); 1834 cson_object_set_s(obj, kRC, json_new_string(json_rc_cstr(FSL_JSON_E_##K)) ); 1839 cson_object_set_s(obj, kRC, json_new_string(json_rc_cstr(FSL_JSON_E_##K)) ); 1835 cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) ); 1840 cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) ); ................................................................................................................................................................................ 1892 jobj = cson_value_get_object(jval); 1897 jobj = cson_value_get_object(jval); 1893 #define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X))) 1898 #define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X))) 1894 FSET(MANIFEST_UUID,"manifestUuid"); 1899 FSET(MANIFEST_UUID,"manifestUuid"); 1895 FSET(MANIFEST_VERSION,"manifestVersion"); 1900 FSET(MANIFEST_VERSION,"manifestVersion"); 1896 FSET(MANIFEST_DATE,"manifestDate"); 1901 FSET(MANIFEST_DATE,"manifestDate"); 1897 FSET(MANIFEST_YEAR,"manifestYear"); 1902 FSET(MANIFEST_YEAR,"manifestYear"); 1898 FSET(RELEASE_VERSION,"releaseVersion"); 1903 FSET(RELEASE_VERSION,"releaseVersion"); 1899 #undef FSET < 1900 cson_object_set( jobj, "releaseVersionNumber", 1904 cson_object_set( jobj, "releaseVersionNumber", 1901 cson_value_new_integer(RELEASE_VERSION_NUMBER) ); 1905 cson_value_new_integer(RELEASE_VERSION_NUMBER) ); 1902 cson_object_set( jobj, "resultCodeParanoiaLevel", 1906 cson_object_set( jobj, "resultCodeParanoiaLevel", 1903 cson_value_new_integer(g.json.errorDetailParanoia) ); 1907 cson_value_new_integer(g.json.errorDetailParanoia) ); > 1908 FSET(FOSSIL_JSON_API_VERSION, "jsonApiVersion" ); > 1909 #undef FSET 1904 return jval; 1910 return jval; 1905 } 1911 } 1906 1912 1907 1913 1908 /* 1914 /* 1909 ** Returns the current user's capabilities string as a String value. 1915 ** Returns the current user's capabilities string as a String value. 1910 ** Returned value is owned by the caller, and will only be NULL if 1916 ** Returned value is owned by the caller, and will only be NULL if ................................................................................................................................................................................ 2001 const char *zDb; 2007 const char *zDb; 2002 enum { BufLen = 1000 }; 2008 enum { BufLen = 1000 }; 2003 char zBuf[BufLen]; 2009 char zBuf[BufLen]; 2004 cson_value * jv = NULL; 2010 cson_value * jv = NULL; 2005 cson_object * jo = NULL; 2011 cson_object * jo = NULL; 2006 cson_value * jv2 = NULL; 2012 cson_value * jv2 = NULL; 2007 cson_object * jo2 = NULL; 2013 cson_object * jo2 = NULL; > 2014 char * zTmp = NULL; 2008 if( !g.perm.Read ){ 2015 if( !g.perm.Read ){ 2009 json_set_err(FSL_JSON_E_DENIED, 2016 json_set_err(FSL_JSON_E_DENIED, 2010 "Requires 'o' permissions."); 2017 "Requires 'o' permissions."); 2011 return NULL; 2018 return NULL; 2012 } 2019 } 2013 full = json_find_option_bool("full",NULL,"f",0); 2020 full = json_find_option_bool("full",NULL,"f",0); 2014 #define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBu 2021 #define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBu 2015 2022 2016 jv = cson_value_new_object(); 2023 jv = cson_value_new_object(); 2017 jo = cson_value_get_object(jv); 2024 jo = cson_value_get_object(jv); 2018 2025 2019 sqlite3_snprintf(BufLen, zBuf, db_get("project-name","")); | 2026 zTmp = db_get("project-name",NULL); 2020 SETBUF(jo, "projectName"); | 2027 cson_object_set(jo, "projectName", json_new_string(zTmp)); 2021 /* FIXME: don't include project-description until we ensure that | 2028 free(zTmp); 2022 zBuf will always be big enough. We "should" replace zBuf | 2029 zTmp = db_get("project-description",NULL); 2023 with a blob for this purpose. | 2030 cson_object_set(jo, "projectDescription", json_new_string(zTmp)); 2024 */ < > 2031 free(zTmp); > 2032 zTmp = NULL; 2025 fsize = file_size(g.zRepositoryName); 2033 fsize = file_size(g.zRepositoryName); 2026 cson_object_set(jo, "repositorySize", cson_value_new_integer((cson_int_t)fsize 2034 cson_object_set(jo, "repositorySize", cson_value_new_integer((cson_int_t)fsize 2027 2035 2028 if(full){ 2036 if(full){ 2029 n = db_int(0, "SELECT count(*) FROM blob"); 2037 n = db_int(0, "SELECT count(*) FROM blob"); 2030 m = db_int(0, "SELECT count(*) FROM delta"); 2038 m = db_int(0, "SELECT count(*) FROM delta"); 2031 cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n)); 2039 cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n)); ................................................................................................................................................................................ 2115 if((zPages+1)->name){ 2123 if((zPages+1)->name){ 2116 blob_append(pOut, ", ",2); 2124 blob_append(pOut, ", ",2); 2117 } 2125 } 2118 } 2126 } 2119 return i; 2127 return i; 2120 } 2128 } 2121 2129 > 2130 /* > 2131 ** Creates an error message using zErrPrefix and the given array of > 2132 ** JSON command definitions, and sets the g.json error state to > 2133 ** reflect FSL_JSON_E_MISSING_ARGS. If zErrPrefix is NULL then > 2134 ** some default is used (e.g. "Try one of: "). If it is "" then > 2135 ** no prefix is used. > 2136 ** > 2137 ** The intention is to provide the user (via the response.resultText) > 2138 ** a list of available commands/subcommands. > 2139 ** > 2140 */ > 2141 void json_dispatch_missing_args_err( JsonPageDef const * pCommands, > 2142 char const * zErrPrefix ){ > 2143 Blob cmdNames = empty_blob; > 2144 blob_init(&cmdNames,NULL,0); > 2145 if( !zErrPrefix ) { > 2146 zErrPrefix = "Try one of: "; > 2147 } > 2148 blob_append( &cmdNames, zErrPrefix, strlen(zErrPrefix) ); > 2149 json_pagedefs_to_string(pCommands, &cmdNames); > 2150 json_set_err(FSL_JSON_E_MISSING_ARGS, "%s", > 2151 blob_str(&cmdNames)); > 2152 blob_reset(&cmdNames); > 2153 } 2122 2154 2123 cson_value * json_page_dispatch_helper(JsonPageDef const * pages){ 2155 cson_value * json_page_dispatch_helper(JsonPageDef const * pages){ 2124 JsonPageDef const * def; 2156 JsonPageDef const * def; 2125 char const * cmd = json_command_arg(1+g.json.dispatchDepth); 2157 char const * cmd = json_command_arg(1+g.json.dispatchDepth); 2126 assert( NULL != pages ); 2158 assert( NULL != pages ); 2127 if( ! cmd ){ 2159 if( ! cmd ){ 2128 Blob cmdNames = empty_blob; | 2160 json_dispatch_missing_args_err(pages, 2129 json_pagedefs_to_string(pages, &cmdNames); < 2130 json_set_err(FSL_JSON_E_MISSING_ARGS, < 2131 "No subcommand specified. Try one of (%s).", | 2161 "No subcommand specified. " 2132 blob_str(&cmdNames)); < 2133 blob_reset(&cmdNames); < > 2162 "Try one of: "); 2134 return NULL; 2163 return NULL; 2135 } 2164 } 2136 def = json_handler_for_name( cmd, pages ); 2165 def = json_handler_for_name( cmd, pages ); 2137 if(!def){ 2166 if(!def){ 2138 g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; | 2167 json_set_err(FSL_JSON_E_UNKNOWN_COMMAND, > 2168 "Unknown subcommand: %s", cmd); 2139 return NULL; 2169 return NULL; 2140 } 2170 } 2141 else{ 2171 else{ 2142 ++g.json.dispatchDepth; 2172 ++g.json.dispatchDepth; 2143 return (*def->func)(); 2173 return (*def->func)(); 2144 } 2174 } 2145 } 2175 } ................................................................................................................................................................................ 2199 cson_value * json_page_query(); 2229 cson_value * json_page_query(); 2200 /* Impl in json_report.c. */ 2230 /* Impl in json_report.c. */ 2201 cson_value * json_page_report(); 2231 cson_value * json_page_report(); 2202 /* Impl in json_tag.c. */ 2232 /* Impl in json_tag.c. */ 2203 cson_value * json_page_tag(); 2233 cson_value * json_page_tag(); 2204 /* Impl in json_user.c. */ 2234 /* Impl in json_user.c. */ 2205 cson_value * json_page_user(); 2235 cson_value * json_page_user(); > 2236 /* Impl in json_config.c. */ > 2237 cson_value * json_page_config(); > 2238 /* Impl in json_finfo.c. */ > 2239 cson_value * json_page_finfo(); 2206 2240 2207 /* 2241 /* 2208 ** Mapping of names to JSON pages/commands. Each name is a subpath of 2242 ** Mapping of names to JSON pages/commands. Each name is a subpath of 2209 ** /json (in CGI mode) or a subcommand of the json command in CLI mode 2243 ** /json (in CGI mode) or a subcommand of the json command in CLI mode 2210 */ 2244 */ 2211 static const JsonPageDef JsonPageDefs[] = { 2245 static const JsonPageDef JsonPageDefs[] = { 2212 /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. 2246 /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. 2213 {"anonymousPassword", json_page_anon_password, 0}, 2247 {"anonymousPassword", json_page_anon_password, 0}, 2214 {"artifact", json_page_artifact, 0}, 2248 {"artifact", json_page_artifact, 0}, 2215 {"branch", json_page_branch,0}, 2249 {"branch", json_page_branch,0}, 2216 {"cap", json_page_cap, 0}, 2250 {"cap", json_page_cap, 0}, > 2251 {"config", json_page_config, 0 }, 2217 {"diff", json_page_diff, 0}, 2252 {"diff", json_page_diff, 0}, 2218 {"dir", json_page_nyi, 0}, | 2253 /*{"dir", json_page_nyi, 0},*/ > 2254 {"finfo", json_page_finfo, 0}, 2219 {"g", json_page_g, 0}, 2255 {"g", json_page_g, 0}, 2220 {"HAI",json_page_version,0}, 2256 {"HAI",json_page_version,0}, 2221 {"login",json_page_login,0}, 2257 {"login",json_page_login,0}, 2222 {"logout",json_page_logout,0}, 2258 {"logout",json_page_logout,0}, 2223 {"query",json_page_query,0}, 2259 {"query",json_page_query,0}, 2224 {"rebuild",json_page_rebuild,0}, 2260 {"rebuild",json_page_rebuild,0}, 2225 {"report", json_page_report, 0}, 2261 {"report", json_page_report, 0}, 2226 {"resultCodes", json_page_resultCodes,0}, 2262 {"resultCodes", json_page_resultCodes,0}, 2227 {"stat",json_page_stat,0}, 2263 {"stat",json_page_stat,0}, 2228 {"tag", json_page_tag,0}, 2264 {"tag", json_page_tag,0}, 2229 {"ticket", json_page_nyi,0}, | 2265 /*{"ticket", json_page_nyi,0},*/ 2230 {"timeline", json_page_timeline,0}, 2266 {"timeline", json_page_timeline,0}, 2231 {"user",json_page_user,0}, 2267 {"user",json_page_user,0}, 2232 {"version",json_page_version,0}, 2268 {"version",json_page_version,0}, 2233 {"whoami",json_page_whoami,0}, 2269 {"whoami",json_page_whoami,0}, 2234 {"wiki",json_page_wiki,0}, 2270 {"wiki",json_page_wiki,0}, 2235 /* Last entry MUST have a NULL name. */ 2271 /* Last entry MUST have a NULL name. */ 2236 {NULL,NULL,0} 2272 {NULL,NULL,0} 2237 }; 2273 }; 2238 2274 2239 < 2240 /* 2275 /* > 2276 ** Internal helper for json_cmd_top() and json_page_top(). 2241 ** Mapping of /json/ticket/XXX commands/paths to callbacks. | 2277 ** > 2278 ** Searches JsonPageDefs for a command with the given name. If found, > 2279 ** it is used to generate and output a JSON response. If not found, it > 2280 ** generates a JSON-style error response. Returns 0 on success, non-0 > 2281 ** on error. On error it will set g.json's error state. 2242 */ 2282 */ 2243 static const JsonPageDef JsonPageDefs_Ticket[] = { < 2244 {"get", json_page_nyi, 0}, < 2245 {"list", json_page_nyi, 0}, < 2246 {"save", json_page_nyi, 1}, < 2247 {"create", json_page_nyi, 1}, | 2283 static int json_dispatch_root_command( char const * zCommand ){ 2248 /* Last entry MUST have a NULL name. */ | 2284 int rc = 0; 2249 {NULL,NULL,0} | 2285 cson_value * payload = NULL; 2250 }; < > 2286 JsonPageDef const * pageDef = NULL; > 2287 pageDef = json_handler_for_name(zCommand,&JsonPageDefs[0]); > 2288 if( ! pageDef ){ > 2289 rc = FSL_JSON_E_UNKNOWN_COMMAND; > 2290 json_set_err( rc, "Unknown command: %s", zCommand ); > 2291 }else if( pageDef->runMode < 0 /*CLI only*/){ > 2292 rc = FSL_JSON_E_WRONG_MODE; > 2293 }else if( (g.isHTTP && (pageDef->runMode < 0 /*CLI only*/)) > 2294 || > 2295 (!g.isHTTP && (pageDef->runMode > 0 /*HTTP only*/)) > 2296 ){ > 2297 rc = FSL_JSON_E_WRONG_MODE; 2251 | 2298 } 2252 /* < 2253 ** Mapping of /json/artifact/XXX commands/paths to callbacks. < 2254 */ < 2255 static const JsonPageDef JsonPageDefs_Artifact[] = { < 2256 {"vinfo", json_page_nyi, 0}, < 2257 {"finfo", json_page_nyi, 0}, < 2258 /* Last entry MUST have a NULL name. */ < 2259 {NULL,NULL,0} < 2260 }; < > 2299 else{ > 2300 rc = 0; > 2301 g.json.dispatchDepth = 1; > 2302 payload = (*pageDef->func)(); 2261 | 2303 } 2262 /* < 2263 ** Mapping of /json/tag/XXX commands/paths to callbacks. < 2264 */ < 2265 static const JsonPageDef JsonPageDefs_Tag[] = { < 2266 {"list", json_page_nyi, 0}, < 2267 {"create", json_page_nyi, 1}, < 2268 /* Last entry MUST have a NULL name. */ | 2304 payload = json_create_response(rc, NULL, payload); 2269 {NULL,NULL,0} < 2270 }; < > 2305 json_send_response(payload); > 2306 cson_value_free(payload); > 2307 return rc; 2271 | 2308 } 2272 2309 2273 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ 2310 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ 2274 /* 2311 /* 2275 ** WEBPAGE: json 2312 ** WEBPAGE: json 2276 ** 2313 ** 2277 ** Pages under /json/... must be entered into JsonPageDefs. 2314 ** Pages under /json/... must be entered into JsonPageDefs. 2278 ** This function dispatches them, and is the HTTP equivalent of 2315 ** This function dispatches them, and is the HTTP equivalent of 2279 ** json_cmd_top(). 2316 ** json_cmd_top(). 2280 */ 2317 */ 2281 void json_page_top(void){ 2318 void json_page_top(void){ 2282 int rc = FSL_JSON_E_UNKNOWN_COMMAND; < 2283 char const * cmd; | 2319 char const * zCommand; 2284 cson_value * payload = NULL; < 2285 JsonPageDef const * pageDef = NULL; < 2286 BEGIN_TIMER; 2320 BEGIN_TIMER; 2287 json_mode_bootstrap(); 2321 json_mode_bootstrap(); 2288 cmd = json_command_arg(1); | 2322 zCommand = json_command_arg(1); > 2323 if(!zCommand || !*zCommand){ > 2324 json_dispatch_missing_args_err( JsonPageDefs, > 2325 "No command (sub-path) specified." 2289 if(!cmd || !*cmd){ | 2326 " Try one of: "); 2290 goto usage; < 2291 } < 2292 /*cgi_printf("{\"cmd\":\"%s\"}\n",cmd); return;*/ < 2293 pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]); < 2294 if( ! pageDef ){ < 2295 json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 ); < 2296 return; 2327 return; 2297 }else if( pageDef->runMode < 0 /*CLI only*/){ < 2298 rc = FSL_JSON_E_WRONG_MODE; < 2299 }else{ < 2300 rc = 0; < 2301 g.json.dispatchDepth = 1; < 2302 payload = (*pageDef->func)(); < 2303 } 2328 } 2304 if( g.json.resultCode ){ < 2305 cson_value_free(payload); < 2306 json_err(g.json.resultCode, NULL, 0); < 2307 }else{ < 2308 cson_value * root = json_create_response(rc, NULL, payload); < 2309 json_send_response(root); < 2310 cson_value_free(root); < > 2329 json_dispatch_root_command( zCommand ); 2311 } | 2330 } 2312 < 2313 return; < 2314 usage: < 2315 { < 2316 Blob cmdNames = empty_blob; < 2317 blob_init(&cmdNames, < 2318 "No command (sub-path) specified. Try one of: ", < 2319 -1); < 2320 json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames); < 2321 json_err(FSL_JSON_E_MISSING_ARGS, < 2322 blob_str(&cmdNames), 0); < 2323 blob_reset(&cmdNames); < 2324 } < 2325 < 2326 } < 2327 #endif /* FOSSIL_ENABLE_JSON */ | 2331 #endif /* FOSSIL_ENABLE_JSON for mkindex */ 2328 2332 2329 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ 2333 #ifdef FOSSIL_ENABLE_JSON /* dupe ifdef needed for mkindex */ 2330 /* 2334 /* 2331 ** This function dispatches json commands and is the CLI equivalent of 2335 ** This function dispatches json commands and is the CLI equivalent of 2332 ** json_page_top(). 2336 ** json_page_top(). 2333 ** 2337 ** 2334 ** COMMAND: json 2338 ** COMMAND: json 2335 ** 2339 ** 2336 ** Usage: %fossil json SUBCOMMAND | 2340 ** Usage: %fossil json SUBCOMMAND ?OPTIONS? > 2341 ** > 2342 ** In CLI mode, the -R REPO common option is supported. Due to limitations > 2343 ** in the argument dispatching code, any -FLAGS must come after the final > 2344 ** sub- (or subsub-) command. 2337 ** 2345 ** 2338 ** The commands include: 2346 ** The commands include: 2339 ** 2347 ** > 2348 ** anonymousPassord > 2349 ** artifact 2340 ** branch 2350 ** branch 2341 ** cap 2351 ** cap > 2352 ** diff > 2353 ** g > 2354 ** login > 2355 ** logout > 2356 ** query > 2357 ** rebuild > 2358 ** report > 2359 ** resultCodes 2342 ** stat 2360 ** stat > 2361 ** tag 2343 ** timeline 2362 ** timeline > 2363 ** user 2344 ** version (alias: HAI) 2364 ** version (alias: HAI) > 2365 ** whoami 2345 ** wiki 2366 ** wiki 2346 ** 2367 ** > 2368 ** Run '%fossil json' without any subcommand to see the full list (but be > 2369 ** aware that some listed might not yet be implemented). 2347 ** 2370 ** 2348 ** TODOs: < 2349 ** < 2350 ** tag < 2351 ** ticket < 2352 ** ... < > 2371 ** PS: the notable TODO-commands include: config, dir, finfo, ticket 2353 ** 2372 ** 2354 */ 2373 */ 2355 void json_cmd_top(void){ 2374 void json_cmd_top(void){ 2356 char const * cmd = NULL; 2375 char const * cmd = NULL; 2357 int rc = FSL_JSON_E_UNKNOWN_COMMAND; | 2376 int rc = 0; 2358 cson_value * payload = NULL; < 2359 JsonPageDef const * pageDef; < 2360 BEGIN_TIMER; 2377 BEGIN_TIMER; 2361 memset( &g.perm, 0xff, sizeof(g.perm) ) 2378 memset( &g.perm, 0xff, sizeof(g.perm) ) 2362 /* In CLI mode fossil does not use permissions 2379 /* In CLI mode fossil does not use permissions 2363 and they all default to false. We enable them 2380 and they all default to false. We enable them 2364 here because (A) fossil doesn't use them in local 2381 here because (A) fossil doesn't use them in local 2365 mode but (B) having them set gives us one less 2382 mode but (B) having them set gives us one less 2366 difference in the CLI/CGI/Server-mode JSON 2383 difference in the CLI/CGI/Server-mode JSON ................................................................................................................................................................................ 2368 */ 2385 */ 2369 ; 2386 ; 2370 json_main_bootstrap(); 2387 json_main_bootstrap(); 2371 json_mode_bootstrap(); 2388 json_mode_bootstrap(); 2372 if( 2 > cson_array_length_get(g.json.cmd.a) ){ 2389 if( 2 > cson_array_length_get(g.json.cmd.a) ){ 2373 goto usage; 2390 goto usage; 2374 } 2391 } 2375 db_find_and_open_repository(0, 0); < 2376 #if 0 2392 #if 0 2377 json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing."); 2393 json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing."); 2378 json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing again."); 2394 json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing again."); 2379 #endif 2395 #endif 2380 cmd = json_command_arg(1); 2396 cmd = json_command_arg(1); 2381 if( !cmd || !*cmd ){ 2397 if( !cmd || !*cmd ){ 2382 goto usage; 2398 goto usage; 2383 } 2399 } 2384 pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]); | 2400 rc = json_dispatch_root_command( cmd ); 2385 if( ! pageDef ){ < 2386 json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 1 ); < 2387 return; < 2388 }else if( pageDef->runMode > 0 /*HTTP only*/){ < 2389 rc = FSL_JSON_E_WRONG_MODE; < 2390 }else{ < 2391 rc = 0; < 2392 g.json.dispatchDepth = 1; < 2393 payload = (*pageDef->func)(); < 2394 } < 2395 if( g.json.resultCode ){ < 2396 cson_value_free(payload); < 2397 json_err(g.json.resultCode, NULL, 1); < 2398 }else{ < 2399 payload = json_create_response(rc, NULL, payload); < 2400 json_send_response(payload); < 2401 cson_value_free( payload ); < 2402 if((0 != rc) && !g.isHTTP){ | 2401 if(0 != rc){ 2403 /* FIXME: we need a way of passing this error back | 2402 /* FIXME: we need a way of passing this error back 2404 up to the routine which called this callback. | 2403 up to the routine which called this callback. 2405 e.g. add g.errCode. | 2404 e.g. add g.errCode. 2406 */ | 2405 */ 2407 fossil_exit(1); | 2406 fossil_exit(1); 2408 } | 2407 } 2409 } < 2410 return; 2408 return; 2411 usage: 2409 usage: 2412 { 2410 { 2413 Blob cmdNames = empty_blob; < 2414 blob_init(&cmdNames, < > 2411 cson_value * payload; > 2412 json_dispatch_missing_args_err( JsonPageDefs, 2415 "No subcommand specified. Try one of: ", -1); | 2413 "No subcommand specified." 2416 json_pagedefs_to_string(&JsonPageDefs[0], &cmdNames); < 2417 json_err(FSL_JSON_E_MISSING_ARGS, < 2418 blob_str(&cmdNames), 1); < 2419 blob_reset(&cmdNames); < > 2414 " Try one of: "); > 2415 payload = json_create_response(0, NULL, NULL); > 2416 json_send_response(payload); > 2417 cson_value_free(payload); 2420 fossil_exit(1); 2418 fossil_exit(1); 2421 } 2419 } 2422 } 2420 } 2423 #endif /* FOSSIL_ENABLE_JSON */ | 2421 #endif /* FOSSIL_ENABLE_JSON for mkindex */ 2424 2422 2425 #undef BITSET_BYTEFOR < 2426 #undef BITSET_SET < 2427 #undef BITSET_UNSET < 2428 #undef BITSET_GET < 2429 #undef BITSET_TOGGLE < 2430 #endif /* FOSSIL_ENABLE_JSON */ 2423 #endif /* FOSSIL_ENABLE_JSON */

Changes to src/json_artifact.c

46 /** 46 /** 47 JSON construction callback. Creates the contents for the 47 JSON construction callback. Creates the contents for the 48 payload.artifact property of /json/artifact responses. 48 payload.artifact property of /json/artifact responses. 49 */ 49 */ 50 artifact_f func; 50 artifact_f func; 51 } ArtifactDispatchEntry; 51 } ArtifactDispatchEntry; 52 52 > 53 > 54 /* > 55 ** Generates a JSON Array reference holding the parent UUIDs (as strings). > 56 ** If it finds no matches then it returns NULL (OOM is a fatal error). > 57 ** > 58 ** Returned value is NULL or an Array owned by the caller. > 59 */ > 60 cson_value * json_parent_uuids_for_ci( int rid ){ > 61 Stmt q = empty_Stmt; > 62 cson_array * pParents = NULL; > 63 db_prepare( &q, > 64 "SELECT uuid FROM plink, blob" > 65 " WHERE plink.cid=%d AND blob.rid=plink.pid" > 66 " ORDER BY plink.isprim DESC", > 67 rid ); > 68 while( SQLITE_ROW==db_step(&q) ){ > 69 if(!pParents) { > 70 pParents = cson_new_array(); > 71 } > 72 cson_array_append( pParents, cson_sqlite3_column_to_value( q.pStmt, 0 ) ); > 73 } > 74 db_finalize(&q); > 75 return cson_array_value(pParents); > 76 } 53 77 54 /* 78 /* 55 ** Generates an artifact Object for the given rid, 79 ** Generates an artifact Object for the given rid, 56 ** which must refer to a Checkin. 80 ** which must refer to a Checkin. 57 ** 81 ** 58 ** Returned value is NULL or an Object owned by the caller. 82 ** Returned value is NULL or an Object owned by the caller. 59 */ 83 */ 60 cson_value * json_artifact_for_ci( int rid, char showFiles ){ 84 cson_value * json_artifact_for_ci( int rid, char showFiles ){ 61 char * zParent = NULL; < 62 cson_value * v = NULL; 85 cson_value * v = NULL; 63 Stmt q; | 86 Stmt q = empty_Stmt; 64 static cson_value * eventTypeLabel = NULL; 87 static cson_value * eventTypeLabel = NULL; 65 if(!eventTypeLabel){ 88 if(!eventTypeLabel){ 66 eventTypeLabel = json_new_string("checkin"); 89 eventTypeLabel = json_new_string("checkin"); 67 json_gc_add("$EVENT_TYPE_LABEL(commit)", eventTypeLabel); 90 json_gc_add("$EVENT_TYPE_LABEL(commit)", eventTypeLabel); 68 } 91 } 69 zParent = db_text(0, < 70 "SELECT uuid FROM plink, blob" < 71 " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", < 72 rid < 73 ); < 74 | 92 75 db_prepare(&q, | 93 db_prepare(&q, 76 "SELECT uuid, " | 94 "SELECT b.uuid, " 77 " cast(strftime('%%s',mtime) as int), " | 95 " cast(strftime('%%s',e.mtime) as int), " > 96 " strftime('%%s',e.omtime)," 78 " user, " | 97 " e.user, " 79 " comment," | 98 " e.comment" 80 " strftime('%%s',omtime)" < 81 " FROM blob, event" | 99 " FROM blob b, event e" 82 " WHERE blob.rid=%d" | 100 " WHERE b.rid=%d" 83 " AND event.objid=%d", | 101 " AND e.objid=%d", 84 rid, rid 102 rid, rid 85 ); 103 ); 86 if( db_step(&q)==SQLITE_ROW ){ 104 if( db_step(&q)==SQLITE_ROW ){ 87 cson_object * o; 105 cson_object * o; 88 cson_value * tmpV = NULL; 106 cson_value * tmpV = NULL; 89 const char *zUuid = db_column_text(&q, 0); 107 const char *zUuid = db_column_text(&q, 0); 90 char * zTmp; < 91 const char *zUser; 108 const char *zUser; 92 const char *zComment; 109 const char *zComment; 93 char * zEUser, * zEComment; 110 char * zEUser, * zEComment; 94 int mtime, omtime; 111 int mtime, omtime; <