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
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
    th1ish/Makefile
    cpp/Makefile
}
foreach {f} $makefiles {
    makeFromDotIn $f
}

if {0} {
Changes to autosetup/README.autosetup.
1

2
3
4
5
6
7
8

1
2
3
4
5
6
7
8
-
+







README.autosetup created by autosetup v0.7.0+
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
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+
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
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"
		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"
		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"
		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
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)

	# Add $argv to CONFIGURE_OPTS, but ignore duplicates and quote if needed
	define CONFIGURE_OPTS ""
	set configure_opts {}
	foreach arg $autosetup(argv) {
		set quoted [quote-if-needed $arg]
		define-append CONFIGURE_OPTS [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)
	# 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
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(optdefault) $opt]} {
				set result [dict get $autosetup(optdefault) $opt]
			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
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} {
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) [list $opt $autosetup(module)]
			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
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 {$equal ne "="} {
				# Was the option given as "name:value=default"?
			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
				# No default value was given by value=default or options-defaults
				# so use the value as the default when the plain option with no
				# the display text and the default value, but only if the user
				# specified the option without the value
				# 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 {
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
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) [list $opthelp $autosetup(module) $desc]
			lappend autosetup(optionhelp) $opthelp $desc
			incr i 2
		}
	}
}

# @module-options optionlist
#
# Deprecated. Simply use 'options' from within a module.
# Like 'options', but used within a module.
proc module-options {opts} {
	set header ""
	if {$::autosetup(showhelp) > 1 && [llength $opts]} {
		set header "Module Options:"
	}
	options $opts
	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
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 ""
	}
}

# Display options (from $autosetup(optionhelp)) for modules that match
# glob pattern $what
proc options-show {what} {
proc options-show {} {
	set local 0
	# Determine the max option width
	set max 0
	foreach help $::autosetup(optionhelp) {
	foreach {opt desc} $::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) {
	foreach {opt desc} $::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]
		}
	}
}

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
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 one of the following forms:
# An argument option (one which takes a parameter) is of the form:
#
## name:value         => "Description of this option"
## 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 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} {
	global autosetup

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

	if {$autosetup(showhelp)} {
		# If --help, stop now to show help
	if {$::autosetup(showhelp)} {
		options-show
		exit 0
		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"
	# 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
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)]} {
			autosetup_load_module $m eval $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]
				autosetup_load_module $m source $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] {
		autosetup_load_module $mod eval $modsource($mod)
		automf_load 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
		automf_load 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

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)]
	}
	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) {
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
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 in {all local}} {
    if {$what eq "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)
            # This relies on auto.def having a call to 'options'
            # which will display options and quit
            source $::autosetup(autodef)
        }
        if {$what eq "all"} {
            set what *
        } else {
            set what auto.def
            options-show
        }
    } else {
        incr ::autosetup(showhelp)
        use $what
        puts "Options for module $what:"
    }
        if {[catch {use $what}]} {
            user-error "Unknown module: $what"
        } else {
            options-show
        }
    options-show $what
    }
    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
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 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
# 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 jimsh0 "$d/jimsh0.c"; } 2>/dev/null || continue
	./jimsh0 "$d/autosetup-test-tclsh" && exit 0
	{ $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


1
2
3
4
5
6
7
8
9
10
-
-
+
+
+







# For this project, disable the pager for --help
set useropts(nopager) 1
# 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
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);
    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
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);
    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
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_UUID_STRLEN==fsl_strlen(uuid));
    f_out("Current checkout: %.*s\n", FSL_UUID_STRLEN, uuid);
    fsl_free(uuid);
    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
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_UUID_STRLEN==c.used);
  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_UUID_STRLEN==c.used);
  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_UUID_STRLEN==c.used);
  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
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);
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
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 );
                                       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
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 UUID strings.

   FIXME: SHA3
   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_UUID_STRLEN = 40,
FSL_UUIDv2_STRLEN = 64,
/**
   The length, in bytes, of a hex-form MD5 hash.
*/
FSL_MD5_STRLEN = 32
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
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_UUID_STRLEN+1];
   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
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_UUID_STRLEN
   + 1) bytes long, for FSL_UUID_STRLEN characters of
   hexidecimal-form SHA1 hash and 1 NUL byte.
   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
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
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_256);
   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
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 value.
   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, enum fsl_sha3_hash_size sz);
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 hashing is complete: finishes the hash
   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);

