Login
Changes On Branch 033d78f03f076322
Login

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

Changes In Branch sha3-port Through [033d78f03f] Excluding Merge-Ins

This is equivalent to a diff from f16a3dd322 to 033d78f03f

2021-02-08
20:45
MILESTONE: f-ls just read a file listing from the main fossil repo (v2 hashes). Set f-mfparse back to hard-coded sha1 for the time being, as i'm not sure i can automatically figure out one or the other hash (need to add a flag to specify which to use). check-in: 8367e0ce6e user: stephan tags: sha3-port
20:35
Ported in more of the fossil(1) hashing-related infrastructure. check-in: 033d78f03f user: stephan tags: sha3-port
18:23
f-apps compile and f-sanity runs. MILESTONE. Still need to test the other f-apps, though. check-in: 76c290eb05 user: stephan tags: sha3-port
16:42
th1 was removed by stephan check-in: cc0bac7250 user: dan tags: trunk
16:03
Baby steps towards dual sha1/sha3 support. This does not yet compile but i'm nervous about losing work done so far. It's been painless but tedious, which is not bad, all things considered. check-in: 240701a6ec user: stephan tags: sha3-port
13:34
Apply the sqlite3.c CPPFLAGS currently used by fossil(1). check-in: f16a3dd322 user: stephan tags: trunk
07:44
Brought core SQL schemas up to date wrt fossil(1). check-in: 490dadcad9 user: stephan tags: trunk

Changes to auto.def.
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# Each generated Makefile requires an input file with a .in extension:
set makefiles {
    config.make
    Makefile
    doc/Doxyfile
    src/Makefile
    f-apps/Makefile
    th1ish/Makefile
    cpp/Makefile
}
foreach {f} $makefiles {
    makeFromDotIn $f
}

if {0} {







<







316
317
318
319
320
321
322

323
324
325
326
327
328
329
# Each generated Makefile requires an input file with a .in extension:
set makefiles {
    config.make
    Makefile
    doc/Doxyfile
    src/Makefile
    f-apps/Makefile

    cpp/Makefile
}
foreach {f} $makefiles {
    makeFromDotIn $f
}

if {0} {
Changes to autosetup/README.autosetup.
1
2
3
4
5
6
7
8
README.autosetup created by autosetup v0.7.0+

This is the autosetup directory for a local install of autosetup.
It contains autosetup, support files and loadable modules.

*.tcl files in this directory are optional modules which
can be loaded with the 'use' directive.

|







1
2
3
4
5
6
7
8
README.autosetup created by autosetup v0.6.9

This is the autosetup directory for a local install of autosetup.
It contains autosetup, support files and loadable modules.

*.tcl files in this directory are optional modules which
can be loaded with the 'use' directive.

Changes to autosetup/autosetup.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/sh
# Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# vim:se syntax=tcl:
# \
dir=`dirname "$0"`; exec "`$dir/autosetup-find-tclsh`" "$0" "$@"

# Note that the version has a trailing + on unreleased versions
set autosetup(version) 0.7.0+

# Can be set to 1 to debug early-init problems
set autosetup(debug) [expr {"--debug" in $argv}]

##################################################################
#
# Main flow of control, option handling








|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/sh
# Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# vim:se syntax=tcl:
# \
dir=`dirname "$0"`; exec "`$dir/autosetup-find-tclsh`" "$0" "$@"

# Note that the version has a trailing + on unreleased versions
set autosetup(version) 0.6.9

# Can be set to 1 to debug early-init problems
set autosetup(debug) [expr {"--debug" in $argv}]

##################################################################
#
# Main flow of control, option handling
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

	# At the is point we don't know what is a valid option
	# We simply parse anything that looks like an option
	set autosetup(getopt) [getopt argv]

	#"=Core Options:"
	options-add {
		help:=all       => "display help and options. Optional: module name, such as --help=system"
		licence license => "display the autosetup license"
		version         => "display the version of autosetup"
		ref:=text manual:=text
		reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'"
		debug           => "display debugging output as autosetup runs"
		install:=.      => "install autosetup to the current or given directory"
	}
	if {$autosetup(installed)} {
		# hidden options so we can produce a nice error
		options-add {
			sysinstall:path
		}
	} else {







|

|


|
|







89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

	# At the is point we don't know what is a valid option
	# We simply parse anything that looks like an option
	set autosetup(getopt) [getopt argv]

	#"=Core Options:"
	options-add {
		help:=local  => "display help and options. Optionally specify a module name, such as --help=system"
		licence license => "display the autosetup license"
		version      => "display the version of autosetup"
		ref:=text manual:=text
		reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'"
		debug        => "display debugging output as autosetup runs"
		install:=.   => "install autosetup to the current or given directory"
	}
	if {$autosetup(installed)} {
		# hidden options so we can produce a nice error
		options-add {
			sysinstall:path
		}
	} else {
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
		} else {
			user-error "Unexpected parameter: $arg"
		}
	}

	autosetup_add_dep $autosetup(autodef)

	# Add $argv to CONFIGURE_OPTS, but ignore duplicates and quote if needed
	set configure_opts {}
	foreach arg $autosetup(argv) {
		set quoted [quote-if-needed $arg]
		# O(n^2), but n will be small
		if {$quoted ni $configure_opts} {
			lappend configure_opts $quoted
		}
	}
	define CONFIGURE_OPTS [join $configure_opts]
	define AUTOREMAKE [file-normalize $autosetup(exe)]
	define-append AUTOREMAKE [get-define CONFIGURE_OPTS]


	# Log how we were invoked
	configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]"
	configlog "Tclsh: [info nameofexecutable]"

	# Load auto.def as module "auto.def"
	autosetup_load_module auto.def source $autosetup(autodef)

	# Could warn here if options {} was not specified

	show-notices

	if {$autosetup(debug)} {
		msg-result "Writing all defines to config.log"







|
<

|
<
<
<
|
<
<








|
|







200
201
202
203
204
205
206
207

208
209



210


211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
		} else {
			user-error "Unexpected parameter: $arg"
		}
	}

	autosetup_add_dep $autosetup(autodef)

	define CONFIGURE_OPTS ""

	foreach arg $autosetup(argv) {
		define-append CONFIGURE_OPTS [quote-if-needed $arg]



	}


	define AUTOREMAKE [file-normalize $autosetup(exe)]
	define-append AUTOREMAKE [get-define CONFIGURE_OPTS]


	# Log how we were invoked
	configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]"
	configlog "Tclsh: [info nameofexecutable]"

	# Note that auto.def is *not* loaded in the global scope
	source $autosetup(autodef)

	# Could warn here if options {} was not specified

	show-notices

	if {$autosetup(debug)} {
		msg-result "Writing all defines to config.log"
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
			set result [lindex [dict get $::autosetup(optset) $opt] end]
		}
	}

	if {![info exists result]} {
		# No user-specified value. Has options-defaults been set?
		foreach opt $names {
			if {[dict exists $::autosetup(optdefault) $opt]} {
				set result [dict get $autosetup(optdefault) $opt]
			}
		}
	}

	if {[info exists result]} {
		set value $result
		if {$retopt} {







|
|







338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
			set result [lindex [dict get $::autosetup(optset) $opt] end]
		}
	}

	if {![info exists result]} {
		# No user-specified value. Has options-defaults been set?
		foreach opt $names {
			if {[dict exists $::autosetup(options-defaults) $opt]} {
				set result [dict get $autosetup(options-defaults) $opt]
			}
		}
	}

	if {[info exists result]} {
		set value $result
		if {$retopt} {
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
		}
	}
}

# Parse the option definition in $opts and update
# ::autosetup(setoptions) and ::autosetup(optionhelp) appropriately
#
proc options-add {opts} {
	global autosetup

	# First weed out comment lines
	set realopts {}
	foreach line [split $opts \n] {
		if {![string match "#*" [string trimleft $line]]} {
			append realopts $line \n
		}
	}
	set opts $realopts

	for {set i 0} {$i < [llength $opts]} {incr i} {
		set opt [lindex $opts $i]
		if {[string match =* $opt]} {
			# This is a special heading
			lappend autosetup(optionhelp) [list $opt $autosetup(module)]

			continue
		}
		unset -nocomplain defaultvalue equal value

		#puts "i=$i, opt=$opt"
		regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value
		if {$name in $autosetup(options)} {







|















|
>







371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
		}
	}
}

# Parse the option definition in $opts and update
# ::autosetup(setoptions) and ::autosetup(optionhelp) appropriately
#
proc options-add {opts {header ""}} {
	global autosetup

	# First weed out comment lines
	set realopts {}
	foreach line [split $opts \n] {
		if {![string match "#*" [string trimleft $line]]} {
			append realopts $line \n
		}
	}
	set opts $realopts

	for {set i 0} {$i < [llength $opts]} {incr i} {
		set opt [lindex $opts $i]
		if {[string match =* $opt]} {
			# This is a special heading
			lappend autosetup(optionhelp) $opt ""
			set header {}
			continue
		}
		unset -nocomplain defaultvalue equal value

		#puts "i=$i, opt=$opt"
		regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value
		if {$name in $autosetup(options)} {
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475

476
477
478
479
480
481
482
483
484
485
				dict set autosetup(optset) $name $setvalue
				#puts "Found boolean option --$name=$setvalue"
			}
		} else {
			# String option.
			lappend autosetup(options) $name

			if {$equal ne "="} {
				# Was the option given as "name:value=default"?
				# If so, set $value to the display name and $defaultvalue to the default
				# (This is the preferred way to set a default value for a string option)
				if {[regexp {^([^=]+)=(.*)$} $value -> value defaultvalue]} {
					dict set autosetup(optdefault) $name $defaultvalue
				}
			}

			# Maybe override the default value
			if {[dict exists $autosetup(options-defaults) $name]} {
				# A default was specified with options-defaults, so use it
				set defaultvalue [dict get $autosetup(options-defaults) $name]
				dict set autosetup(optdefault) $name $defaultvalue
			} elseif {![info exists defaultvalue]} {

				# No default value was given by value=default or options-defaults
				# so use the value as the default when the plain option with no
				# value is given (.e.g. just --opt instead of --opt=value)
				set defaultvalue $value
			}

			if {$equal eq "="} {
				# String option with optional value
				set opthelp "--$name?=$value?"
			} else {







|
|













>
|
|
<







449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473

474
475
476
477
478
479
480
				dict set autosetup(optset) $name $setvalue
				#puts "Found boolean option --$name=$setvalue"
			}
		} else {
			# String option.
			lappend autosetup(options) $name

			if {$colon eq ":"} {
				# Was ":name=default" given?
				# If so, set $value to the display name and $defaultvalue to the default
				# (This is the preferred way to set a default value for a string option)
				if {[regexp {^([^=]+)=(.*)$} $value -> value defaultvalue]} {
					dict set autosetup(optdefault) $name $defaultvalue
				}
			}

			# Maybe override the default value
			if {[dict exists $autosetup(options-defaults) $name]} {
				# A default was specified with options-defaults, so use it
				set defaultvalue [dict get $autosetup(options-defaults) $name]
				dict set autosetup(optdefault) $name $defaultvalue
			} elseif {![info exists defaultvalue]} {
				# For backward compatibility, if ":name" was given, use name as both
				# the display text and the default value, but only if the user
				# specified the option without the value

				set defaultvalue $value
			}

			if {$equal eq "="} {
				# String option with optional value
				set opthelp "--$name?=$value?"
			} else {
509
510
511
512
513
514
515
516





517
518
519
520
521
522
523
524
525
526
527




528








529
530
531
532
533
534
535
		}

		# Now create the help for this option if appropriate
		if {[lindex $opts $i+1] eq "=>"} {
			set desc [lindex $opts $i+2]
			if {[info exists defaultvalue]} {
				set desc [string map [list @default@ $defaultvalue] $desc]
			}





			# A multi-line description
			lappend autosetup(optionhelp) [list $opthelp $autosetup(module) $desc]
			incr i 2
		}
	}
}

# @module-options optionlist
#
# Deprecated. Simply use 'options' from within a module.
proc module-options {opts} {




	options $opts








}

proc max {a b} {
	expr {$a > $b ? $a : $b}
}

proc options-wrap-desc {text length firstprefix nextprefix initial} {








>
>
>
>
>

|







|

>
>
>
>
|
>
>
>
>
>
>
>
>







504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
		}

		# Now create the help for this option if appropriate
		if {[lindex $opts $i+1] eq "=>"} {
			set desc [lindex $opts $i+2]
			if {[info exists defaultvalue]} {
				set desc [string map [list @default@ $defaultvalue] $desc]
			}
			#string match \n* $desc
			if {$header ne ""} {
				lappend autosetup(optionhelp) $header ""
				set header ""
			}
			# A multi-line description
			lappend autosetup(optionhelp) $opthelp $desc
			incr i 2
		}
	}
}

# @module-options optionlist
#
# Like 'options', but used within a module.
proc module-options {opts} {
	set header ""
	if {$::autosetup(showhelp) > 1 && [llength $opts]} {
		set header "Module Options:"
	}
	options-add $opts $header

	if {$::autosetup(showhelp)} {
		# Ensure that the module isn't executed on --help
		# We are running under eval or source, so use break
		# to prevent further execution
		#return -code break -level 2
		return -code break
	}
}

proc max {a b} {
	expr {$a > $b ? $a : $b}
}

proc options-wrap-desc {text length firstprefix nextprefix initial} {
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
		set space " "
	}
	if {$len} {
		puts ""
	}
}

# Display options (from $autosetup(optionhelp)) for modules that match
# glob pattern $what
proc options-show {what} {
	set local 0
	# Determine the max option width
	set max 0
	foreach help $::autosetup(optionhelp) {
		lassign $help opt module desc
		if {![string match $what $module]} {
			continue
		}
		if {[string match =* $opt] || [string match \n* $desc]} {
			continue
		}
		set max [max $max [string length $opt]]
	}
	set indent [string repeat " " [expr $max+4]]
	set cols [getenv COLUMNS 80]
	catch {
		lassign [exec stty size] rows cols
	}
	incr cols -1
	# Now output
	foreach help $::autosetup(optionhelp) {
		lassign $help opt module desc
		if {![string match $what $module]} {
			continue
		}
		if {$local == 0 && $module eq "auto.def"} {
			puts "Local Options:"
			incr local
		}
		if {[string match =* $opt]} {
			# Output a special heading line"
			puts [string range $opt 1 end]
			continue
		}
		puts -nonewline "  [format %-${max}s $opt]"
		if {[string match \n* $desc]} {
			# Output a pre-formatted help description as-is
			puts $desc
		} else {
			options-wrap-desc [string trim $desc] $cols "  " $indent [expr $max + 2]
		}
	}
}








<
<
|
<


|
<
<
<
<












|
<
<
<
<
<
<
<
<

<





<







562
563
564
565
566
567
568


569

570
571
572




573
574
575
576
577
578
579
580
581
582
583
584
585








586

587
588
589
590
591

592
593
594
595
596
597
598
		set space " "
	}
	if {$len} {
		puts ""
	}
}



proc options-show {} {

	# Determine the max option width
	set max 0
	foreach {opt desc} $::autosetup(optionhelp) {




		if {[string match =* $opt] || [string match \n* $desc]} {
			continue
		}
		set max [max $max [string length $opt]]
	}
	set indent [string repeat " " [expr $max+4]]
	set cols [getenv COLUMNS 80]
	catch {
		lassign [exec stty size] rows cols
	}
	incr cols -1
	# Now output
	foreach {opt desc} $::autosetup(optionhelp) {








		if {[string match =* $opt]} {

			puts [string range $opt 1 end]
			continue
		}
		puts -nonewline "  [format %-${max}s $opt]"
		if {[string match \n* $desc]} {

			puts $desc
		} else {
			options-wrap-desc [string trim $desc] $cols "  " $indent [expr $max + 2]
		}
	}
}

611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645

646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
#
## name[=0|1]  => "Description of this boolean option"
#
# The default is 'name=0', meaning that the option is disabled by default.
# If 'name=1' is used to make the option enabled by default, the description should reflect
# that with text like "Disable support for ...".
#
# An argument option (one which takes a parameter) is of one of the following forms:
#
## name:value         => "Description of this option"
## name:value=default => "Description of this option with a default value"
## name:=value        => "Description of this option with an optional value"
#
# If the 'name:value' form is used, the value must be provided with the option (as '--name=myvalue').
# If the 'name:value=default' form is used, the option has the given default value even if not
# specified by the user.
# If the 'name:=value' form is used, the value is optional and the given value is used
# if it is not provided.
#
# The description may contain '@default@', in which case it will be replaced with the default
# value for the option (taking into account defaults specified with 'options-defaults'.
#
# Undocumented options are also supported by omitting the '=> description'.
# These options are not displayed with '--help' and can be useful for internal options or as aliases.
#
# For example, '--disable-lfs' is an alias for '--disable=largefile':
#
## lfs=1 largefile=1 => "Disable large file support"
#
proc options {optlist} {
	global autosetup

	options-add $optlist

	if {$autosetup(showhelp)} {

		# If --help, stop now to show help
		return -code break
	}

	if {$autosetup(module) eq "auto.def"} {
		# Check for invalid options
		if {[opt-bool option-checking]} {
			foreach o [dict keys $::autosetup(getopt)] {
				if {$o ni $::autosetup(options)} {
					user-error "Unknown option --$o"
				}
			}
		}
	}
}

# @options-defaults dictionary
#







|

|
<
<


<
<
|













<
|
|

|
>
|
<


<
|
|
|
|
|
<







606
607
608
609
610
611
612
613
614
615


616
617


618
619
620
621
622
623
624
625
626
627
628
629
630
631

632
633
634
635
636
637

638
639

640
641
642
643
644

645
646
647
648
649
650
651
#
## name[=0|1]  => "Description of this boolean option"
#
# The default is 'name=0', meaning that the option is disabled by default.
# If 'name=1' is used to make the option enabled by default, the description should reflect
# that with text like "Disable support for ...".
#
# An argument option (one which takes a parameter) is of the form:
#
## name:[=]value  => "Description of this option"


#
# If the 'name:value' form is used, the value must be provided with the option (as '--name=myvalue').


# If the 'name:=value' form is used, the value is optional and the given value is used as the default
# if it is not provided.
#
# The description may contain '@default@', in which case it will be replaced with the default
# value for the option (taking into account defaults specified with 'options-defaults'.
#
# Undocumented options are also supported by omitting the '=> description'.
# These options are not displayed with '--help' and can be useful for internal options or as aliases.
#
# For example, '--disable-lfs' is an alias for '--disable=largefile':
#
## lfs=1 largefile=1 => "Disable large file support"
#
proc options {optlist} {

	# Allow options as a list or args
	options-add $optlist "Local Options:"

	if {$::autosetup(showhelp)} {
		options-show
		exit 0

	}


	# Check for invalid options
	if {[opt-bool option-checking]} {
		foreach o [dict keys $::autosetup(getopt)] {
			if {$o ni $::autosetup(options)} {
				user-error "Unknown option --$o"

			}
		}
	}
}

# @options-defaults dictionary
#
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
		lappend dirs $autosetup(srcdir)/autosetup
	}
	foreach m $args {
		if {[info exists libmodule($m)]} {
			continue
		}
		set libmodule($m) 1

		if {[info exists modsource(${m}.tcl)]} {
			autosetup_load_module $m eval $modsource(${m}.tcl)
		} else {
			set locs [list ${m}.tcl ${m}/init.tcl]
			set found 0
			foreach dir $dirs {
				foreach loc $locs {
					set source $dir/$loc
					if {[file exists $source]} {
						incr found
						break
					}
				}
				if {$found} {
					break
				}
			}
			if {$found} {
				# For the convenience of the "use" source, point to the directory
				# it is being loaded from
				set ::usedir [file dirname $source]
				autosetup_load_module $m source $source
				autosetup_add_dep $source
			} else {
				autosetup-error "use: No such module: $m"
			}
		}
	}
}

proc autosetup_load_auto_modules {} {
	global autosetup modsource
	# First load any embedded auto modules
	foreach mod [array names modsource *.auto] {
		autosetup_load_module $mod eval $modsource($mod)
	}
	# Now any external auto modules
	foreach file [glob -nocomplain $autosetup(libdir)/*.auto $autosetup(libdir)/*/*.auto] {
		autosetup_load_module [file tail $file] source $file
	}
}

# Load module source in the global scope by executing the given command
proc autosetup_load_module {module args} {
	global autosetup
	set prev $autosetup(module)
	set autosetup(module) $module

	if {[catch [list uplevel #0 $args] msg opts] ni {0 2 3}} {
		autosetup-full-error [error-dump $msg $opts $::autosetup(debug)]
	}
	set autosetup(module) $prev
}

# Initial settings
set autosetup(exe) $::argv0
set autosetup(istcl) 1
set autosetup(start) [clock millis]
set autosetup(installed) 0
set autosetup(sysinstall) 0
set autosetup(msg-checking) 0
set autosetup(msg-quiet) 0
set autosetup(inittypes) {}
set autosetup(module) autosetup

# Embedded modules are inserted below here
set autosetup(installed) 1
set autosetup(sysinstall) 0
# ----- @module asciidoc-formatting.tcl -----

set modsource(asciidoc-formatting.tcl) {







<

|



















|












|



|




<
<
<
<
|



<











<







1169
1170
1171
1172
1173
1174
1175

1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218




1219
1220
1221
1222

1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233

1234
1235
1236
1237
1238
1239
1240
		lappend dirs $autosetup(srcdir)/autosetup
	}
	foreach m $args {
		if {[info exists libmodule($m)]} {
			continue
		}
		set libmodule($m) 1

		if {[info exists modsource(${m}.tcl)]} {
			automf_load eval $modsource(${m}.tcl)
		} else {
			set locs [list ${m}.tcl ${m}/init.tcl]
			set found 0
			foreach dir $dirs {
				foreach loc $locs {
					set source $dir/$loc
					if {[file exists $source]} {
						incr found
						break
					}
				}
				if {$found} {
					break
				}
			}
			if {$found} {
				# For the convenience of the "use" source, point to the directory
				# it is being loaded from
				set ::usedir [file dirname $source]
				automf_load source $source
				autosetup_add_dep $source
			} else {
				autosetup-error "use: No such module: $m"
			}
		}
	}
}

proc autosetup_load_auto_modules {} {
	global autosetup modsource
	# First load any embedded auto modules
	foreach mod [array names modsource *.auto] {
		automf_load eval $modsource($mod)
	}
	# Now any external auto modules
	foreach file [glob -nocomplain $autosetup(libdir)/*.auto $autosetup(libdir)/*/*.auto] {
		automf_load source $file
	}
}

# Load module source in the global scope by executing the given command




proc automf_load {args} {
	if {[catch [list uplevel #0 $args] msg opts] ni {0 2 3}} {
		autosetup-full-error [error-dump $msg $opts $::autosetup(debug)]
	}

}

# Initial settings
set autosetup(exe) $::argv0
set autosetup(istcl) 1
set autosetup(start) [clock millis]
set autosetup(installed) 0
set autosetup(sysinstall) 0
set autosetup(msg-checking) 0
set autosetup(msg-quiet) 0
set autosetup(inittypes) {}


# Embedded modules are inserted below here
set autosetup(installed) 1
set autosetup(sysinstall) 0
# ----- @module asciidoc-formatting.tcl -----

set modsource(asciidoc-formatting.tcl) {
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461

1462
1463
1464
1465
1466
1467

1468
1469

1470


1471
1472
1473

1474
1475
1476
1477
1478
1479
1480
proc autosetup_help {what} {
    use_pager

    puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n"
    puts "This is [autosetup_version], a build environment \"autoconfigurator\""
    puts "See the documentation online at http://msteveb.github.com/autosetup/\n"

    if {$what in {all local}} {
        # Need to load auto.def now
        if {[file exists $::autosetup(autodef)]} {
            # Load auto.def as module "auto.def"

            autosetup_load_module auto.def source $::autosetup(autodef)
        }
        if {$what eq "all"} {
            set what *
        } else {
            set what auto.def

        }
    } else {

        use $what


        puts "Options for module $what:"
    }
    options-show $what

    exit 0
}

proc autosetup_show_license {} {
    global modsource autosetup
    use_pager








|
<

|
>
|
<
<
<

<
>


>
|
>
>
|
|
<
>







1432
1433
1434
1435
1436
1437
1438
1439

1440
1441
1442
1443



1444

1445
1446
1447
1448
1449
1450
1451
1452
1453

1454
1455
1456
1457
1458
1459
1460
1461
proc autosetup_help {what} {
    use_pager

    puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n"
    puts "This is [autosetup_version], a build environment \"autoconfigurator\""
    puts "See the documentation online at http://msteveb.github.com/autosetup/\n"

    if {$what eq "local"} {

        if {[file exists $::autosetup(autodef)]} {
            # This relies on auto.def having a call to 'options'
            # which will display options and quit
            source $::autosetup(autodef)



        } else {

            options-show
        }
    } else {
        incr ::autosetup(showhelp)
        if {[catch {use $what}]} {
            user-error "Unknown module: $what"
        } else {
            options-show
        }

    }
    exit 0
}

proc autosetup_show_license {} {
    global modsource autosetup
    use_pager

Changes to autosetup/autosetup-find-tclsh.
1
2
3
4
5


6
7
8
9
10
11
12
13
14
15
#!/bin/sh
# Looks for a suitable tclsh or jimsh in the PATH
# If not found, builds a bootstrap jimsh in current dir from source
# Prefer $autosetup_tclsh if is set in the environment (unless ./jimsh0 works)
d="`dirname "$0"`"


for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do
	{ $tclsh "$d/autosetup-test-tclsh"; } 2>/dev/null && exit 0
done
echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0"
for cc in ${CC_FOR_BUILD:-cc} gcc; do
	{ $cc -o jimsh0 "$d/jimsh0.c"; } 2>/dev/null || continue
	./jimsh0 "$d/autosetup-test-tclsh" && exit 0
done
echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc."
echo false


|
|
|
>
>
|




|
|



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

2
3
4
5
6
7
8
9
# For this project, disable the pager for --help

set useropts(nopager) 1

# Searches for a usable Tcl (prefer 8.6, 8.5, 8.4) in the given paths
# Returns a dictionary of the contents of the tclConfig.sh file, or
# empty if not found
proc parse-tclconfig-sh {args} {
	foreach p $args {
		# Allow pointing directly to the path containing tclConfig.sh
|
>
|







1
2
3
4
5
6
7
8
9
10
# For this project, disable the pager for --help and --ref
# The user can still enable by using --nopager=0 or --disable-nopager
dict set autosetup(optdefault) nopager 1

# Searches for a usable Tcl (prefer 8.6, 8.5, 8.4) in the given paths
# Returns a dictionary of the contents of the tclConfig.sh file, or
# empty if not found
proc parse-tclconfig-sh {args} {
	foreach p $args {
		# Allow pointing directly to the path containing tclConfig.sh
Changes to f-apps/f-mfparse.c.
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
    f_out("Rather large - not dumping to console.\n");
  }
  f_out("Dumping mf to file [%s]\n", ofile);
  rc = fsl_buffer_to_filename(&bout, ofile);
  assert(!rc);
  {
    fsl_buffer sha = fsl_buffer_empty;
    rc = fsl_sha1sum_filename(ofile, &sha);
    assert(!rc);
    f_out("SHA of [%s] = [%b]\n", ofile, &sha);
    seemsSafeEnough = (0==fsl_strcmp(fsl_buffer_cstr(&sha),
                                     mf.uuid));
    f_out("SHA match? %s\n",
           seemsSafeEnough ? "yes" : "NO!");
    fsl_buffer_clear(&sha);







|







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
    f_out("Rather large - not dumping to console.\n");
  }
  f_out("Dumping mf to file [%s]\n", ofile);
  rc = fsl_buffer_to_filename(&bout, ofile);
  assert(!rc);
  {
    fsl_buffer sha = fsl_buffer_empty;
    rc = fsl_sha3sum_filename(ofile, &sha);
    assert(!rc);
    f_out("SHA of [%s] = [%b]\n", ofile, &sha);
    seemsSafeEnough = (0==fsl_strcmp(fsl_buffer_cstr(&sha),
                                     mf.uuid));
    f_out("SHA match? %s\n",
           seemsSafeEnough ? "yes" : "NO!");
    fsl_buffer_clear(&sha);
Changes to f-apps/f-timeline.c.
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
                               /* "AND bf.rid=mlink.fid " */
                               /*"AND bm.rid=mlink.mid "*/
                               "ORDER BY filename.name %s",
                               fsl_cx_filename_collation(f));
    if(rc){
      return fsl_cx_uplift_db_error(f, db);
    }
    rc = fsl_stmt_bind_text(st, 1, uuid, FSL_UUID_STRLEN, 0);
    assert(0==rc);
    while(FSL_RC_STEP_ROW==(rc=fsl_stmt_step(st))){
      char const * changeType;
      if(!doneHead){
        doneHead = 1;
        fsl_outputf(f,"\n\t%-11s%s %13s Name\n", "Change", "File UUID", "Size");
      }







|







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
                               /* "AND bf.rid=mlink.fid " */
                               /*"AND bm.rid=mlink.mid "*/
                               "ORDER BY filename.name %s",
                               fsl_cx_filename_collation(f));
    if(rc){
      return fsl_cx_uplift_db_error(f, db);
    }
    rc = fsl_stmt_bind_text(st, 1, uuid, -1, 0);
    assert(0==rc);
    while(FSL_RC_STEP_ROW==(rc=fsl_stmt_step(st))){
      char const * changeType;
      if(!doneHead){
        doneHead = 1;
        fsl_outputf(f,"\n\t%-11s%s %13s Name\n", "Change", "File UUID", "Size");
      }
Changes to f-apps/test.c.
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
    assert(0==rc);
    assert(id>0);

    rc = fsl_sym_to_uuid(f, "current", FSL_CATYPE_CHECKIN, &uuid, &id2);
    assert(0==rc);
    assert(uuid);
    assert(id2 == id);
    assert(FSL_UUID_STRLEN==fsl_strlen(uuid));
    f_out("Current checkout: %.*s\n", FSL_UUID_STRLEN, uuid);
    fsl_free(uuid);
    
  }
  return rc;
}

/* static */ int test_leaves_rebuild(){
  int rc;
  f_out("Rebuilding leaves...\n");







|
|
|
<







112
113
114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
    assert(0==rc);
    assert(id>0);

    rc = fsl_sym_to_uuid(f, "current", FSL_CATYPE_CHECKIN, &uuid, &id2);
    assert(0==rc);
    assert(uuid);
    assert(id2 == id);
    assert(fsl_is_uuid(uuid));
    f_out("Current checkout: %s\n", uuid);
    fsl_free(uuid);    

  }
  return rc;
}

/* static */ int test_leaves_rebuild(){
  int rc;
  f_out("Rebuilding leaves...\n");
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);
  fsl_cx_err_report(f, 1);
  assert(!rc);
  assert(1032==c.used);
  rc = fsl_sha1sum_buffer( &c, &c );
  assert(!rc);
  assert(FSL_UUID_STRLEN==c.used);
  assert(0==fsl_strcmp( sym, fsl_buffer_cstr(&c) ));

  /* Now fetch a few other versions of that same file
     and ensure that they meet our expectations...
  */
  
  sym = "e9b776a8a72753823e4553a309edae21897cb2c4";
  rid = 0;
  rc = fsl_sym_to_rid(f, sym, FSL_CATYPE_ANY, &rid);
  assert(!rc);
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);
  assert(!rc);
  assert(2076==c.used);
  rc = fsl_sha1sum_buffer( &c, &c );
  assert(!rc);
  assert(FSL_UUID_STRLEN==c.used);
  assert(0==fsl_strcmp( sym, fsl_buffer_cstr(&c) ));

  sym = "f7d3a2e155d59a96ecc37001b05de26dee23c0cf";
  rid = 0;
  rc = fsl_sym_to_rid(f, sym, FSL_CATYPE_ANY, &rid);
  assert(!rc);
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);
  assert(!rc);
  assert(2481==c.used);
  rc = fsl_sha1sum_buffer( &c, &c );
  assert(!rc);
  assert(FSL_UUID_STRLEN==c.used);
  assert(0==fsl_strcmp( sym, fsl_buffer_cstr(&c) ));


  sym = "31e01e2a3c";
  rid = 0;
  rc = fsl_sym_to_rid(f, sym, FSL_CATYPE_WIKI, &rid);
  assert(!rc);







|
















|












|







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);
  fsl_cx_err_report(f, 1);
  assert(!rc);
  assert(1032==c.used);
  rc = fsl_sha1sum_buffer( &c, &c );
  assert(!rc);
  assert(fsl_is_uuid_len((int)c.used));
  assert(0==fsl_strcmp( sym, fsl_buffer_cstr(&c) ));

  /* Now fetch a few other versions of that same file
     and ensure that they meet our expectations...
  */
  
  sym = "e9b776a8a72753823e4553a309edae21897cb2c4";
  rid = 0;
  rc = fsl_sym_to_rid(f, sym, FSL_CATYPE_ANY, &rid);
  assert(!rc);
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);
  assert(!rc);
  assert(2076==c.used);
  rc = fsl_sha1sum_buffer( &c, &c );
  assert(!rc);
  assert(fsl_is_uuid_len((int)c.used));
  assert(0==fsl_strcmp( sym, fsl_buffer_cstr(&c) ));

  sym = "f7d3a2e155d59a96ecc37001b05de26dee23c0cf";
  rid = 0;
  rc = fsl_sym_to_rid(f, sym, FSL_CATYPE_ANY, &rid);
  assert(!rc);
  assert(rid>0);
  rc = fsl_content_get(f, rid, &c);
  assert(!rc);
  assert(2481==c.used);
  rc = fsl_sha1sum_buffer( &c, &c );
  assert(!rc);
  assert(fsl_is_uuid_len((int)c.used));
  assert(0==fsl_strcmp( sym, fsl_buffer_cstr(&c) ));


  sym = "31e01e2a3c";
  rid = 0;
  rc = fsl_sym_to_rid(f, sym, FSL_CATYPE_WIKI, &rid);
  assert(!rc);
Changes to include/fossil-scm/fossil-content.h.
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
FSL_EXPORT int fsl_delta_src_id( fsl_cx * f, fsl_id_t deltaRid, fsl_id_t * rv );


/**
   Return true if the given artifact ID should is listed in f's
   shun table, else false.
*/
FSL_EXPORT char fsl_uuid_is_shunned(fsl_cx * f, fsl_uuid_cstr zUuid);

/**
   Given a fsl_cx with an opened checkout, and a filename, this
   function canonicalizes zOrigName to a form suitable for use as
   an in-repo filename, _appending_ the results to pOut. If pOut is
   NULL, it performs its normal checking but does not write a
   result, other than to return 0 for success.







|







2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
FSL_EXPORT int fsl_delta_src_id( fsl_cx * f, fsl_id_t deltaRid, fsl_id_t * rv );


/**
   Return true if the given artifact ID should is listed in f's
   shun table, else false.
*/
FSL_EXPORT int fsl_uuid_is_shunned(fsl_cx * f, fsl_uuid_cstr zUuid);

/**
   Given a fsl_cx with an opened checkout, and a filename, this
   function canonicalizes zOrigName to a form suitable for use as
   an in-repo filename, _appending_ the results to pOut. If pOut is
   NULL, it performs its normal checking but does not write a
   result, other than to return 0 for success.
2994
2995
2996
2997
2998
2999
3000
3001
3002





















3003
3004
3005
3006
3007
3008
3009
   @endcode

   except that (A) bIn is const in this call and non-const in the
   other form (due to cursor traversal requirements) and (B) it
   returns FSL_RC_MISUSE if pIn is NULL.
*/
FSL_EXPORT int fsl_repo_import_buffer( fsl_cx * f, fsl_buffer const * bIn,
                            fsl_id_t * rid, fsl_uuid_str * uuid );























#if 0
/**
   NOT YET IMPLEMENTED - just thinking out loud here.
*/
struct fsl_repo_open_opt {








|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
   @endcode

   except that (A) bIn is const in this call and non-const in the
   other form (due to cursor traversal requirements) and (B) it
   returns FSL_RC_MISUSE if pIn is NULL.
*/
FSL_EXPORT int fsl_repo_import_buffer( fsl_cx * f, fsl_buffer const * bIn,
                                       fsl_id_t * rid, fsl_uuid_str * uuid );


/**
   Hashes all of pIn, appending the hash to pOut. Returns 0 on succes,
   FSL_RC_OOM if allocation of space in pOut fails. The hash algorithm
   used depends on the given fossil context's current hash policy and
   the value of the 2nd argument:

   If the 2nd argument is false, the hash is performed per the first
   argument's policy. If the 2nd argument is true, the hash policy is
   effectively inverted. e.g. if the context prefers SHA3 hashes, the
   alternate form will use SHA1.

   Returns FSL_RC_RANGE if the hash is not possible due to conflicting
   values for the policy and its alternate (e.g. a context with policy
   FSL_HPOLICY_SHA3_ONLY will refuse to apply an SHA1 hash).

   Returns 0 on success.
*/
FSL_EXPORT int fsl_cx_hash_buffer( const fsl_cx * f, int useAlternate,
                                   fsl_buffer const * pIn,
                                   fsl_buffer * pOut);

#if 0
/**
   NOT YET IMPLEMENTED - just thinking out loud here.
*/
struct fsl_repo_open_opt {

Changes to include/fossil-scm/fossil-hash.h.
31
32
33
34
35
36
37
38
39


40
41
42
43
44
45
46



47



























48
49
50
51
52
53
54
#endif

/**
   Various set-in-stone constants used by the API.
*/
enum fsl_hash_constants {
/**
   The length, in bytes, of fossil's hex-form UUID strings.



   FIXME: SHA3
*/
FSL_UUID_STRLEN = 40,
/**
   The length, in bytes, of a hex-form MD5 hash.
*/
FSL_MD5_STRLEN = 32



};





























typedef struct fsl_md5_cx fsl_md5_cx;
typedef struct fsl_sha1_cx fsl_sha1_cx;
typedef struct fsl_sha3_cx fsl_sha3_cx;

/**







|
|
>
>
|

|



|
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#endif

/**
   Various set-in-stone constants used by the API.
*/
enum fsl_hash_constants {
/**
   The length, in bytes, of fossil's hex-form SHA1 UUID strings.
*/
FSL_UUIDv1_STRLEN = 40,
/**
   The length, in bytes, of fossil's hex-form SHA3-256 UUID strings.
*/
FSL_UUIDv2_STRLEN = 64,
/**
   The length, in bytes, of a hex-form MD5 hash.
*/
FSL_MD5_STRLEN = 32,

FSL_UUID_STRLEN_MIN = FSL_UUIDv1_STRLEN,
FSL_UUID_STRLEN_MAX = FSL_UUIDv2_STRLEN
};

/**
   Internal IDs for artifact hash types the library works.
 */
enum fsl_hash_types_t {
/** Invalid hash type. */
FSL_HTYPE_ERROR = 0,
/** SHA1. */
FSL_HTYPE_SHA1 = 1,
/** SHA3-256. */
FSL_HTYPE_K256 = 2
};
typedef enum fsl_hash_types_t fsl_hash_types_t;

enum fsl_hash_policy_t {
/* Use SHA1 hashes */
FSL_HPOLICY_SHA1 = 0,
/* SHA1 but auto-promote to SHA3 */
FSL_HPOLICY_AUTO = 1,
/* Use SHA3 hashes */
FSL_HPOLICY_SHA3 = 2,
/* Use SHA3 hashes exclusively */
FSL_HPOLICY_SHA3_ONLY = 3,
/* Shun all SHA1 objects */
FSL_HPOLICY_SHUN_SHA1 = 4
};
typedef enum fsl_hash_policy_t fsl_hash_policy_t;


typedef struct fsl_md5_cx fsl_md5_cx;
typedef struct fsl_sha1_cx fsl_sha1_cx;
typedef struct fsl_sha3_cx fsl_sha3_cx;

/**
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#endif
/**
   Holds state for SHA1 calculations. It is intended to be used
   like this:

   @code
   unsigned char digest[20]
   char hex[FSL_UUID_STRLEN+1];
   fsl_sha1_cx cx = fsl_sha1_cx_empty;
   // alternately: fsl_sha1_init(&cx)
   ...call fsl_sha1_update(&cx,...) any number of times to
   ...incrementally calculate the hash.
   fsl_sha1_final(&cx, digest); // ends the calculation
   fsl_sha1_digest_to_base16(digest, hex);
   // digest now contains the raw 20-byte SHA1 digest.







|







243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
#endif
/**
   Holds state for SHA1 calculations. It is intended to be used
   like this:

   @code
   unsigned char digest[20]
   char hex[FSL_UUIDv1_STRLEN+1];
   fsl_sha1_cx cx = fsl_sha1_cx_empty;
   // alternately: fsl_sha1_init(&cx)
   ...call fsl_sha1_update(&cx,...) any number of times to
   ...incrementally calculate the hash.
   fsl_sha1_final(&cx, digest); // ends the calculation
   fsl_sha1_digest_to_base16(digest, hex);
   // digest now contains the raw 20-byte SHA1 digest.
300
301
302
303
304
305
306
307








308
309
310
311
312
313
314
315
316
317
318
   returns 0.

   @see fsl_sha1_update()
   @see fsl_sha1_digest_to_base16()
*/
FSL_EXPORT int fsl_sha1_final(fsl_sha1_cx *context, unsigned char * digest);

/**








   Convert a digest into base-16.  digest must be at least 20 bytes
   long and hold an SHA1 digest. zBuf must be at least (FSL_UUID_STRLEN
   + 1) bytes long, for FSL_UUID_STRLEN characters of
   hexidecimal-form SHA1 hash and 1 NUL byte.

   @see fsl_sha1_final()
*/
FSL_EXPORT void fsl_sha1_digest_to_base16(unsigned char *digest, char *zBuf);

/**
   Computes the SHA1 checksum of pIn and stores the resulting








>
>
>
>
>
>
>
>

|
|
|







332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
   returns 0.

   @see fsl_sha1_update()
   @see fsl_sha1_digest_to_base16()
*/
FSL_EXPORT int fsl_sha1_final(fsl_sha1_cx *context, unsigned char * digest);

/**
   A convenience form of fsl_sha1_final() which writes
   FSL_UUIDv1_STRLEN+1 bytes (hash plus terminating NUL byte) to the
   2nd argument and returns a (const char *)-type cast of the 2nd
   argument.
*/
FSL_EXPORT const char * fsl_sha1_final_hex(fsl_sha1_cx *context, char * zHex);

/**
   Convert a digest into base-16.  digest must be at least 20 bytes
   long and hold an SHA1 digest. zBuf must be at least (FSL_UUIDv1_STRLEN
   + 1) bytes long, to which FSL_UUIDv1_STRLEN characters of
   hexidecimal-form SHA1 hash and 1 NUL byte will be written.

   @see fsl_sha1_final()
*/
FSL_EXPORT void fsl_sha1_digest_to_base16(unsigned char *digest, char *zBuf);

/**
   Computes the SHA1 checksum of pIn and stores the resulting
371
372
373
374
375
376
377
378


379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
enum fsl_sha3_hash_size {
/** Sentinel value. Must be 0. */
FSL_SHA3_INVALID = 0,
FSL_SHA3_128 = 128, FSL_SHA3_160 = 160, FSL_SHA3_192 = 192,
FSL_SHA3_224 = 224, FSL_SHA3_256 = 256, FSL_SHA3_288 = 288,
FSL_SHA3_320 = 320, FSL_SHA3_352 = 352, FSL_SHA3_384 = 384,
FSL_SHA3_416 = 416, FSL_SHA3_448 = 448, FSL_SHA3_480 = 480,
FSL_SHA3_512 = 512


};

/**
   Type for holding SHA3 processing state. Each instance must be
   initialized with fsl_sha3_init(), populated with fsl_sha3_update(),
   and "sealed" with fsl_sha3_end().

   Sample usage:

   @code
   fsl_sha3_cx cx;
   fsl_sha3_init(&cx, FSL_SHA3_256);
   fsl_sha3_update(&cx, memory, lengthOfMemory);
   fsl_sha3_end(&cx);
   printf("Hash = %s\n", (char const *)cx.hex);
   @endcode

   After fsl_sha3_end() is called cx.hex contains the hex-string forms
   of the digest. Note that fsl_sha3_update() may be called an arbitrary







|
>
>











|







411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
enum fsl_sha3_hash_size {
/** Sentinel value. Must be 0. */
FSL_SHA3_INVALID = 0,
FSL_SHA3_128 = 128, FSL_SHA3_160 = 160, FSL_SHA3_192 = 192,
FSL_SHA3_224 = 224, FSL_SHA3_256 = 256, FSL_SHA3_288 = 288,
FSL_SHA3_320 = 320, FSL_SHA3_352 = 352, FSL_SHA3_384 = 384,
FSL_SHA3_416 = 416, FSL_SHA3_448 = 448, FSL_SHA3_480 = 480,
FSL_SHA3_512 = 512,
/* Default SHA3 flavor */
FSL_SHA3_DEFAULT = 256
};

/**
   Type for holding SHA3 processing state. Each instance must be
   initialized with fsl_sha3_init(), populated with fsl_sha3_update(),
   and "sealed" with fsl_sha3_end().

   Sample usage:

   @code
   fsl_sha3_cx cx;
   fsl_sha3_init(&cx, FSL_SHA3_DEFAULT);
   fsl_sha3_update(&cx, memory, lengthOfMemory);
   fsl_sha3_end(&cx);
   printf("Hash = %s\n", (char const *)cx.hex);
   @endcode

   After fsl_sha3_end() is called cx.hex contains the hex-string forms
   of the digest. Note that fsl_sha3_update() may be called an arbitrary
416
417
418
419
420
421
422
423
424

425
426
427
428





429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450



451
452
453
454
455
456








457



458



459



460



461





































462
463
464
465
466
467
   entry is returned, else FSL_SHA3_INVALID is returned.

   @see fsl_sha3_init()
*/
FSL_EXPORT enum fsl_sha3_hash_size fsl_sha3_hash_size_for_int(int);

/**
   Initialize a new hash. The second argument specifies the size of the hash
   in bits. Results are undefined if cx is NULL or sz is not a valid value.


   After calling this, use fsl_sha3_update() to hash data and
   fsl_sha3_end() to finalize the hashing process and generate a digest.
*/





FSL_EXPORT void fsl_sha3_init(fsl_sha3_cx *cx, enum fsl_sha3_hash_size sz);

/**
   Updates cx's state to include the first len bytes of data.

   If cx is NULL results are undefined (segfault!). If mem is not
   NULL then it must be at least n bytes long. If n is 0 then this
   function has no side-effects.

   @see fsl_sha3_init()
   @see fsl_sha3_end()
*/
FSL_EXPORT void fsl_sha3_update( fsl_sha3_cx *cx, void const *data, unsigned int len);

/**
   To be called when hashing is complete: finishes the hash
   calculation and populates cx->hex with the final hash code in
   hexidecimal-string form. Returns the binary-form digest value,
   which refers to cx->size/8 bytes of memory which lives in the cx
   object. After this call cx->hex will be populated with cx->size/4
   bytes of lower-case ASCII hex codes plus a terminating NUL byte.




   @see fsl_sha3_init()
   @see fsl_sha3_update()
*/
FSL_EXPORT unsigned char const * fsl_sha3_end(fsl_sha3_cx *cx);

/* TODOs: port the sha1 counterparts of these to sha3: */








/* TODO */FSL_EXPORT void fsl_sha3_digest_to_base16(unsigned char *digest, char *zBuf);



/* TODO */FSL_EXPORT int fsl_sha3sum_buffer(fsl_buffer const *pIn, fsl_buffer *pCksum);



/* TODO */FSL_EXPORT char *fsl_sha3sum_cstr(const char *zIn, fsl_int_t len);



/* TODO */FSL_EXPORT int fsl_sha3sum_stream(fsl_input_f src, void * srcState, fsl_buffer *pCksum);



/* TODO */FSL_EXPORT int fsl_sha1sum_filename(const char *zFilename, fsl_buffer *pCksum);






































#if defined(__cplusplus)
} /*extern "C"*/
#endif
#endif
/* NET_FOSSIL_SCM_FSL_HASH_H_INCLUDED */







|
|
>




>
>
>
>
>
|














|






>
>
>





|
>
>
>
>
>
>
>
>
|
>
>
>
|
>
>
>
|
>
>
>
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
   entry is returned, else FSL_SHA3_INVALID is returned.

   @see fsl_sha3_init()
*/
FSL_EXPORT enum fsl_sha3_hash_size fsl_sha3_hash_size_for_int(int);

/**
   Initialize a new hash. The second argument specifies the size of
   the hash in bits. Results are undefined if cx is NULL or sz is not
   a valid positive value.

   After calling this, use fsl_sha3_update() to hash data and
   fsl_sha3_end() to finalize the hashing process and generate a digest.
*/
FSL_EXPORT void fsl_sha3_init2(fsl_sha3_cx *cx, enum fsl_sha3_hash_size sz);

/**
   Equivalent to fsl_sha3_init2(cx, FSL_SHA3_DEFAULT).
*/
FSL_EXPORT void fsl_sha3_init(fsl_sha3_cx *cx);

/**
   Updates cx's state to include the first len bytes of data.

   If cx is NULL results are undefined (segfault!). If mem is not
   NULL then it must be at least n bytes long. If n is 0 then this
   function has no side-effects.

   @see fsl_sha3_init()
   @see fsl_sha3_end()
*/
FSL_EXPORT void fsl_sha3_update( fsl_sha3_cx *cx, void const *data, unsigned int len);

/**
   To be called when SHA3 hashing is complete: finishes the hash
   calculation and populates cx->hex with the final hash code in
   hexidecimal-string form. Returns the binary-form digest value,
   which refers to cx->size/8 bytes of memory which lives in the cx
   object. After this call cx->hex will be populated with cx->size/4
   bytes of lower-case ASCII hex codes plus a terminating NUL byte.

   Potential TODO: change fsl_sha1_final() and fsl_md5_final() to use
   these same return semantics.

   @see fsl_sha3_init()
   @see fsl_sha3_update()
*/
FSL_EXPORT unsigned char const * fsl_sha3_end(fsl_sha3_cx *cx);


/**
   SHA3-256 counterpart of fsl_sha1_digest_to_base16(). digest must be at least
   32 bytes long and hold an SHA3 digest. zBuf must be at least (FSL_UUIDv2_STRLEN+1)
   bytes long, to which FSL_UUIDv2_STRLEN characters of
   hexidecimal-form SHA3 hash and 1 NUL byte will be written

   @see fsl_sha3_end().
*/
FSL_EXPORT void fsl_sha3_digest_to_base16(unsigned char *digest, char *zBuf);
/**
   SHA3 counter part of fsl_sha1sum_buffer().
*/
FSL_EXPORT int fsl_sha3sum_buffer(fsl_buffer const *pIn, fsl_buffer *pCksum);
/**
   SHA3 counter part of fsl_sha1sum_cstr().
*/
FSL_EXPORT char *fsl_sha3sum_cstr(const char *zIn, fsl_int_t len);
/**
   SHA3 counterpart of fsl_sha1sum_stream().
 */
FSL_EXPORT int fsl_sha3sum_stream(fsl_input_f src, void * srcState, fsl_buffer *pCksum);
/**
   SHA3 counterpart of fsl_sha1sum_filename().
 */
FSL_EXPORT int fsl_sha3sum_filename(const char *zFilename, fsl_buffer *pCksum);

/**
   Expects zHash to be a full-length hash value of one of the
   fsl_hash_types_t-specified types, and nHash to be the length, in
   bytes, of zHash's contents (which must be the full hash length, not
   a prefix). If zHash can be validated as a hash, its corresponding
   hash type is returned, else FSL_HTYPE_ERROR is returned.
*/
FSL_EXPORT fsl_hash_types_t fsl_validate_hash(const char *zHash, int nHash);

/**
   Expects (zHash, nHash) to refer to a full hash (of a supported
   content hash type) of pIn's contents. This routine hashes pIn's
   contents and, if it compares equivalent to zHash then the ID of the
   hash type is returned.  On a mismatch, FSL_HTYPE_ERROR is returned.
*/
FSL_EXPORT fsl_hash_types_t fsl_verify_blob_hash(fsl_buffer const * pIn,
                                                 const char *zHash, int nHash);

/**
   Sets f's hash policy and returns the previous value.
*/
FSL_EXPORT fsl_hash_policy_t fsl_cx_hash_policy_set(fsl_cx *f, fsl_hash_policy_t p);
/**
   Returns f's current hash policy.
*/
FSL_EXPORT fsl_hash_policy_t fsl_cx_hash_policy_get(fsl_cx const*f);
/**
   Returns a human-friendly name for f's current hash policy.
*/
FSL_EXPORT char const * fsl_cx_hash_policy_name(fsl_cx const*f);

/**
   Returns a human-readable name for the given hash type, or its
   second argument h is not a supported hash type.
 */
FSL_EXPORT const char * fsl_hash_type_name(fsl_hash_types_t h, const char *zUnknown);

#if defined(__cplusplus)
} /*extern "C"*/
#endif
#endif
/* NET_FOSSIL_SCM_FSL_HASH_H_INCLUDED */
Changes to include/fossil-scm/fossil-internal.h.
546
547
548
549
550
551
552

553
554
555
556
557
558
559
    fsl_id_t mtimeManifest;

    /**
       The "project-code" config option.
    */
    char * projectCode;


    /**
       Holds various glob lists.
    */
    struct {
      /**
         Holds the "ignore-glob" globs.
      */







>







546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
    fsl_id_t mtimeManifest;

    /**
       The "project-code" config option.
    */
    char * projectCode;

    fsl_hash_policy_t hashPolicy;
    /**
       Holds various glob lists.
    */
    struct {
      /**
         Holds the "ignore-glob" globs.
      */
666
667
668
669
670
671
672

673
674
675
676
677
678
679
680
681
682
683
684
              {/*mf*/ NULL/*head*/,5/*max*/,0/*size*/}, \
              fsl_acache_empty_m/*arty*/,               \
                fsl_id_bag_empty_m/*mfSeen*/,           \
                fsl_id_bag_empty_m/*leafCheck*/,        \
                fsl_id_bag_empty_m/*toVerify*/,         \
                0/*mtimeManifest*/,                     \
                NULL/*projectCode*/,                    \

                {/*globs*/                              \
                  fsl_list_empty_m/*ignore*/,           \
                    fsl_list_empty_m/*binary*/,         \
                    fsl_list_empty_m/*crnl*/            \
                    }                                   \
          },                                            \
          {/*ticket*/                                   \
            fsl_list_empty_m/*customFields*/,           \
              0/*hasTicket*/,                           \
              0/*hasCTime*/,                            \
              0/*hasChng*/,                             \
              0/*hasCngRid*/                            \







>




|







667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
              {/*mf*/ NULL/*head*/,5/*max*/,0/*size*/}, \
              fsl_acache_empty_m/*arty*/,               \
                fsl_id_bag_empty_m/*mfSeen*/,           \
                fsl_id_bag_empty_m/*leafCheck*/,        \
                fsl_id_bag_empty_m/*toVerify*/,         \
                0/*mtimeManifest*/,                     \
                NULL/*projectCode*/,                    \
                FSL_HPOLICY_AUTO/*hashPolicy*/,         \
                {/*globs*/                              \
                  fsl_list_empty_m/*ignore*/,           \
                    fsl_list_empty_m/*binary*/,         \
                    fsl_list_empty_m/*crnl*/            \
                }                                       \
          },                                            \
          {/*ticket*/                                   \
            fsl_list_empty_m/*customFields*/,           \
              0/*hasTicket*/,                           \
              0/*hasCTime*/,                            \
              0/*hasChng*/,                             \
              0/*hasCngRid*/                            \
Changes to include/fossil-scm/fossil-util.h.
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121





122
123
124
125
126
127
128
   arguments. That depends on the routine which uses the comparison
   function.
*/
typedef int (*fsl_generic_cmp_f)( void const * lhs, void const * rhs );

/**
   fsl_uuid_str and fsl_uuid_cstr are "for documentation and
   readability purposes" typedefs used to denote strings which the
   API requires to be in the form of Fossil UUID strings. Such
   strings are exactly FSL_UUID_STRLEN bytes long plus a
   terminating NUL byte and contain only lower-case hexadecimal
   bytes. Where this typedef is used, the library requires,
   enforces, and/or assumes (at different times) that fsl_is_uuid()
   returns true for such strings (if they are not NULL, though not
   all contexts allow a NULL UUID). These typedef are _not_ used to
   denote arguments which may refer to partial UUIDs or symbolic
   names, only 100% bonafide Fossil UUIDs (which are different from
   RFC4122 UUIDs).

   The API guarantees that this typedef will always be (char *) and
   that fsl_uuid_cstr will always ben (char const *), and thus it
   is safe/portable to use those type instead of thse. These
   typedefs serve only to improve the readability of certain APIs
   by implying (through the use of this typedef) the preconditions
   defined for UUID strings.

   @see fsl_is_uuid()
*/
typedef char * fsl_uuid_str;

/**
   The const counterpart of fsl_uuid_str.

   @see fsl_is_uuid()
*/
typedef char const * fsl_uuid_cstr;

/**
   Returns true (non-0) if str is not NULL, is exactly
   FSL_UUID_STRLEN bytes long (meaning its final byte is a NUL),
   and contains only lower-case hexadecimal characters, else
   returns false (0).

   Note that Fossil UUIDs are not RFC4122 UUIDs, but are SHA1
   hash strings. Don't let that disturb you. As Tim Berners-Lee
   writes:

   'The assertion that the space of URIs is a universal space
   sometimes encounters opposition from those who feel there should
   not be one universal space. These people need not oppose the
   concept because it is not of a single universal space: Indeed,
   the fact that URIs form universal space does not prevent anyone
   else from forming their own universal space, which of course by
   definition would be able to envelop within it as a subset the
   universal URI space. Therefore the web meets the "independent
   design" test, that if a similar system had been concurrently and
   independently invented elsewhere, in such a way that the
   arbitrary design decisions were made differently, when they met
   later, the two systems could be made to interoperate.'

   Source: https://www.w3.org/DesignIssues/Axioms.html

   (Just mentally translate URI as UUID.)
*/
FSL_EXPORT char fsl_is_uuid(char const * str);






/**
   Expects str to be a string containing an unsigned decimal
   value. Returns its decoded value, or -1 on error.
*/
FSL_EXPORT fsl_size_t fsl_str_to_size(char const * str);








|
|
|

|
|
|
|
|
|
|



|
















|
|
|
|

|
|
|


















|
>
>
>
>
>







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
   arguments. That depends on the routine which uses the comparison
   function.
*/
typedef int (*fsl_generic_cmp_f)( void const * lhs, void const * rhs );

/**
   fsl_uuid_str and fsl_uuid_cstr are "for documentation and
   readability purposes" typedefs used to denote strings which the API
   requires to be in the form of Fossil UUID strings. Such strings are
   exactly FSL_UUIDv1_STRLEN or FSL_UUIDv2_STRLEN bytes long plus a
   terminating NUL byte and contain only lower-case hexadecimal
   bytes. Where this typedef is used, the library requires, enforces,
   and/or assumes (at different times) that fsl_is_uuid() returns true
   for such strings (if they are not NULL, though not all contexts
   allow a NULL UUID). These typedef are _not_ used to denote
   arguments which may refer to partial UUIDs or symbolic names, only
   100% bonafide Fossil UUIDs (which are different from RFC4122
   UUIDs).

   The API guarantees that this typedef will always be (char *) and
   that fsl_uuid_cstr will always ben (char const *), and thus it
   is safe/portable to use those type instead of these. These
   typedefs serve only to improve the readability of certain APIs
   by implying (through the use of this typedef) the preconditions
   defined for UUID strings.

   @see fsl_is_uuid()
*/
typedef char * fsl_uuid_str;

/**
   The const counterpart of fsl_uuid_str.

   @see fsl_is_uuid()
*/
typedef char const * fsl_uuid_cstr;

/**
   If the NUL-terminated input str is exactly FSL_UUIDv1_STRLEN or
   FSL_UUIDv2_STRLEN bytes long and contains only lower-case
   hexadecimal characters, returns the length of the string, else
   returns 0.

   Note that Fossil UUIDs are not RFC4122 UUIDs, but are SHA1 or
   SHA3-256 hash strings. Don't let that disturb you. As Tim
   Berners-Lee writes:

   'The assertion that the space of URIs is a universal space
   sometimes encounters opposition from those who feel there should
   not be one universal space. These people need not oppose the
   concept because it is not of a single universal space: Indeed,
   the fact that URIs form universal space does not prevent anyone
   else from forming their own universal space, which of course by
   definition would be able to envelop within it as a subset the
   universal URI space. Therefore the web meets the "independent
   design" test, that if a similar system had been concurrently and
   independently invented elsewhere, in such a way that the
   arbitrary design decisions were made differently, when they met
   later, the two systems could be made to interoperate.'

   Source: https://www.w3.org/DesignIssues/Axioms.html

   (Just mentally translate URI as UUID.)
*/
FSL_EXPORT int fsl_is_uuid(char const * str);

/**
   If x is a valid fossil UUID length, it is returned, else 0 is returned.
*/
FSL_EXPORT int fsl_is_uuid_len(int x);

/**
   Expects str to be a string containing an unsigned decimal
   value. Returns its decoded value, or -1 on error.
*/
FSL_EXPORT fsl_size_t fsl_str_to_size(char const * str);

1452
1453
1454
1455
1456
1457
1458
1459










1460
1461
1462
1463
1464
1465
1466
/**
   fsl_strcmp() variant which compares at most nByte bytes of the
   given strings, case-sensitively. Returns 0 if nByte is 0.
*/
FSL_EXPORT int fsl_strncmp(const char *zA, const char *zB, fsl_size_t nByte);

/**
   Equivalent to fsl_strncmp(lhs, rhs, FSL_UUID_STRLEN).










*/
FSL_EXPORT int fsl_uuidcmp( fsl_uuid_cstr lhs, fsl_uuid_cstr rhs );

/**
   Returns false if s is NULL or starts with any of (0 (NUL), '0'
   (ASCII character zero), 'f', 'n', "off"), case-insensitively,
   else it returns true.







|
>
>
>
>
>
>
>
>
>
>







1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
/**
   fsl_strcmp() variant which compares at most nByte bytes of the
   given strings, case-sensitively. Returns 0 if nByte is 0.
*/
FSL_EXPORT int fsl_strncmp(const char *zA, const char *zB, fsl_size_t nByte);

/**
   Equivalent to fsl_strncmp(lhs, rhs, X), where X is either
   FSL_UUIDv1_STRLEN or FSL_UUIDv2_STRLEN: if both lhs and rhs are
   longer than FSL_UUIDv1_STRLEN then they are assumed to be
   FSL_UUIDv2_STRLEN bytes long and are compared as such, else they
   are assumed to be FSL_UUIDv1_STRLEN bytes long and compared as
   such.

   Potential FIXME/TODO: if their lengths differ, i.e. one is v1 and
   one is v2, compare them up to their common length then, if they
   still compare equivalent, treat the shorter one as less-than the
   longer.
*/
FSL_EXPORT int fsl_uuidcmp( fsl_uuid_cstr lhs, fsl_uuid_cstr rhs );

/**
   Returns false if s is NULL or starts with any of (0 (NUL), '0'
   (ASCII character zero), 'f', 'n', "off"), case-insensitively,
   else it returns true.
Changes to src/fsl.c.
183
184
185
186
187
188
189
190
191
192
193




194
195







196

197
198
199
200
201
202
203
    free(mem);
    return NULL;
  }else{
    return realloc(mem, n);
  }
}

char fsl_is_uuid(char const * str){
  fsl_size_t const len = fsl_strlen(str);
  return (FSL_UUID_STRLEN==len)
    && fsl_validate16(str, FSL_UUID_STRLEN);




}










void fsl_error_clear( fsl_error * err ){
  if(err){
    fsl_buffer_clear(&err->msg);
    *err = fsl_error_empty;
  }
}








|

|
|
>
>
>
>
|
|
>
>
>
>
>
>
>
|
>







183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
    free(mem);
    return NULL;
  }else{
    return realloc(mem, n);
  }
}

int fsl_is_uuid(char const * str){
  fsl_size_t const len = fsl_strlen(str);
  if(FSL_UUIDv1_STRLEN==len){
    return fsl_validate16(str, FSL_UUIDv1_STRLEN) ? FSL_UUIDv1_STRLEN : 0;
  }else if(FSL_UUIDv2_STRLEN==len){
    return fsl_validate16(str, FSL_UUIDv2_STRLEN) ? FSL_UUIDv2_STRLEN : 0;
  }else{
    return 0;
  }
}
int fsl_is_uuid_len(int x){
  switch(x){
    case FSL_UUIDv1_STRLEN:
    case FSL_UUIDv2_STRLEN:
      return x;
    default:
      return 0;
  }
}
void fsl_error_clear( fsl_error * err ){
  if(err){
    fsl_buffer_clear(&err->msg);
    *err = fsl_error_empty;
  }
}

368
369
370
371
372
373
374

375





376
377
378
379
380
381
382
    }while( a==b && a!=0 && (--nByte)>0 );
    return (nByte>0) ? (((unsigned char)a) - (unsigned char)b) : 0;
  }
}

  
int fsl_uuidcmp( fsl_uuid_cstr lhs, fsl_uuid_cstr rhs ){

  return fsl_strncmp( lhs, rhs, FSL_UUID_STRLEN );





}

int fsl_strnicmp(const char *zA, const char *zB, fsl_int_t nByte){
  if( zA==0 ){
    if( zB==0 ) return 0;
    return -1;
  }else if( zB==0 ){







>
|
>
>
>
>
>







380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
    }while( a==b && a!=0 && (--nByte)>0 );
    return (nByte>0) ? (((unsigned char)a) - (unsigned char)b) : 0;
  }
}

  
int fsl_uuidcmp( fsl_uuid_cstr lhs, fsl_uuid_cstr rhs ){
  if(lhs[FSL_UUIDv1_STRLEN] && rhs[FSL_UUIDv1_STRLEN]){
    return fsl_strncmp( lhs, rhs, FSL_UUIDv2_STRLEN);
  }else if(!lhs[FSL_UUIDv1_STRLEN] && !rhs[FSL_UUIDv1_STRLEN]){
    return fsl_strncmp( lhs, rhs, FSL_UUIDv1_STRLEN );
  }else{
    return fsl_strcmp(lhs, rhs);
  }      
}

int fsl_strnicmp(const char *zA, const char *zB, fsl_int_t nByte){
  if( zA==0 ){
    if( zB==0 ) return 0;
    return -1;
  }else if( zB==0 ){
Changes to src/fsl_auth.c.
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
                        fsl_strlen(f->cache.projectCode));
        fsl_sha1_update(&hash, "/", 1);
        fsl_sha1_update(&hash, zLoginName, fsl_strlen(zLoginName));
        fsl_sha1_update(&hash, "/", 1);
        fsl_sha1_update(&hash, zPw, fsl_strlen(zPw));
        fsl_sha1_final(&hash, zResult);
        fsl_sha1_digest_to_base16(zResult, zDigest);
        return fsl_strndup( zDigest, FSL_UUID_STRLEN );
    }
}

FSL_EXPORT char * fsl_repo_login_group_name(fsl_cx * f){
  return f
    ? fsl_config_get_text(f, FSL_CONFDB_REPO,
                          "login-group-name", 0)







|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
                        fsl_strlen(f->cache.projectCode));
        fsl_sha1_update(&hash, "/", 1);
        fsl_sha1_update(&hash, zLoginName, fsl_strlen(zLoginName));
        fsl_sha1_update(&hash, "/", 1);
        fsl_sha1_update(&hash, zPw, fsl_strlen(zPw));
        fsl_sha1_final(&hash, zResult);
        fsl_sha1_digest_to_base16(zResult, zDigest);
        return fsl_strndup( zDigest, FSL_UUIDv1_STRLEN );
    }
}

FSL_EXPORT char * fsl_repo_login_group_name(fsl_cx * f){
  return f
    ? fsl_config_get_text(f, FSL_CONFDB_REPO,
                          "login-group-name", 0)
Changes to src/fsl_content.c.
14
15
16
17
18
19
20
21
22
23


24
25
26
27
28
29
30
   Author contact information:
     drh@hwaci.com
     https://www.hwaci.com/drh/
  
  *****************************************************************************
   This file houses the code for the fsl_content_xxx() APIS.
*/
#include <assert.h>

#include "fossil-scm/fossil-internal.h"



/* Only for debugging */
#include <stdio.h>
#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)







<


>
>







14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
30
31
   Author contact information:
     drh@hwaci.com
     https://www.hwaci.com/drh/
  
  *****************************************************************************
   This file houses the code for the fsl_content_xxx() APIS.
*/


#include "fossil-scm/fossil-internal.h"
#include <assert.h>
#include <memory.h> /* memcmp() */

/* Only for debugging */
#include <stdio.h>
#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)
328
329
330
331
332
333
334

335
336
337
338
339
340
341
342
343
344





345












346
347
348
349
350

351
352
353
354
355
356
357
358
359
360
361
362
363
364




365
366
367
368
369
370
371
372
  fsl_stmt * s1 = NULL;
  fsl_buffer cmpr = fsl_buffer_empty;
  fsl_buffer hash = fsl_buffer_empty;
  char markAsUnclustered = 0;
  char markAsUnsent = 1;
  char isDephantomize = 0;
  fsl_db * dbR = fsl_cx_db_repo(f);

  int rc;
  char inTrans = 0;
  assert(f);
  assert(dbR);
  assert(pBlob);
  assert(srcId==0 || zUuid!=NULL);
  assert(!zUuid || fsl_is_uuid(zUuid));
  if(!dbR) return FSL_RC_NOT_A_REPO; 
  if(!zUuid){
    assert(0==uncompSize);





    rc = fsl_sha1sum_buffer(pBlob, &hash);












  }else{
    rc = fsl_buffer_append(&hash, zUuid, FSL_UUID_STRLEN);
  }
  if(rc) goto end;


  if(uncompSize){
    /* pBlob is assumed to be compressed. */
    assert(fsl_buffer_is_compressed(pBlob));
    size = uncompSize;
  }else{
    size = pBlob->used;
    if(srcId>0){
      rc = fsl_delta_applied_size(pBlob->mem, pBlob->used, &size);
      if(rc) goto end;
    }
  }
  rc = fsl_db_transaction_begin(dbR);
  if(rc) goto end;
  inTrans = 1;





  /* Check to see if the entry already exists and if it does whether
     or not the entry is a phantom.
  */
  rc = fsl_db_prepare_cached(dbR, &s1,
                             "SELECT rid, size FROM blob WHERE uuid=?");
  if(rc) goto end;
  rc = fsl_stmt_bind_text( s1, 1, fsl_buffer_cstr(&hash),







>
|





|



>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>

|
<
|
|
>














>
>
>
>
|







329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
  fsl_stmt * s1 = NULL;
  fsl_buffer cmpr = fsl_buffer_empty;
  fsl_buffer hash = fsl_buffer_empty;
  char markAsUnclustered = 0;
  char markAsUnsent = 1;
  char isDephantomize = 0;
  fsl_db * dbR = fsl_cx_db_repo(f);
  int const zUuidLen = zUuid ? fsl_is_uuid(zUuid) : 0;
  int rc = 0;
  char inTrans = 0;
  assert(f);
  assert(dbR);
  assert(pBlob);
  assert(srcId==0 || zUuid!=NULL);
  assert(!zUuid || zUuidLen);
  if(!dbR) return FSL_RC_NOT_A_REPO; 
  if(!zUuid){
    assert(0==uncompSize);
    /* "auxiliary hash" bits from:
       https://fossil-scm.org/fossil/file?ci=c965636958eb58aa&name=src%2Fcontent.c&ln=527-537
    */
    /* First check the auxiliary hash to see if there is already an artifact
    ** that uses the auxiliary hash name */
    rc = fsl_cx_hash_buffer(f, 1, pBlob, &hash);
    if(rc) goto end;
    assert(hash.used>=FSL_UUIDv1_STRLEN);
    rid = fsl_uuid_to_rid(f, fsl_buffer_cstr(&hash));
    assert(rid>=0 && "Cannot have malformed/ambiguous UUID at this point.");
    if(!rid){
      /* No existing artifact with the auxiliary hash name.  Therefore, use
      ** the primary hash name. */
      hash.used = 0;
      rc = fsl_cx_hash_buffer(f, 0, pBlob, &hash);
      if(rc) goto end;
      assert(hash.used>=FSL_UUIDv1_STRLEN);
    }
  }else{
    rc = fsl_buffer_append(&hash, zUuid, zUuidLen);

    if(rc) goto end;
  }
  assert(!rc);
  if(uncompSize){
    /* pBlob is assumed to be compressed. */
    assert(fsl_buffer_is_compressed(pBlob));
    size = uncompSize;
  }else{
    size = pBlob->used;
    if(srcId>0){
      rc = fsl_delta_applied_size(pBlob->mem, pBlob->used, &size);
      if(rc) goto end;
    }
  }
  rc = fsl_db_transaction_begin(dbR);
  if(rc) goto end;
  inTrans = 1;
  if( f->cache.hashPolicy==FSL_HPOLICY_AUTO && hash.used>FSL_UUIDv1_STRLEN ){
    f->cache.hashPolicy = FSL_HPOLICY_SHA3;
    rc = fsl_config_set_int32(f, FSL_CONFDB_REPO, "hash-policy", FSL_HPOLICY_SHA3);
    if(rc) goto end;
  }
  /* Check to see if the entry already exists and if it does whether
     or not the entry is a phantom.
  */
  rc = fsl_db_prepare_cached(dbR, &s1,
                             "SELECT rid, size FROM blob WHERE uuid=?");
  if(rc) goto end;
  rc = fsl_stmt_bind_text( s1, 1, fsl_buffer_cstr(&hash),
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
      TODO: figure out what that is.
    */
    rc = fsl_content_mark_available(f, rid);
    if(rc) goto end;
  }
  if( isDephantomize ){
#if 0
    /* MISSING */
    after_dephantomize(rid, 0);
#else
    assert(!"Missing code: after_dephantomize()");
#endif
  }

  /* Add the element to the unclustered table if has never been







|







552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
      TODO: figure out what that is.
    */
    rc = fsl_content_mark_available(f, rid);
    if(rc) goto end;
  }
  if( isDephantomize ){
#if 0
    /* FIXME?: MISSING */
    after_dephantomize(rid, 0);
#else
    assert(!"Missing code: after_dephantomize()");
#endif
  }

  /* Add the element to the unclustered table if has never been
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
                     "VALUES(%"FSL_ID_T_PFMT")", (fsl_id_t)rid);
    if(rc) goto end;
  }
  
  rc = fsl_repo_verify_before_commit(f, rid);
  if(rc) goto end /* FSL_RC_OOM is basically the "only possible" failure
                     after this point. */;
  
  /* Code after end: relies on the following 2 lines: */
  rc = fsl_db_transaction_end(dbR, 0);
  inTrans = 0;
  if(!rc){
    if(outRid) *outRid = rid;
  }
  end:
  if(inTrans){
    assert(0!=rc);
    fsl_db_transaction_end(dbR,1);
  }
  fsl_buffer_clear(&hash);
  if(!uncompSize){
    fsl_buffer_clear(&cmpr);
  }/* else cmpr.mem (if any) belongs to pBlob */
  return rc;
}


char fsl_acache_expire_oldest(fsl_acache * c){
  fsl_int_t i;
  fsl_int_t mnAge = c->nextAge;
  fsl_int_t mn = -1;
  for(i=0; i<(fsl_int_t)c->used; i++){
    if( c->list[i].age<mnAge ){







<

















<







580
581
582
583
584
585
586

587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603

604
605
606
607
608
609
610
                     "VALUES(%"FSL_ID_T_PFMT")", (fsl_id_t)rid);
    if(rc) goto end;
  }
  
  rc = fsl_repo_verify_before_commit(f, rid);
  if(rc) goto end /* FSL_RC_OOM is basically the "only possible" failure
                     after this point. */;

  /* Code after end: relies on the following 2 lines: */
  rc = fsl_db_transaction_end(dbR, 0);
  inTrans = 0;
  if(!rc){
    if(outRid) *outRid = rid;
  }
  end:
  if(inTrans){
    assert(0!=rc);
    fsl_db_transaction_end(dbR,1);
  }
  fsl_buffer_clear(&hash);
  if(!uncompSize){
    fsl_buffer_clear(&cmpr);
  }/* else cmpr.mem (if any) belongs to pBlob */
  return rc;
}


char fsl_acache_expire_oldest(fsl_acache * c){
  fsl_int_t i;
  fsl_int_t mnAge = c->nextAge;
  fsl_int_t mn = -1;
  for(i=0; i<(fsl_int_t)c->used; i++){
    if( c->list[i].age<mnAge ){
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732

733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
  }
  assert(!"delta-loop in repository");
  return fsl_cx_err_set(f, FSL_RC_CONSISTENCY,
                        "Serious problem: delta-loop in repository");
}

int fsl_content_put( fsl_cx * f, fsl_buffer const * pBlob, fsl_id_t * newRid){
#if 1
  return fsl_content_put_ex(f, pBlob, NULL, 0, 0, 0, newRid);
#else
  /*
    EXPERIMENT: if pBlob appears to be compressed, pass the proper
    uncompressed size value (and required UUID) to put_ex().

    Aaarrggg - there's the catch. We cannot know the UUID without
    decompressing the data.
  */
  fsl_int_t const ucSize = fsl_buffer_is_compressed(pBlob)
    ? fsl_buffer_uncompressed_size(pBlob)
    : 0;
  if(ucSize < 0) return FSL_RC_RANGE;
  else{
    fsl_buffer uuid = fsl_buffer_empty;
    int rc = fsl_sha1sum_buffer(pBlob, &uuid);
    if(!rc){
      rc = fsl_content_put_ex(f, pBlob, fsl_buffer_cstr(&uuid), 0,
                              (fsl_size_t)ucSize, 0, newRid);
    }
    fsl_buffer_clear(&uuid);
    return rc;
  }
#endif
}

char fsl_uuid_is_shunned(fsl_cx * f, fsl_uuid_cstr zUuid){
  fsl_int32_t i = 0;
  fsl_db * db = fsl_cx_db_repo(f);
  if( !db || zUuid==0 || zUuid[0]==0 ) return 0;
  i = fsl_db_g_int32( db, 0,
                      "SELECT 1 FROM shun WHERE uuid=%Q",
                      zUuid);
  return 1==i;
}


int fsl_content_new( fsl_cx * f, fsl_uuid_cstr uuid, char isPrivate,
                     fsl_id_t * newId ){
  fsl_id_t rid = 0;
  int rc;
  fsl_db * db = fsl_cx_db_repo(f);
  fsl_stmt * s1 = NULL, * s2 = NULL;

  if(!f || !uuid) return FSL_RC_MISUSE;
  else if(!fsl_is_uuid(uuid)) return FSL_RC_RANGE;
  if(!db) return FSL_RC_NOT_A_REPO;
  if( fsl_uuid_is_shunned(f, uuid) ){
    return fsl_cx_err_set(f, FSL_RC_ACCESS,
                          "UUID is shunned: %s", uuid)
      /* need new error code? */;
  }
  rc = fsl_db_transaction_begin(db);
  if(rc) return rc;

  rc = fsl_db_prepare_cached(db, &s1,
                             "INSERT INTO blob(rcvid,size,uuid,content)"
                             "VALUES(0,-1,?,NULL)");
  if(rc) goto end;
  rc = fsl_stmt_bind_text(s1, 1, uuid, FSL_UUID_STRLEN, 0);
  if(!rc) rc = fsl_stmt_step(s1);
  fsl_stmt_cached_yield(s1);
  if(FSL_RC_STEP_DONE!=rc) goto end;
  else rc = 0;
  rid = fsl_db_last_insert_id(db);
  assert(rid>0);
  rc = fsl_db_prepare_cached(db, &s2,







<

<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
|
















>

|













|







703
704
705
706
707
708
709

710




711













712






713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
  }
  assert(!"delta-loop in repository");
  return fsl_cx_err_set(f, FSL_RC_CONSISTENCY,
                        "Serious problem: delta-loop in repository");
}

int fsl_content_put( fsl_cx * f, fsl_buffer const * pBlob, fsl_id_t * newRid){

  return fsl_content_put_ex(f, pBlob, NULL, 0, 0, 0, newRid);




}




















int fsl_uuid_is_shunned(fsl_cx * f, fsl_uuid_cstr zUuid){
  fsl_int32_t i = 0;
  fsl_db * db = fsl_cx_db_repo(f);
  if( !db || zUuid==0 || zUuid[0]==0 ) return 0;
  i = fsl_db_g_int32( db, 0,
                      "SELECT 1 FROM shun WHERE uuid=%Q",
                      zUuid);
  return 1==i;
}


int fsl_content_new( fsl_cx * f, fsl_uuid_cstr uuid, char isPrivate,
                     fsl_id_t * newId ){
  fsl_id_t rid = 0;
  int rc;
  fsl_db * db = fsl_cx_db_repo(f);
  fsl_stmt * s1 = NULL, * s2 = NULL;
  int const uuidLen = uuid ? fsl_is_uuid(uuid) : 0;
  if(!f || !uuid) return FSL_RC_MISUSE;
  else if(!uuidLen) return FSL_RC_RANGE;
  if(!db) return FSL_RC_NOT_A_REPO;
  if( fsl_uuid_is_shunned(f, uuid) ){
    return fsl_cx_err_set(f, FSL_RC_ACCESS,
                          "UUID is shunned: %s", uuid)
      /* need new error code? */;
  }
  rc = fsl_db_transaction_begin(db);
  if(rc) return rc;

  rc = fsl_db_prepare_cached(db, &s1,
                             "INSERT INTO blob(rcvid,size,uuid,content)"
                             "VALUES(0,-1,?,NULL)");
  if(rc) goto end;
  rc = fsl_stmt_bind_text(s1, 1, uuid, uuidLen, 0);
  if(!rc) rc = fsl_stmt_step(s1);
  fsl_stmt_cached_yield(s1);
  if(FSL_RC_STEP_DONE!=rc) goto end;
  else rc = 0;
  rid = fsl_db_last_insert_id(db);
  assert(rid>0);
  rc = fsl_db_prepare_cached(db, &s2,
1306
1307
1308
1309
1310
1311
1312







1313











































1314
  assert(0==fbuf->used);
  fsl_buffer_reset(nbuf);
  fsl_buffer_reset(canon);
  if(rc && inTrans) fsl_db_transaction_rollback(db);
  return rc;
}




















































#undef MARKER







>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
  assert(0==fbuf->used);
  fsl_buffer_reset(nbuf);
  fsl_buffer_reset(canon);
  if(rc && inTrans) fsl_db_transaction_rollback(db);
  return rc;
}

fsl_hash_types_t fsl_validate_hash(const char *zHash, int nHash){
  /* fossil(1) counterpart: hname_validate() */
  fsl_hash_types_t rc;
  switch(nHash){
    case FSL_UUIDv1_STRLEN: rc = FSL_HTYPE_SHA1; break;
    case FSL_UUIDv2_STRLEN: rc = FSL_HTYPE_K256; break;
    default: return FSL_HTYPE_ERROR;
  }
  return fsl_validate16(zHash, (fsl_size_t)nHash) ? rc : FSL_HTYPE_ERROR;
}

const char * fsl_hash_type_name(fsl_hash_types_t h, const char *zUnknown){
  /* fossil(1) counterpart: hname_alg() */
  switch(h){
    case FSL_HTYPE_SHA1: return "SHA1";
    case FSL_HTYPE_K256: return "SHA3-256";
    default: return zUnknown;
  }
}

fsl_hash_types_t fsl_verify_blob_hash(fsl_buffer const * pIn,
                                      const char *zHash, int nHash){
  fsl_hash_types_t id = FSL_HTYPE_ERROR;
  switch(nHash){
    case FSL_UUIDv1_STRLEN:{
      fsl_sha1_cx cx;
      char hex[FSL_UUIDv1_STRLEN+1] = {0};
      fsl_sha1_init(&cx);
      fsl_sha1_update(&cx, pIn->mem, (unsigned)pIn->used);
      fsl_sha1_final_hex(&cx, hex);
      if(0==memcmp(hex, zHash, FSL_UUIDv1_STRLEN)){
        id = FSL_HTYPE_SHA1;
      }
      break;
    }
    case FSL_UUIDv2_STRLEN:{
      fsl_sha3_cx cx;
      unsigned char const * hex;
      fsl_sha3_init(&cx);
      fsl_sha3_update(&cx, pIn->mem, (unsigned)pIn->used);
      hex = fsl_sha3_end(&cx);
      if(0==memcmp(hex, zHash, FSL_UUIDv2_STRLEN)){
        id = FSL_HTYPE_K256;
      }
      break;
    }
    default:
      break;
  }
  return id;
}
#undef MARKER
Changes to src/fsl_cx.c.
952
953
954
955
956
957
958

959
960
961
962
963






964
965
966
967
968
969
970
      rc = fsl_cx_db_main_open_or_attach(f, repoDbFile,
                                         FSL_DB_ROLE_REPO,
                                         NULL);
      if(!rc && !(FSL_CX_F_IS_OPENING_CKOUT & f->flags)){
        rc = fsl_cx_after_open(f);
      }
      if(!rc){

        fsl_cx_username_from_repo(f);
        f->cache.allowSymlinks =
          fsl_config_get_bool(f, FSL_CONFDB_REPO,
                              f->cache.allowSymlinks,
                              "allow-symlinks");






      }
    }
    return rc;
  }
}