/* 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);

/**
   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
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
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
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_UUID_STRLEN bytes long plus a
   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).
   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
   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;

/**
   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).
   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
   hash strings. Don't let that disturb you. As Tim Berners-Lee
   writes:
   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 char fsl_is_uuid(char const * str);
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
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, FSL_UUID_STRLEN).
   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
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);
  }
}

char fsl_is_uuid(char const * str){
int 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);
}


  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
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_UUID_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
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 );
        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
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 <assert.h>

#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
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;
  int rc = 0;
  char inTrans = 0;
  assert(f);
  assert(dbR);
  assert(pBlob);
  assert(srcId==0 || zUuid!=NULL);
  assert(!zUuid || fsl_is_uuid(zUuid));
  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_sha1sum_buffer(pBlob, &hash);
    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, FSL_UUID_STRLEN);
    rc = fsl_buffer_append(&hash, zUuid, zUuidLen);
  }
  if(rc) goto end;

    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
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
    /* MISSING */
    /* 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
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
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){
#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){
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(!fsl_is_uuid(uuid)) return FSL_RC_RANGE;
  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, FSL_UUID_STRLEN, 0);
  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
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
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
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_UUID_STRLEN==fsl_strlen(f->ckout.uuid));
      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
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
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);
    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
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 || !fsl_is_uuid(target)
     || (baseline && !fsl_is_uuid(baseline))) return NULL;
  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, FSL_UUID_STRLEN);
      c->target = fsl_strndup(target, targetLen);
      if(!c->target) rc = FSL_RC_OOM;
      else if(baseline){
        c->baseline = fsl_strndup( baseline, FSL_UUID_STRLEN);
        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
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 && !fsl_is_uuid(uuid)) 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 && uuid){
      t->uuid = fsl_strdup(uuid);
    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
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 ){
                                 char letter,
                                 fsl_size_t assertLen,
                                 char ** mfMember ){
  assert(mf);
  assert( assertLen==FSL_UUID_STRLEN || assertLen==FSL_MD5_STRLEN );
  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)){
  }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 );
    *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
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',
                                FSL_UUID_STRLEN, &mf->B.uuid);
                                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
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
    ? fsl_deck_sethex_impl(mf, uuid, 'K', FSL_UUID_STRLEN, &mf->K)
  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(!fsl_is_uuid(uuid)) return FSL_RC_RANGE;
  dupe = fsl_strndup(uuid, FSL_UUID_STRLEN);
  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
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(!fsl_is_uuid(parentUuid)){
  else if(!uLen){
    return fsl_error_set(&mf->error, FSL_RC_RANGE,
                         "Invalid UUID for P-card.");
  }
  dupe = fsl_strndup(parentUuid, FSL_UUID_STRLEN);
  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
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 && !fsl_is_uuid(uuidSrc)){
  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(uuidSrc && *uuidSrc){
      mf->A.src = fsl_strndup(uuidSrc,FSL_UUID_STRLEN);
    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 || !uuid) return FSL_RC_MISUSE;
  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(!fsl_is_uuid(uuid)){
  }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, FSL_UUID_STRLEN);
    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
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 && !fsl_is_uuid(uuid)){
  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
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;
  fsl_size_t len;
  assert(uuid);
  if(!fsl_is_uuid(uuid)){
  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){
    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);
    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
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_UUID_STRLEN + 7 /* event-UUID */] = {0};
    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
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(LEN,MSG) if(token \
#define TOKEN_CHECKHEX(MSG) if(token && (int)tokLen!=fsl_is_uuid((char const *)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")
  { 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
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_CHECKHEX(FSL_MD5_STRLEN,"Malformed 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
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_UUID_STRLEN==tokLen){
        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
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
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 */
  /* SHA1/SHA3 hash or prefix */
  if( symLen>=4
      && symLen<=FSL_UUID_STRLEN
      && symLen<=FSL_UUIDv2_STRLEN
      && fsl_validate16(sym, symLen) ){
    fsl_stmt q = fsl_stmt_empty;
    char zUuid[FSL_UUID_STRLEN+1];
    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
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 * db = fsl_needs_repo(f);
  fsl_size_t uuidLen = (uuid && db) ? fsl_strlen(uuid) : 0;
  if(!f || !uuid) return -1;
  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_UUID_STRLEN){
  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 = (uuidLen==FSL_UUID_STRLEN)
      /* Optimization for the common internally-used case */
    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_UUID_STRLEN, 0);
        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
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++){
  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
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 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);
  rc = fsl_buffer_resize(pCksum, FSL_UUIDv1_STRLEN);
  if(!rc){
    fsl_sha1_final(&ctx, zResult);
    fsl_sha1_digest_to_base16(zResult, fsl_buffer_str(pCksum));
    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[20];
    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
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_UUID_STRLEN);
    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;
    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
    rc = fsl_buffer_resize(pCksum, FSL_UUIDv1_STRLEN
                           /*resize() adds 1 for NUL*/);
    if(!rc){
      fsl_sha1_final(&ctx, zResult);
      fsl_sha1_digest_to_base16(zResult, fsl_buffer_str(pCksum));
      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;
    unsigned char zResult[20];
    char * zDigest = (char *)fsl_malloc(FSL_UUID_STRLEN+1);
    if(!zDigest) return NULL;
    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(&ctx, zResult);
    fsl_sha1_final_hex(&ctx, zHex);
    fsl_sha1_digest_to_base16(zResult, zDigest);
    return zDigest;
    return zHex;
  }
}
Changes to src/fsl_sha3.c.
31
32
33
34
35
36
37








38
39
40
41
42
43
44
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
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 *p, enum fsl_sha3_hash_size iSize){
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

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
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;
  if(uuid && !fsl_is_uuid(uuid)) return NULL;
  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, FSL_UUID_STRLEN);
      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
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);
    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
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
247
248
249
250
251
252
253