/**







>





>
>
>
>
>
>







952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
      rc = fsl_cx_db_main_open_or_attach(f, repoDbFile,
                                         FSL_DB_ROLE_REPO,
                                         NULL);
      if(!rc && !(FSL_CX_F_IS_OPENING_CKOUT & f->flags)){
        rc = fsl_cx_after_open(f);
      }
      if(!rc){
        fsl_db * const db = fsl_cx_db_repo(f);
        fsl_cx_username_from_repo(f);
        f->cache.allowSymlinks =
          fsl_config_get_bool(f, FSL_CONFDB_REPO,
                              f->cache.allowSymlinks,
                              "allow-symlinks");
        if(fsl_db_exists(db, "SELECT 1 FROM blob WHERE length(uuid)>40")
           || !fsl_db_exists(db, "SELECT 1 FROM blob WHERE length(uuid)==40")){
          f->cache.hashPolicy = FSL_HPOLICY_SHA3;
        }else{
          f->cache.hashPolicy = FSL_HPOLICY_AUTO;
        }
      }
    }
    return rc;
  }
}

/**
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
      assert(f->error.code);
      if(!f->error.code){
        rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND,
                            "Could not load UUID for RID %"FSL_ID_T_PFMT,
                            (fsl_id_t)rid);
      }
    }else{
      assert(FSL_UUID_STRLEN==fsl_strlen(f->ckout.uuid));
      rc = 0;
    }
    f->ckout.rid = rid;
  }else if(rid==0){
    /* This is a legal case not possible before libfossil (and only
       afterwards possible in fossil(1)) - an empty repo without an
       active checkin.







|







1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
      assert(f->error.code);
      if(!f->error.code){
        rc = fsl_cx_err_set(f, FSL_RC_NOT_FOUND,
                            "Could not load UUID for RID %"FSL_ID_T_PFMT,
                            (fsl_id_t)rid);
      }
    }else{
      assert(fsl_is_uuid(f->ckout.uuid));
      rc = 0;
    }
    f->ckout.rid = rid;
  }else if(rid==0){
    /* This is a legal case not possible before libfossil (and only
       afterwards possible in fossil(1)) - an empty repo without an
       active checkin.
1537
1538
1539
1540
1541
1542
1543
















































1544
1545
1546
1547
1548
1549
1550
}

int fsl_output_f_fsl_cx(void * state, void const * src, fsl_size_t n ){
  return (state && src && n)
    ? fsl_output((fsl_cx*)state, src, n)
    : (n ? FSL_RC_MISUSE : 0);
}

















































#if 0
struct tm * fsl_cx_localtime( fsl_cx const * f, const time_t * clock ){
  if(!clock) return NULL;
  else if(!f) return localtime(clock);
  else return (f->flags & FSL_CX_F_LOCALTIME_GMT)
         ? gmtime(clock)







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
}

int fsl_output_f_fsl_cx(void * state, void const * src, fsl_size_t n ){
  return (state && src && n)
    ? fsl_output((fsl_cx*)state, src, n)
    : (n ? FSL_RC_MISUSE : 0);
}

int fsl_cx_hash_buffer( const fsl_cx * f, int useAlternate,
                        fsl_buffer const * pIn, fsl_buffer * pOut){
  /* fossil(1) counterpart: hname_hash() */
  if(useAlternate){
    switch(f->cache.hashPolicy){
      case FSL_HPOLICY_AUTO:
      case FSL_HPOLICY_SHA1:
        return fsl_sha3sum_buffer(pIn, pOut);
      case FSL_HPOLICY_SHA3:
        return fsl_sha1sum_buffer(pIn, pOut);
      default: return FSL_RC_RANGE;
    }
  }else{
    switch(f->cache.hashPolicy){
      case FSL_HPOLICY_SHA1:
      case FSL_HPOLICY_AUTO:
        return fsl_sha1sum_buffer(pIn, pOut);
      case FSL_HPOLICY_SHA3:
      case FSL_HPOLICY_SHA3_ONLY:
      case FSL_HPOLICY_SHUN_SHA1:
        return fsl_sha3sum_buffer(pIn, pOut);
    }
  }
  assert(!"not reached");
  return FSL_RC_RANGE;
}

fsl_hash_policy_t fsl_cx_hash_policy_set(fsl_cx *f, fsl_hash_policy_t p){
  fsl_hash_policy_t const old = f->cache.hashPolicy;
  f->cache.hashPolicy = p;
  return old;
}
fsl_hash_policy_t fsl_cx_hash_policy_get(fsl_cx const*f){
  return f->cache.hashPolicy;
}

char const * fsl_cx_hash_policy_name(fsl_cx const*f){
  switch(f->cache.hashPolicy){
    case FSL_HPOLICY_SHUN_SHA1: return "shun-sha1";
    case FSL_HPOLICY_SHA3: return "sha3";
    case FSL_HPOLICY_SHA3_ONLY: return "sha3-only";
    case FSL_HPOLICY_SHA1: return "sha1";
    case FSL_HPOLICY_AUTO: return "auto";
    default: return "unknown";
  }
}


#if 0
struct tm * fsl_cx_localtime( fsl_cx const * f, const time_t * clock ){
  if(!clock) return NULL;
  else if(!f) return localtime(clock);
  else return (f->flags & FSL_CX_F_LOCALTIME_GMT)
         ? gmtime(clock)
Changes to src/fsl_md5.c.
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
      fsl_size_t read = (fsl_size_t)BufSize;
      rc = src(srcState, zBuf, &read);
      if(rc) return rc;
      else if(read) fsl_md5_update(&ctx, (unsigned char*)zBuf, read);
      if(read < (fsl_size_t)BufSize) break;
    }
    fsl_buffer_reset(pCksum);
    rc = fsl_buffer_resize(pCksum, FSL_UUID_STRLEN);
    if(!rc){
      fsl_md5_final(&ctx, zResult);
      fsl_md5_digest_to_base16(zResult, fsl_buffer_str(pCksum));
    }
    return rc;
}