254
255
256
257
258
259
260







-
-







    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);
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
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.
      /* 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);
      uuid = fsl_stmt_g_text(q, 5, &nUuid);
      if(rc){
        if(FSL_RC_OOM != rc){
          fsl_cx_err_set(f, rc, errReadingSha1,
                         fsl_rc_cstr(rc), zName);
      assert(uuid && fsl_is_uuid(uuid)==(int)nUuid);
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);
        }
        goto end;
      if(rc) goto end;
      }
      assert(FSL_UUID_STRLEN==fileCksum->used);
      assert(fsl_is_uuid_len((int)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 */
         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 );
      fileCksum->used = 0;
      rc = fsl_sha1sum_filename(zName, fileCksum);
      uuid = fsl_stmt_g_text(q, 5, &nUuid);
      if(rc){
        if(FSL_RC_OOM != rc){
          fsl_cx_err_set(f, rc, errReadingSha1,
                         fsl_rc_cstr(rc), zName);
      assert(uuid && fsl_is_uuid(uuid)==(int)nUuid);
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);
        }
        goto end;
      if(rc) goto end;
      }
      assert(FSL_UUID_STRLEN==fileCksum->used);
      assert(fsl_is_uuid_len((int)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)
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
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;
  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);
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
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);
      uuid = fsl_stmt_g_text(&q, 5, &nUuid);
      if(rc){
        if(FSL_RC_OOM != rc){
          fsl_cx_err_set(f, rc, errReadingSha1,
                         fsl_rc_cstr(rc), zName);
      assert(uuid && fsl_is_uuid_len((int)nUuid));
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);
        }
        goto end;
      if(rc) goto end;
      }
      assert(FSL_UUID_STRLEN==fileCksum->used);
      assert(fsl_is_uuid_len((int)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);
      uuid = fsl_stmt_g_text(&q, 5, &nUuid);
      if(rc){
        if(FSL_RC_OOM != rc){
          fsl_cx_err_set(f, rc, errReadingSha1,
                         fsl_rc_cstr(rc), zName);
      assert(uuid && fsl_is_uuid_len((int)nUuid));
      rc = fsl_vfile_recheck_file_hash(f, zName, (int)nUuid, fileCksum);
        }
        goto end;
      if(rc) goto end;
      }
      assert(FSL_UUID_STRLEN==fileCksum->used);
      assert(fsl_is_uuid_len((int)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)