|







342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
      fsl_size_t read = (fsl_size_t)BufSize;
      rc = src(srcState, zBuf, &read);
      if(rc) return rc;
      else if(read) fsl_md5_update(&ctx, (unsigned char*)zBuf, read);
      if(read < (fsl_size_t)BufSize) break;
    }
    fsl_buffer_reset(pCksum);
    rc = fsl_buffer_resize(pCksum, FSL_MD5_STRLEN);
    if(!rc){
      fsl_md5_final(&ctx, zResult);
      fsl_md5_digest_to_base16(zResult, fsl_buffer_str(pCksum));
    }
    return rc;
}

Changes to src/fsl_mf.c.
72
73
74
75
76
77
78


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
    fsl_free(t);
  }
}

fsl_card_Q * fsl_card_Q_malloc(int type,
                               fsl_uuid_cstr target,
                               fsl_uuid_cstr baseline){


  if(!type || !target || !fsl_is_uuid(target)
     || (baseline && !fsl_is_uuid(baseline))) return NULL;
  else{
    fsl_card_Q * c =
      (fsl_card_Q*)fsl_malloc(sizeof(fsl_card_Q));
    if(c){
      int rc = 0;
      *c = fsl_card_Q_empty;
      c->type = type;
      c->target = fsl_strndup(target, FSL_UUID_STRLEN);
      if(!c->target) rc = FSL_RC_OOM;
      else if(baseline){
        c->baseline = fsl_strndup( baseline, FSL_UUID_STRLEN);
        if(!c->baseline) rc = FSL_RC_OOM;
      }
      if(rc){
        fsl_card_Q_free(c);
        c = NULL;
      }
    }







>
>
|
|







|


|







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    fsl_free(t);
  }
}

fsl_card_Q * fsl_card_Q_malloc(int type,
                               fsl_uuid_cstr target,
                               fsl_uuid_cstr baseline){
  int const targetLen = target ? fsl_is_uuid(target) : 0;
  int const baselineLen = baseline ? fsl_is_uuid(baseline) : 0;
  if(!type || !target || !targetLen
     || (baseline && !baselineLen)) return NULL;
  else{
    fsl_card_Q * c =
      (fsl_card_Q*)fsl_malloc(sizeof(fsl_card_Q));
    if(c){
      int rc = 0;
      *c = fsl_card_Q_empty;
      c->type = type;
      c->target = fsl_strndup(target, targetLen);
      if(!c->target) rc = FSL_RC_OOM;
      else if(baseline){
        c->baseline = fsl_strndup(baseline, baselineLen);
        if(!c->baseline) rc = FSL_RC_OOM;
      }
      if(rc){
        fsl_card_Q_free(c);
        c = NULL;
      }
    }
147
148
149
150
151
152
153

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
}

fsl_card_F * fsl_card_F_malloc(char const * name,
                               char const * uuid,
                               fsl_file_perm_t perm,
                               char const * oldName){
  fsl_card_F * t;

  if(!name || !*name) return NULL;
  else if(uuid && !fsl_is_uuid(uuid)) return NULL;
  t = (fsl_card_F *)fsl_malloc(sizeof(fsl_card_F));
  if(t){
    int rc = 0;
    *t = fsl_card_F_empty;
    t->perm = perm;
    t->name = fsl_strdup(name);
    if(!t->name) rc = FSL_RC_OOM;
    if(!rc && uuid){
      t->uuid = fsl_strdup(uuid);
      if(!t->uuid) rc = FSL_RC_OOM;
    }
    if(!rc && oldName){
      t->priorName = fsl_strdup(oldName);
      if(!t->priorName) rc = FSL_RC_OOM;
    }
    if(rc){







>

|







|
|







149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
}

fsl_card_F * fsl_card_F_malloc(char const * name,
                               char const * uuid,
                               fsl_file_perm_t perm,
                               char const * oldName){
  fsl_card_F * t;
  int const uLen = uuid ? fsl_is_uuid(uuid) : 0;
  if(!name || !*name) return NULL;
  else if(uuid && !uLen) return NULL;
  t = (fsl_card_F *)fsl_malloc(sizeof(fsl_card_F));
  if(t){
    int rc = 0;
    *t = fsl_card_F_empty;
    t->perm = perm;
    t->name = fsl_strdup(name);
    if(!t->name) rc = FSL_RC_OOM;
    if(!rc && uLen){
      t->uuid = fsl_strndup(uuid, uLen);
      if(!t->uuid) rc = FSL_RC_OOM;
    }
    if(!rc && oldName){
      t->priorName = fsl_strdup(oldName);
      if(!t->priorName) rc = FSL_RC_OOM;
    }
    if(rc){
529
530
531
532
533
534
535
536
537
538
539
540


541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
  
  The function returns FSL_RC_RANGE if (valLen!=ASSERTLEN). ASSERTLEN
  is assumed to be either an SHA1 or MD5 hash value and it is
  validated against fsl_validate16(value,valLen), returning
  FSL_RC_CA_SYNTAX if that check fails.
 */
static int fsl_deck_sethex_impl( fsl_deck * mf, fsl_uuid_cstr value,
                                    char letter,
                                    fsl_size_t assertLen,
                                    char ** mfMember ){
  assert(mf);
  assert( assertLen==FSL_UUID_STRLEN || assertLen==FSL_MD5_STRLEN );


  if(!fsl_deck_check_type(mf,letter)) return FSL_RC_TYPE;
  else if(!value){
    fsl_deck_free_string(mf, *mfMember);
    *mfMember = NULL;
    return 0;
  }else if(fsl_strlen(value) != (assertLen)){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "Invalid length for %c-card: expecting %d.",
                         letter, (int)assertLen);
  }else if(!fsl_validate16(value, assertLen)) {
    return fsl_error_set(&mf->error, FSL_RC_CA_SYNTAX,
                         "Invalid hexadecimal value for %c-card.", letter);
  }else{
    fsl_deck_free_string(mf, *mfMember);
    *mfMember = fsl_strndup( value, assertLen );
    return *mfMember ? 0 : FSL_RC_OOM;
  }
}

/**
    Implements fsl_set_set_XXX() where XXX is a fsl_buffer member of fsl_deck.
 */







|
|
|

|
>
>





|








|







532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  
  The function returns FSL_RC_RANGE if (valLen!=ASSERTLEN). ASSERTLEN
  is assumed to be either an SHA1 or MD5 hash value and it is
  validated against fsl_validate16(value,valLen), returning
  FSL_RC_CA_SYNTAX if that check fails.
 */
static int fsl_deck_sethex_impl( fsl_deck * mf, fsl_uuid_cstr value,
                                 char letter,
                                 fsl_size_t assertLen,
                                 char ** mfMember ){
  assert(mf);
  assert( assertLen==FSL_UUIDv1_STRLEN
          || assertLen==FSL_UUIDv2_STRLEN
          || assertLen==FSL_MD5_STRLEN );
  if(!fsl_deck_check_type(mf,letter)) return FSL_RC_TYPE;
  else if(!value){
    fsl_deck_free_string(mf, *mfMember);
    *mfMember = NULL;
    return 0;
  }else if(fsl_strlen(value) != assertLen){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "Invalid length for %c-card: expecting %d.",
                         letter, (int)assertLen);
  }else if(!fsl_validate16(value, assertLen)) {
    return fsl_error_set(&mf->error, FSL_RC_CA_SYNTAX,
                         "Invalid hexadecimal value for %c-card.", letter);
  }else{
    fsl_deck_free_string(mf, *mfMember);
    *mfMember = fsl_strndup(value, assertLen);
    return *mfMember ? 0 : FSL_RC_OOM;
  }
}

/**
    Implements fsl_set_set_XXX() where XXX is a fsl_buffer member of fsl_deck.
 */
574
575
576
577
578
579
580





581
582
583
584
585
586
587
588
589
590
591
592
593
    return 0;
  }
}

int fsl_deck_B_set( fsl_deck * mf, fsl_uuid_cstr uuidBaseline){
  if(!mf) return FSL_RC_MISUSE;
  else{





    if(mf->B.baseline){
      fsl_deck_finalize(mf->B.baseline);
      mf->B.baseline = NULL;
    }
    return fsl_deck_sethex_impl(mf, uuidBaseline, 'B',
                                FSL_UUID_STRLEN, &mf->B.uuid);
  }
}

/**
    Internal impl for card setters which consider of a simple (char *)
    member.
 */







>
>
>
>
>





|







579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
    return 0;
  }
}

int fsl_deck_B_set( fsl_deck * mf, fsl_uuid_cstr uuidBaseline){
  if(!mf) return FSL_RC_MISUSE;
  else{
    int const bLen = fsl_is_uuid(uuidBaseline);
    if(!bLen){
      return fsl_error_set(&mf->error, FSL_RC_CA_SYNTAX,
                           "Invalid B-card value: %s", uuidBaseline);
    }
    if(mf->B.baseline){
      fsl_deck_finalize(mf->B.baseline);
      mf->B.baseline = NULL;
    }
    return fsl_deck_sethex_impl(mf, uuidBaseline, 'B',
                                bLen, &mf->B.uuid);
  }
}

/**
    Internal impl for card setters which consider of a simple (char *)
    member.
 */
619
620
621
622
623
624
625

626
627
628
629
630
631
632
633
634
635
636
637
638
639

640
641
642
643
644
645
646
647
648
649
650
    }
    return rc;
  }
}


int fsl_deck_K_set( fsl_deck * mf, fsl_uuid_cstr uuid){

  return mf
    ? fsl_deck_sethex_impl(mf, uuid, 'K', FSL_UUID_STRLEN, &mf->K)
    : FSL_RC_MISUSE;
}

int fsl_deck_L_set( fsl_deck * mf, char const * v, fsl_int_t n){
  return mf
    ? fsl_deck_set_string(mf, 'L', &mf->L, v, n)
    : FSL_RC_MISUSE;
}

int fsl_deck_M_add( fsl_deck * mf, char const *uuid){
  int rc;
  char * dupe;

  if(!mf || !uuid) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'M')) return FSL_RC_TYPE;
  else if(!fsl_is_uuid(uuid)) return FSL_RC_RANGE;
  dupe = fsl_strndup(uuid, FSL_UUID_STRLEN);
  if(!dupe) rc = FSL_RC_OOM;
  else{
    rc = fsl_list_append( &mf->M, dupe );
    if(rc){
      fsl_free(dupe);
    }
  }







>
|
|












>


|
|







629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
    }
    return rc;
  }
}


int fsl_deck_K_set( fsl_deck * mf, fsl_uuid_cstr uuid){
  int const uLen = fsl_is_uuid(uuid);
  return (mf && uLen)
    ? fsl_deck_sethex_impl(mf, uuid, 'K', uLen, &mf->K)
    : FSL_RC_MISUSE;
}

int fsl_deck_L_set( fsl_deck * mf, char const * v, fsl_int_t n){
  return mf
    ? fsl_deck_set_string(mf, 'L', &mf->L, v, n)
    : FSL_RC_MISUSE;
}

int fsl_deck_M_add( fsl_deck * mf, char const *uuid){
  int rc;
  char * dupe;
  int const uLen = uuid ? fsl_is_uuid(uuid) : 0;
  if(!mf || !uuid) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'M')) return FSL_RC_TYPE;
  else if(!uLen) return FSL_RC_RANGE;
  dupe = fsl_strndup(uuid, uLen);
  if(!dupe) rc = FSL_RC_OOM;
  else{
    rc = fsl_list_append( &mf->M, dupe );
    if(rc){
      fsl_free(dupe);
    }
  }
658
659
660
661
662
663
664

665
666
667
668
669
670
671
672
673
674
675
676
677
678
    : FSL_RC_MISUSE;
}


int fsl_deck_P_add( fsl_deck * mf, char const *parentUuid){
  int rc;
  char * dupe;

  if(!mf || !parentUuid || !*parentUuid) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'P')) return FSL_RC_TYPE;
  else if(!fsl_is_uuid(parentUuid)){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "Invalid UUID for P-card.");
  }
  dupe = fsl_strndup(parentUuid, FSL_UUID_STRLEN);
  if(!dupe) rc = FSL_RC_OOM;
  else{
    rc = fsl_list_append( &mf->P, dupe );
    if(rc){
      fsl_free(dupe);
    }
  }







>


|



|







670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
    : FSL_RC_MISUSE;
}


int fsl_deck_P_add( fsl_deck * mf, char const *parentUuid){
  int rc;
  char * dupe;
  int const uLen = parentUuid ? fsl_is_uuid(parentUuid) : 0;
  if(!mf || !parentUuid || !*parentUuid) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'P')) return FSL_RC_TYPE;
  else if(!uLen){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "Invalid UUID for P-card.");
  }
  dupe = fsl_strndup(parentUuid, uLen);
  if(!dupe) rc = FSL_RC_OOM;
  else{
    rc = fsl_list_append( &mf->P, dupe );
    if(rc){
      fsl_free(dupe);
    }
  }
866
867
868
869
870
871
872

873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914

915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
    ? fsl_deck_b_setuffer_impl(mf, v, n, 'W', &mf->W)
    : FSL_RC_MISUSE;
}

int fsl_deck_A_set( fsl_deck * mf, char const * name,
                    char const * tgt,
                    char const * uuidSrc ){

  if(!mf || !name || !tgt) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'A')) return FSL_RC_TYPE;
  else if(!*tgt){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                       "Invalid target name in A card.");
  }
  /* TODO: validate tgt based on mf->type and require UUID
     for types EVENT/TICKET.
  */
  else if(uuidSrc && *uuidSrc && !fsl_is_uuid(uuidSrc)){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                       "Invalid source UUID in A card.");
  }
  else{
    int rc = 0;
    fsl_deck_free_string(mf, mf->A.tgt);
    fsl_deck_free_string(mf, mf->A.src);
    fsl_deck_free_string(mf, mf->A.name);
    mf->A.name = mf->A.src = NULL;
    if(! (mf->A.tgt = fsl_strdup(tgt))) rc = FSL_RC_OOM;
    else if( !(mf->A.name = fsl_strdup(name))) rc = FSL_RC_OOM;
    else if(uuidSrc && *uuidSrc){
      mf->A.src = fsl_strndup(uuidSrc,FSL_UUID_STRLEN);
      if(!mf->A.src) rc = FSL_RC_OOM
        /* Leave mf->A.tgt/name for downstream cleanup. */;
    }
    return rc;
  }
}


int fsl_deck_D_set( fsl_deck * mf, fsl_double_t date){
  if(!mf) return FSL_RC_MISUSE;
  else if(date<=0) return FSL_RC_RANGE;
  else if(!fsl_deck_check_type(mf, 'D')) return FSL_RC_TYPE;
  else{
    mf->D = date;
    return 0;
  }
}

int fsl_deck_E_set( fsl_deck * mf, fsl_double_t date, char const * uuid){

  if(!mf || !uuid) return FSL_RC_MISUSE;
  else if(date<=0){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                       "Invalid date value for E card.");
  }else if(!fsl_is_uuid(uuid)){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                       "Invalid UUID for E card.");
  }
  else{
    mf->E.julian = date;
    fsl_deck_free_string(mf, mf->E.uuid);
    mf->E.uuid = fsl_strndup(uuid, FSL_UUID_STRLEN);
    return mf->E.uuid ? 0 : FSL_RC_OOM;
  }
}

int fsl_deck_F_add2( fsl_deck * mf, fsl_card_F * t){
  if(!mf || !t) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'F')) return FSL_RC_TYPE;







>









|











|
|



















>
|



|






|







879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
    ? fsl_deck_b_setuffer_impl(mf, v, n, 'W', &mf->W)
    : FSL_RC_MISUSE;
}

int fsl_deck_A_set( fsl_deck * mf, char const * name,
                    char const * tgt,
                    char const * uuidSrc ){
  int const uLen = (uuidSrc && *uuidSrc) ? fsl_is_uuid(uuidSrc) : 0;
  if(!mf || !name || !tgt) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'A')) return FSL_RC_TYPE;
  else if(!*tgt){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                       "Invalid target name in A card.");
  }
  /* TODO: validate tgt based on mf->type and require UUID
     for types EVENT/TICKET.
  */
  else if(uuidSrc && *uuidSrc && !uLen){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                       "Invalid source UUID in A card.");
  }
  else{
    int rc = 0;
    fsl_deck_free_string(mf, mf->A.tgt);
    fsl_deck_free_string(mf, mf->A.src);
    fsl_deck_free_string(mf, mf->A.name);
    mf->A.name = mf->A.src = NULL;
    if(! (mf->A.tgt = fsl_strdup(tgt))) rc = FSL_RC_OOM;
    else if( !(mf->A.name = fsl_strdup(name))) rc = FSL_RC_OOM;
    else if(uLen){
      mf->A.src = fsl_strndup(uuidSrc,uLen);
      if(!mf->A.src) rc = FSL_RC_OOM
        /* Leave mf->A.tgt/name for downstream cleanup. */;
    }
    return rc;
  }
}


int fsl_deck_D_set( fsl_deck * mf, fsl_double_t date){
  if(!mf) return FSL_RC_MISUSE;
  else if(date<=0) return FSL_RC_RANGE;
  else if(!fsl_deck_check_type(mf, 'D')) return FSL_RC_TYPE;
  else{
    mf->D = date;
    return 0;
  }
}

int fsl_deck_E_set( fsl_deck * mf, fsl_double_t date, char const * uuid){
  int const uLen = uuid ? fsl_is_uuid(uuid) : 0;
  if(!mf || !uLen) return FSL_RC_MISUSE;
  else if(date<=0){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                       "Invalid date value for E card.");
  }else if(!uLen){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                       "Invalid UUID for E card.");
  }
  else{
    mf->E.julian = date;
    fsl_deck_free_string(mf, mf->E.uuid);
    mf->E.uuid = fsl_strndup(uuid, uLen);
    return mf->E.uuid ? 0 : FSL_RC_OOM;
  }
}

int fsl_deck_F_add2( fsl_deck * mf, fsl_card_F * t){
  if(!mf || !t) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'F')) return FSL_RC_TYPE;
946
947
948
949
950
951
952

953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
  }
}

int fsl_deck_F_add( fsl_deck * mf, char const * name,
                    char const * uuid,
                    fsl_file_perm_t perms, 
                    char const * oldName){

  if(!mf || !name) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'F')) return FSL_RC_TYPE;
  else if(!*name){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "F-card name may not be empty.");
  }
  else if(!fsl_is_simple_pathname(name, 1)
          || (oldName && !fsl_is_simple_pathname(oldName, 1))){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "Invalid filename for F-card (simple form required): "
                         "name=[%s], oldName=[%s].", name, oldName);
  }
  else if(uuid && !fsl_is_uuid(uuid)){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "Invalid UUID for F-card.");
  }
  else {
    int rc;
    fsl_card_F * t;
    switch(perms){







>












|







961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
  }
}

int fsl_deck_F_add( fsl_deck * mf, char const * name,
                    char const * uuid,
                    fsl_file_perm_t perms, 
                    char const * oldName){
  int const uLen = uuid ? fsl_is_uuid(uuid) : 0;
  if(!mf || !name) return FSL_RC_MISUSE;
  else if(!fsl_deck_check_type(mf, 'F')) return FSL_RC_TYPE;
  else if(!*name){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "F-card name may not be empty.");
  }
  else if(!fsl_is_simple_pathname(name, 1)
          || (oldName && !fsl_is_simple_pathname(oldName, 1))){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "Invalid filename for F-card (simple form required): "
                         "name=[%s], oldName=[%s].", name, oldName);
  }
  else if(uuid && !uLen){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "Invalid UUID for F-card.");
  }
  else {
    int rc;
    fsl_card_F * t;
    switch(perms){
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474

1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
    fsl_list_visitor_f() impl for outputing P cards. obj must
    be a (fsl_deck_out_state *) and obj->counter must be
    set to 0 before running the visit iteration.
 */
static int fsl_list_v_mf_output_card_P(void * obj, void * visitorState ){
  fsl_deck_out_state * os = (fsl_deck_out_state *)visitorState;
  char const * uuid = (char const *)obj;
  fsl_size_t len;
  assert(uuid);
  if(!fsl_is_uuid(uuid)){

    os->rc = fsl_error_set(&os->error, FSL_RC_RANGE,
                           "Invalid UUID in P card.");
  }
  else if(!os->counter++) fsl_appendf_f_mf(os, "P ", 2);
  else fsl_appendf_f_mf(os, " ", 1);
  /* Reminder: fsl_appendf_f_mf() updates os->rc. */
  if(!os->rc){
    len = (fsl_size_t)fsl_strlen(uuid);
    assert(FSL_UUID_STRLEN==len) /* is enforced by fsl_mf_add_P() */;
    fsl_appendf_f_mf(os, uuid, len);
  }
  return os->rc;
}


static int fsl_deck_out_P( fsl_deck_out_state * os ){
  if(!fsl_deck_out_tcheck(os, 'P')) return os->rc;







<
<
|
>







<
<
|







1481
1482
1483
1484
1485
1486
1487


1488
1489
1490
1491
1492
1493
1494
1495
1496


1497
1498
1499
1500
1501
1502
1503
1504
    fsl_list_visitor_f() impl for outputing P cards. obj must
    be a (fsl_deck_out_state *) and obj->counter must be
    set to 0 before running the visit iteration.
 */
static int fsl_list_v_mf_output_card_P(void * obj, void * visitorState ){
  fsl_deck_out_state * os = (fsl_deck_out_state *)visitorState;
  char const * uuid = (char const *)obj;


  int const uLen = uuid ? fsl_is_uuid(uuid) : 0;
  if(!uLen){
    os->rc = fsl_error_set(&os->error, FSL_RC_RANGE,
                           "Invalid UUID in P card.");
  }
  else if(!os->counter++) fsl_appendf_f_mf(os, "P ", 2);
  else fsl_appendf_f_mf(os, " ", 1);
  /* Reminder: fsl_appendf_f_mf() updates os->rc. */
  if(!os->rc){


    fsl_appendf_f_mf(os, uuid, (fsl_size_t)uLen);
  }
  return os->rc;
}


static int fsl_deck_out_P( fsl_deck_out_state * os ){
  if(!fsl_deck_out_tcheck(os, 'P')) return os->rc;
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
    
    wiki_end:
    fsl_buffer_clear(&buf);
    if(rc) goto end;
  }/*WIKI*/

  if( d->type==FSL_CATYPE_EVENT ){
    char buf[FSL_UUID_STRLEN + 7 /* event-UUID */] = {0};
    char zLength[40] = {0};
    fsl_id_t tagid;
    fsl_id_t prior, subsequent;
    char const * zWiki;
    char const * zTag;
    fsl_size_t nWiki = 0;
    fsl_snprintf(buf, sizeof(buf), "event-%s", d->E.uuid);







|







2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
    
    wiki_end:
    fsl_buffer_clear(&buf);
    if(rc) goto end;
  }/*WIKI*/

  if( d->type==FSL_CATYPE_EVENT ){
    char buf[FSL_UUIDv2_STRLEN + 7 /* event-UUID */] = {0};
    char zLength[40] = {0};
    fsl_id_t tagid;
    fsl_id_t prior, subsequent;
    char const * zWiki;
    char const * zTag;
    fsl_size_t nWiki = 0;
    fsl_snprintf(buf, sizeof(buf), "event-%s", d->E.uuid);
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633


3634
3635
3636
3637
3638
3639
3640
  x.zEnd = z+n;
  x.atEol= 1;

  /* Parsing helpers... */
#define TOKEN(DEFOS) tokLen=0; token = fsl_mf_next_token(&x,&tokLen);    \
  if(token && tokLen && (DEFOS)) fsl_bytes_defossilize(token, &tokLen)
#define TOKEN_EXISTS(MSG_IF_NOT) if(!token){ SYNTAX(MSG_IF_NOT); }(void)0
#define TOKEN_CHECKHEX(LEN,MSG) if(token \
                                   && ((LEN)!=tokLen ||                 \
                                       !fsl_validate16((char const *)token,(LEN)))){ \
                                     SYNTAX(MSG); }
#define TOKEN_UUID(CARD) TOKEN_CHECKHEX(FSL_UUID_STRLEN,"Malformed UUID in " #CARD "-card")


  /**
     Reminder: we do not know the type of the manifest at this point,
     so all of the fsl_deck_add/set() bits below can't do their
     validation. We have to determine at parse-time (or afterwards)
     which type of deck it is based on the cards we've seen. We guess
     the type as early as possible to enable during-parse validation,
     and do a post-parse check for the legality of cards added before







|
<
<
|
|
>
>







3635
3636
3637
3638
3639
3640
3641
3642


3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
  x.zEnd = z+n;
  x.atEol= 1;

  /* Parsing helpers... */
#define TOKEN(DEFOS) tokLen=0; token = fsl_mf_next_token(&x,&tokLen);    \
  if(token && tokLen && (DEFOS)) fsl_bytes_defossilize(token, &tokLen)
#define TOKEN_EXISTS(MSG_IF_NOT) if(!token){ SYNTAX(MSG_IF_NOT); }(void)0
#define TOKEN_CHECKHEX(MSG) if(token && (int)tokLen!=fsl_is_uuid((char const *)token))\


  { SYNTAX(MSG); }
#define TOKEN_UUID(CARD) TOKEN_CHECKHEX("Malformed UUID in " #CARD "-card")
#define TOKEN_MD5(ERRMSG) if(!token || FSL_MD5_STRLEN!=(int)tokLen) \
  {SYNTAX(ERRMSG);}
  /**
     Reminder: we do not know the type of the manifest at this point,
     so all of the fsl_deck_add/set() bits below can't do their
     validation. We have to determine at parse-time (or afterwards)
     which type of deck it is based on the cards we've seen. We guess
     the type as early as possible to enable during-parse validation,
     and do a post-parse check for the legality of cards added before
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
      */
      case 'R':{
        if(1<SEEN(R)){
          ERROR(FSL_RC_RANGE,"More than one R-card");
        }
        TOKEN(0);
        TOKEN_EXISTS("Missing MD5 token in R-card");
        TOKEN_CHECKHEX(FSL_MD5_STRLEN,"Malformed MD5 token in R-card");
        d->R = (char *)token;
        ++stealBuf;
        /* rc = fsl_deck_R_set(d, (char const *)token); */
        break;
      }
      /*
            T (+|*|-)<tagname> <uuid> ?<value>?







|







4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
      */
      case 'R':{
        if(1<SEEN(R)){
          ERROR(FSL_RC_RANGE,"More than one R-card");
        }
        TOKEN(0);
        TOKEN_EXISTS("Missing MD5 token in R-card");
        TOKEN_MD5("Malformed MD5 token in R-card");
        d->R = (char *)token;
        ++stealBuf;
        /* rc = fsl_deck_R_set(d, (char const *)token); */
        break;
      }
      /*
            T (+|*|-)<tagname> <uuid> ?<value>?
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
        unsigned char * name, * value;
        fsl_tag_type tagType = FSL_TAGTYPE_INVALID;
        TOKEN(0);
        TOKEN_EXISTS("Missing name for T-card");
        name = token;
        TOKEN(0);
        TOKEN_EXISTS("Missing UUID on T-card");
        if(FSL_UUID_STRLEN==tokLen){
          TOKEN_UUID(T);
          /* A valid UUID */
          if(FSL_CATYPE_EVENT==d->type){
            SYNTAX("Non-self-referential T-card in Event artifact");
          }
          uuid = token;
        }else if( 1==tokLen && '*'==(char)*token ){







|







4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
        unsigned char * name, * value;
        fsl_tag_type tagType = FSL_TAGTYPE_INVALID;
        TOKEN(0);
        TOKEN_EXISTS("Missing name for T-card");
        name = token;
        TOKEN(0);
        TOKEN_EXISTS("Missing UUID on T-card");
        if(fsl_is_uuid_len((int)tokLen)){
          TOKEN_UUID(T);
          /* A valid UUID */
          if(FSL_CATYPE_EVENT==d->type){
            SYNTAX("Non-self-referential T-card in Event artifact");
          }
          uuid = token;
        }else if( 1==tokLen && '*'==(char)*token ){
4245
4246
4247
4248
4249
4250
4251




4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265



4266
4267
4268
4269
4270
4271
4272
  if(rid<0){
    return fsl_cx_err_set(f, FSL_RC_RANGE,
                          "Invalid RID for fsl_deck_load_rid(): "
                          "%"FSL_ID_T_PFMT, (fsl_id_t)rid);
  }
  rc = fsl_content_get(f, rid, &buf);
  if(!rc){




    fsl_deck_clean(d);
    fsl_deck_init(f, d, FSL_CATYPE_ANY);
#if 0
    /*
      If we set d->type=type, the parser can fail more
      quickly. However, that failure will bypass our more specific
      reporting of the problem (see below).  As the type mismatch case
      is expected to be fairly rare, we'll leave this out for now, but
      it might be worth considering as a small optimization later on.
     */
    d->type = type /* may help parsing fail more quickly if
                      it's not the type we want.*/;
#endif
    rc = fsl_deck_parse(d, &buf);



    if(!rc){
      assert(rid == d->rid);
      if( type!=FSL_CATYPE_ANY && d->type!=type ){
        rc = fsl_cx_err_set(f, FSL_RC_TYPE,
                            "RID %"FSL_ID_T_PFMT" is of type %s, "
                            "but the caller requested type %s.",
                            (fsl_id_t)rid,







>
>
>
>














>
>
>







4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
  if(rid<0){
    return fsl_cx_err_set(f, FSL_RC_RANGE,
                          "Invalid RID for fsl_deck_load_rid(): "
                          "%"FSL_ID_T_PFMT, (fsl_id_t)rid);
  }
  rc = fsl_content_get(f, rid, &buf);
  if(!rc){
#if 0
    MARKER(("fsl_content_get(%d) len=%d =\n%.*s\n",
            (int)rid, (int)buf.used, (int)buf.used, (char const*)buf.mem));
#endif
    fsl_deck_clean(d);
    fsl_deck_init(f, d, FSL_CATYPE_ANY);
#if 0
    /*
      If we set d->type=type, the parser can fail more
      quickly. However, that failure will bypass our more specific
      reporting of the problem (see below).  As the type mismatch case
      is expected to be fairly rare, we'll leave this out for now, but
      it might be worth considering as a small optimization later on.
     */
    d->type = type /* may help parsing fail more quickly if
                      it's not the type we want.*/;
#endif
    rc = fsl_deck_parse(d, &buf);
#if 0
    MARKER(("rid=%d, d->rid=%d\n", (int)rid, (int)d->rid));
#endif
    if(!rc){
      assert(rid == d->rid);
      if( type!=FSL_CATYPE_ANY && d->type!=type ){
        rc = fsl_cx_err_set(f, FSL_RC_TYPE,
                            "RID %"FSL_ID_T_PFMT" is of type %s, "
                            "but the caller requested type %s.",
                            (fsl_id_t)rid,
Changes to src/fsl_repo.c.
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
      rid = fsl_stmt_g_id(&q, 0);
    }while( fsl_stmt_g_int32(&q, 1)==1 && rid>0 );
    fsl_stmt_finalize(&q);
    goto gotit;
  }

  symLen = fsl_strlen(sym);
  /* SHA1 hash or prefix */
  if( symLen>=4
      && symLen<=FSL_UUID_STRLEN
      && fsl_validate16(sym, symLen) ){
    fsl_stmt q = fsl_stmt_empty;
    char zUuid[FSL_UUID_STRLEN+1];
    memcpy(zUuid, sym, symLen);
    zUuid[symLen] = 0;
    fsl_canonical16(zUuid, symLen);
    rid = 0;
    /* Reminder to self: caching these queries would be cool but it
       can't work with the GLOBs.
    */







|

|


|







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
      rid = fsl_stmt_g_id(&q, 0);
    }while( fsl_stmt_g_int32(&q, 1)==1 && rid>0 );
    fsl_stmt_finalize(&q);
    goto gotit;
  }

  symLen = fsl_strlen(sym);
  /* SHA1/SHA3 hash or prefix */
  if( symLen>=4
      && symLen<=FSL_UUIDv2_STRLEN
      && fsl_validate16(sym, symLen) ){
    fsl_stmt q = fsl_stmt_empty;
    char zUuid[FSL_UUIDv2_STRLEN+1];
    memcpy(zUuid, sym, symLen);
    zUuid[symLen] = 0;
    fsl_canonical16(zUuid, symLen);
    rid = 0;
    /* Reminder to self: caching these queries would be cool but it
       can't work with the GLOBs.
    */
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324






325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
      fsl_free( rvv );
    }
  }
  return rc;
}

fsl_id_t fsl_uuid_to_rid( fsl_cx * f, char const * uuid ){
  fsl_db * db = fsl_needs_repo(f);
  fsl_size_t uuidLen = (uuid && db) ? fsl_strlen(uuid) : 0;
  if(!f || !uuid) return -1;
  else if(!db){
    /* f's error state has already been set */
    return -2;
  }
  else if(!fsl_validate16(uuid, uuidLen)){
    fsl_cx_err_set(f, FSL_RC_RANGE, "Invalid UUID (prefix): %s", uuid);
    return -3;
  }
  else if(uuidLen>FSL_UUID_STRLEN){
    fsl_cx_err_set(f, FSL_RC_RANGE, "UUID is too long: %s", uuid);
    return -4;
  }
  else {
    fsl_id_t rid = -5;
    fsl_stmt q = fsl_stmt_empty;
    fsl_stmt * qS = NULL;
    int rc;
    rc = (uuidLen==FSL_UUID_STRLEN)
      /* Optimization for the common internally-used case */






      ? fsl_db_prepare_cached(db, &qS,
                              "SELECT rid FROM blob WHERE "
                              "uuid=?")
      : fsl_db_prepare(db, &q,
                       "SELECT rid FROM blob WHERE "
                       "uuid GLOB '%s*'",
                       uuid);
    if(!rc){
      fsl_stmt * st = qS ? qS : &q;
      if(qS){
        rc = fsl_stmt_bind_text(qS, 1, uuid, FSL_UUID_STRLEN, 0);
      }
      if(!rc){
        rc = fsl_stmt_step(st);
        switch(rc){
          case FSL_RC_STEP_ROW:
            rc = 0;
            rid = fsl_stmt_g_id(st, 0);







|
|
|








|








|
|
>
>
>
>
>
>










|







296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
      fsl_free( rvv );
    }
  }
  return rc;
}

fsl_id_t fsl_uuid_to_rid( fsl_cx * f, char const * uuid ){
  fsl_db * const db = fsl_needs_repo(f);
  fsl_size_t const uuidLen = (uuid && db) ? fsl_strlen(uuid) : 0;
  if(!f || !uuid || !uuidLen) return -1;
  else if(!db){
    /* f's error state has already been set */
    return -2;
  }
  else if(!fsl_validate16(uuid, uuidLen)){
    fsl_cx_err_set(f, FSL_RC_RANGE, "Invalid UUID (prefix): %s", uuid);
    return -3;
  }
  else if(uuidLen>FSL_UUIDv2_STRLEN){
    fsl_cx_err_set(f, FSL_RC_RANGE, "UUID is too long: %s", uuid);
    return -4;
  }
  else {
    fsl_id_t rid = -5;
    fsl_stmt q = fsl_stmt_empty;
    fsl_stmt * qS = NULL;
    int rc;
    rc = fsl_is_uuid_len((int)uuidLen)
      /* Optimization for the common internally-used case.

         FIXME: there is an *astronomically small* chance of a prefix
         collision on a v1-length uuidLen against a v2-length
         blob.uuid value, leading to no match found for an existing v2
         uuid here. Like... a *REALLY* small chance.
      */
      ? fsl_db_prepare_cached(db, &qS,
                              "SELECT rid FROM blob WHERE "
                              "uuid=?")
      : fsl_db_prepare(db, &q,
                       "SELECT rid FROM blob WHERE "
                       "uuid GLOB '%s*'",
                       uuid);
    if(!rc){
      fsl_stmt * st = qS ? qS : &q;
      if(qS){
        rc = fsl_stmt_bind_text(qS, 1, uuid, (fsl_int_t)uuidLen, 0);
      }
      if(!rc){
        rc = fsl_stmt_step(st);
        switch(rc){
          case FSL_RC_STEP_ROW:
            rc = 0;
            rid = fsl_stmt_g_id(st, 0);
Changes to src/fsl_sha1.c.
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
   digest is stored in the first 20 bytes.  zBuf should
   be "char zBuf[41]".
*/
void fsl_sha1_digest_to_base16(unsigned char *digest, char *zBuf){
  static char const zEncode[] = "0123456789abcdef";
  int ix;

  for(ix=0; ix<20; ix++){
    *zBuf++ = zEncode[(*digest>>4)&0xf];
    *zBuf++ = zEncode[*digest++ & 0xf];
  }
  *zBuf = '\0';
}









|







1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
   digest is stored in the first 20 bytes.  zBuf should
   be "char zBuf[41]".
*/
void fsl_sha1_digest_to_base16(unsigned char *digest, char *zBuf){
  static char const zEncode[] = "0123456789abcdef";
  int ix;

  for(ix=0; ix<FSL_UUIDv1_STRLEN/2; ix++){
    *zBuf++ = zEncode[(*digest>>4)&0xf];
    *zBuf++ = zEncode[*digest++ & 0xf];
  }
  *zBuf = '\0';
}


1799
1800
1801
1802
1803
1804
1805
1806







1807

1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
      digest[i] = (unsigned char)
        ((ctx->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
    }
  }
  return 0;
#endif
}








int fsl_sha1sum_stream(fsl_input_f src, void * srcState, fsl_buffer *pCksum){

  fsl_sha1_cx ctx;
  int rc;
  unsigned char zResult[20];
  enum { BufSize = 1024 * 4 };
  unsigned char zBuf[BufSize];
  if(!src || !pCksum) return FSL_RC_MISUSE;
  fsl_sha1_init(&ctx);
  for(;;){
    fsl_size_t read = (fsl_size_t)BufSize;
    rc = src(srcState, zBuf, &read);
    if(rc) return rc;
    else if(read) fsl_sha1_update(&ctx, (unsigned char*)zBuf, read);
    if(read < (fsl_size_t)BufSize) break;
  }
  fsl_buffer_reset(pCksum);
  rc = fsl_buffer_resize(pCksum, FSL_UUID_STRLEN);
  if(!rc){
    fsl_sha1_final(&ctx, zResult);
    fsl_sha1_digest_to_base16(zResult, fsl_buffer_str(pCksum));
  }
  return rc;
}

int fsl_sha1sum_filename(const char *zFilename, fsl_buffer *pCksum){
  if(!zFilename || !pCksum) return FSL_RC_MISUSE;
  else{
#if 1
    int rc;
    FILE *in = fsl_fopen(zFilename, "rb");
    if(!in) rc = FSL_RC_IO;
    else{
      rc = fsl_sha1sum_stream(fsl_input_f_FILE, in, pCksum);
      fsl_fclose(in);
    }
    return rc;
#else
    /* Requires v1 code which has not yet been ported in. */
    FILE *in;
    fsl_sha1_cx ctx;
    unsigned char zResult[20];
    char zBuf[10240];

    if( fsl_wd_islink(zFilename) ){
      /* Instead of file content, return sha1 of link destination path */
      Blob destinationPath;
      int rc;
      








>
>
>
>
>
>
>

>


<
<











|

<
|




















|







1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817


1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830

1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
      digest[i] = (unsigned char)
        ((ctx->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
    }
  }
  return 0;
#endif
}

char const * fsl_sha1_final_hex(fsl_sha1_cx *context, char * zHex){
  unsigned char zResult[FSL_UUIDv1_STRLEN/2];
  fsl_sha1_final(context, zResult);
  fsl_sha1_digest_to_base16(zResult, zHex);
  return (char const *)zHex;
}

int fsl_sha1sum_stream(fsl_input_f src, void * srcState, fsl_buffer *pCksum){
  enum { BufSize = 1024 * 4 };
  fsl_sha1_cx ctx;
  int rc;


  unsigned char zBuf[BufSize];
  if(!src || !pCksum) return FSL_RC_MISUSE;
  fsl_sha1_init(&ctx);
  for(;;){
    fsl_size_t read = (fsl_size_t)BufSize;
    rc = src(srcState, zBuf, &read);
    if(rc) return rc;
    else if(read) fsl_sha1_update(&ctx, (unsigned char*)zBuf, read);
    if(read < (fsl_size_t)BufSize) break;
  }
  fsl_buffer_reset(pCksum);
  rc = fsl_buffer_resize(pCksum, FSL_UUIDv1_STRLEN);
  if(!rc){

    fsl_sha1_final_hex(&ctx, fsl_buffer_str(pCksum));
  }
  return rc;
}

int fsl_sha1sum_filename(const char *zFilename, fsl_buffer *pCksum){
  if(!zFilename || !pCksum) return FSL_RC_MISUSE;
  else{
#if 1
    int rc;
    FILE *in = fsl_fopen(zFilename, "rb");
    if(!in) rc = FSL_RC_IO;
    else{
      rc = fsl_sha1sum_stream(fsl_input_f_FILE, in, pCksum);
      fsl_fclose(in);
    }
    return rc;
#else
    /* Requires v1 code which has not yet been ported in. */
    FILE *in;
    fsl_sha1_cx ctx;
    unsigned char zResult[FSL_UUIDv1_STRLEN/2];
    char zBuf[10240];

    if( fsl_wd_islink(zFilename) ){
      /* Instead of file content, return sha1 of link destination path */
      Blob destinationPath;
      int rc;
      
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
      int n;
      n = fread(zBuf, 1, sizeof(zBuf), in);
      if( n<=0 ) break;
      fsl_sha1_update(&ctx, (unsigned char*)zBuf, (unsigned)n);
    }
    fclose_fclose(in);
    blob_zero(pCksum);
    blob_resize(pCksum, FSL_UUID_STRLEN);
    fsl_sha1_final(&ctx, zResult);
    fsl_sha1_digest_to_base16(zResult, blob_buffer(pCksum));
    return 0;
#endif
  }
}

int fsl_sha1sum_buffer(fsl_buffer const *pIn, fsl_buffer *pCksum){
  if(!pIn || !pCksum) return FSL_RC_MISUSE;
  else{
    fsl_sha1_cx ctx;
    unsigned char zResult[20];
    int rc;
    fsl_sha1_init(&ctx);
    fsl_sha1_update(&ctx, pIn->mem, pIn->used);
    fsl_buffer_reset(pCksum);
    rc = fsl_buffer_resize(pCksum, FSL_UUID_STRLEN
                           /*resize() adds 1 for NUL*/);
    if(!rc){
      fsl_sha1_final(&ctx, zResult);
      fsl_sha1_digest_to_base16(zResult, fsl_buffer_str(pCksum));
      assert(0==pCksum->mem[pCksum->used]);
    }
    return rc;
  }
}

char *fsl_sha1sum_cstr(const char *zIn, fsl_int_t len){
  if(!zIn || !len) return NULL;
  else{
    fsl_sha1_cx ctx;
    unsigned char zResult[20];
    char * zDigest = (char *)fsl_malloc(FSL_UUID_STRLEN+1);
    if(!zDigest) return NULL;
    fsl_sha1_init(&ctx);
    fsl_sha1_update(&ctx, zIn,
                    (len<0) ? fsl_strlen(zIn) : (fsl_size_t)len);
    fsl_sha1_final(&ctx, zResult);
    fsl_sha1_digest_to_base16(zResult, zDigest);
    return zDigest;
  }
}







|











<




|


<
|










<
|
|



|
<
|


1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890

1891
1892
1893
1894
1895
1896
1897

1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908

1909
1910
1911
1912
1913
1914

1915
1916
1917
      int n;
      n = fread(zBuf, 1, sizeof(zBuf), in);
      if( n<=0 ) break;
      fsl_sha1_update(&ctx, (unsigned char*)zBuf, (unsigned)n);
    }
    fclose_fclose(in);
    blob_zero(pCksum);
    blob_resize(pCksum, FSL_UUIDv1_STRLEN);
    fsl_sha1_final(&ctx, zResult);
    fsl_sha1_digest_to_base16(zResult, blob_buffer(pCksum));
    return 0;
#endif
  }
}

int fsl_sha1sum_buffer(fsl_buffer const *pIn, fsl_buffer *pCksum){
  if(!pIn || !pCksum) return FSL_RC_MISUSE;
  else{
    fsl_sha1_cx ctx;

    int rc;
    fsl_sha1_init(&ctx);
    fsl_sha1_update(&ctx, pIn->mem, pIn->used);
    fsl_buffer_reset(pCksum);
    rc = fsl_buffer_resize(pCksum, FSL_UUIDv1_STRLEN
                           /*resize() adds 1 for NUL*/);
    if(!rc){

      fsl_sha1_final_hex(&ctx, fsl_buffer_str(pCksum));
      assert(0==pCksum->mem[pCksum->used]);
    }
    return rc;
  }
}

char *fsl_sha1sum_cstr(const char *zIn, fsl_int_t len){
  if(!zIn || !len) return NULL;
  else{
    fsl_sha1_cx ctx;

    char * zHex = (char *)fsl_malloc(FSL_UUIDv1_STRLEN+1);
    if(!zHex) return NULL;
    fsl_sha1_init(&ctx);
    fsl_sha1_update(&ctx, zIn,
                    (len<0) ? fsl_strlen(zIn) : (fsl_size_t)len);
    fsl_sha1_final_hex(&ctx, zHex);

    return zHex;
  }
}
Changes to src/fsl_sha3.c.
31
32
33
34
35
36
37








38
39
40
41
42
43
44
#include "fossil-scm/fossil-hash.h"
#include <assert.h>
#include <string.h> /* strlen() */
#include <stddef.h> /* NULL on linux */
#include <sys/types.h>
#include <assert.h>









/*
** Macros to determine whether the machine is big or little endian,
** and whether or not that determination is run-time or compile-time.
**
** For best performance, an attempt is made to guess at the byte-order
** using C-preprocessor macros.  If that is unsuccessful, or if
** -DSHA3_BYTEORDER=0 is set, then byte-order is determined







>
>
>
>
>
>
>
>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include "fossil-scm/fossil-hash.h"
#include <assert.h>
#include <string.h> /* strlen() */
#include <stddef.h> /* NULL on linux */
#include <sys/types.h>
#include <assert.h>

#if 0
#include <stdio.h>
#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)
#endif

/*
** Macros to determine whether the machine is big or little endian,
** and whether or not that determination is run-time or compile-time.
**
** For best performance, an attempt is made to guess at the byte-order
** using C-preprocessor macros.  If that is unsuccessful, or if
** -DSHA3_BYTEORDER=0 is set, then byte-order is determined
412
413
414
415
416
417
418
419




420
421
422
423
424
425
426
      case 384: return FSL_SHA3_384; case 416: return FSL_SHA3_416;
      case 448: return FSL_SHA3_448; case 480: return FSL_SHA3_480;
      case 512: return FSL_SHA3_512;
      default: return FSL_SHA3_INVALID;
    }
}

void fsl_sha3_init(fsl_sha3_cx *p, enum fsl_sha3_hash_size iSize){




  memset(p, 0, sizeof(*p));
  p->size = iSize;
  if( iSize>=128 && iSize<=512 ){
    p->nRate = (1600 - ((iSize + 31)&~31)*2)/8;
  }else{
    p->nRate = (1600 - 2*256)/8;
  }







|
>
>
>
>







420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
      case 384: return FSL_SHA3_384; case 416: return FSL_SHA3_416;
      case 448: return FSL_SHA3_448; case 480: return FSL_SHA3_480;
      case 512: return FSL_SHA3_512;
      default: return FSL_SHA3_INVALID;
    }
}

void fsl_sha3_init(fsl_sha3_cx *cx){
  fsl_sha3_init2(cx, FSL_SHA3_DEFAULT);
}
void fsl_sha3_init2(fsl_sha3_cx *p, enum fsl_sha3_hash_size iSize){
  assert(iSize>0);
  memset(p, 0, sizeof(*p));
  p->size = iSize;
  if( iSize>=128 && iSize<=512 ){
    p->nRate = (1600 - ((iSize + 31)&~31)*2)/8;
  }else{
    p->nRate = (1600 - 2*256)/8;
  }
505
506
507
508
509
510
511






512


















































































































513

    p->u.x[i+p->nRate] = p->u.x[i^p->ixMask];
  }
  DigestToBase16( &p->u.x[p->nRate], p->hex, (int)p->size/8 );
  assert(0 == p->hex[(int)p->size/4+1]);
  return &p->u.x[p->nRate];
}


























































































































#undef SHA3_BYTEORDER








>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
    p->u.x[i+p->nRate] = p->u.x[i^p->ixMask];
  }
  DigestToBase16( &p->u.x[p->nRate], p->hex, (int)p->size/8 );
  assert(0 == p->hex[(int)p->size/4+1]);
  return &p->u.x[p->nRate];
}

void fsl_sha3_digest_to_base16(unsigned char *digest, char *zBuf){
  static char const zEncode[] = "0123456789abcdef";
  int ix;
  for(ix=0; ix<FSL_UUIDv2_STRLEN/2; ix++){
    *zBuf++ = zEncode[(*digest>>4)&0xf];
    *zBuf++ = zEncode[*digest++ & 0xf];
  }
  *zBuf = '\0';
}


int fsl_sha3sum_stream(fsl_input_f src, void * srcState, fsl_buffer *pCksum){
  fsl_sha3_cx ctx;
  int rc;
  enum { BufSize = 1024 * 4 };
  unsigned char zBuf[BufSize];
  if(!src || !pCksum) return FSL_RC_MISUSE;
  fsl_sha3_init(&ctx);
  for(;;){
    fsl_size_t read = (fsl_size_t)BufSize;
    rc = src(srcState, zBuf, &read);
    if(rc) return rc;
    else if(read) fsl_sha3_update(&ctx, (unsigned char*)zBuf, read);
    if(read < (fsl_size_t)BufSize) break;
  }
  fsl_buffer_reset(pCksum);
  rc = fsl_buffer_resize(pCksum, FSL_UUIDv2_STRLEN);
  if(!rc){
    fsl_sha3_end(&ctx);
    rc = fsl_buffer_append(pCksum, ctx.hex, fsl_strlen((const char *)ctx.hex));
  }
  return rc;
}

int fsl_sha3sum_buffer(fsl_buffer const *pIn, fsl_buffer *pCksum){
  if(!pIn || !pCksum) return FSL_RC_MISUSE;
  else{
    fsl_sha3_cx ctx;
    int rc;
    fsl_sha3_init(&ctx);
    fsl_sha3_update(&ctx, pIn->mem, pIn->used);
    fsl_buffer_reset(pCksum);
    rc = fsl_buffer_resize(pCksum, FSL_UUIDv2_STRLEN
                           /*resize() adds 1 for NUL*/);
    if(!rc){
      pCksum->used = 0;
      fsl_sha3_end(&ctx);
      assert(fsl_strlen((char const*)ctx.hex)==FSL_UUIDv2_STRLEN);
      rc = fsl_buffer_append(pCksum, ctx.hex, fsl_strlen((char const*)ctx.hex));
      assert(!rc && "Cannot fail - pre-allocated");
      if(!rc){
        assert(0==pCksum->mem[pCksum->used]);
      }
    }
    return rc;
  }
}

char *fsl_sha3sum_cstr(const char *zIn, fsl_int_t len){
  if(!zIn || !len) return NULL;
  else{
    fsl_sha3_cx ctx;
    fsl_sha3_init(&ctx);
    fsl_sha3_update(&ctx, zIn,
                    (len<0) ? fsl_strlen(zIn) : (fsl_size_t)len);
    fsl_sha3_end(&ctx);
    return fsl_strdup((char const *)ctx.hex);
  }
}

int fsl_sha3sum_filename(const char *zFilename, fsl_buffer *pCksum){
  if(!zFilename || !pCksum) return FSL_RC_MISUSE;
  else{
#if 1
    int rc;
    FILE *in = fsl_fopen(zFilename, "rb");
    if(!in) rc = FSL_RC_IO;
    else{
      rc = fsl_sha3sum_stream(fsl_input_f_FILE, in, pCksum);
      fsl_fclose(in);
    }
    return rc;
#else
    /* Requires v1 code which has not yet been ported in. */
    FILE *in;
    fsl_sha1_cx ctx;
    char zBuf[10240];
    int rc;

    if( fsl_wd_islink(zFilename) ){
      /* Instead of file content, return sha3 of link destination path */
      Blob destinationPath;
      
      blob_read_link(&destinationPath, zFilename);
      rc = fsl_sha3sum_buffer(&destinationPath, pCksum);
      fsl_buffer_clear(&destinationPath);
      return rc;
    }

    in = fossil_fopen(zFilename,"rb");
    if( in==0 ){
      return 1;
    }
    fsl_sha3_init(&ctx);
    for(;;){
      int n;
      n = fread(zBuf, 1, sizeof(zBuf), in);
      if( n<=0 ) break;
      fsl_sha3_update(&ctx, (unsigned char*)zBuf, (unsigned)n);
    }
    fclose_fclose(in);
    blob_zero(pCksum);
    blob_resize(pCksum, FSL_UUIDv1_STRLEN);
    fsl_sha3_end(&ctx);
    rc = fsl_buffer_append(pCksum, ctx.hex, fsl_strlen(ctx.hex));
    return rc;
#endif
  }
}


#undef SHA3_BYTEORDER
#undef MARKER
Changes to src/fsl_tag.c.
49
50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
}

fsl_card_T * fsl_card_T_malloc(fsl_tag_type tagType,
                               char const * uuid,
                               char const * name,
                               char const * value){
  fsl_card_T * t;
  if(uuid && !fsl_is_uuid(uuid)) return NULL;

  t = (fsl_card_T *)fsl_malloc(sizeof(fsl_card_T));
  if(t){
    int rc = 0;
    *t = fsl_card_T_empty;
    t->type = tagType;
    if(uuid && *uuid){
      t->uuid = fsl_strndup(uuid, FSL_UUID_STRLEN);
      if(!t->uuid) rc = FSL_RC_OOM;
    }
    if(!rc && name && *name){
      t->name = fsl_strdup(name);
      if(!t->name){
        rc = FSL_RC_OOM;
      }







|
>






|







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
}

fsl_card_T * fsl_card_T_malloc(fsl_tag_type tagType,
                               char const * uuid,
                               char const * name,
                               char const * value){
  fsl_card_T * t;
  int const uuidLen = uuid ? fsl_is_uuid(uuid) : 0;
  if(uuid && !uuidLen) return NULL;
  t = (fsl_card_T *)fsl_malloc(sizeof(fsl_card_T));
  if(t){
    int rc = 0;
    *t = fsl_card_T_empty;
    t->type = tagType;
    if(uuid && *uuid){
      t->uuid = fsl_strndup(uuid, uuidLen);
      if(!t->uuid) rc = FSL_RC_OOM;
    }
    if(!rc && name && *name){
      t->name = fsl_strdup(name);
      if(!t->name){
        rc = FSL_RC_OOM;
      }
Changes to src/fsl_vfile.c.
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  if(rc) goto end;
  rc = fsl_stmt_bind_id_name(&qIns, ":vid", vid);
  while( !rc && !(rc=fsl_deck_F_next(&d, &fc)) && fc){
    fsl_id_t rid;
    fsl_int64_t size;
    if(!fc->uuid /* was removed in this version */
       || fsl_uuid_is_shunned(f,fc->uuid)) continue;
    rc = fsl_stmt_bind_text(&qRid, 1, fc->uuid, FSL_UUID_STRLEN, 0);
    if(rc) break;
    rc = fsl_stmt_step(&qRid);
    if(FSL_RC_STEP_ROW==rc){
      rid = fsl_stmt_g_id(&qRid,0);
      size = fsl_stmt_g_int64(&qRid,1);
    }else if(FSL_RC_STEP_DONE==rc){
      rid = 0;







|







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  if(rc) goto end;
  rc = fsl_stmt_bind_id_name(&qIns, ":vid", vid);
  while( !rc && !(rc=fsl_deck_F_next(&d, &fc)) && fc){
    fsl_id_t rid;
    fsl_int64_t size;
    if(!fc->uuid /* was removed in this version */
       || fsl_uuid_is_shunned(f,fc->uuid)) continue;
    rc = fsl_stmt_bind_text(&qRid, 1, fc->uuid, -1, 0);
    if(rc) break;
    rc = fsl_stmt_step(&qRid);
    if(FSL_RC_STEP_ROW==rc){
      rid = fsl_stmt_g_id(&qRid,0);
      size = fsl_stmt_g_int64(&qRid,1);
    }else if(FSL_RC_STEP_DONE==rc){
      rid = 0;
135
136
137
138
139
140
141































142
143
144
145
146
147
148
  else rc = fsl_db_transaction_commit(dbC);
  if(rc && !f->error.code){
    if(dbC->error.code) fsl_cx_uplift_db_error(f, dbC);
    else if(dbR->error.code) fsl_cx_uplift_db_error(f, dbR);
  }
  return rc;
}
































/**
   UNTESTED single-file version of fsl_vfile_changes_scan().

   vid is the version to scan changes against. If 0 or less, the
   current checkout is used. The vfile table must have been previously
   populated with vid's contents (which it will have been for the







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  else rc = fsl_db_transaction_commit(dbC);
  if(rc && !f->error.code){
    if(dbC->error.code) fsl_cx_uplift_db_error(f, dbC);
    else if(dbR->error.code) fsl_cx_uplift_db_error(f, dbR);
  }
  return rc;
}
/**
   Internal code de-duplifier for places which need to re-check a
   file's hash in order to be sure whether it was really
   modified. hashLen must be the length of the previous (db-side)
   hash of the file. This routine will hash that file using the same
   hash type.

   Returns 0 on success. On error, if *errReported is set to non-0 then the
   error state has already been set
*/
static int fsl_vfile_recheck_file_hash( fsl_cx * f, const char * zName,
                                        int hashLen, fsl_buffer * pTgt ){
  int errReported = 0;
  int rc = 0;
  pTgt->used = 0;
  if(FSL_UUIDv1_STRLEN==hashLen){
    rc = fsl_sha1sum_filename(zName, pTgt);
  }else if(FSL_UUIDv2_STRLEN==hashLen){
    rc = fsl_sha3sum_filename(zName, pTgt);
  }else{
    rc = fsl_cx_err_set(f, FSL_RC_CHECKSUM_MISMATCH,
                        "Cannot determine which hash to use for file: %s",
                        zName);
    errReported = 1;
  }
  if(rc && !errReported && FSL_RC_OOM != rc){
    rc = fsl_cx_err_set(f, rc, "Error %s while reading SHA of file: %s",
                        fsl_rc_cstr(rc), zName);
  }
  return rc;
}

/**
   UNTESTED single-file version of fsl_vfile_changes_scan().

   vid is the version to scan changes against. If 0 or less, the
   current checkout is used. The vfile table must have been previously
   populated with vid's contents (which it will have been for the
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    default:
      assert(!rc);
      goto end;
  }


  {
    static char const * errReadingSha1 =
      "Error %s while reading SHA1 of file: %s";
    fsl_id_t id = fsl_stmt_g_id(q, 0);
    fsl_id_t rid = fsl_stmt_g_id(q, 2);
    /* fsl_size_t nName = 0; */
    char const * zName = fsl_stmt_g_text(q, 1, NULL/* &nName */);
    /* fsl_size_t const rootLen = fsl_strlen(f->ckout.dir); */
    /* char const * relName = zName + rootLen; */
    int const isDeleted = fsl_stmt_g_int32(q, 3);







<
<







247
248
249
250
251
252
253


254
255
256
257
258
259
260
    default:
      assert(!rc);
      goto end;
  }


  {


    fsl_id_t id = fsl_stmt_g_id(q, 0);
    fsl_id_t rid = fsl_stmt_g_id(q, 2);
    /* fsl_size_t nName = 0; */
    char const * zName = fsl_stmt_g_text(q, 1, NULL/* &nName */);
    /* fsl_size_t const rootLen = fsl_strlen(f->ckout.dir); */
    /* char const * relName = zName + rootLen; */
    int const isDeleted = fsl_stmt_g_int32(q, 3);
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
    if(origSize!=currentSize){
      /* A file size change is definitive - the file has changed. No
         need to check the mtime or sha1sum */
      changed = FSL_VFILE_CHANGE_MOD;
    }else if( !forceVFileUpdate
              && changed==FSL_VFILE_CHANGE_MOD
              && rid!=0 && !isDeleted ){
       /* File is believed to have changed but it is the same size.
         Double check that it really has changed by looking at content. */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );
      fileCksum->used = 0;
      rc = fsl_sha1sum_filename(zName, fileCksum);
      if(rc){
        if(FSL_RC_OOM != rc){
          fsl_cx_err_set(f, rc, errReadingSha1,
                         fsl_rc_cstr(rc), zName);
        }
        goto end;
      }
      assert(FSL_UUID_STRLEN==fileCksum->used);
      uuid = fsl_stmt_g_text(q, 5, &nUuid);
      assert(uuid && (FSL_UUID_STRLEN==nUuid));
      if( 0 == fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = 0;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }else if( !forceVFileUpdate
              && (changed==FSL_VFILE_CHANGE_NONE
                  || changed==FSL_VFILE_CHANGE_MERGE_MOD
                  || changed==FSL_VFILE_CHANGE_INTEGRATE_MOD)
              && (useMtime==0 || currentMtime!=oldMtime) ){
      /* For files that were formerly believed to be unchanged or that were
         changed by merging, if their mtime changes, or unconditionally
         if --sha1sum is used, check to see if they have been edited by
         looking at their SHA1 sum */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );
      fileCksum->used = 0;
      rc = fsl_sha1sum_filename(zName, fileCksum);
      if(rc){
        if(FSL_RC_OOM != rc){
          fsl_cx_err_set(f, rc, errReadingSha1,
                         fsl_rc_cstr(rc), zName);
        }
        goto end;
      }
      assert(FSL_UUID_STRLEN==fileCksum->used);
      uuid = fsl_stmt_g_text(q, 5, &nUuid);
      assert(uuid && (FSL_UUID_STRLEN==nUuid));
      if( fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = FSL_VFILE_CHANGE_MOD;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }
    if( (cksigFlags & FSL_VFILE_CKSIG_SETMTIME)







|






<
|
<
<
|
|
<
|
<
|
<
<













|





<
|
<
<
|
|
<
|
<
|
<
<







325
326
327
328
329
330
331
332
333
334
335
336
337
338

339


340
341

342

343


344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362

363


364
365

366

367


368
369
370
371
372
373
374
    if(origSize!=currentSize){
      /* A file size change is definitive - the file has changed. No
         need to check the mtime or sha1sum */
      changed = FSL_VFILE_CHANGE_MOD;
    }else if( !forceVFileUpdate
              && changed==FSL_VFILE_CHANGE_MOD
              && rid!=0 && !isDeleted ){
      /* File is believed to have changed but it is the same size.
         Double check that it really has changed by looking at content. */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );

      uuid = fsl_stmt_g_text(q, 5, &nUuid);


      assert(uuid && fsl_is_uuid(uuid)==(int)nUuid);
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);

      if(rc) goto end;

      assert(fsl_is_uuid_len((int)fileCksum->used));


      if( 0 == fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = 0;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }else if( !forceVFileUpdate
              && (changed==FSL_VFILE_CHANGE_NONE
                  || changed==FSL_VFILE_CHANGE_MERGE_MOD
                  || changed==FSL_VFILE_CHANGE_INTEGRATE_MOD)
              && (useMtime==0 || currentMtime!=oldMtime) ){
      /* For files that were formerly believed to be unchanged or that were
         changed by merging, if their mtime changes, or unconditionally
         if --sha1sum is used, check to see if they have been edited by
         looking at their hash */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );

      uuid = fsl_stmt_g_text(q, 5, &nUuid);


      assert(uuid && fsl_is_uuid(uuid)==(int)nUuid);
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);

      if(rc) goto end;

      assert(fsl_is_uuid_len((int)fileCksum->used));


      if( fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = FSL_VFILE_CHANGE_MOD;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }
    if( (cksigFlags & FSL_VFILE_CKSIG_SETMTIME)
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
  fsl_db * db = fsl_needs_checkout(f);
  fsl_fstat fst = fsl_fstat_empty;
  fsl_deck deck = fsl_deck_empty;
#ifndef _WIN32
  fsl_deck * d = NULL;
#endif
  fsl_size_t rootLen;
  static char const * errReadingSha1 =
    "Error %s while reading SHA1 of file: %s";

  if(!db) return FSL_RC_NOT_A_CHECKOUT;

  assert(f->ckout.dir);
  if(vid<0) vid = f->ckout.rid;
  assert(vid>=0);
  rootLen = fsl_strlen(f->ckout.dir);







<
<







421
422
423
424
425
426
427


428
429
430
431
432
433
434
  fsl_db * db = fsl_needs_checkout(f);
  fsl_fstat fst = fsl_fstat_empty;
  fsl_deck deck = fsl_deck_empty;
#ifndef _WIN32
  fsl_deck * d = NULL;
#endif
  fsl_size_t rootLen;



  if(!db) return FSL_RC_NOT_A_CHECKOUT;

  assert(f->ckout.dir);
  if(vid<0) vid = f->ckout.rid;
  assert(vid>=0);
  rootLen = fsl_strlen(f->ckout.dir);
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
      /* File is believed to have changed but it is the same size.
         Double check that it really has changed by looking at content. */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );
      rc = fsl_sha1sum_filename(zName, fileCksum);
      if(rc){
        if(FSL_RC_OOM != rc){
          fsl_cx_err_set(f, rc, errReadingSha1,
                         fsl_rc_cstr(rc), zName);
        }
        goto end;
      }
      assert(FSL_UUID_STRLEN==fileCksum->used);
      uuid = fsl_stmt_g_text(&q, 5, &nUuid);
      assert(uuid && (FSL_UUID_STRLEN==nUuid));
      if( 0 == fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = 0;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }else if( !forceVFileUpdate
              && (changed==FSL_VFILE_CHANGE_NONE
                  || changed==FSL_VFILE_CHANGE_MERGE_MOD
                  || changed==FSL_VFILE_CHANGE_INTEGRATE_MOD)
              && (useMtime==0 || currentMtime!=oldMtime) ){
      /* For files that were formerly believed to be unchanged or that were
         changed by merging, if their mtime changes, or unconditionally
         if --sha1sum is used, check to see if they have been edited by
         looking at their SHA1 sum */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );
      fileCksum->used = 0;
      rc = fsl_sha1sum_filename(zName, fileCksum);
      if(rc){
        if(FSL_RC_OOM != rc){
          fsl_cx_err_set(f, rc, errReadingSha1,
                         fsl_rc_cstr(rc), zName);
        }
        goto end;
      }
      assert(FSL_UUID_STRLEN==fileCksum->used);
      uuid = fsl_stmt_g_text(&q, 5, &nUuid);
      assert(uuid && (FSL_UUID_STRLEN==nUuid));
      if( fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = FSL_VFILE_CHANGE_MOD;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }
    if( (cksigFlags & FSL_VFILE_CKSIG_SETMTIME)







|
<
<
|
|
<
|
<
|
<
<



















<
|
<
<
|
|
<
|
<
|
<
<







552
553
554
555
556
557
558
559


560
561

562

563


564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582

583


584
585

586

587


588
589
590
591
592
593
594
      /* File is believed to have changed but it is the same size.
         Double check that it really has changed by looking at content. */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );
      uuid = fsl_stmt_g_text(&q, 5, &nUuid);


      assert(uuid && fsl_is_uuid_len((int)nUuid));
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);

      if(rc) goto end;

      assert(fsl_is_uuid_len((int)fileCksum->used));


      if( 0 == fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = 0;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }else if( !forceVFileUpdate
              && (changed==FSL_VFILE_CHANGE_NONE
                  || changed==FSL_VFILE_CHANGE_MERGE_MOD
                  || changed==FSL_VFILE_CHANGE_INTEGRATE_MOD)
              && (useMtime==0 || currentMtime!=oldMtime) ){
      /* For files that were formerly believed to be unchanged or that were
         changed by merging, if their mtime changes, or unconditionally
         if --sha1sum is used, check to see if they have been edited by
         looking at their SHA1 sum */
      fsl_size_t nUuid = 0;
      char const * uuid;
      fsl_buffer * fileCksum = &f->fileContent;
      assert(!fileCksum->used && "Misuse of f->fileContent.");
      assert( origSize==currentSize );

      uuid = fsl_stmt_g_text(&q, 5, &nUuid);


      assert(uuid && fsl_is_uuid_len((int)nUuid));
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);

      if(rc) goto end;

      assert(fsl_is_uuid_len((int)fileCksum->used));


      if( fsl_uuidcmp(fsl_buffer_cstr(fileCksum), uuid) ){
        changed = FSL_VFILE_CHANGE_MOD;
      }
      fileCksum->used = 0;
      /* MARKER(("SHA1 compare says %d: %s\n", changed, zName)); */
    }
    if( (cksigFlags & FSL_VFILE_CKSIG_SETMTIME)