Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Sync with trunk. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | timeline-keyboard-navigation |
| Files: | files | file ages | folders |
| SHA3-256: |
00f7466addc2521ae42970338e447753 |
| User & Date: | florian 2024-09-06 10:47:00.000 |
Context
|
2024-12-12
| ||
| 16:58 | Sync with trunk. ... (check-in: 611f20e817 user: florian tags: timeline-keyboard-navigation) | |
|
2024-09-06
| ||
| 10:47 | Sync with trunk. ... (check-in: 00f7466add user: florian tags: timeline-keyboard-navigation) | |
|
2024-09-05
| ||
| 09:19 | In the 'unsaved changes' error of the patch command, make it explicit that the patch cannot be applied (because it otherwise comes across as informational, not an error). ... (check-in: 8c5faa368f user: stephan tags: trunk) | |
|
2024-01-22
| ||
| 13:39 | Sync with trunk. ... (check-in: f05b04a625 user: florian tags: timeline-keyboard-navigation) | |
Changes
Changes to VERSION.
|
| | | 1 | 2.25 |
Changes to auto.def.
| ︙ | ︙ | |||
34 35 36 37 38 39 40 |
compile-commands=0 =>
"Check for compile_commands.json support."
}
# Update the minimum required SQLite version number here, and also
# in src/main.c near the sqlite3_libversion_number() call. Take care
# that both places agree!
| | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
compile-commands=0 =>
"Check for compile_commands.json support."
}
# Update the minimum required SQLite version number here, and also
# in src/main.c near the sqlite3_libversion_number() call. Take care
# that both places agree!
define MINIMUM_SQLITE_VERSION "3.46.0"
# This is useful for people wanting Fossil to use an external SQLite library
# to compare the one they have against the minimum required
if {[opt-bool print-minimum-sqlite-version]} {
puts [get-define MINIMUM_SQLITE_VERSION]
exit 0
}
|
| ︙ | ︙ | |||
743 744 745 746 747 748 749 |
# cctest_function check for "getpass()" with no args and fail
if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} {
define FOSSIL_HAVE_GETPASS 1
msg-result "Found getpass() with unistd.h"
}
# Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
| | > < | 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 |
# cctest_function check for "getpass()" with no args and fail
if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} {
define FOSSIL_HAVE_GETPASS 1
msg-result "Found getpass() with unistd.h"
}
# Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
if {![cc-check-functions getloadavg] ||
![cctest -link 1 -includes {unistd.h} -code "double a\[3\]; getloadavg(a,3);"]} {
define FOSSIL_OMIT_LOAD_AVERAGE 1
msg-result "Load average support unavailable"
}
# Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
if {![cc-check-functions getpassphrase]} {
# Haiku needs this
cc-check-function-in-lib getpass bsd
}
cc-check-function-in-lib sin m
# Check for the FuseFS library
if {[opt-bool fusefs]} {
if {[opt-bool static]} {
msg-result "FuseFS support disabled due to -static"
} elseif {[cc-check-function-in-lib fuse_mount fuse]} {
define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS
define FOSSIL_HAVE_FUSEFS 1
msg-result "FuseFS support enabled"
}
}
########################################################################
# Checks the compiler for compile_commands.json support.
#
|
| ︙ | ︙ |
Changes to autosetup/README.autosetup.
|
| | | | 1 2 3 4 5 6 7 8 9 10 11 | README.autosetup created by autosetup v0.7.2 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. *.auto files in this directory are auto-loaded. For more information, see https://msteveb.github.io/autosetup/ |
Changes to autosetup/autosetup.
1 2 3 4 5 6 7 8 | #!/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 | | | 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.2
# 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 |
# 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 {
| | | | | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# At the is point we don't know what is a valid option
# We simply parse anything that looks like an option
set autosetup(getopt) [getopt argv]
#"=Core Options:"
options-add {
help:=all => "display help and options. Optional: module name, such as --help=system"
licence license => "display the autosetup license"
version => "display the version of autosetup"
ref:=text manual:=text
reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'"
debug => "display debugging output as autosetup runs"
install:=. => "install autosetup to the current or given directory"
}
if {$autosetup(installed)} {
# hidden options so we can produce a nice error
options-add {
sysinstall:path
}
} else {
|
| ︙ | ︙ | |||
200 201 202 203 204 205 206 |
} else {
user-error "Unexpected parameter: $arg"
}
}
autosetup_add_dep $autosetup(autodef)
| | | < < | < | | | > > | 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 234 235 236 237 238 239 240 241 |
} else {
user-error "Unexpected parameter: $arg"
}
}
autosetup_add_dep $autosetup(autodef)
# Add $argv to CONFIGURE_OPTS
define-append-argv CONFIGURE_OPTS {*}$autosetup(argv)
# Set up AUTOREMAKE to reconfigure with the same args
define-append-argv AUTOREMAKE {*}$autosetup(exe) {*}$autosetup(argv)
# Log how we were invoked
configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]"
configlog "Tclsh: [info nameofexecutable]"
# Load auto.def as module "auto.def"
autosetup_load_module auto.def source $autosetup(autodef)
# Could warn here if options {} was not specified
show-notices
if {$autosetup(debug)} {
msg-result "Writing all defines to config.log"
configlog "================ defines ======================"
foreach n [lsort [array names define]] {
configlog "define $n $define($n)"
}
}
exit 0
}
# @section Option Handling
# @opt-bool ?-nodefault? option ...
#
# Check each of the named, boolean options and if any have been explicitly enabled
# or disabled by the user, return 1 or 0 accordingly.
#
# If the option was specified more than once, the last value wins.
|
| ︙ | ︙ | |||
338 339 340 341 342 343 344 |
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 {
| | | | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
set result [lindex [dict get $::autosetup(optset) $opt] end]
}
}
if {![info exists result]} {
# No user-specified value. Has options-defaults been set?
foreach opt $names {
if {[dict exists $::autosetup(optdefault) $opt]} {
set result [dict get $autosetup(optdefault) $opt]
}
}
}
if {[info exists result]} {
set value $result
if {$retopt} {
|
| ︙ | ︙ | |||
371 372 373 374 375 376 377 | } } } # Parse the option definition in $opts and update # ::autosetup(setoptions) and ::autosetup(optionhelp) appropriately # | | | < | 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 396 397 398 399 400 |
}
}
}
# Parse the option definition in $opts and update
# ::autosetup(setoptions) and ::autosetup(optionhelp) appropriately
#
proc options-add {opts} {
global autosetup
# First weed out comment lines
set realopts {}
foreach line [split $opts \n] {
if {![string match "#*" [string trimleft $line]]} {
append realopts $line \n
}
}
set opts $realopts
for {set i 0} {$i < [llength $opts]} {incr i} {
set opt [lindex $opts $i]
if {[string match =* $opt]} {
# This is a special heading
lappend autosetup(optionhelp) [list $opt $autosetup(module)]
continue
}
unset -nocomplain defaultvalue equal value
#puts "i=$i, opt=$opt"
regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value
if {$name in $autosetup(options)} {
|
| ︙ | ︙ | |||
449 450 451 452 453 454 455 |
dict set autosetup(optset) $name $setvalue
#puts "Found boolean option --$name=$setvalue"
}
} else {
# String option.
lappend autosetup(options) $name
| | | | | | | 447 448 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 |
dict set autosetup(optset) $name $setvalue
#puts "Found boolean option --$name=$setvalue"
}
} else {
# String option.
lappend autosetup(options) $name
if {$equal ne "="} {
# Was the option given as "name:value=default"?
# If so, set $value to the display name and $defaultvalue to the default
# (This is the preferred way to set a default value for a string option)
if {[regexp {^([^=]+)=(.*)$} $value -> value defaultvalue]} {
dict set autosetup(optdefault) $name $defaultvalue
}
}
# Maybe override the default value
if {[dict exists $autosetup(options-defaults) $name]} {
# A default was specified with options-defaults, so use it
set defaultvalue [dict get $autosetup(options-defaults) $name]
dict set autosetup(optdefault) $name $defaultvalue
} elseif {![info exists defaultvalue]} {
# No default value was given by value=default or options-defaults
# so use the value as the default when the plain option with no
# value is given (.e.g. just --opt instead of --opt=value)
set defaultvalue $value
}
if {$equal eq "="} {
# String option with optional value
set opthelp "--$name?=$value?"
} else {
|
| ︙ | ︙ | |||
505 506 507 508 509 510 511 |
# 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]
}
| < < < < < | | < < < < | < < < < < < < < | 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 |
# Now create the help for this option if appropriate
if {[lindex $opts $i+1] eq "=>"} {
set desc [lindex $opts $i+2]
if {[info exists defaultvalue]} {
set desc [string map [list @default@ $defaultvalue] $desc]
}
# A multi-line description
lappend autosetup(optionhelp) [list $opthelp $autosetup(module) $desc]
incr i 2
}
}
}
# @module-options optionlist
#
# Deprecated. Simply use 'options' from within a module.
proc module-options {opts} {
options $opts
}
proc max {a b} {
expr {$a > $b ? $a : $b}
}
proc options-wrap-desc {text length firstprefix nextprefix initial} {
|
| ︙ | ︙ | |||
562 563 564 565 566 567 568 |
set space " "
}
if {$len} {
puts ""
}
}
| > > | > | > > > > | | > > > > > > > > > > | | | > > > > | > | | | < | > > | | | | | > | 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 647 648 649 650 651 652 653 654 655 656 |
set space " "
}
if {$len} {
puts ""
}
}
# Display options (from $autosetup(optionhelp)) for modules that match
# glob pattern $what
proc options-show {what} {
set local 0
# Determine the max option width
set max 0
foreach help $::autosetup(optionhelp) {
lassign $help opt module desc
if {![string match $what $module]} {
continue
}
if {[string match =* $opt] || [string match \n* $desc]} {
continue
}
set max [max $max [string length $opt]]
}
set indent [string repeat " " [expr {$max+4}]]
set cols [getenv COLUMNS 80]
catch {
lassign [exec stty size] rows cols
}
incr cols -1
# Now output
foreach help $::autosetup(optionhelp) {
lassign $help opt module desc
if {![string match $what $module]} {
continue
}
if {$local == 0 && $module eq "auto.def"} {
puts "Local Options:"
incr local
}
if {[string match =* $opt]} {
# Output a special heading line"
puts [string range $opt 1 end]
continue
}
puts -nonewline " [format %-${max}s $opt]"
if {[string match \n* $desc]} {
# Output a pre-formatted help description as-is
puts $desc
} else {
options-wrap-desc [string trim $desc] $cols " " $indent [expr {$max+2}]
}
}
}
# @options optionspec
#
# Specifies configuration-time options which may be selected by the user
# and checked with 'opt-str' and 'opt-bool'. '$optionspec' contains a series
# of options specifications separated by newlines, as follows:
#
# A boolean option is of the form:
#
## name[=0|1] => "Description of this boolean option"
#
# The default is 'name=0', meaning that the option is disabled by default.
# If 'name=1' is used to make the option enabled by default, the description should reflect
# that with text like "Disable support for ...".
#
# An argument option (one which takes a parameter) is of one of the following forms:
#
## name:value => "Description of this option"
## name:value=default => "Description of this option with a default value"
## name:=value => "Description of this option with an optional value"
#
# If the 'name:value' form is used, the value must be provided with the option (as '--name=myvalue').
# If the 'name:value=default' form is used, the option has the given default value even if not
# specified by the user.
# If the 'name:=value' form is used, the value is optional and the given value is used
# if it is not provided.
#
# The description may contain '@default@', in which case it will be replaced with the default
# value for the option (taking into account defaults specified with 'options-defaults'.
#
# Undocumented options are also supported by omitting the '=> description'.
# These options are not displayed with '--help' and can be useful for internal options or as aliases.
#
# For example, '--disable-lfs' is an alias for '--disable=largefile':
#
## lfs=1 largefile=1 => "Disable large file support"
#
proc options {optlist} {
global autosetup
options-add $optlist
if {$autosetup(showhelp)} {
# If --help, stop now to show help
return -code break
}
if {$autosetup(module) eq "auto.def"} {
# Check for invalid options
if {[opt-bool option-checking]} {
foreach o [dict keys $::autosetup(getopt)] {
if {$o ni $::autosetup(options)} {
user-error "Unknown option --$o"
}
}
}
}
}
# @options-defaults dictionary
#
|
| ︙ | ︙ | |||
675 676 677 678 679 680 681 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 |
if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.sub $alias} alias]} {
user-error $alias
}
}
return $alias
}
# @define name ?value=1?
#
# Defines the named variable to the given value.
# These (name, value) pairs represent the results of the configuration check
# and are available to be subsequently checked, modified and substituted.
#
proc define {name {value 1}} {
set ::define($name) $value
#dputs "$name <= $value"
}
# @undefine name
#
# Undefine the named variable.
#
proc undefine {name} {
unset -nocomplain ::define($name)
#dputs "$name <= <undef>"
}
# @define-append name value ...
#
# Appends the given value(s) to the given "defined" variable.
# If the variable is not defined or empty, it is set to '$value'.
# Otherwise the value is appended, separated by a space.
# Any extra values are similarly appended.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < | < > > > > > > > > > > > > > > > > > > > > > | 680 681 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 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 |
if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.sub $alias} alias]} {
user-error $alias
}
}
return $alias
}
# @section Variable Definitions (defines)
# @define name ?value=1?
#
# Defines the named variable to the given value.
# These (name, value) pairs represent the results of the configuration check
# and are available to be subsequently checked, modified and substituted.
#
proc define {name {value 1}} {
set ::define($name) $value
#dputs "$name <= $value"
}
# @define-push {name ...} script
#
# Save the values of the given defines, evaluation the script, then restore.
# For example, to avoid updating AS_FLAGS and AS_CXXFLAGS:
## define-push {AS_CFLAGS AS_CXXFLAGS} {
## cc-check-flags -Wno-error
## }
proc define-push {names script} {
array set unset {}
foreach name $names {
if {[is-defined $name]} {
set save($name) [get-define $name]
} else {
set unset($name) 1
}
}
uplevel 1 $script
array set ::define [array get save]
foreach name [array names unset] {
unset -nocomplain ::define($name)
}
}
# @undefine name
#
# Undefine the named variable.
#
proc undefine {name} {
unset -nocomplain ::define($name)
#dputs "$name <= <undef>"
}
# @define-append name value ...
#
# Appends the given value(s) to the given "defined" variable.
# If the variable is not defined or empty, it is set to '$value'.
# Otherwise the value is appended, separated by a space.
# Any extra values are similarly appended.
#
# Note that define-append is not designed to add values containing spaces.
# If values may contain spaces, consider define-append-argv instead.
#
proc define-append {name args} {
if {[get-define $name ""] ne ""} {
foreach arg $args {
if {$arg eq ""} {
continue
}
append ::define($name) " " $arg
}
} else {
set ::define($name) [join $args]
}
#dputs "$name += [join $args] => $::define($name)"
}
# @define-append-argv name value ...
#
# Similar to define-append except designed to construct shell command
# lines, including correct handling of parameters with spaces.
#
# Each non-empty value is quoted if necessary and then appended to the given variable
# if it does not already exist.
#
proc define-append-argv {name args} {
set seen {}
set new {}
foreach val [list {*}[get-define $name ""] {*}$args] {
if {$val ne {} && ![dict exists $seen $val]} {
lappend new [quote-if-needed $val]
dict set seen $val 1
}
}
set ::define($name) [join $new " "]
#dputs "$name += [join $args] => $::define($name)"
}
# @get-define name ?default=0?
#
# Returns the current value of the "defined" variable, or '$default'
# if not set.
#
proc get-define {name {default 0}} {
|
| ︙ | ︙ | |||
771 772 773 774 775 776 777 778 779 780 781 782 783 784 |
# This is suitable for use with 'dict', 'array set' or 'foreach'
# and allows for arbitrary processing of the defined variables.
#
proc all-defines {} {
array get ::define
}
# @get-env name default
#
# If '$name' was specified on the command line, return it.
# Otherwise if '$name' was set in the environment, return it.
# Otherwise return '$default'.
#
| > | 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 |
# This is suitable for use with 'dict', 'array set' or 'foreach'
# and allows for arbitrary processing of the defined variables.
#
proc all-defines {} {
array get ::define
}
# @section Environment/Helpers
# @get-env name default
#
# If '$name' was specified on the command line, return it.
# Otherwise if '$name' was set in the environment, return it.
# Otherwise return '$default'.
#
|
| ︙ | ︙ | |||
853 854 855 856 857 858 859 860 861 862 863 864 865 866 |
foreach p $list {
if {$p ne ""} {
lappend result $p
}
}
return $result
}
# @find-executable-path name
#
# Searches the path for an executable with the given name.
# Note that the name may include some parameters, e.g. 'cc -mbig-endian',
# in which case the parameters are ignored.
# The full path to the executable if found, or "" if not found.
| > > | 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 |
foreach p $list {
if {$p ne ""} {
lappend result $p
}
}
return $result
}
# @section Paths, Searching
# @find-executable-path name
#
# Searches the path for an executable with the given name.
# Note that the name may include some parameters, e.g. 'cc -mbig-endian',
# in which case the parameters are ignored.
# The full path to the executable if found, or "" if not found.
|
| ︙ | ︙ | |||
922 923 924 925 926 927 928 929 930 931 932 933 934 935 |
user-error "failed to find: [join $args]"
} else {
user-error "failed to find one of: [join $args]"
}
}
return ""
}
# @configlog msg
#
# Writes the given message to the configuration log, 'config.log'.
#
proc configlog {msg} {
if {![info exists ::autosetup(logfh)]} {
| > > | 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 |
user-error "failed to find: [join $args]"
} else {
user-error "failed to find one of: [join $args]"
}
}
return ""
}
# @section Logging, Messages and Errors
# @configlog msg
#
# Writes the given message to the configuration log, 'config.log'.
#
proc configlog {msg} {
if {![info exists ::autosetup(logfh)]} {
|
| ︙ | ︙ | |||
1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 |
# Add filename as a dependency to rerun autosetup
# The name will be normalised (converted to a full path)
#
proc autosetup_add_dep {filename} {
lappend ::autosetup(deps) [file-normalize $filename]
}
##################################################################
#
# Library module support
#
# @use module ...
| > > | 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 |
# Add filename as a dependency to rerun autosetup
# The name will be normalised (converted to a full path)
#
proc autosetup_add_dep {filename} {
lappend ::autosetup(deps) [file-normalize $filename]
}
# @section Modules Support
##################################################################
#
# Library module support
#
# @use module ...
|
| ︙ | ︙ | |||
1169 1170 1171 1172 1173 1174 1175 1176 |
lappend dirs $autosetup(srcdir)/autosetup
}
foreach m $args {
if {[info exists libmodule($m)]} {
continue
}
set libmodule($m) 1
if {[info exists modsource(${m}.tcl)]} {
| > | | | | > > > > | > > | 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 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 |
lappend dirs $autosetup(srcdir)/autosetup
}
foreach m $args {
if {[info exists libmodule($m)]} {
continue
}
set libmodule($m) 1
if {[info exists modsource(${m}.tcl)]} {
autosetup_load_module $m eval $modsource(${m}.tcl)
} else {
set locs [list ${m}.tcl ${m}/init.tcl]
set found 0
foreach dir $dirs {
foreach loc $locs {
set source $dir/$loc
if {[file exists $source]} {
incr found
break
}
}
if {$found} {
break
}
}
if {$found} {
# For the convenience of the "use" source, point to the directory
# it is being loaded from
set ::usedir [file dirname $source]
autosetup_load_module $m source $source
autosetup_add_dep $source
} else {
autosetup-error "use: No such module: $m"
}
}
}
}
proc autosetup_load_auto_modules {} {
global autosetup modsource
# First load any embedded auto modules
foreach mod [array names modsource *.auto] {
autosetup_load_module $mod eval $modsource($mod)
}
# Now any external auto modules
foreach file [glob -nocomplain $autosetup(libdir)/*.auto $autosetup(libdir)/*/*.auto] {
autosetup_load_module [file tail $file] source $file
}
}
# Load module source in the global scope by executing the given command
proc autosetup_load_module {module args} {
global autosetup
set prev $autosetup(module)
set autosetup(module) $module
if {[catch [list uplevel #0 $args] msg opts] ni {0 2 3}} {
autosetup-full-error [error-dump $msg $opts $::autosetup(debug)]
}
set autosetup(module) $prev
}
# Initial settings
set autosetup(exe) $::argv0
set autosetup(istcl) 1
set autosetup(start) [clock millis]
set autosetup(installed) 0
set autosetup(sysinstall) 0
set autosetup(msg-checking) 0
set autosetup(msg-quiet) 0
set autosetup(inittypes) {}
set autosetup(module) autosetup
# Embedded modules are inserted below here
set autosetup(installed) 1
set autosetup(sysinstall) 0
# ----- @module asciidoc-formatting.tcl -----
set modsource(asciidoc-formatting.tcl) {
|
| ︙ | ︙ | |||
1430 1431 1432 1433 1434 1435 1436 |
# Module which provides usage, help and the command reference
proc autosetup_help {what} {
use_pager
puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n"
puts "This is [autosetup_version], a build environment \"autoconfigurator\""
| | | > | < | > > > < > < | < < | | < > | 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 |
# Module which provides usage, help and the command reference
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 https://msteveb.github.io/autosetup/\n"
if {$what in {all local}} {
# Need to load auto.def now
if {[file exists $::autosetup(autodef)]} {
# Load auto.def as module "auto.def"
autosetup_load_module auto.def source $::autosetup(autodef)
}
if {$what eq "all"} {
set what *
} else {
set what auto.def
}
} else {
use $what
puts "Options for module $what:"
}
options-show $what
exit 0
}
proc autosetup_show_license {} {
global modsource autosetup
use_pager
|
| ︙ | ︙ | |||
1503 1504 1505 1506 1507 1508 1509 |
}
title "[autosetup_version] -- Command Reference"
section {Introduction}
p {
| | > | 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 |
}
title "[autosetup_version] -- Command Reference"
section {Introduction}
p {
See https://msteveb.github.io/autosetup/ for the online documentation for 'autosetup'.
This documentation can also be accessed locally with `autosetup --ref`.
}
p {
'autosetup' provides a number of built-in commands which
are documented below. These may be used from 'auto.def' to test
for features, define variables, create files from templates and
other similar actions.
|
| ︙ | ︙ | |||
1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 |
foreach file $files {
set modulename [file rootname [file tail $file]]
set current _core_
set f [open $file]
while {![eof $f]} {
set line [gets $f]
# Find embedded module names
if {[regexp {^#.*@module ([^ ]*)} $line -> modulename]} {
continue
}
# Find lines starting with "# @*" and continuing through the remaining comment lines
if {![regexp {^# @(.*)} $line -> cmd]} {
continue
}
# Synopsis or command?
if {$cmd eq "synopsis:"} {
set current $modulename
lappend doc($current) [list section "Module: $modulename"]
| > > > > > | | 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 |
foreach file $files {
set modulename [file rootname [file tail $file]]
set current _core_
set f [open $file]
while {![eof $f]} {
set line [gets $f]
if {[regexp {^#.*@section (.*)$} $line -> section]} {
lappend doc($current) [list section $section]
continue
}
# Find embedded module names
if {[regexp {^#.*@module ([^ ]*)} $line -> modulename]} {
continue
}
# Find lines starting with "# @*" and continuing through the remaining comment lines
if {![regexp {^# @(.*)} $line -> cmd]} {
continue
}
# Synopsis or command?
if {$cmd eq "synopsis:"} {
set current $modulename
lappend doc($current) [list section "Module: $modulename"]
} else {
lappend doc($current) [list subsection $cmd]
}
set lines {}
set type p
# Now the description
|
| ︙ | ︙ | |||
1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 |
{#!/bin/sh
WRAPPER="$0"; export WRAPPER; "autosetup" "$@"
}
} else {
writefile configure \
{#!/bin/sh
dir="`dirname "$0"`/autosetup"
WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@"
}
}
catch {exec chmod 755 configure}
}
# Append the contents of $file to filehandle $f
| > | 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 |
{#!/bin/sh
WRAPPER="$0"; export WRAPPER; "autosetup" "$@"
}
} else {
writefile configure \
{#!/bin/sh
dir="`dirname "$0"`/autosetup"
#@@INITCHECK@@#
WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@"
}
}
catch {exec chmod 755 configure}
}
# Append the contents of $file to filehandle $f
|
| ︙ | ︙ | |||
1907 1908 1909 1910 1911 1912 1913 |
append readme {
*.tcl files in this directory are optional modules which
can be loaded with the 'use' directive.
*.auto files in this directory are auto-loaded.
| | | 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 |
append readme {
*.tcl files in this directory are optional modules which
can be loaded with the 'use' directive.
*.auto files in this directory are auto-loaded.
For more information, see https://msteveb.github.io/autosetup/
}
dputs "install: autosetup/README.autosetup"
writefile $target $readme
}
}
# ----- @module markdown-formatting.tcl -----
|
| ︙ | ︙ | |||
2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 |
return [lindex $args 0]
}
return -code error "environment variable \"$name\" does not exist"
}
proc isatty? {channel} {
dict exists [fconfigure $channel] -xchar
}
} else {
if {$autosetup(iswin)} {
# On Windows, backslash convert all environment variables
# (Assume that Tcl does this for us)
proc getenv {name args} {
string map {\\ /} [env $name {*}$args]
}
| > > > > > > > > > > > > > | 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 |
return [lindex $args 0]
}
return -code error "environment variable \"$name\" does not exist"
}
proc isatty? {channel} {
dict exists [fconfigure $channel] -xchar
}
# Jim-compatible stacktrace using info frame
proc stacktrace {} {
set stacktrace {}
# 2 to skip the current frame
for {set i 2} {$i < [info frame]} {incr i} {
set frame [info frame -$i]
if {[dict exists $frame file]} {
# We don't need proc, so use ""
lappend stacktrace "" [dict get $frame file] [dict get $frame line]
}
}
return $stacktrace
}
} else {
if {$autosetup(iswin)} {
# On Windows, backslash convert all environment variables
# (Assume that Tcl does this for us)
proc getenv {name args} {
string map {\\ /} [env $name {*}$args]
}
|
| ︙ | ︙ | |||
2094 2095 2096 2097 2098 2099 2100 |
# This is designed to be called for incorrect usage in auto.def, via autosetup-error
#
proc error-location {msg} {
if {$::autosetup(debug)} {
return -code error $msg
}
# Search back through the stack trace for the first error in a .def file
| < < < | < < | | | | 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 |
# This is designed to be called for incorrect usage in auto.def, via autosetup-error
#
proc error-location {msg} {
if {$::autosetup(debug)} {
return -code error $msg
}
# Search back through the stack trace for the first error in a .def file
foreach {p f l} [stacktrace] {
if {[string match *.def $f]} {
return "[relative-path $f]:$l: Error: $msg"
}
#puts "Skipping $f:$l"
}
return $msg
}
# If everything is working properly, the only errors which occur
# should be generated in user code (e.g. auto.def).
# By default, we only want to show the error location in user code.
|
| ︙ | ︙ | |||
2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 |
# ----- @module util.tcl -----
set modsource(util.tcl) {
# Copyright (c) 2012 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# Module which contains miscellaneous utility functions
# @compare-versions version1 version2
#
# Versions are of the form 'a.b.c' (may be any number of numeric components)
#
# Compares the two versions and returns:
## -1 if v1 < v2
| > > | 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 |
# ----- @module util.tcl -----
set modsource(util.tcl) {
# Copyright (c) 2012 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# Module which contains miscellaneous utility functions
# @section Utilities
# @compare-versions version1 version2
#
# Versions are of the form 'a.b.c' (may be any number of numeric components)
#
# Compares the two versions and returns:
## -1 if v1 < v2
|
| ︙ | ︙ |
Changes to autosetup/autosetup-config.guess.
1 2 | #! /bin/sh # Attempt to guess a canonical system name. | | > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2021 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2021-06-03' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but |
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
# the same distribution terms that you use for the rest of that
# program. This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
#
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
| | > > > > > > > > | | 25 26 27 28 29 30 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 |
# the same distribution terms that you use for the rest of that
# program. This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
#
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
#
# Please send patches to <config-patches@gnu.org>.
# The "shellcheck disable" line above the timestamp inhibits complaints
# about features and limitations of the classic Bourne shell that were
# superseded or lifted in POSIX. However, this script identifies a wide
# variety of pre-POSIX systems that do not have POSIX shells at all, and
# even some reasonably current systems (Solaris 10 as case-in-point) still
# have a pre-POSIX /bin/sh.
me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
Usage: $0 [OPTION]
Output the configuration name of the system \`$me' is run on.
Options:
-h, --help print this help, then exit
-t, --time-stamp print date of last modification, then exit
-v, --version print version number, then exit
Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
Copyright 1992-2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help="
Try \`$me --help' for more information."
|
| ︙ | ︙ | |||
80 81 82 83 84 85 86 | done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi | | > > > > > | < < > > | > | | | | | < | | | | | > | | | | | | | | | | > | | | < < | | > > > > > | > | > | > | | < | > > > > > > | < | | | > | | | | | | | | | | > > > | > | | | | | | | | | > > > | | | | | | > > > | | | | > > | | 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 134 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 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 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 |
done
if test $# != 0; then
echo "$me: too many arguments$help" >&2
exit 1
fi
# Just in case it came from the environment.
GUESS=
# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
# compiler to aid in system detection is discouraged as it requires
# temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion.
# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
# use `HOST_CC' if defined, but it is deprecated.
# Portable tmp directory creation inspired by the Autoconf team.
tmp=
# shellcheck disable=SC2172
trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
set_cc_for_build() {
# prevent multiple calls if $tmp is already set
test "$tmp" && return 0
: "${TMPDIR=/tmp}"
# shellcheck disable=SC2039,SC3028
{ tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
dummy=$tmp/dummy
case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
,,) echo "int x;" > "$dummy.c"
for driver in cc gcc c89 c99 ; do
if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
CC_FOR_BUILD=$driver
break
fi
done
if test x"$CC_FOR_BUILD" = x ; then
CC_FOR_BUILD=no_compiler_found
fi
;;
,,*) CC_FOR_BUILD=$CC ;;
,*,*) CC_FOR_BUILD=$HOST_CC ;;
esac
}
# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
# (ghazi@noc.rutgers.edu 1994-08-24)
if test -f /.attbin/uname ; then
PATH=$PATH:/.attbin ; export PATH
fi
UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
case $UNAME_SYSTEM in
Linux|GNU|GNU/*)
LIBC=unknown
set_cc_for_build
cat <<-EOF > "$dummy.c"
#include <features.h>
#if defined(__UCLIBC__)
LIBC=uclibc
#elif defined(__dietlibc__)
LIBC=dietlibc
#elif defined(__GLIBC__)
LIBC=gnu
#else
#include <stdarg.h>
/* First heuristic to detect musl libc. */
#ifdef __DEFINED_va_list
LIBC=musl
#endif
#endif
EOF
cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
eval "$cc_set_libc"
# Second heuristic to detect musl libc.
if [ "$LIBC" = unknown ] &&
command -v ldd >/dev/null &&
ldd --version 2>&1 | grep -q ^musl; then
LIBC=musl
fi
# If the system lacks a compiler, then just pick glibc.
# We could probably try harder.
if [ "$LIBC" = unknown ]; then
LIBC=gnu
fi
;;
esac
# Note: order is significant - the case branches are not exclusive.
case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
*:NetBSD:*:*)
# NetBSD (nbsd) targets should (where applicable) match one or
# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
# compatibility and a consistent mechanism for selecting the
# object file format.
#
# Note: NetBSD doesn't particularly care about the vendor
# portion of the name. We always set it to "unknown".
UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
/usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
echo unknown)`
case $UNAME_MACHINE_ARCH in
aarch64eb) machine=aarch64_be-unknown ;;
armeb) machine=armeb-unknown ;;
arm*) machine=arm-unknown ;;
sh3el) machine=shl-unknown ;;
sh3eb) machine=sh-unknown ;;
sh5el) machine=sh5le-unknown ;;
earmv*)
arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
machine=${arch}${endian}-unknown
;;
*) machine=$UNAME_MACHINE_ARCH-unknown ;;
esac
# The Operating System including object format, if it has switched
# to ELF recently (or will in the future) and ABI.
case $UNAME_MACHINE_ARCH in
earm*)
os=netbsdelf
;;
arm*|i386|m68k|ns32k|sh3*|sparc|vax)
set_cc_for_build
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ELF__
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
os=netbsd
else
os=netbsdelf
fi
;;
*)
os=netbsd
;;
esac
# Determine ABI tags.
case $UNAME_MACHINE_ARCH in
earm*)
expr='s/^earmv[0-9]/-eabi/;s/eb$//'
abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
;;
esac
# The OS release
# Debian GNU/NetBSD machines have a different userland, and
# thus, need a distinct triplet. However, they do not need
# kernel version information, so it can be replaced with a
# suitable tag, in the style of linux-gnu.
case $UNAME_VERSION in
Debian*)
release='-gnu'
;;
*)
release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
;;
esac
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
# contains redundant information, the shorter form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
GUESS=$machine-${os}${release}${abi-}
;;
*:Bitrig:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE
;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE
;;
*:SecBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'`
GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE
;;
*:LibertyBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE
;;
*:MidnightBSD:*:*)
GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE
;;
*:ekkoBSD:*:*)
GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE
;;
*:SolidBSD:*:*)
GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE
;;
*:OS108:*:*)
GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE
;;
macppc:MirBSD:*:*)
GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE
;;
*:MirBSD:*:*)
GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE
;;
*:Sortix:*:*)
GUESS=$UNAME_MACHINE-unknown-sortix
;;
*:Twizzler:*:*)
GUESS=$UNAME_MACHINE-unknown-twizzler
;;
*:Redox:*:*)
GUESS=$UNAME_MACHINE-unknown-redox
;;
mips:OSF1:*.*)
GUESS=mips-dec-osf1
;;
alpha:OSF1:*:*)
# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
trap '' 0
case $UNAME_RELEASE in
*4.0)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
# OSF/1 and Tru64 systems produced since 1995. I hope that
# covers most systems running today. This code pipes the CPU
# types through head -n 1, so we only detect the type of CPU 0.
ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
case $ALPHA_CPU_TYPE in
"EV4 (21064)")
UNAME_MACHINE=alpha ;;
"EV4.5 (21064)")
UNAME_MACHINE=alpha ;;
"LCA4 (21066/21068)")
UNAME_MACHINE=alpha ;;
"EV5 (21164)")
|
| ︙ | ︙ | |||
322 323 324 325 326 327 328 | UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. | | | < < | | | | | | | | | | | | | | | | | | | | | < | < > | | | | | | | > | > | | > | | > | | | | | | > | | > | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 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 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 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 |
UNAME_MACHINE=alphaev79 ;;
esac
# A Pn.n version is a patched version.
# A Vn.n version is a released version.
# A Tn.n version is a released field test version.
# A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r.
OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
GUESS=$UNAME_MACHINE-dec-osf$OSF_REL
;;
Amiga*:UNIX_System_V:4.0:*)
GUESS=m68k-unknown-sysv4
;;
*:[Aa]miga[Oo][Ss]:*:*)
GUESS=$UNAME_MACHINE-unknown-amigaos
;;
*:[Mm]orph[Oo][Ss]:*:*)
GUESS=$UNAME_MACHINE-unknown-morphos
;;
*:OS/390:*:*)
GUESS=i370-ibm-openedition
;;
*:z/VM:*:*)
GUESS=s390-ibm-zvmoe
;;
*:OS400:*:*)
GUESS=powerpc-ibm-os400
;;
arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
GUESS=arm-acorn-riscix$UNAME_RELEASE
;;
arm*:riscos:*:*|arm*:RISCOS:*:*)
GUESS=arm-unknown-riscos
;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
GUESS=hppa1.1-hitachi-hiuxmpp
;;
Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
case `(/bin/universe) 2>/dev/null` in
att) GUESS=pyramid-pyramid-sysv3 ;;
*) GUESS=pyramid-pyramid-bsd ;;
esac
;;
NILE*:*:*:dcosx)
GUESS=pyramid-pyramid-svr4
;;
DRS?6000:unix:4.0:6*)
GUESS=sparc-icl-nx6
;;
DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
case `/usr/bin/uname -p` in
sparc) GUESS=sparc-icl-nx7 ;;
esac
;;
s390x:SunOS:*:*)
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL
;;
sun4H:SunOS:5.*:*)
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=sparc-hal-solaris2$SUN_REL
;;
sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=sparc-sun-solaris2$SUN_REL
;;
i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
GUESS=i386-pc-auroraux$UNAME_RELEASE
;;
i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
set_cc_for_build
SUN_ARCH=i386
# If there is a compiler, see if it is configured for 64-bit objects.
# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
# This test works for both compilers.
if test "$CC_FOR_BUILD" != no_compiler_found; then
if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
SUN_ARCH=x86_64
fi
fi
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=$SUN_ARCH-pc-solaris2$SUN_REL
;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
# SunOS6. Hard to guess exactly what SunOS6 will be like, but
# it's likely to be more like Solaris than SunOS4.
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=sparc-sun-solaris3$SUN_REL
;;
sun4*:SunOS:*:*)
case `/usr/bin/arch -k` in
Series*|S4*)
UNAME_RELEASE=`uname -v`
;;
esac
# Japanese Language versions have a version number like `4.1.3-JL'.
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
GUESS=sparc-sun-sunos$SUN_REL
;;
sun3*:SunOS:*:*)
GUESS=m68k-sun-sunos$UNAME_RELEASE
;;
sun*:*:4.2BSD:*)
UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
case `/bin/arch` in
sun3)
GUESS=m68k-sun-sunos$UNAME_RELEASE
;;
sun4)
GUESS=sparc-sun-sunos$UNAME_RELEASE
;;
esac
;;
aushp:SunOS:*:*)
GUESS=sparc-auspex-sunos$UNAME_RELEASE
;;
# The situation for MiNT is a little confusing. The machine name
# can be virtually everything (everything which is not
# "atarist" or "atariste" at least should have a processor
# > m68000). The system name ranges from "MiNT" over "FreeMiNT"
# to the lowercase version "mint" (or "freemint"). Finally
# the system name "TOS" denotes a system which is actually not
# MiNT. But MiNT is downward compatible to TOS, so this should
# be no problem.
atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
GUESS=m68k-atari-mint$UNAME_RELEASE
;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
GUESS=m68k-atari-mint$UNAME_RELEASE
;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
GUESS=m68k-atari-mint$UNAME_RELEASE
;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
GUESS=m68k-milan-mint$UNAME_RELEASE
;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
GUESS=m68k-hades-mint$UNAME_RELEASE
;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
GUESS=m68k-unknown-mint$UNAME_RELEASE
;;
m68k:machten:*:*)
GUESS=m68k-apple-machten$UNAME_RELEASE
;;
powerpc:machten:*:*)
GUESS=powerpc-apple-machten$UNAME_RELEASE
;;
RISC*:Mach:*:*)
GUESS=mips-dec-mach_bsd4.3
;;
RISC*:ULTRIX:*:*)
GUESS=mips-dec-ultrix$UNAME_RELEASE
;;
VAX*:ULTRIX*:*:*)
GUESS=vax-dec-ultrix$UNAME_RELEASE
;;
2020:CLIX:*:* | 2430:CLIX:*:*)
GUESS=clipper-intergraph-clix$UNAME_RELEASE
;;
mips:*:*:UMIPS | mips:*:*:RISCos)
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#ifdef __cplusplus
#include <stdio.h> /* for printf() prototype */
int main (int argc, char *argv[]) {
#else
int main (argc, argv) int argc; char *argv[]; {
#endif
|
| ︙ | ︙ | |||
504 505 506 507 508 509 510 |
exit (-1);
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
{ echo "$SYSTEM_NAME"; exit; }
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 |
exit (-1);
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
{ echo "$SYSTEM_NAME"; exit; }
GUESS=mips-mips-riscos$UNAME_RELEASE
;;
Motorola:PowerMAX_OS:*:*)
GUESS=powerpc-motorola-powermax
;;
Motorola:*:4.3:PL8-*)
GUESS=powerpc-harris-powermax
;;
Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
GUESS=powerpc-harris-powermax
;;
Night_Hawk:Power_UNIX:*:*)
GUESS=powerpc-harris-powerunix
;;
m88k:CX/UX:7*:*)
GUESS=m88k-harris-cxux7
;;
m88k:*:4*:R4*)
GUESS=m88k-motorola-sysv4
;;
m88k:*:3*:R3*)
GUESS=m88k-motorola-sysv3
;;
AViiON:dgux:*:*)
# DG/UX returns AViiON for all architectures
UNAME_PROCESSOR=`/usr/bin/uname -p`
if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
then
if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
test "$TARGET_BINARY_INTERFACE"x = x
then
GUESS=m88k-dg-dgux$UNAME_RELEASE
else
GUESS=m88k-dg-dguxbcs$UNAME_RELEASE
fi
else
GUESS=i586-dg-dgux$UNAME_RELEASE
fi
;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
GUESS=m88k-dolphin-sysv3
;;
M88*:*:R3*:*)
# Delta 88k system running SVR3
GUESS=m88k-motorola-sysv3
;;
XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
GUESS=m88k-tektronix-sysv3
;;
Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
GUESS=m68k-tektronix-bsd
;;
*:IRIX*:*:*)
IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'`
GUESS=mips-sgi-irix$IRIX_REL
;;
????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id
;; # Note that: echo "'`uname -s`'" gives 'AIX '
i*86:AIX:*:*)
GUESS=i386-ibm-aix
;;
ia64:AIX:*:*)
if test -x /usr/bin/oslevel ; then
IBM_REV=`/usr/bin/oslevel`
else
IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
fi
GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV
;;
*:AIX:2:3)
if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#include <sys/systemcfg.h>
main()
{
if (!__power_pc())
exit(1);
puts("powerpc-ibm-aix3.2.5");
exit(0);
}
EOF
if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
then
GUESS=$SYSTEM_NAME
else
GUESS=rs6000-ibm-aix3.2.5
fi
elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
GUESS=rs6000-ibm-aix3.2.4
else
GUESS=rs6000-ibm-aix3.2
fi
;;
*:AIX:*:[4567])
IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
IBM_ARCH=rs6000
else
IBM_ARCH=powerpc
fi
if test -x /usr/bin/lslpp ; then
IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \
awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
else
IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
fi
GUESS=$IBM_ARCH-ibm-aix$IBM_REV
;;
*:AIX:*:*)
GUESS=rs6000-ibm-aix
;;
ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
GUESS=romp-ibm-bsd4.4
;;
ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to
;; # report: romp-ibm BSD 4.3
*:BOSX:*:*)
GUESS=rs6000-bull-bosx
;;
DPX/2?00:B.O.S.:*:*)
GUESS=m68k-bull-sysv3
;;
9000/[34]??:4.3bsd:1.*:*)
GUESS=m68k-hp-bsd
;;
hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
GUESS=m68k-hp-bsd4.4
;;
9000/[34678]??:HP-UX:*:*)
HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
case $UNAME_MACHINE in
9000/31?) HP_ARCH=m68000 ;;
9000/[34]??) HP_ARCH=m68k ;;
9000/[678][0-9][0-9])
if test -x /usr/bin/getconf; then
sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
case $sc_cpu_version in
523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
532) # CPU_PA_RISC2_0
case $sc_kernel_bits in
32) HP_ARCH=hppa2.0n ;;
64) HP_ARCH=hppa2.0w ;;
'') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
esac ;;
esac
fi
if test "$HP_ARCH" = ""; then
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#define _HPUX_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main ()
|
| ︙ | ︙ | |||
694 695 696 697 698 699 700 | exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac | | | | | | | | | | 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 |
exit (0);
}
EOF
(CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
test -z "$HP_ARCH" && HP_ARCH=hppa
fi ;;
esac
if test "$HP_ARCH" = hppa2.0w
then
set_cc_for_build
# hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
# 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
# generating 64-bit code. GNU and HP use different nomenclature:
#
# $ CC_FOR_BUILD=cc ./config.guess
# => hppa2.0w-hp-hpux11.23
# $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
# => hppa64-hp-hpux11.23
if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
grep -q __LP64__
then
HP_ARCH=hppa2.0w
else
HP_ARCH=hppa64
fi
fi
GUESS=$HP_ARCH-hp-hpux$HPUX_REV
;;
ia64:HP-UX:*:*)
HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
GUESS=ia64-hp-hpux$HPUX_REV
;;
3050*:HI-UX:*:*)
set_cc_for_build
sed 's/^ //' << EOF > "$dummy.c"
#include <unistd.h>
int
main ()
{
long cpu = sysconf (_SC_CPU_VERSION);
/* The order matters, because CPU_IS_HP_MC68K erroneously returns
|
| ︙ | ︙ | |||
750 751 752 753 754 755 756 |
puts ("m68k-hitachi-hiuxwe2");
else puts ("unknown-hitachi-hiuxwe2");
exit (0);
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
{ echo "$SYSTEM_NAME"; exit; }
| | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | > | | > | | > | | > | | | | | | | | | > > > > > > > > | > > > > > | | | > | | | | | | | | | | | | | | | | | | | | | | > | | | > > | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | | | | | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > < > < > | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 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 934 935 936 937 938 939 940 941 942 943 944 945 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 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 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 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 |
puts ("m68k-hitachi-hiuxwe2");
else puts ("unknown-hitachi-hiuxwe2");
exit (0);
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
{ echo "$SYSTEM_NAME"; exit; }
GUESS=unknown-hitachi-hiuxwe2
;;
9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
GUESS=hppa1.1-hp-bsd
;;
9000/8??:4.3bsd:*:*)
GUESS=hppa1.0-hp-bsd
;;
*9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
GUESS=hppa1.0-hp-mpeix
;;
hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
GUESS=hppa1.1-hp-osf
;;
hp8??:OSF1:*:*)
GUESS=hppa1.0-hp-osf
;;
i*86:OSF1:*:*)
if test -x /usr/sbin/sysversion ; then
GUESS=$UNAME_MACHINE-unknown-osf1mk
else
GUESS=$UNAME_MACHINE-unknown-osf1
fi
;;
parisc*:Lites*:*:*)
GUESS=hppa1.1-hp-lites
;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
GUESS=c1-convex-bsd
;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
GUESS=c34-convex-bsd
;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
GUESS=c38-convex-bsd
;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
GUESS=c4-convex-bsd
;;
CRAY*Y-MP:*:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=ymp-cray-unicos$CRAY_REL
;;
CRAY*[A-Z]90:*:*:*)
echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
-e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
-e 's/\.[^.]*$/.X/'
exit ;;
CRAY*TS:*:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=t90-cray-unicos$CRAY_REL
;;
CRAY*T3E:*:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=alphaev5-cray-unicosmk$CRAY_REL
;;
CRAY*SV1:*:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=sv1-cray-unicos$CRAY_REL
;;
*:UNICOS/mp:*:*)
CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
GUESS=craynv-cray-unicosmp$CRAY_REL
;;
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
;;
5000:UNIX_System_V:4.*:*)
FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
;;
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE
;;
sparc*:BSD/OS:*:*)
GUESS=sparc-unknown-bsdi$UNAME_RELEASE
;;
*:BSD/OS:*:*)
GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE
;;
arm:FreeBSD:*:*)
UNAME_PROCESSOR=`uname -p`
set_cc_for_build
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_PCS_VFP
then
FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
else
FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
fi
;;
*:FreeBSD:*:*)
UNAME_PROCESSOR=`/usr/bin/uname -p`
case $UNAME_PROCESSOR in
amd64)
UNAME_PROCESSOR=x86_64 ;;
i386)
UNAME_PROCESSOR=i586 ;;
esac
FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL
;;
i*:CYGWIN*:*)
GUESS=$UNAME_MACHINE-pc-cygwin
;;
*:MINGW64*:*)
GUESS=$UNAME_MACHINE-pc-mingw64
;;
*:MINGW*:*)
GUESS=$UNAME_MACHINE-pc-mingw32
;;
*:MSYS*:*)
GUESS=$UNAME_MACHINE-pc-msys
;;
i*:PW*:*)
GUESS=$UNAME_MACHINE-pc-pw32
;;
*:Interix*:*)
case $UNAME_MACHINE in
x86)
GUESS=i586-pc-interix$UNAME_RELEASE
;;
authenticamd | genuineintel | EM64T)
GUESS=x86_64-unknown-interix$UNAME_RELEASE
;;
IA64)
GUESS=ia64-unknown-interix$UNAME_RELEASE
;;
esac ;;
i*:UWIN*:*)
GUESS=$UNAME_MACHINE-pc-uwin
;;
amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
GUESS=x86_64-pc-cygwin
;;
prep*:SunOS:5.*:*)
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
GUESS=powerpcle-unknown-solaris2$SUN_REL
;;
*:GNU:*:*)
# the GNU system
GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'`
GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'`
GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL
;;
*:GNU/*:*:*)
# other systems with GNU libc and userland
GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
;;
*:Minix:*:*)
GUESS=$UNAME_MACHINE-unknown-minix
;;
aarch64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
aarch64_be:Linux:*:*)
UNAME_MACHINE=aarch64_be
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
alpha:Linux:*:*)
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
EV5) UNAME_MACHINE=alphaev5 ;;
EV56) UNAME_MACHINE=alphaev56 ;;
PCA56) UNAME_MACHINE=alphapca56 ;;
PCA57) UNAME_MACHINE=alphapca56 ;;
EV6) UNAME_MACHINE=alphaev6 ;;
EV67) UNAME_MACHINE=alphaev67 ;;
EV68*) UNAME_MACHINE=alphaev68 ;;
esac
objdump --private-headers /bin/sh | grep -q ld.so.1
if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
arm*:Linux:*:*)
set_cc_for_build
if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_EABI__
then
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
else
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_PCS_VFP
then
GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi
else
GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf
fi
fi
;;
avr32*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
cris:Linux:*:*)
GUESS=$UNAME_MACHINE-axis-linux-$LIBC
;;
crisv32:Linux:*:*)
GUESS=$UNAME_MACHINE-axis-linux-$LIBC
;;
e2k:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
frv:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
hexagon:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
i*86:Linux:*:*)
GUESS=$UNAME_MACHINE-pc-linux-$LIBC
;;
ia64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
k1om:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
m32r*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
m68*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
mips:Linux:*:* | mips64:Linux:*:*)
set_cc_for_build
IS_GLIBC=0
test x"${LIBC}" = xgnu && IS_GLIBC=1
sed 's/^ //' << EOF > "$dummy.c"
#undef CPU
#undef mips
#undef mipsel
#undef mips64
#undef mips64el
#if ${IS_GLIBC} && defined(_ABI64)
LIBCABI=gnuabi64
#else
#if ${IS_GLIBC} && defined(_ABIN32)
LIBCABI=gnuabin32
#else
LIBCABI=${LIBC}
#endif
#endif
#if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
CPU=mipsisa64r6
#else
#if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
CPU=mipsisa32r6
#else
#if defined(__mips64)
CPU=mips64
#else
CPU=mips
#endif
#endif
#endif
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
MIPS_ENDIAN=el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
MIPS_ENDIAN=
#else
MIPS_ENDIAN=
#endif
#endif
EOF
cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`
eval "$cc_set_vars"
test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
;;
mips64el:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
openrisc*:Linux:*:*)
GUESS=or1k-unknown-linux-$LIBC
;;
or32:Linux:*:* | or1k*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
padre:Linux:*:*)
GUESS=sparc-unknown-linux-$LIBC
;;
parisc64:Linux:*:* | hppa64:Linux:*:*)
GUESS=hppa64-unknown-linux-$LIBC
;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;;
PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;;
*) GUESS=hppa-unknown-linux-$LIBC ;;
esac
;;
ppc64:Linux:*:*)
GUESS=powerpc64-unknown-linux-$LIBC
;;
ppc:Linux:*:*)
GUESS=powerpc-unknown-linux-$LIBC
;;
ppc64le:Linux:*:*)
GUESS=powerpc64le-unknown-linux-$LIBC
;;
ppcle:Linux:*:*)
GUESS=powerpcle-unknown-linux-$LIBC
;;
riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
s390:Linux:*:* | s390x:Linux:*:*)
GUESS=$UNAME_MACHINE-ibm-linux-$LIBC
;;
sh64*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
sh*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
sparc:Linux:*:* | sparc64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
tile*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
vax:Linux:*:*)
GUESS=$UNAME_MACHINE-dec-linux-$LIBC
;;
x86_64:Linux:*:*)
set_cc_for_build
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_X32 >/dev/null
then
LIBCABI=${LIBC}x32
fi
fi
GUESS=$UNAME_MACHINE-pc-linux-$LIBCABI
;;
xtensa*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
i*86:DYNIX/ptx:4*:*)
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
# earlier versions are messed up and put the nodename in both
# sysname and nodename.
GUESS=i386-sequent-sysv4
;;
i*86:UNIX_SV:4.2MP:2.*)
# Unixware is an offshoot of SVR4, but it has its own version
# number series starting with 2...
# I am not positive that other SVR4 systems won't match this,
# I just have to hope. -- rms.
# Use sysv4.2uw... so that sysv4* matches it.
GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
;;
i*86:OS/2:*:*)
# If we were able to find `uname', then EMX Unix compatibility
# is probably installed.
GUESS=$UNAME_MACHINE-pc-os2-emx
;;
i*86:XTS-300:*:STOP)
GUESS=$UNAME_MACHINE-unknown-stop
;;
i*86:atheos:*:*)
GUESS=$UNAME_MACHINE-unknown-atheos
;;
i*86:syllable:*:*)
GUESS=$UNAME_MACHINE-pc-syllable
;;
i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
GUESS=i386-unknown-lynxos$UNAME_RELEASE
;;
i*86:*DOS:*:*)
GUESS=$UNAME_MACHINE-pc-msdosdjgpp
;;
i*86:*:4.*:*)
UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL
else
GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL
fi
;;
i*86:*:5:[678]*)
# UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
*Pent*|*Celeron) UNAME_MACHINE=i686 ;;
esac
GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
;;
i*86:*:3.2:*)
if test -f /usr/options/cb.name; then
UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
GUESS=$UNAME_MACHINE-pc-isc$UNAME_REL
elif /bin/uname -X 2>/dev/null >/dev/null ; then
UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
&& UNAME_MACHINE=i586
(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
&& UNAME_MACHINE=i686
(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
&& UNAME_MACHINE=i686
GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL
else
GUESS=$UNAME_MACHINE-pc-sysv32
fi
;;
pc:*:*:*)
# Left here for compatibility:
# uname -m prints for DJGPP always 'pc', but it prints nothing about
# the processor, so we play safe by assuming i586.
# Note: whatever this is, it MUST be the same as what config.sub
# prints for the "djgpp" host, or else GDB configure will decide that
# this is a cross-build.
GUESS=i586-pc-msdosdjgpp
;;
Intel:Mach:3*:*)
GUESS=i386-pc-mach3
;;
paragon:*:*:*)
GUESS=i860-intel-osf1
;;
i860:*:4.*:*) # i860-SVR4
if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4
else # Add other i860-SVR4 vendors below as they are discovered.
GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4
fi
;;
mini*:CTIX:SYS*5:*)
# "miniframe"
GUESS=m68010-convergent-sysv
;;
mc68k:UNIX:SYSTEM5:3.51m)
GUESS=m68k-convergent-sysv
;;
M680?0:D-NIX:5.3:*)
GUESS=m68k-diab-dnix
;;
M68*:*:R3V[5678]*:*)
test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
OS_REL=''
test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
|
| ︙ | ︙ | |||
1172 1173 1174 1175 1176 1177 1178 |
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
&& { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
&& { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | < | | > > > > > > > > > < | | | | | | | | | | | | | | | | < | < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | > | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > | 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 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 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 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 1462 1463 1464 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 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 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 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 |
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
&& { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
&& { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
GUESS=m68k-unknown-lynxos$UNAME_RELEASE
;;
mc68030:UNIX_System_V:4.*:*)
GUESS=m68k-atari-sysv4
;;
TSUNAMI:LynxOS:2.*:*)
GUESS=sparc-unknown-lynxos$UNAME_RELEASE
;;
rs6000:LynxOS:2.*:*)
GUESS=rs6000-unknown-lynxos$UNAME_RELEASE
;;
PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
GUESS=powerpc-unknown-lynxos$UNAME_RELEASE
;;
SM[BE]S:UNIX_SV:*:*)
GUESS=mips-dde-sysv$UNAME_RELEASE
;;
RM*:ReliantUNIX-*:*:*)
GUESS=mips-sni-sysv4
;;
RM*:SINIX-*:*:*)
GUESS=mips-sni-sysv4
;;
*:SINIX-*:*:*)
if uname -p 2>/dev/null >/dev/null ; then
UNAME_MACHINE=`(uname -p) 2>/dev/null`
GUESS=$UNAME_MACHINE-sni-sysv4
else
GUESS=ns32k-sni-sysv
fi
;;
PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
# says <Richard.M.Bartel@ccMail.Census.GOV>
GUESS=i586-unisys-sysv4
;;
*:UNIX_System_V:4*:FTX*)
# From Gerald Hewes <hewes@openmarket.com>.
# How about differentiating between stratus architectures? -djm
GUESS=hppa1.1-stratus-sysv4
;;
*:*:*:FTX*)
# From seanf@swdc.stratus.com.
GUESS=i860-stratus-sysv4
;;
i*86:VOS:*:*)
# From Paul.Green@stratus.com.
GUESS=$UNAME_MACHINE-stratus-vos
;;
*:VOS:*:*)
# From Paul.Green@stratus.com.
GUESS=hppa1.1-stratus-vos
;;
mc68*:A/UX:*:*)
GUESS=m68k-apple-aux$UNAME_RELEASE
;;
news*:NEWS-OS:6*:*)
GUESS=mips-sony-newsos6
;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if test -d /usr/nec; then
GUESS=mips-nec-sysv$UNAME_RELEASE
else
GUESS=mips-unknown-sysv$UNAME_RELEASE
fi
;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
GUESS=powerpc-be-beos
;;
BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
GUESS=powerpc-apple-beos
;;
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
GUESS=i586-pc-beos
;;
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
GUESS=i586-pc-haiku
;;
x86_64:Haiku:*:*)
GUESS=x86_64-unknown-haiku
;;
SX-4:SUPER-UX:*:*)
GUESS=sx4-nec-superux$UNAME_RELEASE
;;
SX-5:SUPER-UX:*:*)
GUESS=sx5-nec-superux$UNAME_RELEASE
;;
SX-6:SUPER-UX:*:*)
GUESS=sx6-nec-superux$UNAME_RELEASE
;;
SX-7:SUPER-UX:*:*)
GUESS=sx7-nec-superux$UNAME_RELEASE
;;
SX-8:SUPER-UX:*:*)
GUESS=sx8-nec-superux$UNAME_RELEASE
;;
SX-8R:SUPER-UX:*:*)
GUESS=sx8r-nec-superux$UNAME_RELEASE
;;
SX-ACE:SUPER-UX:*:*)
GUESS=sxace-nec-superux$UNAME_RELEASE
;;
Power*:Rhapsody:*:*)
GUESS=powerpc-apple-rhapsody$UNAME_RELEASE
;;
*:Rhapsody:*:*)
GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE
;;
arm64:Darwin:*:*)
GUESS=aarch64-apple-darwin$UNAME_RELEASE
;;
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p`
case $UNAME_PROCESSOR in
unknown) UNAME_PROCESSOR=powerpc ;;
esac
if command -v xcode-select > /dev/null 2> /dev/null && \
! xcode-select --print-path > /dev/null 2> /dev/null ; then
# Avoid executing cc if there is no toolchain installed as
# cc will be a stub that puts up a graphical alert
# prompting the user to install developer tools.
CC_FOR_BUILD=no_compiler_found
else
set_cc_for_build
fi
if test "$CC_FOR_BUILD" != no_compiler_found; then
if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
case $UNAME_PROCESSOR in
i386) UNAME_PROCESSOR=x86_64 ;;
powerpc) UNAME_PROCESSOR=powerpc64 ;;
esac
fi
# On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_PPC >/dev/null
then
UNAME_PROCESSOR=powerpc
fi
elif test "$UNAME_PROCESSOR" = i386 ; then
# uname -m returns i386 or x86_64
UNAME_PROCESSOR=$UNAME_MACHINE
fi
GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE
;;
*:procnto*:*:* | *:QNX:[0123456789]*:*)
UNAME_PROCESSOR=`uname -p`
if test "$UNAME_PROCESSOR" = x86; then
UNAME_PROCESSOR=i386
UNAME_MACHINE=pc
fi
GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE
;;
*:QNX:*:4*)
GUESS=i386-pc-qnx
;;
NEO-*:NONSTOP_KERNEL:*:*)
GUESS=neo-tandem-nsk$UNAME_RELEASE
;;
NSE-*:NONSTOP_KERNEL:*:*)
GUESS=nse-tandem-nsk$UNAME_RELEASE
;;
NSR-*:NONSTOP_KERNEL:*:*)
GUESS=nsr-tandem-nsk$UNAME_RELEASE
;;
NSV-*:NONSTOP_KERNEL:*:*)
GUESS=nsv-tandem-nsk$UNAME_RELEASE
;;
NSX-*:NONSTOP_KERNEL:*:*)
GUESS=nsx-tandem-nsk$UNAME_RELEASE
;;
*:NonStop-UX:*:*)
GUESS=mips-compaq-nonstopux
;;
BS2000:POSIX*:*:*)
GUESS=bs2000-siemens-sysv
;;
DS/*:UNIX_System_V:*:*)
GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE
;;
*:Plan9:*:*)
# "uname -m" is not consistent, so use $cputype instead. 386
# is converted to i386 for consistency with other x86
# operating systems.
if test "${cputype-}" = 386; then
UNAME_MACHINE=i386
elif test "x${cputype-}" != x; then
UNAME_MACHINE=$cputype
fi
GUESS=$UNAME_MACHINE-unknown-plan9
;;
*:TOPS-10:*:*)
GUESS=pdp10-unknown-tops10
;;
*:TENEX:*:*)
GUESS=pdp10-unknown-tenex
;;
KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
GUESS=pdp10-dec-tops20
;;
XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
GUESS=pdp10-xkl-tops20
;;
*:TOPS-20:*:*)
GUESS=pdp10-unknown-tops20
;;
*:ITS:*:*)
GUESS=pdp10-unknown-its
;;
SEI:*:*:SEIUX)
GUESS=mips-sei-seiux$UNAME_RELEASE
;;
*:DragonFly:*:*)
DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL
;;
*:*VMS:*:*)
UNAME_MACHINE=`(uname -p) 2>/dev/null`
case $UNAME_MACHINE in
A*) GUESS=alpha-dec-vms ;;
I*) GUESS=ia64-dec-vms ;;
V*) GUESS=vax-dec-vms ;;
esac ;;
*:XENIX:*:SysV)
GUESS=i386-pc-xenix
;;
i*86:skyos:*:*)
SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
;;
i*86:rdos:*:*)
GUESS=$UNAME_MACHINE-pc-rdos
;;
*:AROS:*:*)
GUESS=$UNAME_MACHINE-unknown-aros
;;
x86_64:VMkernel:*:*)
GUESS=$UNAME_MACHINE-unknown-esx
;;
amd64:Isilon\ OneFS:*:*)
GUESS=x86_64-unknown-onefs
;;
*:Unleashed:*:*)
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;;
esac
# Do we have a guess based on uname results?
if test "x$GUESS" != x; then
echo "$GUESS"
exit
fi
# No uname command or uname output not recognized.
set_cc_for_build
cat > "$dummy.c" <<EOF
#ifdef _SEQUENT_
#include <sys/types.h>
#include <sys/utsname.h>
#endif
#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
#include <signal.h>
#if defined(_SIZE_T_) || defined(SIGLOST)
#include <sys/utsname.h>
#endif
#endif
#endif
main ()
{
#if defined (sony)
#if defined (MIPSEB)
/* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
I don't know.... */
printf ("mips-sony-bsd\n"); exit (0);
#else
#include <sys/param.h>
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
"4"
#else
""
#endif
); exit (0);
#endif
#endif
#if defined (NeXT)
#if !defined (__ARCHITECTURE__)
#define __ARCHITECTURE__ "m68k"
#endif
int version;
version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
if (version < 4)
printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
else
printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
exit (0);
#endif
#if defined (MULTIMAX) || defined (n16)
#if defined (UMAXV)
printf ("ns32k-encore-sysv\n"); exit (0);
#else
#if defined (CMU)
printf ("ns32k-encore-mach\n"); exit (0);
#else
printf ("ns32k-encore-bsd\n"); exit (0);
#endif
#endif
#endif
#if defined (__386BSD__)
printf ("i386-pc-bsd\n"); exit (0);
#endif
#if defined (sequent)
#if defined (i386)
printf ("i386-sequent-dynix\n"); exit (0);
#endif
#if defined (ns32000)
printf ("ns32k-sequent-dynix\n"); exit (0);
#endif
#endif
#if defined (_SEQUENT_)
struct utsname un;
uname(&un);
if (strncmp(un.version, "V2", 2) == 0) {
printf ("i386-sequent-ptx2\n"); exit (0);
}
if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
printf ("i386-sequent-ptx1\n"); exit (0);
}
printf ("i386-sequent-ptx\n"); exit (0);
#endif
#if defined (vax)
#if !defined (ultrix)
#include <sys/param.h>
#if defined (BSD)
#if BSD == 43
printf ("vax-dec-bsd4.3\n"); exit (0);
#else
#if BSD == 199006
printf ("vax-dec-bsd4.3reno\n"); exit (0);
#else
printf ("vax-dec-bsd\n"); exit (0);
#endif
#endif
#else
printf ("vax-dec-bsd\n"); exit (0);
#endif
#else
#if defined(_SIZE_T_) || defined(SIGLOST)
struct utsname un;
uname (&un);
printf ("vax-dec-ultrix%s\n", un.release); exit (0);
#else
printf ("vax-dec-ultrix\n"); exit (0);
#endif
#endif
#endif
#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
#if defined(_SIZE_T_) || defined(SIGLOST)
struct utsname *un;
uname (&un);
printf ("mips-dec-ultrix%s\n", un.release); exit (0);
#else
printf ("mips-dec-ultrix\n"); exit (0);
#endif
#endif
#endif
#if defined (alliant) && defined (i860)
printf ("i860-alliant-bsd\n"); exit (0);
#endif
exit (1);
}
EOF
$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` &&
{ echo "$SYSTEM_NAME"; exit; }
# Apollos put the system type in the environment.
test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
echo "$0: unable to guess system type" >&2
case $UNAME_MACHINE:$UNAME_SYSTEM in
mips:Linux | mips64:Linux)
# If we got here on MIPS GNU/Linux, output extra information.
cat >&2 <<EOF
NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
the system type. Please install a C compiler and try again.
EOF
;;
esac
cat >&2 <<EOF
This script (version $timestamp), has failed to recognize the
operating system you are using. If your script is old, overwrite *all*
copies of config.guess and config.sub with the latest versions from:
https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
and
https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
EOF
our_year=`echo $timestamp | sed 's,-.*,,'`
thisyear=`date +%Y`
# shellcheck disable=SC2003
script_age=`expr "$thisyear" - "$our_year"`
if test "$script_age" -lt 3 ; then
cat >&2 <<EOF
If $0 has already been updated, send the following data and any
information you think might be pertinent to config-patches@gnu.org to
provide the necessary information to handle your system.
config.guess timestamp = $timestamp
|
| ︙ | ︙ | |||
1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 | /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: | > | 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 | /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: |
Changes to autosetup/autosetup-config.sub.
1 2 | #! /bin/sh # Configuration validation subroutine script. | | > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2021 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2021-07-03' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but |
| ︙ | ︙ | |||
29 30 31 32 33 34 35 | # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: | | > > > > > > > | | 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 | # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) Copyright 1992-2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." |
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
--help | --h* | -h )
echo "$usage"; exit ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
break ;;
-* )
| | > > > > > | > > > > > > > > > > > | > | | | < | | | > | < | | > | > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | > > > | | > | | > > | < < | > > > > > > > | > | < > > | < < < < < < > | | > > | > | > > > > | > > > > > | | > > > > > > > > > > > > > > > > > > > > | > | > > | > | > > > > | > > | | > > > > > > > > > > > > > | > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > | > | | > | > > > > > > | > | > > > > | > | > > > > | > > > > > > > > > > | > > > > > > > > > | | > | | > | > > | > | | > | > > | > | > > > > > > > > > | > > > > > > > > | > > > > > > > > > > | > | > > > > > | < > > > | > | > > > > | < | > | > > > > | > > > > > | > > > > > > > > > > | > | > > > > > > | > > | > | > > > > | > > > > > | > > | > | > > | > > | > | > > > > | | > | > > > > > > > > | > | | | > > > > > > > > > > > > > | | > > > > > > | > > > > > > > > > > > > > > > | | > > > > > > > > > > > > | | > | > > | > > > | | > > > > > > > > > > > > > > > > > > > > | | | | | > > | | | < | > | < > | > > > > > > > | > > > > > > > | < > > > | > > > | > > > > > > > | | > > | > > > | > > > > | > | < > > > > > > > | < < | > > > | | < | > | | | | > > > > > > > | > | > > > > | > > > | < | > > > > > > > > > > > > > > | > > > > > > > > > | > > > | > > > | > > | > > > > | > > > > > > > > > > > | > > > > | < | > > > > > > > > > | | > > | > > > > > > > > > | | | | < | > > > > > > > > > > > | < | < < > > | > > > > | | | | > > | > > > | < > > | < | | < > > > > > > > > > > > > > > > > > | > > > > > > | > | > | > | > > | > > | > > > > > > | > > > > | < < | < < < > | | < < < < < | < | < < | < | > < | > | < < > | > | > > > > > | | < < < < < < < | < < < < < < < < < > > | < < < < < < | | | | | | | | | | > | < | | < < < < < < < < < < < < > | < < < | > > | < > | < | > | | > | < < < < < | < < < < < > | < < > > > > > | < | < < < < | > < < < | | | | < > > | | | < < < | < | < < < | < < < < < < | < | < < > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | | < < | < < < < < < < < < < < | | | < | | < | | > | > | < < < > | < > | < < < | < < < | | | | | | < | | | < < < < < < > | < | < < < | < < < < < | | < < < | > | < | < > | < < < | > | | > | > | < < > < < | > < | | | < < < < | | | < < < > | | < | | < | < < < < | < < < < < < | > | > | > | > | | < < < < < | < < < < < < | < < < | < < < < < < | < < < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < > > | < < < | > > > | | < > > > | < < | | < > | | < < > > > > | > > | > | < < < < > | < < < < | < | < < | > > | < > > > > | > > > > | | < > | > > > | | < > | | | < > | | | < < < < < < < < < < < < < | < < < < < | < < < < < < | < < < < < < < < < > < < | < < < | < < | < < < < < < | < < | < | < < < > | < | < < < | < | < < | < < > > > | < < < < < < < > | | < | > | | < > | | | < > | | | < < | < < > | | < < > | | < | < < | < < | < < < < < < < | < < < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > | | | | | | < < < | | < < | < > > > | | < > | | < > | | < > | | < > | | < < < < > | < < > > | | > | < | < < < | < < > | | | | > | | > | < > > | < < | | | | | | | < < < < < < | | | | | | | | | | | | | | | | | | | | < < < | | | | | | | | | | | | < < < | | | | | | | | < < < | | | | | | < < < < < < < < < < > > > | | | | | > | | | > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | | | | | | | | | | | | | > > > | | > > > | | | | | | | | < | | 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 134 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 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 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 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 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 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 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 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 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 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 934 935 936 937 938 939 940 941 942 943 944 945 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 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 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 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 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 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 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 1462 1463 1464 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 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 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 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 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 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 |
--help | --h* | -h )
echo "$usage"; exit ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
break ;;
-* )
echo "$me: invalid option $1$help" >&2
exit 1 ;;
*local*)
# First pass through any local machine types.
echo "$1"
exit ;;
* )
break ;;
esac
done
case $# in
0) echo "$me: missing argument$help" >&2
exit 1;;
1) ;;
*) echo "$me: too many arguments$help" >&2
exit 1;;
esac
# Split fields of configuration type
# shellcheck disable=SC2162
IFS="-" read field1 field2 field3 field4 <<EOF
$1
EOF
# Separate into logical components for further validation
case $1 in
*-*-*-*-*)
echo Invalid configuration \`"$1"\': more than four components >&2
exit 1
;;
*-*-*-*)
basic_machine=$field1-$field2
basic_os=$field3-$field4
;;
*-*-*)
# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
# parts
maybe_os=$field2-$field3
case $maybe_os in
nto-qnx* | linux-* | uclinux-uclibc* \
| uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
| netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
| storm-chaos* | os2-emx* | rtmk-nova*)
basic_machine=$field1
basic_os=$maybe_os
;;
android-linux)
basic_machine=$field1-unknown
basic_os=linux-android
;;
*)
basic_machine=$field1-$field2
basic_os=$field3
;;
esac
;;
*-*)
# A lone config we happen to match not fitting any pattern
case $field1-$field2 in
decstation-3100)
basic_machine=mips-dec
basic_os=
;;
*-*)
# Second component is usually, but not always the OS
case $field2 in
# Prevent following clause from handling this valid os
sun*os*)
basic_machine=$field1
basic_os=$field2
;;
# Manufacturers
dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
| att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
| unicom* | ibm* | next | hp | isi* | apollo | altos* \
| convergent* | ncr* | news | 32* | 3600* | 3100* \
| hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
| ultra | tti* | harris | dolphin | highlevel | gould \
| cbm | ns | masscomp | apple | axis | knuth | cray \
| microblaze* | sim | cisco \
| oki | wec | wrs | winbond)
basic_machine=$field1-$field2
basic_os=
;;
*)
basic_machine=$field1
basic_os=$field2
;;
esac
;;
esac
;;
*)
# Convert single-component short-hands not valid as part of
# multi-component configurations.
case $field1 in
386bsd)
basic_machine=i386-pc
basic_os=bsd
;;
a29khif)
basic_machine=a29k-amd
basic_os=udi
;;
adobe68k)
basic_machine=m68010-adobe
basic_os=scout
;;
alliant)
basic_machine=fx80-alliant
basic_os=
;;
altos | altos3068)
basic_machine=m68k-altos
basic_os=
;;
am29k)
basic_machine=a29k-none
basic_os=bsd
;;
amdahl)
basic_machine=580-amdahl
basic_os=sysv
;;
amiga)
basic_machine=m68k-unknown
basic_os=
;;
amigaos | amigados)
basic_machine=m68k-unknown
basic_os=amigaos
;;
amigaunix | amix)
basic_machine=m68k-unknown
basic_os=sysv4
;;
apollo68)
basic_machine=m68k-apollo
basic_os=sysv
;;
apollo68bsd)
basic_machine=m68k-apollo
basic_os=bsd
;;
aros)
basic_machine=i386-pc
basic_os=aros
;;
aux)
basic_machine=m68k-apple
basic_os=aux
;;
balance)
basic_machine=ns32k-sequent
basic_os=dynix
;;
blackfin)
basic_machine=bfin-unknown
basic_os=linux
;;
cegcc)
basic_machine=arm-unknown
basic_os=cegcc
;;
convex-c1)
basic_machine=c1-convex
basic_os=bsd
;;
convex-c2)
basic_machine=c2-convex
basic_os=bsd
;;
convex-c32)
basic_machine=c32-convex
basic_os=bsd
;;
convex-c34)
basic_machine=c34-convex
basic_os=bsd
;;
convex-c38)
basic_machine=c38-convex
basic_os=bsd
;;
cray)
basic_machine=j90-cray
basic_os=unicos
;;
crds | unos)
basic_machine=m68k-crds
basic_os=
;;
da30)
basic_machine=m68k-da30
basic_os=
;;
decstation | pmax | pmin | dec3100 | decstatn)
basic_machine=mips-dec
basic_os=
;;
delta88)
basic_machine=m88k-motorola
basic_os=sysv3
;;
dicos)
basic_machine=i686-pc
basic_os=dicos
;;
djgpp)
basic_machine=i586-pc
basic_os=msdosdjgpp
;;
ebmon29k)
basic_machine=a29k-amd
basic_os=ebmon
;;
es1800 | OSE68k | ose68k | ose | OSE)
basic_machine=m68k-ericsson
basic_os=ose
;;
gmicro)
basic_machine=tron-gmicro
basic_os=sysv
;;
go32)
basic_machine=i386-pc
basic_os=go32
;;
h8300hms)
basic_machine=h8300-hitachi
basic_os=hms
;;
h8300xray)
basic_machine=h8300-hitachi
basic_os=xray
;;
h8500hms)
basic_machine=h8500-hitachi
basic_os=hms
;;
harris)
basic_machine=m88k-harris
basic_os=sysv3
;;
hp300 | hp300hpux)
basic_machine=m68k-hp
basic_os=hpux
;;
hp300bsd)
basic_machine=m68k-hp
basic_os=bsd
;;
hppaosf)
basic_machine=hppa1.1-hp
basic_os=osf
;;
hppro)
basic_machine=hppa1.1-hp
basic_os=proelf
;;
i386mach)
basic_machine=i386-mach
basic_os=mach
;;
isi68 | isi)
basic_machine=m68k-isi
basic_os=sysv
;;
m68knommu)
basic_machine=m68k-unknown
basic_os=linux
;;
magnum | m3230)
basic_machine=mips-mips
basic_os=sysv
;;
merlin)
basic_machine=ns32k-utek
basic_os=sysv
;;
mingw64)
basic_machine=x86_64-pc
basic_os=mingw64
;;
mingw32)
basic_machine=i686-pc
basic_os=mingw32
;;
mingw32ce)
basic_machine=arm-unknown
basic_os=mingw32ce
;;
monitor)
basic_machine=m68k-rom68k
basic_os=coff
;;
morphos)
basic_machine=powerpc-unknown
basic_os=morphos
;;
moxiebox)
basic_machine=moxie-unknown
basic_os=moxiebox
;;
msdos)
basic_machine=i386-pc
basic_os=msdos
;;
msys)
basic_machine=i686-pc
basic_os=msys
;;
mvs)
basic_machine=i370-ibm
basic_os=mvs
;;
nacl)
basic_machine=le32-unknown
basic_os=nacl
;;
ncr3000)
basic_machine=i486-ncr
basic_os=sysv4
;;
netbsd386)
basic_machine=i386-pc
basic_os=netbsd
;;
netwinder)
basic_machine=armv4l-rebel
basic_os=linux
;;
news | news700 | news800 | news900)
basic_machine=m68k-sony
basic_os=newsos
;;
news1000)
basic_machine=m68030-sony
basic_os=newsos
;;
necv70)
basic_machine=v70-nec
basic_os=sysv
;;
nh3000)
basic_machine=m68k-harris
basic_os=cxux
;;
nh[45]000)
basic_machine=m88k-harris
basic_os=cxux
;;
nindy960)
basic_machine=i960-intel
basic_os=nindy
;;
mon960)
basic_machine=i960-intel
basic_os=mon960
;;
nonstopux)
basic_machine=mips-compaq
basic_os=nonstopux
;;
os400)
basic_machine=powerpc-ibm
basic_os=os400
;;
OSE68000 | ose68000)
basic_machine=m68000-ericsson
basic_os=ose
;;
os68k)
basic_machine=m68k-none
basic_os=os68k
;;
paragon)
basic_machine=i860-intel
basic_os=osf
;;
parisc)
basic_machine=hppa-unknown
basic_os=linux
;;
psp)
basic_machine=mipsallegrexel-sony
basic_os=psp
;;
pw32)
basic_machine=i586-unknown
basic_os=pw32
;;
rdos | rdos64)
basic_machine=x86_64-pc
basic_os=rdos
;;
rdos32)
basic_machine=i386-pc
basic_os=rdos
;;
rom68k)
basic_machine=m68k-rom68k
basic_os=coff
;;
sa29200)
basic_machine=a29k-amd
basic_os=udi
;;
sei)
basic_machine=mips-sei
basic_os=seiux
;;
sequent)
basic_machine=i386-sequent
basic_os=
;;
sps7)
basic_machine=m68k-bull
basic_os=sysv2
;;
st2000)
basic_machine=m68k-tandem
basic_os=
;;
stratus)
basic_machine=i860-stratus
basic_os=sysv4
;;
sun2)
basic_machine=m68000-sun
basic_os=
;;
sun2os3)
basic_machine=m68000-sun
basic_os=sunos3
;;
sun2os4)
basic_machine=m68000-sun
basic_os=sunos4
;;
sun3)
basic_machine=m68k-sun
basic_os=
;;
sun3os3)
basic_machine=m68k-sun
basic_os=sunos3
;;
sun3os4)
basic_machine=m68k-sun
basic_os=sunos4
;;
sun4)
basic_machine=sparc-sun
basic_os=
;;
sun4os3)
basic_machine=sparc-sun
basic_os=sunos3
;;
sun4os4)
basic_machine=sparc-sun
basic_os=sunos4
;;
sun4sol2)
basic_machine=sparc-sun
basic_os=solaris2
;;
sun386 | sun386i | roadrunner)
basic_machine=i386-sun
basic_os=
;;
sv1)
basic_machine=sv1-cray
basic_os=unicos
;;
symmetry)
basic_machine=i386-sequent
basic_os=dynix
;;
t3e)
basic_machine=alphaev5-cray
basic_os=unicos
;;
t90)
basic_machine=t90-cray
basic_os=unicos
;;
toad1)
basic_machine=pdp10-xkl
basic_os=tops20
;;
tpf)
basic_machine=s390x-ibm
basic_os=tpf
;;
udi29k)
basic_machine=a29k-amd
basic_os=udi
;;
ultra3)
basic_machine=a29k-nyu
basic_os=sym1
;;
v810 | necv810)
basic_machine=v810-nec
basic_os=none
;;
vaxv)
basic_machine=vax-dec
basic_os=sysv
;;
vms)
basic_machine=vax-dec
basic_os=vms
;;
vsta)
basic_machine=i386-pc
basic_os=vsta
;;
vxworks960)
basic_machine=i960-wrs
basic_os=vxworks
;;
vxworks68)
basic_machine=m68k-wrs
basic_os=vxworks
;;
vxworks29k)
basic_machine=a29k-wrs
basic_os=vxworks
;;
xbox)
basic_machine=i686-pc
basic_os=mingw32
;;
ymp)
basic_machine=ymp-cray
basic_os=unicos
;;
*)
basic_machine=$1
basic_os=
;;
esac
;;
esac
# Decode 1-component or ad-hoc basic machines
case $basic_machine in
# Here we handle the default manufacturer of certain CPU types. It is in
# some cases the only manufacturer, in others, it is the most popular.
w89k)
cpu=hppa1.1
vendor=winbond
;;
op50n)
cpu=hppa1.1
vendor=oki
;;
op60c)
cpu=hppa1.1
vendor=oki
;;
ibm*)
cpu=i370
vendor=ibm
;;
orion105)
cpu=clipper
vendor=highlevel
;;
mac | mpw | mac-mpw)
cpu=m68k
vendor=apple
;;
pmac | pmac-mpw)
cpu=powerpc
vendor=apple
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
cpu=m68000
vendor=att
;;
3b*)
cpu=we32k
vendor=att
;;
bluegene*)
cpu=powerpc
vendor=ibm
basic_os=cnk
;;
decsystem10* | dec10*)
cpu=pdp10
vendor=dec
basic_os=tops10
;;
decsystem20* | dec20*)
cpu=pdp10
vendor=dec
basic_os=tops20
;;
delta | 3300 | motorola-3300 | motorola-delta \
| 3300-motorola | delta-motorola)
cpu=m68k
vendor=motorola
;;
dpx2*)
cpu=m68k
vendor=bull
basic_os=sysv3
;;
encore | umax | mmax)
cpu=ns32k
vendor=encore
;;
elxsi)
cpu=elxsi
vendor=elxsi
basic_os=${basic_os:-bsd}
;;
fx2800)
cpu=i860
vendor=alliant
;;
genix)
cpu=ns32k
vendor=ns
;;
h3050r* | hiux*)
cpu=hppa1.1
vendor=hitachi
basic_os=hiuxwe2
;;
hp3k9[0-9][0-9] | hp9[0-9][0-9])
cpu=hppa1.0
vendor=hp
;;
hp9k2[0-9][0-9] | hp9k31[0-9])
cpu=m68000
vendor=hp
;;
hp9k3[2-9][0-9])
cpu=m68k
vendor=hp
;;
hp9k6[0-9][0-9] | hp6[0-9][0-9])
cpu=hppa1.0
vendor=hp
;;
hp9k7[0-79][0-9] | hp7[0-79][0-9])
cpu=hppa1.1
vendor=hp
;;
hp9k78[0-9] | hp78[0-9])
# FIXME: really hppa2.0-hp
cpu=hppa1.1
vendor=hp
;;
hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
# FIXME: really hppa2.0-hp
cpu=hppa1.1
vendor=hp
;;
hp9k8[0-9][13679] | hp8[0-9][13679])
cpu=hppa1.1
vendor=hp
;;
hp9k8[0-9][0-9] | hp8[0-9][0-9])
cpu=hppa1.0
vendor=hp
;;
i*86v32)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
basic_os=sysv32
;;
i*86v4*)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
basic_os=sysv4
;;
i*86v)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
basic_os=sysv
;;
i*86sol2)
cpu=`echo "$1" | sed -e 's/86.*/86/'`
vendor=pc
basic_os=solaris2
;;
j90 | j90-cray)
cpu=j90
vendor=cray
basic_os=${basic_os:-unicos}
;;
iris | iris4d)
cpu=mips
vendor=sgi
case $basic_os in
irix*)
;;
*)
basic_os=irix4
;;
esac
;;
miniframe)
cpu=m68000
vendor=convergent
;;
*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
cpu=m68k
vendor=atari
basic_os=mint
;;
news-3600 | risc-news)
cpu=mips
vendor=sony
basic_os=newsos
;;
next | m*-next)
cpu=m68k
vendor=next
case $basic_os in
openstep*)
;;
nextstep*)
;;
ns2*)
basic_os=nextstep2
;;
*)
basic_os=nextstep3
;;
esac
;;
np1)
cpu=np1
vendor=gould
;;
op50n-* | op60c-*)
cpu=hppa1.1
vendor=oki
basic_os=proelf
;;
pa-hitachi)
cpu=hppa1.1
vendor=hitachi
basic_os=hiuxwe2
;;
pbd)
cpu=sparc
vendor=tti
;;
pbb)
cpu=m68k
vendor=tti
;;
pc532)
cpu=ns32k
vendor=pc532
;;
pn)
cpu=pn
vendor=gould
;;
power)
cpu=power
vendor=ibm
;;
ps2)
cpu=i386
vendor=ibm
;;
rm[46]00)
cpu=mips
vendor=siemens
;;
rtpc | rtpc-*)
cpu=romp
vendor=ibm
;;
sde)
cpu=mipsisa32
vendor=sde
basic_os=${basic_os:-elf}
;;
simso-wrs)
cpu=sparclite
vendor=wrs
basic_os=vxworks
;;
tower | tower-32)
cpu=m68k
vendor=ncr
;;
vpp*|vx|vx-*)
cpu=f301
vendor=fujitsu
;;
w65)
cpu=w65
vendor=wdc
;;
w89k-*)
cpu=hppa1.1
vendor=winbond
basic_os=proelf
;;
none)
cpu=none
vendor=none
;;
leon|leon[3-9])
cpu=sparc
vendor=$basic_machine
;;
leon-*|leon[3-9]-*)
cpu=sparc
vendor=`echo "$basic_machine" | sed 's/-.*//'`
;;
*-*)
# shellcheck disable=SC2162
IFS="-" read cpu vendor <<EOF
$basic_machine
EOF
;;
# We use `pc' rather than `unknown'
# because (1) that's what they normally are, and
# (2) the word "unknown" tends to confuse beginning users.
i*86 | x86_64)
cpu=$basic_machine
vendor=pc
;;
# These rules are duplicated from below for sake of the special case above;
# i.e. things that normalized to x86 arches should also default to "pc"
pc98)
cpu=i386
vendor=pc
;;
x64 | amd64)
cpu=x86_64
vendor=pc
;;
# Recognize the basic CPU types without company name.
*)
cpu=$basic_machine
vendor=unknown
;;
esac
unset -v basic_machine
# Decode basic machines in the full and proper CPU-Company form.
case $cpu-$vendor in
# Here we handle the default manufacturer of certain CPU types in canonical form. It is in
# some cases the only manufacturer, in others, it is the most popular.
craynv-unknown)
vendor=cray
basic_os=${basic_os:-unicosmp}
;;
c90-unknown | c90-cray)
vendor=cray
basic_os=${Basic_os:-unicos}
;;
fx80-unknown)
vendor=alliant
;;
romp-unknown)
vendor=ibm
;;
mmix-unknown)
vendor=knuth
;;
microblaze-unknown | microblazeel-unknown)
vendor=xilinx
;;
rs6000-unknown)
vendor=ibm
;;
vax-unknown)
vendor=dec
;;
pdp11-unknown)
vendor=dec
;;
we32k-unknown)
vendor=att
;;
cydra-unknown)
vendor=cydrome
;;
i370-ibm*)
vendor=ibm
;;
orion-unknown)
vendor=highlevel
;;
xps-unknown | xps100-unknown)
cpu=xps100
vendor=honeywell
;;
# Here we normalize CPU types with a missing or matching vendor
dpx20-unknown | dpx20-bull)
cpu=rs6000
vendor=bull
basic_os=${basic_os:-bosx}
;;
# Here we normalize CPU types irrespective of the vendor
amd64-*)
cpu=x86_64
;;
blackfin-*)
cpu=bfin
basic_os=linux
;;
c54x-*)
cpu=tic54x
;;
c55x-*)
cpu=tic55x
;;
c6x-*)
cpu=tic6x
;;
e500v[12]-*)
cpu=powerpc
basic_os=${basic_os}"spe"
;;
mips3*-*)
cpu=mips64
;;
ms1-*)
cpu=mt
;;
m68knommu-*)
cpu=m68k
basic_os=linux
;;
m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
cpu=s12z
;;
openrisc-*)
cpu=or32
;;
parisc-*)
cpu=hppa
basic_os=linux
;;
pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
cpu=i586
;;
pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
cpu=i686
;;
pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
cpu=i686
;;
pentium4-*)
cpu=i786
;;
pc98-*)
cpu=i386
;;
ppc-* | ppcbe-*)
cpu=powerpc
;;
ppcle-* | powerpclittle-*)
cpu=powerpcle
;;
ppc64-*)
cpu=powerpc64
;;
ppc64le-* | powerpc64little-*)
cpu=powerpc64le
;;
sb1-*)
cpu=mipsisa64sb1
;;
sb1el-*)
cpu=mipsisa64sb1el
;;
sh5e[lb]-*)
cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
;;
spur-*)
cpu=spur
;;
strongarm-* | thumb-*)
cpu=arm
;;
tx39-*)
cpu=mipstx39
;;
tx39el-*)
cpu=mipstx39el
;;
x64-*)
cpu=x86_64
;;
xscale-* | xscalee[bl]-*)
cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
;;
arm64-*)
cpu=aarch64
;;
# Recognize the canonical CPU Types that limit and/or modify the
# company names they are paired with.
cr16-*)
basic_os=${basic_os:-elf}
;;
crisv32-* | etraxfs*-*)
cpu=crisv32
vendor=axis
;;
cris-* | etrax*-*)
cpu=cris
vendor=axis
;;
crx-*)
basic_os=${basic_os:-elf}
;;
neo-tandem)
cpu=neo
vendor=tandem
;;
nse-tandem)
cpu=nse
vendor=tandem
;;
nsr-tandem)
cpu=nsr
vendor=tandem
;;
nsv-tandem)
cpu=nsv
vendor=tandem
;;
nsx-tandem)
cpu=nsx
vendor=tandem
;;
mipsallegrexel-sony)
cpu=mipsallegrexel
vendor=sony
;;
tile*-*)
basic_os=${basic_os:-linux-gnu}
;;
*)
# Recognize the canonical CPU types that are allowed with any
# company name.
case $cpu in
1750a | 580 \
| a29k \
| aarch64 | aarch64_be \
| abacus \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
| alphapca5[67] | alpha64pca5[67] \
| am33_2.0 \
| amdgcn \
| arc | arceb | arc32 | arc64 \
| arm | arm[lb]e | arme[lb] | armv* \
| avr | avr32 \
| asmjs \
| ba \
| be32 | be64 \
| bfin | bpf | bs2000 \
| c[123]* | c30 | [cjt]90 | c4x \
| c8051 | clipper | craynv | csky | cydra \
| d10v | d30v | dlx | dsp16xx \
| e2k | elxsi | epiphany \
| f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
| h8300 | h8500 \
| hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
| hexagon \
| i370 | i*86 | i860 | i960 | ia16 | ia64 \
| ip2k | iq2000 \
| k1om \
| le32 | le64 \
| lm32 \
| loongarch32 | loongarch64 | loongarchx32 \
| m32c | m32r | m32rle \
| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
| m88110 | m88k | maxq | mb | mcore | mep | metag \
| microblaze | microblazeel \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64eb | mips64el \
| mips64octeon | mips64octeonel \
| mips64orion | mips64orionel \
| mips64r5900 | mips64r5900el \
| mips64vr | mips64vrel \
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
| mips64vr5900 | mips64vr5900el \
| mipsisa32 | mipsisa32el \
| mipsisa32r2 | mipsisa32r2el \
| mipsisa32r3 | mipsisa32r3el \
| mipsisa32r5 | mipsisa32r5el \
| mipsisa32r6 | mipsisa32r6el \
| mipsisa64 | mipsisa64el \
| mipsisa64r2 | mipsisa64r2el \
| mipsisa64r3 | mipsisa64r3el \
| mipsisa64r5 | mipsisa64r5el \
| mipsisa64r6 | mipsisa64r6el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
| mipsr5900 | mipsr5900el \
| mipstx39 | mipstx39el \
| mmix \
| mn10200 | mn10300 \
| moxie \
| mt \
| msp430 \
| nds32 | nds32le | nds32be \
| nfp \
| nios | nios2 | nios2eb | nios2el \
| none | np1 | ns16k | ns32k | nvptx \
| open8 \
| or1k* \
| or32 \
| orion \
| picochip \
| pdp10 | pdp11 | pj | pjl | pn | power \
| powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
| pru \
| pyramid \
| riscv | riscv32 | riscv32be | riscv64 | riscv64be \
| rl78 | romp | rs6000 | rx \
| s390 | s390x \
| score \
| sh | shl \
| sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
| sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
| sparclite \
| sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
| spu \
| tahoe \
| thumbv7* \
| tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
| tron \
| ubicom32 \
| v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
| vax \
| visium \
| w65 \
| wasm32 | wasm64 \
| we32k \
| x86 | x86_64 | xc16x | xgate | xps100 \
| xstormy16 | xtensa* \
| ymp \
| z8k | z80)
;;
*)
echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
exit 1
;;
esac
;;
esac
# Here we canonicalize certain aliases for manufacturers.
case $vendor in
digital*)
vendor=dec
;;
commodore*)
vendor=cbm
;;
*)
;;
esac
# Decode manufacturer-specific aliases for certain operating systems.
if test x$basic_os != x
then
# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just
# set os.
case $basic_os in
gnu/linux*)
kernel=linux
os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
;;
os2-emx)
kernel=os2
os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
;;
nto-qnx*)
kernel=nto
os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
;;
*-*)
# shellcheck disable=SC2162
IFS="-" read kernel os <<EOF
$basic_os
EOF
;;
# Default OS when just kernel was specified
nto*)
kernel=nto
os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
;;
linux*)
kernel=linux
os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
;;
*)
kernel=
os=$basic_os
;;
esac
# Now, normalize the OS (knowing we just have one component, it's not a kernel,
# etc.)
case $os in
# First match some system type aliases that might get confused
# with valid system types.
# solaris* is a basic system type, with this one exception.
auroraux)
os=auroraux
;;
bluegene*)
os=cnk
;;
solaris1 | solaris1.*)
os=`echo "$os" | sed -e 's|solaris1|sunos4|'`
;;
solaris)
os=solaris2
;;
unixware*)
os=sysv4.2uw
;;
# es1800 is here to avoid being matched by es* (a different OS)
es1800*)
os=ose
;;
# Some version numbers need modification
chorusos*)
os=chorusos
;;
isc)
os=isc2.2
;;
sco6)
os=sco5v6
;;
sco5)
os=sco3.2v5
;;
sco4)
os=sco3.2v4
;;
sco3.2.[4-9]*)
os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'`
;;
sco*v* | scout)
# Don't match below
;;
sco*)
os=sco3.2v2
;;
psos*)
os=psos
;;
qnx*)
os=qnx
;;
hiux*)
os=hiuxwe2
;;
lynx*178)
os=lynxos178
;;
lynx*5)
os=lynxos5
;;
lynxos*)
# don't get caught up in next wildcard
;;
lynx*)
os=lynxos
;;
mac[0-9]*)
os=`echo "$os" | sed -e 's|mac|macos|'`
;;
opened*)
os=openedition
;;
os400*)
os=os400
;;
sunos5*)
os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
;;
sunos6*)
os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
;;
wince*)
os=wince
;;
utek*)
os=bsd
;;
dynix*)
os=bsd
;;
acis*)
os=aos
;;
atheos*)
os=atheos
;;
syllable*)
os=syllable
;;
386bsd)
os=bsd
;;
ctix* | uts*)
os=sysv
;;
nova*)
os=rtmk-nova
;;
ns2)
os=nextstep2
;;
# Preserve the version number of sinix5.
sinix5.*)
os=`echo "$os" | sed -e 's|sinix|sysv|'`
;;
sinix*)
os=sysv4
;;
tpf*)
os=tpf
;;
triton*)
os=sysv3
;;
oss*)
os=sysv3
;;
svr4*)
os=sysv4
;;
svr3)
os=sysv3
;;
sysvr4)
os=sysv4
;;
ose*)
os=ose
;;
*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
os=mint
;;
dicos*)
os=dicos
;;
pikeos*)
# Until real need of OS specific support for
# particular features comes up, bare metal
# configurations are quite functional.
case $cpu in
arm*)
os=eabi
;;
*)
os=elf
;;
esac
;;
*)
# No normalization, but not necessarily accepted, that comes below.
;;
esac
else
# Here we handle the default operating systems that come with various machines.
# The value should be what the vendor currently ships out the door with their
# machine or put another way, the most popular os provided with the machine.
# Note that if you're going to try to match "-MANUFACTURER" here (say,
# "-sun"), then you have to tell the case statement up towards the top
# that MANUFACTURER isn't an operating system. Otherwise, code above
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.
kernel=
case $cpu-$vendor in
score-*)
os=elf
;;
spu-*)
os=elf
;;
*-acorn)
os=riscix1.2
;;
arm*-rebel)
kernel=linux
os=gnu
;;
arm*-semi)
os=aout
;;
c4x-* | tic4x-*)
os=coff
;;
c8051-*)
os=elf
;;
clipper-intergraph)
os=clix
;;
hexagon-*)
os=elf
;;
tic54x-*)
os=coff
;;
tic55x-*)
os=coff
;;
tic6x-*)
os=coff
;;
# This must come before the *-dec entry.
pdp10-*)
os=tops20
;;
pdp11-*)
os=none
;;
*-dec | vax-*)
os=ultrix4.2
;;
m68*-apollo)
os=domain
;;
i386-sun)
os=sunos4.0.2
;;
m68000-sun)
os=sunos3
;;
m68*-cisco)
os=aout
;;
mep-*)
os=elf
;;
mips*-cisco)
os=elf
;;
mips*-*)
os=elf
;;
or32-*)
os=coff
;;
*-tti) # must be before sparc entry or we get the wrong os.
os=sysv3
;;
sparc-* | *-sun)
os=sunos4.1.1
;;
pru-*)
os=elf
;;
*-be)
os=beos
;;
*-ibm)
os=aix
;;
*-knuth)
os=mmixware
;;
*-wec)
os=proelf
;;
*-winbond)
os=proelf
;;
*-oki)
os=proelf
;;
*-hp)
os=hpux
;;
*-hitachi)
os=hiux
;;
i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
os=sysv
;;
*-cbm)
os=amigaos
;;
*-dg)
os=dgux
;;
*-dolphin)
os=sysv3
;;
m68k-ccur)
os=rtu
;;
m88k-omron*)
os=luna
;;
*-next)
os=nextstep
;;
*-sequent)
os=ptx
;;
*-crds)
os=unos
;;
*-ns)
os=genix
;;
i370-*)
os=mvs
;;
*-gould)
os=sysv
;;
*-highlevel)
os=bsd
;;
*-encore)
os=bsd
;;
*-sgi)
os=irix
;;
*-siemens)
os=sysv4
;;
*-masscomp)
os=rtu
;;
f30[01]-fujitsu | f700-fujitsu)
os=uxpv
;;
*-rom68k)
os=coff
;;
*-*bug)
os=coff
;;
*-apple)
os=macos
;;
*-atari*)
os=mint
;;
*-wrs)
os=vxworks
;;
*)
os=none
;;
esac
fi
# Now, validate our (potentially fixed-up) OS.
case $os in
# Sometimes we do "kernel-libc", so those need to count as OSes.
musl* | newlib* | uclibc*)
;;
# Likewise for "kernel-abi"
eabi* | gnueabi*)
;;
# VxWorks passes extra cpu info in the 4th filed.
simlinux | simwindows | spe)
;;
# Now accept the basic system types.
# The portable systems comes first.
# Each alternative MUST end in a * to match a version number.
gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
| *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \
| hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
| sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \
| hiux* | abug | nacl* | netware* | windows* \
| os9* | macos* | osx* | ios* \
| mpw* | magic* | mmixware* | mon960* | lnews* \
| amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
| aos* | aros* | cloudabi* | sortix* | twizzler* \
| nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
| clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
| mirbsd* | netbsd* | dicos* | openedition* | ose* \
| bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \
| ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
| bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
| ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
| udi* | lites* | ieee* | go32* | aux* | hcos* \
| chorusrdb* | cegcc* | glidix* | serenity* \
| cygwin* | msys* | pe* | moss* | proelf* | rtems* \
| midipix* | mingw32* | mingw64* | mint* \
| uxpv* | beos* | mpeix* | udk* | moxiebox* \
| interix* | uwin* | mks* | rhapsody* | darwin* \
| openstep* | oskit* | conix* | pw32* | nonstopux* \
| storm-chaos* | tops10* | tenex* | tops20* | its* \
| os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \
| scout* | superux* | sysv* | rtmk* | tpf* | windiss* \
| powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
| skyos* | haiku* | rdos* | toppers* | drops* | es* \
| onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
| midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
| nsk* | powerunix* | genode* | zvmoe* | qnx* | emx*)
;;
# This one is extra strict with allowed versions
sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
# Don't forget version if it is 3.2v4 or newer.
;;
none)
;;
*)
echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2
exit 1
;;
esac
# As a final step for OS-related things, validate the OS-kernel combination
# (given a valid OS), if there is a kernel.
case $kernel-$os in
linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* )
;;
uclinux-uclibc* )
;;
-dietlibc* | -newlib* | -musl* | -uclibc* )
# These are just libc implementations, not actual OSes, and thus
# require a kernel.
echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2
exit 1
;;
kfreebsd*-gnu* | kopensolaris*-gnu*)
;;
vxworks-simlinux | vxworks-simwindows | vxworks-spe)
;;
nto-qnx*)
;;
os2-emx)
;;
*-eabi* | *-gnueabi*)
;;
-*)
# Blank kernel with real OS is always fine.
;;
*-*)
echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2
exit 1
;;
esac
# Here we handle the case where we know the os, and the CPU type, but not the
# manufacturer. We pick the logical manufacturer.
case $vendor in
unknown)
case $cpu-$os in
*-riscix*)
vendor=acorn
;;
*-sunos*)
vendor=sun
;;
*-cnk* | *-aix*)
vendor=ibm
;;
*-beos*)
vendor=be
;;
*-hpux*)
vendor=hp
;;
*-mpeix*)
vendor=hp
;;
*-hiux*)
vendor=hitachi
;;
*-unos*)
vendor=crds
;;
*-dgux*)
vendor=dg
;;
*-luna*)
vendor=omron
;;
*-genix*)
vendor=ns
;;
*-clix*)
vendor=intergraph
;;
*-mvs* | *-opened*)
vendor=ibm
;;
*-os400*)
vendor=ibm
;;
s390-* | s390x-*)
vendor=ibm
;;
*-ptx*)
vendor=sequent
;;
*-tpf*)
vendor=ibm
;;
*-vxsim* | *-vxworks* | *-windiss*)
vendor=wrs
;;
*-aux*)
vendor=apple
;;
*-hms*)
vendor=hitachi
;;
*-mpw* | *-macos*)
vendor=apple
;;
*-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
vendor=atari
;;
*-vos*)
vendor=stratus
;;
esac
;;
esac
echo "$cpu-$vendor-${kernel:+$kernel-}$os"
exit
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
|
Changes to autosetup/autosetup-find-tclsh.
1 2 | #!/bin/sh # Looks for a suitable tclsh or jimsh in the PATH | | | > | < < | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#!/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)
# If an argument is given, use that as the test instead of autosetup-test-tclsh
d="`dirname "$0"`"
for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do
{ $tclsh "$d/${1-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>&1 >/dev/null || continue
./jimsh0 "$d/${1-autosetup-test-tclsh}" && exit 0
done
echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc."
echo false
|
autosetup/autosetup-test-tclsh became a regular file.
| ︙ | ︙ |
Changes to autosetup/cc-db.tcl.
1 2 3 4 5 6 7 8 9 10 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc-db' module provides a knowledge-base of system idiosyncrasies. # In general, this module can always be included. use cc | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# @synopsis:
#
# The 'cc-db' module provides a knowledge-base of system idiosyncrasies.
# In general, this module can always be included.
use cc
options {}
# openbsd needs sys/types.h to detect some system headers
cc-include-needs sys/socket.h sys/types.h
cc-include-needs netinet/in.h sys/types.h
|
Changes to autosetup/cc-lib.tcl.
1 2 3 4 5 6 7 8 9 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # Provides a library of common tests on top of the 'cc' module. use cc | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # Provides a library of common tests on top of the 'cc' module. use cc # @cc-check-lfs # # The equivalent of the 'AC_SYS_LARGEFILE' macro. # # defines 'HAVE_LFS' if LFS is available, # and defines '_FILE_OFFSET_BITS=64' if necessary # |
| ︙ | ︙ | |||
76 77 78 79 80 81 82 | return $rc } # @cc-check-flags flag ?...? # # Checks whether the given C/C++ compiler flags can be used. Defines feature # names prefixed with 'HAVE_CFLAG' and 'HAVE_CXXFLAG' respectively, and | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
return $rc
}
# @cc-check-flags flag ?...?
#
# Checks whether the given C/C++ compiler flags can be used. Defines feature
# names prefixed with 'HAVE_CFLAG' and 'HAVE_CXXFLAG' respectively, and
# appends working flags to '-cflags' and 'AS_CFLAGS' or 'AS_CXXFLAGS'.
proc cc-check-flags {args} {
set result 1
array set opts [cc-get-settings]
switch -exact -- $opts(-lang) {
c++ {
set lang C++
set prefix CXXFLAG
|
| ︙ | ︙ | |||
99 100 101 102 103 104 105 |
}
foreach flag $args {
msg-checking "Checking whether the $lang compiler accepts $flag..."
if {[cctest -cflags $flag]} {
msg-result yes
define-feature $prefix$flag
cc-with [list -cflags [list $flag]]
| | | | | 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 |
}
foreach flag $args {
msg-checking "Checking whether the $lang compiler accepts $flag..."
if {[cctest -cflags $flag]} {
msg-result yes
define-feature $prefix$flag
cc-with [list -cflags [list $flag]]
define-append AS_${prefix}S $flag
} else {
msg-result no
set result 0
}
}
return $result
}
# @cc-check-standards ver ?...?
#
# Checks whether the C/C++ compiler accepts one of the specified '-std=$ver'
# options, and appends the first working one to '-cflags' and 'AS_CFLAGS' or
# 'AS_CXXFLAGS'.
proc cc-check-standards {args} {
array set opts [cc-get-settings]
foreach std $args {
if {[cc-check-flags -std=$std]} {
return $std
}
}
|
| ︙ | ︙ |
Changes to autosetup/cc-shared.tcl.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 | ## SHOBJ_LDFLAGS Flags to use linking a shared object, undefined symbols allowed ## SHOBJ_LDFLAGS_R - as above, but all symbols must be resolved ## SH_LINKRPATH Format for setting the rpath when linking an executable, %s = path ## SH_LINKFLAGS Flags to use linking an executable which will load shared objects ## LD_LIBRARY_PATH Environment variable which specifies path to shared libraries ## STRIPLIBFLAGS Arguments to strip a dynamic library | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
## SHOBJ_LDFLAGS Flags to use linking a shared object, undefined symbols allowed
## SHOBJ_LDFLAGS_R - as above, but all symbols must be resolved
## SH_LINKRPATH Format for setting the rpath when linking an executable, %s = path
## SH_LINKFLAGS Flags to use linking an executable which will load shared objects
## LD_LIBRARY_PATH Environment variable which specifies path to shared libraries
## STRIPLIBFLAGS Arguments to strip a dynamic library
options {}
# Defaults: gcc on unix
define SHOBJ_CFLAGS -fPIC
define SHOBJ_LDFLAGS -shared
define SH_CFLAGS -fPIC
define SH_LDFLAGS -shared
define SH_LINKFLAGS -rdynamic
|
| ︙ | ︙ |
Changes to autosetup/cc.tcl.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # # The following environment variables are used if set: # ## CC - C compiler ## CXX - C++ compiler ## CPP - C preprocessor ## CCACHE - Set to "none" to disable automatic use of ccache ## CFLAGS - Additional C compiler flags ## CXXFLAGS - Additional C++ compiler flags ## LDFLAGS - Additional compiler flags during linking ## LIBS - Additional libraries to use (for all tests) ## CROSS - Tool prefix for cross compilation # # The following variables are defined from the corresponding # environment variables if set. # | > > < < | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#
# The following environment variables are used if set:
#
## CC - C compiler
## CXX - C++ compiler
## CPP - C preprocessor
## CCACHE - Set to "none" to disable automatic use of ccache
## CPPFLAGS - Additional C preprocessor compiler flags (C and C++), before CFLAGS, CXXFLAGS
## CFLAGS - Additional C compiler flags
## CXXFLAGS - Additional C++ compiler flags
## LDFLAGS - Additional compiler flags during linking
## LINKFLAGS - ?How is this different from LDFLAGS?
## LIBS - Additional libraries to use (for all tests)
## CROSS - Tool prefix for cross compilation
#
# The following variables are defined from the corresponding
# environment variables if set.
#
## CC_FOR_BUILD
## LD
use system
options {}
# Checks for the existence of the given function by linking
#
proc cctest_function {function} {
cctest -link 1 -declare "extern void $function\(void);" -code "$function\();"
}
|
| ︙ | ︙ | |||
260 261 262 263 264 265 266 |
#
# It is an error if the executable is not found.
#
proc cc-check-tools {args} {
foreach tool $args {
set TOOL [string toupper $tool]
set exe [get-env $TOOL [get-define cross]$tool]
| | | | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
#
# It is an error if the executable is not found.
#
proc cc-check-tools {args} {
foreach tool $args {
set TOOL [string toupper $tool]
set exe [get-env $TOOL [get-define cross]$tool]
if {[find-executable $exe]} {
define $TOOL $exe
continue
}
if {[find-executable $tool]} {
msg-result "Warning: Failed to find $exe, falling back to $tool which may be incorrect"
define $TOOL $tool
continue
}
user-error "Failed to find $exe"
}
}
|
| ︙ | ︙ | |||
414 415 416 417 418 419 420 421 422 423 424 425 426 427 |
#
## cc-with {-libs {-lc -lm}} {
## cc-with {-libs -ldl} {
## cctest -libs -lsocket ...
## # libs will be in this order: -lsocket -ldl -lc -lm
## }
## }
proc cc-with {settings args} {
if {[llength $args] == 0} {
cc-add-settings $settings
} elseif {[llength $args] > 1} {
autosetup-error "usage: cc-with settings ?script?"
} else {
set save [cc-add-settings $settings]
| > > > > > > > | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
#
## cc-with {-libs {-lc -lm}} {
## cc-with {-libs -ldl} {
## cctest -libs -lsocket ...
## # libs will be in this order: -lsocket -ldl -lc -lm
## }
## }
#
# If you wish to invoke something like cc-check-flags but not have -cflags updated,
# use the following idiom:
#
## cc-with {} {
## cc-check-flags ...
## }
proc cc-with {settings args} {
if {[llength $args] == 0} {
cc-add-settings $settings
} elseif {[llength $args] > 1} {
autosetup-error "usage: cc-with settings ?script?"
} else {
set save [cc-add-settings $settings]
|
| ︙ | ︙ | |||
451 452 453 454 455 456 457 | ## -source code Compile a complete program. Ignore -includes, -declare and -code ## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file] ## -nooutput 1 Treat any compiler output (e.g. a warning) as an error # # Unless '-source' or '-sourcefile' is specified, the C program looks like: # ## #include <firstinclude> /* same for remaining includes in the list */ | < < > > > > > > > > > > | 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 |
## -source code Compile a complete program. Ignore -includes, -declare and -code
## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file]
## -nooutput 1 Treat any compiler output (e.g. a warning) as an error
#
# Unless '-source' or '-sourcefile' is specified, the C program looks like:
#
## #include <firstinclude> /* same for remaining includes in the list */
## declare-code /* any code in -declare, verbatim */
## int main(void) {
## code /* any code in -code, verbatim */
## return 0;
## }
#
# And the command line looks like:
#
## CC -cflags CFLAGS CPPFLAGS conftest.c -o conftest.o
## CXX -cflags CXXFLAGS CPPFLAGS conftest.cpp -o conftest.o
#
# And if linking:
#
## CC LDFLAGS -cflags CFLAGS conftest.c -o conftest -libs LIBS
## CXX LDFLAGS -cflags CXXFLAGS conftest.c -o conftest -libs LIBS
#
# Any failures are recorded in 'config.log'
#
proc cctest {args} {
set tmp conftest__
# Easiest way to merge in the settings
cc-with $args {
|
| ︙ | ︙ | |||
504 505 506 507 508 509 510 |
# Build the command line
set cmdline {}
lappend cmdline {*}[get-define CCACHE]
switch -exact -- $opts(-lang) {
c++ {
set src conftest__.cpp
| | > | > > | | | | 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 |
# Build the command line
set cmdline {}
lappend cmdline {*}[get-define CCACHE]
switch -exact -- $opts(-lang) {
c++ {
set src conftest__.cpp
lappend cmdline {*}[get-define CXX]
set cflags [get-define CXXFLAGS]
}
c {
set src conftest__.c
lappend cmdline {*}[get-define CC]
set cflags [get-define CFLAGS]
}
default {
autosetup-error "cctest called with unknown language: $opts(-lang)"
}
}
if {$opts(-link)} {
lappend cmdline {*}[get-define LDFLAGS]
} else {
lappend cflags {*}[get-define CPPFLAGS]
set tmp conftest__.o
lappend cmdline -c
}
lappend cmdline {*}$opts(-cflags) {*}[get-define cc-default-debug ""] {*}$cflags
lappend cmdline $src -o $tmp
if {$opts(-link)} {
lappend cmdline {*}$opts(-libs) {*}[get-define LIBS]
}
# At this point we have the complete command line and the
# complete source to be compiled. Get the result from cache if
# we can
if {[info exists ::cc_cache($cmdline,$lines)]} {
msg-checking "(cached) "
|
| ︙ | ︙ | |||
656 657 658 659 660 661 662 | } } } return "" } # Initialise some values from the environment or commandline or default settings | | | | > > > > > | | | | 674 675 676 677 678 679 680 681 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 |
}
}
}
return ""
}
# Initialise some values from the environment or commandline or default settings
foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS CFLAGS} {
lassign $i var default
define $var [get-env $var $default]
}
if {[env-is-set CC]} {
# Set by the user, so don't try anything else
set try [list [get-env CC ""]]
} else {
# Try some reasonable options
set try [list [get-define cross]cc [get-define cross]gcc]
}
define CC [find-an-executable {*}$try]
if {[get-define CC] eq ""} {
user-error "Could not find a C compiler. Tried: [join $try ", "]"
}
define CPP [get-env CPP "[get-define CC] -E"]
# XXX: Could avoid looking for a C++ compiler until requested
# If CXX isn't found, it is set to the empty string.
if {[env-is-set CXX]} {
define CXX [find-an-executable -required [get-env CXX ""]]
} else {
define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++]
}
# CXXFLAGS default to CFLAGS if not specified
define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]]
# May need a CC_FOR_BUILD, so look for one
define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false]
if {[get-define CC] eq ""} {
user-error "Could not find a C compiler. Tried: [join $try ", "]"
}
# These start empty and never come from the user or environment
define AS_CFLAGS ""
define AS_CPPFLAGS ""
define AS_CXXFLAGS ""
define CCACHE [find-an-executable [get-env CCACHE ccache]]
# If any of these are set in the environment, propagate them to the AUTOREMAKE commandline
foreach i {CC CXX CCACHE CPP CFLAGS CXXFLAGS CXXFLAGS LDFLAGS LIBS CROSS CPPFLAGS LINKFLAGS CC_FOR_BUILD LD} {
if {[env-is-set $i]} {
# Note: If the variable is set on the command line, get-env will return that value
# so the command line will continue to override the environment
define-append-argv AUTOREMAKE $i=[get-env $i ""]
}
}
# Initial cctest settings
cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0}
set autosetup(cc-include-deps) {}
msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS] [get-define CPPFLAGS]"
if {[get-define CXX] ne "false"} {
msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS] [get-define CPPFLAGS]"
}
msg-result "Build C compiler...[get-define CC_FOR_BUILD]"
# On Darwin, we prefer to use -g0 to avoid creating .dSYM directories
# but some compilers may not support it, so test here.
switch -glob -- [get-define host] {
*-*-darwin* {
|
| ︙ | ︙ |
Changes to autosetup/jimsh0.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */ #define JIM_TCL_COMPAT #define JIM_ANSIC #define JIM_REGEXP #define HAVE_NO_AUTOCONF #define _JIMAUTOCONF_H #define TCL_LIBRARY "." #define jim_ext_bootstrap #define jim_ext_aio #define jim_ext_readdir #define jim_ext_regexp #define jim_ext_file #define jim_ext_glob #define jim_ext_exec #define jim_ext_clock #define jim_ext_array #define jim_ext_stdlib #define jim_ext_tclcompat #if defined(_MSC_VER) #define TCL_PLATFORM_OS "windows" #define TCL_PLATFORM_PLATFORM "windows" | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /* This is single source file, bootstrap version of Jim Tcl. See http://jim.tcl.tk/ */ #define JIM_TCL_COMPAT #define JIM_ANSIC #define JIM_REGEXP #define HAVE_NO_AUTOCONF #define JIM_TINY #define _JIMAUTOCONF_H #define TCL_LIBRARY "." #define jim_ext_bootstrap #define jim_ext_aio #define jim_ext_readdir #define jim_ext_regexp #define jim_ext_file #define jim_ext_glob #define jim_ext_exec #define jim_ext_posix #define jim_ext_clock #define jim_ext_array #define jim_ext_stdlib #define jim_ext_tclcompat #if defined(_MSC_VER) #define TCL_PLATFORM_OS "windows" #define TCL_PLATFORM_PLATFORM "windows" |
| ︙ | ︙ | |||
45 46 47 48 49 50 51 | #define TCL_PLATFORM_PATH_SEPARATOR ":" #ifdef _MINIX #define vfork fork #define _POSIX_SOURCE #else #define _GNU_SOURCE #endif | | > > | | 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 |
#define TCL_PLATFORM_PATH_SEPARATOR ":"
#ifdef _MINIX
#define vfork fork
#define _POSIX_SOURCE
#else
#define _GNU_SOURCE
#endif
#define HAVE_FORK
#define HAVE_WAITPID
#define HAVE_ISATTY
#define HAVE_MKSTEMP
#define HAVE_LINK
#define HAVE_SYS_TIME_H
#define HAVE_DIRENT_H
#define HAVE_UNISTD_H
#define HAVE_UMASK
#define HAVE_PIPE
#define _FILE_OFFSET_BITS 64
#endif
#define JIM_VERSION 83
#ifndef JIM_WIN32COMPAT_H
#define JIM_WIN32COMPAT_H
#ifdef __cplusplus
extern "C" {
|
| ︙ | ︙ | |||
182 183 184 185 186 187 188 |
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
#include <limits.h>
| < | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
#include <limits.h>
#include <stdlib.h>
#include <stdarg.h>
#ifndef HAVE_NO_AUTOCONF
#endif
|
| ︙ | ︙ | |||
221 222 223 224 225 226 227 228 229 230 231 232 233 234 | # define strtoull strtoul # endif #endif #define UCHAR(c) ((unsigned char)(c)) #define JIM_OK 0 #define JIM_ERR 1 #define JIM_RETURN 2 #define JIM_BREAK 3 #define JIM_CONTINUE 4 #define JIM_SIGNAL 5 #define JIM_EXIT 6 | > > > | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | # define strtoull strtoul # endif #endif #define UCHAR(c) ((unsigned char)(c)) #define JIM_ABI_VERSION 101 #define JIM_OK 0 #define JIM_ERR 1 #define JIM_RETURN 2 #define JIM_BREAK 3 #define JIM_CONTINUE 4 #define JIM_SIGNAL 5 #define JIM_EXIT 6 |
| ︙ | ︙ | |||
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | #define JIM_PRIV_FLAG_SHIFT 20 #define JIM_NONE 0 #define JIM_ERRMSG 1 #define JIM_ENUM_ABBREV 2 #define JIM_UNSHARED 4 #define JIM_MUSTEXIST 8 #define JIM_SUBST_NOVAR 1 #define JIM_SUBST_NOCMD 2 #define JIM_SUBST_NOESC 4 #define JIM_SUBST_FLAG 128 #define JIM_CASESENS 0 #define JIM_NOCASE 1 #define JIM_PATH_LEN 1024 #define JIM_NOTUSED(V) ((void) V) | > > | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | #define JIM_PRIV_FLAG_SHIFT 20 #define JIM_NONE 0 #define JIM_ERRMSG 1 #define JIM_ENUM_ABBREV 2 #define JIM_UNSHARED 4 #define JIM_MUSTEXIST 8 #define JIM_NORESULT 16 #define JIM_SUBST_NOVAR 1 #define JIM_SUBST_NOCMD 2 #define JIM_SUBST_NOESC 4 #define JIM_SUBST_FLAG 128 #define JIM_CASESENS 0 #define JIM_NOCASE 1 #define JIM_OPT_END 2 #define JIM_PATH_LEN 1024 #define JIM_NOTUSED(V) ((void) V) |
| ︙ | ︙ | |||
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 |
#define Jim_SetHashVal(ht, entry, _val_) do { \
if ((ht)->type->valDup) \
(entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
else \
(entry)->u.val = (_val_); \
} while(0)
#define Jim_FreeEntryKey(ht, entry) \
if ((ht)->type->keyDestructor) \
(ht)->type->keyDestructor((ht)->privdata, (entry)->key)
#define Jim_SetHashKey(ht, entry, _key_) do { \
if ((ht)->type->keyDup) \
(entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
else \
(entry)->key = (void *)(_key_); \
} while(0)
#define Jim_CompareHashKeys(ht, key1, key2) \
(((ht)->type->keyCompare) ? \
(ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
(key1) == (key2))
#define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
#define Jim_GetHashEntryKey(he) ((he)->key)
#define Jim_GetHashEntryVal(he) ((he)->u.val)
#define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
#define Jim_GetHashTableSize(ht) ((ht)->size)
#define Jim_GetHashTableUsed(ht) ((ht)->used)
typedef struct Jim_Obj {
char *bytes;
| > > > | 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 |
#define Jim_SetHashVal(ht, entry, _val_) do { \
if ((ht)->type->valDup) \
(entry)->u.val = (ht)->type->valDup((ht)->privdata, (_val_)); \
else \
(entry)->u.val = (_val_); \
} while(0)
#define Jim_SetHashIntVal(ht, entry, _val_) (entry)->u.intval = (_val_)
#define Jim_FreeEntryKey(ht, entry) \
if ((ht)->type->keyDestructor) \
(ht)->type->keyDestructor((ht)->privdata, (entry)->key)
#define Jim_SetHashKey(ht, entry, _key_) do { \
if ((ht)->type->keyDup) \
(entry)->key = (ht)->type->keyDup((ht)->privdata, (_key_)); \
else \
(entry)->key = (void *)(_key_); \
} while(0)
#define Jim_CompareHashKeys(ht, key1, key2) \
(((ht)->type->keyCompare) ? \
(ht)->type->keyCompare((ht)->privdata, (key1), (key2)) : \
(key1) == (key2))
#define Jim_HashKey(ht, key) ((ht)->type->hashFunction(key) + (ht)->uniq)
#define Jim_GetHashEntryKey(he) ((he)->key)
#define Jim_GetHashEntryVal(he) ((he)->u.val)
#define Jim_GetHashEntryIntVal(he) ((he)->u.intval)
#define Jim_GetHashTableCollisions(ht) ((ht)->collisions)
#define Jim_GetHashTableSize(ht) ((ht)->size)
#define Jim_GetHashTableUsed(ht) ((ht)->used)
typedef struct Jim_Obj {
char *bytes;
|
| ︙ | ︙ | |||
373 374 375 376 377 378 379 |
struct {
void *ptr;
int int1;
int int2;
} ptrIntValue;
struct {
| | > > | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
struct {
void *ptr;
int int1;
int int2;
} ptrIntValue;
struct {
struct Jim_VarVal *vv;
unsigned long callFrameId;
int global;
} varValue;
struct {
struct Jim_Obj *nsObj;
struct Jim_Cmd *cmdPtr;
unsigned long procEpoch;
} cmdValue;
struct {
struct Jim_Obj **ele;
int len;
int maxLen;
} listValue;
struct Jim_Dict *dictValue;
struct {
int maxLength;
int charLength;
} strValue;
struct {
|
| ︙ | ︙ | |||
475 476 477 478 479 480 481 |
struct Jim_CallFrame *parent;
Jim_Obj *const *argv;
int argc;
Jim_Obj *procArgsObjPtr;
Jim_Obj *procBodyObjPtr;
struct Jim_CallFrame *next;
Jim_Obj *nsObj;
| | | > | > > > > > > > > > > > > | | > > > > > > > > > > > > > | 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 |
struct Jim_CallFrame *parent;
Jim_Obj *const *argv;
int argc;
Jim_Obj *procArgsObjPtr;
Jim_Obj *procBodyObjPtr;
struct Jim_CallFrame *next;
Jim_Obj *nsObj;
Jim_Obj *unused_fileNameObj;
int unused_line;
Jim_Stack *localCommands;
struct Jim_Obj *tailcallObj;
struct Jim_Cmd *tailcallCmd;
} Jim_CallFrame;
typedef struct Jim_EvalFrame {
Jim_CallFrame *framePtr;
int level;
int procLevel;
struct Jim_Cmd *cmd;
struct Jim_EvalFrame *parent;
Jim_Obj *const *argv;
int argc;
Jim_Obj *scriptObj;
} Jim_EvalFrame;
typedef struct Jim_VarVal {
Jim_Obj *objPtr;
struct Jim_CallFrame *linkFramePtr;
int refCount;
} Jim_VarVal;
typedef int Jim_CmdProc(struct Jim_Interp *interp, int argc,
Jim_Obj *const *argv);
typedef void Jim_DelCmdProc(struct Jim_Interp *interp, void *privData);
typedef struct Jim_Dict {
struct JimDictHashEntry {
int offset;
unsigned hash;
} *ht;
unsigned int size;
unsigned int sizemask;
unsigned int uniq;
Jim_Obj **table;
int len;
int maxLen;
unsigned int dummy;
} Jim_Dict;
typedef struct Jim_Cmd {
int inUse;
int isproc;
struct Jim_Cmd *prevCmd;
Jim_Obj *cmdNameObj;
union {
struct {
Jim_CmdProc *cmdProc;
Jim_DelCmdProc *delProc;
void *privData;
} native;
|
| ︙ | ︙ | |||
532 533 534 535 536 537 538 |
typedef struct Jim_PrngState {
unsigned char sbox[256];
unsigned int i, j;
} Jim_PrngState;
typedef struct Jim_Interp {
Jim_Obj *result;
| | | | | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 |
typedef struct Jim_PrngState {
unsigned char sbox[256];
unsigned int i, j;
} Jim_PrngState;
typedef struct Jim_Interp {
Jim_Obj *result;
int unused_errorLine;
Jim_Obj *currentFilenameObj;
int break_level;
int maxCallFrameDepth;
int maxEvalDepth;
int evalDepth;
int returnCode;
int returnLevel;
int exitCode;
long id;
|
| ︙ | ︙ | |||
556 557 558 559 560 561 562 563 564 |
of procedures names lookup caching
may no longer be valid. */
unsigned long callFrameEpoch; /* Incremented every time a new
callframe is created. This id is used for the
'ID' field contained in the Jim_CallFrame
structure. */
int local;
Jim_Obj *liveList;
Jim_Obj *freeList;
| > > | > > > > | > > > > < | 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 647 648 649 |
of procedures names lookup caching
may no longer be valid. */
unsigned long callFrameEpoch; /* Incremented every time a new
callframe is created. This id is used for the
'ID' field contained in the Jim_CallFrame
structure. */
int local;
int quitting;
int safeexpr;
Jim_Obj *liveList;
Jim_Obj *freeList;
Jim_Obj *unused_currentScriptObj;
Jim_EvalFrame topEvalFrame;
Jim_EvalFrame *evalFrame;
int procLevel;
Jim_Obj * const *unused_argv;
Jim_Obj *nullScriptObj;
Jim_Obj *emptyObj;
Jim_Obj *trueObj;
Jim_Obj *falseObj;
unsigned long referenceNextId;
struct Jim_HashTable references;
unsigned long lastCollectId; /* reference max Id of the last GC
execution. It's set to ~0 while the collection
is running as sentinel to avoid to recursive
calls via the [collect] command inside
finalizers. */
jim_wide lastCollectTime;
Jim_Obj *stackTrace;
Jim_Obj *errorProc;
Jim_Obj *unknown;
Jim_Obj *defer;
Jim_Obj *traceCmdObj;
int unknown_called;
int errorFlag;
void *cmdPrivData; /* Used to pass the private data pointer to
a command. It is set to what the user specified
via Jim_CreateCommand(). */
Jim_Cmd *oldCmdCache;
int oldCmdCacheSize;
struct Jim_CallFrame *freeFramesList;
struct Jim_HashTable assocData;
Jim_PrngState *prngState;
struct Jim_HashTable packages;
Jim_Stack *loadHandles;
} Jim_Interp;
#define Jim_SetResultString(i,s,l) Jim_SetResult(i, Jim_NewStringObj(i,s,l))
#define Jim_SetResultInt(i,intval) Jim_SetResult(i, Jim_NewIntObj(i,intval))
#define Jim_SetResultBool(i,b) Jim_SetResultInt(i, b)
#define Jim_SetEmptyResult(i) Jim_SetResult(i, (i)->emptyObj)
#define Jim_GetResult(i) ((i)->result)
#define Jim_CmdPrivData(i) ((i)->cmdPrivData)
|
| ︙ | ︙ | |||
619 620 621 622 623 624 625 |
char tag[JIM_REFERENCE_TAGLEN+1];
} Jim_Reference;
#define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
#define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
| | | | | > > > > > > > > > > > > > | 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 |
char tag[JIM_REFERENCE_TAGLEN+1];
} Jim_Reference;
#define Jim_NewEmptyStringObj(i) Jim_NewStringObj(i, "", 0)
#define Jim_FreeHashTableIterator(iter) Jim_Free(iter)
#define JIM_EXPORT extern
JIM_EXPORT void *(*Jim_Allocator)(void *ptr, size_t size);
#define Jim_Free(P) Jim_Allocator((P), 0)
#define Jim_Realloc(P, S) Jim_Allocator((P), (S))
#define Jim_Alloc(S) Jim_Allocator(NULL, (S))
JIM_EXPORT char * Jim_StrDup (const char *s);
JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
JIM_EXPORT char **Jim_GetEnviron(void);
JIM_EXPORT void Jim_SetEnviron(char **env);
JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file);
#ifndef CLOCK_REALTIME
# define CLOCK_REALTIME 0
#endif
#ifndef CLOCK_MONOTONIC
# define CLOCK_MONOTONIC 1
#endif
#ifndef CLOCK_MONOTONIC_RAW
# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
#endif
JIM_EXPORT jim_wide Jim_GetTimeUsec(unsigned type);
JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
JIM_EXPORT int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script);
|
| ︙ | ︙ | |||
655 656 657 658 659 660 661 662 663 664 665 666 667 668 |
JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
int objc, Jim_Obj *const *objv);
#define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
Jim_Obj **resObjPtrPtr, int flags);
JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
| > > > > > > > > | 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 |
JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix,
int objc, Jim_Obj *const *objv);
#define Jim_EvalPrefix(i, p, oc, ov) Jim_EvalObjPrefix((i), Jim_NewStringObj((i), (p), -1), (oc), (ov))
JIM_EXPORT int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj);
JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr,
Jim_Obj **resObjPtrPtr, int flags);
JIM_EXPORT Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
int *lineptr);
JIM_EXPORT void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *fileNameObj, int lineNumber);
JIM_EXPORT void Jim_InitStack(Jim_Stack *stack);
JIM_EXPORT void Jim_FreeStack(Jim_Stack *stack);
JIM_EXPORT int Jim_StackLen(Jim_Stack *stack);
JIM_EXPORT void Jim_StackPush(Jim_Stack *stack, void *element);
JIM_EXPORT void * Jim_StackPop(Jim_Stack *stack);
JIM_EXPORT void * Jim_StackPeek(Jim_Stack *stack);
|
| ︙ | ︙ | |||
678 679 680 681 682 683 684 |
JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
const void *key, void *val);
JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
const void *key);
JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
const void *key);
| < | 747 748 749 750 751 752 753 754 755 756 757 758 759 760 |
JIM_EXPORT int Jim_ReplaceHashEntry (Jim_HashTable *ht,
const void *key, void *val);
JIM_EXPORT int Jim_DeleteHashEntry (Jim_HashTable *ht,
const void *key);
JIM_EXPORT int Jim_FreeHashTable (Jim_HashTable *ht);
JIM_EXPORT Jim_HashEntry * Jim_FindHashEntry (Jim_HashTable *ht,
const void *key);
JIM_EXPORT Jim_HashTableIterator *Jim_GetHashTableIterator
(Jim_HashTable *ht);
JIM_EXPORT Jim_HashEntry * Jim_NextHashEntry
(Jim_HashTableIterator *iter);
JIM_EXPORT Jim_Obj * Jim_NewObj (Jim_Interp *interp);
|
| ︙ | ︙ | |||
722 723 724 725 726 727 728 |
Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
Jim_Obj *fmtObjPtr, int flags);
JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
Jim_Obj *objPtr, const char *str);
JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
Jim_Obj *secondObjPtr, int nocase);
| < < | 790 791 792 793 794 795 796 797 798 799 800 801 802 803 |
Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv);
JIM_EXPORT Jim_Obj * Jim_ScanString (Jim_Interp *interp, Jim_Obj *strObjPtr,
Jim_Obj *fmtObjPtr, int flags);
JIM_EXPORT int Jim_CompareStringImmediate (Jim_Interp *interp,
Jim_Obj *objPtr, const char *str);
JIM_EXPORT int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr,
Jim_Obj *secondObjPtr, int nocase);
JIM_EXPORT int Jim_Utf8Length(Jim_Interp *interp, Jim_Obj *objPtr);
JIM_EXPORT Jim_Obj * Jim_NewReference (Jim_Interp *interp,
Jim_Obj *objPtr, Jim_Obj *tagPtr, Jim_Obj *cmdNamePtr);
JIM_EXPORT Jim_Reference * Jim_GetReference (Jim_Interp *interp,
Jim_Obj *objPtr);
|
| ︙ | ︙ | |||
747 748 749 750 751 752 753 |
JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
Jim_DelCmdProc *delProc);
JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
| | | | 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 |
JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
JIM_EXPORT int Jim_CreateCommand (Jim_Interp *interp,
const char *cmdName, Jim_CmdProc *cmdProc, void *privData,
Jim_DelCmdProc *delProc);
JIM_EXPORT int Jim_DeleteCommand (Jim_Interp *interp,
Jim_Obj *cmdNameObj);
JIM_EXPORT int Jim_RenameCommand (Jim_Interp *interp,
Jim_Obj *oldNameObj, Jim_Obj *newNameObj);
JIM_EXPORT Jim_Cmd * Jim_GetCommand (Jim_Interp *interp,
Jim_Obj *objPtr, int flags);
JIM_EXPORT int Jim_SetVariable (Jim_Interp *interp,
Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr);
JIM_EXPORT int Jim_SetVariableStr (Jim_Interp *interp,
const char *name, Jim_Obj *objPtr);
JIM_EXPORT int Jim_SetGlobalVariableStr (Jim_Interp *interp,
|
| ︙ | ︙ | |||
820 821 822 823 824 825 826 |
Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
Jim_Obj **objPtrPtr, int flags);
JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
Jim_Obj *newObjPtr, int flags);
| | | | 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 |
Jim_Obj *keyPtr, Jim_Obj **objPtrPtr, int flags);
JIM_EXPORT int Jim_DictKeysVector (Jim_Interp *interp,
Jim_Obj *dictPtr, Jim_Obj *const *keyv, int keyc,
Jim_Obj **objPtrPtr, int flags);
JIM_EXPORT int Jim_SetDictKeysVector (Jim_Interp *interp,
Jim_Obj *varNamePtr, Jim_Obj *const *keyv, int keyc,
Jim_Obj *newObjPtr, int flags);
JIM_EXPORT Jim_Obj **Jim_DictPairs(Jim_Interp *interp,
Jim_Obj *dictPtr, int *len);
JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr);
#define JIM_DICTMATCH_KEYS 0x0001
#define JIM_DICTMATCH_VALUES 0x002
JIM_EXPORT int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types);
|
| ︙ | ︙ | |||
849 850 851 852 853 854 855 856 857 858 859 860 861 862 |
JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
int *booleanPtr);
JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
jim_wide *widePtr);
JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
long *longPtr);
#define Jim_NewWideObj Jim_NewIntObj
JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
jim_wide wideValue);
| > > | 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 |
JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
int *booleanPtr);
JIM_EXPORT int Jim_GetWide (Jim_Interp *interp, Jim_Obj *objPtr,
jim_wide *widePtr);
JIM_EXPORT int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr,
jim_wide *widePtr);
JIM_EXPORT int Jim_GetLong (Jim_Interp *interp, Jim_Obj *objPtr,
long *longPtr);
#define Jim_NewWideObj Jim_NewIntObj
JIM_EXPORT Jim_Obj * Jim_NewIntObj (Jim_Interp *interp,
jim_wide wideValue);
|
| ︙ | ︙ | |||
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 |
typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
Jim_InterpDeleteProc *delProc, void *data);
JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
const char *name, const char *ver, int flags);
JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
const char *name, int flags);
JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
JIM_EXPORT void Jim_HistoryLoad(const char *filename);
JIM_EXPORT void Jim_HistorySave(const char *filename);
JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt);
| > > > > > | > > > > | | 949 950 951 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 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 |
typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
JIM_EXPORT void * Jim_GetAssocData(Jim_Interp *interp, const char *key);
JIM_EXPORT int Jim_SetAssocData(Jim_Interp *interp, const char *key,
Jim_InterpDeleteProc *delProc, void *data);
JIM_EXPORT int Jim_DeleteAssocData(Jim_Interp *interp, const char *key);
JIM_EXPORT int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version);
JIM_EXPORT int Jim_PackageProvide (Jim_Interp *interp,
const char *name, const char *ver, int flags);
JIM_EXPORT int Jim_PackageRequire (Jim_Interp *interp,
const char *name, int flags);
#define Jim_PackageProvideCheck(INTERP, NAME) \
if (Jim_CheckAbiVersion(INTERP, JIM_ABI_VERSION) == JIM_ERR || Jim_PackageProvide(INTERP, NAME, "1.0", JIM_ERRMSG)) \
return JIM_ERR
JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp);
JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp);
JIM_EXPORT void Jim_HistoryLoad(const char *filename);
JIM_EXPORT void Jim_HistorySave(const char *filename);
JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt);
JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj);
JIM_EXPORT void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj);
JIM_EXPORT void Jim_HistoryAdd(const char *line);
JIM_EXPORT void Jim_HistoryShow(void);
JIM_EXPORT void Jim_HistorySetMaxLen(int length);
JIM_EXPORT int Jim_HistoryGetMaxLen(void);
JIM_EXPORT int Jim_InitStaticExtensions(Jim_Interp *interp);
JIM_EXPORT int Jim_StringToWide(const char *str, jim_wide *widePtr, int base);
JIM_EXPORT int Jim_IsBigEndian(void);
#define Jim_CheckSignal(i) ((i)->signal_level && (i)->sigmask)
JIM_EXPORT void Jim_SignalSetIgnored(jim_wide mask);
JIM_EXPORT int Jim_LoadLibrary(Jim_Interp *interp, const char *pathName);
JIM_EXPORT void Jim_FreeLoadHandles(Jim_Interp *interp);
JIM_EXPORT int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command);
JIM_EXPORT int Jim_IsDict(Jim_Obj *objPtr);
JIM_EXPORT int Jim_IsList(Jim_Obj *objPtr);
#ifdef __cplusplus
}
|
| ︙ | ︙ | |||
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 | const char *args; jim_subcmd_function *function; short minargs; short maxargs; unsigned short flags; } jim_subcmd_type; const jim_subcmd_type * Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv); int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv); int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv); #ifdef __cplusplus } #endif #endif #ifndef JIMREGEXP_H | > > > > > | 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 |
const char *args;
jim_subcmd_function *function;
short minargs;
short maxargs;
unsigned short flags;
} jim_subcmd_type;
#define JIM_DEF_SUBCMD(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs }
#define JIM_DEF_SUBCMD_HIDDEN(name, args, minargs, maxargs) { name, args, NULL, minargs, maxargs, JIM_MODFLAG_HIDDEN }
const jim_subcmd_type *
Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type *command_table, int argc, Jim_Obj *const *argv);
int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
int Jim_CallSubCmd(Jim_Interp *interp, const jim_subcmd_type *ct, int argc, Jim_Obj *const *argv);
void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type *ct, Jim_Obj *subcmd);
#ifdef __cplusplus
}
#endif
#endif
#ifndef JIMREGEXP_H
|
| ︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 | REG_ERR_UNMATCHED_BRACES, REG_ERR_BAD_COUNT, REG_ERR_JUNK_ON_END, REG_ERR_OPERAND_COULD_BE_EMPTY, REG_ERR_NESTED_COUNT, REG_ERR_INTERNAL, REG_ERR_COUNT_FOLLOWS_NOTHING, | | > | | | | | 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 | REG_ERR_UNMATCHED_BRACES, REG_ERR_BAD_COUNT, REG_ERR_JUNK_ON_END, REG_ERR_OPERAND_COULD_BE_EMPTY, REG_ERR_NESTED_COUNT, REG_ERR_INTERNAL, REG_ERR_COUNT_FOLLOWS_NOTHING, REG_ERR_INVALID_ESCAPE, REG_ERR_CORRUPTED, REG_ERR_NULL_CHAR, REG_ERR_UNMATCHED_BRACKET, REG_ERR_NUM }; int jim_regcomp(regex_t *preg, const char *regex, int cflags); int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags); size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size); void jim_regfree(regex_t *preg); #ifdef __cplusplus } #endif #endif #ifndef JIM_SIGNAL_H |
| ︙ | ︙ | |||
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 |
#endif
#ifndef JIMIOCOMPAT_H
#define JIMIOCOMPAT_H
#include <stdio.h>
#include <errno.h>
void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);
int Jim_OpenForWrite(const char *filename, int append);
int Jim_OpenForRead(const char *filename);
#if defined(__MINGW32__)
#ifndef STRICT
#define STRICT
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
| > | < < | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < | | | > | | | > > > > > > > > < | > | < > | 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 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 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 |
#endif
#ifndef JIMIOCOMPAT_H
#define JIMIOCOMPAT_H
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);
int Jim_OpenForWrite(const char *filename, int append);
int Jim_OpenForRead(const char *filename);
#if defined(__MINGW32__)
#ifndef STRICT
#define STRICT
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
typedef HANDLE phandle_t;
#define JIM_BAD_PHANDLE INVALID_HANDLE_VALUE
#define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0)
#define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff)
#define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0)
#define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff)
#define WNOHANG 1
int Jim_Errno(void);
long waitpid(phandle_t phandle, int *status, int nohang);
phandle_t JimWaitPid(long processid, int *status, int nohang);
long JimProcessPid(phandle_t phandle);
#define HAVE_PIPE
#define pipe(P) _pipe((P), 0, O_NOINHERIT)
typedef struct __stat64 jim_stat_t;
#define Jim_Stat _stat64
#define Jim_FileStat _fstat64
#define Jim_Lseek _lseeki64
#else
#if defined(HAVE_STAT64)
typedef struct stat64 jim_stat_t;
#define Jim_Stat stat64
#if defined(HAVE_FSTAT64)
#define Jim_FileStat fstat64
#endif
#if defined(HAVE_LSTAT64)
#define Jim_LinkStat lstat64
#endif
#else
typedef struct stat jim_stat_t;
#define Jim_Stat stat
#if defined(HAVE_FSTAT)
#define Jim_FileStat fstat
#endif
#if defined(HAVE_LSTAT)
#define Jim_LinkStat lstat
#endif
#endif
#if defined(HAVE_LSEEK64)
#define Jim_Lseek lseek64
#else
#define Jim_Lseek lseek
#endif
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
typedef int phandle_t;
#define Jim_Errno() errno
#define JIM_BAD_PHANDLE -1
#define JimProcessPid(PIDTYPE) (PIDTYPE)
#define JimWaitPid waitpid
#ifndef HAVE_EXECVPE
#define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
#endif
#endif
#endif
#ifndef O_TEXT
#define O_TEXT 0
#endif
int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb);
#endif
int Jim_bootstrapInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "bootstrap", "1.0", JIM_ERRMSG))
return JIM_ERR;
return Jim_EvalSource(interp, "bootstrap.tcl", 1,
"\n"
"proc package {cmd args} {\n"
" if {$cmd eq \"require\"} {\n"
" foreach path $::auto_path {\n"
" lassign $args pkg\n"
" set pkgpath $path/$pkg.tcl\n"
" if {$path eq \".\"} {\n"
" set pkgpath $pkg.tcl\n"
" }\n"
" if {[file exists $pkgpath]} {\n"
" tailcall uplevel #0 [list source $pkgpath]\n"
" }\n"
" }\n"
" }\n"
"}\n"
"set tcl_platform(bootstrap) 1\n"
);
}
int Jim_initjimshInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "initjimsh", "1.0", JIM_ERRMSG))
return JIM_ERR;
|
| ︙ | ︙ | |||
1197 1198 1199 1200 1201 1202 1203 |
"}\n"
"\n"
"if {$tcl_platform(platform) eq \"windows\"} {\n"
" set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
"}\n"
"\n"
"\n"
| | | 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 |
"}\n"
"\n"
"if {$tcl_platform(platform) eq \"windows\"} {\n"
" set jim::argv0 [string map {\\\\ /} $jim::argv0]\n"
"}\n"
"\n"
"\n"
"set tcl::autocomplete_commands {array clock debug dict file history info namespace package signal socket string tcl::prefix zlib}\n"
"\n"
"\n"
"\n"
"proc tcl::autocomplete {prefix} {\n"
" if {[set space [string first \" \" $prefix]] != -1} {\n"
" set cmd [string range $prefix 0 $space-1]\n"
" if {$cmd in $::tcl::autocomplete_commands || [info channel $cmd] ne \"\"} {\n"
|
| ︙ | ︙ | |||
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 |
" if {[string match \"* *\" $p]} {\n"
" continue\n"
" }\n"
" function $p\n"
" }]\n"
"}\n"
"\n"
"_jimsh_init\n"
);
}
int Jim_globInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
return JIM_ERR;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 |
" if {[string match \"* *\" $p]} {\n"
" continue\n"
" }\n"
" function $p\n"
" }]\n"
"}\n"
"\n"
"\n"
"set tcl::stdhint_commands {array clock debug dict file history info namespace package signal string zlib}\n"
"\n"
"set tcl::stdhint_cols {\n"
" none {0}\n"
" black {30}\n"
" red {31}\n"
" green {32}\n"
" yellow {33}\n"
" blue {34}\n"
" purple {35}\n"
" cyan {36}\n"
" normal {37}\n"
" grey {30 1}\n"
" gray {30 1}\n"
" lred {31 1}\n"
" lgreen {32 1}\n"
" lyellow {33 1}\n"
" lblue {34 1}\n"
" lpurple {35 1}\n"
" lcyan {36 1}\n"
" white {37 1}\n"
"}\n"
"\n"
"\n"
"set tcl::stdhint_col $tcl::stdhint_cols(lcyan)\n"
"\n"
"\n"
"proc tcl::stdhint {string} {\n"
" set result \"\"\n"
" if {[llength $string] >= 2} {\n"
" lassign $string cmd arg\n"
" if {$cmd in $::tcl::stdhint_commands || [info channel $cmd] ne \"\"} {\n"
" catch {\n"
" set help [$cmd -help $arg]\n"
" if {[string match \"Usage: $cmd *\" $help]} {\n"
" set n [llength $string]\n"
" set subcmd [lindex $help $n]\n"
" incr n\n"
" set hint [join [lrange $help $n end]]\n"
" set prefix \"\"\n"
" if {![string match \"* \" $string]} {\n"
" if {$n == 3 && $subcmd ne $arg} {\n"
"\n"
" set prefix \"[string range $subcmd [string length $arg] end] \"\n"
" } else {\n"
" set prefix \" \"\n"
" }\n"
" }\n"
" set result [list $prefix$hint {*}$::tcl::stdhint_col]\n"
" }\n"
" }\n"
" }\n"
" }\n"
" return $result\n"
"}\n"
"\n"
"_jimsh_init\n"
);
}
int Jim_globInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "glob", "1.0", JIM_ERRMSG))
return JIM_ERR;
|
| ︙ | ︙ | |||
1478 1479 1480 1481 1482 1483 1484 |
"\n"
"\n"
"proc function {value} {\n"
" return $value\n"
"}\n"
"\n"
"\n"
| < < < < < < < < < < < < > | < < | | < | | > > > > > > > > | > < < | | 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 |
"\n"
"\n"
"proc function {value} {\n"
" return $value\n"
"}\n"
"\n"
"\n"
"proc stackdump {stacktrace} {\n"
" set lines {}\n"
" lappend lines \"Traceback (most recent call last):\"\n"
" foreach {cmd l f p} [lreverse $stacktrace] {\n"
" set line {}\n"
" if {$f ne \"\"} {\n"
" append line \" File \\\"$f\\\", line $l\"\n"
" }\n"
" if {$p ne \"\"} {\n"
" append line \", in $p\"\n"
" }\n"
" if {$line ne \"\"} {\n"
" lappend lines $line\n"
" if {$cmd ne \"\"} {\n"
" set nl [string first \\n $cmd 1]\n"
" if {$nl >= 0} {\n"
" set cmd [string range $cmd 0 $nl-1]...\n"
" }\n"
" lappend lines \" $cmd\"\n"
" }\n"
" }\n"
" }\n"
" if {[llength $lines] > 1} {\n"
" return [join $lines \\n]\n"
" }\n"
"}\n"
"\n"
"\n"
"\n"
"proc defer {script} {\n"
" upvar jim::defer v\n"
" lappend v $script\n"
"}\n"
"\n"
"\n"
"\n"
"proc errorInfo {msg {stacktrace \"\"}} {\n"
" if {$stacktrace eq \"\"} {\n"
"\n"
" set stacktrace [info stacktrace]\n"
" }\n"
" lassign $stacktrace p f l cmd\n"
" if {$f ne \"\"} {\n"
" set result \"$f:$l: Error: \"\n"
" }\n"
" append result \"$msg\\n\"\n"
" append result [stackdump $stacktrace]\n"
"\n"
"\n"
|
| ︙ | ︙ | |||
1639 1640 1641 1642 1643 1644 1645 | "\n" "\n" "\n" "\n" "set env [env]\n" "\n" "\n" | | | 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 |
"\n"
"\n"
"\n"
"\n"
"set env [env]\n"
"\n"
"\n"
"if {[exists -command stdout]} {\n"
"\n"
" foreach p {gets flush close eof seek tell} {\n"
" proc $p {chan args} {p} {\n"
" tailcall $chan $p {*}$args\n"
" }\n"
" }\n"
" unset p\n"
|
| ︙ | ︙ | |||
1813 1814 1815 1816 1817 1818 1819 | " return \"\"\n" " }\n" " return $pids\n" "}\n" "\n" "\n" "\n" | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > < < > | | < | | | | > > > > > > > > > > > > | > > > > | > > > | > > > > > > > > > > > > > > > | < < | | > < < < > > | | | | | < | > > > > > > > > | | < > | > | < | < < | > | < | | > < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 |
" return \"\"\n"
" }\n"
" return $pids\n"
"}\n"
"\n"
"\n"
"\n"
"proc throw {code {msg \"\"}} {\n"
" return -code $code $msg\n"
"}\n"
"\n"
"\n"
"proc {file delete force} {path} {\n"
" foreach e [readdir $path] {\n"
" file delete -force $path/$e\n"
" }\n"
" file delete $path\n"
"}\n"
);
}
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#include <sys/stat.h>
#endif
#ifdef HAVE_UTIL_H
#include <util.h>
#endif
#ifdef HAVE_PTY_H
#include <pty.h>
#endif
#if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
#define HAVE_SOCKETS
#elif defined (__MINGW32__)
#endif
#if defined(JIM_SSL)
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
#ifdef HAVE_TERMIOS_H
#endif
#define AIO_CMD_LEN 32
#define AIO_BUF_LEN 256
#define AIO_WBUF_FULL_SIZE (64 * 1024)
#define AIO_KEEPOPEN 1
#define AIO_NODELETE 2
#define AIO_EOF 4
#define AIO_WBUF_NONE 8
#define AIO_NONBLOCK 16
enum wbuftype {
WBUF_OPT_NONE,
WBUF_OPT_LINE,
WBUF_OPT_FULL,
};
#if defined(JIM_IPV6)
#define IPV6 1
#else
#define IPV6 0
#ifndef PF_INET6
#define PF_INET6 0
#endif
#endif
#if defined(HAVE_SYS_UN_H) && defined(PF_UNIX)
#define UNIX_SOCKETS 1
#else
#define UNIX_SOCKETS 0
#endif
#ifndef MAXPATHLEN
#define MAXPATHLEN JIM_PATH_LEN
#endif
static int JimReadableTimeout(int fd, long ms)
{
#ifdef HAVE_SELECT
int retval;
struct timeval tv;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
retval = select(fd + 1, &rfds, NULL, NULL, ms == 0 ? NULL : &tv);
if (retval > 0) {
return JIM_OK;
}
return JIM_ERR;
#else
return JIM_OK;
#endif
}
struct AioFile;
typedef struct {
int (*writer)(struct AioFile *af, const char *buf, int len);
int (*reader)(struct AioFile *af, char *buf, int len, int pending);
int (*error)(const struct AioFile *af);
const char *(*strerror)(struct AioFile *af);
int (*verify)(struct AioFile *af);
} JimAioFopsType;
typedef struct AioFile
{
Jim_Obj *filename;
int wbuft;
int flags;
long timeout;
int fd;
int addr_family;
void *ssl;
const JimAioFopsType *fops;
Jim_Obj *readbuf;
Jim_Obj *writebuf;
} AioFile;
static int stdio_writer(struct AioFile *af, const char *buf, int len)
{
return write(af->fd, buf, len);
}
static int stdio_reader(struct AioFile *af, char *buf, int len, int nb)
{
if (nb || af->timeout == 0 || JimReadableTimeout(af->fd, af->timeout) == JIM_OK) {
int ret;
errno = 0;
ret = read(af->fd, buf, len);
if (ret <= 0 && errno != EAGAIN && errno != EINTR) {
af->flags |= AIO_EOF;
}
return ret;
}
errno = ETIMEDOUT;
return -1;
}
static int stdio_error(const AioFile *af)
{
if (af->flags & AIO_EOF) {
return JIM_OK;
}
switch (errno) {
case EAGAIN:
case EINTR:
case ETIMEDOUT:
#ifdef ECONNRESET
case ECONNRESET:
#endif
#ifdef ECONNABORTED
case ECONNABORTED:
#endif
return JIM_OK;
default:
return JIM_ERR;
}
}
static const char *stdio_strerror(struct AioFile *af)
{
return strerror(errno);
}
static const JimAioFopsType stdio_fops = {
stdio_writer,
stdio_reader,
stdio_error,
stdio_strerror,
NULL,
};
static void aio_set_nonblocking(AioFile *af, int nb)
{
#ifdef O_NDELAY
int old = !!(af->flags & AIO_NONBLOCK);
if (old != nb) {
int fmode = fcntl(af->fd, F_GETFL);
if (nb) {
fmode |= O_NDELAY;
af->flags |= AIO_NONBLOCK;
}
else {
fmode &= ~O_NDELAY;
af->flags &= ~AIO_NONBLOCK;
}
(void)fcntl(af->fd, F_SETFL, fmode);
}
#endif
}
static int aio_start_nonblocking(AioFile *af)
{
int old = !!(af->flags & AIO_NONBLOCK);
if (af->timeout) {
aio_set_nonblocking(af, 1);
}
return old;
}
static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
const char *hdlfmt, int family, int flags);
static const char *JimAioErrorString(AioFile *af)
{
if (af && af->fops)
return af->fops->strerror(af);
|
| ︙ | ︙ | |||
2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 |
if (name) {
Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
}
else {
Jim_SetResultString(interp, JimAioErrorString(af), -1);
}
}
static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
{
| > > > > > > > | | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > < < > > > > > > > > > > > > > | > > | | < | < > > | | | | | | | > > > | < < < < < < | < < > | > | | < < < | < < | | | < < < | > | | | | | | | | | > > > > | | < < < < < < < < < < < < | | > > | < < < > | > > > > > > > > > > > > > > > > > > > > > > > | > | > > | > | > | > > | | | > > > > > > > > | > > > | < | < | > | < < | | > | > < > > | | > | > > > | > | | > > > | > > > > > > > > > | > > | > > | > | > > | > | > > > > > > | > > > | | | < < < < < | > > | | | < < > > > > > | | | | > | > > > > > > | > > | | | > > > > | | 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 |
if (name) {
Jim_SetResultFormatted(interp, "%#s: %s", name, JimAioErrorString(af));
}
else {
Jim_SetResultString(interp, JimAioErrorString(af), -1);
}
}
static int aio_eof(AioFile *af)
{
return af->flags & AIO_EOF;
}
static int JimCheckStreamError(Jim_Interp *interp, AioFile *af)
{
int ret = 0;
if (!aio_eof(af)) {
ret = af->fops->error(af);
if (ret) {
JimAioSetError(interp, af->filename);
}
}
return ret;
}
static void aio_consume(Jim_Obj *objPtr, int n)
{
assert(objPtr->bytes);
assert(n <= objPtr->length);
memmove(objPtr->bytes, objPtr->bytes + n, objPtr->length - n + 1);
objPtr->length -= n;
}
static int aio_autoflush(Jim_Interp *interp, void *clientData, int mask);
static int aio_flush(Jim_Interp *interp, AioFile *af)
{
int len;
const char *pt = Jim_GetString(af->writebuf, &len);
if (len) {
int ret = af->fops->writer(af, pt, len);
if (ret > 0) {
aio_consume(af->writebuf, ret);
}
if (ret < 0) {
return JimCheckStreamError(interp, af);
}
if (Jim_Length(af->writebuf)) {
#ifdef jim_ext_eventloop
void *handler = Jim_FindFileHandler(interp, af->fd, JIM_EVENT_WRITABLE);
if (handler == NULL) {
Jim_CreateFileHandler(interp, af->fd, JIM_EVENT_WRITABLE, aio_autoflush, af, NULL);
return JIM_OK;
}
else if (handler == af) {
return JIM_OK;
}
#endif
Jim_SetResultString(interp, "send buffer is full", -1);
return JIM_ERR;
}
}
return JIM_OK;
}
static int aio_autoflush(Jim_Interp *interp, void *clientData, int mask)
{
AioFile *af = clientData;
aio_flush(interp, af);
if (Jim_Length(af->writebuf) == 0) {
return -1;
}
return 0;
}
static int aio_read_len(Jim_Interp *interp, AioFile *af, int nb, char *buf, size_t buflen, int neededLen)
{
if (!af->readbuf) {
af->readbuf = Jim_NewStringObj(interp, NULL, 0);
}
if (neededLen >= 0) {
neededLen -= Jim_Length(af->readbuf);
if (neededLen <= 0) {
return JIM_OK;
}
}
while (neededLen && !aio_eof(af)) {
int retval;
int readlen;
if (neededLen == -1) {
readlen = AIO_BUF_LEN;
}
else {
readlen = (neededLen > AIO_BUF_LEN ? AIO_BUF_LEN : neededLen);
}
retval = af->fops->reader(af, buf, readlen, nb);
if (retval > 0) {
Jim_AppendString(interp, af->readbuf, buf, retval);
if (neededLen != -1) {
neededLen -= retval;
}
continue;
}
if (JimCheckStreamError(interp, af)) {
return JIM_ERR;
}
break;
}
return JIM_OK;
}
static Jim_Obj *aio_read_consume(Jim_Interp *interp, AioFile *af, int neededLen)
{
Jim_Obj *objPtr = NULL;
if (neededLen < 0 || af->readbuf == NULL || Jim_Length(af->readbuf) <= neededLen) {
objPtr = af->readbuf;
af->readbuf = NULL;
}
else if (af->readbuf) {
int len;
const char *pt = Jim_GetString(af->readbuf, &len);
objPtr = Jim_NewStringObj(interp, pt, neededLen);
aio_consume(af->readbuf, neededLen);
}
return objPtr;
}
static void JimAioDelProc(Jim_Interp *interp, void *privData)
{
AioFile *af = privData;
JIM_NOTUSED(interp);
aio_flush(interp, af);
Jim_DecrRefCount(interp, af->writebuf);
#if UNIX_SOCKETS
if (af->addr_family == PF_UNIX && (af->flags & AIO_NODELETE) == 0) {
Jim_Obj *filenameObj = aio_sockname(interp, af->fd);
if (filenameObj) {
if (Jim_Length(filenameObj)) {
remove(Jim_String(filenameObj));
}
Jim_FreeNewObj(interp, filenameObj);
}
}
#endif
Jim_DecrRefCount(interp, af->filename);
#ifdef jim_ext_eventloop
Jim_DeleteFileHandler(interp, af->fd, JIM_EVENT_READABLE | JIM_EVENT_WRITABLE | JIM_EVENT_EXCEPTION);
#endif
#if defined(JIM_SSL)
if (af->ssl != NULL) {
SSL_free(af->ssl);
}
#endif
if (!(af->flags & AIO_KEEPOPEN)) {
close(af->fd);
}
if (af->readbuf) {
Jim_FreeNewObj(interp, af->readbuf);
}
Jim_Free(af);
}
static int aio_cmd_read(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int nonewline = 0;
jim_wide neededLen = -1;
static const char * const options[] = { "-pending", "-nonewline", NULL };
enum { OPT_PENDING, OPT_NONEWLINE };
int option;
int nb;
Jim_Obj *objPtr;
char buf[AIO_BUF_LEN];
if (argc) {
if (*Jim_String(argv[0]) == '-') {
if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
switch (option) {
case OPT_PENDING:
break;
case OPT_NONEWLINE:
nonewline++;
break;
}
}
else {
if (Jim_GetWide(interp, argv[0], &neededLen) != JIM_OK)
return JIM_ERR;
if (neededLen < 0) {
Jim_SetResultString(interp, "invalid parameter: negative len", -1);
return JIM_ERR;
}
}
argc--;
argv++;
}
if (argc) {
return -1;
}
nb = aio_start_nonblocking(af);
if (aio_read_len(interp, af, nb, buf, sizeof(buf), neededLen) != JIM_OK) {
aio_set_nonblocking(af, nb);
return JIM_ERR;
}
objPtr = aio_read_consume(interp, af, neededLen);
aio_set_nonblocking(af, nb);
if (objPtr) {
if (nonewline) {
int len;
const char *s = Jim_GetString(objPtr, &len);
if (len > 0 && s[len - 1] == '\n') {
objPtr->length--;
objPtr->bytes[objPtr->length] = '\0';
}
}
Jim_SetResult(interp, objPtr);
}
else {
Jim_SetEmptyResult(interp);
}
return JIM_OK;
}
int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
{
Jim_Cmd *cmdPtr = Jim_GetCommand(interp, command, JIM_ERRMSG);
if (cmdPtr && !cmdPtr->isproc && cmdPtr->u.native.cmdProc == JimAioSubCmdProc) {
return ((AioFile *) cmdPtr->u.native.privData)->fd;
}
Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", command);
return -1;
}
static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
aio_flush(interp, af);
Jim_SetResultInt(interp, af->fd);
return JIM_OK;
}
static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
jim_wide count = 0;
jim_wide maxlen = JIM_WIDE_MAX;
char buf[AIO_BUF_LEN];
char *bufp = buf;
int buflen = sizeof(buf);
int ok = 1;
Jim_Obj *objv[4];
if (argc == 2) {
if (Jim_GetWide(interp, argv[1], &maxlen) != JIM_OK) {
return JIM_ERR;
}
}
objv[0] = argv[0];
objv[1] = Jim_NewStringObj(interp, "flush", -1);
if (Jim_EvalObjVector(interp, 2, objv) != JIM_OK) {
Jim_SetResultFormatted(interp, "Not a filehandle: \"%#s\"", argv[0]);
return JIM_ERR;
}
objv[0] = argv[0];
objv[1] = Jim_NewStringObj(interp, "puts", -1);
objv[2] = Jim_NewStringObj(interp, "-nonewline", -1);
Jim_IncrRefCount(objv[1]);
Jim_IncrRefCount(objv[2]);
while (count < maxlen) {
jim_wide len = maxlen - count;
if (len > buflen) {
len = buflen;
}
if (aio_read_len(interp, af, 0, bufp, buflen, len) != JIM_OK) {
ok = 0;
break;
}
objv[3] = aio_read_consume(interp, af, len);
count += Jim_Length(objv[3]);
if (Jim_EvalObjVector(interp, 4, objv) != JIM_OK) {
ok = 0;
break;
}
if (aio_eof(af)) {
break;
}
if (count >= 16384 && bufp == buf) {
buflen = 65536;
bufp = Jim_Alloc(buflen);
}
}
if (bufp != buf) {
Jim_Free(bufp);
}
Jim_DecrRefCount(interp, objv[1]);
Jim_DecrRefCount(interp, objv[2]);
if (!ok) {
return JIM_ERR;
}
Jim_SetResultInt(interp, count);
return JIM_OK;
}
static int aio_cmd_gets(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
char buf[AIO_BUF_LEN];
Jim_Obj *objPtr = NULL;
int len;
int nb;
char *nl = NULL;
int offset = 0;
errno = 0;
nb = aio_start_nonblocking(af);
if (!af->readbuf) {
af->readbuf = Jim_NewStringObj(interp, NULL, 0);
}
while (!aio_eof(af)) {
const char *pt = Jim_GetString(af->readbuf, &len);
nl = memchr(pt + offset, '\n', len - offset);
if (nl) {
objPtr = Jim_NewStringObj(interp, pt, nl - pt);
aio_consume(af->readbuf, nl - pt + 1);
break;
}
offset = len;
len = af->fops->reader(af, buf, AIO_BUF_LEN, nb);
if (len <= 0) {
break;
}
Jim_AppendString(interp, af->readbuf, buf, len);
}
aio_set_nonblocking(af, nb);
if (!nl && aio_eof(af)) {
objPtr = af->readbuf;
af->readbuf = NULL;
}
else if (!objPtr) {
objPtr = Jim_NewStringObj(interp, NULL, 0);
}
if (argc) {
if (Jim_SetVariable(interp, argv[0], objPtr) != JIM_OK) {
Jim_FreeNewObj(interp, objPtr);
return JIM_ERR;
}
len = Jim_Length(objPtr);
if (!nl && len == 0) {
len = -1;
}
Jim_SetResultInt(interp, len);
}
else {
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
}
static int aio_cmd_puts(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int wlen;
const char *wdata;
Jim_Obj *strObj;
int wnow = 0;
int nl = 1;
if (argc == 2) {
if (!Jim_CompareStringImmediate(interp, argv[0], "-nonewline")) {
return -1;
}
strObj = argv[1];
nl = 0;
}
else {
strObj = argv[0];
}
Jim_AppendObj(interp, af->writebuf, strObj);
if (nl) {
Jim_AppendString(interp, af->writebuf, "\n", 1);
}
wdata = Jim_GetString(af->writebuf, &wlen);
switch (af->wbuft) {
case WBUF_OPT_NONE:
wnow = 1;
break;
case WBUF_OPT_LINE:
if (nl || memchr(wdata, '\n', wlen) != NULL) {
wnow = 1;
}
break;
case WBUF_OPT_FULL:
if (wlen >= AIO_WBUF_FULL_SIZE) {
wnow = 1;
}
break;
}
if (wnow) {
return aio_flush(interp, af);
}
return JIM_OK;
}
static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
#ifdef HAVE_ISATTY
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResultInt(interp, isatty(af->fd));
#else
Jim_SetResultInt(interp, 0);
#endif
return JIM_OK;
}
static int aio_cmd_flush(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_flush(interp, af);
}
static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResultInt(interp, !!aio_eof(af));
return JIM_OK;
}
static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
if (argc == 3) {
int option = -1;
#if defined(HAVE_SOCKETS)
static const char * const options[] = { "r", "w", "-nodelete", NULL };
enum { OPT_R, OPT_W, OPT_NODELETE };
if (Jim_GetEnum(interp, argv[2], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
#endif
switch (option) {
#if defined(HAVE_SHUTDOWN)
case OPT_R:
case OPT_W:
if (shutdown(af->fd, option == OPT_R ? SHUT_RD : SHUT_WR) == 0) {
return JIM_OK;
}
JimAioSetError(interp, NULL);
return JIM_ERR;
#endif
#if UNIX_SOCKETS
case OPT_NODELETE:
if (af->addr_family == PF_UNIX) {
af->flags |= AIO_NODELETE;
break;
}
#endif
default:
Jim_SetResultString(interp, "not supported", -1);
return JIM_ERR;
}
}
af->flags &= ~AIO_KEEPOPEN;
return Jim_DeleteCommand(interp, argv[0]);
}
static int aio_cmd_seek(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
int orig = SEEK_SET;
jim_wide offset;
|
| ︙ | ︙ | |||
2395 2396 2397 2398 2399 2400 2401 |
else {
return -1;
}
}
if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
return JIM_ERR;
}
| > > > > | > > > > > | < < < < < | < | < < | | > > | > > > | | < < < | | | | | | | < > | | | | < < | | > > > > > > < < < < < < < < < < < < < < < | > | | < | | < | | < < < < < < < < | | | > > > > > > > > > > > > > > | | 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 |
else {
return -1;
}
}
if (Jim_GetWide(interp, argv[0], &offset) != JIM_OK) {
return JIM_ERR;
}
if (orig != SEEK_CUR || offset != 0) {
aio_flush(interp, af);
}
if (Jim_Lseek(af->fd, offset, orig) == -1) {
JimAioSetError(interp, af->filename);
return JIM_ERR;
}
if (af->readbuf) {
Jim_FreeNewObj(interp, af->readbuf);
af->readbuf = NULL;
}
af->flags &= ~AIO_EOF;
return JIM_OK;
}
static int aio_cmd_tell(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResultInt(interp, Jim_Lseek(af->fd, 0, SEEK_CUR));
return JIM_OK;
}
static int aio_cmd_filename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
Jim_SetResult(interp, af->filename);
return JIM_OK;
}
#ifdef O_NDELAY
static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
if (argc) {
long nb;
if (Jim_GetLong(interp, argv[0], &nb) != JIM_OK) {
return JIM_ERR;
}
aio_set_nonblocking(af, nb);
}
Jim_SetResultInt(interp, (af->flags & AIO_NONBLOCK) ? 1 : 0);
return JIM_OK;
}
#endif
#ifdef HAVE_FSYNC
static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
if (aio_flush(interp, af) != JIM_OK) {
return JIM_ERR;
}
fsync(af->fd);
return JIM_OK;
}
#endif
static int aio_cmd_buffering(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
static const char * const options[] = {
"none",
"line",
"full",
NULL
};
if (Jim_GetEnum(interp, argv[0], options, &af->wbuft, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
if (af->wbuft == WBUF_OPT_NONE) {
return aio_flush(interp, af);
}
return JIM_OK;
}
static int aio_cmd_timeout(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
#ifdef HAVE_SELECT
AioFile *af = Jim_CmdPrivData(interp);
if (argc == 1) {
if (Jim_GetLong(interp, argv[0], &af->timeout) != JIM_OK) {
return JIM_ERR;
}
}
Jim_SetResultInt(interp, af->timeout);
return JIM_OK;
#else
Jim_SetResultString(interp, "timeout not supported", -1);
return JIM_ERR;
#endif
}
#ifdef jim_ext_eventloop
static int aio_eventinfo(Jim_Interp *interp, AioFile * af, unsigned mask,
int argc, Jim_Obj * const *argv)
{
if (argc == 0) {
Jim_Obj *objPtr = Jim_FindFileHandler(interp, af->fd, mask);
if (objPtr) {
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
}
Jim_DeleteFileHandler(interp, af->fd, mask);
if (Jim_Length(argv[0])) {
Jim_CreateScriptFileHandler(interp, af->fd, mask, argv[0]);
}
return JIM_OK;
}
static int aio_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_eventinfo(interp, af, JIM_EVENT_READABLE, argc, argv);
}
static int aio_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_eventinfo(interp, af, JIM_EVENT_WRITABLE, argc, argv);
}
static int aio_cmd_onexception(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
return aio_eventinfo(interp, af, JIM_EVENT_EXCEPTION, argc, argv);
}
#endif
#if defined(jim_ext_file) && defined(Jim_FileStat)
static int aio_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
AioFile *af = Jim_CmdPrivData(interp);
if (Jim_FileStat(af->fd, &sb) == -1) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
return Jim_FileStoreStatData(interp, argc == 0 ? NULL : argv[0], &sb);
}
#endif
static const jim_subcmd_type aio_command_table[] = {
{ "read",
"?-nonewline|len?",
aio_cmd_read,
0,
2,
},
{ "copyto",
"handle ?size?",
|
| ︙ | ︙ | |||
2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 |
{ "buffering",
"none|line|full",
aio_cmd_buffering,
1,
1,
},
#ifdef jim_ext_eventloop
{ "readable",
"?readable-script?",
aio_cmd_readable,
0,
1,
| > > > > > > > > > | 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 |
{ "buffering",
"none|line|full",
aio_cmd_buffering,
1,
1,
},
#if defined(jim_ext_file) && defined(Jim_FileStat)
{ "stat",
"?var?",
aio_cmd_stat,
0,
1,
},
#endif
#ifdef jim_ext_eventloop
{ "readable",
"?readable-script?",
aio_cmd_readable,
0,
1,
|
| ︙ | ︙ | |||
2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 |
},
{ "onexception",
"?exception-script?",
aio_cmd_onexception,
0,
1,
},
#endif
{ NULL }
};
static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
}
static int JimAioOpenCommand(Jim_Interp *interp, int argc,
Jim_Obj *const *argv)
{
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | | | < > | | > | | > | > > > > > > > > > > > > > > | > | | | | < < < < < | < < < < < < | < < < < < < < < < < < < < < < < < < < | > > > > > > > > > > > > > > | > | | | > | | | | | | | < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 |
},
{ "onexception",
"?exception-script?",
aio_cmd_onexception,
0,
1,
},
{ "timeout",
"?ms?",
aio_cmd_timeout,
0,
1,
},
#endif
{ NULL }
};
static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return Jim_CallSubCmd(interp, Jim_ParseSubCmd(interp, aio_command_table, argc, argv), argc, argv);
}
static int parse_posix_open_mode(Jim_Interp *interp, Jim_Obj *modeObj)
{
int i;
int flags = 0;
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
static const char * const modetypes[] = {
"RDONLY", "WRONLY", "RDWR", "APPEND", "BINARY", "CREAT", "EXCL", "NOCTTY", "TRUNC", NULL
};
static const int modeflags[] = {
O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, 0, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC,
};
for (i = 0; i < Jim_ListLength(interp, modeObj); i++) {
int opt;
Jim_Obj *objPtr = Jim_ListGetIndex(interp, modeObj, i);
if (Jim_GetEnum(interp, objPtr, modetypes, &opt, "access mode", JIM_ERRMSG) != JIM_OK) {
return -1;
}
flags |= modeflags[opt];
}
return flags;
}
static int parse_open_mode(Jim_Interp *interp, Jim_Obj *filenameObj, Jim_Obj *modeObj)
{
int flags;
const char *mode = Jim_String(modeObj);
if (*mode == 'R' || *mode == 'W') {
return parse_posix_open_mode(interp, modeObj);
}
if (*mode == 'r') {
flags = O_RDONLY;
}
else if (*mode == 'w') {
flags = O_WRONLY | O_CREAT | O_TRUNC;
}
else if (*mode == 'a') {
flags = O_WRONLY | O_CREAT | O_APPEND;
}
else {
Jim_SetResultFormatted(interp, "%s: invalid open mode '%s'", Jim_String(filenameObj), mode);
return -1;
}
mode++;
if (*mode == 'b') {
#ifdef O_BINARY
flags |= O_BINARY;
#endif
mode++;
}
if (*mode == 't') {
#ifdef O_TEXT
flags |= O_TEXT;
#endif
mode++;
}
if (*mode == '+') {
mode++;
flags &= ~(O_RDONLY | O_WRONLY);
flags |= O_RDWR;
}
if (*mode == 'x') {
mode++;
#ifdef O_EXCL
flags |= O_EXCL;
#endif
}
if (*mode == 'F') {
mode++;
#ifdef O_LARGEFILE
flags |= O_LARGEFILE;
#endif
}
if (*mode == 'e') {
mode++;
}
return flags;
}
static int JimAioOpenCommand(Jim_Interp *interp, int argc,
Jim_Obj *const *argv)
{
int openflags;
const char *filename;
int fd = -1;
int n = 0;
int flags = 0;
if (argc > 2 && Jim_CompareStringImmediate(interp, argv[2], "-noclose")) {
flags = AIO_KEEPOPEN;
n++;
}
if (argc < 2 || argc > 3 + n) {
Jim_WrongNumArgs(interp, 1, argv, "filename ?-noclose? ?mode?");
return JIM_ERR;
}
filename = Jim_String(argv[1]);
#ifdef jim_ext_tclcompat
{
if (*filename == '|') {
Jim_Obj *evalObj[3];
int i = 0;
evalObj[i++] = Jim_NewStringObj(interp, "::popen", -1);
evalObj[i++] = Jim_NewStringObj(interp, filename + 1, -1);
if (argc == 3 + n) {
evalObj[i++] = argv[2 + n];
}
return Jim_EvalObjVector(interp, i, evalObj);
}
}
#endif
if (argc == 3 + n) {
openflags = parse_open_mode(interp, argv[1], argv[2 + n]);
if (openflags == -1) {
return JIM_ERR;
}
}
else {
openflags = O_RDONLY;
}
fd = open(filename, openflags, 0666);
if (fd < 0) {
JimAioSetError(interp, argv[1]);
return JIM_ERR;
}
return JimMakeChannel(interp, fd, argv[1], "aio.handle%ld", 0, flags) ? JIM_OK : JIM_ERR;
}
static AioFile *JimMakeChannel(Jim_Interp *interp, int fd, Jim_Obj *filename,
const char *hdlfmt, int family, int flags)
{
AioFile *af;
char buf[AIO_CMD_LEN];
Jim_Obj *cmdname;
snprintf(buf, sizeof(buf), hdlfmt, Jim_GetId(interp));
cmdname = Jim_NewStringObj(interp, buf, -1);
if (!filename) {
filename = cmdname;
}
Jim_IncrRefCount(filename);
af = Jim_Alloc(sizeof(*af));
memset(af, 0, sizeof(*af));
af->filename = filename;
af->fd = fd;
af->addr_family = family;
af->fops = &stdio_fops;
af->ssl = NULL;
if (flags & AIO_WBUF_NONE) {
af->wbuft = WBUF_OPT_NONE;
}
else {
#ifdef HAVE_ISATTY
af->wbuft = isatty(af->fd) ? WBUF_OPT_LINE : WBUF_OPT_FULL;
#else
af->wbuft = WBUF_OPT_FULL;
#endif
}
#ifdef FD_CLOEXEC
if ((flags & AIO_KEEPOPEN) == 0) {
(void)fcntl(af->fd, F_SETFD, FD_CLOEXEC);
}
#endif
aio_set_nonblocking(af, !!(flags & AIO_NONBLOCK));
af->flags |= flags;
af->writebuf = Jim_NewStringObj(interp, NULL, 0);
Jim_IncrRefCount(af->writebuf);
Jim_CreateCommand(interp, buf, JimAioSubCmdProc, af, JimAioDelProc);
Jim_SetResult(interp, Jim_MakeGlobalNamespaceName(interp, cmdname));
return af;
}
#if defined(HAVE_PIPE) || (defined(HAVE_SOCKETPAIR) && UNIX_SOCKETS) || defined(HAVE_OPENPTY)
static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
const char *hdlfmt, int family, int flags)
{
if (JimMakeChannel(interp, p[0], filename, hdlfmt, family, flags)) {
Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
if (JimMakeChannel(interp, p[1], filename, hdlfmt, family, flags)) {
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
}
close(p[0]);
close(p[1]);
JimAioSetError(interp, NULL);
return JIM_ERR;
}
#endif
#ifdef HAVE_PIPE
static int JimCreatePipe(Jim_Interp *interp, Jim_Obj *filenameObj, int flags)
{
int p[2];
if (pipe(p) != 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
return JimMakeChannelPair(interp, p, filenameObj, "aio.pipe%ld", 0, flags);
}
static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "");
return JIM_ERR;
}
return JimCreatePipe(interp, argv[0], 0);
}
#endif
#ifdef HAVE_OPENPTY
static int JimAioOpenPtyCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int p[2];
char path[MAXPATHLEN];
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "");
return JIM_ERR;
}
if (openpty(&p[0], &p[1], path, NULL, NULL) != 0) {
JimAioSetError(interp, NULL);
return JIM_ERR;
}
return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
return JimMakeChannelPair(interp, p, Jim_NewStringObj(interp, path, -1), "aio.pty%ld", 0, 0);
}
#endif
int Jim_aioInit(Jim_Interp *interp)
{
|
| ︙ | ︙ | |||
2866 2867 2868 2869 2870 2871 2872 |
Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
#endif
#ifdef HAVE_PIPE
Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
#endif
| | | | | 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 |
Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
#endif
#ifdef HAVE_PIPE
Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
#endif
JimMakeChannel(interp, fileno(stdin), NULL, "stdin", 0, AIO_KEEPOPEN);
JimMakeChannel(interp, fileno(stdout), NULL, "stdout", 0, AIO_KEEPOPEN);
JimMakeChannel(interp, fileno(stderr), NULL, "stderr", 0, AIO_KEEPOPEN | AIO_WBUF_NONE);
return JIM_OK;
}
#include <errno.h>
#include <stdio.h>
#include <string.h>
|
| ︙ | ︙ | |||
2930 2931 2932 2933 2934 2935 2936 |
return JIM_OK;
}
}
int Jim_readdirInit(Jim_Interp *interp)
{
| | < < > > > > | | 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 |
return JIM_OK;
}
}
int Jim_readdirInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "readdir");
Jim_CreateCommand(interp, "readdir", Jim_ReaddirCmd, NULL, NULL);
return JIM_OK;
}
#include <stdlib.h>
#include <string.h>
#if defined(JIM_REGEXP)
#else
#include <regex.h>
#define jim_regcomp regcomp
#define jim_regexec regexec
#define jim_regerror regerror
#define jim_regfree regfree
#endif
static void FreeRegexpInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
jim_regfree(objPtr->internalRep.ptrIntValue.ptr);
Jim_Free(objPtr->internalRep.ptrIntValue.ptr);
}
static const Jim_ObjType regexpObjType = {
"regexp",
FreeRegexpInternalRep,
NULL,
|
| ︙ | ︙ | |||
2978 2979 2980 2981 2982 2983 2984 |
pattern = Jim_String(objPtr);
compre = Jim_Alloc(sizeof(regex_t));
| | | | | 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 |
pattern = Jim_String(objPtr);
compre = Jim_Alloc(sizeof(regex_t));
if ((ret = jim_regcomp(compre, pattern, REG_EXTENDED | flags)) != 0) {
char buf[100];
jim_regerror(ret, compre, buf, sizeof(buf));
Jim_SetResultFormatted(interp, "couldn't compile regular expression pattern: %s", buf);
jim_regfree(compre);
Jim_Free(compre);
return NULL;
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = ®expObjType;
|
| ︙ | ︙ | |||
3108 3109 3110 3111 3112 3113 3114 |
if (offset < 0) {
offset += source_len + 1;
}
if (offset > source_len) {
source_str += source_len;
}
else if (offset > 0) {
| | | | | 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 |
if (offset < 0) {
offset += source_len + 1;
}
if (offset > source_len) {
source_str += source_len;
}
else if (offset > 0) {
source_str += utf8_index(source_str, offset);
}
eflags |= REG_NOTBOL;
}
if (opt_inline) {
resultListObj = Jim_NewListObj(interp, NULL, 0);
}
next_match:
match = jim_regexec(regex, source_str, num_vars + 1, pmatch, eflags);
if (match >= REG_BADPAT) {
char buf[100];
jim_regerror(match, regex, buf, sizeof(buf));
Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
result = JIM_ERR;
goto done;
}
if (match == REG_NOMATCH) {
goto done;
|
| ︙ | ︙ | |||
3158 3159 3160 3161 3162 3163 3164 |
if (pmatch[j].rm_so == -1) {
if (opt_indices) {
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
}
}
else {
| | | > | < | < | | | 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 |
if (pmatch[j].rm_so == -1) {
if (opt_indices) {
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, -1));
}
}
else {
if (opt_indices) {
int so = utf8_strlen(source_str, pmatch[j].rm_so);
int eo = utf8_strlen(source_str, pmatch[j].rm_eo);
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + so));
Jim_ListAppendElement(interp, resultObj, Jim_NewIntObj(interp, offset + eo - 1));
}
else {
Jim_AppendString(interp, resultObj, source_str + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
}
}
if (opt_inline) {
Jim_ListAppendElement(interp, resultListObj, resultObj);
}
else {
result = Jim_SetVariable(interp, argv[i], resultObj);
if (result != JIM_OK) {
Jim_FreeObj(interp, resultObj);
break;
}
}
}
try_next_match:
if (opt_all && (pattern[0] != '^' || (regcomp_flags & REG_NEWLINE)) && *source_str) {
if (pmatch[0].rm_eo) {
offset += utf8_strlen(source_str, pmatch[0].rm_eo);
source_str += pmatch[0].rm_eo;
}
else {
source_str++;
offset++;
}
if (*source_str) {
|
| ︙ | ︙ | |||
3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 |
#define MAX_SUB_MATCHES 50
int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int regcomp_flags = 0;
int regexec_flags = 0;
int opt_all = 0;
int offset = 0;
regex_t *regex;
const char *p;
| > | > > | | | | 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 |
#define MAX_SUB_MATCHES 50
int Jim_RegsubCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int regcomp_flags = 0;
int regexec_flags = 0;
int opt_all = 0;
int opt_command = 0;
int offset = 0;
regex_t *regex;
const char *p;
int result = JIM_OK;
regmatch_t pmatch[MAX_SUB_MATCHES + 1];
int num_matches = 0;
int i, j, n;
Jim_Obj *varname;
Jim_Obj *resultObj;
Jim_Obj *cmd_prefix = NULL;
Jim_Obj *regcomp_obj = NULL;
const char *source_str;
int source_len;
const char *replace_str = NULL;
int replace_len;
const char *pattern;
int option;
enum {
OPT_NOCASE, OPT_LINE, OPT_ALL, OPT_START, OPT_COMMAND, OPT_END
};
static const char * const options[] = {
"-nocase", "-line", "-all", "-start", "-command", "--", NULL
};
if (argc < 4) {
wrongNumArgs:
Jim_WrongNumArgs(interp, 1, argv,
"?-switch ...? exp string subSpec ?varName?");
return JIM_ERR;
|
| ︙ | ︙ | |||
3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 |
if (++i == argc) {
goto wrongNumArgs;
}
if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
return JIM_ERR;
}
break;
}
}
if (argc - i != 3 && argc - i != 4) {
goto wrongNumArgs;
}
| > > > > > > > | > > > > > > > > > > > | > > > | | > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 |
if (++i == argc) {
goto wrongNumArgs;
}
if (Jim_GetIndex(interp, argv[i], &offset) != JIM_OK) {
return JIM_ERR;
}
break;
case OPT_COMMAND:
opt_command = 1;
break;
}
}
if (argc - i != 3 && argc - i != 4) {
goto wrongNumArgs;
}
regcomp_obj = Jim_DuplicateObj(interp, argv[i]);
Jim_IncrRefCount(regcomp_obj);
regex = SetRegexpFromAny(interp, regcomp_obj, regcomp_flags);
if (!regex) {
Jim_DecrRefCount(interp, regcomp_obj);
return JIM_ERR;
}
pattern = Jim_String(argv[i]);
source_str = Jim_GetString(argv[i + 1], &source_len);
if (opt_command) {
cmd_prefix = argv[i + 2];
if (Jim_ListLength(interp, cmd_prefix) == 0) {
Jim_SetResultString(interp, "command prefix must be a list of at least one element", -1);
Jim_DecrRefCount(interp, regcomp_obj);
return JIM_ERR;
}
Jim_IncrRefCount(cmd_prefix);
}
else {
replace_str = Jim_GetString(argv[i + 2], &replace_len);
}
varname = argv[i + 3];
resultObj = Jim_NewStringObj(interp, "", 0);
if (offset) {
if (offset < 0) {
offset += source_len + 1;
}
if (offset > source_len) {
offset = source_len;
}
else if (offset < 0) {
offset = 0;
}
}
offset = utf8_index(source_str, offset);
Jim_AppendString(interp, resultObj, source_str, offset);
n = source_len - offset;
p = source_str + offset;
do {
int match = jim_regexec(regex, p, MAX_SUB_MATCHES, pmatch, regexec_flags);
if (match >= REG_BADPAT) {
char buf[100];
jim_regerror(match, regex, buf, sizeof(buf));
Jim_SetResultFormatted(interp, "error while matching pattern: %s", buf);
return JIM_ERR;
}
if (match == REG_NOMATCH) {
break;
}
num_matches++;
Jim_AppendString(interp, resultObj, p, pmatch[0].rm_so);
if (opt_command) {
Jim_Obj *cmdListObj = Jim_DuplicateObj(interp, cmd_prefix);
for (j = 0; j < MAX_SUB_MATCHES; j++) {
if (pmatch[j].rm_so == -1) {
break;
}
else {
Jim_Obj *srcObj = Jim_NewStringObj(interp, p + pmatch[j].rm_so, pmatch[j].rm_eo - pmatch[j].rm_so);
Jim_ListAppendElement(interp, cmdListObj, srcObj);
}
}
Jim_IncrRefCount(cmdListObj);
result = Jim_EvalObj(interp, cmdListObj);
Jim_DecrRefCount(interp, cmdListObj);
if (result != JIM_OK) {
goto cmd_error;
}
Jim_AppendString(interp, resultObj, Jim_String(Jim_GetResult(interp)), -1);
}
else {
for (j = 0; j < replace_len; j++) {
int idx;
int c = replace_str[j];
if (c == '&') {
idx = 0;
}
else if (c == '\\' && j < replace_len) {
c = replace_str[++j];
if ((c >= '0') && (c <= '9')) {
idx = c - '0';
}
else if ((c == '\\') || (c == '&')) {
Jim_AppendString(interp, resultObj, replace_str + j, 1);
continue;
}
else {
Jim_AppendString(interp, resultObj, replace_str + j - 1, (j == replace_len) ? 1 : 2);
continue;
}
}
else {
Jim_AppendString(interp, resultObj, replace_str + j, 1);
continue;
}
if ((idx < MAX_SUB_MATCHES) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
Jim_AppendString(interp, resultObj, p + pmatch[idx].rm_so,
pmatch[idx].rm_eo - pmatch[idx].rm_so);
}
}
}
p += pmatch[0].rm_eo;
n -= pmatch[0].rm_eo;
|
| ︙ | ︙ | |||
3394 3395 3396 3397 3398 3399 3400 |
if (pattern[0] == '\0' && n) {
Jim_AppendString(interp, resultObj, p, 1);
p++;
n--;
}
| > > > > > | > > > > | | | | | | | | | | | | | > > > > > > > > > > | < < < > > > > > > > > > > > > > > > > > > | 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 |
if (pattern[0] == '\0' && n) {
Jim_AppendString(interp, resultObj, p, 1);
p++;
n--;
}
if (pmatch[0].rm_eo == pmatch[0].rm_so) {
regexec_flags = REG_NOTBOL;
}
else {
regexec_flags = 0;
}
} while (n);
Jim_AppendString(interp, resultObj, p, -1);
cmd_error:
if (result == JIM_OK) {
if (argc - i == 4) {
result = Jim_SetVariable(interp, varname, resultObj);
if (result == JIM_OK) {
Jim_SetResultInt(interp, num_matches);
}
else {
Jim_FreeObj(interp, resultObj);
}
}
else {
Jim_SetResult(interp, resultObj);
result = JIM_OK;
}
}
else {
Jim_FreeObj(interp, resultObj);
}
if (opt_command) {
Jim_DecrRefCount(interp, cmd_prefix);
}
Jim_DecrRefCount(interp, regcomp_obj);
return result;
}
int Jim_regexpInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "regexp");
Jim_CreateCommand(interp, "regexp", Jim_RegexpCmd, NULL, NULL);
Jim_CreateCommand(interp, "regsub", Jim_RegsubCmd, NULL, NULL);
return JIM_OK;
}
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#ifdef HAVE_UTIMES
#include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#elif defined(_MSC_VER)
#include <direct.h>
#define F_OK 0
#define W_OK 2
#define R_OK 4
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
# ifndef MAXPATHLEN
# ifdef PATH_MAX
# define MAXPATHLEN PATH_MAX
# else
# define MAXPATHLEN JIM_PATH_LEN
# endif
# endif
#if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER)
#define ISWINDOWS 1
#undef HAVE_SYMLINK
#else
#define ISWINDOWS 0
#endif
#if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
#define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000)
#elif defined(HAVE_STRUCT_STAT_ST_MTIM)
#define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000)
#endif
static void JimFixPath(char *path)
{
if (ISWINDOWS) {
char *p = path;
while ((p = strchr(p, '\\')) != NULL) {
*p++ = '/';
}
}
}
static const char *JimGetFileType(int mode)
{
if (S_ISREG(mode)) {
return "file";
}
else if (S_ISDIR(mode)) {
|
| ︙ | ︙ | |||
3510 3511 3512 3513 3514 3515 3516 |
static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
{
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
}
| | | 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 |
static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
{
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
}
int Jim_FileStoreStatData(Jim_Interp *interp, Jim_Obj *varName, const jim_stat_t *sb)
{
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
AppendStatElement(interp, listObj, "dev", sb->st_dev);
AppendStatElement(interp, listObj, "ino", sb->st_ino);
AppendStatElement(interp, listObj, "mode", sb->st_mode);
|
| ︙ | ︙ | |||
3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 |
}
Jim_SetResult(interp, listObj);
return JIM_OK;
}
static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
| > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > > > > > > > > > > > > > > > < | | > < | | | < < < < < | 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 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 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 |
}
Jim_SetResult(interp, listObj);
return JIM_OK;
}
static int JimPathLenNoTrailingSlashes(const char *path, int len)
{
int i;
for (i = len; i > 1 && path[i - 1] == '/'; i--) {
if (ISWINDOWS && path[i - 2] == ':') {
break;
}
}
return i;
}
static Jim_Obj *JimStripTrailingSlashes(Jim_Interp *interp, Jim_Obj *objPtr)
{
int len = Jim_Length(objPtr);
const char *path = Jim_String(objPtr);
int i = JimPathLenNoTrailingSlashes(path, len);
if (i != len) {
objPtr = Jim_NewStringObj(interp, path, i);
}
Jim_IncrRefCount(objPtr);
return objPtr;
}
static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
const char *path = Jim_String(objPtr);
const char *p = strrchr(path, '/');
if (!p) {
Jim_SetResultString(interp, ".", -1);
}
else if (p[1] == 0) {
Jim_SetResult(interp, objPtr);
}
else if (p == path) {
Jim_SetResultString(interp, "/", -1);
}
else if (ISWINDOWS && p[-1] == ':') {
Jim_SetResultString(interp, path, p - path + 1);
}
else {
int len = JimPathLenNoTrailingSlashes(path, p - path);
Jim_SetResultString(interp, path, len);
}
Jim_DecrRefCount(interp, objPtr);
return JIM_OK;
}
static int file_cmd_split(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
const char *path = Jim_String(argv[0]);
if (*path == '/') {
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "/", 1));
}
while (1) {
while (*path == '/') {
path++;
}
if (*path) {
const char *pt = strchr(path, '/');
if (pt) {
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, pt - path));
path = pt;
continue;
}
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, path, -1));
}
break;
}
Jim_SetResult(interp, listObj);
return JIM_OK;
}
static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *path = Jim_String(argv[0]);
const char *lastSlash = strrchr(path, '/');
const char *p = strrchr(path, '.');
if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
Jim_SetResult(interp, argv[0]);
}
else {
Jim_SetResultString(interp, path, p - path);
}
return JIM_OK;
}
static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
const char *path = Jim_String(objPtr);
const char *lastSlash = strrchr(path, '/');
const char *p = strrchr(path, '.');
if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
p = "";
}
Jim_SetResultString(interp, p, -1);
Jim_DecrRefCount(interp, objPtr);
return JIM_OK;
}
static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr = JimStripTrailingSlashes(interp, argv[0]);
const char *path = Jim_String(objPtr);
const char *lastSlash = strrchr(path, '/');
if (lastSlash) {
Jim_SetResultString(interp, lastSlash + 1, -1);
}
else {
Jim_SetResult(interp, objPtr);
}
Jim_DecrRefCount(interp, objPtr);
return JIM_OK;
}
#ifndef HAVE_RESTRICT
#define restrict
#endif
static char *JimRealPath(const char *restrict path, char *restrict resolved_path, size_t len)
{
#if defined(HAVE__FULLPATH)
return _fullpath(resolved_path, path, len);
#elif defined(HAVE_REALPATH)
return realpath(path, resolved_path);
#else
return NULL;
#endif
}
static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *path = Jim_String(argv[0]);
char *newname = Jim_Alloc(MAXPATHLEN);
if (JimRealPath(path, newname, MAXPATHLEN)) {
JimFixPath(newname);
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
return JIM_OK;
}
Jim_Free(newname);
Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
return JIM_ERR;
}
static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int i;
char *newname = Jim_Alloc(MAXPATHLEN + 1);
char *last = newname;
|
| ︙ | ︙ | |||
3753 3754 3755 3756 3757 3758 3759 |
}
static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
| | | | 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 |
}
static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
argc--;
argv++;
}
while (argc--) {
const char *path = Jim_String(argv[0]);
if (unlink(path) == -1 && errno != ENOENT) {
if (rmdir(path) == -1) {
|
| ︙ | ︙ | |||
3811 3812 3813 3814 3815 3816 3817 |
}
if (errno == ENOENT) {
continue;
}
if (errno == EEXIST) {
| | | | 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 |
}
if (errno == ENOENT) {
continue;
}
if (errno == EEXIST) {
jim_stat_t sb;
if (Jim_Stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
return 0;
}
errno = EEXIST;
}
break;
|
| ︙ | ︙ | |||
3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 |
dest = Jim_String(argv[1]);
if (!force && access(dest, F_OK) == 0) {
Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
argv[1]);
return JIM_ERR;
}
if (rename(source, dest) != 0) {
Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
strerror(errno));
return JIM_ERR;
}
return JIM_OK;
| > > > > > | 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 |
dest = Jim_String(argv[1]);
if (!force && access(dest, F_OK) == 0) {
Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
argv[1]);
return JIM_ERR;
}
#if ISWINDOWS
if (access(dest, F_OK) == 0) {
remove(dest);
}
#endif
if (rename(source, dest) != 0) {
Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
strerror(errno));
return JIM_ERR;
}
return JIM_OK;
|
| ︙ | ︙ | |||
3925 3926 3927 3928 3929 3930 3931 |
return JIM_ERR;
}
return JIM_OK;
}
#endif
| | | | | | | | 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 |
return JIM_ERR;
}
return JIM_OK;
}
#endif
static int file_stat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
{
const char *path = Jim_String(filename);
if (Jim_Stat(path, sb) == -1) {
Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
return JIM_ERR;
}
return JIM_OK;
}
#ifdef Jim_LinkStat
static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, jim_stat_t *sb)
{
const char *path = Jim_String(filename);
if (Jim_LinkStat(path, sb) == -1) {
Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
return JIM_ERR;
}
return JIM_OK;
}
#else
#define file_lstat file_stat
#endif
static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResultInt(interp, sb.st_atime);
return JIM_OK;
}
|
| ︙ | ︙ | |||
3983 3984 3985 3986 3987 3988 3989 |
Jim_SetResultString(interp, "Not implemented", -1);
return JIM_ERR;
#endif
}
static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
| | | | 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 |
Jim_SetResultString(interp, "Not implemented", -1);
return JIM_ERR;
#endif
}
static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (argc == 2) {
jim_wide secs;
if (Jim_GetWide(interp, argv[1], &secs) != JIM_OK) {
return JIM_ERR;
}
return JimSetFileTimes(interp, Jim_String(argv[0]), secs * 1000000);
}
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResultInt(interp, sb.st_mtime);
return JIM_OK;
}
#ifdef STAT_MTIME_US
static int file_cmd_mtimeus(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (argc == 2) {
jim_wide us;
if (Jim_GetWide(interp, argv[1], &us) != JIM_OK) {
return JIM_ERR;
}
return JimSetFileTimes(interp, Jim_String(argv[0]), us);
|
| ︙ | ︙ | |||
4026 4027 4028 4029 4030 4031 4032 |
static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return Jim_EvalPrefix(interp, "file copy", argc, argv);
}
static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
| | | | | | | | | | | | | 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 |
static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return Jim_EvalPrefix(interp, "file copy", argc, argv);
}
static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResultInt(interp, sb.st_size);
return JIM_OK;
}
static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
int ret = 0;
if (file_stat(interp, argv[0], &sb) == JIM_OK) {
ret = S_ISDIR(sb.st_mode);
}
Jim_SetResultInt(interp, ret);
return JIM_OK;
}
static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
int ret = 0;
if (file_stat(interp, argv[0], &sb) == JIM_OK) {
ret = S_ISREG(sb.st_mode);
}
Jim_SetResultInt(interp, ret);
return JIM_OK;
}
#ifdef HAVE_GETEUID
static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
int ret = 0;
if (file_stat(interp, argv[0], &sb) == JIM_OK) {
ret = (geteuid() == sb.st_uid);
}
Jim_SetResultInt(interp, ret);
return JIM_OK;
}
#endif
#if defined(HAVE_READLINK)
static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
const char *path = Jim_String(argv[0]);
char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
int linkLength = readlink(path, linkValue, MAXPATHLEN);
if (linkLength == -1) {
Jim_Free(linkValue);
Jim_SetResultFormatted(interp, "could not read link \"%#s\": %s", argv[0], strerror(errno));
return JIM_ERR;
}
linkValue[linkLength] = 0;
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
return JIM_OK;
}
#endif
static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
return JIM_OK;
}
#ifdef Jim_LinkStat
static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
}
#else
#define file_cmd_lstat file_cmd_stat
#endif
static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_stat_t sb;
if (file_stat(interp, argv[0], &sb) != JIM_OK) {
return JIM_ERR;
}
return Jim_FileStoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
}
static const jim_subcmd_type file_command_table[] = {
{ "atime",
"name",
file_cmd_atime,
1,
|
| ︙ | ︙ | |||
4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 |
},
{ "tail",
"name",
file_cmd_tail,
1,
1,
},
{ "normalize",
"name",
file_cmd_normalize,
1,
1,
| > > > > > > > | 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 |
},
{ "tail",
"name",
file_cmd_tail,
1,
1,
},
{ "split",
"name",
file_cmd_split,
1,
1,
},
{ "normalize",
"name",
file_cmd_normalize,
1,
1,
|
| ︙ | ︙ | |||
4358 4359 4360 4361 4362 4363 4364 |
char *cwd = Jim_Alloc(MAXPATHLEN);
if (getcwd(cwd, MAXPATHLEN) == NULL) {
Jim_SetResultString(interp, "Failed to get pwd", -1);
Jim_Free(cwd);
return JIM_ERR;
}
| < | < < < < < < | < < < < < | | 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 |
char *cwd = Jim_Alloc(MAXPATHLEN);
if (getcwd(cwd, MAXPATHLEN) == NULL) {
Jim_SetResultString(interp, "Failed to get pwd", -1);
Jim_Free(cwd);
return JIM_ERR;
}
JimFixPath(cwd);
Jim_SetResultString(interp, cwd, -1);
Jim_Free(cwd);
return JIM_OK;
}
int Jim_fileInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "file");
Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
return JIM_OK;
}
#include <string.h>
#include <ctype.h>
#if (!(defined(HAVE_VFORK) || defined(HAVE_FORK)) || !defined(HAVE_WAITPID)) && !defined(__MINGW32__)
static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *cmdlineObj = Jim_NewEmptyStringObj(interp);
int i, j;
int rc;
|
| ︙ | ︙ | |||
4438 4439 4440 4441 4442 4443 4444 |
}
return JIM_OK;
}
int Jim_execInit(Jim_Interp *interp)
{
| | < < | | | | < < < < < | | | | 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 |
}
return JIM_OK;
}
int Jim_execInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "exec");
Jim_CreateCommand(interp, "exec", Jim_ExecCmd, NULL, NULL);
return JIM_OK;
}
#else
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
struct WaitInfoTable;
static char **JimOriginalEnviron(void);
static char **JimSaveEnv(char **env);
static void JimRestoreEnv(char **env);
static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
phandle_t **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr);
static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj);
static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
#if defined(__MINGW32__)
static phandle_t JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId);
#endif
static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
{
int len;
const char *s = Jim_GetString(objPtr, &len);
if (len > 0 && s[len - 1] == '\n') {
objPtr->length--;
objPtr->bytes[objPtr->length] = '\0';
}
}
static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
{
char buf[256];
int ret = 0;
while (1) {
int retval = read(fd, buf, sizeof(buf));
if (retval > 0) {
ret = 1;
Jim_AppendString(interp, strObj, buf, retval);
}
if (retval <= 0) {
break;
}
}
close(fd);
return ret;
}
static char **JimBuildEnv(Jim_Interp *interp)
{
int i;
int size;
|
| ︙ | ︙ | |||
4556 4557 4558 4559 4560 4561 4562 |
static void JimFreeEnv(char **env, char **original_environ)
{
if (env != original_environ) {
Jim_Free(env);
}
}
| | | | | | 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 |
static void JimFreeEnv(char **env, char **original_environ)
{
if (env != original_environ) {
Jim_Free(env);
}
}
static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
{
Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
if (pid <= 0) {
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1));
}
else if (WIFEXITED(waitStatus)) {
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
}
else {
const char *type;
const char *action;
const char *signame;
|
| ︙ | ︙ | |||
4592 4593 4594 4595 4596 4597 4598 |
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
if (errStrObj) {
Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
}
| | | | | 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 |
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
if (errStrObj) {
Jim_AppendStrings(interp, errStrObj, "child ", action, " by signal ", Jim_SignalId(WTERMSIG(waitStatus)), "\n", NULL);
}
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, pid));
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1));
}
return errorCode;
}
static int JimCheckWaitStatus(Jim_Interp *interp, long pid, int waitStatus, Jim_Obj *errStrObj)
{
if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
return JIM_OK;
}
Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj));
return JIM_ERR;
}
struct WaitInfo
{
phandle_t phandle;
int status;
int flags;
};
struct WaitInfoTable {
struct WaitInfo *info;
|
| ︙ | ︙ | |||
4649 4650 4651 4652 4653 4654 4655 |
table->info = NULL;
table->size = table->used = 0;
table->refcount = 1;
return table;
}
| | | | | | 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 |
table->info = NULL;
table->size = table->used = 0;
table->refcount = 1;
return table;
}
static int JimWaitRemove(struct WaitInfoTable *table, phandle_t phandle)
{
int i;
for (i = 0; i < table->used; i++) {
if (phandle == table->info[i].phandle) {
if (i != table->used - 1) {
table->info[i] = table->info[table->used - 1];
}
table->used--;
return 0;
}
}
return -1;
}
static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int outputId;
int errorId;
phandle_t *pidPtr;
int numPids, result;
int child_siginfo = 1;
Jim_Obj *childErrObj;
Jim_Obj *errStrObj;
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
if (argc > 1 && Jim_CompareStringImmediate(interp, argv[argc - 1], "&")) {
Jim_Obj *listObj;
int i;
argc--;
numPids = JimCreatePipeline(interp, argc - 1, argv + 1, &pidPtr, NULL, NULL, NULL);
if (numPids < 0) {
return JIM_ERR;
}
listObj = Jim_NewListObj(interp, NULL, 0);
for (i = 0; i < numPids; i++) {
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, JimProcessPid(pidPtr[i])));
}
Jim_SetResult(interp, listObj);
JimDetachPids(table, numPids, pidPtr);
Jim_Free(pidPtr);
return JIM_OK;
}
|
| ︙ | ︙ | |||
4726 4727 4728 4729 4730 4731 4732 |
if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
result = JIM_ERR;
}
if (errorId != -1) {
int ret;
| | | 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 |
if (JimCleanupChildren(interp, numPids, pidPtr, childErrObj) != JIM_OK) {
result = JIM_ERR;
}
if (errorId != -1) {
int ret;
Jim_Lseek(errorId, 0, SEEK_SET);
ret = JimAppendStreamToString(interp, errorId, errStrObj);
if (ret < 0) {
Jim_SetResultErrno(interp, "error reading from error pipe");
result = JIM_ERR;
}
else if (ret > 0) {
|
| ︙ | ︙ | |||
4753 4754 4755 4756 4757 4758 4759 |
Jim_SetResult(interp, errStrObj);
return result;
}
| | | | < | | | | 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 |
Jim_SetResult(interp, errStrObj);
return result;
}
static long JimWaitForProcess(struct WaitInfoTable *table, phandle_t phandle, int *statusPtr)
{
if (JimWaitRemove(table, phandle) == 0) {
return waitpid(phandle, statusPtr, 0);
}
return -1;
}
static void JimDetachPids(struct WaitInfoTable *table, int numPids, const phandle_t *pidPtr)
{
int j;
for (j = 0; j < numPids; j++) {
int i;
for (i = 0; i < table->used; i++) {
if (pidPtr[j] == table->info[i].phandle) {
table->info[i].flags |= WI_DETACHED;
break;
}
}
}
}
|
| ︙ | ︙ | |||
4812 4813 4814 4815 4816 4817 4818 |
}
waitPtr = table->info;
dest = 0;
for (count = table->used; count > 0; waitPtr++, count--) {
if (waitPtr->flags & WI_DETACHED) {
int status;
| | | < | > | > | > > > > > > > > | | | | | 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 |
}
waitPtr = table->info;
dest = 0;
for (count = table->used; count > 0; waitPtr++, count--) {
if (waitPtr->flags & WI_DETACHED) {
int status;
long pid = waitpid(waitPtr->phandle, &status, WNOHANG);
if (pid > 0) {
table->used--;
continue;
}
}
if (waitPtr != &table->info[dest]) {
table->info[dest] = *waitPtr;
}
dest++;
}
}
static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
int nohang = 0;
long pid;
phandle_t phandle;
int status;
Jim_Obj *errCodeObj;
if (argc == 1) {
JimReapDetachedPids(table);
return JIM_OK;
}
if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
nohang = 1;
}
if (argc != nohang + 2) {
Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
return JIM_ERR;
}
if (Jim_GetLong(interp, argv[nohang + 1], &pid) != JIM_OK) {
return JIM_ERR;
}
phandle = JimWaitPid(pid, &status, nohang ? WNOHANG : 0);
if (phandle == JIM_BAD_PHANDLE) {
pid = -1;
}
#ifndef __MINGW32__
else if (pid < 0) {
pid = phandle;
}
#endif
errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);
if (phandle != JIM_BAD_PHANDLE && (WIFEXITED(status) || WIFSIGNALED(status))) {
JimWaitRemove(table, phandle);
}
Jim_SetResult(interp, errCodeObj);
return JIM_OK;
}
static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "");
return JIM_ERR;
}
Jim_SetResultInt(interp, (jim_wide)getpid());
return JIM_OK;
}
static int
JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, phandle_t **pidArrayPtr,
int *inPipePtr, int *outPipePtr, int *errFilePtr)
{
phandle_t *pidPtr = NULL; /* Points to alloc-ed array holding all
* the pids of child processes. */
int numPids = 0; /* Actual number of processes that exist
* at *pidPtr right now. */
int cmdCount; /* Count of number of distinct commands
* found in argc/argv. */
const char *input = NULL; /* Describes input for pipeline, depending
* on "inputFile". NULL means take input
|
| ︙ | ︙ | |||
4923 4924 4925 4926 4927 4928 4929 |
int errorId = -1;
int lastOutputId = -1;
int pipeIds[2];
int firstArg, lastArg; /* Indexes of first and last arguments in
* current command. */
int lastBar;
int i;
| | | | 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 |
int errorId = -1;
int lastOutputId = -1;
int pipeIds[2];
int firstArg, lastArg; /* Indexes of first and last arguments in
* current command. */
int lastBar;
int i;
phandle_t phandle;
char **save_environ;
#if defined(HAVE_EXECVPE) && !defined(__MINGW32__)
char **child_environ;
#endif
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
int arg_count = 0;
|
| ︙ | ︙ | |||
5051 5052 5053 5054 5055 5056 5057 |
goto error;
}
if (write(inputId, input, input_len) != input_len) {
Jim_SetResultErrno(interp, "couldn't write temp file");
close(inputId);
goto error;
}
| | | 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 |
goto error;
}
if (write(inputId, input, input_len) != input_len) {
Jim_SetResultErrno(interp, "couldn't write temp file");
close(inputId);
goto error;
}
Jim_Lseek(inputId, 0L, SEEK_SET);
}
else if (inputFile == FILE_HANDLE) {
int fd = JimGetChannelFd(interp, input);
if (fd < 0) {
goto error;
}
|
| ︙ | ︙ | |||
5143 5144 5145 5146 5147 5148 5149 |
goto error;
}
*errFilePtr = dup(errorId);
}
pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
| < < < | 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 |
goto error;
}
*errFilePtr = dup(errorId);
}
pidPtr = Jim_Alloc(cmdCount * sizeof(*pidPtr));
for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
int pipe_dup_err = 0;
int origErrorId = errorId;
for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
if (strcmp(arg_array[lastArg], "|") == 0) {
break;
|
| ︙ | ︙ | |||
5187 5188 5189 5190 5191 5192 5193 |
if (pipe_dup_err) {
errorId = outputId;
}
#ifdef __MINGW32__
| | | > > > | > > > | | | | | | | < < < | 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 |
if (pipe_dup_err) {
errorId = outputId;
}
#ifdef __MINGW32__
phandle = JimStartWinProcess(interp, &arg_array[firstArg], save_environ, inputId, outputId, errorId);
if (phandle == JIM_BAD_PHANDLE) {
Jim_SetResultFormatted(interp, "couldn't exec \"%s\"", arg_array[firstArg]);
goto error;
}
#else
i = strlen(arg_array[firstArg]);
#ifdef HAVE_EXECVPE
child_environ = Jim_GetEnviron();
#endif
#ifdef HAVE_VFORK
phandle = vfork();
#else
phandle = fork();
#endif
if (phandle < 0) {
Jim_SetResultErrno(interp, "couldn't fork child process");
goto error;
}
if (phandle == 0) {
if (inputId != -1 && inputId != fileno(stdin)) {
dup2(inputId, fileno(stdin));
close(inputId);
}
if (outputId != -1 && outputId != fileno(stdout)) {
dup2(outputId, fileno(stdout));
if (outputId != errorId) {
close(outputId);
}
}
if (errorId != -1 && errorId != fileno(stderr)) {
dup2(errorId, fileno(stderr));
close(errorId);
}
if (outPipePtr && *outPipePtr != -1) {
close(*outPipePtr);
}
if (errFilePtr && *errFilePtr != -1) {
close(*errFilePtr);
}
if (pipeIds[0] != -1) {
close(pipeIds[0]);
}
if (lastOutputId != -1) {
close(lastOutputId);
}
execvpe(arg_array[firstArg], &arg_array[firstArg], child_environ);
if (write(fileno(stderr), "couldn't exec \"", 15) &&
write(fileno(stderr), arg_array[firstArg], i) &&
write(fileno(stderr), "\"\n", 2)) {
}
|
| ︙ | ︙ | |||
5260 5261 5262 5263 5264 5265 5266 |
if (table->used == table->size) {
table->size += WAIT_TABLE_GROW_BY;
table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
}
| | | | 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 |
if (table->used == table->size) {
table->size += WAIT_TABLE_GROW_BY;
table->info = Jim_Realloc(table->info, table->size * sizeof(*table->info));
}
table->info[table->used].phandle = phandle;
table->info[table->used].flags = 0;
table->used++;
pidPtr[numPids] = phandle;
errorId = origErrorId;
if (inputId != -1) {
close(inputId);
|
| ︙ | ︙ | |||
5320 5321 5322 5323 5324 5325 5326 |
close(pipeIds[0]);
}
if (pipeIds[1] != -1) {
close(pipeIds[1]);
}
if (pidPtr != NULL) {
for (i = 0; i < numPids; i++) {
| | | | > | < < < < < > | 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 |
close(pipeIds[0]);
}
if (pipeIds[1] != -1) {
close(pipeIds[1]);
}
if (pidPtr != NULL) {
for (i = 0; i < numPids; i++) {
if (pidPtr[i] != JIM_BAD_PHANDLE) {
JimDetachPids(table, 1, &pidPtr[i]);
}
}
Jim_Free(pidPtr);
}
numPids = -1;
goto cleanup;
}
static int JimCleanupChildren(Jim_Interp *interp, int numPids, phandle_t *pidPtr, Jim_Obj *errStrObj)
{
struct WaitInfoTable *table = Jim_CmdPrivData(interp);
int result = JIM_OK;
int i;
for (i = 0; i < numPids; i++) {
int waitStatus = 0;
long pid = JimWaitForProcess(table, pidPtr[i], &waitStatus);
if (pid > 0) {
if (JimCheckWaitStatus(interp, pid, waitStatus, errStrObj) != JIM_OK) {
result = JIM_ERR;
}
}
}
Jim_Free(pidPtr);
return result;
}
int Jim_execInit(Jim_Interp *interp)
{
struct WaitInfoTable *waitinfo;
Jim_PackageProvideCheck(interp, "exec");
waitinfo = JimAllocWaitInfoTable();
Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
waitinfo->refcount++;
Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);
|
| ︙ | ︙ | |||
5481 5482 5483 5484 5485 5486 5487 |
if (quote) {
Jim_AppendString(interp, strObj, "\"", 1);
}
}
return strObj;
}
| | | | | 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 |
if (quote) {
Jim_AppendString(interp, strObj, "\"", 1);
}
}
return strObj;
}
static phandle_t
JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId)
{
STARTUPINFO startInfo;
PROCESS_INFORMATION procInfo;
HANDLE hProcess;
char execPath[MAX_PATH];
phandle_t phandle = INVALID_HANDLE_VALUE;
Jim_Obj *cmdLineObj;
char *winenv;
if (JimWinFindExecutable(argv[0], execPath) < 0) {
return phandle;
}
argv[0] = execPath;
hProcess = GetCurrentProcess();
cmdLineObj = JimWinBuildCommandLine(interp, argv);
|
| ︙ | ︙ | |||
5556 5557 5558 5559 5560 5561 5562 |
goto end;
}
WaitForInputIdle(procInfo.hProcess, 5000);
CloseHandle(procInfo.hThread);
| | | | 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 |
goto end;
}
WaitForInputIdle(procInfo.hProcess, 5000);
CloseHandle(procInfo.hThread);
phandle = procInfo.hProcess;
end:
Jim_FreeNewObj(interp, cmdLineObj);
if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdInput);
}
if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdOutput);
}
if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdError);
}
return phandle;
}
#else
static char **JimOriginalEnviron(void)
{
return Jim_GetEnviron();
|
| ︙ | ︙ | |||
5595 5596 5597 5598 5599 5600 5601 |
JimFreeEnv(Jim_GetEnviron(), env);
Jim_SetEnviron(env);
}
#endif
#endif
| < < < < < < < < < < < < | 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 |
JimFreeEnv(Jim_GetEnviron(), env);
Jim_SetEnviron(env);
}
#endif
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#ifdef HAVE_SYS_TIME_H
|
| ︙ | ︙ | |||
5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 |
pt = strptime(Jim_String(argv[0]), options.format, &tm);
if (pt == 0 || *pt != 0) {
Jim_SetResultString(interp, "Failed to parse time according to format", -1);
return JIM_ERR;
}
Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm));
return JIM_OK;
}
#endif
static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
| > | > | > > > > < < < < | < < < < < | < | | 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 |
pt = strptime(Jim_String(argv[0]), options.format, &tm);
if (pt == 0 || *pt != 0) {
Jim_SetResultString(interp, "Failed to parse time according to format", -1);
return JIM_ERR;
}
tm.tm_isdst = options.gmt ? 0 : -1;
Jim_SetResultInt(interp, options.gmt ? jim_timegm(&tm) : mktime(&tm));
return JIM_OK;
}
#endif
static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000000);
return JIM_OK;
}
static int clock_cmd_clicks(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW));
return JIM_OK;
}
static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME));
return JIM_OK;
}
static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_SetResultInt(interp, Jim_GetTimeUsec(CLOCK_REALTIME) / 1000);
return JIM_OK;
}
static const jim_subcmd_type clock_command_table[] = {
{ "clicks",
NULL,
clock_cmd_clicks,
0,
0,
},
{ "format",
"seconds ?-format string? ?-gmt boolean?",
clock_cmd_format,
|
| ︙ | ︙ | |||
5807 5808 5809 5810 5811 5812 5813 |
},
{ NULL }
};
int Jim_clockInit(Jim_Interp *interp)
{
| | < < | 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 |
},
{ NULL }
};
int Jim_clockInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "clock");
Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
return JIM_OK;
}
#include <limits.h>
#include <stdlib.h>
#include <string.h>
|
| ︙ | ︙ | |||
5884 5885 5886 5887 5888 5889 5890 |
objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
if (objPtr == NULL) {
return JIM_OK;
}
| | > < | 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 |
objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE);
if (objPtr == NULL) {
return JIM_OK;
}
dictValuesObj = Jim_DictPairs(interp, objPtr, &len);
if (dictValuesObj == NULL) {
Jim_SetResultString(interp, "", -1);
return JIM_OK;
}
resultObj = Jim_NewDictObj(interp, NULL, 0);
for (i = 0; i < len; i += 2) {
if (!Jim_StringMatchObj(interp, argv[1], dictValuesObj[i], 0)) {
Jim_DictAddElement(interp, resultObj, dictValuesObj[i], dictValuesObj[i + 1]);
}
}
Jim_SetVariable(interp, argv[0], resultObj);
return JIM_OK;
}
static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
|
| ︙ | ︙ | |||
6029 6030 6031 6032 6033 6034 6035 |
},
{ NULL
}
};
int Jim_arrayInit(Jim_Interp *interp)
{
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | > > > > < < | | 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 |
},
{ NULL
}
};
int Jim_arrayInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "array");
Jim_CreateCommand(interp, "array", Jim_SubCmdProc, (void *)array_command_table, NULL);
return JIM_OK;
}
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#ifdef HAVE_SYS_SYSINFO_H
#include <sys/sysinfo.h>
#endif
static void Jim_PosixSetError(Jim_Interp *interp)
{
Jim_SetResultString(interp, strerror(errno), -1);
}
#if defined(HAVE_FORK)
static int Jim_PosixForkCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
pid_t pid;
JIM_NOTUSED(argv);
if (argc != 1) {
Jim_WrongNumArgs(interp, 1, argv, "");
return JIM_ERR;
}
if ((pid = fork()) == -1) {
Jim_PosixSetError(interp);
return JIM_ERR;
}
Jim_SetResultInt(interp, (jim_wide) pid);
return JIM_OK;
}
#endif
int Jim_posixInit(Jim_Interp *interp)
{
Jim_PackageProvideCheck(interp, "posix");
#ifdef HAVE_FORK
Jim_CreateCommand(interp, "os.fork", Jim_PosixForkCommand, NULL, NULL);
#endif
return JIM_OK;
}
int Jim_InitStaticExtensions(Jim_Interp *interp)
{
extern int Jim_bootstrapInit(Jim_Interp *);
extern int Jim_aioInit(Jim_Interp *);
extern int Jim_readdirInit(Jim_Interp *);
extern int Jim_regexpInit(Jim_Interp *);
extern int Jim_fileInit(Jim_Interp *);
extern int Jim_globInit(Jim_Interp *);
extern int Jim_execInit(Jim_Interp *);
extern int Jim_posixInit(Jim_Interp *);
extern int Jim_clockInit(Jim_Interp *);
extern int Jim_arrayInit(Jim_Interp *);
extern int Jim_stdlibInit(Jim_Interp *);
extern int Jim_tclcompatInit(Jim_Interp *);
Jim_bootstrapInit(interp);
Jim_aioInit(interp);
Jim_readdirInit(interp);
Jim_regexpInit(interp);
Jim_fileInit(interp);
Jim_globInit(interp);
Jim_execInit(interp);
Jim_posixInit(interp);
Jim_clockInit(interp);
Jim_arrayInit(interp);
Jim_stdlibInit(interp);
Jim_tclcompatInit(interp);
return JIM_OK;
}
#ifndef JIM_TINY
#define JIM_OPTIMIZATION
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
#include <setjmp.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
#ifdef HAVE_CRT_EXTERNS_H
#include <crt_externs.h>
#endif
|
| ︙ | ︙ | |||
6124 6125 6126 6127 6128 6129 6130 | #define JIM_DEBUG_PANIC #endif #define JIM_INTEGER_SPACE 24 | > | > > > > < < > > > > > > > > > > > > > > > > > | > > | | > > | > > | > | < > | 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 |
#define JIM_DEBUG_PANIC
#endif
#define JIM_INTEGER_SPACE 24
#if defined(DEBUG_SHOW_SCRIPT) || defined(DEBUG_SHOW_SCRIPT_TOKENS) || defined(JIM_DEBUG_COMMAND) || defined(DEBUG_SHOW_SUBST)
static const char *jim_tt_name(int type);
#endif
#ifdef JIM_DEBUG_PANIC
static void JimPanicDump(int fail_condition, const char *fmt, ...);
#define JimPanic(X) JimPanicDump X
#else
#define JimPanic(X)
#endif
#ifdef JIM_OPTIMIZATION
static int JimIsWide(Jim_Obj *objPtr);
#define JIM_IF_OPTIM(X) X
#else
#define JIM_IF_OPTIM(X)
#endif
static char JimEmptyStringRep[] = "";
static void JimFreeCallFrame(Jim_Interp *interp, Jim_CallFrame *cf, int action);
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int listindex, Jim_Obj *newObjPtr,
int flags);
static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr, Jim_Obj *const *indexv, int indexc,
Jim_Obj **resultObj, int flags);
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands);
static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype,
const char *prefix, const char *const *tablePtr, const char *name);
static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv);
static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr);
static int JimSign(jim_wide w);
static void JimPrngSeed(Jim_Interp *interp, unsigned char *seed, int seedLen);
static void JimRandomBytes(Jim_Interp *interp, void *dest, unsigned int len);
static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv);
static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr);
static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
#define JIM_DICT_SUGAR 100
#define JimWideValue(objPtr) (objPtr)->internalRep.wideValue
#define JimObjTypeName(O) ((O)->typePtr ? (O)->typePtr->name : "none")
static int utf8_tounicode_case(const char *s, int *uc, int upper)
{
int l = utf8_tounicode(s, uc);
if (upper) {
*uc = utf8_upper(*uc);
}
return l;
}
static Jim_Obj *JimPushInterpObjImpl(Jim_Obj **iop, Jim_Obj *no)
{
Jim_Obj *io = *iop;
Jim_IncrRefCount(no);
*iop = no;
return io;
}
#define JimPushInterpObj(IO, NO) JimPushInterpObjImpl(&(IO), NO)
#define JimPopInterpObj(I, IO, SO) do { Jim_DecrRefCount(I, IO); IO = SO; } while (0)
#define JIM_CHARSET_SCAN 2
#define JIM_CHARSET_GLOB 0
static const char *JimCharsetMatch(const char *pattern, int plen, int c, int flags)
{
int not = 0;
int pchar;
int match = 0;
int nocase = 0;
int n;
if (flags & JIM_NOCASE) {
nocase++;
c = utf8_upper(c);
}
if (flags & JIM_CHARSET_SCAN) {
if (*pattern == '^') {
not++;
pattern++;
plen--;
}
if (*pattern == ']') {
goto first;
}
}
while (plen && *pattern != ']') {
if (pattern[0] == '\\') {
first:
n = utf8_tounicode_case(pattern, &pchar, nocase);
pattern += n;
plen -= n;
}
else {
int start;
int end;
n = utf8_tounicode_case(pattern, &start, nocase);
pattern += n;
plen -= n;
if (pattern[0] == '-' && plen > 1) {
n = 1 + utf8_tounicode_case(pattern + 1, &end, nocase);
pattern += n;
plen -= n;
if ((c >= start && c <= end) || (c >= end && c <= start)) {
match = 1;
}
continue;
}
|
| ︙ | ︙ | |||
6240 6241 6242 6243 6244 6245 6246 |
}
return match ? pattern : NULL;
}
| | > > | | > > | | | | > > | > > | > > | | > | > > > | > > | > > | | > | | | < < | < < | < < | < < < | < < | | < | | | | 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 |
}
return match ? pattern : NULL;
}
static int JimGlobMatch(const char *pattern, int plen, const char *string, int slen, int nocase)
{
int c;
int pchar;
int n;
const char *p;
while (plen) {
switch (pattern[0]) {
case '*':
while (pattern[1] == '*' && plen) {
pattern++;
plen--;
}
pattern++;
plen--;
if (!plen) {
return 1;
}
while (slen) {
if (JimGlobMatch(pattern, plen, string, slen, nocase))
return 1;
n = utf8_tounicode(string, &c);
string += n;
slen -= n;
}
return 0;
case '?':
n = utf8_tounicode(string, &c);
string += n;
slen -= n;
break;
case '[': {
n = utf8_tounicode(string, &c);
string += n;
slen -= n;
p = JimCharsetMatch(pattern + 1, plen - 1, c, nocase ? JIM_NOCASE : 0);
if (!p) {
return 0;
}
plen -= p - pattern;
pattern = p;
if (!plen) {
continue;
}
break;
}
case '\\':
if (pattern[1]) {
pattern++;
plen--;
}
default:
n = utf8_tounicode_case(string, &c, nocase);
string += n;
slen -= n;
utf8_tounicode_case(pattern, &pchar, nocase);
if (pchar != c) {
return 0;
}
break;
}
n = utf8_tounicode_case(pattern, &pchar, nocase);
pattern += n;
plen -= n;
if (!slen) {
while (*pattern == '*' && plen) {
pattern++;
plen--;
}
break;
}
}
if (!plen && !slen) {
return 1;
}
return 0;
}
static int JimStringCompareUtf8(const char *s1, int l1, const char *s2, int l2, int nocase)
{
int minlen = l1;
if (l2 < l1) {
minlen = l2;
}
while (minlen) {
int c1, c2;
s1 += utf8_tounicode_case(s1, &c1, nocase);
s2 += utf8_tounicode_case(s2, &c2, nocase);
if (c1 != c2) {
return JimSign(c1 - c2);
}
minlen--;
}
if (l1 < l2) {
return -1;
}
if (l1 > l2) {
return 1;
}
return 0;
}
static int JimStringFirst(const char *s1, int l1, const char *s2, int l2, int idx)
{
int i;
|
| ︙ | ︙ | |||
6414 6415 6416 6417 6418 6419 6420 |
return JIM_OK;
}
static int JimNumberBase(const char *str, int *base, int *sign)
{
int i = 0;
| | | 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 |
return JIM_OK;
}
static int JimNumberBase(const char *str, int *base, int *sign)
{
int i = 0;
*base = 0;
while (isspace(UCHAR(str[i]))) {
i++;
}
if (str[i] == '-') {
*sign = -1;
|
| ︙ | ︙ | |||
6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 |
}
switch (str[i + 1]) {
case 'x': case 'X': *base = 16; break;
case 'o': case 'O': *base = 8; break;
case 'b': case 'B': *base = 2; break;
default: return 0;
}
i += 2;
if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
return i;
}
| > | | | | 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 |
}
switch (str[i + 1]) {
case 'x': case 'X': *base = 16; break;
case 'o': case 'O': *base = 8; break;
case 'b': case 'B': *base = 2; break;
case 'd': case 'D': *base = 10; break;
default: return 0;
}
i += 2;
if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
return i;
}
*base = 0;
return 0;
}
static long jim_strtol(const char *str, char **endptr)
{
int sign;
int base;
int i = JimNumberBase(str, &base, &sign);
if (base != 0) {
long value = strtol(str + i, endptr, base);
if (endptr == NULL || *endptr != str + i) {
return value * sign;
}
}
return strtol(str, endptr, 10);
}
static jim_wide jim_strtoull(const char *str, char **endptr)
{
#ifdef HAVE_LONG_LONG
int sign;
int base;
int i = JimNumberBase(str, &base, &sign);
if (base != 0) {
jim_wide value = strtoull(str + i, endptr, base);
if (endptr == NULL || *endptr != str + i) {
return value * sign;
}
}
|
| ︙ | ︙ | |||
6561 6562 6563 6564 6565 6566 6567 |
va_start(ap, fmt);
fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n\n");
va_end(ap);
| | | > > | | > > | < < > | | | < | | < | | > | < > > | > | > > > > > > | > | | > > > > > | 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 |
va_start(ap, fmt);
fprintf(stderr, "\nJIM INTERPRETER PANIC: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n\n");
va_end(ap);
#if defined(HAVE_BACKTRACE)
{
void *array[40];
int size, i;
char **strings;
size = backtrace(array, 40);
strings = backtrace_symbols(array, size);
for (i = 0; i < size; i++)
fprintf(stderr, "[backtrace] %s\n", strings[i]);
fprintf(stderr, "[backtrace] Include the above lines and the output\n");
fprintf(stderr, "[backtrace] of 'nm <executable>' in the bug report.\n");
}
#endif
exit(1);
}
#endif
void *JimDefaultAllocator(void *ptr, size_t size)
{
if (size == 0) {
free(ptr);
return NULL;
}
else if (ptr) {
return realloc(ptr, size);
}
else {
return malloc(size);
}
}
void *(*Jim_Allocator)(void *ptr, size_t size) = JimDefaultAllocator;
char *Jim_StrDup(const char *s)
{
return Jim_StrDupLen(s, strlen(s));
}
char *Jim_StrDupLen(const char *s, int l)
{
char *copy = Jim_Alloc(l + 1);
memcpy(copy, s, l);
copy[l] = 0;
return copy;
}
jim_wide Jim_GetTimeUsec(unsigned type)
{
long long now;
struct timeval tv;
#if defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
if (clock_gettime(type, &ts) == 0) {
now = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
}
else
#endif
{
gettimeofday(&tv, NULL);
now = tv.tv_sec * 1000000LL + tv.tv_usec;
}
return now;
}
static void JimExpandHashTableIfNeeded(Jim_HashTable *ht);
static unsigned int JimHashTableNextPower(unsigned int size);
static Jim_HashEntry *JimInsertHashEntry(Jim_HashTable *ht, const void *key, int replace);
|
| ︙ | ︙ | |||
6640 6641 6642 6643 6644 6645 6646 |
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
| > | | | | | < | > | | 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 |
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
unsigned int Jim_GenHashFunction(const unsigned char *string, int length)
{
unsigned result = 0;
string += length;
while (length--) {
result += (result << 3) + (unsigned char)(*--string);
}
return result;
}
static void JimResetHashTable(Jim_HashTable *ht)
{
ht->table = NULL;
ht->size = 0;
|
| ︙ | ︙ | |||
6683 6684 6685 6686 6687 6688 6689 |
{
JimResetHashTable(ht);
ht->type = type;
ht->privdata = privDataPtr;
return JIM_OK;
}
| < < < < < < < < < | 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 |
{
JimResetHashTable(ht);
ht->type = type;
ht->privdata = privDataPtr;
return JIM_OK;
}
void Jim_ExpandHashTable(Jim_HashTable *ht, unsigned int size)
{
Jim_HashTable n;
unsigned int realsize = JimHashTableNextPower(size), i;
if (size <= ht->used)
|
| ︙ | ︙ | |||
6740 6741 6742 6743 6744 6745 6746 |
assert(ht->used == 0);
Jim_Free(ht->table);
*ht = n;
}
| < < < | | 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 |
assert(ht->used == 0);
Jim_Free(ht->table);
*ht = n;
}
int Jim_AddHashEntry(Jim_HashTable *ht, const void *key, void *val)
{
Jim_HashEntry *entry = JimInsertHashEntry(ht, key, 0);;
if (entry == NULL)
return JIM_ERR;
Jim_SetHashKey(ht, entry, key);
Jim_SetHashVal(ht, entry, val);
return JIM_OK;
|
| ︙ | ︙ | |||
6784 6785 6786 6787 6788 6789 6790 |
Jim_SetHashVal(ht, entry, val);
existed = 0;
}
return existed;
}
| < < < < | < | > | < | | | | | | > | | | < | | | | | < | > | > | | < > > > > > > | 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 |
Jim_SetHashVal(ht, entry, val);
existed = 0;
}
return existed;
}
int Jim_DeleteHashEntry(Jim_HashTable *ht, const void *key)
{
if (ht->used) {
unsigned int h = Jim_HashKey(ht, key) & ht->sizemask;
Jim_HashEntry *prevHe = NULL;
Jim_HashEntry *he = ht->table[h];
while (he) {
if (Jim_CompareHashKeys(ht, key, he->key)) {
if (prevHe)
prevHe->next = he->next;
else
ht->table[h] = he->next;
ht->used--;
Jim_FreeEntryKey(ht, he);
Jim_FreeEntryVal(ht, he);
Jim_Free(he);
return JIM_OK;
}
prevHe = he;
he = he->next;
}
}
return JIM_ERR;
}
void Jim_ClearHashTable(Jim_HashTable *ht)
{
unsigned int i;
for (i = 0; ht->used > 0; i++) {
Jim_HashEntry *he, *nextHe;
he = ht->table[i];
while (he) {
nextHe = he->next;
Jim_FreeEntryKey(ht, he);
Jim_FreeEntryVal(ht, he);
Jim_Free(he);
ht->used--;
he = nextHe;
}
ht->table[i] = NULL;
}
}
int Jim_FreeHashTable(Jim_HashTable *ht)
{
Jim_ClearHashTable(ht);
Jim_Free(ht->table);
JimResetHashTable(ht);
return JIM_OK;
}
|
| ︙ | ︙ | |||
7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 |
const char *tend;
int tline;
int tt;
int eof;
int inquote;
int comment;
struct JimParseMissing missing;
};
static int JimParseScript(struct JimParserCtx *pc);
static int JimParseSep(struct JimParserCtx *pc);
static int JimParseEol(struct JimParserCtx *pc);
static int JimParseCmd(struct JimParserCtx *pc);
static int JimParseQuote(struct JimParserCtx *pc);
| > | 7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 |
const char *tend;
int tline;
int tt;
int eof;
int inquote;
int comment;
struct JimParseMissing missing;
const char *errmsg;
};
static int JimParseScript(struct JimParserCtx *pc);
static int JimParseSep(struct JimParserCtx *pc);
static int JimParseEol(struct JimParserCtx *pc);
static int JimParseCmd(struct JimParserCtx *pc);
static int JimParseQuote(struct JimParserCtx *pc);
|
| ︙ | ︙ | |||
7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 |
{
while (1) {
if (!pc->len) {
pc->tstart = pc->p;
pc->tend = pc->p - 1;
pc->tline = pc->linenr;
pc->tt = JIM_TT_EOL;
pc->eof = 1;
return JIM_OK;
}
switch (*(pc->p)) {
case '\\':
if (*(pc->p + 1) == '\n' && !pc->inquote) {
return JimParseSep(pc);
| > > > | 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 |
{
while (1) {
if (!pc->len) {
pc->tstart = pc->p;
pc->tend = pc->p - 1;
pc->tline = pc->linenr;
pc->tt = JIM_TT_EOL;
if (pc->inquote) {
pc->missing.ch = '"';
}
pc->eof = 1;
return JIM_OK;
}
switch (*(pc->p)) {
case '\\':
if (*(pc->p + 1) == '\n' && !pc->inquote) {
return JimParseSep(pc);
|
| ︙ | ︙ | |||
7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 |
return;
}
break;
case '"':
if (startofword) {
JimParseSubQuote(pc);
continue;
}
break;
case '{':
JimParseSubBrace(pc);
startofword = 0;
| > > > | 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 |
return;
}
break;
case '"':
if (startofword) {
JimParseSubQuote(pc);
if (pc->missing.ch == '"') {
return;
}
continue;
}
break;
case '{':
JimParseSubBrace(pc);
startofword = 0;
|
| ︙ | ︙ | |||
8303 8304 8305 8306 8307 8308 8309 |
return Alen == Blen && memcmp(sA, sB, Alen) == 0;
}
}
int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
{
| > | > > < < < < < < < < < < < < < > | | | | 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 |
return Alen == Blen && memcmp(sA, sB, Alen) == 0;
}
}
int Jim_StringMatchObj(Jim_Interp *interp, Jim_Obj *patternObjPtr, Jim_Obj *objPtr, int nocase)
{
int plen, slen;
const char *pattern = Jim_GetString(patternObjPtr, &plen);
const char *string = Jim_GetString(objPtr, &slen);
return JimGlobMatch(pattern, plen, string, slen, nocase);
}
int Jim_StringCompareObj(Jim_Interp *interp, Jim_Obj *firstObjPtr, Jim_Obj *secondObjPtr, int nocase)
{
const char *s1 = Jim_String(firstObjPtr);
int l1 = Jim_Utf8Length(interp, firstObjPtr);
const char *s2 = Jim_String(secondObjPtr);
int l2 = Jim_Utf8Length(interp, secondObjPtr);
return JimStringCompareUtf8(s1, l1, s2, l2, nocase);
}
static int JimRelToAbsIndex(int len, int idx)
{
if (idx < 0 && idx > -INT_MAX)
return len + idx;
return idx;
}
static void JimRelToAbsRange(int len, int *firstPtr, int *lastPtr, int *rangeLenPtr)
{
int rangeLen;
|
| ︙ | ︙ | |||
8802 8803 8804 8805 8806 8807 8808 |
void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
}
| < < < < < < < < < < < | 9569 9570 9571 9572 9573 9574 9575 9576 9577 9578 9579 9580 9581 9582 |
void DupSourceInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
dupPtr->internalRep.sourceValue = srcPtr->internalRep.sourceValue;
Jim_IncrRefCount(dupPtr->internalRep.sourceValue.fileNameObj);
}
static const Jim_ObjType scriptLineObjType = {
"scriptline",
NULL,
NULL,
NULL,
JIM_NONE,
};
|
| ︙ | ︙ | |||
8847 8848 8849 8850 8851 8852 8853 |
static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static const Jim_ObjType scriptObjType = {
"script",
FreeScriptInternalRep,
DupScriptInternalRep,
NULL,
| | | 9603 9604 9605 9606 9607 9608 9609 9610 9611 9612 9613 9614 9615 9616 9617 |
static void DupScriptInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static const Jim_ObjType scriptObjType = {
"script",
FreeScriptInternalRep,
DupScriptInternalRep,
NULL,
JIM_TYPE_NONE,
};
typedef struct ScriptToken
{
Jim_Obj *objPtr;
int type;
} ScriptToken;
|
| ︙ | ︙ | |||
8873 8874 8875 8876 8877 8878 8879 8880 8881 8882 8883 8884 8885 8886 |
int linenr;
int missing;
} ScriptObj;
static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
static int JimParseCheckMissing(Jim_Interp *interp, int ch);
static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
int i;
struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
if (--script->inUse != 0)
| > | 9629 9630 9631 9632 9633 9634 9635 9636 9637 9638 9639 9640 9641 9642 9643 |
int linenr;
int missing;
} ScriptObj;
static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
static int JimParseCheckMissing(Jim_Interp *interp, int ch);
static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr);
static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script);
void FreeScriptInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
int i;
struct ScriptObj *script = (void *)objPtr->internalRep.ptr;
if (--script->inUse != 0)
|
| ︙ | ︙ | |||
9088 9089 9090 9091 9092 9093 9094 |
while (wordtokens--) {
const ParseToken *t = &tokenlist->list[i++];
token->type = t->type;
token->objPtr = JimMakeScriptObj(interp, t);
Jim_IncrRefCount(token->objPtr);
| | | 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 |
while (wordtokens--) {
const ParseToken *t = &tokenlist->list[i++];
token->type = t->type;
token->objPtr = JimMakeScriptObj(interp, t);
Jim_IncrRefCount(token->objPtr);
Jim_SetSourceInfo(interp, token->objPtr, script->fileNameObj, t->line);
token++;
}
}
if (lineargs == 0) {
token--;
}
|
| ︙ | ︙ | |||
9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 |
msg = "missing quote";
break;
}
Jim_SetResultString(interp, msg, -1);
return JIM_ERR;
}
static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
ParseTokenList *tokenlist)
{
int i;
struct ScriptToken *token;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 9916 9917 9918 9919 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 |
msg = "missing quote";
break;
}
Jim_SetResultString(interp, msg, -1);
return JIM_ERR;
}
Jim_Obj *Jim_GetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr, int *lineptr)
{
int line;
Jim_Obj *fileNameObj;
if (objPtr->typePtr == &sourceObjType) {
fileNameObj = objPtr->internalRep.sourceValue.fileNameObj;
line = objPtr->internalRep.sourceValue.lineNumber;
}
else if (objPtr->typePtr == &scriptObjType) {
ScriptObj *script = JimGetScript(interp, objPtr);
fileNameObj = script->fileNameObj;
line = script->firstline;
}
else {
fileNameObj = interp->emptyObj;
line = 1;
}
*lineptr = line;
return fileNameObj;
}
void Jim_SetSourceInfo(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *fileNameObj, int lineNumber)
{
JimPanic((Jim_IsShared(objPtr), "Jim_SetSourceInfo called with shared object"));
Jim_FreeIntRep(interp, objPtr);
Jim_IncrRefCount(fileNameObj);
objPtr->internalRep.sourceValue.fileNameObj = fileNameObj;
objPtr->internalRep.sourceValue.lineNumber = lineNumber;
objPtr->typePtr = &sourceObjType;
}
static void SubstObjAddTokens(Jim_Interp *interp, struct ScriptObj *script,
ParseTokenList *tokenlist)
{
int i;
struct ScriptToken *token;
|
| ︙ | ︙ | |||
9176 9177 9178 9179 9180 9181 9182 |
static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
int scriptTextLen;
const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
struct JimParserCtx parser;
struct ScriptObj *script;
ParseTokenList tokenlist;
| > | | < < < < < < | < < < | < | | | > > | > | > | | 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 10017 10018 10019 10020 10021 10022 10023 10024 10025 10026 10027 10028 10029 10030 10031 10032 10033 10034 10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 |
static void JimSetScriptFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
int scriptTextLen;
const char *scriptText = Jim_GetString(objPtr, &scriptTextLen);
struct JimParserCtx parser;
struct ScriptObj *script;
ParseTokenList tokenlist;
Jim_Obj *fileNameObj;
int line;
fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
ScriptTokenListInit(&tokenlist);
JimParserInit(&parser, scriptText, scriptTextLen, line);
while (!parser.eof) {
JimParseScript(&parser);
ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
parser.tline);
}
ScriptAddToken(&tokenlist, scriptText + scriptTextLen, 0, JIM_TT_EOF, 0);
script = Jim_Alloc(sizeof(*script));
memset(script, 0, sizeof(*script));
script->inUse = 1;
script->fileNameObj = fileNameObj;
Jim_IncrRefCount(script->fileNameObj);
script->missing = parser.missing.ch;
script->linenr = parser.missing.line;
ScriptObjAddTokens(interp, script, &tokenlist);
ScriptTokenListFree(&tokenlist);
Jim_FreeIntRep(interp, objPtr);
Jim_SetIntRepPtr(objPtr, script);
objPtr->typePtr = &scriptObjType;
}
static ScriptObj *JimGetScript(Jim_Interp *interp, Jim_Obj *objPtr)
{
if (objPtr == interp->emptyObj) {
objPtr = interp->nullScriptObj;
}
if (objPtr->typePtr != &scriptObjType || ((struct ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags) {
JimSetScriptFromAny(interp, objPtr);
}
return (ScriptObj *)Jim_GetIntRepPtr(objPtr);
}
void Jim_InterpIncrProcEpoch(Jim_Interp *interp)
{
interp->procEpoch++;
while (interp->oldCmdCache) {
Jim_Cmd *next = interp->oldCmdCache->prevCmd;
Jim_Free(interp->oldCmdCache);
interp->oldCmdCache = next;
}
interp->oldCmdCacheSize = 0;
}
static void JimIncrCmdRefCount(Jim_Cmd *cmdPtr)
{
cmdPtr->inUse++;
}
static void JimDecrCmdRefCount(Jim_Interp *interp, Jim_Cmd *cmdPtr)
|
| ︙ | ︙ | |||
9274 9275 9276 9277 9278 9279 9280 |
cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
}
}
if (cmdPtr->prevCmd) {
JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
}
| > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > | | < > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < < < < < < < < < < < < < < < < < < > > > > | < | < < < < < | > > > | | < | < < < | | < | < | < < | < | | < < | | | > | | < < < < < | | | > | < | < | < | < | > | | > | > > > > > > > > | | < | | < | < < < < < < < < | | | < < < < < < | < < < < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > | > | > > | 10059 10060 10061 10062 10063 10064 10065 10066 10067 10068 10069 10070 10071 10072 10073 10074 10075 10076 10077 10078 10079 10080 10081 10082 10083 10084 10085 10086 10087 10088 10089 10090 10091 10092 10093 10094 10095 10096 10097 10098 10099 10100 10101 10102 10103 10104 10105 10106 10107 10108 10109 10110 10111 10112 10113 10114 10115 10116 10117 10118 10119 10120 10121 10122 10123 10124 10125 10126 10127 10128 10129 10130 10131 10132 10133 10134 10135 10136 10137 10138 10139 10140 10141 10142 10143 10144 10145 10146 10147 10148 10149 10150 10151 10152 10153 10154 10155 10156 10157 10158 10159 10160 10161 10162 10163 10164 10165 10166 10167 10168 10169 10170 10171 10172 10173 10174 10175 10176 10177 10178 10179 10180 10181 10182 10183 10184 10185 10186 10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 10230 10231 10232 10233 10234 10235 10236 10237 10238 10239 10240 10241 10242 10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10261 10262 10263 10264 10265 10266 10267 10268 10269 10270 10271 10272 10273 10274 10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 10312 10313 10314 10315 10316 10317 10318 10319 10320 10321 10322 10323 10324 10325 10326 10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 10343 10344 10345 10346 10347 10348 10349 10350 10351 10352 10353 10354 10355 10356 10357 10358 10359 10360 10361 10362 10363 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375 10376 10377 10378 10379 10380 10381 10382 10383 10384 10385 10386 10387 10388 10389 10390 10391 10392 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402 10403 10404 10405 10406 10407 10408 10409 10410 10411 10412 10413 10414 10415 10416 10417 10418 10419 10420 10421 10422 10423 10424 10425 10426 10427 10428 10429 10430 10431 10432 10433 10434 10435 10436 10437 10438 10439 10440 10441 10442 10443 10444 10445 10446 10447 |
cmdPtr->u.native.delProc(interp, cmdPtr->u.native.privData);
}
}
if (cmdPtr->prevCmd) {
JimDecrCmdRefCount(interp, cmdPtr->prevCmd);
}
cmdPtr->prevCmd = interp->oldCmdCache;
interp->oldCmdCache = cmdPtr;
if (!interp->quitting && ++interp->oldCmdCacheSize >= 1000) {
Jim_InterpIncrProcEpoch(interp);
}
}
}
static void JimIncrVarRef(Jim_VarVal *vv)
{
vv->refCount++;
}
static void JimDecrVarRef(Jim_Interp *interp, Jim_VarVal *vv)
{
assert(vv->refCount > 0);
if (--vv->refCount == 0) {
if (vv->objPtr) {
Jim_DecrRefCount(interp, vv->objPtr);
}
Jim_Free(vv);
}
}
static void JimVariablesHTValDestructor(void *interp, void *val)
{
JimDecrVarRef(interp, val);
}
static unsigned int JimObjectHTHashFunction(const void *key)
{
Jim_Obj *keyObj = (Jim_Obj *)key;
int length;
const char *string;
#ifdef JIM_OPTIMIZATION
if (JimIsWide(keyObj) && keyObj->bytes == NULL) {
jim_wide objValue = JimWideValue(keyObj);
if (objValue > INT_MIN && objValue < INT_MAX) {
unsigned result = 0;
unsigned value = (unsigned)objValue;
if (objValue < 0) {
value = (unsigned)-objValue;
}
do {
result += (result << 3) + (value % 10 + '0');
value /= 10;
} while (value);
if (objValue < 0) {
result += (result << 3) + '-';
}
return result;
}
}
#endif
string = Jim_GetString(keyObj, &length);
return Jim_GenHashFunction((const unsigned char *)string, length);
}
static int JimObjectHTKeyCompare(void *privdata, const void *key1, const void *key2)
{
return Jim_StringEqObj((Jim_Obj *)key1, (Jim_Obj *)key2);
}
static void *JimObjectHTKeyValDup(void *privdata, const void *val)
{
Jim_IncrRefCount((Jim_Obj *)val);
return (void *)val;
}
static void JimObjectHTKeyValDestructor(void *interp, void *val)
{
Jim_DecrRefCount(interp, (Jim_Obj *)val);
}
static void *JimVariablesHTValDup(void *privdata, const void *val)
{
JimIncrVarRef((Jim_VarVal *)val);
return (void *)val;
}
static const Jim_HashTableType JimVariablesHashTableType = {
JimObjectHTHashFunction,
JimObjectHTKeyValDup,
JimVariablesHTValDup,
JimObjectHTKeyCompare,
JimObjectHTKeyValDestructor,
JimVariablesHTValDestructor
};
static const char *Jim_GetStringNoQualifier(Jim_Obj *objPtr, int *length)
{
int len;
const char *str = Jim_GetString(objPtr, &len);
if (len >= 2 && str[0] == ':' && str[1] == ':') {
while (len && *str == ':') {
len--;
str++;
}
}
*length = len;
return str;
}
static unsigned int JimCommandsHT_HashFunction(const void *key)
{
int len;
const char *str = Jim_GetStringNoQualifier((Jim_Obj *)key, &len);
return Jim_GenHashFunction((const unsigned char *)str, len);
}
static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void *key2)
{
int len1, len2;
const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1);
const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2);
return len1 == len2 && memcmp(str1, str2, len1) == 0;
}
static void JimCommandsHT_ValDestructor(void *interp, void *val)
{
JimDecrCmdRefCount(interp, val);
}
static const Jim_HashTableType JimCommandsHashTableType = {
JimCommandsHT_HashFunction,
JimObjectHTKeyValDup,
NULL,
JimCommandsHT_KeyCompare,
JimObjectHTKeyValDestructor,
JimCommandsHT_ValDestructor
};
Jim_Obj *Jim_MakeGlobalNamespaceName(Jim_Interp *interp, Jim_Obj *nameObjPtr)
{
#ifdef jim_ext_namespace
Jim_Obj *resultObj;
const char *name = Jim_String(nameObjPtr);
if (name[0] == ':' && name[1] == ':') {
return nameObjPtr;
}
Jim_IncrRefCount(nameObjPtr);
resultObj = Jim_NewStringObj(interp, "::", -1);
Jim_AppendObj(interp, resultObj, nameObjPtr);
Jim_DecrRefCount(interp, nameObjPtr);
return resultObj;
#else
return nameObjPtr;
#endif
}
static Jim_Obj *JimQualifyName(Jim_Interp *interp, Jim_Obj *objPtr)
{
#ifdef jim_ext_namespace
if (Jim_Length(interp->framePtr->nsObj)) {
int len;
const char *name = Jim_GetString(objPtr, &len);
if (len < 2 || name[0] != ':' || name[1] != ':') {
objPtr = Jim_DuplicateObj(interp, interp->framePtr->nsObj);
Jim_AppendStrings(interp, objPtr, "::", name, NULL);
}
}
#endif
Jim_IncrRefCount(objPtr);
return objPtr;
}
static void JimCreateCommand(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Cmd *cmd)
{
JimPanic((nameObjPtr->refCount == 0, "JimCreateCommand called with zero ref count name"));
if (interp->local) {
Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, nameObjPtr);
if (he) {
cmd->prevCmd = Jim_GetHashEntryVal(he);
Jim_SetHashVal(&interp->commands, he, cmd);
Jim_InterpIncrProcEpoch(interp);
return;
}
}
Jim_ReplaceHashEntry(&interp->commands, nameObjPtr, cmd);
}
int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj,
Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
{
Jim_Cmd *cmdPtr = Jim_Alloc(sizeof(*cmdPtr));
memset(cmdPtr, 0, sizeof(*cmdPtr));
cmdPtr->inUse = 1;
cmdPtr->u.native.delProc = delProc;
cmdPtr->u.native.cmdProc = cmdProc;
cmdPtr->u.native.privData = privData;
Jim_IncrRefCount(cmdNameObj);
JimCreateCommand(interp, cmdNameObj, cmdPtr);
Jim_DecrRefCount(interp, cmdNameObj);
return JIM_OK;
}
int Jim_CreateCommand(Jim_Interp *interp, const char *cmdNameStr,
Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
{
return Jim_CreateCommandObj(interp, Jim_NewStringObj(interp, cmdNameStr, -1), cmdProc, privData, delProc);
}
static int JimCreateProcedureStatics(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *staticsListObjPtr)
{
int len, i;
len = Jim_ListLength(interp, staticsListObjPtr);
if (len == 0) {
return JIM_OK;
}
cmdPtr->u.proc.staticVars = Jim_Alloc(sizeof(Jim_HashTable));
Jim_InitHashTable(cmdPtr->u.proc.staticVars, &JimVariablesHashTableType, interp);
for (i = 0; i < len; i++) {
Jim_Obj *initObjPtr = NULL;
Jim_Obj *nameObjPtr;
Jim_VarVal *vv = NULL;
Jim_Obj *objPtr = Jim_ListGetIndex(interp, staticsListObjPtr, i);
int subLen = Jim_ListLength(interp, objPtr);
int byref = 0;
if (subLen != 1 && subLen != 2) {
Jim_SetResultFormatted(interp, "too many fields in static specifier \"%#s\"",
objPtr);
return JIM_ERR;
}
nameObjPtr = Jim_ListGetIndex(interp, objPtr, 0);
if (subLen == 1) {
int len;
const char *pt = Jim_GetString(nameObjPtr, &len);
if (*pt == '&') {
nameObjPtr = Jim_NewStringObj(interp, pt + 1, len - 1);
byref = 1;
}
}
Jim_IncrRefCount(nameObjPtr);
if (subLen == 1) {
switch (SetVariableFromAny(interp, nameObjPtr)) {
case JIM_DICT_SUGAR:
if (byref) {
Jim_SetResultFormatted(interp, "Can't link to array element \"%#s\"", nameObjPtr);
}
else {
Jim_SetResultFormatted(interp, "Can't initialise array element \"%#s\"", nameObjPtr);
}
Jim_DecrRefCount(interp, nameObjPtr);
return JIM_ERR;
case JIM_OK:
if (byref) {
vv = nameObjPtr->internalRep.varValue.vv;
}
else {
initObjPtr = Jim_GetVariable(interp, nameObjPtr, JIM_NONE);
}
break;
case JIM_ERR:
Jim_SetResultFormatted(interp,
"variable for initialization of static \"%#s\" not found in the local context",
nameObjPtr);
Jim_DecrRefCount(interp, nameObjPtr);
return JIM_ERR;
}
}
else {
initObjPtr = Jim_ListGetIndex(interp, objPtr, 1);
}
if (vv == NULL) {
vv = Jim_Alloc(sizeof(*vv));
vv->objPtr = initObjPtr;
Jim_IncrRefCount(vv->objPtr);
vv->linkFramePtr = NULL;
vv->refCount = 0;
}
if (JimSetNewVariable(cmdPtr->u.proc.staticVars, nameObjPtr, vv) != JIM_OK) {
Jim_SetResultFormatted(interp,
"static variable name \"%#s\" duplicated in statics list", nameObjPtr);
JimIncrVarRef(vv);
JimDecrVarRef(interp, vv);
Jim_DecrRefCount(interp, nameObjPtr);
return JIM_ERR;
}
Jim_DecrRefCount(interp, nameObjPtr);
}
return JIM_OK;
}
#ifdef jim_ext_namespace
static const char *Jim_memrchr(const char *p, int c, int len)
{
int i;
for (i = len; i > 0; i--) {
if (p[i] == c) {
return p + i;
}
}
return NULL;
}
#endif
static void JimUpdateProcNamespace(Jim_Interp *interp, Jim_Cmd *cmdPtr, Jim_Obj *nameObjPtr)
{
#ifdef jim_ext_namespace
if (cmdPtr->isproc) {
int len;
const char *cmdname = Jim_GetStringNoQualifier(nameObjPtr, &len);
const char *pt = Jim_memrchr(cmdname, ':', len);
if (pt && pt != cmdname && pt[-1] == ':') {
pt++;
Jim_DecrRefCount(interp, cmdPtr->u.proc.nsObj);
cmdPtr->u.proc.nsObj = Jim_NewStringObj(interp, cmdname, pt - cmdname - 2);
Jim_IncrRefCount(cmdPtr->u.proc.nsObj);
Jim_Obj *tempObj = Jim_NewStringObj(interp, pt, len - (pt - cmdname));
if (Jim_FindHashEntry(&interp->commands, tempObj)) {
Jim_InterpIncrProcEpoch(interp);
}
Jim_FreeNewObj(interp, tempObj);
}
}
#endif
}
static Jim_Cmd *JimCreateProcedureCmd(Jim_Interp *interp, Jim_Obj *argListObjPtr,
Jim_Obj *staticsListObjPtr, Jim_Obj *bodyObjPtr, Jim_Obj *nsObj)
{
Jim_Cmd *cmdPtr;
int argListLen;
int i;
argListLen = Jim_ListLength(interp, argListObjPtr);
cmdPtr = Jim_Alloc(sizeof(*cmdPtr) + sizeof(struct Jim_ProcArg) * argListLen);
assert(cmdPtr);
memset(cmdPtr, 0, sizeof(*cmdPtr));
cmdPtr->inUse = 1;
cmdPtr->isproc = 1;
cmdPtr->u.proc.argListObjPtr = argListObjPtr;
cmdPtr->u.proc.argListLen = argListLen;
cmdPtr->u.proc.bodyObjPtr = bodyObjPtr;
cmdPtr->u.proc.argsPos = -1;
|
| ︙ | ︙ | |||
9583 9584 9585 9586 9587 9588 9589 |
cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
}
return cmdPtr;
}
| | | | | | < < < | < | < < < < | | > > | | | | | | < > > > > > | | | | | | | | | | > | 10509 10510 10511 10512 10513 10514 10515 10516 10517 10518 10519 10520 10521 10522 10523 10524 10525 10526 10527 10528 10529 10530 10531 10532 10533 10534 10535 10536 10537 10538 10539 10540 10541 10542 10543 10544 10545 10546 10547 10548 10549 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 10565 10566 10567 10568 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 10582 10583 |
cmdPtr->u.proc.arglist[i].nameObjPtr = nameObjPtr;
cmdPtr->u.proc.arglist[i].defaultObjPtr = defaultObjPtr;
}
return cmdPtr;
}
int Jim_DeleteCommand(Jim_Interp *interp, Jim_Obj *nameObj)
{
int ret = JIM_OK;
nameObj = JimQualifyName(interp, nameObj);
if (Jim_DeleteHashEntry(&interp->commands, nameObj) == JIM_ERR) {
Jim_SetResultFormatted(interp, "can't delete \"%#s\": command doesn't exist", nameObj);
ret = JIM_ERR;
}
Jim_DecrRefCount(interp, nameObj);
return ret;
}
int Jim_RenameCommand(Jim_Interp *interp, Jim_Obj *oldNameObj, Jim_Obj *newNameObj)
{
int ret = JIM_ERR;
Jim_HashEntry *he;
Jim_Cmd *cmdPtr;
if (Jim_Length(newNameObj) == 0) {
return Jim_DeleteCommand(interp, oldNameObj);
}
oldNameObj = JimQualifyName(interp, oldNameObj);
newNameObj = JimQualifyName(interp, newNameObj);
he = Jim_FindHashEntry(&interp->commands, oldNameObj);
if (he == NULL) {
Jim_SetResultFormatted(interp, "can't rename \"%#s\": command doesn't exist", oldNameObj);
}
else if (Jim_FindHashEntry(&interp->commands, newNameObj)) {
Jim_SetResultFormatted(interp, "can't rename to \"%#s\": command already exists", newNameObj);
}
else {
cmdPtr = Jim_GetHashEntryVal(he);
if (cmdPtr->prevCmd) {
Jim_SetResultFormatted(interp, "can't rename local command \"%#s\"", oldNameObj);
}
else {
JimIncrCmdRefCount(cmdPtr);
JimUpdateProcNamespace(interp, cmdPtr, newNameObj);
Jim_AddHashEntry(&interp->commands, newNameObj, cmdPtr);
Jim_DeleteHashEntry(&interp->commands, oldNameObj);
Jim_InterpIncrProcEpoch(interp);
ret = JIM_OK;
}
}
Jim_DecrRefCount(interp, oldNameObj);
Jim_DecrRefCount(interp, newNameObj);
return ret;
}
static void FreeCommandInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
|
| ︙ | ︙ | |||
9674 9675 9676 9677 9678 9679 9680 |
JIM_TYPE_REFERENCES,
};
Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
Jim_Cmd *cmd;
| | | | | | | > | | < < < < < | < < < | < < < | < < < < > < < < > > < < | < < < < < < < < < < < < < < < < < < < > < < < < | > > > > > > | > > > | < < < | < | | | < | > > > > > > > > > > > > > > > > > > > | > | | | > | | > > > < | | < | | < | < < < | | | | | | | 10599 10600 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 10612 10613 10614 10615 10616 10617 10618 10619 10620 10621 10622 10623 10624 10625 10626 10627 10628 10629 10630 10631 10632 10633 10634 10635 10636 10637 10638 10639 10640 10641 10642 10643 10644 10645 10646 10647 10648 10649 10650 10651 10652 10653 10654 10655 10656 10657 10658 10659 10660 10661 10662 10663 10664 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675 10676 10677 10678 10679 10680 10681 10682 10683 10684 10685 10686 10687 10688 10689 10690 10691 10692 10693 10694 10695 10696 10697 10698 10699 10700 10701 10702 10703 10704 10705 10706 10707 10708 10709 10710 10711 10712 10713 10714 10715 10716 10717 10718 10719 10720 10721 10722 10723 10724 10725 10726 10727 10728 10729 10730 10731 10732 10733 10734 10735 10736 10737 10738 10739 10740 10741 10742 10743 10744 10745 10746 10747 10748 10749 10750 10751 10752 10753 10754 10755 10756 10757 10758 10759 10760 10761 10762 10763 10764 10765 10766 10767 10768 10769 10770 10771 10772 10773 10774 10775 10776 10777 10778 10779 10780 10781 10782 10783 10784 10785 10786 10787 10788 10789 10790 10791 10792 10793 10794 10795 10796 10797 10798 10799 10800 10801 10802 10803 10804 10805 10806 10807 10808 10809 10810 10811 10812 10813 10814 10815 10816 10817 10818 10819 |
JIM_TYPE_REFERENCES,
};
Jim_Cmd *Jim_GetCommand(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
Jim_Cmd *cmd;
if (objPtr->typePtr == &commandObjType
&& objPtr->internalRep.cmdValue.procEpoch == interp->procEpoch
#ifdef jim_ext_namespace
&& Jim_StringEqObj(objPtr->internalRep.cmdValue.nsObj, interp->framePtr->nsObj)
#endif
&& objPtr->internalRep.cmdValue.cmdPtr->inUse) {
cmd = objPtr->internalRep.cmdValue.cmdPtr;
}
else {
Jim_Obj *qualifiedNameObj = JimQualifyName(interp, objPtr);
Jim_HashEntry *he = Jim_FindHashEntry(&interp->commands, qualifiedNameObj);
#ifdef jim_ext_namespace
if (he == NULL && Jim_Length(interp->framePtr->nsObj)) {
he = Jim_FindHashEntry(&interp->commands, objPtr);
}
#endif
if (he == NULL) {
if (flags & JIM_ERRMSG) {
Jim_SetResultFormatted(interp, "invalid command name \"%#s\"", objPtr);
}
Jim_DecrRefCount(interp, qualifiedNameObj);
return NULL;
}
cmd = Jim_GetHashEntryVal(he);
cmd->cmdNameObj = Jim_GetHashEntryKey(he);
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &commandObjType;
objPtr->internalRep.cmdValue.procEpoch = interp->procEpoch;
objPtr->internalRep.cmdValue.cmdPtr = cmd;
objPtr->internalRep.cmdValue.nsObj = interp->framePtr->nsObj;
Jim_IncrRefCount(interp->framePtr->nsObj);
Jim_DecrRefCount(interp, qualifiedNameObj);
}
while (cmd->u.proc.upcall) {
cmd = cmd->prevCmd;
}
return cmd;
}
static const Jim_ObjType variableObjType = {
"variable",
NULL,
NULL,
NULL,
JIM_TYPE_REFERENCES,
};
static int SetVariableFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
const char *varName;
Jim_CallFrame *framePtr;
int global;
int len;
Jim_VarVal *vv;
if (objPtr->typePtr == &variableObjType) {
framePtr = objPtr->internalRep.varValue.global ? interp->topFramePtr : interp->framePtr;
if (objPtr->internalRep.varValue.callFrameId == framePtr->id) {
return JIM_OK;
}
}
else if (objPtr->typePtr == &dictSubstObjType) {
return JIM_DICT_SUGAR;
}
varName = Jim_GetString(objPtr, &len);
if (len && varName[len - 1] == ')' && strchr(varName, '(') != NULL) {
return JIM_DICT_SUGAR;
}
if (varName[0] == ':' && varName[1] == ':') {
while (*varName == ':') {
varName++;
len--;
}
global = 1;
framePtr = interp->topFramePtr;
Jim_Obj *tempObj = Jim_NewStringObj(interp, varName, len);
vv = JimFindVariable(&framePtr->vars, tempObj);
Jim_FreeNewObj(interp, tempObj);
}
else {
global = 0;
framePtr = interp->framePtr;
vv = JimFindVariable(&framePtr->vars, objPtr);
if (vv == NULL && framePtr->staticVars) {
vv = JimFindVariable(framePtr->staticVars, objPtr);
}
}
if (vv == NULL) {
return JIM_ERR;
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &variableObjType;
objPtr->internalRep.varValue.callFrameId = framePtr->id;
objPtr->internalRep.varValue.vv = vv;
objPtr->internalRep.varValue.global = global;
return JIM_OK;
}
static int JimDictSugarSet(Jim_Interp *interp, Jim_Obj *ObjPtr, Jim_Obj *valObjPtr);
static Jim_Obj *JimDictSugarGet(Jim_Interp *interp, Jim_Obj *ObjPtr, int flags);
static int JimSetNewVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr, Jim_VarVal *vv)
{
return Jim_AddHashEntry(ht, nameObjPtr, vv);
}
static Jim_VarVal *JimFindVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
{
Jim_HashEntry *he = Jim_FindHashEntry(ht, nameObjPtr);
if (he) {
return (Jim_VarVal *)Jim_GetHashEntryVal(he);
}
return NULL;
}
static int JimUnsetVariable(Jim_HashTable *ht, Jim_Obj *nameObjPtr)
{
return Jim_DeleteHashEntry(ht, nameObjPtr);
}
static Jim_VarVal *JimCreateVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
{
const char *name;
Jim_CallFrame *framePtr;
int global;
int len;
Jim_VarVal *vv = Jim_Alloc(sizeof(*vv));
vv->objPtr = valObjPtr;
Jim_IncrRefCount(valObjPtr);
vv->linkFramePtr = NULL;
vv->refCount = 0;
name = Jim_GetString(nameObjPtr, &len);
if (name[0] == ':' && name[1] == ':') {
while (*name == ':') {
name++;
len--;
}
framePtr = interp->topFramePtr;
global = 1;
JimSetNewVariable(&framePtr->vars, Jim_NewStringObj(interp, name, len), vv);
}
else {
framePtr = interp->framePtr;
global = 0;
JimSetNewVariable(&framePtr->vars, nameObjPtr, vv);
}
Jim_FreeIntRep(interp, nameObjPtr);
nameObjPtr->typePtr = &variableObjType;
nameObjPtr->internalRep.varValue.callFrameId = framePtr->id;
nameObjPtr->internalRep.varValue.vv = vv;
nameObjPtr->internalRep.varValue.global = global;
return vv;
}
int Jim_SetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, Jim_Obj *valObjPtr)
{
int err;
Jim_VarVal *vv;
switch (SetVariableFromAny(interp, nameObjPtr)) {
case JIM_DICT_SUGAR:
return JimDictSugarSet(interp, nameObjPtr, valObjPtr);
case JIM_ERR:
JimCreateVariable(interp, nameObjPtr, valObjPtr);
break;
case JIM_OK:
vv = nameObjPtr->internalRep.varValue.vv;
if (vv->linkFramePtr == NULL) {
Jim_IncrRefCount(valObjPtr);
Jim_DecrRefCount(interp, vv->objPtr);
vv->objPtr = valObjPtr;
}
else {
Jim_CallFrame *savedCallFrame;
savedCallFrame = interp->framePtr;
interp->framePtr = vv->linkFramePtr;
err = Jim_SetVariable(interp, vv->objPtr, valObjPtr);
interp->framePtr = savedCallFrame;
if (err != JIM_OK)
return err;
}
}
return JIM_OK;
}
|
| ︙ | ︙ | |||
9947 9948 9949 9950 9951 9952 9953 |
int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
{
const char *varName;
const char *targetName;
Jim_CallFrame *framePtr;
| | > > | | | | | > > | | > > | | | | | | > > > | | | | | | 10856 10857 10858 10859 10860 10861 10862 10863 10864 10865 10866 10867 10868 10869 10870 10871 10872 10873 10874 10875 10876 10877 10878 10879 10880 10881 10882 10883 10884 10885 10886 10887 10888 10889 10890 10891 10892 10893 10894 10895 10896 10897 10898 10899 10900 10901 10902 10903 10904 10905 10906 10907 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 10950 10951 10952 10953 10954 10955 10956 10957 10958 10959 10960 10961 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 |
int Jim_SetVariableLink(Jim_Interp *interp, Jim_Obj *nameObjPtr,
Jim_Obj *targetNameObjPtr, Jim_CallFrame *targetCallFrame)
{
const char *varName;
const char *targetName;
Jim_CallFrame *framePtr;
Jim_VarVal *vv;
int len;
int varnamelen;
switch (SetVariableFromAny(interp, nameObjPtr)) {
case JIM_DICT_SUGAR:
Jim_SetResultFormatted(interp, "bad variable name \"%#s\": upvar won't create a scalar variable that looks like an array element", nameObjPtr);
return JIM_ERR;
case JIM_OK:
vv = nameObjPtr->internalRep.varValue.vv;
if (vv->linkFramePtr == NULL) {
Jim_SetResultFormatted(interp, "variable \"%#s\" already exists", nameObjPtr);
return JIM_ERR;
}
vv->linkFramePtr = NULL;
break;
}
varName = Jim_GetString(nameObjPtr, &varnamelen);
if (varName[0] == ':' && varName[1] == ':') {
while (*varName == ':') {
varName++;
varnamelen--;
}
framePtr = interp->topFramePtr;
}
else {
framePtr = interp->framePtr;
}
targetName = Jim_GetString(targetNameObjPtr, &len);
if (targetName[0] == ':' && targetName[1] == ':') {
while (*targetName == ':') {
targetName++;
len--;
}
targetNameObjPtr = Jim_NewStringObj(interp, targetName, len);
targetCallFrame = interp->topFramePtr;
}
Jim_IncrRefCount(targetNameObjPtr);
if (framePtr->level < targetCallFrame->level) {
Jim_SetResultFormatted(interp,
"bad variable name \"%#s\": upvar won't create namespace variable that refers to procedure variable",
nameObjPtr);
Jim_DecrRefCount(interp, targetNameObjPtr);
return JIM_ERR;
}
if (framePtr == targetCallFrame) {
Jim_Obj *objPtr = targetNameObjPtr;
while (1) {
if (Jim_Length(objPtr) == varnamelen && memcmp(Jim_String(objPtr), varName, varnamelen) == 0) {
Jim_SetResultString(interp, "can't upvar from variable to itself", -1);
Jim_DecrRefCount(interp, targetNameObjPtr);
return JIM_ERR;
}
if (SetVariableFromAny(interp, objPtr) != JIM_OK)
break;
vv = objPtr->internalRep.varValue.vv;
if (vv->linkFramePtr != targetCallFrame)
break;
objPtr = vv->objPtr;
}
}
Jim_SetVariable(interp, nameObjPtr, targetNameObjPtr);
nameObjPtr->internalRep.varValue.vv->linkFramePtr = targetCallFrame;
Jim_DecrRefCount(interp, targetNameObjPtr);
return JIM_OK;
}
Jim_Obj *Jim_GetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
{
if (interp->safeexpr) {
return nameObjPtr;
}
switch (SetVariableFromAny(interp, nameObjPtr)) {
case JIM_OK:{
Jim_VarVal *vv = nameObjPtr->internalRep.varValue.vv;
if (vv->linkFramePtr == NULL) {
return vv->objPtr;
}
else {
Jim_Obj *objPtr;
Jim_CallFrame *savedCallFrame = interp->framePtr;
interp->framePtr = vv->linkFramePtr;
objPtr = Jim_GetVariable(interp, vv->objPtr, flags);
interp->framePtr = savedCallFrame;
if (objPtr) {
return objPtr;
}
}
}
|
| ︙ | ︙ | |||
10103 10104 10105 10106 10107 10108 10109 |
interp->framePtr = savedFramePtr;
return objPtr;
}
int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
{
| | | | | | < > > > | > > > > > > < | 11021 11022 11023 11024 11025 11026 11027 11028 11029 11030 11031 11032 11033 11034 11035 11036 11037 11038 11039 11040 11041 11042 11043 11044 11045 11046 11047 11048 11049 11050 11051 11052 11053 11054 11055 11056 11057 11058 11059 11060 11061 11062 11063 11064 11065 11066 11067 11068 11069 11070 11071 |
interp->framePtr = savedFramePtr;
return objPtr;
}
int Jim_UnsetVariable(Jim_Interp *interp, Jim_Obj *nameObjPtr, int flags)
{
Jim_VarVal *vv;
int retval;
Jim_CallFrame *framePtr;
retval = SetVariableFromAny(interp, nameObjPtr);
if (retval == JIM_DICT_SUGAR) {
return JimDictSugarSet(interp, nameObjPtr, NULL);
}
else if (retval == JIM_OK) {
vv = nameObjPtr->internalRep.varValue.vv;
if (vv->linkFramePtr) {
framePtr = interp->framePtr;
interp->framePtr = vv->linkFramePtr;
retval = Jim_UnsetVariable(interp, vv->objPtr, JIM_NONE);
interp->framePtr = framePtr;
}
else {
if (nameObjPtr->internalRep.varValue.global) {
int len;
const char *name = Jim_GetString(nameObjPtr, &len);
while (*name == ':') {
name++;
len--;
}
framePtr = interp->topFramePtr;
Jim_Obj *tempObj = Jim_NewStringObj(interp, name, len);
retval = JimUnsetVariable(&framePtr->vars, tempObj);
Jim_FreeNewObj(interp, tempObj);
}
else {
framePtr = interp->framePtr;
retval = JimUnsetVariable(&framePtr->vars, nameObjPtr);
}
if (retval == JIM_OK) {
framePtr->id = interp->callFrameEpoch++;
}
}
}
if (retval != JIM_OK && (flags & JIM_ERRMSG)) {
|
| ︙ | ︙ | |||
10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 10306 10307 10308 10309 10310 10311 |
}
}
static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
{
Jim_Obj *resObjPtr = NULL;
Jim_Obj *substKeyObjPtr = NULL;
SetDictSubstFromAny(interp, objPtr);
if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
&substKeyObjPtr, JIM_NONE)
!= JIM_OK) {
return NULL;
}
Jim_IncrRefCount(substKeyObjPtr);
resObjPtr =
JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
substKeyObjPtr, 0);
Jim_DecrRefCount(interp, substKeyObjPtr);
return resObjPtr;
}
| > > > > < < < < < < < < | 11213 11214 11215 11216 11217 11218 11219 11220 11221 11222 11223 11224 11225 11226 11227 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 11240 11241 11242 11243 11244 11245 11246 11247 |
}
}
static Jim_Obj *JimExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr)
{
Jim_Obj *resObjPtr = NULL;
Jim_Obj *substKeyObjPtr = NULL;
if (interp->safeexpr) {
return objPtr;
}
SetDictSubstFromAny(interp, objPtr);
if (Jim_SubstObj(interp, objPtr->internalRep.dictSubstValue.indexObjPtr,
&substKeyObjPtr, JIM_NONE)
!= JIM_OK) {
return NULL;
}
Jim_IncrRefCount(substKeyObjPtr);
resObjPtr =
JimDictExpandArrayVariable(interp, objPtr->internalRep.dictSubstValue.varNameObjPtr,
substKeyObjPtr, 0);
Jim_DecrRefCount(interp, substKeyObjPtr);
return resObjPtr;
}
static Jim_CallFrame *JimCreateCallFrame(Jim_Interp *interp, Jim_CallFrame *parent, Jim_Obj *nsObj)
{
Jim_CallFrame *cf;
if (interp->freeFramesList) {
cf = interp->freeFramesList;
|
| ︙ | ︙ | |||
10355 10356 10357 10358 10359 10360 10361 |
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
{
if (localCommands) {
Jim_Obj *cmdNameObj;
while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
| < < < < < | < | < < | < | | 11276 11277 11278 11279 11280 11281 11282 11283 11284 11285 11286 11287 11288 11289 11290 11291 11292 11293 11294 11295 11296 11297 11298 11299 11300 11301 11302 11303 11304 11305 11306 11307 11308 11309 11310 11311 11312 11313 11314 11315 11316 11317 11318 11319 11320 11321 11322 11323 11324 |
static int JimDeleteLocalProcs(Jim_Interp *interp, Jim_Stack *localCommands)
{
if (localCommands) {
Jim_Obj *cmdNameObj;
while ((cmdNameObj = Jim_StackPop(localCommands)) != NULL) {
Jim_HashTable *ht = &interp->commands;
Jim_HashEntry *he = Jim_FindHashEntry(ht, cmdNameObj);
if (he) {
Jim_Cmd *cmd = Jim_GetHashEntryVal(he);
if (cmd->prevCmd) {
Jim_Cmd *prevCmd = cmd->prevCmd;
cmd->prevCmd = NULL;
JimDecrCmdRefCount(interp, cmd);
Jim_SetHashVal(ht, he, prevCmd);
}
else {
Jim_DeleteHashEntry(ht, cmdNameObj);
}
}
Jim_DecrRefCount(interp, cmdNameObj);
}
Jim_FreeStack(localCommands);
Jim_Free(localCommands);
}
return JIM_OK;
}
static int JimInvokeDefer(Jim_Interp *interp, int retcode)
{
Jim_Obj *objPtr;
if (JimFindVariable(&interp->framePtr->vars, interp->defer) == NULL) {
return retcode;
}
objPtr = Jim_GetVariable(interp, interp->defer, JIM_NONE);
if (objPtr) {
int ret = JIM_OK;
int i;
int listLen = Jim_ListLength(interp, objPtr);
Jim_Obj *resultObjPtr;
|
| ︙ | ︙ | |||
10450 10451 10452 10453 10454 10455 10456 |
Jim_DecrRefCount(interp, cf->procArgsObjPtr);
if (cf->procBodyObjPtr)
Jim_DecrRefCount(interp, cf->procBodyObjPtr);
Jim_DecrRefCount(interp, cf->nsObj);
if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
Jim_FreeHashTable(&cf->vars);
else {
| < < | < < < < < < < < < < < < < < < | 11362 11363 11364 11365 11366 11367 11368 11369 11370 11371 11372 11373 11374 11375 11376 |
Jim_DecrRefCount(interp, cf->procArgsObjPtr);
if (cf->procBodyObjPtr)
Jim_DecrRefCount(interp, cf->procBodyObjPtr);
Jim_DecrRefCount(interp, cf->nsObj);
if (action == JIM_FCF_FULL || cf->vars.size != JIM_HT_INITIAL_SIZE)
Jim_FreeHashTable(&cf->vars);
else {
Jim_ClearHashTable(&cf->vars);
}
cf->next = interp->freeFramesList;
interp->freeFramesList = cf;
}
|
| ︙ | ︙ | |||
10494 10495 10496 10497 10498 10499 10500 |
{
Jim_Interp *i = Jim_Alloc(sizeof(*i));
memset(i, 0, sizeof(*i));
i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
| | < > | > | < | > > > > > | | | > > > > > > > > | 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 11406 11407 11408 11409 11410 11411 11412 11413 11414 11415 11416 11417 11418 11419 11420 11421 11422 11423 11424 11425 11426 11427 11428 11429 11430 11431 11432 11433 11434 11435 11436 11437 11438 11439 11440 11441 11442 11443 11444 11445 11446 11447 11448 11449 11450 11451 11452 11453 11454 11455 11456 11457 11458 11459 11460 11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 11486 11487 11488 11489 11490 11491 11492 |
{
Jim_Interp *i = Jim_Alloc(sizeof(*i));
memset(i, 0, sizeof(*i));
i->maxCallFrameDepth = JIM_MAX_CALLFRAME_DEPTH;
i->maxEvalDepth = JIM_MAX_EVAL_DEPTH;
i->lastCollectTime = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
Jim_InitHashTable(&i->commands, &JimCommandsHashTableType, i);
#ifdef JIM_REFERENCES
Jim_InitHashTable(&i->references, &JimReferencesHashTableType, i);
#endif
Jim_InitHashTable(&i->assocData, &JimAssocDataHashTableType, i);
Jim_InitHashTable(&i->packages, &JimPackageHashTableType, NULL);
i->emptyObj = Jim_NewEmptyStringObj(i);
i->trueObj = Jim_NewIntObj(i, 1);
i->falseObj = Jim_NewIntObj(i, 0);
i->framePtr = i->topFramePtr = JimCreateCallFrame(i, NULL, i->emptyObj);
i->result = i->emptyObj;
i->stackTrace = Jim_NewListObj(i, NULL, 0);
i->unknown = Jim_NewStringObj(i, "unknown", -1);
i->defer = Jim_NewStringObj(i, "jim::defer", -1);
i->errorProc = i->emptyObj;
i->nullScriptObj = Jim_NewEmptyStringObj(i);
i->evalFrame = &i->topEvalFrame;
i->currentFilenameObj = Jim_NewEmptyStringObj(i);
Jim_IncrRefCount(i->emptyObj);
Jim_IncrRefCount(i->result);
Jim_IncrRefCount(i->stackTrace);
Jim_IncrRefCount(i->unknown);
Jim_IncrRefCount(i->defer);
Jim_IncrRefCount(i->nullScriptObj);
Jim_IncrRefCount(i->errorProc);
Jim_IncrRefCount(i->trueObj);
Jim_IncrRefCount(i->falseObj);
Jim_IncrRefCount(i->currentFilenameObj);
Jim_SetVariableStrWithStr(i, JIM_LIBPATH, TCL_LIBRARY);
Jim_SetVariableStrWithStr(i, JIM_INTERACTIVE, "0");
Jim_SetVariableStrWithStr(i, "tcl_platform(engine)", "Jim");
Jim_SetVariableStrWithStr(i, "tcl_platform(os)", TCL_PLATFORM_OS);
Jim_SetVariableStrWithStr(i, "tcl_platform(platform)", TCL_PLATFORM_PLATFORM);
Jim_SetVariableStrWithStr(i, "tcl_platform(pathSeparator)", TCL_PLATFORM_PATH_SEPARATOR);
Jim_SetVariableStrWithStr(i, "tcl_platform(byteOrder)", Jim_IsBigEndian() ? "bigEndian" : "littleEndian");
Jim_SetVariableStrWithStr(i, "tcl_platform(threaded)", "0");
Jim_SetVariableStrWithStr(i, "tcl_platform(bootstrap)", "0");
Jim_SetVariableStr(i, "tcl_platform(pointerSize)", Jim_NewIntObj(i, sizeof(void *)));
Jim_SetVariableStr(i, "tcl_platform(wordSize)", Jim_NewIntObj(i, sizeof(jim_wide)));
Jim_SetVariableStr(i, "tcl_platform(stackFormat)", Jim_NewIntObj(i, 4));
return i;
}
void Jim_FreeInterp(Jim_Interp *i)
{
Jim_CallFrame *cf, *cfx;
Jim_Obj *objPtr, *nextObjPtr;
i->quitting = 1;
for (cf = i->framePtr; cf; cf = cfx) {
JimInvokeDefer(i, JIM_OK);
cfx = cf->parent;
JimFreeCallFrame(i, cf, JIM_FCF_FULL);
}
Jim_DecrRefCount(i, i->emptyObj);
Jim_DecrRefCount(i, i->trueObj);
Jim_DecrRefCount(i, i->falseObj);
Jim_DecrRefCount(i, i->result);
Jim_DecrRefCount(i, i->stackTrace);
Jim_DecrRefCount(i, i->errorProc);
Jim_DecrRefCount(i, i->unknown);
Jim_DecrRefCount(i, i->defer);
Jim_DecrRefCount(i, i->nullScriptObj);
Jim_DecrRefCount(i, i->currentFilenameObj);
Jim_FreeHashTable(&i->commands);
Jim_InterpIncrProcEpoch(i);
#ifdef JIM_REFERENCES
Jim_FreeHashTable(&i->references);
#endif
Jim_FreeHashTable(&i->packages);
Jim_Free(i->prngState);
Jim_FreeHashTable(&i->assocData);
if (i->traceCmdObj) {
Jim_DecrRefCount(i, i->traceCmdObj);
}
#ifdef JIM_MAINTAINER
if (i->liveList != NULL) {
objPtr = i->liveList;
printf("\n-------------------------------------\n");
printf("Objects still in the free list:\n");
|
| ︙ | ︙ | |||
10669 10670 10671 10672 10673 10674 10675 |
}
}
Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
return NULL;
}
| | < < | < | | | | > | > > | | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > | > > > > > | > > > > < < | < < < < | < < | < < < < < < | < < | < < | < | | | | | | | | < < | | | | < < < > < < < < | 11577 11578 11579 11580 11581 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 11594 11595 11596 11597 11598 11599 11600 11601 11602 11603 11604 11605 11606 11607 11608 11609 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 11623 11624 11625 11626 11627 11628 11629 11630 11631 11632 11633 11634 11635 11636 11637 11638 11639 11640 11641 11642 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 11654 11655 11656 11657 11658 11659 11660 11661 11662 11663 11664 11665 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 11681 11682 11683 11684 11685 11686 11687 11688 11689 11690 11691 11692 11693 11694 11695 11696 11697 11698 11699 11700 11701 11702 11703 |
}
}
Jim_SetResultFormatted(interp, "bad level \"%s\"", str);
return NULL;
}
static Jim_CallFrame *JimGetCallFrameByInteger(Jim_Interp *interp, long level)
{
Jim_CallFrame *framePtr;
if (level == 0) {
return interp->framePtr;
}
if (level < 0) {
level = interp->framePtr->level + level;
}
if (level > 0) {
for (framePtr = interp->framePtr; framePtr; framePtr = framePtr->parent) {
if (framePtr->level == level) {
return framePtr;
}
}
}
return NULL;
}
static Jim_EvalFrame *JimGetEvalFrameByProcLevel(Jim_Interp *interp, int proclevel)
{
Jim_EvalFrame *evalFrame;
if (proclevel == 0) {
return interp->evalFrame;
}
if (proclevel < 0) {
proclevel = interp->procLevel + proclevel;
}
if (proclevel >= 0) {
for (evalFrame = interp->evalFrame; evalFrame; evalFrame = evalFrame->parent) {
if (evalFrame->procLevel == proclevel) {
return evalFrame;
}
}
}
return NULL;
}
static Jim_Obj *JimProcForEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame)
{
if (frame == interp->evalFrame || (frame->cmd && frame->cmd->cmdNameObj)) {
Jim_EvalFrame *e;
for (e = frame->parent; e; e = e->parent) {
if (e->cmd && e->cmd->isproc && e->cmd->cmdNameObj) {
break;
}
}
if (e && e->cmd && e->cmd->cmdNameObj) {
return e->cmd->cmdNameObj;
}
}
return NULL;
}
static void JimAddStackFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *listObj)
{
Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
Jim_Obj *fileNameObj = interp->emptyObj;
int linenr = 1;
if (frame->scriptObj) {
ScriptObj *script = JimGetScript(interp, frame->scriptObj);
fileNameObj = script->fileNameObj;
linenr = script->linenr;
}
Jim_ListAppendElement(interp, listObj, procNameObj ? procNameObj : interp->emptyObj);
Jim_ListAppendElement(interp, listObj, fileNameObj);
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, linenr));
Jim_ListAppendElement(interp, listObj, Jim_NewListObj(interp, frame->argv, frame->argc));
}
static void JimSetStackTrace(Jim_Interp *interp, Jim_Obj *stackTraceObj)
{
Jim_IncrRefCount(stackTraceObj);
Jim_DecrRefCount(interp, interp->stackTrace);
interp->stackTrace = stackTraceObj;
interp->errorFlag = 1;
}
static void JimSetErrorStack(Jim_Interp *interp, ScriptObj *script)
{
if (!interp->errorFlag) {
int i;
Jim_Obj *stackTrace = Jim_NewListObj(interp, NULL, 0);
if (interp->procLevel == 0 && script) {
Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
Jim_ListAppendElement(interp, stackTrace, script->fileNameObj);
Jim_ListAppendElement(interp, stackTrace, Jim_NewIntObj(interp, script->linenr));
Jim_ListAppendElement(interp, stackTrace, interp->emptyObj);
}
else {
for (i = 0; i <= interp->procLevel; i++) {
Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
if (frame) {
JimAddStackFrame(interp, frame, stackTrace);
}
}
}
JimSetStackTrace(interp, stackTrace);
}
}
int Jim_SetAssocData(Jim_Interp *interp, const char *key, Jim_InterpDeleteProc * delProc,
void *data)
{
AssocDataValue *assocEntryPtr = (AssocDataValue *) Jim_Alloc(sizeof(AssocDataValue));
|
| ︙ | ︙ | |||
10895 10896 10897 10898 10899 10900 10901 10902 10903 10904 10905 10906 10907 10908 |
{
if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
return JIM_ERR;
*widePtr = JimWideValue(objPtr);
return JIM_OK;
}
static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
{
if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
return JIM_ERR;
*widePtr = JimWideValue(objPtr);
return JIM_OK;
| > > > > > > > > > > > > > > > > > > > > > > > > > > | 11824 11825 11826 11827 11828 11829 11830 11831 11832 11833 11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 11844 11845 11846 11847 11848 11849 11850 11851 11852 11853 11854 11855 11856 11857 11858 11859 11860 11861 11862 11863 |
{
if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
return JIM_ERR;
*widePtr = JimWideValue(objPtr);
return JIM_OK;
}
int Jim_GetWideExpr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
{
int ret = JIM_OK;
if (objPtr->typePtr == &sourceObjType || objPtr->typePtr == NULL) {
SetIntFromAny(interp, objPtr, 0);
}
if (objPtr->typePtr == &intObjType) {
*widePtr = JimWideValue(objPtr);
}
else {
JimPanic((interp->safeexpr, "interp->safeexpr is set"));
interp->safeexpr++;
ret = Jim_EvalExpression(interp, objPtr);
interp->safeexpr--;
if (ret == JIM_OK) {
ret = Jim_GetWide(interp, Jim_GetResult(interp), widePtr);
}
if (ret != JIM_OK) {
Jim_SetResultFormatted(interp, "expected integer expression but got \"%#s\"", objPtr);
}
}
return ret;
}
static int JimGetWideNoErr(Jim_Interp *interp, Jim_Obj *objPtr, jim_wide * widePtr)
{
if (objPtr->typePtr != &intObjType && SetIntFromAny(interp, objPtr, JIM_NONE) == JIM_ERR)
return JIM_ERR;
*widePtr = JimWideValue(objPtr);
return JIM_OK;
|
| ︙ | ︙ | |||
10941 10942 10943 10944 10945 10946 10947 |
"double",
NULL,
NULL,
UpdateStringOfDouble,
JIM_TYPE_NONE,
};
| | | | 11896 11897 11898 11899 11900 11901 11902 11903 11904 11905 11906 11907 11908 11909 11910 11911 11912 11913 11914 |
"double",
NULL,
NULL,
UpdateStringOfDouble,
JIM_TYPE_NONE,
};
#if !HAVE_DECL_ISNAN
#undef isnan
#define isnan(X) ((X) != (X))
#endif
#if !HAVE_DECL_ISINF
#undef isinf
#define isinf(X) (1.0 / (X) == 0.0)
#endif
static void UpdateStringOfDouble(struct Jim_Obj *objPtr)
{
double value = objPtr->internalRep.doubleValue;
|
| ︙ | ︙ | |||
11076 11077 11078 11079 11080 11081 11082 11083 |
int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
{
if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
return JIM_ERR;
*booleanPtr = (int) JimWideValue(objPtr);
return JIM_OK;
}
| < < | > | | | | > > | | | | < > | < < < > | | 12031 12032 12033 12034 12035 12036 12037 12038 12039 12040 12041 12042 12043 12044 12045 12046 12047 12048 12049 12050 12051 12052 12053 12054 12055 12056 12057 12058 12059 12060 12061 12062 12063 12064 12065 12066 12067 12068 12069 12070 12071 |
int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr, int * booleanPtr)
{
if (objPtr->typePtr != &intObjType && SetBooleanFromAny(interp, objPtr, JIM_ERRMSG) == JIM_ERR)
return JIM_ERR;
*booleanPtr = (int) JimWideValue(objPtr);
return JIM_OK;
}
static const char * const jim_true_false_strings[8] = {
"1", "true", "yes", "on",
"0", "false", "no", "off"
};
static const int jim_true_false_lens[8] = {
1, 4, 3, 2,
1, 5, 2, 3,
};
static int SetBooleanFromAny(Jim_Interp *interp, Jim_Obj *objPtr, int flags)
{
int index = Jim_FindByName(Jim_String(objPtr), jim_true_false_strings,
sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings));
if (index < 0) {
if (flags & JIM_ERRMSG) {
Jim_SetResultFormatted(interp, "expected boolean but got \"%#s\"", objPtr);
}
return JIM_ERR;
}
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &intObjType;
objPtr->internalRep.wideValue = index < 4 ? 1 : 0;
return JIM_OK;
}
static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec);
static void ListAppendElement(Jim_Obj *listPtr, Jim_Obj *objPtr);
static void FreeListInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupListInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
|
| ︙ | ︙ | |||
11402 11403 11404 11405 11406 11407 11408 11409 11410 |
int strLen;
Jim_Obj *fileNameObj;
int linenr;
if (objPtr->typePtr == &listObjType) {
return JIM_OK;
}
if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
| > < < < | < < < | < < | | | > > > > > < < < < < | < < | 12356 12357 12358 12359 12360 12361 12362 12363 12364 12365 12366 12367 12368 12369 12370 12371 12372 12373 12374 12375 12376 12377 12378 12379 12380 12381 12382 12383 12384 12385 12386 12387 12388 12389 12390 |
int strLen;
Jim_Obj *fileNameObj;
int linenr;
if (objPtr->typePtr == &listObjType) {
return JIM_OK;
}
if (Jim_IsDict(objPtr) && objPtr->bytes == NULL) {
Jim_Dict *dict = objPtr->internalRep.dictValue;
objPtr->typePtr = &listObjType;
objPtr->internalRep.listValue.len = dict->len;
objPtr->internalRep.listValue.maxLen = dict->maxLen;
objPtr->internalRep.listValue.ele = dict->table;
Jim_Free(dict->ht);
Jim_Free(dict);
return JIM_OK;
}
fileNameObj = Jim_GetSourceInfo(interp, objPtr, &linenr);
Jim_IncrRefCount(fileNameObj);
str = Jim_GetString(objPtr, &strLen);
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &listObjType;
|
| ︙ | ︙ | |||
11453 11454 11455 11456 11457 11458 11459 |
while (!parser.eof) {
Jim_Obj *elementPtr;
JimParseList(&parser);
if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
continue;
elementPtr = JimParserGetTokenObj(interp, &parser);
| | | 12398 12399 12400 12401 12402 12403 12404 12405 12406 12407 12408 12409 12410 12411 12412 |
while (!parser.eof) {
Jim_Obj *elementPtr;
JimParseList(&parser);
if (parser.tt != JIM_TT_STR && parser.tt != JIM_TT_ESC)
continue;
elementPtr = JimParserGetTokenObj(interp, &parser);
Jim_SetSourceInfo(interp, elementPtr, fileNameObj, parser.tline);
ListAppendElement(objPtr, elementPtr);
}
}
Jim_DecrRefCount(interp, fileNameObj);
return JIM_OK;
}
|
| ︙ | ︙ | |||
11508 11509 11510 11511 11512 11513 11514 |
Jim_Obj *command;
Jim_Interp *interp;
enum {
JIM_LSORT_ASCII,
JIM_LSORT_NOCASE,
JIM_LSORT_INTEGER,
JIM_LSORT_REAL,
| | > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 12453 12454 12455 12456 12457 12458 12459 12460 12461 12462 12463 12464 12465 12466 12467 12468 12469 12470 12471 12472 12473 12474 12475 12476 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 12489 12490 12491 12492 12493 12494 12495 12496 12497 12498 12499 12500 12501 12502 12503 12504 12505 12506 12507 12508 12509 12510 12511 12512 12513 12514 12515 12516 12517 12518 12519 12520 12521 12522 12523 12524 12525 12526 12527 12528 12529 12530 12531 12532 12533 12534 12535 12536 |
Jim_Obj *command;
Jim_Interp *interp;
enum {
JIM_LSORT_ASCII,
JIM_LSORT_NOCASE,
JIM_LSORT_INTEGER,
JIM_LSORT_REAL,
JIM_LSORT_COMMAND,
JIM_LSORT_DICT
} type;
int order;
Jim_Obj **indexv;
int indexc;
int unique;
int (*subfn)(Jim_Obj **, Jim_Obj **);
};
static struct lsort_info *sort_info;
static int ListSortIndexHelper(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
Jim_Obj *lObj, *rObj;
if (Jim_ListIndices(sort_info->interp, *lhsObj, sort_info->indexv, sort_info->indexc, &lObj, JIM_ERRMSG) != JIM_OK ||
Jim_ListIndices(sort_info->interp, *rhsObj, sort_info->indexv, sort_info->indexc, &rObj, JIM_ERRMSG) != JIM_OK) {
longjmp(sort_info->jmpbuf, JIM_ERR);
}
return sort_info->subfn(&lObj, &rObj);
}
static int ListSortString(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
}
static int ListSortStringNoCase(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 1) * sort_info->order;
}
static int ListSortDict(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
const char *left = Jim_String(*lhsObj);
const char *right = Jim_String(*rhsObj);
while (1) {
if (isdigit(UCHAR(*left)) && isdigit(UCHAR(*right))) {
jim_wide lint, rint;
char *lend, *rend;
lint = jim_strtoull(left, &lend);
rint = jim_strtoull(right, &rend);
if (lint != rint) {
return JimSign(lint - rint) * sort_info->order;
}
if (lend -left != rend - right) {
return JimSign((lend - left) - (rend - right)) * sort_info->order;
}
left = lend;
right = rend;
}
else {
int cl, cr;
left += utf8_tounicode_case(left, &cl, 1);
right += utf8_tounicode_case(right, &cr, 1);
if (cl != cr) {
return JimSign(cl - cr) * sort_info->order;
}
if (cl == 0) {
return Jim_StringCompareObj(sort_info->interp, *lhsObj, *rhsObj, 0) * sort_info->order;
}
}
}
}
static int ListSortInteger(Jim_Obj **lhsObj, Jim_Obj **rhsObj)
{
jim_wide lhs = 0, rhs = 0;
if (Jim_GetWide(sort_info->interp, *lhsObj, &lhs) != JIM_OK ||
Jim_GetWide(sort_info->interp, *rhsObj, &rhs) != JIM_OK) {
|
| ︙ | ︙ | |||
11655 11656 11657 11658 11659 11660 11661 11662 11663 11664 11665 11666 11667 |
break;
case JIM_LSORT_REAL:
fn = ListSortReal;
break;
case JIM_LSORT_COMMAND:
fn = ListSortCommand;
break;
default:
fn = NULL;
JimPanic((1, "ListSort called with invalid sort type"));
return -1;
}
| > > > | > > > > > > > > > > > > > > > > < | | | < < | > > < | | | | 12638 12639 12640 12641 12642 12643 12644 12645 12646 12647 12648 12649 12650 12651 12652 12653 12654 12655 12656 12657 12658 12659 12660 12661 12662 12663 12664 12665 12666 12667 12668 12669 12670 12671 12672 12673 12674 12675 12676 12677 12678 12679 12680 12681 12682 12683 12684 12685 12686 12687 12688 12689 12690 12691 12692 12693 12694 12695 12696 12697 12698 12699 12700 12701 12702 12703 12704 12705 12706 12707 12708 12709 12710 12711 12712 12713 12714 |
break;
case JIM_LSORT_REAL:
fn = ListSortReal;
break;
case JIM_LSORT_COMMAND:
fn = ListSortCommand;
break;
case JIM_LSORT_DICT:
fn = ListSortDict;
break;
default:
fn = NULL;
JimPanic((1, "ListSort called with invalid sort type"));
return -1;
}
if (info->indexc) {
info->subfn = fn;
fn = ListSortIndexHelper;
}
if ((rc = setjmp(info->jmpbuf)) == 0) {
qsort(vector, len, sizeof(Jim_Obj *), (qsort_comparator *) fn);
if (info->unique && len > 1) {
ListRemoveDuplicates(listObjPtr, fn);
}
Jim_InvalidateStringRep(listObjPtr);
}
sort_info = prev_info;
return rc;
}
static void ListEnsureLength(Jim_Obj *listPtr, int idx)
{
assert(idx >= 0);
if (idx >= listPtr->internalRep.listValue.maxLen) {
if (idx < 4) {
idx = 4;
}
listPtr->internalRep.listValue.ele = Jim_Realloc(listPtr->internalRep.listValue.ele,
sizeof(Jim_Obj *) * idx);
listPtr->internalRep.listValue.maxLen = idx;
}
}
static void ListInsertElements(Jim_Obj *listPtr, int idx, int elemc, Jim_Obj *const *elemVec)
{
int currentLen = listPtr->internalRep.listValue.len;
int requiredLen = currentLen + elemc;
int i;
Jim_Obj **point;
if (elemc == 0) {
return;
}
if (requiredLen > listPtr->internalRep.listValue.maxLen) {
if (currentLen) {
requiredLen *= 2;
}
ListEnsureLength(listPtr, requiredLen);
}
if (idx < 0) {
idx = currentLen;
}
point = listPtr->internalRep.listValue.ele + idx;
memmove(point + elemc, point, (currentLen - idx) * sizeof(Jim_Obj *));
for (i = 0; i < elemc; ++i) {
|
| ︙ | ︙ | |||
11784 11785 11786 11787 11788 11789 11790 11791 11792 11793 11794 11795 11796 11797 |
if (flags & JIM_ERRMSG) {
Jim_SetResultString(interp, "list index out of range", -1);
}
return JIM_ERR;
}
return JIM_OK;
}
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
Jim_Obj *newObjPtr, int flags)
{
SetListFromAny(interp, listPtr);
if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
(idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 12784 12785 12786 12787 12788 12789 12790 12791 12792 12793 12794 12795 12796 12797 12798 12799 12800 12801 12802 12803 12804 12805 12806 12807 12808 12809 12810 12811 12812 12813 12814 12815 12816 12817 12818 12819 12820 12821 12822 12823 12824 12825 12826 12827 12828 12829 12830 12831 12832 12833 12834 12835 12836 12837 12838 |
if (flags & JIM_ERRMSG) {
Jim_SetResultString(interp, "list index out of range", -1);
}
return JIM_ERR;
}
return JIM_OK;
}
static int Jim_ListIndices(Jim_Interp *interp, Jim_Obj *listPtr,
Jim_Obj *const *indexv, int indexc, Jim_Obj **resultObj, int flags)
{
int i;
int static_idxes[5];
int *idxes = static_idxes;
int ret = JIM_OK;
if (indexc > sizeof(static_idxes) / sizeof(*static_idxes)) {
idxes = Jim_Alloc(indexc * sizeof(*idxes));
}
for (i = 0; i < indexc; i++) {
ret = Jim_GetIndex(interp, indexv[i], &idxes[i]);
if (ret != JIM_OK) {
goto err;
}
}
for (i = 0; i < indexc; i++) {
Jim_Obj *objPtr = Jim_ListGetIndex(interp, listPtr, idxes[i]);
if (!objPtr) {
if (flags & JIM_ERRMSG) {
if (idxes[i] < 0 || idxes[i] > Jim_ListLength(interp, listPtr)) {
Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
}
else {
Jim_SetResultFormatted(interp, "element %#s missing from sublist \"%#s\"", indexv[i], listPtr);
}
}
return -1;
}
listPtr = objPtr;
}
*resultObj = listPtr;
err:
if (idxes != static_idxes)
Jim_Free(idxes);
return ret;
}
static int ListSetIndex(Jim_Interp *interp, Jim_Obj *listPtr, int idx,
Jim_Obj *newObjPtr, int flags)
{
SetListFromAny(interp, listPtr);
if ((idx >= 0 && idx >= listPtr->internalRep.listValue.len) ||
(idx < 0 && (-idx - 1) >= listPtr->internalRep.listValue.len)) {
|
| ︙ | ︙ | |||
11819 11820 11821 11822 11823 11824 11825 |
return JIM_ERR;
if ((shared = Jim_IsShared(objPtr)))
varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
for (i = 0; i < indexc - 1; i++) {
listObjPtr = objPtr;
if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
goto err;
| > | > > | 12860 12861 12862 12863 12864 12865 12866 12867 12868 12869 12870 12871 12872 12873 12874 12875 12876 12877 |
return JIM_ERR;
if ((shared = Jim_IsShared(objPtr)))
varObjPtr = objPtr = Jim_DuplicateObj(interp, objPtr);
for (i = 0; i < indexc - 1; i++) {
listObjPtr = objPtr;
if (Jim_GetIndex(interp, indexv[i], &idx) != JIM_OK)
goto err;
objPtr = Jim_ListGetIndex(interp, listObjPtr, idx);
if (objPtr == NULL) {
Jim_SetResultFormatted(interp, "index \"%#s\" out of range", indexv[i]);
goto err;
}
if (Jim_IsShared(objPtr)) {
objPtr = Jim_DuplicateObj(interp, objPtr);
ListSetIndex(interp, listObjPtr, idx, objPtr, JIM_NONE);
}
Jim_InvalidateStringRep(listObjPtr);
|
| ︙ | ︙ | |||
11946 11947 11948 11949 11950 11951 11952 | static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr); static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr); static void UpdateStringOfDict(struct Jim_Obj *objPtr); static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr); | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > | > > | > > > | > > > | > > > > > > | > | > | > > > | | < > | | > > | > > > > > | > > | > > | | > > | > > > > > > > > > > > > > | > > > > > > | > > > > | > > > > > > > > > > > > | > > > > | > > > | > < < | < | | | < | | | > | | > | | | < > < | | > > | > > | | > > | > | > > > > | > > > > > | | | > > > | > > > > > > > > > > > | | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | > | 12990 12991 12992 12993 12994 12995 12996 12997 12998 12999 13000 13001 13002 13003 13004 13005 13006 13007 13008 13009 13010 13011 13012 13013 13014 13015 13016 13017 13018 13019 13020 13021 13022 13023 13024 13025 13026 13027 13028 13029 13030 13031 13032 13033 13034 13035 13036 13037 13038 13039 13040 13041 13042 13043 13044 13045 13046 13047 13048 13049 13050 13051 13052 13053 13054 13055 13056 13057 13058 13059 13060 13061 13062 13063 13064 13065 13066 13067 13068 13069 13070 13071 13072 13073 13074 13075 13076 13077 13078 13079 13080 13081 13082 13083 13084 13085 13086 13087 13088 13089 13090 13091 13092 13093 13094 13095 13096 13097 13098 13099 13100 13101 13102 13103 13104 13105 13106 13107 13108 13109 13110 13111 13112 13113 13114 13115 13116 13117 13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 13155 13156 13157 13158 13159 13160 13161 13162 13163 13164 13165 13166 13167 13168 13169 13170 13171 13172 13173 13174 13175 13176 13177 13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 13227 13228 13229 13230 13231 13232 13233 13234 13235 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 13248 13249 13250 13251 13252 13253 13254 13255 13256 13257 13258 13259 13260 13261 13262 13263 13264 13265 13266 13267 13268 13269 13270 13271 13272 13273 13274 13275 13276 13277 13278 13279 13280 13281 13282 13283 13284 13285 13286 13287 13288 13289 13290 13291 13292 |
static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static void UpdateStringOfDict(struct Jim_Obj *objPtr);
static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
static const Jim_ObjType dictObjType = {
"dict",
FreeDictInternalRep,
DupDictInternalRep,
UpdateStringOfDict,
JIM_TYPE_NONE,
};
static void JimFreeDict(Jim_Interp *interp, Jim_Dict *dict)
{
int i;
for (i = 0; i < dict->len; i++) {
Jim_DecrRefCount(interp, dict->table[i]);
}
Jim_Free(dict->table);
Jim_Free(dict->ht);
Jim_Free(dict);
}
enum {
DICT_HASH_FIND = -1,
DICT_HASH_REMOVE = -2,
DICT_HASH_ADD = -3,
};
static int JimDictHashFind(Jim_Dict *dict, Jim_Obj *keyObjPtr, int op_tvoffset)
{
unsigned h = (JimObjectHTHashFunction(keyObjPtr) + dict->uniq);
unsigned idx = h & dict->sizemask;
int tvoffset = 0;
unsigned peturb = h;
unsigned first_removed = ~0;
if (dict->len) {
while ((tvoffset = dict->ht[idx].offset)) {
if (tvoffset == -1) {
if (first_removed == ~0) {
first_removed = idx;
}
}
else if (dict->ht[idx].hash == h) {
if (Jim_StringEqObj(keyObjPtr, dict->table[tvoffset - 1])) {
break;
}
}
peturb >>= 5;
idx = (5 * idx + 1 + peturb) & dict->sizemask;
}
}
switch (op_tvoffset) {
case DICT_HASH_FIND:
break;
case DICT_HASH_REMOVE:
if (tvoffset) {
dict->ht[idx].offset = -1;
dict->dummy++;
}
break;
case DICT_HASH_ADD:
if (tvoffset == 0) {
if (first_removed != ~0) {
idx = first_removed;
dict->dummy--;
}
dict->ht[idx].offset = dict->len + 1;
dict->ht[idx].hash = h;
}
break;
default:
assert(tvoffset);
dict->ht[idx].offset = op_tvoffset;
break;
}
return tvoffset;
}
static void JimDictExpandHashTable(Jim_Dict *dict, unsigned int size)
{
int i;
struct JimDictHashEntry *prevht = dict->ht;
int prevsize = dict->size;
dict->size = JimHashTableNextPower(size);
dict->sizemask = dict->size - 1;
dict->ht = Jim_Alloc(dict->size * sizeof(*dict->ht));
memset(dict->ht, 0, dict->size * sizeof(*dict->ht));
for (i = 0; i < prevsize; i++) {
if (prevht[i].offset > 0) {
unsigned h = prevht[i].hash;
unsigned idx = h & dict->sizemask;
unsigned peturb = h;
while (dict->ht[idx].offset) {
peturb >>= 5;
idx = (5 * idx + 1 + peturb) & dict->sizemask;
}
dict->ht[idx].offset = prevht[i].offset;
dict->ht[idx].hash = h;
}
}
Jim_Free(prevht);
}
static int JimDictAdd(Jim_Dict *dict, Jim_Obj *keyObjPtr)
{
if (dict->size <= dict->len + dict->dummy) {
JimDictExpandHashTable(dict, dict->size ? dict->size * 2 : 8);
}
return JimDictHashFind(dict, keyObjPtr, DICT_HASH_ADD);
}
static Jim_Dict *JimDictNew(Jim_Interp *interp, int table_size, int ht_size)
{
Jim_Dict *dict = Jim_Alloc(sizeof(*dict));
memset(dict, 0, sizeof(*dict));
if (ht_size) {
JimDictExpandHashTable(dict, ht_size);
}
if (table_size) {
dict->table = Jim_Alloc(table_size * sizeof(*dict->table));
dict->maxLen = table_size;
}
#ifdef JIM_RANDOMISE_HASH
dict->uniq = (rand() ^ time(NULL) ^ clock());
#endif
return dict;
}
static void FreeDictInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
JimFreeDict(interp, objPtr->internalRep.dictValue);
}
static void DupDictInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
Jim_Dict *oldDict = srcPtr->internalRep.dictValue;
int i;
Jim_Dict *newDict = JimDictNew(interp, oldDict->maxLen, oldDict->size);
for (i = 0; i < oldDict->len; i++) {
newDict->table[i] = oldDict->table[i];
Jim_IncrRefCount(newDict->table[i]);
}
newDict->len = oldDict->len;
newDict->uniq = oldDict->uniq;
memcpy(newDict->ht, oldDict->ht, sizeof(*oldDict->ht) * oldDict->size);
dupPtr->internalRep.dictValue = newDict;
dupPtr->typePtr = &dictObjType;
}
static void UpdateStringOfDict(struct Jim_Obj *objPtr)
{
JimMakeListStringRep(objPtr, objPtr->internalRep.dictValue->table, objPtr->internalRep.dictValue->len);
}
static int SetDictFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
int listlen;
if (objPtr->typePtr == &dictObjType) {
return JIM_OK;
}
if (Jim_IsList(objPtr) && Jim_IsShared(objPtr)) {
Jim_String(objPtr);
}
listlen = Jim_ListLength(interp, objPtr);
if (listlen % 2) {
Jim_SetResultString(interp, "missing value to go with key", -1);
return JIM_ERR;
}
else {
Jim_Dict *dict = JimDictNew(interp, 0, listlen);
int i;
dict->table = objPtr->internalRep.listValue.ele;
dict->maxLen = objPtr->internalRep.listValue.maxLen;
for (i = 0; i < listlen; i += 2) {
int tvoffset = JimDictAdd(dict, dict->table[i]);
if (tvoffset) {
Jim_DecrRefCount(interp, dict->table[tvoffset]);
dict->table[tvoffset] = dict->table[i + 1];
Jim_DecrRefCount(interp, dict->table[i]);
}
else {
if (dict->len != i) {
dict->table[dict->len++] = dict->table[i];
dict->table[dict->len++] = dict->table[i + 1];
}
else {
dict->len += 2;
}
}
}
objPtr->typePtr = &dictObjType;
objPtr->internalRep.dictValue = dict;
return JIM_OK;
}
}
static int DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
{
Jim_Dict *dict = objPtr->internalRep.dictValue;
if (valueObjPtr == NULL) {
int tvoffset = JimDictHashFind(dict, keyObjPtr, DICT_HASH_REMOVE);
if (tvoffset) {
Jim_DecrRefCount(interp, dict->table[tvoffset - 1]);
Jim_DecrRefCount(interp, dict->table[tvoffset]);
dict->len -= 2;
if (tvoffset != dict->len + 1) {
dict->table[tvoffset - 1] = dict->table[dict->len];
dict->table[tvoffset] = dict->table[dict->len + 1];
JimDictHashFind(dict, dict->table[tvoffset - 1], tvoffset);
}
return JIM_OK;
}
return JIM_ERR;
}
else {
int tvoffset = JimDictAdd(dict, keyObjPtr);
if (tvoffset) {
Jim_IncrRefCount(valueObjPtr);
Jim_DecrRefCount(interp, dict->table[tvoffset]);
dict->table[tvoffset] = valueObjPtr;
}
else {
if (dict->maxLen == dict->len) {
if (dict->maxLen < 4) {
dict->maxLen = 4;
}
else {
dict->maxLen *= 2;
}
dict->table = Jim_Realloc(dict->table, dict->maxLen * sizeof(*dict->table));
}
Jim_IncrRefCount(keyObjPtr);
Jim_IncrRefCount(valueObjPtr);
dict->table[dict->len++] = keyObjPtr;
dict->table[dict->len++] = valueObjPtr;
}
return JIM_OK;
}
}
int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr,
Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr)
{
JimPanic((Jim_IsShared(objPtr), "Jim_DictAddElement called with shared object"));
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
|
| ︙ | ︙ | |||
12126 12127 12128 12129 12130 12131 12132 |
int i;
JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
objPtr = Jim_NewObj(interp);
objPtr->typePtr = &dictObjType;
objPtr->bytes = NULL;
| | | | | | | > < | | | | | | < | > > | > | | | | > > > | | > > | | 13302 13303 13304 13305 13306 13307 13308 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 13327 13328 13329 13330 13331 13332 13333 13334 13335 13336 13337 13338 13339 13340 13341 13342 13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 13355 13356 13357 13358 13359 13360 13361 13362 |
int i;
JimPanic((len % 2, "Jim_NewDictObj() 'len' argument must be even"));
objPtr = Jim_NewObj(interp);
objPtr->typePtr = &dictObjType;
objPtr->bytes = NULL;
objPtr->internalRep.dictValue = JimDictNew(interp, len, len);
for (i = 0; i < len; i += 2)
DictAddElement(interp, objPtr, elements[i], elements[i + 1]);
return objPtr;
}
int Jim_DictKey(Jim_Interp *interp, Jim_Obj *dictPtr, Jim_Obj *keyPtr,
Jim_Obj **objPtrPtr, int flags)
{
int tvoffset;
Jim_Dict *dict;
if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
return -1;
}
dict = dictPtr->internalRep.dictValue;
tvoffset = JimDictHashFind(dict, keyPtr, DICT_HASH_FIND);
if (tvoffset == 0) {
if (flags & JIM_ERRMSG) {
Jim_SetResultFormatted(interp, "key \"%#s\" not known in dictionary", keyPtr);
}
return JIM_ERR;
}
*objPtrPtr = dict->table[tvoffset];
return JIM_OK;
}
Jim_Obj **Jim_DictPairs(Jim_Interp *interp, Jim_Obj *dictPtr, int *len)
{
if (Jim_IsList(dictPtr)) {
Jim_Obj **table;
JimListGetElements(interp, dictPtr, len, &table);
if (*len % 2 == 0) {
return table;
}
}
if (SetDictFromAny(interp, dictPtr) != JIM_OK) {
*len = 1;
return NULL;
}
*len = dictPtr->internalRep.dictValue->len;
return dictPtr->internalRep.dictValue->table;
}
int Jim_DictKeysVector(Jim_Interp *interp, Jim_Obj *dictPtr,
Jim_Obj *const *keyv, int keyc, Jim_Obj **objPtrPtr, int flags)
{
int i;
|
| ︙ | ︙ | |||
12252 12253 12254 12255 12256 12257 12258 |
}
Jim_InvalidateStringRep(objPtr);
Jim_InvalidateStringRep(varObjPtr);
if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
goto err;
}
| > > | > | 13435 13436 13437 13438 13439 13440 13441 13442 13443 13444 13445 13446 13447 13448 13449 13450 13451 13452 |
}
Jim_InvalidateStringRep(objPtr);
Jim_InvalidateStringRep(varObjPtr);
if (Jim_SetVariable(interp, varNamePtr, varObjPtr) != JIM_OK) {
goto err;
}
if (!(flags & JIM_NORESULT)) {
Jim_SetResult(interp, varObjPtr);
}
return JIM_OK;
err:
if (shared) {
Jim_FreeNewObj(interp, varObjPtr);
}
return JIM_ERR;
}
|
| ︙ | ︙ | |||
12279 12280 12281 12282 12283 12284 12285 |
static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
{
if (objPtr->internalRep.intValue == -1) {
JimSetStringBytes(objPtr, "end");
}
else {
char buf[JIM_INTEGER_SPACE + 1];
| | > | > | > > > > > | | > | > < > | < | | < < | < > > | < < < < < < < | 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 13503 13504 13505 13506 13507 13508 13509 13510 13511 13512 13513 13514 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 13530 |
static void UpdateStringOfIndex(struct Jim_Obj *objPtr)
{
if (objPtr->internalRep.intValue == -1) {
JimSetStringBytes(objPtr, "end");
}
else {
char buf[JIM_INTEGER_SPACE + 1];
if (objPtr->internalRep.intValue >= 0 || objPtr->internalRep.intValue == -INT_MAX) {
sprintf(buf, "%d", objPtr->internalRep.intValue);
}
else {
sprintf(buf, "end%d", objPtr->internalRep.intValue + 1);
}
JimSetStringBytes(objPtr, buf);
}
}
static int SetIndexFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
jim_wide idx;
int end = 0;
const char *str;
Jim_Obj *exprObj = objPtr;
JimPanic((objPtr->refCount == 0, "SetIndexFromAny() called with zero refcount object"));
str = Jim_String(objPtr);
if (strncmp(str, "end", 3) == 0) {
end = 1;
str += 3;
idx = 0;
switch (*str) {
case '\0':
exprObj = NULL;
break;
case '-':
case '+':
exprObj = Jim_NewStringObj(interp, str, -1);
break;
default:
goto badindex;
}
}
if (exprObj) {
int ret;
Jim_IncrRefCount(exprObj);
ret = Jim_GetWideExpr(interp, exprObj, &idx);
Jim_DecrRefCount(interp, exprObj);
if (ret != JIM_OK) {
goto badindex;
}
}
if (end) {
if (idx > 0) {
idx = INT_MAX;
}
else {
idx--;
|
| ︙ | ︙ | |||
12352 12353 12354 12355 12356 12357 12358 |
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &indexObjType;
objPtr->internalRep.intValue = idx;
return JIM_OK;
badindex:
Jim_SetResultFormatted(interp,
| | | 13538 13539 13540 13541 13542 13543 13544 13545 13546 13547 13548 13549 13550 13551 13552 |
Jim_FreeIntRep(interp, objPtr);
objPtr->typePtr = &indexObjType;
objPtr->internalRep.intValue = idx;
return JIM_OK;
badindex:
Jim_SetResultFormatted(interp,
"bad index \"%#s\": must be intexpr or end?[+-]intexpr?", objPtr);
return JIM_ERR;
}
int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
{
if (objPtr->typePtr == &intObjType) {
|
| ︙ | ︙ | |||
12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 12489 12490 |
JIM_EXPROP_POW,
JIM_EXPROP_STREQ,
JIM_EXPROP_STRNE,
JIM_EXPROP_STRIN,
JIM_EXPROP_STRNI,
JIM_EXPROP_NOT,
JIM_EXPROP_BITNOT,
JIM_EXPROP_UNARYMINUS,
JIM_EXPROP_UNARYPLUS,
| > > > > | 13663 13664 13665 13666 13667 13668 13669 13670 13671 13672 13673 13674 13675 13676 13677 13678 13679 13680 |
JIM_EXPROP_POW,
JIM_EXPROP_STREQ,
JIM_EXPROP_STRNE,
JIM_EXPROP_STRIN,
JIM_EXPROP_STRNI,
JIM_EXPROP_STRLT,
JIM_EXPROP_STRGT,
JIM_EXPROP_STRLE,
JIM_EXPROP_STRGE,
JIM_EXPROP_NOT,
JIM_EXPROP_BITNOT,
JIM_EXPROP_UNARYMINUS,
JIM_EXPROP_UNARYPLUS,
|
| ︙ | ︙ | |||
12542 12543 12544 12545 12546 12547 12548 |
static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
{
int intresult = 1;
| | | 13732 13733 13734 13735 13736 13737 13738 13739 13740 13741 13742 13743 13744 13745 13746 |
static int JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node, Jim_Obj **objPtrPtr);
static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);
static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
{
int intresult = 1;
int rc, bA = 0;
double dA, dC = 0;
jim_wide wA, wC = 0;
Jim_Obj *A;
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
return rc;
}
|
| ︙ | ︙ | |||
12609 12610 12611 12612 12613 12614 12615 12616 12617 12618 12619 12620 12621 12622 12623 12624 12625 12626 12627 12628 12629 12630 12631 12632 12633 12634 12635 |
case JIM_EXPROP_NOT:
wC = !dA;
break;
default:
abort();
}
}
if (rc == JIM_OK) {
if (intresult) {
Jim_SetResultInt(interp, wC);
}
else {
Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
}
}
Jim_DecrRefCount(interp, A);
return rc;
}
static double JimRandDouble(Jim_Interp *interp)
{
unsigned long x;
JimRandomBytes(interp, &x, sizeof(x));
| > > > > > > > > > | | 13799 13800 13801 13802 13803 13804 13805 13806 13807 13808 13809 13810 13811 13812 13813 13814 13815 13816 13817 13818 13819 13820 13821 13822 13823 13824 13825 13826 13827 13828 13829 13830 13831 13832 13833 13834 13835 13836 13837 13838 13839 13840 13841 13842 |
case JIM_EXPROP_NOT:
wC = !dA;
break;
default:
abort();
}
}
else if ((rc = Jim_GetBoolean(interp, A, &bA)) == JIM_OK) {
switch (node->type) {
case JIM_EXPROP_NOT:
wC = !bA;
break;
default:
abort();
}
}
if (rc == JIM_OK) {
if (intresult) {
Jim_SetResultInt(interp, wC);
}
else {
Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
}
}
Jim_DecrRefCount(interp, A);
return rc;
}
static double JimRandDouble(Jim_Interp *interp)
{
unsigned long x;
JimRandomBytes(interp, &x, sizeof(x));
return (double)x / (double)~0UL;
}
static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
{
jim_wide wA;
Jim_Obj *A;
int rc;
|
| ︙ | ︙ | |||
13038 13039 13040 13041 13042 13043 13044 |
static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
{
Jim_Obj *A, *B;
jim_wide wC;
| | > > > > > > > > > > > > > > > | 14237 14238 14239 14240 14241 14242 14243 14244 14245 14246 14247 14248 14249 14250 14251 14252 14253 14254 14255 14256 14257 14258 14259 14260 14261 14262 14263 14264 14265 14266 14267 14268 14269 14270 14271 14272 14273 14274 14275 14276 14277 14278 14279 14280 14281 14282 |
static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
{
Jim_Obj *A, *B;
jim_wide wC;
int comp, rc;
if ((rc = JimExprGetTerm(interp, node->left, &A)) != JIM_OK) {
return rc;
}
if ((rc = JimExprGetTerm(interp, node->right, &B)) != JIM_OK) {
Jim_DecrRefCount(interp, A);
return rc;
}
switch (node->type) {
case JIM_EXPROP_STREQ:
case JIM_EXPROP_STRNE:
wC = Jim_StringEqObj(A, B);
if (node->type == JIM_EXPROP_STRNE) {
wC = !wC;
}
break;
case JIM_EXPROP_STRLT:
case JIM_EXPROP_STRGT:
case JIM_EXPROP_STRLE:
case JIM_EXPROP_STRGE:
comp = Jim_StringCompareObj(interp, A, B, 0);
if (node->type == JIM_EXPROP_STRLT) {
wC = comp == -1;
} else if (node->type == JIM_EXPROP_STRGT) {
wC = comp == 1;
} else if (node->type == JIM_EXPROP_STRLE) {
wC = comp == -1 || comp == 0;
} else {
wC = comp == 0 || comp == 1;
}
break;
case JIM_EXPROP_STRIN:
wC = JimSearchList(interp, B, A);
break;
case JIM_EXPROP_STRNI:
wC = !JimSearchList(interp, B, A);
break;
|
| ︙ | ︙ | |||
13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 |
OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
OPRINIT("eq", 60, 2, JimExprOpStrBin),
OPRINIT("ne", 60, 2, JimExprOpStrBin),
OPRINIT("in", 55, 2, JimExprOpStrBin),
OPRINIT("ni", 55, 2, JimExprOpStrBin),
OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
| > > > > > | 14408 14409 14410 14411 14412 14413 14414 14415 14416 14417 14418 14419 14420 14421 14422 14423 14424 14425 14426 |
OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),
OPRINIT("eq", 60, 2, JimExprOpStrBin),
OPRINIT("ne", 60, 2, JimExprOpStrBin),
OPRINIT("in", 55, 2, JimExprOpStrBin),
OPRINIT("ni", 55, 2, JimExprOpStrBin),
OPRINIT("lt", 75, 2, JimExprOpStrBin),
OPRINIT("gt", 75, 2, JimExprOpStrBin),
OPRINIT("le", 75, 2, JimExprOpStrBin),
OPRINIT("ge", 75, 2, JimExprOpStrBin),
OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
|
| ︙ | ︙ | |||
13240 13241 13242 13243 13244 13245 13246 13247 |
#undef OPRINIT_ATTR
#define JIM_EXPR_OPERATORS_NUM \
(sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
static int JimParseExpression(struct JimParserCtx *pc)
{
| > > > | | | | | | > > > > > > > > | 14459 14460 14461 14462 14463 14464 14465 14466 14467 14468 14469 14470 14471 14472 14473 14474 14475 14476 14477 14478 14479 14480 14481 14482 14483 14484 14485 14486 14487 14488 14489 14490 |
#undef OPRINIT_ATTR
#define JIM_EXPR_OPERATORS_NUM \
(sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))
static int JimParseExpression(struct JimParserCtx *pc)
{
pc->errmsg = NULL;
while (1) {
while (isspace(UCHAR(*pc->p)) || (*(pc->p) == '\\' && *(pc->p + 1) == '\n')) {
if (*pc->p == '\n') {
pc->linenr++;
}
pc->p++;
pc->len--;
}
if (*pc->p == '#') {
JimParseComment(pc);
continue;
}
break;
}
pc->tline = pc->linenr;
pc->tstart = pc->p;
if (pc->len == 0) {
|
| ︙ | ︙ | |||
13281 13282 13283 13284 13285 13286 13287 13288 13289 13290 13291 13292 13293 13294 |
return JimParseCmd(pc);
case '$':
if (JimParseVar(pc) == JIM_ERR)
return JimParseExprOperator(pc);
else {
if (pc->tt == JIM_TT_EXPRSUGAR) {
return JIM_ERR;
}
return JIM_OK;
}
break;
case '0':
case '1':
| > | 14511 14512 14513 14514 14515 14516 14517 14518 14519 14520 14521 14522 14523 14524 14525 |
return JimParseCmd(pc);
case '$':
if (JimParseVar(pc) == JIM_ERR)
return JimParseExprOperator(pc);
else {
if (pc->tt == JIM_TT_EXPRSUGAR) {
pc->errmsg = "nesting expr in expr is not allowed";
return JIM_ERR;
}
return JIM_OK;
}
break;
case '0':
case '1':
|
| ︙ | ︙ | |||
13370 13371 13372 13373 13374 13375 13376 |
}
}
return JIM_ERR;
}
static int JimParseExprBoolean(struct JimParserCtx *pc)
{
| < < | < < < | < | | | 14601 14602 14603 14604 14605 14606 14607 14608 14609 14610 14611 14612 14613 14614 14615 14616 14617 14618 14619 |
}
}
return JIM_ERR;
}
static int JimParseExprBoolean(struct JimParserCtx *pc)
{
int i;
for (i = 0; i < sizeof(jim_true_false_strings) / sizeof(*jim_true_false_strings); i++) {
if (strncmp(pc->p, jim_true_false_strings[i], jim_true_false_lens[i]) == 0) {
pc->p += jim_true_false_lens[i];
pc->len -= jim_true_false_lens[i];
pc->tend = pc->p - 1;
pc->tt = JIM_TT_EXPR_BOOLEAN;
return JIM_OK;
}
}
return JIM_ERR;
}
|
| ︙ | ︙ | |||
13431 13432 13433 13434 13435 13436 13437 13438 13439 13440 13441 13442 13443 13444 13445 13446 13447 13448 |
int len = pc->len - bestLen;
while (len && isspace(UCHAR(*p))) {
len--;
p++;
}
if (*p != '(') {
return JIM_ERR;
}
}
pc->tend = pc->p + bestLen - 1;
pc->p += bestLen;
pc->len -= bestLen;
pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
return JIM_OK;
}
| > < < < < < < < < < < < < < < < < < < < < < < < < < | | 14656 14657 14658 14659 14660 14661 14662 14663 14664 14665 14666 14667 14668 14669 14670 14671 14672 14673 14674 14675 14676 14677 14678 14679 14680 14681 14682 14683 14684 14685 14686 14687 14688 14689 14690 14691 14692 |
int len = pc->len - bestLen;
while (len && isspace(UCHAR(*p))) {
len--;
p++;
}
if (*p != '(') {
pc->errmsg = "function requires parentheses";
return JIM_ERR;
}
}
pc->tend = pc->p + bestLen - 1;
pc->p += bestLen;
pc->len -= bestLen;
pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
return JIM_OK;
}
static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr);
static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr);
static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr);
static const Jim_ObjType exprObjType = {
"expression",
FreeExprInternalRep,
DupExprInternalRep,
NULL,
JIM_TYPE_NONE,
};
struct ExprTree
{
struct JimExprNode *expr;
struct JimExprNode *nodes;
|
| ︙ | ︙ | |||
13569 13570 13571 13572 13573 13574 13575 | } #endif #define EXPR_UNTIL_CLOSE 0x0001 #define EXPR_FUNC_ARGS 0x0002 #define EXPR_TERNARY 0x0004 | | < | 14770 14771 14772 14773 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 |
}
#endif
#define EXPR_UNTIL_CLOSE 0x0001
#define EXPR_FUNC_ARGS 0x0002
#define EXPR_TERNARY 0x0004
static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms) {
int rc;
struct JimExprNode *node;
int exp_stacklen = builder->stack.len + exp_numterms;
if (builder->level++ > 200) {
Jim_SetResultString(interp, "Expression too complex", -1);
|
| ︙ | ︙ | |||
13773 13774 13775 13776 13777 13778 13779 |
}
if (!objPtr) {
objPtr = Jim_NewStringObj(interp, t->token, t->len);
if (t->type == JIM_TT_CMD) {
| | | 14973 14974 14975 14976 14977 14978 14979 14980 14981 14982 14983 14984 14985 14986 14987 |
}
if (!objPtr) {
objPtr = Jim_NewStringObj(interp, t->token, t->len);
if (t->type == JIM_TT_CMD) {
Jim_SetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
}
}
node = builder->next++;
node->objPtr = objPtr;
Jim_IncrRefCount(node->objPtr);
|
| ︙ | ︙ | |||
13824 13825 13826 13827 13828 13829 13830 |
builder.parencount = 0;
builder.level = 0;
builder.token = builder.first_token = tokenlist->list;
builder.exprObjPtr = exprObjPtr;
builder.fileNameObj = fileNameObj;
| | | 15024 15025 15026 15027 15028 15029 15030 15031 15032 15033 15034 15035 15036 15037 15038 |
builder.parencount = 0;
builder.level = 0;
builder.token = builder.first_token = tokenlist->list;
builder.exprObjPtr = exprObjPtr;
builder.fileNameObj = fileNameObj;
builder.nodes = Jim_Alloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
builder.next = builder.nodes;
Jim_InitStack(&builder.stack);
rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);
if (rc == JIM_OK) {
|
| ︙ | ︙ | |||
13871 13872 13873 13874 13875 13876 13877 |
struct ExprTree *expr;
ParseTokenList tokenlist;
int line;
Jim_Obj *fileNameObj;
int rc = JIM_ERR;
| < < < < < | < < > > > > > > > > | > > | | 15071 15072 15073 15074 15075 15076 15077 15078 15079 15080 15081 15082 15083 15084 15085 15086 15087 15088 15089 15090 15091 15092 15093 15094 15095 15096 15097 15098 15099 15100 15101 15102 15103 15104 15105 15106 15107 15108 15109 15110 15111 15112 15113 15114 15115 15116 15117 15118 15119 15120 15121 15122 15123 15124 15125 15126 15127 15128 15129 15130 |
struct ExprTree *expr;
ParseTokenList tokenlist;
int line;
Jim_Obj *fileNameObj;
int rc = JIM_ERR;
fileNameObj = Jim_GetSourceInfo(interp, objPtr, &line);
Jim_IncrRefCount(fileNameObj);
exprText = Jim_GetString(objPtr, &exprTextLen);
ScriptTokenListInit(&tokenlist);
JimParserInit(&parser, exprText, exprTextLen, line);
while (!parser.eof) {
if (JimParseExpression(&parser) != JIM_OK) {
ScriptTokenListFree(&tokenlist);
Jim_SetResultFormatted(interp, "syntax error in expression: \"%#s\"", objPtr);
if (parser.errmsg) {
Jim_AppendStrings(interp, Jim_GetResult(interp), ": ", parser.errmsg, NULL);
}
expr = NULL;
goto err;
}
ScriptAddToken(&tokenlist, parser.tstart, parser.tend - parser.tstart + 1, parser.tt,
parser.tline);
}
#ifdef DEBUG_SHOW_EXPR_TOKENS
{
int i;
printf("==== Expr Tokens (%s) ====\n", Jim_String(fileNameObj));
for (i = 0; i < tokenlist.count; i++) {
printf("[%2d]@%d %s '%.*s'\n", i, tokenlist.list[i].line, jim_tt_name(tokenlist.list[i].type),
tokenlist.list[i].len, tokenlist.list[i].token);
}
}
#endif
if (tokenlist.count <= 1) {
Jim_SetResultString(interp, "empty expression", -1);
rc = JIM_ERR;
}
else {
rc = JimParseCheckMissing(interp, parser.missing.ch);
}
if (rc != JIM_OK) {
ScriptTokenListFree(&tokenlist);
Jim_DecrRefCount(interp, fileNameObj);
return rc;
}
expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);
ScriptTokenListFree(&tokenlist);
|
| ︙ | ︙ | |||
14002 14003 14004 14005 14006 14007 14008 14009 14010 14011 14012 14013 14014 14015 14016 14017 14018 14019 14020 14021 14022 |
if (objPtr) {
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
return JIM_ERR;
case JIM_TT_ESC:
if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
return JIM_ERR;
case JIM_TT_CMD:
return Jim_EvalObj(interp, node->objPtr);
default:
return JIM_ERR;
}
}
| > > > > > > | 15205 15206 15207 15208 15209 15210 15211 15212 15213 15214 15215 15216 15217 15218 15219 15220 15221 15222 15223 15224 15225 15226 15227 15228 15229 15230 15231 |
if (objPtr) {
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
return JIM_ERR;
case JIM_TT_ESC:
if (interp->safeexpr) {
return JIM_ERR;
}
if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
return JIM_ERR;
case JIM_TT_CMD:
if (interp->safeexpr) {
return JIM_ERR;
}
return Jim_EvalObj(interp, node->objPtr);
default:
return JIM_ERR;
}
}
|
| ︙ | ︙ | |||
14041 14042 14043 14044 14045 14046 14047 14048 14049 |
}
int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
{
struct ExprTree *expr;
int retcode = JIM_OK;
expr = JimGetExpression(interp, exprObjPtr);
if (!expr) {
| > | > < > | | | 15250 15251 15252 15253 15254 15255 15256 15257 15258 15259 15260 15261 15262 15263 15264 15265 15266 15267 15268 15269 15270 15271 15272 15273 15274 15275 15276 15277 15278 15279 15280 15281 15282 15283 15284 15285 15286 15287 15288 15289 15290 15291 |
}
int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
{
struct ExprTree *expr;
int retcode = JIM_OK;
Jim_IncrRefCount(exprObjPtr);
expr = JimGetExpression(interp, exprObjPtr);
if (!expr) {
retcode = JIM_ERR;
goto done;
}
#ifdef JIM_OPTIMIZATION
if (!interp->safeexpr) {
Jim_Obj *objPtr;
switch (expr->len) {
case 1:
objPtr = JimExprIntValOrVar(interp, expr->expr);
if (objPtr) {
Jim_SetResult(interp, objPtr);
goto done;
}
break;
case 2:
if (expr->expr->type == JIM_EXPROP_NOT) {
objPtr = JimExprIntValOrVar(interp, expr->expr->left);
if (objPtr && JimIsWide(objPtr)) {
Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);
goto done;
}
}
break;
case 3:
objPtr = JimExprIntValOrVar(interp, expr->expr->left);
if (objPtr && JimIsWide(objPtr)) {
|
| ︙ | ︙ | |||
14102 14103 14104 14105 14106 14107 14108 |
case JIM_EXPROP_NUMNE:
cmpRes = wideValueA != wideValueB;
break;
default:
goto noopt;
}
Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
| | | > > > > > > | 15313 15314 15315 15316 15317 15318 15319 15320 15321 15322 15323 15324 15325 15326 15327 15328 15329 15330 15331 15332 15333 15334 15335 15336 15337 15338 15339 15340 15341 15342 15343 15344 15345 15346 15347 |
case JIM_EXPROP_NUMNE:
cmpRes = wideValueA != wideValueB;
break;
default:
goto noopt;
}
Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);
goto done;
}
}
break;
}
}
noopt:
#endif
expr->inUse++;
retcode = JimExprEvalTermNode(interp, expr->expr);
Jim_FreeIntRep(interp, exprObjPtr);
exprObjPtr->typePtr = &exprObjType;
Jim_SetIntRepPtr(exprObjPtr, expr);
done:
Jim_DecrRefCount(interp, exprObjPtr);
return retcode;
}
int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
{
int retcode = Jim_EvalExpression(interp, exprObjPtr);
|
| ︙ | ︙ | |||
14403 14404 14405 14406 14407 14408 14409 |
int c;
int n;
if (!sdescr && isspace(UCHAR(*str)))
break;
n = utf8_tounicode(str, &c);
| | | | | | > | | | | | | | 15620 15621 15622 15623 15624 15625 15626 15627 15628 15629 15630 15631 15632 15633 15634 15635 15636 15637 15638 15639 15640 15641 15642 15643 15644 15645 15646 15647 15648 15649 15650 15651 15652 15653 15654 15655 15656 15657 15658 15659 15660 15661 15662 15663 15664 15665 15666 15667 15668 15669 15670 15671 15672 15673 15674 15675 15676 15677 15678 15679 15680 15681 15682 15683 15684 15685 15686 15687 15688 15689 15690 15691 15692 15693 15694 15695 15696 15697 15698 15699 |
int c;
int n;
if (!sdescr && isspace(UCHAR(*str)))
break;
n = utf8_tounicode(str, &c);
if (sdescr && !JimCharsetMatch(sdescr, strlen(sdescr), c, JIM_CHARSET_SCAN))
break;
while (n--)
*p++ = *str++;
}
*p = 0;
return Jim_NewStringObjNoAlloc(interp, buffer, p - buffer);
}
static int ScanOneEntry(Jim_Interp *interp, const char *str, int pos, int str_bytelen,
ScanFmtStringObj * fmtObj, long idx, Jim_Obj **valObjPtr)
{
const char *tok;
const ScanFmtPartDescr *descr = &fmtObj->descr[idx];
size_t scanned = 0;
size_t anchor = pos;
int i;
Jim_Obj *tmpObj = NULL;
*valObjPtr = 0;
if (descr->prefix) {
for (i = 0; pos < str_bytelen && descr->prefix[i]; ++i) {
if (isspace(UCHAR(descr->prefix[i])))
while (pos < str_bytelen && isspace(UCHAR(str[pos])))
++pos;
else if (descr->prefix[i] != str[pos])
break;
else
++pos;
}
if (pos >= str_bytelen) {
return -1;
}
else if (descr->prefix[i] != 0)
return 0;
}
if (descr->type != 'c' && descr->type != '[' && descr->type != 'n')
while (isspace(UCHAR(str[pos])))
++pos;
scanned = pos - anchor;
if (descr->type == 'n') {
*valObjPtr = Jim_NewIntObj(interp, anchor + scanned);
}
else if (pos >= str_bytelen) {
return -1;
}
else if (descr->type == 'c') {
int c;
scanned += utf8_tounicode(&str[pos], &c);
*valObjPtr = Jim_NewIntObj(interp, c);
return scanned;
}
else {
if (descr->width > 0) {
size_t sLen = utf8_strlen(&str[pos], str_bytelen - pos);
size_t tLen = descr->width > sLen ? sLen : descr->width;
tmpObj = Jim_NewStringObjUtf8(interp, str + pos, tLen);
tok = tmpObj->bytes;
}
else {
|
| ︙ | ︙ | |||
14546 14547 14548 14549 14550 14551 14552 |
Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
{
size_t i, pos;
int scanned = 1;
const char *str = Jim_String(strObjPtr);
| | | 15764 15765 15766 15767 15768 15769 15770 15771 15772 15773 15774 15775 15776 15777 15778 |
Jim_Obj *Jim_ScanString(Jim_Interp *interp, Jim_Obj *strObjPtr, Jim_Obj *fmtObjPtr, int flags)
{
size_t i, pos;
int scanned = 1;
const char *str = Jim_String(strObjPtr);
int str_bytelen = Jim_Length(strObjPtr);
Jim_Obj *resultList = 0;
Jim_Obj **resultVec = 0;
int resultc;
Jim_Obj *emptyStr = 0;
ScanFmtStringObj *fmtObj;
|
| ︙ | ︙ | |||
14583 14584 14585 14586 14587 14588 14589 |
Jim_Obj *value = 0;
if (descr->type == 0)
continue;
if (scanned > 0)
| | | 15801 15802 15803 15804 15805 15806 15807 15808 15809 15810 15811 15812 15813 15814 15815 |
Jim_Obj *value = 0;
if (descr->type == 0)
continue;
if (scanned > 0)
scanned = ScanOneEntry(interp, str, pos, str_bytelen, fmtObj, i, &value);
if (scanned == -1 && i == 0)
goto eof;
pos += scanned;
|
| ︙ | ︙ | |||
14705 14706 14707 14708 14709 14710 14711 |
Jim_Obj *intObjPtr;
if (argc != 2 && argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
return JIM_ERR;
}
if (argc == 3) {
| | | 15923 15924 15925 15926 15927 15928 15929 15930 15931 15932 15933 15934 15935 15936 15937 |
Jim_Obj *intObjPtr;
if (argc != 2 && argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "varName ?increment?");
return JIM_ERR;
}
if (argc == 3) {
if (Jim_GetWideExpr(interp, argv[2], &increment) != JIM_OK)
return JIM_ERR;
}
intObjPtr = Jim_GetVariable(interp, argv[1], JIM_UNSHARED);
if (!intObjPtr) {
wideValue = 0;
}
|
| ︙ | ︙ | |||
14741 14742 14743 14744 14745 14746 14747 14748 14749 14750 14751 14752 14753 14754 |
return JIM_OK;
}
#define JIM_EVAL_SARGV_LEN 8
#define JIM_EVAL_SINTV_LEN 8
static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retcode;
if (interp->unknown_called > 50) {
return JIM_ERR;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 15959 15960 15961 15962 15963 15964 15965 15966 15967 15968 15969 15970 15971 15972 15973 15974 15975 15976 15977 15978 15979 15980 15981 15982 15983 15984 15985 15986 15987 15988 15989 15990 15991 15992 15993 15994 15995 15996 15997 15998 15999 16000 16001 16002 16003 16004 16005 16006 16007 16008 16009 16010 16011 16012 16013 16014 16015 16016 |
return JIM_OK;
}
#define JIM_EVAL_SARGV_LEN 8
#define JIM_EVAL_SINTV_LEN 8
static int JimTraceCallback(Jim_Interp *interp, const char *type, int argc, Jim_Obj *const *argv)
{
JimPanic((interp->traceCmdObj == NULL, "xtrace invoked with no object"));
int ret;
Jim_Obj *nargv[7];
Jim_Obj *traceCmdObj = interp->traceCmdObj;
Jim_Obj *resultObj = Jim_GetResult(interp);
ScriptObj *script = NULL;
if (interp->evalFrame->scriptObj) {
script = JimGetScript(interp, interp->evalFrame->scriptObj);
}
nargv[0] = traceCmdObj;
nargv[1] = Jim_NewStringObj(interp, type, -1);
nargv[2] = script ? script->fileNameObj : interp->emptyObj;
nargv[3] = Jim_NewIntObj(interp, script ? script->linenr : 1);
nargv[4] = resultObj;
nargv[5] = argv[0];
nargv[6] = Jim_NewListObj(interp, argv + 1, argc - 1);
interp->traceCmdObj = NULL;
Jim_IncrRefCount(resultObj);
ret = Jim_EvalObjVector(interp, 7, nargv);
Jim_DecrRefCount(interp, resultObj);
if (ret == JIM_OK || ret == JIM_RETURN) {
interp->traceCmdObj = traceCmdObj;
Jim_SetEmptyResult(interp);
ret = JIM_OK;
}
else {
Jim_DecrRefCount(interp, traceCmdObj);
}
return ret;
}
static int JimUnknown(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retcode;
if (interp->unknown_called > 50) {
return JIM_ERR;
|
| ︙ | ︙ | |||
14762 14763 14764 14765 14766 14767 14768 14769 14770 14771 14772 14773 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 |
interp->unknown_called++;
retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
interp->unknown_called--;
return retcode;
}
static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
int retcode;
Jim_Cmd *cmdPtr;
void *prevPrivData;
#if 0
printf("invoke");
int j;
for (j = 0; j < objc; j++) {
printf(" '%s'", Jim_String(objv[j]));
}
printf("\n");
#endif
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < | | | | | < > > > > > > > > | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | > > > > > > | | < | < | < < | < < < | | 16024 16025 16026 16027 16028 16029 16030 16031 16032 16033 16034 16035 16036 16037 16038 16039 16040 16041 16042 16043 16044 16045 16046 16047 16048 16049 16050 16051 16052 16053 16054 16055 16056 16057 16058 16059 16060 16061 16062 16063 16064 16065 16066 16067 16068 16069 16070 16071 16072 16073 16074 16075 16076 16077 16078 16079 16080 16081 16082 16083 16084 16085 16086 16087 16088 16089 16090 16091 16092 16093 16094 16095 16096 16097 16098 16099 16100 16101 16102 16103 16104 16105 16106 16107 16108 16109 16110 16111 16112 16113 16114 16115 16116 16117 16118 16119 16120 16121 16122 16123 16124 16125 16126 16127 16128 16129 16130 16131 16132 16133 16134 16135 16136 16137 16138 16139 16140 16141 16142 16143 16144 16145 16146 16147 16148 16149 16150 16151 16152 16153 16154 16155 16156 16157 16158 16159 16160 16161 16162 16163 16164 16165 16166 16167 16168 16169 16170 16171 16172 16173 16174 16175 16176 16177 16178 16179 16180 16181 16182 16183 16184 16185 16186 16187 16188 16189 16190 16191 16192 16193 16194 16195 16196 16197 16198 16199 16200 16201 16202 16203 16204 16205 16206 16207 16208 16209 16210 16211 16212 16213 16214 16215 16216 16217 16218 16219 16220 16221 16222 16223 16224 16225 16226 16227 16228 16229 16230 16231 16232 16233 16234 16235 16236 16237 16238 16239 16240 16241 |
interp->unknown_called++;
retcode = Jim_EvalObjPrefix(interp, interp->unknown, argc, argv);
interp->unknown_called--;
return retcode;
}
static void JimPushEvalFrame(Jim_Interp *interp, Jim_EvalFrame *frame, Jim_Obj *scriptObj)
{
memset(frame, 0, sizeof(*frame));
frame->parent = interp->evalFrame;
frame->level = frame->parent->level + 1;
frame->procLevel = interp->procLevel;
frame->framePtr = interp->framePtr;
if (scriptObj) {
frame->scriptObj = scriptObj;
}
else {
frame->scriptObj = frame->parent->scriptObj;
}
interp->evalFrame = frame;
#if 0
if (frame->scriptObj) {
printf("script: %.*s\n", 20, Jim_String(frame->scriptObj));
}
#endif
}
static void JimPopEvalFrame(Jim_Interp *interp)
{
interp->evalFrame = interp->evalFrame->parent;
}
static int JimInvokeCommand(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
int retcode;
Jim_Cmd *cmdPtr;
void *prevPrivData;
Jim_Obj *tailcallObj = NULL;
#if 0
printf("invoke");
int j;
for (j = 0; j < objc; j++) {
printf(" '%s'", Jim_String(objv[j]));
}
printf("\n");
#endif
cmdPtr = Jim_GetCommand(interp, objv[0], JIM_ERRMSG);
if (cmdPtr == NULL) {
return JimUnknown(interp, objc, objv);
}
JimIncrCmdRefCount(cmdPtr);
if (interp->evalDepth == interp->maxEvalDepth) {
Jim_SetResultString(interp, "Infinite eval recursion", -1);
retcode = JIM_ERR;
goto out;
}
interp->evalDepth++;
prevPrivData = interp->cmdPrivData;
tailcall:
interp->evalFrame->argc = objc;
interp->evalFrame->argv = objv;
interp->evalFrame->cmd = cmdPtr;
if (!interp->traceCmdObj ||
(retcode = JimTraceCallback(interp, "cmd", objc, objv)) == JIM_OK) {
Jim_SetEmptyResult(interp);
if (cmdPtr->isproc) {
retcode = JimCallProcedure(interp, cmdPtr, objc, objv);
}
else {
interp->cmdPrivData = cmdPtr->u.native.privData;
retcode = cmdPtr->u.native.cmdProc(interp, objc, objv);
}
if (retcode == JIM_ERR) {
JimSetErrorStack(interp, NULL);
}
}
if (tailcallObj) {
Jim_DecrRefCount(interp, tailcallObj);
tailcallObj = NULL;
}
interp->evalFrame->argc = 0;
interp->evalFrame->argv = NULL;
if (retcode == JIM_EVAL && interp->framePtr->tailcallObj) {
JimDecrCmdRefCount(interp, cmdPtr);
cmdPtr = interp->framePtr->tailcallCmd;
interp->framePtr->tailcallCmd = NULL;
tailcallObj = interp->framePtr->tailcallObj;
interp->framePtr->tailcallObj = NULL;
objc = tailcallObj->internalRep.listValue.len;
objv = tailcallObj->internalRep.listValue.ele;
goto tailcall;
}
interp->cmdPrivData = prevPrivData;
interp->evalDepth--;
out:
JimDecrCmdRefCount(interp, cmdPtr);
if (retcode == JIM_ERR) {
JimSetErrorStack(interp, NULL);
}
if (interp->framePtr->tailcallObj) {
JimDecrCmdRefCount(interp, interp->framePtr->tailcallCmd);
Jim_DecrRefCount(interp, interp->framePtr->tailcallObj);
interp->framePtr->tailcallCmd = NULL;
interp->framePtr->tailcallObj = NULL;
}
return retcode;
}
int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
int i, retcode;
Jim_EvalFrame frame;
for (i = 0; i < objc; i++)
Jim_IncrRefCount(objv[i]);
JimPushEvalFrame(interp, &frame, NULL);
retcode = JimInvokeCommand(interp, objc, objv);
JimPopEvalFrame(interp);
for (i = 0; i < objc; i++)
Jim_DecrRefCount(interp, objv[i]);
return retcode;
}
int Jim_EvalObjPrefix(Jim_Interp *interp, Jim_Obj *prefix, int objc, Jim_Obj *const *objv)
{
int ret;
Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv));
nargv[0] = prefix;
memcpy(&nargv[1], &objv[0], sizeof(nargv[0]) * objc);
ret = Jim_EvalObjVector(interp, objc + 1, nargv);
Jim_Free(nargv);
return ret;
}
static int JimSubstOneToken(Jim_Interp *interp, const ScriptToken *token, Jim_Obj **objPtrPtr)
{
Jim_Obj *objPtr;
int ret = JIM_ERR;
switch (token->type) {
case JIM_TT_STR:
case JIM_TT_ESC:
objPtr = token->objPtr;
break;
case JIM_TT_VAR:
objPtr = Jim_GetVariable(interp, token->objPtr, JIM_ERRMSG);
break;
case JIM_TT_DICTSUGAR:
objPtr = JimExpandDictSugar(interp, token->objPtr);
break;
case JIM_TT_EXPRSUGAR:
ret = Jim_EvalExpression(interp, token->objPtr);
if (ret == JIM_OK) {
objPtr = Jim_GetResult(interp);
}
else {
objPtr = NULL;
}
break;
case JIM_TT_CMD:
ret = Jim_EvalObj(interp, token->objPtr);
if (ret == JIM_OK || ret == JIM_RETURN) {
objPtr = interp->result;
} else {
objPtr = NULL;
}
break;
default:
JimPanic((1,
"default token type (%d) reached " "in Jim_SubstObj().", token->type));
objPtr = NULL;
break;
}
if (objPtr) {
*objPtrPtr = objPtr;
return JIM_OK;
}
return ret;
}
static Jim_Obj *JimInterpolateTokens(Jim_Interp *interp, const ScriptToken * token, int tokens, int flags)
{
int totlen = 0, i;
Jim_Obj **intv;
Jim_Obj *sintv[JIM_EVAL_SINTV_LEN];
|
| ︙ | ︙ | |||
14990 14991 14992 14993 14994 14995 14996 |
objPtr->typePtr = &interpolatedObjType;
objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
Jim_IncrRefCount(intv[2]);
}
else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
| > > | | 16296 16297 16298 16299 16300 16301 16302 16303 16304 16305 16306 16307 16308 16309 16310 16311 16312 |
objPtr->typePtr = &interpolatedObjType;
objPtr->internalRep.dictSubstValue.varNameObjPtr = token[0].objPtr;
objPtr->internalRep.dictSubstValue.indexObjPtr = intv[2];
Jim_IncrRefCount(intv[2]);
}
else if (tokens && intv[0] && intv[0]->typePtr == &sourceObjType) {
int line;
Jim_Obj *fileNameObj = Jim_GetSourceInfo(interp, intv[0], &line);
Jim_SetSourceInfo(interp, objPtr, fileNameObj, line);
}
s = objPtr->bytes = Jim_Alloc(totlen + 1);
objPtr->length = totlen;
for (i = 0; i < tokens; i++) {
if (intv[i]) {
|
| ︙ | ︙ | |||
15016 15017 15018 15019 15020 15021 15022 15023 15024 15025 15026 15027 15028 15029 15030 15031 15032 15033 15034 15035 15036 15037 15038 15039 15040 15041 15042 15043 15044 15045 15046 15047 15048 |
return objPtr;
}
static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
{
int retcode = JIM_OK;
JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
if (listPtr->internalRep.listValue.len) {
Jim_IncrRefCount(listPtr);
retcode = JimInvokeCommand(interp,
listPtr->internalRep.listValue.len,
listPtr->internalRep.listValue.ele);
Jim_DecrRefCount(interp, listPtr);
}
return retcode;
}
int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
{
SetListFromAny(interp, listPtr);
return JimEvalObjList(interp, listPtr);
}
int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
{
int i;
ScriptObj *script;
ScriptToken *token;
int retcode = JIM_OK;
Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
| > > > > > > | > | | 16324 16325 16326 16327 16328 16329 16330 16331 16332 16333 16334 16335 16336 16337 16338 16339 16340 16341 16342 16343 16344 16345 16346 16347 16348 16349 16350 16351 16352 16353 16354 16355 16356 16357 16358 16359 16360 16361 16362 16363 16364 16365 16366 16367 16368 16369 16370 16371 16372 16373 16374 16375 16376 16377 16378 16379 |
return objPtr;
}
static int JimEvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
{
int retcode = JIM_OK;
Jim_EvalFrame frame;
JimPanic((Jim_IsList(listPtr) == 0, "JimEvalObjList() invoked on non-list."));
JimPushEvalFrame(interp, &frame, NULL);
if (listPtr->internalRep.listValue.len) {
Jim_IncrRefCount(listPtr);
retcode = JimInvokeCommand(interp,
listPtr->internalRep.listValue.len,
listPtr->internalRep.listValue.ele);
Jim_DecrRefCount(interp, listPtr);
}
JimPopEvalFrame(interp);
return retcode;
}
int Jim_EvalObjList(Jim_Interp *interp, Jim_Obj *listPtr)
{
SetListFromAny(interp, listPtr);
return JimEvalObjList(interp, listPtr);
}
int Jim_EvalObj(Jim_Interp *interp, Jim_Obj *scriptObjPtr)
{
int i;
ScriptObj *script;
ScriptToken *token;
int retcode = JIM_OK;
Jim_Obj *sargv[JIM_EVAL_SARGV_LEN], **argv = NULL;
Jim_EvalFrame frame;
if (Jim_IsList(scriptObjPtr) && scriptObjPtr->bytes == NULL) {
return JimEvalObjList(interp, scriptObjPtr);
}
Jim_IncrRefCount(scriptObjPtr);
script = JimGetScript(interp, scriptObjPtr);
if (JimParseCheckMissing(interp, script->missing) == JIM_ERR) {
JimSetErrorStack(interp, script);
Jim_DecrRefCount(interp, scriptObjPtr);
return JIM_ERR;
}
Jim_SetEmptyResult(interp);
token = script->token;
|
| ︙ | ︙ | |||
15084 15085 15086 15087 15088 15089 15090 15091 |
return JIM_OK;
}
}
#endif
script->inUse++;
| > < < | 16399 16400 16401 16402 16403 16404 16405 16406 16407 16408 16409 16410 16411 16412 16413 16414 |
return JIM_OK;
}
}
#endif
script->inUse++;
JimPushEvalFrame(interp, &frame, scriptObjPtr);
interp->errorFlag = 0;
argv = sargv;
for (i = 0; i < script->len && retcode == JIM_OK; ) {
int argc;
int j;
|
| ︙ | ︙ | |||
15130 15131 15132 15133 15134 15135 15136 |
case JIM_TT_STR:
wordObjPtr = token[i].objPtr;
break;
case JIM_TT_VAR:
wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
break;
case JIM_TT_EXPRSUGAR:
| > > > > > | > | 16444 16445 16446 16447 16448 16449 16450 16451 16452 16453 16454 16455 16456 16457 16458 16459 16460 16461 16462 16463 16464 |
case JIM_TT_STR:
wordObjPtr = token[i].objPtr;
break;
case JIM_TT_VAR:
wordObjPtr = Jim_GetVariable(interp, token[i].objPtr, JIM_ERRMSG);
break;
case JIM_TT_EXPRSUGAR:
retcode = Jim_EvalExpression(interp, token[i].objPtr);
if (retcode == JIM_OK) {
wordObjPtr = Jim_GetResult(interp);
}
else {
wordObjPtr = NULL;
}
break;
case JIM_TT_DICTSUGAR:
wordObjPtr = JimExpandDictSugar(interp, token[i].objPtr);
break;
case JIM_TT_CMD:
retcode = Jim_EvalObj(interp, token[i].objPtr);
if (retcode == JIM_OK) {
|
| ︙ | ︙ | |||
15217 15218 15219 15220 15221 15222 15223 |
Jim_Free(argv);
argv = sargv;
}
}
if (retcode == JIM_ERR) {
| | < | < < < < < | 16537 16538 16539 16540 16541 16542 16543 16544 16545 16546 16547 16548 16549 16550 16551 16552 16553 16554 |
Jim_Free(argv);
argv = sargv;
}
}
if (retcode == JIM_ERR) {
JimSetErrorStack(interp, NULL);
}
JimPopEvalFrame(interp);
Jim_FreeIntRep(interp, scriptObjPtr);
scriptObjPtr->typePtr = &scriptObjType;
Jim_SetIntRepPtr(scriptObjPtr, script);
Jim_DecrRefCount(interp, scriptObjPtr);
return retcode;
|
| ︙ | ︙ | |||
15283 15284 15285 15286 15287 15288 15289 |
Jim_AppendString(interp, argmsg, "?", 1);
Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
Jim_AppendString(interp, argmsg, " ...?", -1);
}
else {
| | | 16597 16598 16599 16600 16601 16602 16603 16604 16605 16606 16607 16608 16609 16610 16611 |
Jim_AppendString(interp, argmsg, "?", 1);
Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].defaultObjPtr);
Jim_AppendString(interp, argmsg, " ...?", -1);
}
else {
Jim_AppendString(interp, argmsg, "?arg ...?", -1);
}
}
else {
if (cmd->u.proc.arglist[i].defaultObjPtr) {
Jim_AppendString(interp, argmsg, "?", 1);
Jim_AppendObj(interp, argmsg, cmd->u.proc.arglist[i].nameObjPtr);
Jim_AppendString(interp, argmsg, "?", 1);
|
| ︙ | ︙ | |||
15312 15313 15314 15315 15316 15317 15318 |
int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
{
Jim_CallFrame *callFramePtr;
int retcode;
callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
| | | < < | 16626 16627 16628 16629 16630 16631 16632 16633 16634 16635 16636 16637 16638 16639 16640 16641 16642 16643 16644 |
int Jim_EvalNamespace(Jim_Interp *interp, Jim_Obj *scriptObj, Jim_Obj *nsObj)
{
Jim_CallFrame *callFramePtr;
int retcode;
callFramePtr = JimCreateCallFrame(interp, interp->framePtr, nsObj);
callFramePtr->argv = interp->evalFrame->argv;
callFramePtr->argc = interp->evalFrame->argc;
callFramePtr->procArgsObjPtr = NULL;
callFramePtr->procBodyObjPtr = scriptObj;
callFramePtr->staticVars = NULL;
Jim_IncrRefCount(scriptObj);
interp->framePtr = callFramePtr;
if (interp->framePtr->level == interp->maxCallFrameDepth) {
Jim_SetResultString(interp, "Too many nested calls. Infinite recursion?", -1);
retcode = JIM_ERR;
|
| ︙ | ︙ | |||
15344 15345 15346 15347 15348 15349 15350 |
}
#endif
static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
{
Jim_CallFrame *callFramePtr;
int i, d, retcode, optargs;
| < | 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665 16666 16667 16668 16669 |
}
#endif
static int JimCallProcedure(Jim_Interp *interp, Jim_Cmd *cmd, int argc, Jim_Obj *const *argv)
{
Jim_CallFrame *callFramePtr;
int i, d, retcode, optargs;
if (argc - 1 < cmd->u.proc.reqArity ||
(cmd->u.proc.argsPos < 0 && argc - 1 > cmd->u.proc.reqArity + cmd->u.proc.optArity)) {
JimSetProcWrongArgs(interp, argv[0], cmd);
return JIM_ERR;
}
|
| ︙ | ︙ | |||
15372 15373 15374 15375 15376 15377 15378 |
callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
callFramePtr->argv = argv;
callFramePtr->argc = argc;
callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
callFramePtr->staticVars = cmd->u.proc.staticVars;
| | < < < | 16683 16684 16685 16686 16687 16688 16689 16690 16691 16692 16693 16694 16695 16696 16697 |
callFramePtr = JimCreateCallFrame(interp, interp->framePtr, cmd->u.proc.nsObj);
callFramePtr->argv = argv;
callFramePtr->argc = argc;
callFramePtr->procArgsObjPtr = cmd->u.proc.argListObjPtr;
callFramePtr->procBodyObjPtr = cmd->u.proc.bodyObjPtr;
callFramePtr->staticVars = cmd->u.proc.staticVars;
interp->procLevel++;
Jim_IncrRefCount(cmd->u.proc.argListObjPtr);
Jim_IncrRefCount(cmd->u.proc.bodyObjPtr);
interp->framePtr = callFramePtr;
optargs = (argc - 1 - cmd->u.proc.reqArity);
|
| ︙ | ︙ | |||
15423 15424 15425 15426 15427 15428 15429 15430 |
retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
}
if (retcode != JIM_OK) {
goto badargset;
}
}
| > > | > < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < | | < < < | < < < < < < | 16731 16732 16733 16734 16735 16736 16737 16738 16739 16740 16741 16742 16743 16744 16745 16746 16747 16748 16749 16750 16751 16752 16753 16754 16755 16756 16757 16758 16759 16760 16761 16762 16763 16764 16765 16766 16767 16768 16769 16770 16771 16772 16773 16774 16775 16776 16777 16778 16779 16780 16781 |
retcode = Jim_SetVariable(interp, nameObjPtr, cmd->u.proc.arglist[d].defaultObjPtr);
}
if (retcode != JIM_OK) {
goto badargset;
}
}
if (interp->traceCmdObj == NULL ||
(retcode = JimTraceCallback(interp, "proc", argc, argv)) == JIM_OK) {
retcode = Jim_EvalObj(interp, cmd->u.proc.bodyObjPtr);
}
badargset:
retcode = JimInvokeDefer(interp, retcode);
interp->framePtr = interp->framePtr->parent;
JimFreeCallFrame(interp, callFramePtr, JIM_FCF_REUSE);
if (retcode == JIM_RETURN) {
if (--interp->returnLevel <= 0) {
retcode = interp->returnCode;
interp->returnCode = JIM_OK;
interp->returnLevel = 0;
}
}
interp->procLevel--;
return retcode;
}
int Jim_EvalSource(Jim_Interp *interp, const char *filename, int lineno, const char *script)
{
int retval;
Jim_Obj *scriptObjPtr;
scriptObjPtr = Jim_NewStringObj(interp, script, -1);
Jim_IncrRefCount(scriptObjPtr);
if (filename) {
Jim_SetSourceInfo(interp, scriptObjPtr, Jim_NewStringObj(interp, filename, -1), lineno);
}
retval = Jim_EvalObj(interp, scriptObjPtr);
Jim_DecrRefCount(interp, scriptObjPtr);
return retval;
}
int Jim_Eval(Jim_Interp *interp, const char *script)
{
return Jim_EvalObj(interp, Jim_NewStringObj(interp, script, -1));
|
| ︙ | ︙ | |||
15533 15534 15535 15536 15537 15538 15539 |
interp->framePtr = savedFramePtr;
return retval;
}
#include <sys/stat.h>
| | > | < < < < | | | > | > > > | > > > | | > > > | | > > | > > > | > > < < | | < < | > > < < < < < < < < | 16804 16805 16806 16807 16808 16809 16810 16811 16812 16813 16814 16815 16816 16817 16818 16819 16820 16821 16822 16823 16824 16825 16826 16827 16828 16829 16830 16831 16832 16833 16834 16835 16836 16837 16838 16839 16840 16841 16842 16843 16844 16845 16846 16847 16848 16849 16850 16851 16852 16853 16854 16855 16856 16857 16858 16859 16860 16861 16862 16863 16864 16865 16866 16867 16868 16869 16870 16871 16872 16873 16874 16875 16876 |
interp->framePtr = savedFramePtr;
return retval;
}
#include <sys/stat.h>
static Jim_Obj *JimReadTextFile(Jim_Interp *interp, const char *filename)
{
jim_stat_t sb;
int fd;
char *buf;
int readlen;
if (Jim_Stat(filename, &sb) == -1 || (fd = open(filename, O_RDONLY | O_TEXT, 0666)) < 0) {
Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", filename, strerror(errno));
return NULL;
}
buf = Jim_Alloc(sb.st_size + 1);
readlen = read(fd, buf, sb.st_size);
close(fd);
if (readlen < 0) {
Jim_Free(buf);
Jim_SetResultFormatted(interp, "failed to load file \"%s\": %s", filename, strerror(errno));
return NULL;
}
else {
Jim_Obj *objPtr;
buf[readlen] = 0;
objPtr = Jim_NewStringObjNoAlloc(interp, buf, readlen);
return objPtr;
}
}
int Jim_EvalFile(Jim_Interp *interp, const char *filename)
{
Jim_Obj *filenameObj;
Jim_Obj *oldFilenameObj;
Jim_Obj *scriptObjPtr;
int retcode;
scriptObjPtr = JimReadTextFile(interp, filename);
if (!scriptObjPtr) {
return JIM_ERR;
}
filenameObj = Jim_NewStringObj(interp, filename, -1);
Jim_SetSourceInfo(interp, scriptObjPtr, filenameObj, 1);
oldFilenameObj = JimPushInterpObj(interp->currentFilenameObj, filenameObj);
retcode = Jim_EvalObj(interp, scriptObjPtr);
JimPopInterpObj(interp, interp->currentFilenameObj, oldFilenameObj);
if (retcode == JIM_RETURN) {
if (--interp->returnLevel <= 0) {
retcode = interp->returnCode;
interp->returnCode = JIM_OK;
interp->returnLevel = 0;
}
}
return retcode;
}
static void JimParseSubst(struct JimParserCtx *pc, int flags)
{
pc->tstart = pc->p;
|
| ︙ | ︙ | |||
15613 15614 15615 15616 15617 15618 15619 |
}
if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
if (JimParseVar(pc) == JIM_OK) {
return;
}
pc->tstart = pc->p;
| | > > | 16888 16889 16890 16891 16892 16893 16894 16895 16896 16897 16898 16899 16900 16901 16902 16903 16904 |
}
if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
if (JimParseVar(pc) == JIM_OK) {
return;
}
pc->tstart = pc->p;
pc->p++;
pc->len--;
}
while (pc->len) {
if (*pc->p == '$' && !(flags & JIM_SUBST_NOVAR)) {
break;
}
if (*pc->p == '[' && !(flags & JIM_SUBST_NOCMD)) {
break;
|
| ︙ | ︙ | |||
15694 15695 15696 15697 15698 15699 15700 |
if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
SetSubstFromAny(interp, objPtr, flags);
return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
}
int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
{
| > > > > | | 16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 16985 16986 16987 16988 16989 |
if (objPtr->typePtr != &scriptObjType || ((ScriptObj *)Jim_GetIntRepPtr(objPtr))->substFlags != flags)
SetSubstFromAny(interp, objPtr, flags);
return (ScriptObj *) Jim_GetIntRepPtr(objPtr);
}
int Jim_SubstObj(Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags)
{
ScriptObj *script;
JimPanic((substObjPtr->refCount == 0, "Jim_SubstObj() called with zero refcount object"));
script = Jim_GetSubst(interp, substObjPtr, flags);
Jim_IncrRefCount(substObjPtr);
script->inUse++;
*resObjPtrPtr = JimInterpolateTokens(interp, script->token, script->len, flags);
script->inUse--;
|
| ︙ | ︙ | |||
15729 15730 15731 15732 15733 15734 15735 |
objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
Jim_DecrRefCount(interp, listObjPtr);
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
}
typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
| | | | > < | < > | | < < | | > > > > > > > > > > > > > | | < | > | > | | | > | | | > | > | > | | > | > > | > | > | | | < | | | | | > > | > > | | > > > > > > > | > > > > > > | > > > > > | | | > > > > > | | | > > > | 17010 17011 17012 17013 17014 17015 17016 17017 17018 17019 17020 17021 17022 17023 17024 17025 17026 17027 17028 17029 17030 17031 17032 17033 17034 17035 17036 17037 17038 17039 17040 17041 17042 17043 17044 17045 17046 17047 17048 17049 17050 17051 17052 17053 17054 17055 17056 17057 17058 17059 17060 17061 17062 17063 17064 17065 17066 17067 17068 17069 17070 17071 17072 17073 17074 17075 17076 17077 17078 17079 17080 17081 17082 17083 17084 17085 17086 17087 17088 17089 17090 17091 17092 17093 17094 17095 17096 17097 17098 17099 17100 17101 17102 17103 17104 17105 17106 17107 17108 17109 17110 17111 17112 17113 17114 17115 17116 17117 17118 17119 17120 17121 17122 17123 17124 17125 17126 17127 17128 17129 17130 17131 17132 17133 17134 17135 17136 17137 17138 17139 17140 17141 17142 17143 17144 17145 17146 17147 17148 17149 17150 17151 17152 17153 17154 17155 17156 17157 17158 17159 17160 17161 17162 17163 17164 17165 17166 17167 17168 17169 17170 17171 17172 17173 17174 17175 17176 17177 17178 17179 17180 17181 17182 17183 17184 17185 17186 17187 17188 17189 17190 17191 17192 17193 |
objPtr = Jim_ListJoin(interp, listObjPtr, " ", 1);
Jim_DecrRefCount(interp, listObjPtr);
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s\"", objPtr);
}
typedef void JimHashtableIteratorCallbackType(Jim_Interp *interp, Jim_Obj *listObjPtr,
Jim_Obj *keyObjPtr, void *value, Jim_Obj *patternObjPtr, int type);
#define JimTrivialMatch(pattern) (strpbrk((pattern), "*[?\\") == NULL)
static Jim_Obj *JimHashtablePatternMatch(Jim_Interp *interp, Jim_HashTable *ht, Jim_Obj *patternObjPtr,
JimHashtableIteratorCallbackType *callback, int type)
{
Jim_HashEntry *he;
Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
if (patternObjPtr && JimTrivialMatch(Jim_String(patternObjPtr))) {
he = Jim_FindHashEntry(ht, patternObjPtr);
if (he) {
callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
patternObjPtr, type);
}
}
else {
Jim_HashTableIterator htiter;
JimInitHashTableIterator(ht, &htiter);
while ((he = Jim_NextHashEntry(&htiter)) != NULL) {
callback(interp, listObjPtr, Jim_GetHashEntryKey(he), Jim_GetHashEntryVal(he),
patternObjPtr, type);
}
}
return listObjPtr;
}
#define JIM_CMDLIST_COMMANDS 0
#define JIM_CMDLIST_PROCS 1
#define JIM_CMDLIST_CHANNELS 2
static void JimCommandMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
{
Jim_Cmd *cmdPtr = (Jim_Cmd *)value;
if (type == JIM_CMDLIST_PROCS && !cmdPtr->isproc) {
return;
}
Jim_IncrRefCount(keyObj);
if (type != JIM_CMDLIST_CHANNELS || Jim_AioFilehandle(interp, keyObj) >= 0) {
int match = 1;
if (patternObj) {
int plen, slen;
const char *pattern = Jim_GetStringNoQualifier(patternObj, &plen);
const char *str = Jim_GetStringNoQualifier(keyObj, &slen);
#ifdef JIM_NO_INTROSPECTION
match = (JimStringCompareUtf8(pattern, plen, str, slen, 0) == 0);
#else
match = JimGlobMatch(pattern, plen, str, slen, 0);
#endif
}
if (match) {
Jim_ListAppendElement(interp, listObjPtr, keyObj);
}
}
Jim_DecrRefCount(interp, keyObj);
}
static Jim_Obj *JimCommandsList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int type)
{
return JimHashtablePatternMatch(interp, &interp->commands, patternObjPtr, JimCommandMatch, type);
}
#define JIM_VARLIST_GLOBALS 0
#define JIM_VARLIST_LOCALS 1
#define JIM_VARLIST_VARS 2
#define JIM_VARLIST_MASK 0x000f
#define JIM_VARLIST_VALUES 0x1000
static void JimVariablesMatch(Jim_Interp *interp, Jim_Obj *listObjPtr,
Jim_Obj *keyObj, void *value, Jim_Obj *patternObj, int type)
{
Jim_VarVal *vv = (Jim_VarVal *)value;
if ((type & JIM_VARLIST_MASK) != JIM_VARLIST_LOCALS || vv->linkFramePtr == NULL) {
if (patternObj == NULL || Jim_StringMatchObj(interp, patternObj, keyObj, 0)) {
Jim_ListAppendElement(interp, listObjPtr, keyObj);
if (type & JIM_VARLIST_VALUES) {
Jim_ListAppendElement(interp, listObjPtr, vv->objPtr);
}
}
}
}
static Jim_Obj *JimVariablesList(Jim_Interp *interp, Jim_Obj *patternObjPtr, int mode)
{
if (mode == JIM_VARLIST_LOCALS && interp->framePtr == interp->topFramePtr) {
return interp->emptyObj;
}
else {
Jim_CallFrame *framePtr = (mode == JIM_VARLIST_GLOBALS) ? interp->topFramePtr : interp->framePtr;
return JimHashtablePatternMatch(interp, &framePtr->vars, patternObjPtr, JimVariablesMatch,
mode);
}
}
static int JimInfoLevel(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
{
long level;
if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
Jim_CallFrame *targetCallFrame = JimGetCallFrameByInteger(interp, level);
if (targetCallFrame && targetCallFrame != interp->topFramePtr) {
#ifdef JIM_NO_INTROSPECTION
*objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, 1);
#else
*objPtrPtr = Jim_NewListObj(interp, targetCallFrame->argv, targetCallFrame->argc);
#endif
return JIM_OK;
}
}
Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
return JIM_ERR;
}
static int JimInfoFrame(Jim_Interp *interp, Jim_Obj *levelObjPtr, Jim_Obj **objPtrPtr)
{
long level;
if (Jim_GetLong(interp, levelObjPtr, &level) == JIM_OK) {
Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, level);
if (frame) {
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "source", -1));
if (frame->scriptObj) {
ScriptObj *script = JimGetScript(interp, frame->scriptObj);
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "line", -1));
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, script->linenr));
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "file", -1));
Jim_ListAppendElement(interp, listObj, script->fileNameObj);
}
#ifndef JIM_NO_INTROSPECTION
{
Jim_Obj *cmdObj = Jim_NewListObj(interp, frame->argv, frame->argc);
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "cmd", -1));
Jim_ListAppendElement(interp, listObj, cmdObj);
}
#endif
{
Jim_Obj *procNameObj = JimProcForEvalFrame(interp, frame);
if (procNameObj) {
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "proc", -1));
Jim_ListAppendElement(interp, listObj, procNameObj);
}
}
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "level", -1));
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, interp->framePtr->level - frame->framePtr->level));
*objPtrPtr = listObj;
return JIM_OK;
}
}
Jim_SetResultFormatted(interp, "bad level \"%#s\"", levelObjPtr);
return JIM_ERR;
}
static int Jim_PutsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 2 && argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "?-nonewline? string");
return JIM_ERR;
|
| ︙ | ︙ | |||
16054 16055 16056 16057 16058 16059 16060 16061 16062 16063 16064 16065 16066 16067 16068 16069 16070 16071 16072 16073 |
while (i < argc) {
if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
&& complain) {
return JIM_ERR;
}
i++;
}
return JIM_OK;
}
static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "condition body");
return JIM_ERR;
}
while (1) {
| > > > > > > > > > > > > | > > > < < > | 17385 17386 17387 17388 17389 17390 17391 17392 17393 17394 17395 17396 17397 17398 17399 17400 17401 17402 17403 17404 17405 17406 17407 17408 17409 17410 17411 17412 17413 17414 17415 17416 17417 17418 17419 17420 17421 17422 17423 17424 17425 17426 17427 17428 17429 17430 17431 17432 17433 17434 17435 17436 17437 17438 17439 17440 17441 17442 17443 17444 17445 17446 17447 17448 17449 17450 17451 17452 17453 17454 17455 |
while (i < argc) {
if (Jim_UnsetVariable(interp, argv[i], complain ? JIM_ERRMSG : JIM_NONE) != JIM_OK
&& complain) {
return JIM_ERR;
}
i++;
}
Jim_SetEmptyResult(interp);
return JIM_OK;
}
static int JimCheckLoopRetcode(Jim_Interp *interp, int retval)
{
if (retval == JIM_BREAK || retval == JIM_CONTINUE) {
if (--interp->break_level > 0) {
return 1;
}
}
return 0;
}
static int Jim_WhileCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "condition body");
return JIM_ERR;
}
while (1) {
int boolean = 0, retval;
if ((retval = Jim_GetBoolFromExpr(interp, argv[1], &boolean)) != JIM_OK)
return retval;
if (!boolean)
break;
if ((retval = Jim_EvalObj(interp, argv[2])) != JIM_OK) {
if (JimCheckLoopRetcode(interp, retval)) {
return retval;
}
switch (retval) {
case JIM_BREAK:
goto out;
case JIM_CONTINUE:
continue;
default:
return retval;
}
}
}
out:
Jim_SetEmptyResult(interp);
return JIM_OK;
}
static int Jim_ForCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retval;
int boolean = 1;
int immediate = 0;
Jim_Obj *varNamePtr = NULL;
Jim_Obj *stopVarNamePtr = NULL;
if (argc != 5) {
Jim_WrongNumArgs(interp, 1, argv, "start test next body");
return JIM_ERR;
}
|
| ︙ | ︙ | |||
16164 16165 16166 16167 16168 16169 16170 |
if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
goto evalstart;
}
if (expr->expr->right->type == JIM_TT_EXPR_INT) {
| | | 17509 17510 17511 17512 17513 17514 17515 17516 17517 17518 17519 17520 17521 17522 17523 |
if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
goto evalstart;
}
if (expr->expr->right->type == JIM_TT_EXPR_INT) {
if (Jim_GetWideExpr(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
goto evalstart;
}
}
else {
stopVarNamePtr = expr->expr->right->objPtr;
Jim_IncrRefCount(stopVarNamePtr);
|
| ︙ | ︙ | |||
16203 16204 16205 16206 16207 16208 16209 16210 16211 16212 16213 16214 16215 16216 |
if (currentVal >= stop + cmpOffset) {
break;
}
retval = Jim_EvalObj(interp, argv[4]);
if (retval == JIM_OK || retval == JIM_CONTINUE) {
retval = JIM_OK;
objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
if (objPtr == NULL) {
| > > > > | 17548 17549 17550 17551 17552 17553 17554 17555 17556 17557 17558 17559 17560 17561 17562 17563 17564 17565 |
if (currentVal >= stop + cmpOffset) {
break;
}
retval = Jim_EvalObj(interp, argv[4]);
if (JimCheckLoopRetcode(interp, retval)) {
immediate++;
goto out;
}
if (retval == JIM_OK || retval == JIM_CONTINUE) {
retval = JIM_OK;
objPtr = Jim_GetVariable(interp, varNamePtr, JIM_ERRMSG);
if (objPtr == NULL) {
|
| ︙ | ︙ | |||
16234 16235 16236 16237 16238 16239 16240 |
}
evalstart:
#endif
while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
retval = Jim_EvalObj(interp, argv[4]);
| > > > | > | | | > | | | > | > > | > > | > > > > | | > > > | 17583 17584 17585 17586 17587 17588 17589 17590 17591 17592 17593 17594 17595 17596 17597 17598 17599 17600 17601 17602 17603 17604 17605 17606 17607 17608 17609 17610 17611 17612 17613 17614 17615 17616 17617 17618 17619 17620 17621 17622 17623 17624 17625 17626 17627 17628 17629 17630 17631 17632 17633 17634 17635 17636 17637 17638 17639 17640 17641 17642 17643 17644 17645 17646 17647 17648 17649 17650 17651 17652 17653 17654 17655 17656 17657 17658 17659 17660 17661 17662 17663 17664 17665 17666 |
}
evalstart:
#endif
while (boolean && (retval == JIM_OK || retval == JIM_CONTINUE)) {
retval = Jim_EvalObj(interp, argv[4]);
if (JimCheckLoopRetcode(interp, retval)) {
immediate++;
break;
}
if (retval == JIM_OK || retval == JIM_CONTINUE) {
JIM_IF_OPTIM(evalnext:)
retval = Jim_EvalObj(interp, argv[3]);
if (retval == JIM_OK || retval == JIM_CONTINUE) {
JIM_IF_OPTIM(testcond:)
retval = Jim_GetBoolFromExpr(interp, argv[2], &boolean);
}
}
}
JIM_IF_OPTIM(out:)
if (stopVarNamePtr) {
Jim_DecrRefCount(interp, stopVarNamePtr);
}
if (varNamePtr) {
Jim_DecrRefCount(interp, varNamePtr);
}
if (!immediate) {
if (retval == JIM_CONTINUE || retval == JIM_BREAK || retval == JIM_OK) {
Jim_SetEmptyResult(interp);
return JIM_OK;
}
}
return retval;
}
static int Jim_LoopCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retval;
jim_wide i;
jim_wide limit;
jim_wide incr = 1;
Jim_Obj *bodyObjPtr;
if (argc < 4 || argc > 6) {
Jim_WrongNumArgs(interp, 1, argv, "var ?first? limit ?incr? body");
return JIM_ERR;
}
retval = Jim_GetWideExpr(interp, argv[2], &i);
if (argc > 4 && retval == JIM_OK) {
retval = Jim_GetWideExpr(interp, argv[3], &limit);
}
if (argc > 5 && retval == JIM_OK) {
Jim_GetWideExpr(interp, argv[4], &incr);
}
if (retval != JIM_OK) {
return retval;
}
if (argc == 4) {
limit = i;
i = 0;
}
bodyObjPtr = argv[argc - 1];
retval = Jim_SetVariable(interp, argv[1], Jim_NewIntObj(interp, i));
while (((i < limit && incr > 0) || (i > limit && incr < 0)) && retval == JIM_OK) {
retval = Jim_EvalObj(interp, bodyObjPtr);
if (JimCheckLoopRetcode(interp, retval)) {
return retval;
}
if (retval == JIM_OK || retval == JIM_CONTINUE) {
Jim_Obj *objPtr = Jim_GetVariable(interp, argv[1], JIM_ERRMSG);
retval = JIM_OK;
i += incr;
|
| ︙ | ︙ | |||
16427 16428 16429 16430 16431 16432 16433 |
result = Jim_SetVariable(interp, varName, valObj);
Jim_DecrRefCount(interp, valObj);
if (result != JIM_OK) {
goto err;
}
}
}
| | > > > > | 17793 17794 17795 17796 17797 17798 17799 17800 17801 17802 17803 17804 17805 17806 17807 17808 17809 17810 17811 |
result = Jim_SetVariable(interp, varName, valObj);
Jim_DecrRefCount(interp, valObj);
if (result != JIM_OK) {
goto err;
}
}
}
result = Jim_EvalObj(interp, script);
if (JimCheckLoopRetcode(interp, result)) {
goto err;
}
switch (result) {
case JIM_OK:
if (doMap) {
Jim_ListAppendElement(interp, resultObj, interp->result);
}
break;
case JIM_CONTINUE:
break;
|
| ︙ | ︙ | |||
16548 16549 16550 16551 16552 16553 16554 |
}
err:
Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
return JIM_ERR;
}
| < | | | > > > > | 17918 17919 17920 17921 17922 17923 17924 17925 17926 17927 17928 17929 17930 17931 17932 17933 17934 17935 17936 17937 17938 17939 17940 17941 17942 17943 17944 17945 17946 17947 17948 17949 17950 17951 17952 17953 17954 17955 17956 17957 17958 17959 17960 17961 17962 17963 17964 |
}
err:
Jim_WrongNumArgs(interp, 1, argv, "condition ?then? trueBody ?elseif ...? ?else? falseBody");
return JIM_ERR;
}
int Jim_CommandMatchObj(Jim_Interp *interp, Jim_Obj *commandObj, Jim_Obj *patternObj,
Jim_Obj *stringObj, int flags)
{
Jim_Obj *parms[5];
int argc = 0;
long eq;
int rc;
parms[argc++] = commandObj;
if (flags & JIM_NOCASE) {
parms[argc++] = Jim_NewStringObj(interp, "-nocase", -1);
}
if (flags & JIM_OPT_END) {
parms[argc++] = Jim_NewStringObj(interp, "--", -1);
}
parms[argc++] = patternObj;
parms[argc++] = stringObj;
rc = Jim_EvalObjVector(interp, argc, parms);
if (rc != JIM_OK || Jim_GetLong(interp, Jim_GetResult(interp), &eq) != JIM_OK) {
eq = -rc;
}
return eq;
}
static int Jim_SwitchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
enum { SWITCH_EXACT, SWITCH_GLOB, SWITCH_RE, SWITCH_CMD };
int matchOpt = SWITCH_EXACT, opt = 1, patCount, i;
int match_flags = 0;
Jim_Obj *command = NULL, *scriptObj = NULL, *strObj;
Jim_Obj **caseList;
if (argc < 3) {
wrongnumargs:
Jim_WrongNumArgs(interp, 1, argv, "?options? string "
"pattern body ... ?default body? or " "{pattern body ?pattern body ...?}");
|
| ︙ | ︙ | |||
16600 16601 16602 16603 16604 16605 16606 |
++opt;
break;
}
else if (strncmp(option, "-exact", 2) == 0)
matchOpt = SWITCH_EXACT;
else if (strncmp(option, "-glob", 2) == 0)
matchOpt = SWITCH_GLOB;
| | > > | 17973 17974 17975 17976 17977 17978 17979 17980 17981 17982 17983 17984 17985 17986 17987 17988 17989 17990 |
++opt;
break;
}
else if (strncmp(option, "-exact", 2) == 0)
matchOpt = SWITCH_EXACT;
else if (strncmp(option, "-glob", 2) == 0)
matchOpt = SWITCH_GLOB;
else if (strncmp(option, "-regexp", 2) == 0) {
matchOpt = SWITCH_RE;
match_flags |= JIM_OPT_END;
}
else if (strncmp(option, "-command", 2) == 0) {
matchOpt = SWITCH_CMD;
if ((argc - opt) < 2)
goto wrongnumargs;
command = argv[++opt];
}
else {
|
| ︙ | ︙ | |||
16644 16645 16646 16647 16648 16649 16650 |
if (Jim_StringMatchObj(interp, patObj, strObj, 0))
scriptObj = caseList[i + 1];
break;
case SWITCH_RE:
command = Jim_NewStringObj(interp, "regexp", -1);
case SWITCH_CMD:{
| | | 18019 18020 18021 18022 18023 18024 18025 18026 18027 18028 18029 18030 18031 18032 18033 |
if (Jim_StringMatchObj(interp, patObj, strObj, 0))
scriptObj = caseList[i + 1];
break;
case SWITCH_RE:
command = Jim_NewStringObj(interp, "regexp", -1);
case SWITCH_CMD:{
int rc = Jim_CommandMatchObj(interp, command, patObj, strObj, match_flags);
if (argc - opt == 1) {
JimListGetElements(interp, argv[opt], &patCount, &caseList);
}
if (rc < 0) {
return -rc;
|
| ︙ | ︙ | |||
16689 16690 16691 16692 16693 16694 16695 |
Jim_SetResult(interp, listObjPtr);
return JIM_OK;
}
static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
| | | < < < < < | < | < | < | < | > | < < < | | | < > > > | | > > > > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > | | | < | < < < < < < | > > > > > > > > > > > > | > > > > > > > | 18064 18065 18066 18067 18068 18069 18070 18071 18072 18073 18074 18075 18076 18077 18078 18079 18080 18081 18082 18083 18084 18085 18086 18087 18088 18089 18090 18091 18092 18093 18094 18095 18096 18097 18098 18099 18100 18101 18102 18103 18104 18105 18106 18107 18108 18109 18110 18111 18112 18113 18114 18115 18116 18117 18118 18119 18120 18121 18122 18123 18124 18125 18126 18127 18128 18129 18130 18131 18132 18133 18134 18135 18136 18137 18138 18139 18140 18141 18142 18143 18144 18145 18146 18147 18148 18149 18150 18151 18152 18153 18154 18155 18156 18157 18158 18159 18160 18161 18162 18163 18164 18165 18166 18167 18168 18169 18170 18171 18172 18173 18174 18175 18176 18177 18178 18179 18180 18181 18182 18183 18184 18185 18186 18187 18188 18189 18190 18191 18192 18193 18194 18195 18196 18197 18198 18199 18200 18201 18202 18203 18204 18205 18206 18207 18208 18209 18210 18211 18212 18213 18214 18215 18216 18217 18218 18219 18220 18221 18222 18223 18224 18225 18226 18227 18228 18229 18230 18231 18232 18233 18234 18235 18236 18237 18238 18239 18240 18241 18242 18243 18244 18245 18246 18247 18248 18249 18250 18251 18252 18253 18254 18255 18256 18257 18258 18259 18260 18261 18262 18263 18264 18265 18266 18267 18268 18269 18270 18271 18272 18273 18274 18275 18276 18277 18278 18279 18280 18281 18282 18283 18284 18285 18286 18287 18288 18289 18290 18291 18292 18293 18294 18295 18296 18297 18298 18299 18300 18301 18302 18303 18304 18305 18306 18307 18308 18309 18310 18311 18312 18313 18314 18315 18316 18317 18318 18319 18320 18321 18322 18323 18324 18325 18326 18327 |
Jim_SetResult(interp, listObjPtr);
return JIM_OK;
}
static int Jim_LindexCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
int ret;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "list ?index ...?");
return JIM_ERR;
}
ret = Jim_ListIndices(interp, argv[1], argv + 2, argc - 2, &objPtr, JIM_NONE);
if (ret < 0) {
ret = JIM_OK;
Jim_SetEmptyResult(interp);
}
else if (ret == JIM_OK) {
Jim_SetResult(interp, objPtr);
}
return ret;
}
static int Jim_LlengthCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "list");
return JIM_ERR;
}
Jim_SetResultInt(interp, Jim_ListLength(interp, argv[1]));
return JIM_OK;
}
static int Jim_LsearchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
static const char * const options[] = {
"-bool", "-not", "-nocase", "-exact", "-glob", "-regexp", "-all", "-inline", "-command",
"-stride", "-index", NULL
};
enum
{ OPT_BOOL, OPT_NOT, OPT_NOCASE, OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_ALL, OPT_INLINE,
OPT_COMMAND, OPT_STRIDE, OPT_INDEX };
int i;
int opt_bool = 0;
int opt_not = 0;
int opt_all = 0;
int opt_inline = 0;
int opt_match = OPT_EXACT;
int listlen;
int rc = JIM_OK;
Jim_Obj *listObjPtr = NULL;
Jim_Obj *commandObj = NULL;
Jim_Obj *indexObj = NULL;
int match_flags = 0;
long stride = 1;
if (argc < 3) {
wrongargs:
Jim_WrongNumArgs(interp, 1, argv,
"?-exact|-glob|-regexp|-command 'command'? ?-bool|-inline? ?-not? ?-nocase? ?-all? ?-stride len? ?-index val? list value");
return JIM_ERR;
}
for (i = 1; i < argc - 2; i++) {
int option;
if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
switch (option) {
case OPT_BOOL:
opt_bool = 1;
opt_inline = 0;
break;
case OPT_NOT:
opt_not = 1;
break;
case OPT_NOCASE:
match_flags |= JIM_NOCASE;
break;
case OPT_INLINE:
opt_inline = 1;
opt_bool = 0;
break;
case OPT_ALL:
opt_all = 1;
break;
case OPT_REGEXP:
opt_match = option;
match_flags |= JIM_OPT_END;
break;
case OPT_COMMAND:
if (i >= argc - 2) {
goto wrongargs;
}
commandObj = argv[++i];
case OPT_EXACT:
case OPT_GLOB:
opt_match = option;
break;
case OPT_INDEX:
if (i >= argc - 2) {
goto wrongargs;
}
indexObj = argv[++i];
break;
case OPT_STRIDE:
if (i >= argc - 2) {
goto wrongargs;
}
if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
return JIM_ERR;
}
if (stride < 1) {
Jim_SetResultString(interp, "stride length must be at least 1", -1);
return JIM_ERR;
}
break;
}
}
argc -= i;
if (argc < 2) {
goto wrongargs;
}
argv += i;
listlen = Jim_ListLength(interp, argv[0]);
if (listlen % stride) {
Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
return JIM_ERR;
}
if (opt_all) {
listObjPtr = Jim_NewListObj(interp, NULL, 0);
}
if (opt_match == OPT_REGEXP) {
commandObj = Jim_NewStringObj(interp, "regexp", -1);
}
if (commandObj) {
Jim_IncrRefCount(commandObj);
}
for (i = 0; i < listlen; i += stride) {
int eq = 0;
Jim_Obj *searchListObj;
Jim_Obj *objPtr;
int offset;
if (indexObj) {
int indexlen = Jim_ListLength(interp, indexObj);
if (stride == 1) {
searchListObj = Jim_ListGetIndex(interp, argv[0], i);
}
else {
searchListObj = Jim_NewListObj(interp, argv[0]->internalRep.listValue.ele + i, stride);
}
Jim_IncrRefCount(searchListObj);
rc = Jim_ListIndices(interp, searchListObj, indexObj->internalRep.listValue.ele, indexlen, &objPtr, JIM_ERRMSG);
if (rc != JIM_OK) {
Jim_DecrRefCount(interp, searchListObj);
rc = JIM_ERR;
goto done;
}
offset = 0;
}
else {
searchListObj = argv[0];
offset = i;
objPtr = Jim_ListGetIndex(interp, searchListObj, i);
Jim_IncrRefCount(searchListObj);
}
switch (opt_match) {
case OPT_EXACT:
eq = Jim_StringCompareObj(interp, argv[1], objPtr, match_flags) == 0;
break;
case OPT_GLOB:
eq = Jim_StringMatchObj(interp, argv[1], objPtr, match_flags);
break;
case OPT_REGEXP:
case OPT_COMMAND:
eq = Jim_CommandMatchObj(interp, commandObj, argv[1], objPtr, match_flags);
if (eq < 0) {
Jim_DecrRefCount(interp, searchListObj);
rc = JIM_ERR;
goto done;
}
break;
}
if ((!opt_bool && eq == !opt_not) || (opt_bool && (eq || opt_all))) {
Jim_Obj *resultObj;
if (opt_bool) {
resultObj = Jim_NewIntObj(interp, eq ^ opt_not);
}
else if (!opt_inline) {
resultObj = Jim_NewIntObj(interp, i);
}
else if (stride == 1) {
resultObj = objPtr;
}
else if (opt_all) {
ListInsertElements(listObjPtr, -1, stride,
searchListObj->internalRep.listValue.ele + offset);
resultObj = NULL;
}
else {
resultObj = Jim_NewListObj(interp, searchListObj->internalRep.listValue.ele + offset, stride);
}
if (opt_all) {
if (stride == 1) {
Jim_ListAppendElement(interp, listObjPtr, resultObj);
}
}
else {
Jim_SetResult(interp, resultObj);
Jim_DecrRefCount(interp, searchListObj);
goto done;
}
}
Jim_DecrRefCount(interp, searchListObj);
}
if (opt_all) {
Jim_SetResult(interp, listObjPtr);
listObjPtr = NULL;
}
else {
if (opt_bool) {
Jim_SetResultBool(interp, opt_not);
}
else if (!opt_inline) {
Jim_SetResultInt(interp, -1);
}
}
done:
if (listObjPtr) {
Jim_FreeNewObj(interp, listObjPtr);
}
if (commandObj) {
Jim_DecrRefCount(interp, commandObj);
}
return rc;
}
|
| ︙ | ︙ | |||
16991 16992 16993 16994 16995 16996 16997 |
return JIM_OK;
}
static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc < 3) {
| | | > | | > > > > > > | > > > | 18427 18428 18429 18430 18431 18432 18433 18434 18435 18436 18437 18438 18439 18440 18441 18442 18443 18444 18445 18446 18447 18448 18449 18450 18451 18452 18453 18454 18455 18456 18457 18458 18459 18460 18461 18462 18463 18464 18465 18466 18467 18468 18469 18470 18471 18472 18473 18474 18475 18476 18477 18478 18479 18480 18481 18482 18483 18484 18485 18486 18487 18488 18489 18490 18491 18492 18493 18494 18495 18496 18497 18498 18499 18500 |
return JIM_OK;
}
static int Jim_LsetCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc < 3) {
Jim_WrongNumArgs(interp, 1, argv, "listVar ?index ...? value");
return JIM_ERR;
}
else if (argc == 3) {
if (Jim_SetVariable(interp, argv[1], argv[2]) != JIM_OK)
return JIM_ERR;
Jim_SetResult(interp, argv[2]);
return JIM_OK;
}
return Jim_ListSetIndex(interp, argv[1], argv + 2, argc - 3, argv[argc - 1]);
}
static int Jim_LsortCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const argv[])
{
static const char * const options[] = {
"-ascii", "-nocase", "-increasing", "-decreasing", "-command", "-integer", "-real", "-index", "-unique",
"-stride", "-dictionary", NULL
};
enum {
OPT_ASCII, OPT_NOCASE, OPT_INCREASING, OPT_DECREASING, OPT_COMMAND, OPT_INTEGER, OPT_REAL, OPT_INDEX, OPT_UNIQUE,
OPT_STRIDE, OPT_DICT
};
Jim_Obj *resObj;
int i;
int retCode;
int shared;
long stride = 1;
Jim_Obj **elements;
int listlen;
struct lsort_info info;
if (argc < 2) {
wrongargs:
Jim_WrongNumArgs(interp, 1, argv, "?options? list");
return JIM_ERR;
}
info.type = JIM_LSORT_ASCII;
info.order = 1;
info.indexc = 0;
info.unique = 0;
info.command = NULL;
info.interp = interp;
for (i = 1; i < (argc - 1); i++) {
int option;
if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG)
!= JIM_OK)
return JIM_ERR;
switch (option) {
case OPT_ASCII:
info.type = JIM_LSORT_ASCII;
break;
case OPT_DICT:
info.type = JIM_LSORT_DICT;
break;
case OPT_NOCASE:
info.type = JIM_LSORT_NOCASE;
break;
case OPT_INTEGER:
info.type = JIM_LSORT_INTEGER;
break;
case OPT_REAL:
|
| ︙ | ︙ | |||
17067 17068 17069 17070 17071 17072 17073 17074 17075 17076 17077 17078 17079 |
if (i >= (argc - 2)) {
Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
return JIM_ERR;
}
info.type = JIM_LSORT_COMMAND;
info.command = argv[i + 1];
i++;
break;
case OPT_INDEX:
if (i >= (argc - 2)) {
Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
return JIM_ERR;
}
| > > > > > > > > > > > > > | | > < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | | | | | | > | 18513 18514 18515 18516 18517 18518 18519 18520 18521 18522 18523 18524 18525 18526 18527 18528 18529 18530 18531 18532 18533 18534 18535 18536 18537 18538 18539 18540 18541 18542 18543 18544 18545 18546 18547 18548 18549 18550 18551 18552 18553 18554 18555 18556 18557 18558 18559 18560 18561 18562 18563 18564 18565 18566 18567 18568 18569 18570 18571 18572 18573 18574 18575 18576 18577 18578 18579 18580 18581 18582 18583 18584 18585 18586 18587 18588 18589 18590 18591 18592 18593 18594 18595 18596 18597 |
if (i >= (argc - 2)) {
Jim_SetResultString(interp, "\"-command\" option must be followed by comparison command", -1);
return JIM_ERR;
}
info.type = JIM_LSORT_COMMAND;
info.command = argv[i + 1];
i++;
break;
case OPT_STRIDE:
if (i >= argc - 2) {
goto wrongargs;
}
if (Jim_GetLong(interp, argv[++i], &stride) != JIM_OK) {
return JIM_ERR;
}
if (stride < 2) {
Jim_SetResultString(interp, "stride length must be at least 2", -1);
return JIM_ERR;
}
break;
case OPT_INDEX:
if (i >= (argc - 2)) {
badindex:
Jim_SetResultString(interp, "\"-index\" option must be followed by list index", -1);
return JIM_ERR;
}
JimListGetElements(interp, argv[i + 1], &info.indexc, &info.indexv);
if (info.indexc == 0) {
goto badindex;
}
i++;
break;
}
}
resObj = argv[argc - 1];
JimListGetElements(interp, resObj, &listlen, &elements);
if (listlen <= 1) {
Jim_SetResult(interp, resObj);
return JIM_OK;
}
if (stride > 1) {
Jim_Obj *tmpListObj;
int i;
if (listlen % stride) {
Jim_SetResultString(interp, "list size must be a multiple of the stride length", -1);
return JIM_ERR;
}
tmpListObj = Jim_NewListObj(interp, NULL, 0);
Jim_IncrRefCount(tmpListObj);
for (i = 0; i < listlen; i += stride) {
Jim_ListAppendElement(interp, tmpListObj, Jim_NewListObj(interp, elements + i, stride));
}
retCode = ListSortElements(interp, tmpListObj, &info);
if (retCode == JIM_OK) {
resObj = Jim_NewListObj(interp, NULL, 0);
for (i = 0; i < listlen; i += stride) {
Jim_ListAppendList(interp, resObj, Jim_ListGetIndex(interp, tmpListObj, i / stride));
}
Jim_SetResult(interp, resObj);
}
Jim_DecrRefCount(interp, tmpListObj);
}
else {
if ((shared = Jim_IsShared(resObj))) {
resObj = Jim_DuplicateObj(interp, resObj);
}
retCode = ListSortElements(interp, resObj, &info);
if (retCode == JIM_OK) {
Jim_SetResult(interp, resObj);
}
else if (shared) {
Jim_FreeNewObj(interp, resObj);
}
}
return retCode;
}
static int Jim_AppendCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
|
| ︙ | ︙ | |||
17137 17138 17139 17140 17141 17142 17143 |
}
Jim_SetResult(interp, stringObjPtr);
return JIM_OK;
}
| < < < < < < < < < < < | 18631 18632 18633 18634 18635 18636 18637 18638 18639 18640 18641 18642 18643 18644 18645 18646 18647 18648 18649 18650 18651 18652 18653 18654 18655 18656 18657 18658 18659 18660 18661 18662 |
}
Jim_SetResult(interp, stringObjPtr);
return JIM_OK;
}
static int Jim_EvalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int rc;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "arg ?arg ...?");
return JIM_ERR;
}
if (argc == 2) {
rc = Jim_EvalObj(interp, argv[1]);
}
else {
rc = Jim_EvalObj(interp, Jim_ConcatObj(interp, argc - 1, argv + 1));
}
return rc;
}
static int Jim_UplevelCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc >= 2) {
|
| ︙ | ︙ | |||
17222 17223 17224 17225 17226 17227 17228 17229 17230 17231 17232 17233 17234 17235 17236 17237 17238 17239 17240 |
static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retcode;
if (argc == 2) {
retcode = Jim_EvalExpression(interp, argv[1]);
}
else if (argc > 2) {
Jim_Obj *objPtr;
objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
Jim_IncrRefCount(objPtr);
retcode = Jim_EvalExpression(interp, objPtr);
Jim_DecrRefCount(interp, objPtr);
}
else {
Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
return JIM_ERR;
}
| > > > > > > | | > > > > > > | > > > > > > > > > > < < | < < > > > > > > > > > > > | | | | > > > | > > > > > > > > > > > > > | 18705 18706 18707 18708 18709 18710 18711 18712 18713 18714 18715 18716 18717 18718 18719 18720 18721 18722 18723 18724 18725 18726 18727 18728 18729 18730 18731 18732 18733 18734 18735 18736 18737 18738 18739 18740 18741 18742 18743 18744 18745 18746 18747 18748 18749 18750 18751 18752 18753 18754 18755 18756 18757 18758 18759 18760 18761 18762 18763 18764 18765 18766 18767 18768 18769 18770 18771 18772 18773 18774 18775 18776 18777 18778 18779 18780 18781 18782 18783 18784 18785 18786 18787 18788 18789 18790 18791 18792 18793 18794 18795 18796 18797 18798 |
static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retcode;
if (argc == 2) {
retcode = Jim_EvalExpression(interp, argv[1]);
}
#ifndef JIM_COMPAT
else {
Jim_WrongNumArgs(interp, 1, argv, "expression");
retcode = JIM_ERR;
}
#else
else if (argc > 2) {
Jim_Obj *objPtr;
objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
Jim_IncrRefCount(objPtr);
retcode = Jim_EvalExpression(interp, objPtr);
Jim_DecrRefCount(interp, objPtr);
}
else {
Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
return JIM_ERR;
}
#endif
return retcode;
}
static int JimBreakContinueHelper(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int retcode)
{
if (argc != 1 && argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "?level?");
return JIM_ERR;
}
if (argc == 2) {
long level;
int ret = Jim_GetLong(interp, argv[1], &level);
if (ret != JIM_OK) {
return ret;
}
interp->break_level = level;
}
return retcode;
}
static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimBreakContinueHelper(interp, argc, argv, JIM_BREAK);
}
static int Jim_ContinueCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimBreakContinueHelper(interp, argc, argv, JIM_CONTINUE);
}
static int Jim_StacktraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *listObj;
int i;
jim_wide skip = 0;
jim_wide last = 0;
if (argc > 1) {
if (Jim_GetWideExpr(interp, argv[1], &skip) != JIM_OK) {
return JIM_ERR;
}
}
if (argc > 2) {
if (Jim_GetWideExpr(interp, argv[2], &last) != JIM_OK) {
return JIM_ERR;
}
}
listObj = Jim_NewListObj(interp, NULL, 0);
for (i = skip; i <= interp->procLevel; i++) {
Jim_EvalFrame *frame = JimGetEvalFrameByProcLevel(interp, -i);
if (frame->procLevel < last) {
break;
}
JimAddStackFrame(interp, frame, listObj);
}
Jim_SetResult(interp, listObj);
return JIM_OK;
}
static int Jim_ReturnCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int i;
Jim_Obj *stackTraceObj = NULL;
|
| ︙ | ︙ | |||
17310 17311 17312 17313 17314 17315 17316 |
}
interp->returnCode = returnCode;
interp->returnLevel = level;
if (i == argc - 1) {
Jim_SetResult(interp, argv[i]);
}
| | | 18838 18839 18840 18841 18842 18843 18844 18845 18846 18847 18848 18849 18850 18851 18852 |
}
interp->returnCode = returnCode;
interp->returnLevel = level;
if (i == argc - 1) {
Jim_SetResult(interp, argv[i]);
}
return level == 0 ? returnCode : JIM_RETURN;
}
static int Jim_TailcallCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (interp->framePtr->level == 0) {
Jim_SetResultString(interp, "tailcall can only be called from a proc or lambda", -1);
|
| ︙ | ︙ | |||
17368 17369 17370 17371 17372 17373 17374 |
Jim_Obj *prefixListObj = privData;
Jim_DecrRefCount(interp, prefixListObj);
}
static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *prefixListObj;
| < < < < < < < | < < < < < | < | | | < > > > > > > > > > > > > > > > > > > > > > | 18896 18897 18898 18899 18900 18901 18902 18903 18904 18905 18906 18907 18908 18909 18910 18911 18912 18913 18914 18915 18916 18917 18918 18919 18920 18921 18922 18923 18924 18925 18926 18927 18928 18929 18930 18931 18932 18933 18934 18935 18936 18937 18938 18939 18940 18941 18942 18943 18944 18945 18946 18947 18948 18949 18950 18951 18952 18953 18954 18955 18956 18957 18958 18959 18960 18961 18962 18963 18964 18965 18966 18967 18968 18969 18970 18971 18972 18973 18974 18975 18976 |
Jim_Obj *prefixListObj = privData;
Jim_DecrRefCount(interp, prefixListObj);
}
static int Jim_AliasCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *prefixListObj;
if (argc < 3) {
Jim_WrongNumArgs(interp, 1, argv, "newname command ?args ...?");
return JIM_ERR;
}
prefixListObj = Jim_NewListObj(interp, argv + 2, argc - 2);
Jim_IncrRefCount(prefixListObj);
Jim_SetResult(interp, argv[1]);
return Jim_CreateCommandObj(interp, argv[1], JimAliasCmd, prefixListObj, JimAliasCmdDelete);
}
static int Jim_ProcCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Cmd *cmd;
if (argc != 4 && argc != 5) {
Jim_WrongNumArgs(interp, 1, argv, "name arglist ?statics? body");
return JIM_ERR;
}
if (argc == 4) {
cmd = JimCreateProcedureCmd(interp, argv[2], NULL, argv[3], NULL);
}
else {
cmd = JimCreateProcedureCmd(interp, argv[2], argv[3], argv[4], NULL);
}
if (cmd) {
Jim_Obj *nameObjPtr = JimQualifyName(interp, argv[1]);
JimCreateCommand(interp, nameObjPtr, cmd);
JimUpdateProcNamespace(interp, cmd, nameObjPtr);
Jim_DecrRefCount(interp, nameObjPtr);
Jim_SetResult(interp, argv[1]);
return JIM_OK;
}
return JIM_ERR;
}
static int Jim_XtraceCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "callback");
return JIM_ERR;
}
if (interp->traceCmdObj) {
Jim_DecrRefCount(interp, interp->traceCmdObj);
interp->traceCmdObj = NULL;
}
if (Jim_Length(argv[1])) {
interp->traceCmdObj = argv[1];
Jim_IncrRefCount(interp->traceCmdObj);
}
return JIM_OK;
}
static int Jim_LocalCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int retcode;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "cmd ?args ...?");
|
| ︙ | ︙ | |||
17516 17517 17518 17519 17520 17521 17522 |
Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
return JIM_ERR;
}
if (len == 3) {
#ifdef jim_ext_namespace
| | | 19051 19052 19053 19054 19055 19056 19057 19058 19059 19060 19061 19062 19063 19064 19065 |
Jim_SetResultFormatted(interp, "can't interpret \"%#s\" as a lambda expression", argv[1]);
return JIM_ERR;
}
if (len == 3) {
#ifdef jim_ext_namespace
nsObj = Jim_ListGetIndex(interp, argv[1], 2);
#else
Jim_SetResultString(interp, "namespaces not enabled", -1);
return JIM_ERR;
#endif
}
argListObjPtr = Jim_ListGetIndex(interp, argv[1], 0);
bodyObjPtr = Jim_ListGetIndex(interp, argv[1], 1);
|
| ︙ | ︙ | |||
17639 17640 17641 17642 17643 17644 17645 |
eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
k = Jim_String(eachObjPtr);
kl = Jim_Utf8Length(interp, eachObjPtr);
if (strLen >= kl && kl) {
int rc;
| | | 19174 19175 19176 19177 19178 19179 19180 19181 19182 19183 19184 19185 19186 19187 19188 |
eachObjPtr = Jim_ListGetIndex(interp, mapListObjPtr, i);
k = Jim_String(eachObjPtr);
kl = Jim_Utf8Length(interp, eachObjPtr);
if (strLen >= kl && kl) {
int rc;
rc = JimStringCompareUtf8(str, kl, k, kl, nocase);
if (rc == 0) {
if (noMatchStart) {
Jim_AppendString(interp, resultObjPtr, noMatchStart, str - noMatchStart);
noMatchStart = NULL;
}
Jim_AppendObj(interp, resultObjPtr, Jim_ListGetIndex(interp, mapListObjPtr, i + 1));
str += utf8_index(str, kl);
|
| ︙ | ︙ | |||
17672 17673 17674 17675 17676 17677 17678 |
static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int len;
int opt_case = 1;
int option;
| | < < | > | > | > | > > > > > > > > > | > > > > > > > > | > > > | > > > | < | > > > > | > > > > > > > > > > > > > | | < > | > > | | < < | | | | < < < < < | | 19207 19208 19209 19210 19211 19212 19213 19214 19215 19216 19217 19218 19219 19220 19221 19222 19223 19224 19225 19226 19227 19228 19229 19230 19231 19232 19233 19234 19235 19236 19237 19238 19239 19240 19241 19242 19243 19244 19245 19246 19247 19248 19249 19250 19251 19252 19253 19254 19255 19256 19257 19258 19259 19260 19261 19262 19263 19264 19265 19266 19267 19268 19269 19270 19271 19272 19273 19274 19275 19276 19277 19278 19279 19280 19281 19282 19283 19284 19285 19286 19287 19288 19289 19290 19291 19292 19293 19294 19295 |
static int Jim_StringCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
int len;
int opt_case = 1;
int option;
static const char * const nocase_options[] = {
"-nocase", NULL
};
static const char * const nocase_length_options[] = {
"-nocase", "-length", NULL
};
enum {
OPT_BYTELENGTH,
OPT_BYTERANGE,
OPT_CAT,
OPT_COMPARE,
OPT_EQUAL,
OPT_FIRST,
OPT_INDEX,
OPT_IS,
OPT_LAST,
OPT_LENGTH,
OPT_MAP,
OPT_MATCH,
OPT_RANGE,
OPT_REPEAT,
OPT_REPLACE,
OPT_REVERSE,
OPT_TOLOWER,
OPT_TOTITLE,
OPT_TOUPPER,
OPT_TRIM,
OPT_TRIMLEFT,
OPT_TRIMRIGHT,
OPT_COUNT
};
static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
JIM_DEF_SUBCMD("bytelength", "string", 1, 1),
JIM_DEF_SUBCMD("byterange", "string first last", 3, 3),
JIM_DEF_SUBCMD("cat", "?...?", 0, -1),
JIM_DEF_SUBCMD("compare", "?-nocase? ?-length int? string1 string2", 2, 5),
JIM_DEF_SUBCMD("equal", "?-nocase? ?-length int? string1 string2", 2, 5),
JIM_DEF_SUBCMD("first", "subString string ?index?", 2, 3),
JIM_DEF_SUBCMD("index", "string index", 2, 2),
JIM_DEF_SUBCMD("is", "class ?-strict? str", 2, 3),
JIM_DEF_SUBCMD("last", "subString string ?index?", 2, 3),
JIM_DEF_SUBCMD("length","string", 1, 1),
JIM_DEF_SUBCMD("map", "?-nocase? mapList string", 2, 3),
JIM_DEF_SUBCMD("match", "?-nocase? pattern string", 2, 3),
JIM_DEF_SUBCMD("range", "string first last", 3, 3),
JIM_DEF_SUBCMD("repeat", "string count", 2, 2),
JIM_DEF_SUBCMD("replace", "string first last ?string?", 3, 4),
JIM_DEF_SUBCMD("reverse", "string", 1, 1),
JIM_DEF_SUBCMD("tolower", "string", 1, 1),
JIM_DEF_SUBCMD("totitle", "string", 1, 1),
JIM_DEF_SUBCMD("toupper", "string", 1, 1),
JIM_DEF_SUBCMD("trim", "string ?trimchars?", 1, 2),
JIM_DEF_SUBCMD("trimleft", "string ?trimchars?", 1, 2),
JIM_DEF_SUBCMD("trimright", "string ?trimchars?", 1, 2),
{ }
};
const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
if (!ct) {
return JIM_ERR;
}
if (ct->function) {
return ct->function(interp, argc, argv);
}
option = ct - cmds;
switch (option) {
case OPT_LENGTH:
Jim_SetResultInt(interp, Jim_Utf8Length(interp, argv[2]));
return JIM_OK;
case OPT_BYTELENGTH:
Jim_SetResultInt(interp, Jim_Length(argv[2]));
return JIM_OK;
case OPT_CAT:{
Jim_Obj *objPtr;
if (argc == 3) {
objPtr = argv[2];
|
| ︙ | ︙ | |||
17745 17746 17747 17748 17749 17750 17751 |
int n = argc - 4;
int i = 2;
while (n > 0) {
int subopt;
if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
JIM_ENUM_ABBREV) != JIM_OK) {
badcompareargs:
| | | 19315 19316 19317 19318 19319 19320 19321 19322 19323 19324 19325 19326 19327 19328 19329 |
int n = argc - 4;
int i = 2;
while (n > 0) {
int subopt;
if (Jim_GetEnum(interp, argv[i++], nocase_length_options, &subopt, NULL,
JIM_ENUM_ABBREV) != JIM_OK) {
badcompareargs:
Jim_SubCmdArgError(interp, ct, argv[0]);
return JIM_ERR;
}
if (subopt == 0) {
opt_case = 0;
n--;
}
|
| ︙ | ︙ | |||
17773 17774 17775 17776 17777 17778 17779 17780 |
}
argv += argc - 2;
if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
}
else {
if (opt_length >= 0) {
| > > > > > | | < | > | > > | 19343 19344 19345 19346 19347 19348 19349 19350 19351 19352 19353 19354 19355 19356 19357 19358 19359 19360 19361 19362 19363 19364 19365 19366 19367 19368 19369 |
}
argv += argc - 2;
if (opt_length < 0 && option != OPT_COMPARE && opt_case) {
Jim_SetResultBool(interp, Jim_StringEqObj(argv[0], argv[1]));
}
else {
const char *s1 = Jim_String(argv[0]);
int l1 = Jim_Utf8Length(interp, argv[0]);
const char *s2 = Jim_String(argv[1]);
int l2 = Jim_Utf8Length(interp, argv[1]);
if (opt_length >= 0) {
if (l1 > opt_length) {
l1 = opt_length;
}
if (l2 > opt_length) {
l2 = opt_length;
}
}
n = JimStringCompareUtf8(s1, l1, s2, l2, !opt_case);
Jim_SetResultInt(interp, option == OPT_COMPARE ? n : n == 0);
}
return JIM_OK;
}
case OPT_MATCH:
if (argc != 4 &&
|
| ︙ | ︙ | |||
17820 17821 17822 17823 17824 17825 17826 |
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
| | < | | < < < | > | < | > | < < < < < < < < | < < < < | < < < < < > < < < < < | < < < < < > > > < < < | < < | < < < < < | | < > | | < | < < < < < < < < > | < > | < | | > | < | < < | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > | 19397 19398 19399 19400 19401 19402 19403 19404 19405 19406 19407 19408 19409 19410 19411 19412 19413 19414 19415 19416 19417 19418 19419 19420 19421 19422 19423 19424 19425 19426 19427 19428 19429 19430 19431 19432 19433 19434 19435 19436 19437 19438 19439 19440 19441 19442 19443 19444 19445 19446 19447 19448 19449 19450 19451 19452 19453 19454 19455 19456 19457 19458 19459 19460 19461 19462 19463 19464 19465 19466 19467 19468 19469 19470 19471 19472 19473 19474 19475 19476 19477 19478 19479 19480 19481 19482 19483 19484 19485 19486 19487 19488 19489 19490 19491 19492 19493 19494 19495 19496 19497 19498 19499 19500 19501 19502 19503 19504 19505 19506 19507 19508 19509 19510 19511 19512 19513 19514 19515 19516 19517 19518 19519 19520 19521 19522 19523 19524 19525 19526 19527 19528 19529 19530 19531 19532 19533 19534 19535 19536 19537 19538 19539 19540 19541 19542 19543 19544 19545 19546 19547 19548 19549 19550 19551 19552 19553 19554 19555 19556 19557 19558 19559 19560 19561 19562 19563 19564 19565 19566 19567 19568 19569 19570 19571 19572 19573 19574 19575 19576 19577 19578 19579 19580 19581 19582 19583 19584 19585 19586 19587 19588 19589 19590 19591 19592 19593 19594 19595 19596 19597 19598 19599 19600 19601 19602 19603 19604 19605 19606 19607 19608 19609 19610 19611 19612 19613 19614 19615 19616 19617 19618 19619 19620 19621 19622 19623 19624 19625 19626 19627 19628 19629 19630 19631 19632 19633 19634 19635 19636 19637 19638 19639 19640 19641 19642 19643 19644 19645 19646 19647 19648 19649 19650 19651 19652 19653 19654 19655 19656 19657 19658 19659 19660 19661 19662 19663 19664 19665 19666 19667 19668 19669 19670 19671 19672 19673 19674 19675 19676 19677 19678 19679 19680 19681 19682 19683 19684 19685 19686 19687 19688 19689 19690 19691 19692 19693 19694 19695 19696 19697 19698 19699 19700 19701 19702 19703 19704 19705 19706 19707 19708 19709 19710 19711 19712 19713 19714 19715 19716 19717 19718 19719 19720 19721 19722 19723 19724 19725 19726 |
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_RANGE:{
Jim_Obj *objPtr = Jim_StringRangeObj(interp, argv[2], argv[3], argv[4]);
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_BYTERANGE:{
Jim_Obj *objPtr = Jim_StringByteRangeObj(interp, argv[2], argv[3], argv[4]);
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_REPLACE:{
Jim_Obj *objPtr = JimStringReplaceObj(interp, argv[2], argv[3], argv[4], argc == 6 ? argv[5] : NULL);
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_REPEAT:{
Jim_Obj *objPtr;
jim_wide count;
if (Jim_GetWideExpr(interp, argv[3], &count) != JIM_OK) {
return JIM_ERR;
}
objPtr = Jim_NewStringObj(interp, "", 0);
if (count > 0) {
while (count--) {
Jim_AppendObj(interp, objPtr, argv[2]);
}
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
case OPT_REVERSE:{
char *buf, *p;
const char *str;
int i;
str = Jim_GetString(argv[2], &len);
buf = Jim_Alloc(len + 1);
assert(buf);
p = buf + len;
*p = 0;
for (i = 0; i < len; ) {
int c;
int l = utf8_tounicode(str, &c);
memcpy(p - l, str, l);
p -= l;
i += l;
str += l;
}
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
return JIM_OK;
}
case OPT_INDEX:{
int idx;
const char *str;
if (Jim_GetIndex(interp, argv[3], &idx) != JIM_OK) {
return JIM_ERR;
}
str = Jim_String(argv[2]);
len = Jim_Utf8Length(interp, argv[2]);
idx = JimRelToAbsIndex(len, idx);
if (idx < 0 || idx >= len || str == NULL) {
Jim_SetResultString(interp, "", 0);
}
else if (len == Jim_Length(argv[2])) {
Jim_SetResultString(interp, str + idx, 1);
}
else {
int c;
int i = utf8_index(str, idx);
Jim_SetResultString(interp, str + i, utf8_tounicode(str + i, &c));
}
return JIM_OK;
}
case OPT_FIRST:
case OPT_LAST:{
int idx = 0, l1, l2;
const char *s1, *s2;
s1 = Jim_String(argv[2]);
s2 = Jim_String(argv[3]);
l1 = Jim_Utf8Length(interp, argv[2]);
l2 = Jim_Utf8Length(interp, argv[3]);
if (argc == 5) {
if (Jim_GetIndex(interp, argv[4], &idx) != JIM_OK) {
return JIM_ERR;
}
idx = JimRelToAbsIndex(l2, idx);
if (idx < 0) {
idx = 0;
}
}
else if (option == OPT_LAST) {
idx = l2;
}
if (option == OPT_FIRST) {
Jim_SetResultInt(interp, JimStringFirst(s1, l1, s2, l2, idx));
}
else {
#ifdef JIM_UTF8
Jim_SetResultInt(interp, JimStringLastUtf8(s1, l1, s2, idx));
#else
Jim_SetResultInt(interp, JimStringLast(s1, l1, s2, idx));
#endif
}
return JIM_OK;
}
case OPT_TRIM:
Jim_SetResult(interp, JimStringTrim(interp, argv[2], argc == 4 ? argv[3] : NULL));
return JIM_OK;
case OPT_TRIMLEFT:
Jim_SetResult(interp, JimStringTrimLeft(interp, argv[2], argc == 4 ? argv[3] : NULL));
return JIM_OK;
case OPT_TRIMRIGHT:{
Jim_SetResult(interp, JimStringTrimRight(interp, argv[2], argc == 4 ? argv[3] : NULL));
return JIM_OK;
}
case OPT_TOLOWER:
Jim_SetResult(interp, JimStringToLower(interp, argv[2]));
return JIM_OK;
case OPT_TOUPPER:
Jim_SetResult(interp, JimStringToUpper(interp, argv[2]));
return JIM_OK;
case OPT_TOTITLE:
Jim_SetResult(interp, JimStringToTitle(interp, argv[2]));
return JIM_OK;
case OPT_IS:
if (argc == 5 && !Jim_CompareStringImmediate(interp, argv[3], "-strict")) {
Jim_SubCmdArgError(interp, ct, argv[0]);
return JIM_ERR;
}
return JimStringIs(interp, argv[argc - 1], argv[2], argc == 5);
}
return JIM_OK;
}
static int Jim_TimeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
long i, count = 1;
jim_wide start, elapsed;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "script ?count?");
return JIM_ERR;
}
if (argc == 3) {
if (Jim_GetLong(interp, argv[2], &count) != JIM_OK)
return JIM_ERR;
}
if (count < 0)
return JIM_OK;
i = count;
start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
while (i-- > 0) {
int retval;
retval = Jim_EvalObj(interp, argv[1]);
if (retval != JIM_OK) {
return retval;
}
}
elapsed = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
if (elapsed < count * 10) {
Jim_SetResult(interp, Jim_NewDoubleObj(interp, elapsed * 1.0 / count));
}
else {
Jim_SetResultInt(interp, count == 0 ? 0 : elapsed / count);
}
Jim_AppendString(interp, Jim_GetResult(interp)," microseconds per iteration", -1);
return JIM_OK;
}
static int Jim_TimeRateCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
long us = 0;
jim_wide start, delta, overhead;
Jim_Obj *objPtr;
double us_per_iter;
int count;
int n;
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "script ?milliseconds?");
return JIM_ERR;
}
if (argc == 3) {
if (Jim_GetLong(interp, argv[2], &us) != JIM_OK)
return JIM_ERR;
us *= 1000;
}
if (us < 1) {
us = 1000 * 1000;
}
start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
count = 0;
do {
int retval = Jim_EvalObj(interp, argv[1]);
delta = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
if (retval != JIM_OK) {
return retval;
}
count++;
} while (delta < us);
start = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW);
n = 0;
do {
int retval = Jim_EvalObj(interp, interp->nullScriptObj);
overhead = Jim_GetTimeUsec(CLOCK_MONOTONIC_RAW) - start;
if (retval != JIM_OK) {
return retval;
}
n++;
} while (n < count);
delta -= overhead;
us_per_iter = (double)delta / count;
objPtr = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "us_per_iter", -1));
Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, us_per_iter));
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "iters_per_sec", -1));
Jim_ListAppendElement(interp, objPtr, Jim_NewDoubleObj(interp, 1e6 / us_per_iter));
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "count", -1));
Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, count));
Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, "elapsed_us", -1));
Jim_ListAppendElement(interp, objPtr, Jim_NewIntObj(interp, delta));
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
static int Jim_ExitCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
long exitCode = 0;
if (argc > 2) {
Jim_WrongNumArgs(interp, 1, argv, "?exitCode?");
return JIM_ERR;
}
if (argc == 2) {
if (Jim_GetLong(interp, argv[1], &exitCode) != JIM_OK)
return JIM_ERR;
Jim_SetResult(interp, argv[1]);
}
interp->exitCode = exitCode;
return JIM_EXIT;
}
static int JimMatchReturnCodes(Jim_Interp *interp, Jim_Obj *retcodeListObj, int rc)
{
int len = Jim_ListLength(interp, retcodeListObj);
int i;
for (i = 0; i < len; i++) {
int returncode;
if (Jim_GetReturnCode(interp, Jim_ListGetIndex(interp, retcodeListObj, i), &returncode) != JIM_OK) {
return JIM_ERR;
}
if (rc == returncode) {
return JIM_OK;
}
}
return -1;
}
static int JimCatchTryHelper(Jim_Interp *interp, int istry, int argc, Jim_Obj *const *argv)
{
static const char * const wrongargs_catchtry[2] = {
"?-?no?code ... --? script ?resultVarName? ?optionVarName?",
"?-?no?code ... --? script ?on|trap codes vars script? ... ?finally script?"
};
int exitCode = 0;
int i;
int sig = 0;
int ok;
Jim_Obj *finallyScriptObj = NULL;
Jim_Obj *msgVarObj = NULL;
Jim_Obj *optsVarObj = NULL;
Jim_Obj *handlerScriptObj = NULL;
Jim_Obj *errorCodeObj;
int idx;
jim_wide ignore_mask = (1 << JIM_EXIT) | (1 << JIM_EVAL) | (1 << JIM_SIGNAL);
static const int max_ignore_code = sizeof(ignore_mask) * 8;
JimPanic((istry != 0 && istry != 1, "wrong args to JimCatchTryHelper"));
Jim_SetGlobalVariableStr(interp, "errorCode", Jim_NewStringObj(interp, "NONE", -1));
for (i = 1; i < argc - 1; i++) {
const char *arg = Jim_String(argv[i]);
jim_wide option;
int ignore;
|
| ︙ | ︙ | |||
18127 18128 18129 18130 18131 18132 18133 |
ignore_mask |= ((jim_wide)1 << option);
}
else {
ignore_mask &= (~((jim_wide)1 << option));
}
}
| | > | | | < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | < > > | | | | | | | | < | | | < | | | | | | > > > > | > | | > | > > > > > > > > > > > > | | > > > > > > > > > > > > > > < < < < | < | > > | | > | | | | | | | | | > | | < < < < < < < > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < | 19757 19758 19759 19760 19761 19762 19763 19764 19765 19766 19767 19768 19769 19770 19771 19772 19773 19774 19775 19776 19777 19778 19779 19780 19781 19782 19783 19784 19785 19786 19787 19788 19789 19790 19791 19792 19793 19794 19795 19796 19797 19798 19799 19800 19801 19802 19803 19804 19805 19806 19807 19808 19809 19810 19811 19812 19813 19814 19815 19816 19817 19818 19819 19820 19821 19822 19823 19824 19825 19826 19827 19828 19829 19830 19831 19832 19833 19834 19835 19836 19837 19838 19839 19840 19841 19842 19843 19844 19845 19846 19847 19848 19849 19850 19851 19852 19853 19854 19855 19856 19857 19858 19859 19860 19861 19862 19863 19864 19865 19866 19867 19868 19869 19870 19871 19872 19873 19874 19875 19876 19877 19878 19879 19880 19881 19882 19883 19884 19885 19886 19887 19888 19889 19890 19891 19892 19893 19894 19895 19896 19897 19898 19899 19900 19901 19902 19903 19904 19905 19906 19907 19908 19909 19910 19911 19912 19913 19914 19915 19916 19917 19918 19919 19920 19921 19922 19923 19924 19925 19926 19927 19928 19929 19930 19931 19932 19933 19934 19935 19936 19937 19938 19939 19940 19941 19942 19943 19944 19945 19946 19947 19948 19949 19950 19951 19952 19953 19954 19955 19956 19957 19958 19959 19960 19961 19962 19963 19964 19965 19966 19967 19968 19969 19970 19971 19972 19973 19974 19975 19976 19977 19978 19979 19980 19981 19982 19983 19984 19985 19986 19987 19988 19989 19990 19991 19992 19993 19994 19995 19996 19997 19998 19999 20000 20001 20002 20003 20004 20005 20006 20007 20008 20009 20010 20011 20012 20013 20014 20015 20016 20017 20018 20019 20020 20021 20022 20023 20024 20025 20026 20027 20028 20029 20030 20031 20032 20033 20034 20035 20036 20037 20038 20039 20040 20041 20042 20043 20044 20045 20046 20047 20048 20049 20050 20051 |
ignore_mask |= ((jim_wide)1 << option);
}
else {
ignore_mask &= (~((jim_wide)1 << option));
}
}
idx = i;
if (argc - idx < 1) {
wrongargs:
Jim_WrongNumArgs(interp, 1, argv, wrongargs_catchtry[istry]);
return JIM_ERR;
}
if ((ignore_mask & (1 << JIM_SIGNAL)) == 0) {
sig++;
}
interp->signal_level += sig;
if (Jim_CheckSignal(interp)) {
exitCode = JIM_SIGNAL;
}
else {
exitCode = Jim_EvalObj(interp, argv[idx]);
interp->errorFlag = 0;
}
interp->signal_level -= sig;
errorCodeObj = Jim_GetGlobalVariableStr(interp, "errorCode", JIM_NONE);
idx++;
if (istry) {
while (idx < argc) {
int option;
int ret;
static const char * const try_options[] = { "on", "trap", "finally", NULL };
enum { TRY_ON, TRY_TRAP, TRY_FINALLY, };
if (Jim_GetEnum(interp, argv[idx], try_options, &option, "handler", JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
switch (option) {
case TRY_ON:
case TRY_TRAP:
if (idx + 4 > argc) {
goto wrongargs;
}
if (option == TRY_ON) {
ret = JimMatchReturnCodes(interp, argv[idx + 1], exitCode);
if (ret > JIM_OK) {
goto wrongargs;
}
}
else if (errorCodeObj) {
int len = Jim_ListLength(interp, argv[idx + 1]);
int i;
ret = JIM_OK;
for (i = 0; i < len; i++) {
Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
ret = -1;
break;
}
}
}
else {
ret = -1;
}
if (ret == JIM_OK && handlerScriptObj == NULL) {
msgVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 0);
optsVarObj = Jim_ListGetIndex(interp, argv[idx + 2], 1);
handlerScriptObj = argv[idx + 3];
}
idx += 4;
break;
case TRY_FINALLY:
if (idx + 2 != argc) {
goto wrongargs;
}
finallyScriptObj = argv[idx + 1];
idx += 2;
break;
}
}
}
else {
if (argc - idx >= 1) {
msgVarObj = argv[idx];
idx++;
if (argc - idx >= 1) {
optsVarObj = argv[idx];
idx++;
}
}
}
if (exitCode >= 0 && exitCode < max_ignore_code && (((unsigned jim_wide)1 << exitCode) & ignore_mask)) {
if (finallyScriptObj) {
Jim_EvalObj(interp, finallyScriptObj);
}
return exitCode;
}
if (sig && exitCode == JIM_SIGNAL) {
if (interp->signal_set_result) {
interp->signal_set_result(interp, interp->sigmask);
}
else if (!istry) {
Jim_SetResultInt(interp, interp->sigmask);
}
interp->sigmask = 0;
}
ok = 1;
if (msgVarObj && Jim_Length(msgVarObj)) {
if (Jim_SetVariable(interp, msgVarObj, Jim_GetResult(interp)) != JIM_OK) {
ok = 0;
}
}
if (ok && optsVarObj && Jim_Length(optsVarObj)) {
Jim_Obj *optListObj = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-code", -1));
Jim_ListAppendElement(interp, optListObj,
Jim_NewIntObj(interp, exitCode == JIM_RETURN ? interp->returnCode : exitCode));
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-level", -1));
Jim_ListAppendElement(interp, optListObj, Jim_NewIntObj(interp, interp->returnLevel));
if (exitCode == JIM_ERR) {
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorinfo",
-1));
Jim_ListAppendElement(interp, optListObj, interp->stackTrace);
if (errorCodeObj) {
Jim_ListAppendElement(interp, optListObj, Jim_NewStringObj(interp, "-errorcode", -1));
Jim_ListAppendElement(interp, optListObj, errorCodeObj);
}
}
if (Jim_SetVariable(interp, optsVarObj, optListObj) != JIM_OK) {
ok = 0;
}
}
if (ok && handlerScriptObj) {
exitCode = Jim_EvalObj(interp, handlerScriptObj);
}
if (finallyScriptObj) {
Jim_Obj *prevResultObj = Jim_GetResult(interp);
Jim_IncrRefCount(prevResultObj);
int ret = Jim_EvalObj(interp, finallyScriptObj);
if (ret == JIM_OK) {
Jim_SetResult(interp, prevResultObj);
}
else {
exitCode = ret;
}
Jim_DecrRefCount(interp, prevResultObj);
}
if (!istry) {
Jim_SetResultInt(interp, exitCode);
exitCode = JIM_OK;
}
return exitCode;
}
static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimCatchTryHelper(interp, 0, argc, argv);
}
static int Jim_TryCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JimCatchTryHelper(interp, 1, argc, argv);
}
static int Jim_RenameCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 3) {
Jim_WrongNumArgs(interp, 1, argv, "oldName newName");
return JIM_ERR;
}
return Jim_RenameCommand(interp, argv[1], argv[2]);
}
#define JIM_DICTMATCH_KEYS 0x0001
#define JIM_DICTMATCH_VALUES 0x002
int Jim_DictMatchTypes(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj, int match_type, int return_types)
{
Jim_Obj *listObjPtr;
Jim_Dict *dict;
int i;
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
return JIM_ERR;
}
dict = objPtr->internalRep.dictValue;
listObjPtr = Jim_NewListObj(interp, NULL, 0);
for (i = 0; i < dict->len; i += 2 ) {
Jim_Obj *keyObj = dict->table[i];
Jim_Obj *valObj = dict->table[i + 1];
if (patternObj) {
Jim_Obj *matchObj = (match_type == JIM_DICTMATCH_KEYS) ? keyObj : valObj;
if (!Jim_StringMatchObj(interp, patternObj, matchObj, 0)) {
continue;
}
}
if (return_types & JIM_DICTMATCH_KEYS) {
Jim_ListAppendElement(interp, listObjPtr, keyObj);
}
if (return_types & JIM_DICTMATCH_VALUES) {
Jim_ListAppendElement(interp, listObjPtr, valObj);
}
}
Jim_SetResult(interp, listObjPtr);
return JIM_OK;
}
int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr)
{
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
return -1;
}
return objPtr->internalRep.dictValue->len / 2;
}
Jim_Obj *Jim_DictMerge(Jim_Interp *interp, int objc, Jim_Obj *const *objv)
{
Jim_Obj *objPtr = Jim_NewDictObj(interp, NULL, 0);
int i;
JimPanic((objc == 0, "Jim_DictMerge called with objc=0"));
for (i = 0; i < objc; i++) {
Jim_Obj **table;
int tablelen;
int j;
table = Jim_DictPairs(interp, objv[i], &tablelen);
if (tablelen && !table) {
Jim_FreeNewObj(interp, objPtr);
return NULL;
}
for (j = 0; j < tablelen; j += 2) {
DictAddElement(interp, objPtr, table[j], table[j + 1]);
}
}
return objPtr;
}
int Jim_DictInfo(Jim_Interp *interp, Jim_Obj *objPtr)
{
char buffer[100];
Jim_Obj *output;
Jim_Dict *dict;
if (SetDictFromAny(interp, objPtr) != JIM_OK) {
return JIM_ERR;
}
dict = objPtr->internalRep.dictValue;
snprintf(buffer, sizeof(buffer), "%d entries in table, %d buckets", dict->len, dict->size);
output = Jim_NewStringObj(interp, buffer, -1);
Jim_SetResult(interp, output);
return JIM_OK;
}
static int Jim_EvalEnsemble(Jim_Interp *interp, const char *basecmd, const char *subcmd, int argc, Jim_Obj *const *argv)
{
Jim_Obj *prefixObj = Jim_NewStringObj(interp, basecmd, -1);
|
| ︙ | ︙ | |||
18364 18365 18366 18367 18368 18369 18370 |
dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
| | > < > > | | | > < < | < < < < < | < | > > > > > > > > > | > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > | < > < | > | < < < < | > > | | > | | | < | > | | | > > > > | < < < < | < < < < < < < < | < < < < < < < < < < < < < < < > > > | < < | | | > | > | 20067 20068 20069 20070 20071 20072 20073 20074 20075 20076 20077 20078 20079 20080 20081 20082 20083 20084 20085 20086 20087 20088 20089 20090 20091 20092 20093 20094 20095 20096 20097 20098 20099 20100 20101 20102 20103 20104 20105 20106 20107 20108 20109 20110 20111 20112 20113 20114 20115 20116 20117 20118 20119 20120 20121 20122 20123 20124 20125 20126 20127 20128 20129 20130 20131 20132 20133 20134 20135 20136 20137 20138 20139 20140 20141 20142 20143 20144 20145 20146 20147 20148 20149 20150 20151 20152 20153 20154 20155 20156 20157 20158 20159 20160 20161 20162 20163 20164 20165 20166 20167 20168 20169 20170 20171 20172 20173 20174 20175 20176 20177 20178 20179 20180 20181 20182 20183 20184 20185 20186 20187 20188 20189 20190 20191 20192 20193 20194 20195 20196 20197 20198 20199 20200 20201 20202 20203 20204 20205 20206 20207 20208 20209 20210 20211 20212 20213 20214 20215 20216 20217 20218 20219 20220 20221 20222 20223 20224 20225 20226 20227 20228 20229 20230 20231 20232 20233 20234 20235 20236 20237 20238 20239 20240 20241 20242 20243 20244 20245 20246 20247 20248 20249 20250 20251 20252 20253 20254 20255 20256 20257 20258 20259 20260 20261 20262 20263 20264 20265 20266 20267 |
dictObj = Jim_GetVariable(interp, dictVarName, JIM_ERRMSG);
if (dictObj == NULL || Jim_DictKeysVector(interp, dictObj, keyv, keyc, &objPtr, JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
dictValues = Jim_DictPairs(interp, objPtr, &len);
if (len && dictValues == NULL) {
return JIM_ERR;
}
for (i = 0; i < len; i += 2) {
if (Jim_SetVariable(interp, dictValues[i], dictValues[i + 1]) == JIM_ERR) {
return JIM_ERR;
}
}
if (Jim_Length(scriptObj)) {
ret = Jim_EvalObj(interp, scriptObj);
if (ret == JIM_OK && Jim_GetVariable(interp, dictVarName, 0) != NULL) {
Jim_Obj **newkeyv = Jim_Alloc(sizeof(*newkeyv) * (keyc + 1));
for (i = 0; i < keyc; i++) {
newkeyv[i] = keyv[i];
}
for (i = 0; i < len; i += 2) {
if (Jim_StringCompareObj(interp, dictVarName, dictValues[i], 0) != 0) {
objPtr = Jim_GetVariable(interp, dictValues[i], 0);
newkeyv[keyc] = dictValues[i];
Jim_SetDictKeysVector(interp, dictVarName, newkeyv, keyc + 1, objPtr, JIM_NORESULT);
}
}
Jim_Free(newkeyv);
}
}
return ret;
}
static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
int types = JIM_DICTMATCH_KEYS;
enum {
OPT_CREATE,
OPT_GET,
OPT_GETDEF,
OPT_GETWITHDEFAULT,
OPT_SET,
OPT_UNSET,
OPT_EXISTS,
OPT_KEYS,
OPT_SIZE,
OPT_INFO,
OPT_MERGE,
OPT_WITH,
OPT_APPEND,
OPT_LAPPEND,
OPT_INCR,
OPT_REMOVE,
OPT_VALUES,
OPT_FOR,
OPT_REPLACE,
OPT_UPDATE,
OPT_COUNT
};
static const jim_subcmd_type cmds[OPT_COUNT + 1] = {
JIM_DEF_SUBCMD("create", "?key value ...?", 0, -2),
JIM_DEF_SUBCMD("get", "dictionary ?key ...?", 1, -1),
JIM_DEF_SUBCMD_HIDDEN("getdef", "dictionary ?key ...? key default", 3, -1),
JIM_DEF_SUBCMD("getwithdefault", "dictionary ?key ...? key default", 3, -1),
JIM_DEF_SUBCMD("set", "varName key ?key ...? value", 3, -1),
JIM_DEF_SUBCMD("unset", "varName key ?key ...?", 2, -1),
JIM_DEF_SUBCMD("exists", "dictionary key ?key ...?", 2, -1),
JIM_DEF_SUBCMD("keys", "dictionary ?pattern?", 1, 2),
JIM_DEF_SUBCMD("size", "dictionary", 1, 1),
JIM_DEF_SUBCMD("info", "dictionary", 1, 1),
JIM_DEF_SUBCMD("merge", "?...?", 0, -1),
JIM_DEF_SUBCMD("with", "dictVar ?key ...? script", 2, -1),
JIM_DEF_SUBCMD("append", "varName key ?value ...?", 2, -1),
JIM_DEF_SUBCMD("lappend", "varName key ?value ...?", 2, -1),
JIM_DEF_SUBCMD("incr", "varName key ?increment?", 2, 3),
JIM_DEF_SUBCMD("remove", "dictionary ?key ...?", 1, -1),
JIM_DEF_SUBCMD("values", "dictionary ?pattern?", 1, 2),
JIM_DEF_SUBCMD("for", "vars dictionary script", 3, 3),
JIM_DEF_SUBCMD("replace", "dictionary ?key value ...?", 1, -1),
JIM_DEF_SUBCMD("update", "varName ?arg ...? script", 2, -1),
{ }
};
const jim_subcmd_type *ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
if (!ct) {
return JIM_ERR;
}
if (ct->function) {
return ct->function(interp, argc, argv);
}
switch (ct - cmds) {
case OPT_GET:
if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr,
JIM_ERRMSG) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
case OPT_GETDEF:
case OPT_GETWITHDEFAULT:{
int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 4, &objPtr, JIM_ERRMSG);
if (rc == -1) {
return JIM_ERR;
}
if (rc == JIM_ERR) {
Jim_SetResult(interp, argv[argc - 1]);
}
else {
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
}
case OPT_SET:
return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
case OPT_EXISTS:{
int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE);
if (rc < 0) {
return JIM_ERR;
}
Jim_SetResultBool(interp, rc == JIM_OK);
return JIM_OK;
}
case OPT_UNSET:
if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE) != JIM_OK) {
return JIM_ERR;
}
return JIM_OK;
case OPT_VALUES:
types = JIM_DICTMATCH_VALUES;
case OPT_KEYS:
return Jim_DictMatchTypes(interp, argv[2], argc == 4 ? argv[3] : NULL, types, types);
case OPT_SIZE:
if (Jim_DictSize(interp, argv[2]) < 0) {
return JIM_ERR;
}
Jim_SetResultInt(interp, Jim_DictSize(interp, argv[2]));
return JIM_OK;
case OPT_MERGE:
if (argc == 2) {
return JIM_OK;
}
objPtr = Jim_DictMerge(interp, argc - 2, argv + 2);
if (objPtr == NULL) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
case OPT_CREATE:
objPtr = Jim_NewDictObj(interp, argv + 2, argc - 2);
Jim_SetResult(interp, objPtr);
return JIM_OK;
case OPT_INFO:
return Jim_DictInfo(interp, argv[2]);
case OPT_WITH:
return JimDictWith(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]);
case OPT_UPDATE:
if (argc < 6 || argc % 2) {
argc = 2;
}
default:
return Jim_EvalEnsemble(interp, "dict", Jim_String(argv[1]), argc - 2, argv + 2);
}
}
static int Jim_SubstCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
static const char * const options[] = {
"-nobackslashes", "-nocommands", "-novariables", NULL
|
| ︙ | ︙ | |||
18580 18581 18582 18583 18584 18585 18586 18587 18588 18589 |
if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
| > > > > > > > > > < | < < < < < | > > > | > > > | > | > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | | < > | | > | < < < < | < < < < | 20298 20299 20300 20301 20302 20303 20304 20305 20306 20307 20308 20309 20310 20311 20312 20313 20314 20315 20316 20317 20318 20319 20320 20321 20322 20323 20324 20325 20326 20327 20328 20329 20330 20331 20332 20333 20334 20335 20336 20337 20338 20339 20340 20341 20342 20343 20344 20345 20346 20347 20348 20349 20350 20351 20352 20353 20354 20355 20356 20357 20358 20359 20360 20361 20362 20363 20364 20365 20366 20367 20368 20369 20370 20371 20372 20373 20374 20375 20376 20377 20378 20379 20380 20381 20382 20383 20384 20385 20386 20387 20388 20389 20390 20391 20392 20393 20394 20395 20396 20397 20398 20399 20400 20401 20402 20403 20404 20405 20406 20407 20408 20409 |
if (Jim_SubstObj(interp, argv[argc - 1], &objPtr, flags) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
#ifdef jim_ext_namespace
static int JimIsGlobalNamespace(Jim_Obj *objPtr)
{
int len;
const char *str = Jim_GetString(objPtr, &len);
return len >= 2 && str[0] == ':' && str[1] == ':';
}
#endif
static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
int mode = 0;
enum {
INFO_ALIAS,
INFO_ARGS,
INFO_BODY,
INFO_CHANNELS,
INFO_COMMANDS,
INFO_COMPLETE,
INFO_EXISTS,
INFO_FRAME,
INFO_GLOBALS,
INFO_HOSTNAME,
INFO_LEVEL,
INFO_LOCALS,
INFO_NAMEOFEXECUTABLE,
INFO_PATCHLEVEL,
INFO_PROCS,
INFO_REFERENCES,
INFO_RETURNCODES,
INFO_SCRIPT,
INFO_SOURCE,
INFO_STACKTRACE,
INFO_STATICS,
INFO_VARS,
INFO_VERSION,
INFO_COUNT
};
static const jim_subcmd_type cmds[INFO_COUNT + 1] = {
JIM_DEF_SUBCMD("alias", "command", 1, 1),
JIM_DEF_SUBCMD("args", "procname", 1, 1),
JIM_DEF_SUBCMD("body", "procname", 1, 1),
JIM_DEF_SUBCMD("channels", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("commands", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("complete", "script ?missing?", 1, 2),
JIM_DEF_SUBCMD("exists", "varName", 1, 1),
JIM_DEF_SUBCMD("frame", "?levelNum?", 0, 1),
JIM_DEF_SUBCMD("globals", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("hostname", NULL, 0, 0),
JIM_DEF_SUBCMD("level", "?levelNum?", 0, 1),
JIM_DEF_SUBCMD("locals", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("nameofexecutable", NULL, 0, 0),
JIM_DEF_SUBCMD("patchlevel", NULL, 0, 0),
JIM_DEF_SUBCMD("procs", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("references", NULL, 0, 0),
JIM_DEF_SUBCMD("returncodes", "?code?", 0, 1),
JIM_DEF_SUBCMD("script", "?filename?", 0, 1),
JIM_DEF_SUBCMD("source", "source ?filename line?", 1, 3),
JIM_DEF_SUBCMD("stacktrace", NULL, 0, 0),
JIM_DEF_SUBCMD("statics", "procname", 1, 1),
JIM_DEF_SUBCMD("vars", "?pattern?", 0, 1),
JIM_DEF_SUBCMD("version", NULL, 0, 0),
{ }
};
const jim_subcmd_type *ct;
#ifdef jim_ext_namespace
int nons = 0;
if (argc > 2 && Jim_CompareStringImmediate(interp, argv[1], "-nons")) {
argc--;
argv++;
nons = 1;
}
#endif
ct = Jim_ParseSubCmd(interp, cmds, argc, argv);
if (!ct) {
return JIM_ERR;
}
if (ct->function) {
return ct->function(interp, argc, argv);
}
int option = ct - cmds;
switch (option) {
case INFO_EXISTS:
Jim_SetResultBool(interp, Jim_GetVariable(interp, argv[2], 0) != NULL);
return JIM_OK;
case INFO_ALIAS:{
Jim_Cmd *cmdPtr;
if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
return JIM_ERR;
}
if (cmdPtr->isproc || cmdPtr->u.native.cmdProc != JimAliasCmd) {
Jim_SetResultFormatted(interp, "command \"%#s\" is not an alias", argv[2]);
return JIM_ERR;
}
|
| ︙ | ︙ | |||
18659 18660 18661 18662 18663 18664 18665 |
#endif
case INFO_PROCS:
mode++;
case INFO_COMMANDS:
| < < < < | | < < < < | | | > | < > | | < | | > | < < < < < < < | < < | < < | | < | < | < | | | | | | > > | > > > | | | | > > | < < < < | > > > > > > | | | < < < < < > < | 20419 20420 20421 20422 20423 20424 20425 20426 20427 20428 20429 20430 20431 20432 20433 20434 20435 20436 20437 20438 20439 20440 20441 20442 20443 20444 20445 20446 20447 20448 20449 20450 20451 20452 20453 20454 20455 20456 20457 20458 20459 20460 20461 20462 20463 20464 20465 20466 20467 20468 20469 20470 20471 20472 20473 20474 20475 20476 20477 20478 20479 20480 20481 20482 20483 20484 20485 20486 20487 20488 20489 20490 20491 20492 20493 20494 20495 20496 20497 20498 20499 20500 20501 20502 20503 20504 20505 20506 20507 20508 20509 20510 20511 20512 20513 20514 20515 20516 20517 20518 20519 20520 20521 20522 20523 20524 20525 20526 20527 20528 20529 20530 20531 20532 20533 20534 20535 20536 20537 20538 20539 20540 20541 20542 20543 20544 20545 20546 20547 20548 20549 20550 20551 20552 20553 20554 20555 20556 20557 20558 20559 20560 20561 20562 20563 20564 20565 20566 20567 20568 20569 20570 20571 20572 20573 20574 20575 20576 20577 |
#endif
case INFO_PROCS:
mode++;
case INFO_COMMANDS:
#ifdef jim_ext_namespace
if (!nons) {
if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
}
}
#endif
Jim_SetResult(interp, JimCommandsList(interp, (argc == 3) ? argv[2] : NULL, mode));
return JIM_OK;
case INFO_VARS:
mode++;
case INFO_LOCALS:
mode++;
case INFO_GLOBALS:
#ifdef jim_ext_namespace
if (!nons) {
if (Jim_Length(interp->framePtr->nsObj) || (argc == 3 && JimIsGlobalNamespace(argv[2]))) {
return Jim_EvalPrefix(interp, "namespace info", argc - 1, argv + 1);
}
}
#endif
Jim_SetResult(interp, JimVariablesList(interp, argc == 3 ? argv[2] : NULL, mode));
return JIM_OK;
case INFO_SCRIPT:
if (argc == 3) {
Jim_IncrRefCount(argv[2]);
Jim_DecrRefCount(interp, interp->currentFilenameObj);
interp->currentFilenameObj = argv[2];
}
Jim_SetResult(interp, interp->currentFilenameObj);
return JIM_OK;
case INFO_SOURCE:{
Jim_Obj *resObjPtr;
Jim_Obj *fileNameObj;
if (argc == 4) {
Jim_SubCmdArgError(interp, ct, argv[0]);
return JIM_ERR;
}
if (argc == 5) {
jim_wide line;
if (Jim_GetWide(interp, argv[4], &line) != JIM_OK) {
return JIM_ERR;
}
resObjPtr = Jim_NewStringObj(interp, Jim_String(argv[2]), Jim_Length(argv[2]));
Jim_SetSourceInfo(interp, resObjPtr, argv[3], line);
}
else {
int line;
fileNameObj = Jim_GetSourceInfo(interp, argv[2], &line);
resObjPtr = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, resObjPtr, fileNameObj);
Jim_ListAppendElement(interp, resObjPtr, Jim_NewIntObj(interp, line));
}
Jim_SetResult(interp, resObjPtr);
return JIM_OK;
}
case INFO_STACKTRACE:
Jim_SetResult(interp, interp->stackTrace);
return JIM_OK;
case INFO_LEVEL:
if (argc == 2) {
Jim_SetResultInt(interp, interp->framePtr->level);
}
else {
if (JimInfoLevel(interp, argv[2], &objPtr) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
case INFO_FRAME:
if (argc == 2) {
Jim_SetResultInt(interp, interp->procLevel + 1);
}
else {
if (JimInfoFrame(interp, argv[2], &objPtr) != JIM_OK) {
return JIM_ERR;
}
Jim_SetResult(interp, objPtr);
}
return JIM_OK;
case INFO_BODY:
case INFO_STATICS:
case INFO_ARGS:{
Jim_Cmd *cmdPtr;
if ((cmdPtr = Jim_GetCommand(interp, argv[2], JIM_ERRMSG)) == NULL) {
return JIM_ERR;
}
if (!cmdPtr->isproc) {
Jim_SetResultFormatted(interp, "command \"%#s\" is not a procedure", argv[2]);
return JIM_ERR;
}
switch (option) {
#ifdef JIM_NO_INTROSPECTION
default:
Jim_SetResultString(interp, "unsupported", -1);
return JIM_ERR;
#else
case INFO_BODY:
Jim_SetResult(interp, cmdPtr->u.proc.bodyObjPtr);
break;
case INFO_ARGS:
Jim_SetResult(interp, cmdPtr->u.proc.argListObjPtr);
break;
#endif
case INFO_STATICS:
if (cmdPtr->u.proc.staticVars) {
Jim_SetResult(interp, JimHashtablePatternMatch(interp, cmdPtr->u.proc.staticVars,
NULL, JimVariablesMatch, JIM_VARLIST_LOCALS | JIM_VARLIST_VALUES));
}
break;
}
return JIM_OK;
}
case INFO_VERSION:
case INFO_PATCHLEVEL:{
char buf[(JIM_INTEGER_SPACE * 2) + 1];
sprintf(buf, "%d.%d", JIM_VERSION / 100, JIM_VERSION % 100);
Jim_SetResultString(interp, buf, -1);
return JIM_OK;
}
case INFO_COMPLETE: {
char missing;
Jim_SetResultBool(interp, Jim_ScriptIsComplete(interp, argv[2], &missing));
if (missing != ' ' && argc == 4) {
Jim_SetVariable(interp, argv[3], Jim_NewStringObj(interp, &missing, 1));
}
return JIM_OK;
}
case INFO_HOSTNAME:
return Jim_Eval(interp, "os.gethostname");
case INFO_NAMEOFEXECUTABLE:
|
| ︙ | ︙ | |||
18858 18859 18860 18861 18862 18863 18864 |
if (*name == '?') {
Jim_SetResultInt(interp, code);
}
else {
Jim_SetResultString(interp, name, -1);
}
}
| < < | < < > > < | 20601 20602 20603 20604 20605 20606 20607 20608 20609 20610 20611 20612 20613 20614 20615 20616 20617 20618 20619 20620 20621 20622 20623 20624 20625 |
if (*name == '?') {
Jim_SetResultInt(interp, code);
}
else {
Jim_SetResultString(interp, name, -1);
}
}
return JIM_OK;
case INFO_REFERENCES:
#ifdef JIM_REFERENCES
return JimInfoReferences(interp, argc, argv);
#else
Jim_SetResultString(interp, "not supported", -1);
return JIM_ERR;
#endif
default:
abort();
}
}
static int Jim_ExistsCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
int result = 0;
|
| ︙ | ︙ | |||
19143 19144 19145 19146 19147 19148 19149 |
return JIM_ERR;
}
Jim_SetResult(interp, argv[1]);
if (argc == 3) {
JimSetStackTrace(interp, argv[2]);
return JIM_ERR;
}
| < | 20883 20884 20885 20886 20887 20888 20889 20890 20891 20892 20893 20894 20895 20896 |
return JIM_ERR;
}
Jim_SetResult(interp, argv[1]);
if (argc == 3) {
JimSetStackTrace(interp, argv[2]);
return JIM_ERR;
}
return JIM_ERR;
}
static int Jim_LrangeCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
|
| ︙ | ︙ | |||
19166 19167 19168 19169 19170 19171 19172 |
return JIM_OK;
}
static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
| | | < > | > | | 20905 20906 20907 20908 20909 20910 20911 20912 20913 20914 20915 20916 20917 20918 20919 20920 20921 20922 20923 20924 20925 20926 20927 20928 20929 20930 20931 20932 20933 20934 20935 |
return JIM_OK;
}
static int Jim_LrepeatCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objPtr;
jim_wide count;
if (argc < 2 || Jim_GetWideExpr(interp, argv[1], &count) != JIM_OK || count < 0) {
Jim_WrongNumArgs(interp, 1, argv, "count ?value ...?");
return JIM_ERR;
}
if (count == 0 || argc == 2) {
Jim_SetEmptyResult(interp);
return JIM_OK;
}
argc -= 2;
argv += 2;
objPtr = Jim_NewListObj(interp, NULL, 0);
ListEnsureLength(objPtr, argc * count);
while (count--) {
ListInsertElements(objPtr, -1, argc, argv);
}
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
|
| ︙ | ︙ | |||
19241 19242 19243 19244 19245 19246 19247 |
}
}
Jim_SetResult(interp, listObjPtr);
return JIM_OK;
}
| | | 20981 20982 20983 20984 20985 20986 20987 20988 20989 20990 20991 20992 20993 20994 20995 |
}
}
Jim_SetResult(interp, listObjPtr);
return JIM_OK;
}
if (argc > 3) {
Jim_WrongNumArgs(interp, 1, argv, "varName ?default?");
return JIM_ERR;
}
key = Jim_String(argv[1]);
val = getenv(key);
if (val == NULL) {
if (argc < 3) {
|
| ︙ | ︙ | |||
19284 19285 19286 19287 19288 19289 19290 |
int len;
if (argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "list");
return JIM_ERR;
}
JimListGetElements(interp, argv[1], &len, &ele);
| < > > | 21024 21025 21026 21027 21028 21029 21030 21031 21032 21033 21034 21035 21036 21037 21038 21039 21040 |
int len;
if (argc != 2) {
Jim_WrongNumArgs(interp, 1, argv, "list");
return JIM_ERR;
}
JimListGetElements(interp, argv[1], &len, &ele);
revObjPtr = Jim_NewListObj(interp, NULL, 0);
ListEnsureLength(revObjPtr, len);
len--;
while (len >= 0)
ListAppendElement(revObjPtr, ele[len--]);
Jim_SetResult(interp, revObjPtr);
return JIM_OK;
}
static int JimRangeLen(jim_wide start, jim_wide end, jim_wide step)
|
| ︙ | ︙ | |||
19327 19328 19329 19330 19331 19332 19333 |
Jim_Obj *objPtr;
if (argc < 2 || argc > 4) {
Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
return JIM_ERR;
}
if (argc == 2) {
| | | | | > | | | | 21068 21069 21070 21071 21072 21073 21074 21075 21076 21077 21078 21079 21080 21081 21082 21083 21084 21085 21086 21087 21088 21089 21090 21091 21092 21093 21094 21095 21096 21097 21098 21099 21100 21101 21102 21103 21104 21105 21106 21107 21108 21109 21110 21111 21112 21113 21114 21115 21116 21117 21118 21119 21120 |
Jim_Obj *objPtr;
if (argc < 2 || argc > 4) {
Jim_WrongNumArgs(interp, 1, argv, "?start? end ?step?");
return JIM_ERR;
}
if (argc == 2) {
if (Jim_GetWideExpr(interp, argv[1], &end) != JIM_OK)
return JIM_ERR;
}
else {
if (Jim_GetWideExpr(interp, argv[1], &start) != JIM_OK ||
Jim_GetWideExpr(interp, argv[2], &end) != JIM_OK)
return JIM_ERR;
if (argc == 4 && Jim_GetWideExpr(interp, argv[3], &step) != JIM_OK)
return JIM_ERR;
}
if ((len = JimRangeLen(start, end, step)) == -1) {
Jim_SetResultString(interp, "Invalid (infinite?) range specified", -1);
return JIM_ERR;
}
objPtr = Jim_NewListObj(interp, NULL, 0);
ListEnsureLength(objPtr, len);
for (i = 0; i < len; i++)
ListAppendElement(objPtr, Jim_NewIntObj(interp, start + i * step));
Jim_SetResult(interp, objPtr);
return JIM_OK;
}
static int Jim_RandCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
jim_wide min = 0, max = 0, len, maxMul;
if (argc < 1 || argc > 3) {
Jim_WrongNumArgs(interp, 1, argv, "?min? max");
return JIM_ERR;
}
if (argc == 1) {
max = JIM_WIDE_MAX;
} else if (argc == 2) {
if (Jim_GetWideExpr(interp, argv[1], &max) != JIM_OK)
return JIM_ERR;
} else if (argc == 3) {
if (Jim_GetWideExpr(interp, argv[1], &min) != JIM_OK ||
Jim_GetWideExpr(interp, argv[2], &max) != JIM_OK)
return JIM_ERR;
}
len = max-min;
if (len < 0) {
Jim_SetResultString(interp, "Invalid arguments (max < min)", -1);
return JIM_ERR;
}
|
| ︙ | ︙ | |||
19415 19416 19417 19418 19419 19420 19421 |
{"lsearch", Jim_LsearchCoreCommand},
{"llength", Jim_LlengthCoreCommand},
{"lappend", Jim_LappendCoreCommand},
{"linsert", Jim_LinsertCoreCommand},
{"lreplace", Jim_LreplaceCoreCommand},
{"lsort", Jim_LsortCoreCommand},
{"append", Jim_AppendCoreCommand},
| < > > > | 21157 21158 21159 21160 21161 21162 21163 21164 21165 21166 21167 21168 21169 21170 21171 21172 21173 21174 21175 21176 21177 21178 21179 21180 21181 21182 21183 21184 21185 21186 21187 |
{"lsearch", Jim_LsearchCoreCommand},
{"llength", Jim_LlengthCoreCommand},
{"lappend", Jim_LappendCoreCommand},
{"linsert", Jim_LinsertCoreCommand},
{"lreplace", Jim_LreplaceCoreCommand},
{"lsort", Jim_LsortCoreCommand},
{"append", Jim_AppendCoreCommand},
{"eval", Jim_EvalCoreCommand},
{"uplevel", Jim_UplevelCoreCommand},
{"expr", Jim_ExprCoreCommand},
{"break", Jim_BreakCoreCommand},
{"continue", Jim_ContinueCoreCommand},
{"proc", Jim_ProcCoreCommand},
{"xtrace", Jim_XtraceCoreCommand},
{"concat", Jim_ConcatCoreCommand},
{"return", Jim_ReturnCoreCommand},
{"upvar", Jim_UpvarCoreCommand},
{"global", Jim_GlobalCoreCommand},
{"string", Jim_StringCoreCommand},
{"time", Jim_TimeCoreCommand},
{"timerate", Jim_TimeRateCoreCommand},
{"exit", Jim_ExitCoreCommand},
{"catch", Jim_CatchCoreCommand},
{"try", Jim_TryCoreCommand},
#ifdef JIM_REFERENCES
{"ref", Jim_RefCoreCommand},
{"getref", Jim_GetrefCoreCommand},
{"setref", Jim_SetrefCoreCommand},
{"finalize", Jim_FinalizeCoreCommand},
{"collect", Jim_CollectCoreCommand},
#endif
|
| ︙ | ︙ | |||
19458 19459 19460 19461 19462 19463 19464 19465 19466 19467 19468 19469 19470 19471 |
{"lreverse", Jim_LreverseCoreCommand},
{"range", Jim_RangeCoreCommand},
{"rand", Jim_RandCoreCommand},
{"tailcall", Jim_TailcallCoreCommand},
{"local", Jim_LocalCoreCommand},
{"upcall", Jim_UpcallCoreCommand},
{"apply", Jim_ApplyCoreCommand},
{NULL, NULL},
};
void Jim_RegisterCoreCommands(Jim_Interp *interp)
{
int i = 0;
| > | 21202 21203 21204 21205 21206 21207 21208 21209 21210 21211 21212 21213 21214 21215 21216 |
{"lreverse", Jim_LreverseCoreCommand},
{"range", Jim_RangeCoreCommand},
{"rand", Jim_RandCoreCommand},
{"tailcall", Jim_TailcallCoreCommand},
{"local", Jim_LocalCoreCommand},
{"upcall", Jim_UpcallCoreCommand},
{"apply", Jim_ApplyCoreCommand},
{"stacktrace", Jim_StacktraceCoreCommand},
{NULL, NULL},
};
void Jim_RegisterCoreCommands(Jim_Interp *interp)
{
int i = 0;
|
| ︙ | ︙ | |||
19685 19686 19687 19688 19689 19690 19691 19692 19693 19694 19695 19696 19697 19698 19699 |
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
for (i = 0; i < nobjparam; i++) {
Jim_DecrRefCount(interp, objparam[i]);
}
}
#ifndef jim_ext_package
int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
{
return JIM_OK;
}
#endif
#ifndef jim_ext_aio
| > > > > > > > > > | < | | | > > > > > | | > > | > > > > | | | | | | > | 21430 21431 21432 21433 21434 21435 21436 21437 21438 21439 21440 21441 21442 21443 21444 21445 21446 21447 21448 21449 21450 21451 21452 21453 21454 21455 21456 21457 21458 21459 21460 21461 21462 21463 21464 21465 21466 21467 21468 21469 21470 21471 21472 21473 21474 21475 21476 21477 21478 21479 21480 21481 21482 21483 21484 21485 21486 21487 21488 21489 21490 21491 21492 21493 21494 21495 21496 21497 21498 21499 21500 21501 21502 21503 21504 21505 21506 21507 21508 21509 21510 21511 21512 21513 21514 21515 21516 21517 21518 21519 21520 21521 21522 21523 21524 21525 21526 21527 21528 21529 21530 21531 21532 21533 21534 21535 21536 21537 21538 21539 21540 21541 21542 21543 21544 21545 21546 21547 21548 21549 21550 21551 21552 21553 21554 |
Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, buf, len));
for (i = 0; i < nobjparam; i++) {
Jim_DecrRefCount(interp, objparam[i]);
}
}
int Jim_CheckAbiVersion(Jim_Interp *interp, int abi_version)
{
if (abi_version != JIM_ABI_VERSION) {
Jim_SetResultString(interp, "ABI version mismatch", -1);
return JIM_ERR;
}
return JIM_OK;
}
#ifndef jim_ext_package
int Jim_PackageProvide(Jim_Interp *interp, const char *name, const char *ver, int flags)
{
return JIM_OK;
}
#endif
#ifndef jim_ext_aio
int Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *fhObj)
{
return -1;
}
#endif
#include <stdio.h>
#include <string.h>
static int subcmd_null(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
return JIM_OK;
}
static const jim_subcmd_type dummy_subcmd = {
"dummy", NULL, subcmd_null, 0, 0, JIM_MODFLAG_HIDDEN
};
static Jim_Obj *subcmd_cmd_list(Jim_Interp *interp, const jim_subcmd_type * ct, const char *sep)
{
Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
Jim_Obj *sortCmd[2];
for (; ct->cmd; ct++) {
if (!(ct->flags & JIM_MODFLAG_HIDDEN)) {
Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, ct->cmd, -1));
}
}
sortCmd[0] = Jim_NewStringObj(interp, "lsort", -1);
sortCmd[1] = listObj;
if (Jim_EvalObjVector(interp, 2, sortCmd) == JIM_OK) {
return Jim_ListJoin(interp, Jim_GetResult(interp), sep, strlen(sep));
}
return Jim_GetResult(interp);
}
static void bad_subcmd(Jim_Interp *interp, const jim_subcmd_type * command_table, const char *type,
Jim_Obj *cmd, Jim_Obj *subcmd)
{
Jim_SetResultFormatted(interp, "%#s, %s command \"%#s\": should be %#s", cmd, type,
subcmd, subcmd_cmd_list(interp, command_table, ", "));
}
static void show_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * command_table, int argc,
Jim_Obj *const *argv)
{
Jim_SetResultFormatted(interp, "Usage: \"%#s command ... \", where command is one of: %#s",
argv[0], subcmd_cmd_list(interp, command_table, ", "));
}
static void add_cmd_usage(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *cmd)
{
if (cmd) {
Jim_AppendStrings(interp, Jim_GetResult(interp), Jim_String(cmd), " ", NULL);
}
Jim_AppendStrings(interp, Jim_GetResult(interp), ct->cmd, NULL);
if (ct->args && *ct->args) {
Jim_AppendStrings(interp, Jim_GetResult(interp), " ", ct->args, NULL);
}
}
void Jim_SubCmdArgError(Jim_Interp *interp, const jim_subcmd_type * ct, Jim_Obj *subcmd)
{
Jim_SetResultString(interp, "wrong # args: should be \"", -1);
add_cmd_usage(interp, ct, subcmd);
Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
}
static const Jim_ObjType subcmdLookupObjType = {
"subcmd-lookup",
NULL,
NULL,
NULL,
JIM_TYPE_REFERENCES
};
const jim_subcmd_type *Jim_ParseSubCmd(Jim_Interp *interp, const jim_subcmd_type * command_table,
int argc, Jim_Obj *const *argv)
{
const jim_subcmd_type *ct;
const jim_subcmd_type *partial = 0;
int cmdlen;
Jim_Obj *cmd;
const char *cmdstr;
int help = 0;
int argsok = 1;
if (argc < 2) {
Jim_SetResultFormatted(interp, "wrong # args: should be \"%#s command ...\"\n"
"Use \"%#s -help ?command?\" for help", argv[0], argv[0]);
return 0;
}
|
| ︙ | ︙ | |||
19808 19809 19810 19811 19812 19813 19814 |
cmd = argv[2];
}
if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
| < < | | 21573 21574 21575 21576 21577 21578 21579 21580 21581 21582 21583 21584 21585 21586 21587 |
cmd = argv[2];
}
if (Jim_CompareStringImmediate(interp, cmd, "-commands")) {
Jim_SetResult(interp, subcmd_cmd_list(interp, command_table, " "));
return &dummy_subcmd;
}
cmdstr = Jim_GetString(cmd, &cmdlen);
for (ct = command_table; ct->cmd; ct++) {
if (Jim_CompareStringImmediate(interp, cmd, ct->cmd)) {
|
| ︙ | ︙ | |||
19868 19869 19870 19871 19872 19873 19874 |
Jim_FreeIntRep(interp, cmd);
cmd->typePtr = &subcmdLookupObjType;
cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
cmd->internalRep.ptrIntValue.int1 = ct - command_table;
found:
| > | > > > > > > > > > > | 21631 21632 21633 21634 21635 21636 21637 21638 21639 21640 21641 21642 21643 21644 21645 21646 21647 21648 21649 21650 21651 21652 21653 21654 21655 21656 |
Jim_FreeIntRep(interp, cmd);
cmd->typePtr = &subcmdLookupObjType;
cmd->internalRep.ptrIntValue.ptr = (void *)command_table;
cmd->internalRep.ptrIntValue.int1 = ct - command_table;
found:
if (argc - 2 < ct->minargs) {
argsok = 0;
}
else if (ct->maxargs >= 0 && argc - 2 > ct->maxargs) {
argsok = 0;
}
else if (ct->maxargs < -1 && (argc - 2) % -ct->maxargs != 0) {
argsok = 0;
}
if (!argsok) {
Jim_SetResultString(interp, "wrong # args: should be \"", -1);
add_cmd_usage(interp, ct, argv[0]);
Jim_AppendStrings(interp, Jim_GetResult(interp), "\"", NULL);
return 0;
}
|
| ︙ | ︙ | |||
19893 19894 19895 19896 19897 19898 19899 |
if (ct->flags & JIM_MODFLAG_FULLARGV) {
ret = ct->function(interp, argc, argv);
}
else {
ret = ct->function(interp, argc - 2, argv + 2);
}
if (ret < 0) {
| | | 21667 21668 21669 21670 21671 21672 21673 21674 21675 21676 21677 21678 21679 21680 21681 |
if (ct->flags & JIM_MODFLAG_FULLARGV) {
ret = ct->function(interp, argc, argv);
}
else {
ret = ct->function(interp, argc - 2, argv + 2);
}
if (ret < 0) {
Jim_SubCmdArgError(interp, ct, argv[0]);
ret = JIM_ERR;
}
}
return ret;
}
int Jim_SubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
| ︙ | ︙ | |||
19944 19945 19946 19947 19948 19949 19950 19951 19952 19953 19954 19955 19956 19957 |
*p = 0x80 | (uc & 0x3f);
return 4;
}
}
#include <ctype.h>
#include <string.h>
#define JIM_INTEGER_SPACE 24
#define MAX_FLOAT_WIDTH 320
Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
{
| > | 21718 21719 21720 21721 21722 21723 21724 21725 21726 21727 21728 21729 21730 21731 21732 |
*p = 0x80 | (uc & 0x3f);
return 4;
}
}
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#define JIM_INTEGER_SPACE 24
#define MAX_FLOAT_WIDTH 320
Jim_Obj *Jim_FormatString(Jim_Interp *interp, Jim_Obj *fmtObjPtr, int objc, Jim_Obj *const *objv)
{
|
| ︙ | ︙ | |||
20190 20191 20192 20193 20194 20195 20196 |
num_buffer_size = length + 1;
num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
}
j = 0;
for (i = length; i > 0; ) {
i--;
| | | 21965 21966 21967 21968 21969 21970 21971 21972 21973 21974 21975 21976 21977 21978 21979 |
num_buffer_size = length + 1;
num_buffer = Jim_Realloc(num_buffer, num_buffer_size);
}
j = 0;
for (i = length; i > 0; ) {
i--;
if (w & ((unsigned jim_wide)1 << i)) {
num_buffer[j++] = '1';
}
else if (j || i == 0) {
num_buffer[j++] = '0';
}
}
num_buffer[j] = 0;
|
| ︙ | ︙ | |||
20426 20427 20428 20429 20430 20431 20432 |
int n = 0;
while (*seq++) {
n++;
}
return n;
}
| | | 22201 22202 22203 22204 22205 22206 22207 22208 22209 22210 22211 22212 22213 22214 22215 |
int n = 0;
while (*seq++) {
n++;
}
return n;
}
int jim_regcomp(regex_t *preg, const char *exp, int cflags)
{
int scan;
int longest;
unsigned len;
int flags;
#ifdef DEBUG
|
| ︙ | ︙ | |||
20841 20842 20843 20844 20845 20846 20847 |
if (*pattern == ']' || *pattern == '-') {
reg_addrange(preg, *pattern, *pattern);
pattern++;
}
| | > > > > > | 22616 22617 22618 22619 22620 22621 22622 22623 22624 22625 22626 22627 22628 22629 22630 22631 22632 22633 22634 22635 22636 22637 22638 22639 22640 22641 22642 22643 22644 22645 |
if (*pattern == ']' || *pattern == '-') {
reg_addrange(preg, *pattern, *pattern);
pattern++;
}
while (*pattern != ']') {
int start;
int end;
enum {
CC_ALPHA, CC_ALNUM, CC_SPACE, CC_BLANK, CC_UPPER, CC_LOWER,
CC_DIGIT, CC_XDIGIT, CC_CNTRL, CC_GRAPH, CC_PRINT, CC_PUNCT,
CC_NUM
};
int cc;
if (!*pattern) {
preg->err = REG_ERR_UNMATCHED_BRACKET;
return 0;
}
pattern += reg_utf8_tounicode_case(pattern, &start, nocase);
if (start == '\\') {
switch (*pattern) {
case 's':
pattern++;
|
| ︙ | ︙ | |||
20876 20877 20878 20879 20880 20881 20882 20883 20884 20885 20886 20887 20888 20889 20890 20891 20892 20893 20894 20895 20896 20897 20898 20899 20900 20901 20902 20903 20904 20905 20906 20907 20908 20909 20910 20911 20912 20913 20914 |
goto cc_switch;
}
pattern += reg_decode_escape(pattern, &start);
if (start == 0) {
preg->err = REG_ERR_NULL_CHAR;
return 0;
}
}
if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
pattern += utf8_tounicode(pattern, &end);
pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
if (end == '\\') {
pattern += reg_decode_escape(pattern, &end);
if (end == 0) {
preg->err = REG_ERR_NULL_CHAR;
return 0;
}
}
reg_addrange(preg, start, end);
continue;
}
if (start == '[' && pattern[0] == ':') {
static const char *character_class[] = {
":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
};
for (cc = 0; cc < CC_NUM; cc++) {
n = strlen(character_class[cc]);
if (strncmp(pattern, character_class[cc], n) == 0) {
pattern += n + 1;
break;
}
}
if (cc != CC_NUM) {
cc_switch:
| > > > > > > > > > > > > | 22656 22657 22658 22659 22660 22661 22662 22663 22664 22665 22666 22667 22668 22669 22670 22671 22672 22673 22674 22675 22676 22677 22678 22679 22680 22681 22682 22683 22684 22685 22686 22687 22688 22689 22690 22691 22692 22693 22694 22695 22696 22697 22698 22699 22700 22701 22702 22703 22704 22705 22706 |
goto cc_switch;
}
pattern += reg_decode_escape(pattern, &start);
if (start == 0) {
preg->err = REG_ERR_NULL_CHAR;
return 0;
}
if (start == '\\' && *pattern == 0) {
preg->err = REG_ERR_INVALID_ESCAPE;
return 0;
}
}
if (pattern[0] == '-' && pattern[1] && pattern[1] != ']') {
pattern += utf8_tounicode(pattern, &end);
pattern += reg_utf8_tounicode_case(pattern, &end, nocase);
if (end == '\\') {
pattern += reg_decode_escape(pattern, &end);
if (end == 0) {
preg->err = REG_ERR_NULL_CHAR;
return 0;
}
if (end == '\\' && *pattern == 0) {
preg->err = REG_ERR_INVALID_ESCAPE;
return 0;
}
}
reg_addrange(preg, start, end);
continue;
}
if (start == '[' && pattern[0] == ':') {
static const char *character_class[] = {
":alpha:", ":alnum:", ":space:", ":blank:", ":upper:", ":lower:",
":digit:", ":xdigit:", ":cntrl:", ":graph:", ":print:", ":punct:",
};
for (cc = 0; cc < CC_NUM; cc++) {
n = strlen(character_class[cc]);
if (strncmp(pattern, character_class[cc], n) == 0) {
if (pattern[n] != ']') {
preg->err = REG_ERR_UNMATCHED_BRACKET;
return 0;
}
pattern += n + 1;
break;
}
}
if (cc != CC_NUM) {
cc_switch:
|
| ︙ | ︙ | |||
20991 20992 20993 20994 20995 20996 20997 |
case '{':
preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
return 0;
case '\\':
ch = *preg->regparse++;
switch (ch) {
case '\0':
| | | 22783 22784 22785 22786 22787 22788 22789 22790 22791 22792 22793 22794 22795 22796 22797 |
case '{':
preg->err = REG_ERR_COUNT_FOLLOWS_NOTHING;
return 0;
case '\\':
ch = *preg->regparse++;
switch (ch) {
case '\0':
preg->err = REG_ERR_INVALID_ESCAPE;
return 0;
case 'A':
ret = regnode(preg, BOLX);
break;
case 'Z':
ret = regnode(preg, EOLX);
break;
|
| ︙ | ︙ | |||
21178 21179 21180 21181 21182 21183 21184 | } static int regtry(regex_t *preg, const char *string ); static int regmatch(regex_t *preg, int prog); static int regrepeat(regex_t *preg, int p, int max); | | | 22970 22971 22972 22973 22974 22975 22976 22977 22978 22979 22980 22981 22982 22983 22984 |
}
static int regtry(regex_t *preg, const char *string );
static int regmatch(regex_t *preg, int prog);
static int regrepeat(regex_t *preg, int p, int max);
int jim_regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
{
const char *s;
int scan;
if (preg == NULL || preg->program == NULL || string == NULL) {
return REG_ERR_NULL_ARGUMENT;
|
| ︙ | ︙ | |||
21519 21520 21521 21522 21523 21524 21525 |
(isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
return(0);
break;
case WORDZ:
if (preg->reginput > preg->regbol) {
| | | 23311 23312 23313 23314 23315 23316 23317 23318 23319 23320 23321 23322 23323 23324 23325 |
(isalnum(UCHAR(preg->reginput[-1])) || preg->reginput[-1] == '_'))
return(0);
break;
case WORDZ:
if (preg->reginput > preg->regbol) {
if (reg_iseol(preg, c) || !(isalnum(UCHAR(c)) || c == '_')) {
c = preg->reginput[-1];
if (isalnum(UCHAR(c)) || c == '_') {
break;
}
}
}
|
| ︙ | ︙ | |||
21615 21616 21617 21618 21619 21620 21621 21622 21623 21624 21625 21626 21627 21628 |
int no = OP(preg, scan) - CLOSE;
if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
preg->pmatch[no].rm_eo = save - preg->start;
}
}
return(1);
}
return(0);
}
return REG_ERR_INTERNAL;
}
scan = next;
}
| > > | 23407 23408 23409 23410 23411 23412 23413 23414 23415 23416 23417 23418 23419 23420 23421 23422 |
int no = OP(preg, scan) - CLOSE;
if (no < preg->nmatch && preg->pmatch[no].rm_eo == -1) {
preg->pmatch[no].rm_eo = save - preg->start;
}
}
return(1);
}
preg->reginput = save;
return(0);
}
return REG_ERR_INTERNAL;
}
scan = next;
}
|
| ︙ | ︙ | |||
21638 21639 21640 21641 21642 21643 21644 |
int ch;
int n;
scan = preg->reginput;
opnd = OPERAND(p);
switch (OP(preg, p)) {
case ANY:
| < | | 23432 23433 23434 23435 23436 23437 23438 23439 23440 23441 23442 23443 23444 23445 23446 23447 23448 |
int ch;
int n;
scan = preg->reginput;
opnd = OPERAND(p);
switch (OP(preg, p)) {
case ANY:
while (!reg_iseol(preg, *scan) && count < max) {
count++;
scan += utf8_charlen(*scan);
}
break;
case EXACTLY:
while (count < max) {
n = reg_utf8_tounicode_case(scan, &ch, preg->cflags & REG_ICASE);
if (preg->program[opnd] != ch) {
break;
|
| ︙ | ︙ | |||
21722 21723 21724 21725 21726 21727 21728 | return s - p; } } return 2; } | | | > | | 23515 23516 23517 23518 23519 23520 23521 23522 23523 23524 23525 23526 23527 23528 23529 23530 23531 23532 23533 23534 23535 23536 23537 23538 23539 23540 23541 23542 23543 23544 23545 23546 23547 23548 23549 23550 23551 23552 23553 23554 23555 23556 23557 23558 23559 23560 23561 23562 23563 23564 23565 |
return s - p;
}
}
return 2;
}
size_t jim_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
{
static const char *error_strings[] = {
"success",
"no match",
"bad pattern",
"null argument",
"unknown error",
"too big",
"out of memory",
"too many ()",
"parentheses () not balanced",
"braces {} not balanced",
"invalid repetition count(s)",
"extra characters",
"*+ of empty atom",
"nested count",
"internal error",
"count follows nothing",
"invalid escape \\ sequence",
"corrupted program",
"contains null char",
"brackets [] not balanced",
};
const char *err;
if (errcode < 0 || errcode >= REG_ERR_NUM) {
err = "Bad error code";
}
else {
err = error_strings[errcode];
}
return snprintf(errbuf, errbuf_size, "%s", err);
}
void jim_regfree(regex_t *preg)
{
free(preg->program);
}
#endif
#include <string.h>
|
| ︙ | ︙ | |||
21854 21855 21856 21857 21858 21859 21860 |
case ERROR_PIPE_BUSY: return EAGAIN;
case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
case ERROR_DIRECTORY: return ENOTDIR;
}
return EINVAL;
}
| > > > > > > > > | > > > > > > > > > > > > > > > > | | | > > | | 23648 23649 23650 23651 23652 23653 23654 23655 23656 23657 23658 23659 23660 23661 23662 23663 23664 23665 23666 23667 23668 23669 23670 23671 23672 23673 23674 23675 23676 23677 23678 23679 23680 23681 23682 23683 23684 23685 23686 23687 23688 23689 23690 23691 23692 23693 23694 23695 23696 23697 |
case ERROR_PIPE_BUSY: return EAGAIN;
case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
case ERROR_DIRECTORY: return ENOTDIR;
}
return EINVAL;
}
long JimProcessPid(phandle_t pid)
{
if (pid == INVALID_HANDLE_VALUE) {
return -1;
}
return GetProcessId(pid);
}
phandle_t JimWaitPid(long pid, int *status, int nohang)
{
if (pid > 0) {
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, pid);
if (h) {
long pid = waitpid(h, status, nohang);
CloseHandle(h);
if (pid > 0) {
return h;
}
}
}
return JIM_BAD_PHANDLE;
}
long waitpid(phandle_t phandle, int *status, int nohang)
{
long pid;
DWORD ret = WaitForSingleObject(phandle, nohang ? 0 : INFINITE);
if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
return -1;
}
GetExitCodeProcess(phandle, &ret);
*status = ret;
pid = GetProcessId(phandle);
CloseHandle(phandle);
return pid;
}
int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
{
char name[MAX_PATH];
HANDLE handle;
|
| ︙ | ︙ | |||
21885 21886 21887 21888 21889 21890 21891 |
NULL);
if (handle == INVALID_HANDLE_VALUE) {
goto error;
}
Jim_SetResultString(interp, name, -1);
| | | 23705 23706 23707 23708 23709 23710 23711 23712 23713 23714 23715 23716 23717 23718 23719 |
NULL);
if (handle == INVALID_HANDLE_VALUE) {
goto error;
}
Jim_SetResultString(interp, name, -1);
return _open_osfhandle((intptr_t)handle, _O_RDWR | _O_TEXT);
error:
Jim_SetResultErrno(interp, name);
DeleteFile(name);
return -1;
}
|
| ︙ | ︙ | |||
21940 21941 21942 21943 21944 21945 21946 21947 21948 21949 21950 21951 21952 21953 21954 21955 21956 21957 21958 21959 21960 21961 21962 21963 21964 21965 |
Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
}
else {
filenameObj = Jim_NewStringObj(interp, filename_template, -1);
}
mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
#ifdef HAVE_MKSTEMP
fd = mkstemp(filenameObj->bytes);
#else
if (mktemp(filenameObj->bytes) == NULL) {
fd = -1;
}
else {
fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
}
#endif
umask(mask);
if (fd < 0) {
Jim_SetResultErrno(interp, Jim_String(filenameObj));
Jim_FreeNewObj(interp, filenameObj);
return -1;
}
if (unlink_file) {
remove(Jim_String(filenameObj));
| > > > > | 23760 23761 23762 23763 23764 23765 23766 23767 23768 23769 23770 23771 23772 23773 23774 23775 23776 23777 23778 23779 23780 23781 23782 23783 23784 23785 23786 23787 23788 23789 |
Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
}
else {
filenameObj = Jim_NewStringObj(interp, filename_template, -1);
}
#ifdef HAVE_UMASK
mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
#endif
#ifdef HAVE_MKSTEMP
fd = mkstemp(filenameObj->bytes);
#else
if (mktemp(filenameObj->bytes) == NULL) {
fd = -1;
}
else {
fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
}
#endif
#ifdef HAVE_UMASK
umask(mask);
#endif
if (fd < 0) {
Jim_SetResultErrno(interp, Jim_String(filenameObj));
Jim_FreeNewObj(interp, filenameObj);
return -1;
}
if (unlink_file) {
remove(Jim_String(filenameObj));
|
| ︙ | ︙ | |||
22123 22124 22125 22126 22127 22128 22129 22130 22131 22132 22133 22134 22135 22136 22137 22138 22139 22140 22141 22142 22143 22144 22145 22146 22147 22148 22149 22150 22151 |
}
snprintf(buf, sizeof(buf), "%d", sig);
return buf;
}
#ifndef JIM_BOOTSTRAP_LIB_ONLY
#include <errno.h>
#include <string.h>
#ifdef USE_LINENOISE
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "linenoise.h"
#else
#define MAX_LINE_LEN 512
#endif
#ifdef USE_LINENOISE
static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
static const char completion_callback_assoc_key[] = "interactive-completion";
#endif
char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
{
#ifdef USE_LINENOISE
| > > > > > > > > > > > | | > > > > > > | | | 23947 23948 23949 23950 23951 23952 23953 23954 23955 23956 23957 23958 23959 23960 23961 23962 23963 23964 23965 23966 23967 23968 23969 23970 23971 23972 23973 23974 23975 23976 23977 23978 23979 23980 23981 23982 23983 23984 23985 23986 23987 23988 23989 23990 23991 23992 23993 23994 23995 23996 23997 23998 23999 24000 24001 24002 24003 24004 24005 24006 24007 24008 24009 24010 24011 24012 24013 24014 24015 24016 24017 24018 24019 24020 24021 24022 24023 24024 |
}
snprintf(buf, sizeof(buf), "%d", sig);
return buf;
}
#ifndef JIM_BOOTSTRAP_LIB_ONLY
#include <errno.h>
#include <string.h>
#include <stdio.h>
#ifdef USE_LINENOISE
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "linenoise.h"
#else
#define MAX_LINE_LEN 512
#endif
#ifdef USE_LINENOISE
struct JimCompletionInfo {
Jim_Interp *interp;
Jim_Obj *completion_command;
Jim_Obj *hints_command;
};
static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp);
static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata);
static const char completion_callback_assoc_key[] = "interactive-completion";
static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata);
static void JimFreeHintsCallback(void *hint, void *userdata);
#endif
char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt)
{
#ifdef USE_LINENOISE
struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
char *result;
Jim_Obj *objPtr;
long mlmode = 0;
if (compinfo->completion_command) {
linenoiseSetCompletionCallback(JimCompletionCallback, compinfo);
}
if (compinfo->hints_command) {
linenoiseSetHintsCallback(JimHintsCallback, compinfo);
linenoiseSetFreeHintsCallback(JimFreeHintsCallback);
}
objPtr = Jim_GetVariableStr(interp, "history::multiline", JIM_NONE);
if (objPtr && Jim_GetLong(interp, objPtr, &mlmode) == JIM_NONE) {
linenoiseSetMultiLine(mlmode);
}
result = linenoise(prompt);
linenoiseSetCompletionCallback(NULL, NULL);
linenoiseSetHintsCallback(NULL, NULL);
linenoiseSetFreeHintsCallback(NULL);
return result;
#else
int len;
char *line = Jim_Alloc(MAX_LINE_LEN);
fputs(prompt, stdout);
fflush(stdout);
if (fgets(line, MAX_LINE_LEN, stdin) == NULL) {
Jim_Free(line);
return NULL;
}
len = strlen(line);
if (len && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
return line;
|
| ︙ | ︙ | |||
22222 22223 22224 22225 22226 22227 22228 22229 |
char **history = linenoiseHistory(&len);
for (i = 0; i < len; i++) {
printf("%4d %s\n", i + 1, history[i]);
}
#endif
}
#ifdef USE_LINENOISE
| > > > > > | | > > > | < > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | > > | | > > | | > | | > > > | | | > > > | > > | 24063 24064 24065 24066 24067 24068 24069 24070 24071 24072 24073 24074 24075 24076 24077 24078 24079 24080 24081 24082 24083 24084 24085 24086 24087 24088 24089 24090 24091 24092 24093 24094 24095 24096 24097 24098 24099 24100 24101 24102 24103 24104 24105 24106 24107 24108 24109 24110 24111 24112 24113 24114 24115 24116 24117 24118 24119 24120 24121 24122 24123 24124 24125 24126 24127 24128 24129 24130 24131 24132 24133 24134 24135 24136 24137 24138 24139 24140 24141 24142 24143 24144 24145 24146 24147 24148 24149 24150 24151 24152 24153 24154 24155 24156 24157 24158 24159 24160 24161 24162 24163 24164 24165 24166 24167 24168 24169 24170 24171 24172 24173 24174 24175 24176 24177 24178 24179 24180 24181 24182 24183 24184 24185 24186 24187 24188 24189 24190 24191 24192 24193 24194 24195 24196 24197 24198 24199 24200 24201 24202 24203 24204 24205 24206 24207 24208 24209 24210 24211 24212 24213 24214 24215 24216 24217 24218 24219 24220 24221 24222 24223 24224 24225 24226 24227 24228 24229 |
char **history = linenoiseHistory(&len);
for (i = 0; i < len; i++) {
printf("%4d %s\n", i + 1, history[i]);
}
#endif
}
void Jim_HistorySetMaxLen(int length)
{
#ifdef USE_LINENOISE
linenoiseHistorySetMaxLen(length);
#endif
}
int Jim_HistoryGetMaxLen(void)
{
#ifdef USE_LINENOISE
return linenoiseHistoryGetMaxLen();
#endif
return 0;
}
#ifdef USE_LINENOISE
static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata)
{
struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
Jim_Obj *objv[2];
int ret;
objv[0] = info->completion_command;
objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
ret = Jim_EvalObjVector(info->interp, 2, objv);
if (ret == JIM_OK) {
int i;
Jim_Obj *listObj = Jim_GetResult(info->interp);
int len = Jim_ListLength(info->interp, listObj);
for (i = 0; i < len; i++) {
linenoiseAddCompletion(comp, Jim_String(Jim_ListGetIndex(info->interp, listObj, i)));
}
}
}
static char *JimHintsCallback(const char *prefix, int *color, int *bold, void *userdata)
{
struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata;
Jim_Obj *objv[2];
int ret;
char *result = NULL;
objv[0] = info->hints_command;
objv[1] = Jim_NewStringObj(info->interp, prefix, -1);
ret = Jim_EvalObjVector(info->interp, 2, objv);
if (ret == JIM_OK) {
Jim_Obj *listObj = Jim_GetResult(info->interp);
Jim_IncrRefCount(listObj);
int len = Jim_ListLength(info->interp, listObj);
if (len >= 1) {
long x;
result = Jim_StrDup(Jim_String(Jim_ListGetIndex(info->interp, listObj, 0)));
if (len >= 2 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 1), &x) == JIM_OK) {
*color = x;
}
if (len >= 3 && Jim_GetLong(info->interp, Jim_ListGetIndex(info->interp, listObj, 2), &x) == JIM_OK) {
*bold = x;
}
}
Jim_DecrRefCount(info->interp, listObj);
}
return result;
}
static void JimFreeHintsCallback(void *hint, void *userdata)
{
Jim_Free(hint);
}
static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data)
{
struct JimCompletionInfo *compinfo = data;
if (compinfo->completion_command) {
Jim_DecrRefCount(interp, compinfo->completion_command);
}
if (compinfo->hints_command) {
Jim_DecrRefCount(interp, compinfo->hints_command);
}
Jim_Free(compinfo);
}
static struct JimCompletionInfo *JimGetCompletionInfo(Jim_Interp *interp)
{
struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key);
if (compinfo == NULL) {
compinfo = Jim_Alloc(sizeof(*compinfo));
compinfo->interp = interp;
compinfo->completion_command = NULL;
compinfo->hints_command = NULL;
Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo);
}
return compinfo;
}
#endif
void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *completionCommandObj)
{
#ifdef USE_LINENOISE
struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
if (completionCommandObj) {
Jim_IncrRefCount(completionCommandObj);
}
if (compinfo->completion_command) {
Jim_DecrRefCount(interp, compinfo->completion_command);
}
compinfo->completion_command = completionCommandObj;
#endif
}
void Jim_HistorySetHints(Jim_Interp *interp, Jim_Obj *hintsCommandObj)
{
#ifdef USE_LINENOISE
struct JimCompletionInfo *compinfo = JimGetCompletionInfo(interp);
if (hintsCommandObj) {
Jim_IncrRefCount(hintsCommandObj);
}
if (compinfo->hints_command) {
Jim_DecrRefCount(interp, compinfo->hints_command);
}
compinfo->hints_command = hintsCommandObj;
#endif
}
int Jim_InteractivePrompt(Jim_Interp *interp)
{
int retcode = JIM_OK;
char *history_file = NULL;
#ifdef USE_LINENOISE
const char *home;
home = getenv("HOME");
if (home && isatty(STDIN_FILENO)) {
int history_len = strlen(home) + sizeof("/.jim_history");
history_file = Jim_Alloc(history_len);
snprintf(history_file, history_len, "%s/.jim_history", home);
Jim_HistoryLoad(history_file);
}
Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1));
Jim_HistorySetHints(interp, Jim_NewStringObj(interp, "tcl::stdhint", -1));
#endif
printf("Welcome to Jim version %d.%d\n",
JIM_VERSION / 100, JIM_VERSION % 100);
Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, "1");
while (1) {
|
| ︙ | ︙ | |||
22342 22343 22344 22345 22346 22347 22348 |
goto out;
}
if (Jim_Length(scriptObjPtr) != 0) {
Jim_AppendString(interp, scriptObjPtr, "\n", 1);
}
Jim_AppendString(interp, scriptObjPtr, line, -1);
| | | 24262 24263 24264 24265 24266 24267 24268 24269 24270 24271 24272 24273 24274 24275 24276 |
goto out;
}
if (Jim_Length(scriptObjPtr) != 0) {
Jim_AppendString(interp, scriptObjPtr, "\n", 1);
}
Jim_AppendString(interp, scriptObjPtr, line, -1);
Jim_Free(line);
if (Jim_ScriptIsComplete(interp, scriptObjPtr, &state))
break;
snprintf(prompt, sizeof(prompt), "%c> ", state);
}
#ifdef USE_LINENOISE
if (strcmp(Jim_String(scriptObjPtr), "h") == 0) {
|
| ︙ | ︙ | |||
22372 22373 22374 22375 22376 22377 22378 |
break;
}
if (retcode == JIM_ERR) {
Jim_MakeErrorMessage(interp);
}
result = Jim_GetString(Jim_GetResult(interp), &reslen);
if (reslen) {
| > | > > | 24292 24293 24294 24295 24296 24297 24298 24299 24300 24301 24302 24303 24304 24305 24306 24307 24308 24309 |
break;
}
if (retcode == JIM_ERR) {
Jim_MakeErrorMessage(interp);
}
result = Jim_GetString(Jim_GetResult(interp), &reslen);
if (reslen) {
if (fwrite(result, reslen, 1, stdout) == 0) {
}
putchar('\n');
}
}
out:
Jim_Free(history_file);
return retcode;
}
|
| ︙ | ︙ | |||
22455 22456 22457 22458 22459 22460 22461 22462 22463 22464 22465 22466 22467 22468 22469 22470 22471 22472 22473 22474 22475 22476 22477 22478 22479 22480 |
if (Jim_InitStaticExtensions(interp) != JIM_OK) {
JimPrintErrorMessage(interp);
}
Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
retcode = Jim_initjimshInit(interp);
if (argc == 1) {
if (retcode == JIM_ERR) {
JimPrintErrorMessage(interp);
}
if (retcode != JIM_EXIT) {
JimSetArgv(interp, 0, NULL);
retcode = Jim_InteractivePrompt(interp);
}
}
else {
if (argc > 2 && strcmp(argv[1], "-e") == 0) {
JimSetArgv(interp, argc - 3, argv + 3);
retcode = Jim_Eval(interp, argv[2]);
if (retcode != JIM_ERR) {
| > > > > > > | > > > > | 24378 24379 24380 24381 24382 24383 24384 24385 24386 24387 24388 24389 24390 24391 24392 24393 24394 24395 24396 24397 24398 24399 24400 24401 24402 24403 24404 24405 24406 24407 24408 24409 24410 24411 24412 24413 24414 24415 24416 24417 24418 24419 24420 24421 |
if (Jim_InitStaticExtensions(interp) != JIM_OK) {
JimPrintErrorMessage(interp);
}
Jim_SetVariableStrWithStr(interp, "jim::argv0", orig_argv0);
Jim_SetVariableStrWithStr(interp, JIM_INTERACTIVE, argc == 1 ? "1" : "0");
#ifdef USE_LINENOISE
Jim_SetVariableStrWithStr(interp, "jim::lineedit", "1");
#else
Jim_SetVariableStrWithStr(interp, "jim::lineedit", "0");
#endif
retcode = Jim_initjimshInit(interp);
if (argc == 1) {
if (retcode == JIM_ERR) {
JimPrintErrorMessage(interp);
}
if (retcode != JIM_EXIT) {
JimSetArgv(interp, 0, NULL);
retcode = Jim_InteractivePrompt(interp);
}
}
else {
if (argc > 2 && strcmp(argv[1], "-e") == 0) {
JimSetArgv(interp, argc - 3, argv + 3);
retcode = Jim_Eval(interp, argv[2]);
if (retcode != JIM_ERR) {
int len;
const char *msg = Jim_GetString(Jim_GetResult(interp), &len);
if (fwrite(msg, len, 1, stdout) == 0) {
}
putchar('\n');
}
}
else {
Jim_SetVariableStr(interp, "argv0", Jim_NewStringObj(interp, argv[1], -1));
JimSetArgv(interp, argc - 2, argv + 2);
if (strcmp(argv[1], "-") == 0) {
retcode = Jim_Eval(interp, "eval [info source [stdin read] stdin 1]");
|
| ︙ | ︙ |
Changes to autosetup/pkg-config.tcl.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 | # If the C compiler doesn't support '-print-sysroot', the path can be supplied # by the '--sysroot' option or by defining 'SYSROOT'. # # 'PKG_CONFIG' may be set to use an alternative to 'pkg-config'. use cc | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# If the C compiler doesn't support '-print-sysroot', the path can be supplied
# by the '--sysroot' option or by defining 'SYSROOT'.
#
# 'PKG_CONFIG' may be set to use an alternative to 'pkg-config'.
use cc
options {
sysroot:dir => "Override compiler sysroot for pkg-config search path"
}
# @pkg-config-init ?required?
#
# Initialises the 'pkg-config' system. Unless '$required' is set to 0,
# it is a fatal error if a usable 'pkg-config' is not found .
|
| ︙ | ︙ | |||
49 50 51 52 53 54 55 |
set found 1
if {[opt-str sysroot o]} {
define SYSROOT [file-normalize $o]
msg-result "Using specified sysroot [get-define SYSROOT]"
} elseif {[get-define build] ne [get-define host]} {
| | > > > > | 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 |
set found 1
if {[opt-str sysroot o]} {
define SYSROOT [file-normalize $o]
msg-result "Using specified sysroot [get-define SYSROOT]"
} elseif {[get-define build] ne [get-define host]} {
if {[catch {exec-with-stderr {*}[get-define CC] -print-sysroot} result errinfo] == 0} {
# Use the compiler sysroot, if there is one
define SYSROOT $result
msg-result "Found compiler sysroot $result"
} else {
configlog "[get-define CC] -print-sysroot: $result"
set msg "pkg-config: Cross compiling, but no compiler sysroot and no --sysroot supplied"
if {$required} {
user-error $msg
} else {
msg-result $msg
}
set found 0
}
}
if {[is-defined SYSROOT]} {
set sysroot [get-define SYSROOT]
# XXX: It's possible that these should be set only when invoking pkg-config
global env
set env(PKG_CONFIG_DIR) ""
# Supposedly setting PKG_CONFIG_LIBDIR means that PKG_CONFIG_PATH is ignored,
# but it doesn't seem to work that way in practice
set env(PKG_CONFIG_PATH) ""
# Do we need to try /usr/local as well or instead?
set env(PKG_CONFIG_LIBDIR) $sysroot/usr/lib/pkgconfig:$sysroot/usr/share/pkgconfig
set env(PKG_CONFIG_SYSROOT_DIR) $sysroot
}
}
define HAVE_PKG_CONFIG $found
return $found
|
| ︙ | ︙ | |||
104 105 106 107 108 109 110 |
msg-checking "Checking for $module $args..."
if {!$ok} {
msg-result "no pkg-config"
return 0
}
| > > | > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > | 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 134 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 |
msg-checking "Checking for $module $args..."
if {!$ok} {
msg-result "no pkg-config"
return 0
}
set pkgconfig [get-define PKG_CONFIG]
set ret [catch {exec $pkgconfig --modversion "$module $args"} version]
configlog "$pkgconfig --modversion $module $args: $version"
if {$ret} {
msg-result "not found"
return 0
}
# Sometimes --modversion succeeds but because of dependencies it isn't usable
# This seems to show up with --cflags
set ret [catch {exec $pkgconfig --cflags $module} cflags]
if {$ret} {
msg-result "unusable ($version - see config.log)"
configlog "$pkgconfig --cflags $module"
configlog $cflags
return 0
}
msg-result $version
set prefix [feature-define-name $module PKG_]
define HAVE_${prefix}
define ${prefix}_VERSION $version
define ${prefix}_CFLAGS $cflags
define ${prefix}_LIBS [exec $pkgconfig --libs-only-l $module]
define ${prefix}_LDFLAGS [exec $pkgconfig --libs-only-L $module]
return 1
}
# @pkg-config-get module setting
#
# Convenience access to the results of 'pkg-config'.
#
# For example, '[pkg-config-get pango CFLAGS]' returns
# the value of 'PKG_PANGO_CFLAGS', or '""' if not defined.
proc pkg-config-get {module name} {
set prefix [feature-define-name $module PKG_]
get-define ${prefix}_${name} ""
}
# @pkg-config-get-var module variable
#
# Return the value of the given variable from the given pkg-config module.
# The module must already have been successfully detected with pkg-config.
# e.g.
#
## if {[pkg-config harfbuzz >= 2.5]} {
## define harfbuzz_libdir [pkg-config-get-var harfbuzz libdir]
## }
#
# Returns the empty string if the variable isn't defined.
proc pkg-config-get-var {module variable} {
set pkgconfig [get-define PKG_CONFIG]
set prefix [feature-define-name $module HAVE_PKG_]
exec $pkgconfig $module --variable $variable
}
|
Changes to autosetup/system.tcl.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 | ## sysconfdir ## sharedstatedir ## localstatedir ## infodir ## mandir ## includedir # | | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
## sysconfdir
## sharedstatedir
## localstatedir
## infodir
## mandir
## includedir
#
# If '--prefix' is not supplied, it defaults to '/usr/local' unless 'options-defaults { prefix ... }' is used *before*
# including the 'system' module.
if {[is-defined defaultprefix]} {
user-notice "Note: defaultprefix is deprecated. Use options-defaults to set default options"
options-defaults [list prefix [get-define defaultprefix]]
}
options {
host:host-alias => {a complete or partial cpu-vendor-opsys for the system where
the application will run (defaults to the same value as --build)}
build:build-alias => {a complete or partial cpu-vendor-opsys for the system
where the application will be built (defaults to the
result of running config.guess)}
prefix:dir=/usr/local => {the target directory for the build (default: '@default@')}
|
| ︙ | ︙ | |||
48 49 50 51 52 53 54 | sysconfdir: sharedstatedir: localstatedir: runstatedir: maintainer-mode=0 dependency-tracking=0 silent-rules=0 | < > > > > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
sysconfdir:
sharedstatedir:
localstatedir:
runstatedir:
maintainer-mode=0
dependency-tracking=0
silent-rules=0
program-prefix:
program-suffix:
program-transform-name:
}
# @check-feature name { script }
#
# defines feature '$name' to the return value of '$script',
# which should be 1 if found or 0 if not found.
#
# e.g. the following will define 'HAVE_CONST' to 0 or 1.
|
| ︙ | ︙ | |||
202 203 204 205 206 207 208 |
if {[llength $condstack] == 0} {
user-notice "$infile:$linenum: Error: @endif missing @if"
}
lpop condstack
}
}
continue
| > > > > > | > | < < < < | 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 234 235 |
if {[llength $condstack] == 0} {
user-notice "$infile:$linenum: Error: @endif missing @if"
}
lpop condstack
}
}
continue
}
# Only continue if the stack contains all "true"
if {"0" in $condstack} {
continue
}
if {[regexp {^@include\s+(.*)} $line -> filearg]} {
set incfile [string map $mapping $filearg]
if {[file exists $incfile]} {
lappend ::autosetup(deps) [file-normalize $incfile]
lappend result {*}[include-file $incfile $mapping]
} else {
user-error "$infile:$linenum: Include file $incfile is missing"
}
continue
}
if {[regexp {^@define\s+(\w+)\s+(.*)} $line -> var val]} {
define $var $val
continue
}
lappend result $line
}
return $result
}
|
| ︙ | ︙ |
Changes to autosetup/tmake.auto.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 52 53 54 55 |
# Not configured, so don't process subdirs
AutoSubDirs off
# And don't process this file any further
ifconfig false
}
}
if {![file exists build.spec]} {
puts "Note: I don't see build.spec. Try running: tmake --genie"
}
}
| > > > > > > > > > > > > > > > > > > | 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 |
# Not configured, so don't process subdirs
AutoSubDirs off
# And don't process this file any further
ifconfig false
}
}
set configure [readfile configure]
# XXX Do we need also need to support a system install of tmake?
if {[string first {#@TMAKEUPDATED@} $configure] < 0} {
if {[regsub {#@@INITCHECK@@#} $configure \
{test -z "$TMAKE" -a -x "$dir/tmake" \&\& exec "$dir/tmake" --force --configure "$@"; #@TMAKEUPDATED@} configure]} {
writefile configure $configure\n
exec chmod +x configure
puts "Updated configure to invoke local tmake."
if {![file exec autosetup/tmake]} {
puts "Warning: autosetup/tmake is missing."
puts " Install it with: tmake --install=autosetup"
}
} else {
puts "Warning: configure isn't created by a recent autosetup, not updating."
}
} else {
puts "I see configure for tmake already exists."
}
if {![file exists build.spec]} {
puts "Note: I don't see build.spec. Try running: tmake --genie"
}
}
|
Changes to autosetup/tmake.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'tmake' module makes it easy to support the tmake build system. # # The following variables are set: # ## CONFIGURED - to indicate that the project is configured use system | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# @synopsis:
#
# The 'tmake' module makes it easy to support the tmake build system.
#
# The following variables are set:
#
## CONFIGURED - to indicate that the project is configured
use system
options {}
define CONFIGURED
# @make-tmake-settings outfile patterns ...
#
# Examines all defined variables which match the given patterns (defaults to '*')
# and writes a tmake-compatible .conf file defining those variables.
|
| ︙ | ︙ |
Changes to compat/zlib/CMakeLists.txt.
1 2 3 4 5 | cmake_minimum_required(VERSION 2.4.4...3.15.0) set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON) project(zlib C) | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
cmake_minimum_required(VERSION 2.4.4...3.15.0)
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
project(zlib C)
set(VERSION "1.3.1")
option(ZLIB_BUILD_EXAMPLES "Enable Zlib Examples" ON)
set(INSTALL_BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation directory for executables")
set(INSTALL_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation directory for libraries")
set(INSTALL_INC_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "Installation directory for headers")
set(INSTALL_MAN_DIR "${CMAKE_INSTALL_PREFIX}/share/man" CACHE PATH "Installation directory for manual pages")
set(INSTALL_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH "Installation directory for pkgconfig (.pc) files")
|
| ︙ | ︙ | |||
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 |
-I ${CMAKE_CURRENT_BINARY_DIR}
-o ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj
-i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zlib1.rc)
set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj)
endif(MINGW)
add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL)
set_target_properties(zlib PROPERTIES SOVERSION 1)
if(NOT CYGWIN)
# This property causes shared libraries on Linux to have the full version
# encoded into their final filename. We disable this on Cygwin because
# it causes cygz-${ZLIB_FULL_VERSION}.dll to be created when cygz.dll
# seems to be the default.
#
# This has no effect with MSVC, on that platform the version info for
# the DLL comes from the resource file win32/zlib1.rc
set_target_properties(zlib PROPERTIES VERSION ${ZLIB_FULL_VERSION})
endif()
if(UNIX)
# On unix-like platforms the library is almost always called libz
set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z)
| > > | | 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 180 |
-I ${CMAKE_CURRENT_BINARY_DIR}
-o ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj
-i ${CMAKE_CURRENT_SOURCE_DIR}/win32/zlib1.rc)
set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj)
endif(MINGW)
add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
target_include_directories(zlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
target_include_directories(zlibstatic PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL)
set_target_properties(zlib PROPERTIES SOVERSION 1)
if(NOT CYGWIN)
# This property causes shared libraries on Linux to have the full version
# encoded into their final filename. We disable this on Cygwin because
# it causes cygz-${ZLIB_FULL_VERSION}.dll to be created when cygz.dll
# seems to be the default.
#
# This has no effect with MSVC, on that platform the version info for
# the DLL comes from the resource file win32/zlib1.rc
set_target_properties(zlib PROPERTIES VERSION ${ZLIB_FULL_VERSION})
endif()
if(UNIX)
# On unix-like platforms the library is almost always called libz
set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z)
if(NOT APPLE AND NOT(CMAKE_SYSTEM_NAME STREQUAL AIX))
set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\"")
endif()
elseif(BUILD_SHARED_LIBS AND WIN32)
# Creates zlib1.dll when building shared library version
set_target_properties(zlib PROPERTIES SUFFIX "1.dll")
endif()
|
| ︙ | ︙ | |||
189 190 191 192 193 194 195 |
if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
install(FILES ${ZLIB_PC} DESTINATION "${INSTALL_PKGCONFIG_DIR}")
endif()
#============================================================================
# Example binaries
#============================================================================
| | | | | | | | | | | | | | | | > | 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
if(NOT SKIP_INSTALL_FILES AND NOT SKIP_INSTALL_ALL )
install(FILES ${ZLIB_PC} DESTINATION "${INSTALL_PKGCONFIG_DIR}")
endif()
#============================================================================
# Example binaries
#============================================================================
if(ZLIB_BUILD_EXAMPLES)
add_executable(example test/example.c)
target_link_libraries(example zlib)
add_test(example example)
add_executable(minigzip test/minigzip.c)
target_link_libraries(minigzip zlib)
if(HAVE_OFF64_T)
add_executable(example64 test/example.c)
target_link_libraries(example64 zlib)
set_target_properties(example64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64")
add_test(example64 example64)
add_executable(minigzip64 test/minigzip.c)
target_link_libraries(minigzip64 zlib)
set_target_properties(minigzip64 PROPERTIES COMPILE_FLAGS "-D_FILE_OFFSET_BITS=64")
endif()
endif()
|
Changes to compat/zlib/ChangeLog.
1 2 3 4 5 6 7 8 9 10 |
ChangeLog file for zlib
Changes in 1.3 (18 Aug 2023)
- Remove K&R function definitions and zlib2ansi
- Fix bug in deflateBound() for level 0 and memLevel 9
- Fix bug when gzungetc() is used immediately after gzopen()
- Fix bug when using gzflush() with a very small buffer
- Fix crash when gzsetparams() attempted for transparent write
- Fix test/example.c to work with FORCE_STORED
| > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
ChangeLog file for zlib
Changes in 1.3.1 (22 Jan 2024)
- Reject overflows of zip header fields in minizip
- Fix bug in inflateSync() for data held in bit buffer
- Add LIT_MEM define to use more memory for a small deflate speedup
- Fix decision on the emission of Zip64 end records in minizip
- Add bounds checking to ERR_MSG() macro, used by zError()
- Neutralize zip file traversal attacks in miniunz
- Fix a bug in ZLIB_DEBUG compiles in check_match()
- Various portability and appearance improvements
Changes in 1.3 (18 Aug 2023)
- Remove K&R function definitions and zlib2ansi
- Fix bug in deflateBound() for level 0 and memLevel 9
- Fix bug when gzungetc() is used immediately after gzopen()
- Fix bug when using gzflush() with a very small buffer
- Fix crash when gzsetparams() attempted for transparent write
- Fix test/example.c to work with FORCE_STORED
|
| ︙ | ︙ |
Changes to compat/zlib/FAQ.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
1. Is zlib Y2K-compliant?
Yes. zlib doesn't handle dates.
2. Where can I get a Windows DLL version?
The zlib sources can be compiled without change to produce a DLL. See the
| | < | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
1. Is zlib Y2K-compliant?
Yes. zlib doesn't handle dates.
2. Where can I get a Windows DLL version?
The zlib sources can be compiled without change to produce a DLL. See the
file win32/DLL_FAQ.txt in the zlib distribution.
3. Where can I get a Visual Basic interface to zlib?
See
* http://marknelson.us/1997/01/01/zlib-engine/
* win32/DLL_FAQ.txt in the zlib distribution
|
| ︙ | ︙ |
Changes to compat/zlib/Makefile.in.
1 | # Makefile for zlib | | | 1 2 3 4 5 6 7 8 9 | # Makefile for zlib # Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler # For conditions of distribution and use, see copyright notice in zlib.h # To compile and test, type: # ./configure; make test # Normally configure builds both a static and a shared library. # If you want to build just a static library, use: ./configure --static |
| ︙ | ︙ | |||
18 19 20 21 22 23 24 | #CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 #CFLAGS=-g -DZLIB_DEBUG #CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ # -Wstrict-prototypes -Wmissing-prototypes SFLAGS=-O LDFLAGS= | | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 #CFLAGS=-g -DZLIB_DEBUG #CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ # -Wstrict-prototypes -Wmissing-prototypes SFLAGS=-O LDFLAGS= TEST_LIBS=-L. libz.a LDSHARED=$(CC) CPP=$(CC) -E STATICLIB=libz.a SHAREDLIB=libz.so SHAREDLIBV=libz.so.1.3.1 SHAREDLIBM=libz.so.1 LIBS=$(STATICLIB) $(SHAREDLIBV) AR=ar ARFLAGS=rc RANLIB=ranlib LDCONFIG=ldconfig |
| ︙ | ︙ | |||
278 279 280 281 282 283 284 | $(LDSHARED) $(SFLAGS) -o $@ $(PIC_OBJS) $(LDSHAREDLIBC) $(LDFLAGS) rm -f $(SHAREDLIB) $(SHAREDLIBM) ln -s $@ $(SHAREDLIB) ln -s $@ $(SHAREDLIBM) -@rmdir objs example$(EXE): example.o $(STATICLIB) | | | | | | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | $(LDSHARED) $(SFLAGS) -o $@ $(PIC_OBJS) $(LDSHAREDLIBC) $(LDFLAGS) rm -f $(SHAREDLIB) $(SHAREDLIBM) ln -s $@ $(SHAREDLIB) ln -s $@ $(SHAREDLIBM) -@rmdir objs example$(EXE): example.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ example.o $(TEST_LIBS) minigzip$(EXE): minigzip.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip.o $(TEST_LIBS) examplesh$(EXE): example.o $(SHAREDLIBV) $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) -L. $(SHAREDLIBV) minigzipsh$(EXE): minigzip.o $(SHAREDLIBV) $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS) -L. $(SHAREDLIBV) example64$(EXE): example64.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ example64.o $(TEST_LIBS) minigzip64$(EXE): minigzip64.o $(STATICLIB) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ minigzip64.o $(TEST_LIBS) install-libs: $(LIBS) -@if [ ! -d $(DESTDIR)$(exec_prefix) ]; then mkdir -p $(DESTDIR)$(exec_prefix); fi -@if [ ! -d $(DESTDIR)$(libdir) ]; then mkdir -p $(DESTDIR)$(libdir); fi -@if [ ! -d $(DESTDIR)$(sharedlibdir) ]; then mkdir -p $(DESTDIR)$(sharedlibdir); fi -@if [ ! -d $(DESTDIR)$(man3dir) ]; then mkdir -p $(DESTDIR)$(man3dir); fi -@if [ ! -d $(DESTDIR)$(pkgconfigdir) ]; then mkdir -p $(DESTDIR)$(pkgconfigdir); fi |
| ︙ | ︙ | |||
356 357 358 359 360 361 362 | touch -r $(SRCDIR)zconf.h.in $@ &&\ rm $$TEMPFILE zconf: $(SRCDIR)zconf.h.in cp -p $(SRCDIR)zconf.h.in zconf.h minizip-test: static | | | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
touch -r $(SRCDIR)zconf.h.in $@ &&\
rm $$TEMPFILE
zconf: $(SRCDIR)zconf.h.in
cp -p $(SRCDIR)zconf.h.in zconf.h
minizip-test: static
cd contrib/minizip && { CC="$(CC)" CFLAGS="$(CFLAGS)" $(MAKE) test ; cd ../.. ; }
minizip-clean:
cd contrib/minizip && { $(MAKE) clean ; cd ../.. ; }
mostlyclean: clean
clean: minizip-clean
rm -f *.o *.lo *~ \
|
| ︙ | ︙ |
Changes to compat/zlib/README.
1 2 | ZLIB DATA COMPRESSION LIBRARY | | | 1 2 3 4 5 6 7 8 9 10 | ZLIB DATA COMPRESSION LIBRARY zlib 1.3.1 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). All functions of the compression library are documented in the file zlib.h (volunteer to write man pages welcome, contact zlib@gzip.org). A usage example |
| ︙ | ︙ | |||
27 28 29 30 31 32 33 | PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at https://marknelson.us/posts/1997/01/01/zlib-engine.html . | | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997 issue of Dr. Dobb's Journal; a copy of the article is available at https://marknelson.us/posts/1997/01/01/zlib-engine.html . The changes made in version 1.3.1 are documented in the file ChangeLog. Unsupported third party contributions are provided in directory contrib/ . zlib is available in Java using the java.util.zip package. Follow the API Documentation link at: https://docs.oracle.com/search/?q=java.util.zip . A Perl interface to zlib and bzip2 written by Paul Marquess <pmqs@cpan.org> |
| ︙ | ︙ | |||
79 80 81 82 83 84 85 | The deflate format used by zlib was defined by Phil Katz. The deflate and zlib specifications were written by L. Peter Deutsch. Thanks to all the people who reported problems and suggested various improvements in zlib; they are too numerous to cite here. Copyright notice: | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | The deflate format used by zlib was defined by Phil Katz. The deflate and zlib specifications were written by L. Peter Deutsch. Thanks to all the people who reported problems and suggested various improvements in zlib; they are too numerous to cite here. Copyright notice: (C) 1995-2024 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it |
| ︙ | ︙ |
Changes to compat/zlib/configure.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
# get source directory
SRCDIR=`dirname $0`
if test $SRCDIR = "."; then
ZINC=""
ZINCOUT="-I."
SRCDIR=""
else
| | > | | 21 22 23 24 25 26 27 28 29 30 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 |
# get source directory
SRCDIR=`dirname $0`
if test $SRCDIR = "."; then
ZINC=""
ZINCOUT="-I."
SRCDIR=""
else
ZINC='-I. -include zconf.h'
ZINCOUT='-I. -I$(SRCDIR)'
SRCDIR="$SRCDIR/"
fi
# set command prefix for cross-compilation
if [ -n "${CHOST}" ]; then
uname=${CHOST}
mname=${CHOST}
CROSS_PREFIX="${CHOST}-"
else
mname=`(uname -a || echo unknown) 2>/dev/null`
fi
# destination name for static library
STATICLIB=libz.a
# extract zlib version numbers from zlib.h
VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < ${SRCDIR}zlib.h`
VER3=`echo ${VER}|sed -n -e 's/\([0-9]\{1,\}\(\\.[0-9]\{1,\}\)\{1,2\}\).*/\1/p'`
VER1=`echo ${VER}|sed -n -e 's/\([0-9]\{1,\}\)\\..*/\1/p'`
# establish commands for library building
if "${CROSS_PREFIX}ar" --version >/dev/null 2>/dev/null || test $? -lt 126; then
AR=${AR-"${CROSS_PREFIX}ar"}
test -n "${CROSS_PREFIX}" && echo Using ${AR} | tee -a configure.log
else
AR=${AR-"ar"}
|
| ︙ | ︙ | |||
259 260 261 262 263 264 265 |
AIX*)
LDFLAGS="${LDFLAGS} -Wl,-brtl" ;;
Darwin* | darwin* | *-darwin*)
shared_ext='.dylib'
SHAREDLIB=libz$shared_ext
SHAREDLIBV=libz.$VER$shared_ext
SHAREDLIBM=libz.$VER1$shared_ext
| | | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
AIX*)
LDFLAGS="${LDFLAGS} -Wl,-brtl" ;;
Darwin* | darwin* | *-darwin*)
shared_ext='.dylib'
SHAREDLIB=libz$shared_ext
SHAREDLIBV=libz.$VER$shared_ext
SHAREDLIBM=libz.$VER1$shared_ext
LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name $libdir/$SHAREDLIBM -compatibility_version $VER1 -current_version $VER3"}
if "${CROSS_PREFIX}libtool" -V 2>&1 | grep Apple > /dev/null; then
AR="${CROSS_PREFIX}libtool"
elif libtool -V 2>&1 | grep Apple > /dev/null; then
AR="libtool"
else
AR="/usr/bin/libtool"
fi
|
| ︙ | ︙ | |||
437 438 439 440 441 442 443 |
cat > $test.c <<EOF
extern int getchar();
int hello() {return getchar();}
EOF
if test $shared -eq 1; then
echo Checking for shared library support... | tee -a configure.log
# we must test in two steps (cc then ld), required at least on SunOS 4.x
| | | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
cat > $test.c <<EOF
extern int getchar();
int hello() {return getchar();}
EOF
if test $shared -eq 1; then
echo Checking for shared library support... | tee -a configure.log
# we must test in two steps (cc then ld), required at least on SunOS 4.x
if try $CC -c $SFLAGS $test.c &&
try $LDSHARED $SFLAGS -o $test$shared_ext $test.o; then
echo Building shared library $SHAREDLIBV with $CC. | tee -a configure.log
elif test -z "$old_cc" -a -z "$old_cflags"; then
echo No shared library support. | tee -a configure.log
shared=0;
else
echo 'No shared library support; try without defining CC and CFLAGS' | tee -a configure.log
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/delphi/ZLib.pas.
| ︙ | ︙ | |||
148 149 150 151 152 153 154 |
InBytes = number of bytes in InBuf
Out: OutBuf = ptr to user-allocated buffer to contain decompressed data
BufSize = number of bytes in OutBuf }
procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer;
const OutBuf: Pointer; BufSize: Integer);
const
| | | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
InBytes = number of bytes in InBuf
Out: OutBuf = ptr to user-allocated buffer to contain decompressed data
BufSize = number of bytes in OutBuf }
procedure DecompressToUserBuf(const InBuf: Pointer; InBytes: Integer;
const OutBuf: Pointer; BufSize: Integer);
const
zlib_version = '1.3.1';
type
EZlibError = class(Exception);
ECompressionError = class(EZlibError);
EDecompressionError = class(EZlibError);
implementation
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 |
/// </summary>
public ChecksumGeneratorBase()
{
_current = 0;
}
/// <summary>
| | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
/// </summary>
public ChecksumGeneratorBase()
{
_current = 0;
}
/// <summary>
/// Initializes a new instance of the checksum generator base with a specified value
/// </summary>
/// <param name="initialValue">The value to set the current checksum to</param>
public ChecksumGeneratorBase(uint initialValue)
{
_current = initialValue;
}
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs.
| ︙ | ︙ | |||
152 153 154 155 156 157 158 |
public class InfoTests
{
#region Info tests
[Test]
public void Info_Version()
{
Info info = new Info();
| | | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
public class InfoTests
{
#region Info tests
[Test]
public void Info_Version()
{
Info info = new Info();
Assert.AreEqual("1.3.1", Info.Version);
Assert.AreEqual(32, info.SizeOfUInt);
Assert.AreEqual(32, info.SizeOfULong);
Assert.AreEqual(32, info.SizeOfPointer);
Assert.AreEqual(32, info.SizeOfOffset);
}
#endregion
}
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/infback9/inftree9.c.
1 | /* inftree9.c -- generate Huffman trees for efficient decoding | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* inftree9.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftree9.h" #define MAXBITS 15 const char inflate9_copyright[] = " inflate9 1.3.1 Copyright 1995-2024 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ |
| ︙ | ︙ | |||
55 56 57 58 59 60 61 |
static const unsigned short lbase[31] = { /* Length codes 257..285 base */
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17,
19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115,
131, 163, 195, 227, 3, 0, 0};
static const unsigned short lext[31] = { /* Length codes 257..285 extra */
128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129,
130, 130, 130, 130, 131, 131, 131, 131, 132, 132, 132, 132,
| | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
static const unsigned short lbase[31] = { /* Length codes 257..285 base */
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17,
19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115,
131, 163, 195, 227, 3, 0, 0};
static const unsigned short lext[31] = { /* Length codes 257..285 extra */
128, 128, 128, 128, 128, 128, 128, 128, 129, 129, 129, 129,
130, 130, 130, 130, 131, 131, 131, 131, 132, 132, 132, 132,
133, 133, 133, 133, 144, 203, 77};
static const unsigned short dbase[32] = { /* Distance codes 0..31 base */
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49,
65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073,
4097, 6145, 8193, 12289, 16385, 24577, 32769, 49153};
static const unsigned short dext[32] = { /* Distance codes 0..31 extra */
128, 128, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132,
133, 133, 134, 134, 135, 135, 136, 136, 137, 137, 138, 138,
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/infback9/inftree9.h.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 | /* Maximum size of the dynamic table. The maximum number of code structures is 1446, which is the sum of 852 for literal/length codes and 594 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes | | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /* Maximum size of the dynamic table. The maximum number of code structures is 1446, which is the sum of 852 for literal/length codes and 594 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes returns 852, and "enough 32 6 15" for distance codes returns 594. The initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in infback9.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ #define ENOUGH_LENS 852 #define ENOUGH_DISTS 594 #define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) /* Type of code to build for inflate_table9() */ |
| ︙ | ︙ |
Changes to compat/zlib/contrib/iostream3/zfstream.h.
| ︙ | ︙ | |||
409 410 411 412 413 414 415 |
* This class defines a two-argument manipulator for gzofstream. It is used
* as base for the setcompression(int,int) manipulator.
*/
template<typename T1, typename T2>
class gzomanip2
{
public:
| | | 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 |
* This class defines a two-argument manipulator for gzofstream. It is used
* as base for the setcompression(int,int) manipulator.
*/
template<typename T1, typename T2>
class gzomanip2
{
public:
// Allows inserter to peek at internals
template <typename Ta, typename Tb>
friend gzofstream&
operator<<(gzofstream&,
const gzomanip2<Ta,Tb>&);
// Constructor
gzomanip2(gzofstream& (*f)(gzofstream&, T1, T2),
|
| ︙ | ︙ | |||
448 449 450 451 452 453 454 |
inline
gzomanip2<T1,T2>::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2),
T1 v1,
T2 v2)
: func(f), val1(v1), val2(v2)
{ }
| | | 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 |
inline
gzomanip2<T1,T2>::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2),
T1 v1,
T2 v2)
: func(f), val1(v1), val2(v2)
{ }
// Inserter applies underlying manipulator function to stream
template<typename T1, typename T2>
inline gzofstream&
operator<<(gzofstream& s, const gzomanip2<T1,T2>& m)
{ return (*m.func)(s, m.val1, m.val2); }
// Insert this onto stream to simplify setting of compression level
inline gzomanip2<int,int>
setcompression(int l, int s = Z_DEFAULT_STRATEGY)
{ return gzomanip2<int,int>(&setcompression, l, s); }
#endif // ZFSTREAM_H
|
Changes to compat/zlib/contrib/minizip/Makefile.
|
| | | 1 2 3 4 5 6 7 8 | CC?=cc CFLAGS := $(CFLAGS) -O -I../.. UNZ_OBJS = miniunz.o unzip.o ioapi.o ../../libz.a ZIP_OBJS = minizip.o zip.o ioapi.o ../../libz.a .c.o: $(CC) -c $(CFLAGS) $*.c |
| ︙ | ︙ |
Changes to compat/zlib/contrib/minizip/configure.ac.
1 2 3 | # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. | | | 1 2 3 4 5 6 7 8 9 10 11 | # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_INIT([minizip], [1.3.1], [bugzilla.redhat.com]) AC_CONFIG_SRCDIR([minizip.c]) AM_INIT_AUTOMAKE([foreign]) LT_INIT AC_MSG_CHECKING([whether to build example programs]) AC_ARG_ENABLE([demos], AC_HELP_STRING([--enable-demos], [build example programs])) AM_CONDITIONAL([COND_DEMOS], [test "$enable_demos" = yes]) |
| ︙ | ︙ |
Changes to compat/zlib/contrib/minizip/ioapi.h.
| ︙ | ︙ | |||
140 141 142 143 144 145 146 | typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin); | | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream);
typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream);
typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream);
typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin);
/* here is the "old" 32 bits structure */
typedef struct zlib_filefunc_def_s
{
open_file_func zopen_file;
read_file_func zread_file;
write_file_func zwrite_file;
tell_file_func ztell_file;
seek_file_func zseek_file;
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/minizip/miniunz.c.
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT
if it exists
*/
/* change_file_date : change the date/time of a file
filename : the filename of the file where date/time must be modified
| | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT
if it exists
*/
/* change_file_date : change the date/time of a file
filename : the filename of the file where date/time must be modified
dosdate : the new date at the MSDOS format (4 bytes)
tmu_date : the SAME new date at the tm_unz format */
static void change_file_date(const char *filename, uLong dosdate, tm_unz tmu_date) {
#ifdef _WIN32
HANDLE hFile;
FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE,
|
| ︙ | ︙ | |||
182 183 184 185 186 187 188 |
*p++ = hold;
}
free(buffer);
return 1;
}
static void do_banner(void) {
| | | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
*p++ = hold;
}
free(buffer);
return 1;
}
static void do_banner(void) {
printf("MiniUnz 1.1, demo of zLib + Unz package written by Gilles Vollant\n");
printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n");
}
static void do_help(void) {
printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \
" -e Extract without pathname (junk paths)\n" \
" -x Extract with pathname\n" \
|
| ︙ | ︙ | |||
351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
const char* write_filename;
int skip=0;
if ((*popt_extract_without_path)==0)
write_filename = filename_inzip;
else
write_filename = filename_withoutpath;
err = unzOpenCurrentFilePassword(uf,password);
if (err!=UNZ_OK)
{
printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
}
| > > > > > > > > > > > > > > | 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 |
const char* write_filename;
int skip=0;
if ((*popt_extract_without_path)==0)
write_filename = filename_inzip;
else
write_filename = filename_withoutpath;
if (write_filename[0]!='\0')
{
const char* relative_check = write_filename;
while (relative_check[1]!='\0')
{
if (relative_check[0]=='.' && relative_check[1]=='.')
write_filename = relative_check;
relative_check++;
}
}
while (write_filename[0]=='/' || write_filename[0]=='.')
write_filename++;
err = unzOpenCurrentFilePassword(uf,password);
if (err!=UNZ_OK)
{
printf("error %d with zipfile in unzOpenCurrentFilePassword\n",err);
}
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/minizip/unzip.c.
| ︙ | ︙ | |||
113 114 115 116 117 118 119 | #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; | | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
#define SIZECENTRALDIRITEM (0x2e)
#define SIZEZIPLOCALHEADER (0x1e)
const char unz_copyright[] =
" unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
/* unz_file_info64_internal contain internal info about a file in zipfile*/
typedef struct unz_file_info64_internal_s
{
ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */
} unz_file_info64_internal;
/* file_in_zip_read_info_s contain internal information about a file in zipfile,
|
| ︙ | ︙ | |||
446 447 448 449 450 451 452 |
if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0)
return CENTRALDIRINVALID;
/* the signature, already checked */
if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
return CENTRALDIRINVALID;
| | | 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0)
return CENTRALDIRINVALID;
/* the signature, already checked */
if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
return CENTRALDIRINVALID;
/* number of the disk with the start of the zip64 end of central directory */
if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK)
return CENTRALDIRINVALID;
if (uL != 0)
return CENTRALDIRINVALID;
/* relative offset of the zip64 end of central directory record */
if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK)
|
| ︙ | ︙ | |||
493 494 495 496 497 498 499 |
zlib_filefunc64_32_def* pzlib_filefunc64_32_def,
int is64bitOpenFunction) {
unz64_s us;
unz64_s *s;
ZPOS64_T central_pos;
uLong uL;
| | | | 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
zlib_filefunc64_32_def* pzlib_filefunc64_32_def,
int is64bitOpenFunction) {
unz64_s us;
unz64_s *s;
ZPOS64_T central_pos;
uLong uL;
uLong number_disk; /* number of the current disk, used for
spanning ZIP, unsupported, always 0*/
uLong number_disk_with_CD; /* number the disk with central dir, used
for spanning ZIP, unsupported, always 0*/
ZPOS64_T number_entry_CD; /* total number of entries in
the central dir
(same than number_entry on nospan) */
int err=UNZ_OK;
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/minizip/unzip.h.
| ︙ | ︙ | |||
302 303 304 305 306 307 308 |
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize);
/*
Get Info about the current file
if pfile_info!=NULL, the *pfile_info structure will contain some info about
the current file
| | | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
uLong extraFieldBufferSize,
char *szComment,
uLong commentBufferSize);
/*
Get Info about the current file
if pfile_info!=NULL, the *pfile_info structure will contain some info about
the current file
if szFileName!=NULL, the filename string will be copied in szFileName
(fileNameBufferSize is the size of the buffer)
if extraField!=NULL, the extra field information will be copied in extraField
(extraFieldBufferSize is the size of the buffer).
This is the Central-header version of the extra field
if szComment!=NULL, the comment string of the file will be copied in szComment
(commentBufferSize is the size of the buffer)
*/
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/minizip/zip.c.
| ︙ | ︙ | |||
571 572 573 574 575 576 577 |
if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0)
return 0;
/* the signature, already checked */
if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
return 0;
| | | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 |
if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0)
return 0;
/* the signature, already checked */
if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
return 0;
/* number of the disk with the start of the zip64 end of central directory */
if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK)
return 0;
if (uL != 0)
return 0;
/* relative offset of the zip64 end of central directory record */
if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK)
|
| ︙ | ︙ | |||
610 611 612 613 614 615 616 | ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ ZPOS64_T size_central_dir; /* size of the central directory */ ZPOS64_T offset_central_dir; /* offset of start of central directory */ ZPOS64_T central_pos; uLong uL; | | | | 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 |
ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
ZPOS64_T size_central_dir; /* size of the central directory */
ZPOS64_T offset_central_dir; /* offset of start of central directory */
ZPOS64_T central_pos;
uLong uL;
uLong number_disk; /* number of the current disk, used for
spanning ZIP, unsupported, always 0*/
uLong number_disk_with_CD; /* number of the disk with central dir, used
for spanning ZIP, unsupported, always 0*/
ZPOS64_T number_entry;
ZPOS64_T number_entry_CD; /* total number of entries in
the central dir
(same than number_entry on nospan) */
uLong VersionMadeBy;
uLong VersionNeeded;
|
| ︙ | ︙ | |||
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 |
#ifdef HAVE_BZIP2
if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED))
return ZIP_PARAMERROR;
#else
if ((method!=0) && (method!=Z_DEFLATED))
return ZIP_PARAMERROR;
#endif
zi = (zip64_internal*)file;
if (zi->in_opened_file_inzip == 1)
{
err = zipCloseFileInZip (file);
if (err != ZIP_OK)
| > > > > > > > > > > > | 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 |
#ifdef HAVE_BZIP2
if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED))
return ZIP_PARAMERROR;
#else
if ((method!=0) && (method!=Z_DEFLATED))
return ZIP_PARAMERROR;
#endif
// The filename and comment length must fit in 16 bits.
if ((filename!=NULL) && (strlen(filename)>0xffff))
return ZIP_PARAMERROR;
if ((comment!=NULL) && (strlen(comment)>0xffff))
return ZIP_PARAMERROR;
// The extra field length must fit in 16 bits. If the member also requires
// a Zip64 extra block, that will also need to fit within that 16-bit
// length, but that will be checked for later.
if ((size_extrafield_local>0xffff) || (size_extrafield_global>0xffff))
return ZIP_PARAMERROR;
zi = (zip64_internal*)file;
if (zi->in_opened_file_inzip == 1)
{
err = zipCloseFileInZip (file);
if (err != ZIP_OK)
|
| ︙ | ︙ | |||
1593 1594 1595 1596 1597 1598 1599 |
if(datasize > 0)
{
char* p = NULL;
if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree)
{
| | | 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 |
if(datasize > 0)
{
char* p = NULL;
if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree)
{
// we cannot write more data to the buffer that we have room for.
return ZIP_BADZIPFILE;
}
p = zi->ci.central_header + zi->ci.size_centralheader;
// Add Extra Information Header for 'ZIP64 information'
zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID
|
| ︙ | ︙ | |||
1857 1858 1859 1860 1861 1862 1863 |
size_centraldir += ldi->filled_in_this_block;
ldi = ldi->next_datablock;
}
}
free_linkedlist(&(zi->central_dir));
pos = centraldir_pos_inzip - zi->add_position_when_writing_offset;
| | | 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 |
size_centraldir += ldi->filled_in_this_block;
ldi = ldi->next_datablock;
}
}
free_linkedlist(&(zi->central_dir));
pos = centraldir_pos_inzip - zi->add_position_when_writing_offset;
if(pos >= 0xffffffff || zi->number_entry >= 0xFFFF)
{
ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream);
Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip);
Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos);
}
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/minizip/zip.h.
| ︙ | ︙ | |||
173 174 175 176 177 178 179 |
int zip64);
/*
Open a file in the ZIP for writing.
filename : the filename in zip (if NULL, '-' without quote will be used
*zipfi contain supplemental information
if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
| | | | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
int zip64);
/*
Open a file in the ZIP for writing.
filename : the filename in zip (if NULL, '-' without quote will be used
*zipfi contain supplemental information
if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
contains the extrafield data for the local header
if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
contains the extrafield data for the global header
if comment != NULL, comment contain the comment string
method contain the compression method (0 for store, Z_DEFLATED for deflate)
level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
zip64 is set to 1 if a zip64 extended information block should be added to the local file header.
this MUST be '1' if the uncompressed size is >= 0xffffffff.
*/
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/pascal/zlibpas.pas.
1 2 3 4 5 6 7 8 9 10 11 12 | (* zlibpas -- Pascal interface to the zlib data compression library * * Copyright (C) 2003 Cosmin Truta. * Derived from original sources by Bob Dellaca. * For conditions of distribution and use, see copyright notice in readme.txt *) unit zlibpas; interface const | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
(* zlibpas -- Pascal interface to the zlib data compression library
*
* Copyright (C) 2003 Cosmin Truta.
* Derived from original sources by Bob Dellaca.
* For conditions of distribution and use, see copyright notice in readme.txt
*)
unit zlibpas;
interface
const
ZLIB_VERSION = '1.3.1';
ZLIB_VERNUM = $12a0;
type
alloc_func = function(opaque: Pointer; items, size: Integer): Pointer;
cdecl;
free_func = procedure(opaque, address: Pointer);
cdecl;
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/puff/puff.c.
| ︙ | ︙ | |||
589 590 591 592 593 594 595 | * from the number of bits in each code. Therefore the code descriptions * are simply a list of code lengths for each symbol. * * - The code lengths are stored in order for the symbols, so lengths are * provided for each of the literal/length symbols, and for each of the * distance symbols. * | | | | | | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | * from the number of bits in each code. Therefore the code descriptions * are simply a list of code lengths for each symbol. * * - The code lengths are stored in order for the symbols, so lengths are * provided for each of the literal/length symbols, and for each of the * distance symbols. * * - If a symbol is not used in the block, this is represented by a zero as the * code length. This does not mean a zero-length code, but rather that no * code should be created for this symbol. There is no way in the deflate * format to represent a zero-length code. * * - The maximum number of bits in a code is 15, so the possible lengths for * any code are 1..15. * * - The fact that a length of zero is not permitted for a code has an * interesting consequence. Normally if only one symbol is used for a given * code, then in fact that code could be represented with zero bits. However |
| ︙ | ︙ |
Changes to compat/zlib/contrib/vstudio/readme.txt.
|
| | | 1 2 3 4 5 6 7 8 | Building instructions for the DLL versions of Zlib 1.3.1 ======================================================== This directory contains projects that build zlib and minizip using Microsoft Visual C++ 9.0/10.0. You don't need to build these projects yourself. You can download the binaries from: |
| ︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 | - Open contrib\vstudio\vc12\zlibvc.sln with Microsoft Visual C++ 2013 Build instructions for Visual Studio 2015 (32 bits or 64 bits) -------------------------------------------------------------- - Decompress current zlib, including all contrib/* files - Open contrib\vstudio\vc14\zlibvc.sln with Microsoft Visual C++ 2015 Important --------- - To use zlibwapi.dll in your application, you must define the macro ZLIB_WINAPI when compiling your application's source files. | > > > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | - Open contrib\vstudio\vc12\zlibvc.sln with Microsoft Visual C++ 2013 Build instructions for Visual Studio 2015 (32 bits or 64 bits) -------------------------------------------------------------- - Decompress current zlib, including all contrib/* files - Open contrib\vstudio\vc14\zlibvc.sln with Microsoft Visual C++ 2015 Build instructions for Visual Studio 2022 (64 bits) -------------------------------------------------------------- - Decompress current zlib, including all contrib/* files - Open contrib\vstudio\vc143\zlibvc.sln with Microsoft Visual C++ 2022 Important --------- - To use zlibwapi.dll in your application, you must define the macro ZLIB_WINAPI when compiling your application's source files. |
| ︙ | ︙ | |||
67 68 69 70 71 72 73 | has a slightly different effect. To avoid compatibility problems, do not define it here. Gilles Vollant info@winimage.com | | | 73 74 75 76 77 78 79 80 81 | has a slightly different effect. To avoid compatibility problems, do not define it here. Gilles Vollant info@winimage.com Visual Studio 2013, 2015, and 2022 Projects from Sean Hunt seandhunt_7@yahoo.com |
Changes to compat/zlib/contrib/vstudio/vc10/zlib.rc.
1 2 3 4 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <windows.h>
#define IDR_VERSION1 1
IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE
FILEVERSION 1, 3, 1, 0
PRODUCTVERSION 1, 3, 1, 0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0
FILEOS VOS_DOS_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0 // not used
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
//language ID = U.S. English, char set = Windows, Multilingual
BEGIN
VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0"
VALUE "FileVersion", "1.3.1\0"
VALUE "InternalName", "zlib\0"
VALUE "OriginalFilename", "zlibwapi.dll\0"
VALUE "ProductName", "ZLib.DLL\0"
VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0"
VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1252
END
END
|
Changes to compat/zlib/contrib/vstudio/vc10/zlibvc.def.
1 2 3 | LIBRARY ; zlib data compression and ZIP file I/O library | | | 1 2 3 4 5 6 7 8 9 10 11 |
LIBRARY
; zlib data compression and ZIP file I/O library
VERSION 1.3.1
EXPORTS
adler32 @1
compress @2
crc32 @3
deflate @4
deflateCopy @5
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/vstudio/vc11/zlib.rc.
1 2 3 4 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <windows.h>
#define IDR_VERSION1 1
IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE
FILEVERSION 1, 3, 1, 0
PRODUCTVERSION 1, 3, 1, 0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0
FILEOS VOS_DOS_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0 // not used
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
//language ID = U.S. English, char set = Windows, Multilingual
BEGIN
VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0"
VALUE "FileVersion", "1.3.1\0"
VALUE "InternalName", "zlib\0"
VALUE "OriginalFilename", "zlibwapi.dll\0"
VALUE "ProductName", "ZLib.DLL\0"
VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0"
VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1252
END
END
|
Changes to compat/zlib/contrib/vstudio/vc11/zlibvc.def.
1 2 3 | LIBRARY ; zlib data compression and ZIP file I/O library | | | 1 2 3 4 5 6 7 8 9 10 11 |
LIBRARY
; zlib data compression and ZIP file I/O library
VERSION 1.3.1
EXPORTS
adler32 @1
compress @2
crc32 @3
deflate @4
deflateCopy @5
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/vstudio/vc12/zlib.rc.
1 2 3 4 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <windows.h>
#define IDR_VERSION1 1
IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE
FILEVERSION 1, 3, 1, 0
PRODUCTVERSION 1, 3, 1, 0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0
FILEOS VOS_DOS_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0 // not used
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
//language ID = U.S. English, char set = Windows, Multilingual
BEGIN
VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0"
VALUE "FileVersion", "1.3.1\0"
VALUE "InternalName", "zlib\0"
VALUE "OriginalFilename", "zlibwapi.dll\0"
VALUE "ProductName", "ZLib.DLL\0"
VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0"
VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1252
END
END
|
Changes to compat/zlib/contrib/vstudio/vc12/zlibvc.def.
1 2 3 | LIBRARY ; zlib data compression and ZIP file I/O library | | | 1 2 3 4 5 6 7 8 9 10 11 |
LIBRARY
; zlib data compression and ZIP file I/O library
VERSION 1.3.1
EXPORTS
adler32 @1
compress @2
crc32 @3
deflate @4
deflateCopy @5
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/vstudio/vc14/zlib.rc.
1 2 3 4 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <windows.h>
#define IDR_VERSION1 1
IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE
FILEVERSION 1, 3, 1, 0
PRODUCTVERSION 1, 3, 1, 0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0
FILEOS VOS_DOS_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0 // not used
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
//language ID = U.S. English, char set = Windows, Multilingual
BEGIN
VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0"
VALUE "FileVersion", "1.3.1\0"
VALUE "InternalName", "zlib\0"
VALUE "OriginalFilename", "zlibwapi.dll\0"
VALUE "ProductName", "ZLib.DLL\0"
VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0"
VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1252
END
END
|
Changes to compat/zlib/contrib/vstudio/vc14/zlibvc.def.
1 2 3 | LIBRARY ; zlib data compression and ZIP file I/O library | | | 1 2 3 4 5 6 7 8 9 10 11 |
LIBRARY
; zlib data compression and ZIP file I/O library
VERSION 1.3.1
EXPORTS
adler32 @1
compress @2
crc32 @3
deflate @4
deflateCopy @5
|
| ︙ | ︙ |
Changes to compat/zlib/contrib/vstudio/vc9/zlib.rc.
1 2 3 4 | #include <windows.h> #define IDR_VERSION1 1 IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <windows.h>
#define IDR_VERSION1 1
IDR_VERSION1 VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE
FILEVERSION 1, 3, 1, 0
PRODUCTVERSION 1, 3, 1, 0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS 0
FILEOS VOS_DOS_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0 // not used
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
//language ID = U.S. English, char set = Windows, Multilingual
BEGIN
VALUE "FileDescription", "zlib data compression and ZIP file I/O library\0"
VALUE "FileVersion", "1.3.1\0"
VALUE "InternalName", "zlib\0"
VALUE "OriginalFilename", "zlibwapi.dll\0"
VALUE "ProductName", "ZLib.DLL\0"
VALUE "Comments","DLL support by Alessandro Iacopetti & Gilles Vollant\0"
VALUE "LegalCopyright", "(C) 1995-2024 Jean-loup Gailly & Mark Adler\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1252
END
END
|
Changes to compat/zlib/contrib/vstudio/vc9/zlibvc.def.
1 2 3 | LIBRARY ; zlib data compression and ZIP file I/O library | | | 1 2 3 4 5 6 7 8 9 10 11 |
LIBRARY
; zlib data compression and ZIP file I/O library
VERSION 1.3.1
EXPORTS
adler32 @1
compress @2
crc32 @3
deflate @4
deflateCopy @5
|
| ︙ | ︙ |
Changes to compat/zlib/deflate.c.
1 | /* deflate.c -- compress data using the deflation algorithm | | | 1 2 3 4 5 6 7 8 9 | /* deflate.c -- compress data using the deflation algorithm * Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * * The "deflation" process depends on being able to identify portions |
| ︙ | ︙ | |||
48 49 50 51 52 53 54 | */ /* @(#) $Id$ */ #include "deflate.h" const char deflate_copyright[] = | | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | */ /* @(#) $Id$ */ #include "deflate.h" const char deflate_copyright[] = " deflate 1.3.1 Copyright 1995-2024 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ |
| ︙ | ︙ | |||
489 490 491 492 493 494 495 |
* blocks. A dynamic-code block will only be chosen to be emitted if it has
* fewer bits than a fixed-code block would for the same set of symbols.
* Therefore its average symbol length is assured to be less than 31. So
* the compressed data for a dynamic block also cannot overwrite the
* symbols from which it is being constructed.
*/
| | > > > > > > | 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 |
* blocks. A dynamic-code block will only be chosen to be emitted if it has
* fewer bits than a fixed-code block would for the same set of symbols.
* Therefore its average symbol length is assured to be less than 31. So
* the compressed data for a dynamic block also cannot overwrite the
* symbols from which it is being constructed.
*/
s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, LIT_BUFS);
s->pending_buf_size = (ulg)s->lit_bufsize * 4;
if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
s->pending_buf == Z_NULL) {
s->status = FINISH_STATE;
strm->msg = ERR_MSG(Z_MEM_ERROR);
deflateEnd (strm);
return Z_MEM_ERROR;
}
#ifdef LIT_MEM
s->d_buf = (ushf *)(s->pending_buf + (s->lit_bufsize << 1));
s->l_buf = s->pending_buf + (s->lit_bufsize << 2);
s->sym_end = s->lit_bufsize - 1;
#else
s->sym_buf = s->pending_buf + s->lit_bufsize;
s->sym_end = (s->lit_bufsize - 1) * 3;
#endif
/* We avoid equality with lit_bufsize*3 because of wraparound at 64K
* on 16 bit machines and because stored blocks are restricted to
* 64K-1 bytes.
*/
s->level = level;
s->strategy = strategy;
|
| ︙ | ︙ | |||
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 |
/* ========================================================================= */
int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) {
deflate_state *s;
int put;
if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
s = strm->state;
if (bits < 0 || bits > 16 ||
s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3))
return Z_BUF_ERROR;
do {
put = Buf_size - s->bi_valid;
if (put > bits)
put = bits;
s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid);
s->bi_valid += put;
_tr_flush_bits(s);
| > > > > > > | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 |
/* ========================================================================= */
int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) {
deflate_state *s;
int put;
if (deflateStateCheck(strm)) return Z_STREAM_ERROR;
s = strm->state;
#ifdef LIT_MEM
if (bits < 0 || bits > 16 ||
(uchf *)s->d_buf < s->pending_out + ((Buf_size + 7) >> 3))
return Z_BUF_ERROR;
#else
if (bits < 0 || bits > 16 ||
s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3))
return Z_BUF_ERROR;
#endif
do {
put = Buf_size - s->bi_valid;
if (put > bits)
put = bits;
s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid);
s->bi_valid += put;
_tr_flush_bits(s);
|
| ︙ | ︙ | |||
1290 1291 1292 1293 1294 1295 1296 |
dest->state = (struct internal_state FAR *) ds;
zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state));
ds->strm = dest;
ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
| | | > > > > > | 1302 1303 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 |
dest->state = (struct internal_state FAR *) ds;
zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state));
ds->strm = dest;
ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, LIT_BUFS);
if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
ds->pending_buf == Z_NULL) {
deflateEnd (dest);
return Z_MEM_ERROR;
}
/* following zmemcpy do not work for 16-bit MSDOS */
zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos));
zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos));
zmemcpy(ds->pending_buf, ss->pending_buf, ds->lit_bufsize * LIT_BUFS);
ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
#ifdef LIT_MEM
ds->d_buf = (ushf *)(ds->pending_buf + (ds->lit_bufsize << 1));
ds->l_buf = ds->pending_buf + (ds->lit_bufsize << 2);
#else
ds->sym_buf = ds->pending_buf + ds->lit_bufsize;
#endif
ds->l_desc.dyn_tree = ds->dyn_ltree;
ds->d_desc.dyn_tree = ds->dyn_dtree;
ds->bl_desc.dyn_tree = ds->bl_tree;
return Z_OK;
#endif /* MAXSEG_64K */
|
| ︙ | ︙ | |||
1535 1536 1537 1538 1539 1540 1541 |
/* result of memcmp for equal strings */
/* ===========================================================================
* Check that the match at match_start is indeed a match.
*/
local void check_match(deflate_state *s, IPos start, IPos match, int length) {
/* check that the match is indeed a match */
| | > > > > > > > > | | | | | | 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 |
/* result of memcmp for equal strings */
/* ===========================================================================
* Check that the match at match_start is indeed a match.
*/
local void check_match(deflate_state *s, IPos start, IPos match, int length) {
/* check that the match is indeed a match */
Bytef *back = s->window + (int)match, *here = s->window + start;
IPos len = length;
if (match == (IPos)-1) {
/* match starts one byte before the current window -- just compare the
subsequent length-1 bytes */
back++;
here++;
len--;
}
if (zmemcmp(back, here, len) != EQUAL) {
fprintf(stderr, " start %u, match %d, length %d\n",
start, (int)match, length);
do {
fprintf(stderr, "(%02x %02x)", *back++, *here++);
} while (--len != 0);
z_error("invalid match");
}
if (z_verbose > 1) {
fprintf(stderr,"\\[%d,%d]", start - match, length);
do { putc(s->window[start++], stderr); } while (--length != 0);
}
}
|
| ︙ | ︙ |
Changes to compat/zlib/deflate.h.
1 | /* deflate.h -- internal compression state | | | 1 2 3 4 5 6 7 8 9 | /* deflate.h -- internal compression state * Copyright (C) 1995-2024 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ |
| ︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /* define NO_GZIP when compiling if you want to disable gzip header and trailer creation by deflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip encoding should be left enabled. */ #ifndef NO_GZIP # define GZIP #endif /* =========================================================================== * Internal compression state. */ #define LENGTH_CODES 29 /* number of length codes, not counting the special END_BLOCK code */ | > > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /* define NO_GZIP when compiling if you want to disable gzip header and trailer creation by deflate(). NO_GZIP would be used to avoid linking in the crc code when it is not needed. For shared libraries, gzip encoding should be left enabled. */ #ifndef NO_GZIP # define GZIP #endif /* define LIT_MEM to slightly increase the speed of deflate (order 1% to 2%) at the cost of a larger memory footprint */ /* #define LIT_MEM */ /* =========================================================================== * Internal compression state. */ #define LENGTH_CODES 29 /* number of length codes, not counting the special END_BLOCK code */ |
| ︙ | ︙ | |||
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
* The same heap array is used to build all trees.
*/
uch depth[2*L_CODES+1];
/* Depth of each subtree used as tie breaker for trees of equal frequency
*/
uchf *sym_buf; /* buffer for distances and literals/lengths */
uInt lit_bufsize;
/* Size of match buffer for literals/lengths. There are 4 reasons for
* limiting lit_bufsize to 64K:
* - frequencies can be kept in 16 bit counters
* - if compression is not successful for the first block, all input
* data is still in the window so we can still emit a stored block even
| > > > > > > > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
* The same heap array is used to build all trees.
*/
uch depth[2*L_CODES+1];
/* Depth of each subtree used as tie breaker for trees of equal frequency
*/
#ifdef LIT_MEM
# define LIT_BUFS 5
ushf *d_buf; /* buffer for distances */
uchf *l_buf; /* buffer for literals/lengths */
#else
# define LIT_BUFS 4
uchf *sym_buf; /* buffer for distances and literals/lengths */
#endif
uInt lit_bufsize;
/* Size of match buffer for literals/lengths. There are 4 reasons for
* limiting lit_bufsize to 64K:
* - frequencies can be kept in 16 bit counters
* - if compression is not successful for the first block, all input
* data is still in the window so we can still emit a stored block even
|
| ︙ | ︙ | |||
235 236 237 238 239 240 241 |
* example a binary file with poorly compressible code followed by
* a highly compressible string table.) Smaller buffer sizes give
* fast adaptation but have of course the overhead of transmitting
* trees more frequently.
* - I can't count above 4
*/
| | | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
* example a binary file with poorly compressible code followed by
* a highly compressible string table.) Smaller buffer sizes give
* fast adaptation but have of course the overhead of transmitting
* trees more frequently.
* - I can't count above 4
*/
uInt sym_next; /* running index in symbol buffer */
uInt sym_end; /* symbol table full when sym_next reaches this */
ulg opt_len; /* bit length of current block with optimal trees */
ulg static_len; /* bit length of current block with static trees */
uInt matches; /* number of string matches in current block */
uInt insert; /* bytes at end of window left to insert */
|
| ︙ | ︙ | |||
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 |
extern uch ZLIB_INTERNAL _length_code[];
extern uch ZLIB_INTERNAL _dist_code[];
#else
extern const uch ZLIB_INTERNAL _length_code[];
extern const uch ZLIB_INTERNAL _dist_code[];
#endif
# define _tr_tally_lit(s, c, flush) \
{ uch cc = (c); \
s->sym_buf[s->sym_next++] = 0; \
s->sym_buf[s->sym_next++] = 0; \
s->sym_buf[s->sym_next++] = cc; \
s->dyn_ltree[cc].Freq++; \
flush = (s->sym_next == s->sym_end); \
}
# define _tr_tally_dist(s, distance, length, flush) \
{ uch len = (uch)(length); \
ush dist = (ush)(distance); \
s->sym_buf[s->sym_next++] = (uch)dist; \
s->sym_buf[s->sym_next++] = (uch)(dist >> 8); \
s->sym_buf[s->sym_next++] = len; \
dist--; \
s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
s->dyn_dtree[d_code(dist)].Freq++; \
flush = (s->sym_next == s->sym_end); \
}
#else
# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
# define _tr_tally_dist(s, distance, length, flush) \
flush = _tr_tally(s, distance, length)
#endif
#endif /* DEFLATE_H */
| > > > > > > > > > > > > > > > > > > > > | 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 375 376 377 |
extern uch ZLIB_INTERNAL _length_code[];
extern uch ZLIB_INTERNAL _dist_code[];
#else
extern const uch ZLIB_INTERNAL _length_code[];
extern const uch ZLIB_INTERNAL _dist_code[];
#endif
#ifdef LIT_MEM
# define _tr_tally_lit(s, c, flush) \
{ uch cc = (c); \
s->d_buf[s->sym_next] = 0; \
s->l_buf[s->sym_next++] = cc; \
s->dyn_ltree[cc].Freq++; \
flush = (s->sym_next == s->sym_end); \
}
# define _tr_tally_dist(s, distance, length, flush) \
{ uch len = (uch)(length); \
ush dist = (ush)(distance); \
s->d_buf[s->sym_next] = dist; \
s->l_buf[s->sym_next++] = len; \
dist--; \
s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
s->dyn_dtree[d_code(dist)].Freq++; \
flush = (s->sym_next == s->sym_end); \
}
#else
# define _tr_tally_lit(s, c, flush) \
{ uch cc = (c); \
s->sym_buf[s->sym_next++] = 0; \
s->sym_buf[s->sym_next++] = 0; \
s->sym_buf[s->sym_next++] = cc; \
s->dyn_ltree[cc].Freq++; \
flush = (s->sym_next == s->sym_end); \
}
# define _tr_tally_dist(s, distance, length, flush) \
{ uch len = (uch)(length); \
ush dist = (ush)(distance); \
s->sym_buf[s->sym_next++] = (uch)dist; \
s->sym_buf[s->sym_next++] = (uch)(dist >> 8); \
s->sym_buf[s->sym_next++] = len; \
dist--; \
s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
s->dyn_dtree[d_code(dist)].Freq++; \
flush = (s->sym_next == s->sym_end); \
}
#endif
#else
# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
# define _tr_tally_dist(s, distance, length, flush) \
flush = _tr_tally(s, distance, length)
#endif
#endif /* DEFLATE_H */
|
Changes to compat/zlib/examples/gzlog.c.
| ︙ | ︙ | |||
208 209 210 211 212 213 214 |
- Write over the extra field, marking foo.gz as complete.
Recovery procedure:
- If not a replace recovery, read in the foo.add file, and provide that data
to the appropriate recovery below. If there is no foo.add file, provide
a zero data length to the recovery. In that case, the append recovery
restores the foo.gz to the previous compressed + uncompressed data state.
| | | | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
- Write over the extra field, marking foo.gz as complete.
Recovery procedure:
- If not a replace recovery, read in the foo.add file, and provide that data
to the appropriate recovery below. If there is no foo.add file, provide
a zero data length to the recovery. In that case, the append recovery
restores the foo.gz to the previous compressed + uncompressed data state.
For the compress recovery, a missing foo.add file results in foo.gz being
restored to the previous compressed-only data state.
- Append recovery:
- Pick up append at + step above
- Compress recovery:
- Pick up compress at * step above
- Replace recovery:
- Pick up compress at @ step above
- Log the repair with a date stamp in foo.repairs
|
| ︙ | ︙ |
Changes to compat/zlib/examples/zran.c.
| ︙ | ︙ | |||
263 264 265 266 267 268 269 |
value >>= k; // drop the bits appended
bits -= k;
k = 8; // now at a byte boundary
*++in = value;
}
}
| | | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
value >>= k; // drop the bits appended
bits -= k;
k = 8; // now at a byte boundary
*++in = value;
}
}
// Insert enough bits in the form of empty deflate blocks in front of the
// low bits bits of value, in order to bring the sequence to a byte boundary.
// Then feed that to inflate(). This does what inflatePrime() does, except that
// a negative value of bits is not supported. bits must be in 0..16. If the
// arguments are invalid, Z_STREAM_ERROR is returned. Otherwise the return
// value from inflate() is returned.
static int inflatePreface(z_stream *strm, int bits, int value) {
// Check input.
|
| ︙ | ︙ |
Changes to compat/zlib/gzguts.h.
1 | /* gzguts.h -- zlib internal header definitions for gz* operations | | | 1 2 3 4 5 6 7 8 9 | /* gzguts.h -- zlib internal header definitions for gz* operations * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #ifdef _LARGEFILE64_SOURCE # ifndef _LARGEFILE_SOURCE # define _LARGEFILE_SOURCE 1 # endif |
| ︙ | ︙ | |||
206 207 208 209 210 211 212 | #if defined UNDER_CE char ZLIB_INTERNAL *gz_strwinerror(DWORD error); #endif /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ | < < < | < | 206 207 208 209 210 211 212 213 214 | #if defined UNDER_CE char ZLIB_INTERNAL *gz_strwinerror(DWORD error); #endif /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ unsigned ZLIB_INTERNAL gz_intmax(void); #define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) |
Changes to compat/zlib/gzlib.c.
1 | /* gzlib.c -- zlib functions common to reading and writing gzip files | | | 1 2 3 4 5 6 7 8 9 | /* gzlib.c -- zlib functions common to reading and writing gzip files * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "gzguts.h" #if defined(_WIN32) && !defined(__BORLANDC__) # define LSEEK _lseeki64 |
| ︙ | ︙ | |||
559 560 561 562 563 564 565 |
#else
strcpy(state->msg, state->path);
strcat(state->msg, ": ");
strcat(state->msg, msg);
#endif
}
| < > > > | < < < > | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
#else
strcpy(state->msg, state->path);
strcat(state->msg, ": ");
strcat(state->msg, msg);
#endif
}
/* portably return maximum value for an int (when limits.h presumed not
available) -- we need to do this to cover cases where 2's complement not
used, since C standard permits 1's complement and sign-bit representations,
otherwise we could just use ((unsigned)-1) >> 1 */
unsigned ZLIB_INTERNAL gz_intmax(void) {
#ifdef INT_MAX
return INT_MAX;
#else
unsigned p = 1, q;
do {
q = p;
p <<= 1;
p++;
} while (p > q);
return q >> 1;
#endif
}
|
Changes to compat/zlib/inflate.c.
| ︙ | ︙ | |||
1383 1384 1385 1386 1387 1388 1389 |
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
state = (struct inflate_state FAR *)strm->state;
if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
/* if first time, start search in bit buffer */
if (state->mode != SYNC) {
state->mode = SYNC;
| | | 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 |
if (inflateStateCheck(strm)) return Z_STREAM_ERROR;
state = (struct inflate_state FAR *)strm->state;
if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
/* if first time, start search in bit buffer */
if (state->mode != SYNC) {
state->mode = SYNC;
state->hold >>= state->bits & 7;
state->bits -= state->bits & 7;
len = 0;
while (state->bits >= 8) {
buf[len++] = (unsigned char)(state->hold);
state->hold >>= 8;
state->bits -= 8;
}
|
| ︙ | ︙ |
Changes to compat/zlib/inftrees.c.
1 | /* inftrees.c -- generate Huffman trees for efficient decoding | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* inftrees.c -- generate Huffman trees for efficient decoding * Copyright (C) 1995-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ #include "zutil.h" #include "inftrees.h" #define MAXBITS 15 const char inflate_copyright[] = " inflate 1.3.1 Copyright 1995-2024 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot include such an acknowledgment, I would appreciate that you keep this copyright string in the executable of your product. */ |
| ︙ | ︙ | |||
53 54 55 56 57 58 59 |
unsigned short count[MAXBITS+1]; /* number of codes of each length */
unsigned short offs[MAXBITS+1]; /* offsets in table for each length */
static const unsigned short lbase[31] = { /* Length codes 257..285 base */
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
static const unsigned short lext[31] = { /* Length codes 257..285 extra */
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
| | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
unsigned short count[MAXBITS+1]; /* number of codes of each length */
unsigned short offs[MAXBITS+1]; /* offsets in table for each length */
static const unsigned short lbase[31] = { /* Length codes 257..285 base */
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
static const unsigned short lext[31] = { /* Length codes 257..285 extra */
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 203, 77};
static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577, 0, 0};
static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
|
| ︙ | ︙ |
Changes to compat/zlib/inftrees.h.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 | /* Maximum size of the dynamic table. The maximum number of code structures is 1444, which is the sum of 852 for literal/length codes and 592 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes | | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /* Maximum size of the dynamic table. The maximum number of code structures is 1444, which is the sum of 852 for literal/length codes and 592 for distance codes. These values were found by exhaustive searches using the program examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes returns 852, and "enough 30 6 15" for distance codes returns 592. The initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in inflate.c and infback.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ #define ENOUGH_LENS 852 #define ENOUGH_DISTS 592 #define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) |
| ︙ | ︙ |
Changes to compat/zlib/old/visual-basic.txt.
| ︙ | ︙ | |||
111 112 113 114 115 116 117 |
ReDim bytaryCpr(lngCprSiz - 1)
If lngfncCpr(bytaryCpr(0), lngCprSiz, bytaryOri(0), lngOriSiz, intLvl) =
SUCCESS Then
lngpvtPcnSml = (1# - (lngCprSiz / lngOriSiz)) * 100
ReDim Preserve bytaryCpr(lngCprSiz - 1)
Open strCprPth For Binary Access Write As #1
Put #1, , bytaryCpr()
| | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
ReDim bytaryCpr(lngCprSiz - 1)
If lngfncCpr(bytaryCpr(0), lngCprSiz, bytaryOri(0), lngOriSiz, intLvl) =
SUCCESS Then
lngpvtPcnSml = (1# - (lngCprSiz / lngOriSiz)) * 100
ReDim Preserve bytaryCpr(lngCprSiz - 1)
Open strCprPth For Binary Access Write As #1
Put #1, , bytaryCpr()
Put #1, , lngOriSiz 'Add the original size value to the end
(last 4 bytes)
Close #1
Else
MsgBox "Compression error"
End If
Erase bytaryCpr
Erase bytaryOri
|
| ︙ | ︙ |
Changes to compat/zlib/os400/README400.
|
| | | 1 2 3 4 5 6 7 8 |
ZLIB version 1.3.1 for OS/400 installation instructions
1) Download and unpack the zlib tarball to some IFS directory.
(i.e.: /path/to/the/zlib/ifs/source/directory)
If the installed IFS command supports gzip format, this is straightforward,
else you have to unpack first to some directory on a system supporting it,
then move the whole directory to the IFS via the network (via SMB or FTP).
|
| ︙ | ︙ |
Changes to compat/zlib/os400/zlib.inc.
1 2 3 |
* ZLIB.INC - Interface to the general purpose compression library
*
* ILE RPG400 version by Patrick Monnerat, DATASPHERE.
| | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
* ZLIB.INC - Interface to the general purpose compression library
*
* ILE RPG400 version by Patrick Monnerat, DATASPHERE.
* Version 1.3.1
*
*
* WARNING:
* Procedures inflateInit(), inflateInit2(), deflateInit(),
* deflateInit2() and inflateBackInit() need to be called with
* two additional arguments:
* the package version string and the stream control structure.
* size. This is needed because RPG lacks some macro feature.
* Call these procedures as:
* inflateInit(...: ZLIB_VERSION: %size(z_stream))
*
/if not defined(ZLIB_H_)
/define ZLIB_H_
*
**************************************************************************
* Constants
**************************************************************************
*
* Versioning information.
*
D ZLIB_VERSION C '1.3.1'
D ZLIB_VERNUM C X'12a0'
D ZLIB_VER_MAJOR C 1
D ZLIB_VER_MINOR C 3
D ZLIB_VER_REVISION...
D C 1
D ZLIB_VER_SUBREVISION...
D C 0
*
* Other equates.
*
D Z_NO_FLUSH C 0
D Z_PARTIAL_FLUSH...
|
| ︙ | ︙ |
Changes to compat/zlib/qnx/package.qpg.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
<QPG:EmailAddress></QPG:EmailAddress>
</QPG:Responsible>
<QPG:Values>
<QPG:Files>
<QPG:Add file="../zconf.h" install="/opt/include/" user="root:sys" permission="644"/>
<QPG:Add file="../zlib.h" install="/opt/include/" user="root:sys" permission="644"/>
| | | | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<QPG:EmailAddress></QPG:EmailAddress>
</QPG:Responsible>
<QPG:Values>
<QPG:Files>
<QPG:Add file="../zconf.h" install="/opt/include/" user="root:sys" permission="644"/>
<QPG:Add file="../zlib.h" install="/opt/include/" user="root:sys" permission="644"/>
<QPG:Add file="../libz.so.1.3.1" install="/opt/lib/" user="root:bin" permission="644"/>
<QPG:Add file="libz.so" install="/opt/lib/" component="dev" filetype="symlink" linkto="libz.so.1.3.1"/>
<QPG:Add file="libz.so.1" install="/opt/lib/" filetype="symlink" linkto="libz.so.1.3.1"/>
<QPG:Add file="../libz.so.1.3.1" install="/opt/lib/" component="slib"/>
</QPG:Files>
<QPG:PackageFilter>
<QPM:PackageManifest>
<QPM:PackageDescription>
<QPM:PackageType>Library</QPM:PackageType>
<QPM:PackageReleaseNotes></QPM:PackageReleaseNotes>
|
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
<QPM:ProductDescriptionShort>A massively spiffy yet delicately unobtrusive compression library.</QPM:ProductDescriptionShort>
<QPM:ProductDescriptionLong>zlib is designed to be a free, general-purpose, legally unencumbered, lossless data compression library for use on virtually any computer hardware and operating system.</QPM:ProductDescriptionLong>
<QPM:ProductDescriptionURL>http://www.gzip.org/zlib</QPM:ProductDescriptionURL>
<QPM:ProductDescriptionEmbedURL></QPM:ProductDescriptionEmbedURL>
</QPM:ProductDescription>
<QPM:ReleaseDescription>
| | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
<QPM:ProductDescriptionShort>A massively spiffy yet delicately unobtrusive compression library.</QPM:ProductDescriptionShort>
<QPM:ProductDescriptionLong>zlib is designed to be a free, general-purpose, legally unencumbered, lossless data compression library for use on virtually any computer hardware and operating system.</QPM:ProductDescriptionLong>
<QPM:ProductDescriptionURL>http://www.gzip.org/zlib</QPM:ProductDescriptionURL>
<QPM:ProductDescriptionEmbedURL></QPM:ProductDescriptionEmbedURL>
</QPM:ProductDescription>
<QPM:ReleaseDescription>
<QPM:ReleaseVersion>1.3.1</QPM:ReleaseVersion>
<QPM:ReleaseUrgency>Medium</QPM:ReleaseUrgency>
<QPM:ReleaseStability>Stable</QPM:ReleaseStability>
<QPM:ReleaseNoteMinor></QPM:ReleaseNoteMinor>
<QPM:ReleaseNoteMajor></QPM:ReleaseNoteMajor>
<QPM:ExcludeCountries>
<QPM:Country></QPM:Country>
</QPM:ExcludeCountries>
|
| ︙ | ︙ |
Changes to compat/zlib/test/example.c.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 | */ static const char dictionary[] = "hello"; static uLong dictId; /* Adler32 value of the dictionary */ #ifdef Z_SOLO | | | | | 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 |
*/
static const char dictionary[] = "hello";
static uLong dictId; /* Adler32 value of the dictionary */
#ifdef Z_SOLO
static void *myalloc(void *q, unsigned n, unsigned m) {
(void)q;
return calloc(n, m);
}
static void myfree(void *q, void *p) {
(void)q;
free(p);
}
static alloc_func zalloc = myalloc;
static free_func zfree = myfree;
#else /* !Z_SOLO */
static alloc_func zalloc = (alloc_func)0;
static free_func zfree = (free_func)0;
/* ===========================================================================
* Test compress() and uncompress()
*/
static void test_compress(Byte *compr, uLong comprLen, Byte *uncompr,
uLong uncomprLen) {
int err;
uLong len = (uLong)strlen(hello)+1;
err = compress(compr, &comprLen, (const Bytef*)hello, len);
CHECK_ERR(err, "compress");
|
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
printf("uncompress(): %s\n", (char *)uncompr);
}
}
/* ===========================================================================
* Test read/write of .gz files
*/
| | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
printf("uncompress(): %s\n", (char *)uncompr);
}
}
/* ===========================================================================
* Test read/write of .gz files
*/
static void test_gzio(const char *fname, Byte *uncompr, uLong uncomprLen) {
#ifdef NO_GZCOMPRESS
fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n");
#else
int err;
int len = (int)strlen(hello)+1;
gzFile file;
z_off_t pos;
|
| ︙ | ︙ | |||
159 160 161 162 163 164 165 | } #endif /* Z_SOLO */ /* =========================================================================== * Test deflate() with small buffers */ | | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
}
#endif /* Z_SOLO */
/* ===========================================================================
* Test deflate() with small buffers
*/
static void test_deflate(Byte *compr, uLong comprLen) {
z_stream c_stream; /* compression stream */
int err;
uLong len = (uLong)strlen(hello)+1;
c_stream.zalloc = zalloc;
c_stream.zfree = zfree;
c_stream.opaque = (voidpf)0;
|
| ︙ | ︙ | |||
194 195 196 197 198 199 200 |
err = deflateEnd(&c_stream);
CHECK_ERR(err, "deflateEnd");
}
/* ===========================================================================
* Test inflate() with small buffers
*/
| | | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
err = deflateEnd(&c_stream);
CHECK_ERR(err, "deflateEnd");
}
/* ===========================================================================
* Test inflate() with small buffers
*/
static void test_inflate(Byte *compr, uLong comprLen, Byte *uncompr,
uLong uncomprLen) {
int err;
z_stream d_stream; /* decompression stream */
strcpy((char*)uncompr, "garbage");
d_stream.zalloc = zalloc;
|
| ︙ | ︙ | |||
233 234 235 236 237 238 239 |
printf("inflate(): %s\n", (char *)uncompr);
}
}
/* ===========================================================================
* Test deflate() with large buffers and dynamic change of compression level
*/
| | | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
printf("inflate(): %s\n", (char *)uncompr);
}
}
/* ===========================================================================
* Test deflate() with large buffers and dynamic change of compression level
*/
static void test_large_deflate(Byte *compr, uLong comprLen, Byte *uncompr,
uLong uncomprLen) {
z_stream c_stream; /* compression stream */
int err;
c_stream.zalloc = zalloc;
c_stream.zfree = zfree;
c_stream.opaque = (voidpf)0;
|
| ︙ | ︙ | |||
286 287 288 289 290 291 292 |
err = deflateEnd(&c_stream);
CHECK_ERR(err, "deflateEnd");
}
/* ===========================================================================
* Test inflate() with large buffers
*/
| | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
err = deflateEnd(&c_stream);
CHECK_ERR(err, "deflateEnd");
}
/* ===========================================================================
* Test inflate() with large buffers
*/
static void test_large_inflate(Byte *compr, uLong comprLen, Byte *uncompr,
uLong uncomprLen) {
int err;
z_stream d_stream; /* decompression stream */
strcpy((char*)uncompr, "garbage");
d_stream.zalloc = zalloc;
|
| ︙ | ︙ | |||
325 326 327 328 329 330 331 |
printf("large_inflate(): OK\n");
}
}
/* ===========================================================================
* Test deflate() with full flush
*/
| | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
printf("large_inflate(): OK\n");
}
}
/* ===========================================================================
* Test deflate() with full flush
*/
static void test_flush(Byte *compr, uLong *comprLen) {
z_stream c_stream; /* compression stream */
int err;
uInt len = (uInt)strlen(hello)+1;
c_stream.zalloc = zalloc;
c_stream.zfree = zfree;
c_stream.opaque = (voidpf)0;
|
| ︙ | ︙ | |||
360 361 362 363 364 365 366 |
*comprLen = c_stream.total_out;
}
/* ===========================================================================
* Test inflateSync()
*/
| | > | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
*comprLen = c_stream.total_out;
}
/* ===========================================================================
* Test inflateSync()
*/
static void test_sync(Byte *compr, uLong comprLen, Byte *uncompr,
uLong uncomprLen) {
int err;
z_stream d_stream; /* decompression stream */
strcpy((char*)uncompr, "garbage");
d_stream.zalloc = zalloc;
d_stream.zfree = zfree;
|
| ︙ | ︙ | |||
400 401 402 403 404 405 406 |
printf("after inflateSync(): hel%s\n", (char *)uncompr);
}
/* ===========================================================================
* Test deflate() with preset dictionary
*/
| | | 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
printf("after inflateSync(): hel%s\n", (char *)uncompr);
}
/* ===========================================================================
* Test deflate() with preset dictionary
*/
static void test_dict_deflate(Byte *compr, uLong comprLen) {
z_stream c_stream; /* compression stream */
int err;
c_stream.zalloc = zalloc;
c_stream.zfree = zfree;
c_stream.opaque = (voidpf)0;
|
| ︙ | ︙ | |||
434 435 436 437 438 439 440 |
err = deflateEnd(&c_stream);
CHECK_ERR(err, "deflateEnd");
}
/* ===========================================================================
* Test inflate() with a preset dictionary
*/
| | | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
err = deflateEnd(&c_stream);
CHECK_ERR(err, "deflateEnd");
}
/* ===========================================================================
* Test inflate() with a preset dictionary
*/
static void test_dict_inflate(Byte *compr, uLong comprLen, Byte *uncompr,
uLong uncomprLen) {
int err;
z_stream d_stream; /* decompression stream */
strcpy((char*)uncompr, "garbage");
d_stream.zalloc = zalloc;
|
| ︙ | ︙ |
Changes to compat/zlib/test/minigzip.c.
| ︙ | ︙ | |||
145 146 147 148 149 150 151 | #ifdef Z_SOLO /* for Z_SOLO, create simplified gz* functions using deflate and inflate */ #if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE) # include <unistd.h> /* for unlink() */ #endif | | | | | 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 |
#ifdef Z_SOLO
/* for Z_SOLO, create simplified gz* functions using deflate and inflate */
#if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE)
# include <unistd.h> /* for unlink() */
#endif
static void *myalloc(void *q, unsigned n, unsigned m) {
(void)q;
return calloc(n, m);
}
static void myfree(void *q, void *p) {
(void)q;
free(p);
}
typedef struct gzFile_s {
FILE *file;
int write;
int err;
char *msg;
z_stream strm;
} *gzFile;
static gzFile gz_open(const char *path, int fd, const char *mode) {
gzFile gz;
int ret;
gz = malloc(sizeof(struct gzFile_s));
if (gz == NULL)
return NULL;
gz->write = strchr(mode, 'w') != NULL;
|
| ︙ | ︙ | |||
197 198 199 200 201 202 203 |
return NULL;
}
gz->err = 0;
gz->msg = "";
return gz;
}
| | | | | | 197 198 199 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 234 235 236 237 |
return NULL;
}
gz->err = 0;
gz->msg = "";
return gz;
}
static gzFile gzopen(const char *path, const char *mode) {
return gz_open(path, -1, mode);
}
static gzFile gzdopen(int fd, const char *mode) {
return gz_open(NULL, fd, mode);
}
static int gzwrite(gzFile gz, const void *buf, unsigned len) {
z_stream *strm;
unsigned char out[BUFLEN];
if (gz == NULL || !gz->write)
return 0;
strm = &(gz->strm);
strm->next_in = (void *)buf;
strm->avail_in = len;
do {
strm->next_out = out;
strm->avail_out = BUFLEN;
(void)deflate(strm, Z_NO_FLUSH);
fwrite(out, 1, BUFLEN - strm->avail_out, gz->file);
} while (strm->avail_out == 0);
return len;
}
static int gzread(gzFile gz, void *buf, unsigned len) {
int ret;
unsigned got;
unsigned char in[1];
z_stream *strm;
if (gz == NULL || gz->write)
return 0;
|
| ︙ | ︙ | |||
254 255 256 257 258 259 260 |
}
if (ret == Z_STREAM_END)
inflateReset(strm);
} while (strm->avail_out);
return len - strm->avail_out;
}
| | | 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
}
if (ret == Z_STREAM_END)
inflateReset(strm);
} while (strm->avail_out);
return len - strm->avail_out;
}
static int gzclose(gzFile gz) {
z_stream *strm;
unsigned char out[BUFLEN];
if (gz == NULL)
return Z_STREAM_ERROR;
strm = &(gz->strm);
if (gz->write) {
|
| ︙ | ︙ | |||
279 280 281 282 283 284 285 |
else
inflateEnd(strm);
fclose(gz->file);
free(gz);
return Z_OK;
}
| | | | | | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
else
inflateEnd(strm);
fclose(gz->file);
free(gz);
return Z_OK;
}
static const char *gzerror(gzFile gz, int *err) {
*err = gz->err;
return gz->msg;
}
#endif
static char *prog;
/* ===========================================================================
* Display error message and exit
*/
static void error(const char *msg) {
fprintf(stderr, "%s: %s\n", prog, msg);
exit(1);
}
#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
/* Try compressing the input file at once using mmap. Return Z_OK if
* success, Z_ERRNO otherwise.
*/
static int gz_compress_mmap(FILE *in, gzFile out) {
int len;
int err;
int ifd = fileno(in);
caddr_t buf; /* mmap'ed buffer for the entire input file */
off_t buf_len; /* length of the input file */
struct stat sb;
|
| ︙ | ︙ | |||
334 335 336 337 338 339 340 | } #endif /* USE_MMAP */ /* =========================================================================== * Compress input to output then close both files. */ | | | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
}
#endif /* USE_MMAP */
/* ===========================================================================
* Compress input to output then close both files.
*/
static void gz_compress(FILE *in, gzFile out) {
local char buf[BUFLEN];
int len;
int err;
#ifdef USE_MMAP
/* Try first compressing with mmap. If mmap fails (minigzip used in a
* pipe), use the normal fread loop.
|
| ︙ | ︙ | |||
362 363 364 365 366 367 368 |
fclose(in);
if (gzclose(out) != Z_OK) error("failed gzclose");
}
/* ===========================================================================
* Uncompress input to output then close both files.
*/
| | | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
fclose(in);
if (gzclose(out) != Z_OK) error("failed gzclose");
}
/* ===========================================================================
* Uncompress input to output then close both files.
*/
static void gz_uncompress(gzFile in, FILE *out) {
local char buf[BUFLEN];
int len;
int err;
for (;;) {
len = gzread(in, buf, sizeof(buf));
if (len < 0) error (gzerror(in, &err));
|
| ︙ | ︙ | |||
386 387 388 389 390 391 392 | } /* =========================================================================== * Compress the given file: create a corresponding .gz file and remove the * original. */ | | | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
}
/* ===========================================================================
* Compress the given file: create a corresponding .gz file and remove the
* original.
*/
static void file_compress(char *file, char *mode) {
local char outfile[MAX_NAME_LEN];
FILE *in;
gzFile out;
if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
fprintf(stderr, "%s: filename too long\n", prog);
exit(1);
|
| ︙ | ︙ | |||
422 423 424 425 426 427 428 |
unlink(file);
}
/* ===========================================================================
* Uncompress the given file and remove the original.
*/
| | | 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
unlink(file);
}
/* ===========================================================================
* Uncompress the given file and remove the original.
*/
static void file_uncompress(char *file) {
local char buf[MAX_NAME_LEN];
char *infile, *outfile;
FILE *out;
gzFile in;
z_size_t len = strlen(file);
if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
|
| ︙ | ︙ |
Changes to compat/zlib/treebuild.xml.
1 | <?xml version="1.0" ?> | | | | 1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" ?>
<package name="zlib" version="1.3.1">
<library name="zlib" dlversion="1.3.1" dlname="z">
<property name="description"> zip compression library </property>
<property name="include-target-dir" value="$(@PACKAGE/install-includedir)" />
<!-- fixme: not implemented yet -->
<property name="compiler/c/inline" value="yes" />
<include-file name="zlib.h" scope="public" mode="644" />
|
| ︙ | ︙ |
Changes to compat/zlib/trees.c.
1 | /* trees.c -- output deflated data using Huffman coding | | | 1 2 3 4 5 6 7 8 9 | /* trees.c -- output deflated data using Huffman coding * Copyright (C) 1995-2024 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ /* * ALGORITHM * |
| ︙ | ︙ | |||
895 896 897 898 899 900 901 |
/* ===========================================================================
* Send the block data compressed using the given Huffman trees
*/
local void compress_block(deflate_state *s, const ct_data *ltree,
const ct_data *dtree) {
unsigned dist; /* distance of matched string */
int lc; /* match length or unmatched char (if dist == 0) */
| | > > > > > | 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 |
/* ===========================================================================
* Send the block data compressed using the given Huffman trees
*/
local void compress_block(deflate_state *s, const ct_data *ltree,
const ct_data *dtree) {
unsigned dist; /* distance of matched string */
int lc; /* match length or unmatched char (if dist == 0) */
unsigned sx = 0; /* running index in symbol buffers */
unsigned code; /* the code to send */
int extra; /* number of extra bits to send */
if (s->sym_next != 0) do {
#ifdef LIT_MEM
dist = s->d_buf[sx];
lc = s->l_buf[sx++];
#else
dist = s->sym_buf[sx++] & 0xff;
dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8;
lc = s->sym_buf[sx++];
#endif
if (dist == 0) {
send_code(s, lc, ltree); /* send a literal byte */
Tracecv(isgraph(lc), (stderr," '%c' ", lc));
} else {
/* Here, lc is the match length - MIN_MATCH */
code = _length_code[lc];
send_code(s, code + LITERALS + 1, ltree); /* send length code */
|
| ︙ | ︙ | |||
927 928 929 930 931 932 933 |
extra = extra_dbits[code];
if (extra != 0) {
dist -= (unsigned)base_dist[code];
send_bits(s, dist, extra); /* send the extra distance bits */
}
} /* literal or match pair ? */
| | > > > > | 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 |
extra = extra_dbits[code];
if (extra != 0) {
dist -= (unsigned)base_dist[code];
send_bits(s, dist, extra); /* send the extra distance bits */
}
} /* literal or match pair ? */
/* Check for no overlay of pending_buf on needed symbols */
#ifdef LIT_MEM
Assert(s->pending < 2 * (s->lit_bufsize + sx), "pendingBuf overflow");
#else
Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow");
#endif
} while (sx < s->sym_next);
send_code(s, END_BLOCK, ltree);
}
/* ===========================================================================
|
| ︙ | ︙ | |||
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 |
}
/* ===========================================================================
* Save the match info and tally the frequency counts. Return true if
* the current block must be flushed.
*/
int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) {
s->sym_buf[s->sym_next++] = (uch)dist;
s->sym_buf[s->sym_next++] = (uch)(dist >> 8);
s->sym_buf[s->sym_next++] = (uch)lc;
if (dist == 0) {
/* lc is the unmatched char */
s->dyn_ltree[lc].Freq++;
} else {
s->matches++;
/* Here, lc is the match length - MIN_MATCH */
dist--; /* dist = match distance - 1 */
| > > > > > | 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 |
}
/* ===========================================================================
* Save the match info and tally the frequency counts. Return true if
* the current block must be flushed.
*/
int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) {
#ifdef LIT_MEM
s->d_buf[s->sym_next] = (ush)dist;
s->l_buf[s->sym_next++] = (uch)lc;
#else
s->sym_buf[s->sym_next++] = (uch)dist;
s->sym_buf[s->sym_next++] = (uch)(dist >> 8);
s->sym_buf[s->sym_next++] = (uch)lc;
#endif
if (dist == 0) {
/* lc is the unmatched char */
s->dyn_ltree[lc].Freq++;
} else {
s->matches++;
/* Here, lc is the match length - MIN_MATCH */
dist--; /* dist = match distance - 1 */
|
| ︙ | ︙ |
Changes to compat/zlib/win32/DLL_FAQ.txt.
1 2 3 4 5 |
Frequently Asked Questions about ZLIB1.DLL
This document describes the design, the rationale, and the usage
| | | < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Frequently Asked Questions about ZLIB1.DLL
This document describes the design, the rationale, and the usage
of the common DLL build of zlib, named ZLIB1.DLL. If you have
general questions about zlib, you should see the file "FAQ" found
in the zlib distribution, or at the following location:
http://www.gzip.org/zlib/zlib_faq.html
1. What is ZLIB1.DLL, and how can I get it?
- ZLIB1.DLL is the common build of zlib as a DLL.
(Please remark the character '1' in the name.)
Applications that link to ZLIB1.DLL can rely on the following
specification:
* The exported symbols are exclusively defined in the source
files "zlib.h" and "zlib.def", found in an official zlib
source distribution.
* The symbols are exported by name, not by ordinal.
|
| ︙ | ︙ | |||
375 376 377 378 379 380 381 |
macros like NO_GZCOMPRESS or NO_GZIP at compile time?
- No. A legitimate build of ZLIB1.DLL must provide the complete
zlib functionality, as implemented in the official zlib source
code. But you can make your own private DLL build, under a
different file name, as suggested in the previous answer.
| < < < < < < < < < < < < | 371 372 373 374 375 376 377 378 379 380 381 |
macros like NO_GZCOMPRESS or NO_GZIP at compile time?
- No. A legitimate build of ZLIB1.DLL must provide the complete
zlib functionality, as implemented in the official zlib source
code. But you can make your own private DLL build, under a
different file name, as suggested in the previous answer.
**
This document is written and maintained by
Cosmin Truta <cosmint@cs.ubbcluj.ro>
|
Changes to compat/zlib/win32/README-WIN32.txt.
1 2 | ZLIB DATA COMPRESSION LIBRARY | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ZLIB DATA COMPRESSION LIBRARY zlib 1.3.1 is a general purpose data compression library. All the code is thread safe. The data format used by the zlib library is described by RFCs (Request for Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). All functions of the compression library are documented in the file zlib.h (volunteer to write man pages welcome, contact zlib@gzip.org). Two compiled examples are distributed in this package, example and minigzip. The example_d and minigzip_d flavors validate that the zlib1.dll file is working correctly. Questions about zlib should be sent to <zlib@gzip.org>. The zlib home page is http://zlib.net/ . Before reporting a problem, please check this site to verify that you have the latest version of zlib; otherwise get the latest version and check whether the problem still exists or not. PLEASE read DLL_FAQ.txt, and the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. Manifest: The package zlib-1.3.1-win32-x86.zip will contain the following files: README-WIN32.txt This document ChangeLog Changes since previous zlib packages DLL_FAQ.txt Frequently asked questions about zlib1.dll zlib.3.pdf Documentation of this library in Adobe Acrobat format example.exe A statically-bound example (using zlib.lib, not the dll) |
| ︙ | ︙ |
Changes to compat/zlib/zconf.h.
1 | /* zconf.h -- configuration of the zlib compression library | | | 1 2 3 4 5 6 7 8 9 | /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H |
| ︙ | ︙ | |||
296 297 298 299 300 301 302 | # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif | < < < < < < < < | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT |
| ︙ | ︙ |
Changes to compat/zlib/zconf.h.cmakein.
1 | /* zconf.h -- configuration of the zlib compression library | | | 1 2 3 4 5 6 7 8 9 | /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H |
| ︙ | ︙ | |||
298 299 300 301 302 303 304 | # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif | < < < < < < < < | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT |
| ︙ | ︙ |
Changes to compat/zlib/zconf.h.in.
1 | /* zconf.h -- configuration of the zlib compression library | | | 1 2 3 4 5 6 7 8 9 | /* zconf.h -- configuration of the zlib compression library * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* @(#) $Id$ */ #ifndef ZCONF_H #define ZCONF_H |
| ︙ | ︙ | |||
296 297 298 299 300 301 302 | # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif | < < < < < < < < | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | # ifdef STDC # define OF(args) args # else # define OF(args) () # endif #endif /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, * just define FAR to be empty. */ #ifdef SYS16BIT |
| ︙ | ︙ |
Changes to compat/zlib/zlib.3.
|
| | | 1 2 3 4 5 6 7 8 | .TH ZLIB 3 "22 Jan 2024" .SH NAME zlib \- compression/decompression library .SH SYNOPSIS [see .I zlib.h for full description] .SH DESCRIPTION |
| ︙ | ︙ | |||
101 102 103 104 105 106 107 | .IP http://zlib.net/zlib_faq.html .LP before asking for help. Send questions and/or comments to zlib@gzip.org, or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). .SH AUTHORS AND LICENSE | | | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | .IP http://zlib.net/zlib_faq.html .LP before asking for help. Send questions and/or comments to zlib@gzip.org, or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). .SH AUTHORS AND LICENSE Version 1.3.1 .LP Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler .LP This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. .LP Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it |
| ︙ | ︙ |
Changes to compat/zlib/zlib.3.pdf.
cannot compute difference between binary files
Changes to compat/zlib/zlib.h.
1 | /* zlib.h -- interface of the 'zlib' general purpose compression library | | | | 1 2 3 4 5 6 7 8 9 10 11 | /* zlib.h -- interface of the 'zlib' general purpose compression library version 1.3.1, January 22nd, 2024 Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it |
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
#include "zconf.h"
#ifdef __cplusplus
extern "C" {
#endif
| | | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include "zconf.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ZLIB_VERSION "1.3.1"
#define ZLIB_VERNUM 0x1310
#define ZLIB_VER_MAJOR 1
#define ZLIB_VER_MINOR 3
#define ZLIB_VER_REVISION 1
#define ZLIB_VER_SUBREVISION 0
/*
The 'zlib' compression library provides in-memory compression and
decompression functions, including integrity checks of the uncompressed data.
This version of the library supports only one compression method (deflation)
but other algorithms will be added later and will have the same stream
|
| ︙ | ︙ | |||
932 933 934 935 936 937 938 |
inflateSync searches for a 00 00 FF FF pattern in the compressed data.
All full flush points have this pattern, but not all occurrences of this
pattern are full flush points.
inflateSync returns Z_OK if a possible full flush point has been found,
Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
| | | | | | 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 |
inflateSync searches for a 00 00 FF FF pattern in the compressed data.
All full flush points have this pattern, but not all occurrences of this
pattern are full flush points.
inflateSync returns Z_OK if a possible full flush point has been found,
Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
In the success case, the application may save the current value of total_in
which indicates where valid compressed data was found. In the error case,
the application may repeatedly call inflateSync, providing more input each
time, until success or end of the input data.
*/
ZEXTERN int ZEXPORT inflateCopy(z_streamp dest,
z_streamp source);
/*
Sets the destination stream as a complete copy of the source stream.
|
| ︙ | ︙ | |||
1754 1755 1756 1757 1758 1759 1760 |
/*
ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2);
Combine two CRC-32 check values into one. For two sequences of bytes,
seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32
check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
| | | | 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 |
/*
ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2);
Combine two CRC-32 check values into one. For two sequences of bytes,
seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32
check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
len2. len2 must be non-negative.
*/
/*
ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2);
Return the operator corresponding to length len2, to be used with
crc32_combine_op(). len2 must be non-negative.
*/
ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op);
/*
Give the same result as crc32_combine(), using op in place of len2. op is
is generated from len2 by crc32_combine_gen(). This will be faster than
crc32_combine() if the generated op is used more than once.
|
| ︙ | ︙ |
Changes to compat/zlib/zlib.map.
| ︙ | ︙ |
Changes to compat/zlib/zutil.h.
1 | /* zutil.h -- internal interface and configuration of the compression library | | | 1 2 3 4 5 6 7 8 9 | /* zutil.h -- internal interface and configuration of the compression library * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is part of the implementation of the compression library and is subject to change. Applications should only use zlib.h. */ |
| ︙ | ︙ | |||
52 53 54 55 56 57 58 | # define Z_U8 unsigned # endif #endif extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# define Z_U8 unsigned
# endif
#endif
extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
/* (size given to avoid silly warnings with Visual C++) */
#define ERR_MSG(err) z_errmsg[(err) < -6 || (err) > 2 ? 9 : 2 - (err)]
#define ERR_RETURN(strm,err) \
return (strm->msg = ERR_MSG(err), (err))
/* To be used only when the state is known to be valid */
/* common constants */
|
| ︙ | ︙ | |||
133 134 135 136 137 138 139 | #ifdef OS2 # define OS_CODE 6 # if defined(M_I86) && !defined(Z_SOLO) # include <malloc.h> # endif #endif | | < < < < < < < < < | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | #ifdef OS2 # define OS_CODE 6 # if defined(M_I86) && !defined(Z_SOLO) # include <malloc.h> # endif #endif #if defined(MACOS) # define OS_CODE 7 #endif #ifdef __acorn # define OS_CODE 13 #endif #if defined(WIN32) && !defined(__CYGWIN__) |
| ︙ | ︙ | |||
166 167 168 169 170 171 172 | # define OS_CODE 18 #endif #ifdef __APPLE__ # define OS_CODE 19 #endif | < < < < < < < < < < < < | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | # define OS_CODE 18 #endif #ifdef __APPLE__ # define OS_CODE 19 #endif #if defined(__BORLANDC__) && !defined(MSDOS) #pragma warn -8004 #pragma warn -8008 #pragma warn -8066 #endif /* provide prototypes for these when building zlib without LFS */ |
| ︙ | ︙ |
Changes to configure.
1 2 3 | #!/bin/sh dir="`dirname "$0"`/autosetup" WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@" | > | 1 2 3 4 | #!/bin/sh dir="`dirname "$0"`/autosetup" #@@INITCHECK@@# WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@" |
Changes to extsrc/cson_amalgamation.c.
| ︙ | ︙ | |||
1433 1434 1435 1436 1437 1438 1439 |
#endif
#if defined(__cplusplus)
extern "C" {
#endif
| < | 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 |
#endif
#if defined(__cplusplus)
extern "C" {
#endif
/**
This type holds the "vtbl" for type-specific operations when
working with cson_value objects.
All cson_values of a given logical type share a pointer to a single
library-internal instance of this class.
*/
|
| ︙ | ︙ | |||
1684 1685 1686 1687 1688 1689 1690 |
&& ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) )
? 1
: 0;
}
char const * cson_rc_string(int rc)
{
| | | | > | 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 |
&& ( m < (void const *)&CSON_SPECIAL_VALUES[CSON_INTERNAL_VALUES_LENGTH]) )
? 1
: 0;
}
char const * cson_rc_string(int rc)
{
switch(rc){
#define CHECK(N) case CSON_RC_##N: return #N;
CHECK(OK);
CHECK(ArgError);
CHECK(RangeError);
CHECK(TypeError);
CHECK(IOError);
CHECK(AllocError);
CHECK(NYIError);
CHECK(InternalError);
CHECK(UnsupportedError);
CHECK(NotFoundError);
CHECK(UnknownError);
CHECK(Parse_INVALID_CHAR);
CHECK(Parse_INVALID_KEYWORD);
CHECK(Parse_INVALID_ESCAPE_SEQUENCE);
CHECK(Parse_INVALID_UNICODE_SEQUENCE);
CHECK(Parse_INVALID_NUMBER);
CHECK(Parse_NESTING_DEPTH_REACHED);
CHECK(Parse_UNBALANCED_COLLECTION);
CHECK(Parse_EXPECTED_KEY);
CHECK(Parse_EXPECTED_COLON);
default: return "UnknownError";
}
#undef CHECK
}
/**
If CSON_LOG_ALLOC is true then the cson_malloc/realloc/free() routines
will log a message to stderr.
*/
|
| ︙ | ︙ | |||
1852 1853 1854 1855 1856 1857 1858 |
++cv->refcount;
}
}
#if 0
int cson_value_refcount_set( cson_value * cv, unsigned short rc )
{
| | | | | 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 |
++cv->refcount;
}
}
#if 0
int cson_value_refcount_set( cson_value * cv, unsigned short rc )
{
if( NULL == cv ) return CSON_RC_ArgError;
else
{
cv->refcount = rc;
return 0;
}
}
#endif
int cson_value_add_reference( cson_value * cv )
{
if( NULL == cv ) return CSON_RC_ArgError;
else if( (cv->refcount+1) < cv->refcount )
{
return CSON_RC_RangeError;
}
else
{
cson_refcount_incr( cv );
return 0;
}
}
|
| ︙ | ︙ | |||
2333 2334 2335 2336 2337 2338 2339 |
val->api->cleanup(val);
*val = cson_value_undef;
val->refcount = rc;
}
}
}
| | | | | | | | 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 |
val->api->cleanup(val);
*val = cson_value_undef;
val->refcount = rc;
}
}
}
static cson_value * cson_value_array_alloc(void)
{
cson_value * v = cson_value_new(CSON_TYPE_ARRAY,0);
if( NULL != v )
{
cson_array * ar = CSON_ARRAY(v);
assert(NULL != ar);
*ar = cson_array_empty;
}
return v;
}
static cson_value * cson_value_object_alloc(void)
{
cson_value * v = cson_value_new(CSON_TYPE_OBJECT,0);
if( NULL != v )
{
cson_object * obj = CSON_OBJ(v);
assert(NULL != obj);
*obj = cson_object_empty;
}
return v;
}
cson_value * cson_value_new_object(void)
{
return cson_value_object_alloc();
}
cson_object * cson_new_object(void)
{
return cson_value_get_object( cson_value_new_object() );
}
cson_value * cson_value_new_array(void)
{
return cson_value_array_alloc();
}
cson_array * cson_new_array(void)
{
return cson_value_get_array( cson_value_new_array() );
}
/**
Frees kvp->key and kvp->value and sets them to NULL, but does not free
kvp. If !kvp then this is a no-op.
|
| ︙ | ︙ | |||
2500 2501 2502 2503 2504 2505 2506 |
int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state )
{
int rc;
enum { BufSize = 1024 * 4 };
char rbuf[BufSize];
size_t total = 0;
unsigned int rlen = 0;
| | | 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 |
int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state )
{
int rc;
enum { BufSize = 1024 * 4 };
char rbuf[BufSize];
size_t total = 0;
unsigned int rlen = 0;
if( ! dest || ! src ) return CSON_RC_ArgError;
dest->used = 0;
while(1)
{
rlen = BufSize;
rc = src( state, rbuf, &rlen );
if( rc ) break;
total += rlen;
|
| ︙ | ︙ | |||
2528 2529 2530 2531 2532 2533 2534 |
}
return rc;
}
int cson_data_source_FILE( void * state, void * dest, unsigned int * n )
{
FILE * f = (FILE*) state;
| | | | | | 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 |
}
return rc;
}
int cson_data_source_FILE( void * state, void * dest, unsigned int * n )
{
FILE * f = (FILE*) state;
if( ! state || ! n || !dest ) return CSON_RC_ArgError;
else if( !*n ) return CSON_RC_RangeError;
*n = (unsigned int)fread( dest, 1, *n, f );
if( !*n )
{
return feof(f) ? 0 : CSON_RC_IOError;
}
return 0;
}
int cson_parse_FILE( cson_value ** tgt, FILE * src,
cson_parse_opt const * opt, cson_parse_info * err )
{
return cson_parse( tgt, cson_data_source_FILE, src, opt, err );
}
int cson_value_fetch_bool( cson_value const * val, char * v )
{
/**
FIXME: move the to-bool operation into cson_value_api, like we
do in the C++ API.
*/
if( ! val || !val->api ) return CSON_RC_ArgError;
else
{
int rc = 0;
char b = 0;
switch( val->api->typeID )
{
case CSON_TYPE_ARRAY:
|
| ︙ | ︙ | |||
2586 2587 2588 2589 2590 2591 2592 |
case CSON_TYPE_DOUBLE: {
cson_double_t d = 0.0;
cson_value_fetch_double( val, &d );
b = (0.0==d) ? 0 : 1;
break;
}
default:
| | | | 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 |
case CSON_TYPE_DOUBLE: {
cson_double_t d = 0.0;
cson_value_fetch_double( val, &d );
b = (0.0==d) ? 0 : 1;
break;
}
default:
rc = CSON_RC_TypeError;
break;
}
if( v ) *v = b;
return rc;
}
}
char cson_value_get_bool( cson_value const * val )
{
char i = 0;
cson_value_fetch_bool( val, &i );
return i;
}
int cson_value_fetch_integer( cson_value const * val, cson_int_t * v )
{
if( ! val || !val->api ) return CSON_RC_ArgError;
else
{
cson_int_t i = 0;
int rc = 0;
switch(val->api->typeID)
{
case CSON_TYPE_UNDEF:
|
| ︙ | ︙ | |||
2639 2640 2641 2642 2643 2644 2645 |
i = (cson_int_t)d;
break;
}
case CSON_TYPE_STRING:
case CSON_TYPE_ARRAY:
case CSON_TYPE_OBJECT:
default:
| | | | 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 |
i = (cson_int_t)d;
break;
}
case CSON_TYPE_STRING:
case CSON_TYPE_ARRAY:
case CSON_TYPE_OBJECT:
default:
rc = CSON_RC_TypeError;
break;
}
if(!rc && v) *v = i;
return rc;
}
}
cson_int_t cson_value_get_integer( cson_value const * val )
{
cson_int_t i = 0;
cson_value_fetch_integer( val, &i );
return i;
}
int cson_value_fetch_double( cson_value const * val, cson_double_t * v )
{
if( ! val || !val->api ) return CSON_RC_ArgError;
else
{
cson_double_t d = 0.0;
int rc = 0;
switch(val->api->typeID)
{
case CSON_TYPE_UNDEF:
|
| ︙ | ︙ | |||
2685 2686 2687 2688 2689 2690 2691 |
}
case CSON_TYPE_DOUBLE: {
cson_double_t const* dv = CSON_DBL(val);
d = dv ? *dv : 0.0;
break;
}
default:
| | | | | 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 |
}
case CSON_TYPE_DOUBLE: {
cson_double_t const* dv = CSON_DBL(val);
d = dv ? *dv : 0.0;
break;
}
default:
rc = CSON_RC_TypeError;
break;
}
if(v) *v = d;
return rc;
}
}
cson_double_t cson_value_get_double( cson_value const * val )
{
cson_double_t i = 0.0;
cson_value_fetch_double( val, &i );
return i;
}
int cson_value_fetch_string( cson_value const * val, cson_string ** dest )
{
if( ! val || ! dest ) return CSON_RC_ArgError;
else if( ! cson_value_is_string(val) ) return CSON_RC_TypeError;
else
{
if( dest ) *dest = CSON_STR(val);
return 0;
}
}
|
| ︙ | ︙ | |||
2725 2726 2727 2728 2729 2730 2731 |
char const * cson_value_get_cstr( cson_value const * val )
{
return cson_string_cstr( cson_value_get_string(val) );
}
int cson_value_fetch_object( cson_value const * val, cson_object ** obj )
{
| | | | | | | | | | 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 |
char const * cson_value_get_cstr( cson_value const * val )
{
return cson_string_cstr( cson_value_get_string(val) );
}
int cson_value_fetch_object( cson_value const * val, cson_object ** obj )
{
if( ! val ) return CSON_RC_ArgError;
else if( ! cson_value_is_object(val) ) return CSON_RC_TypeError;
else
{
if(obj) *obj = CSON_OBJ(val);
return 0;
}
}
cson_object * cson_value_get_object( cson_value const * v )
{
cson_object * obj = NULL;
cson_value_fetch_object( v, &obj );
return obj;
}
int cson_value_fetch_array( cson_value const * val, cson_array ** ar)
{
if( ! val ) return CSON_RC_ArgError;
else if( !cson_value_is_array(val) ) return CSON_RC_TypeError;
else
{
if(ar) *ar = CSON_ARRAY(val);
return 0;
}
}
cson_array * cson_value_get_array( cson_value const * v )
{
cson_array * ar = NULL;
cson_value_fetch_array( v, &ar );
return ar;
}
cson_kvp * cson_kvp_alloc(void)
{
cson_kvp * kvp = (cson_kvp*)cson_malloc(sizeof(cson_kvp),"cson_kvp");
if( kvp )
{
*kvp = cson_kvp_empty;
}
return kvp;
}
int cson_array_append( cson_array * ar, cson_value * v )
{
if( !ar || !v ) return CSON_RC_ArgError;
else if( (ar->list.count+1) < ar->list.count ) return CSON_RC_RangeError;
else
{
if( !ar->list.alloced || (ar->list.count == ar->list.alloced-1))
{
unsigned int const n = ar->list.count ? (ar->list.count*2) : 7;
if( n > cson_value_list_reserve( &ar->list, n ) )
{
return CSON_RC_AllocError;
}
}
return cson_array_set( ar, ar->list.count, v );
}
}
#if 0
|
| ︙ | ︙ | |||
2830 2831 2832 2833 2834 2835 2836 |
#endif
cson_value * cson_value_new_bool( char v )
{
return v ? &CSON_SPECIAL_VALUES[CSON_VAL_TRUE] : &CSON_SPECIAL_VALUES[CSON_VAL_FALSE];
}
| | | | | 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 |
#endif
cson_value * cson_value_new_bool( char v )
{
return v ? &CSON_SPECIAL_VALUES[CSON_VAL_TRUE] : &CSON_SPECIAL_VALUES[CSON_VAL_FALSE];
}
cson_value * cson_value_true(void)
{
return &CSON_SPECIAL_VALUES[CSON_VAL_TRUE];
}
cson_value * cson_value_false(void)
{
return &CSON_SPECIAL_VALUES[CSON_VAL_FALSE];
}
cson_value * cson_value_null(void)
{
return &CSON_SPECIAL_VALUES[CSON_VAL_NULL];
}
cson_value * cson_new_int( cson_int_t v )
{
return cson_value_new_integer(v);
|
| ︙ | ︙ | |||
2915 2916 2917 2918 2919 2920 2921 |
cson_value * cson_value_new_string( char const * str, unsigned int len )
{
return cson_string_value( cson_new_string(str, len) );
}
int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v )
{
| | | | | | | | | | 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 |
cson_value * cson_value_new_string( char const * str, unsigned int len )
{
return cson_string_value( cson_new_string(str, len) );
}
int cson_array_value_fetch( cson_array const * ar, unsigned int pos, cson_value ** v )
{
if( !ar) return CSON_RC_ArgError;
if( pos >= ar->list.count ) return CSON_RC_RangeError;
else
{
if(v) *v = ar->list.list[pos];
return 0;
}
}
cson_value * cson_array_get( cson_array const * ar, unsigned int pos )
{
cson_value *v = NULL;
cson_array_value_fetch(ar, pos, &v);
return v;
}
int cson_array_length_fetch( cson_array const * ar, unsigned int * v )
{
if( ! ar || !v ) return CSON_RC_ArgError;
else
{
if(v) *v = ar->list.count;
return 0;
}
}
unsigned int cson_array_length_get( cson_array const * ar )
{
unsigned int i = 0;
cson_array_length_fetch(ar, &i);
return i;
}
int cson_array_reserve( cson_array * ar, unsigned int size )
{
if( ! ar ) return CSON_RC_ArgError;
else if( size <= ar->list.alloced )
{
/* We don't want to introduce a can of worms by trying to
handle the cleanup from here.
*/
return 0;
}
else
{
return (ar->list.alloced > cson_value_list_reserve( &ar->list, size ))
? CSON_RC_AllocError
: 0
;
}
}
int cson_array_set( cson_array * ar, unsigned int ndx, cson_value * v )
{
if( !ar || !v ) return CSON_RC_ArgError;
else if( (ndx+1) < ndx) /* overflow */return CSON_RC_RangeError;
else
{
unsigned const int len = cson_value_list_reserve( &ar->list, ndx+1 );
if( len <= ndx ) return CSON_RC_AllocError;
else
{
cson_value * old = ar->list.list[ndx];
if( old )
{
if(old == v) return 0;
else cson_value_free(old);
|
| ︙ | ︙ | |||
3074 3075 3076 3077 3078 3079 3080 |
}
}
#endif
int cson_object_unset( cson_object * obj, char const * key )
{
| | | | 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 |
}
}
#endif
int cson_object_unset( cson_object * obj, char const * key )
{
if( ! obj || !key || !*key ) return CSON_RC_ArgError;
else
{
unsigned int ndx = 0;
cson_kvp * kvp = cson_object_search_impl( obj, key, &ndx );
if( ! kvp )
{
return CSON_RC_NotFoundError;
}
assert( obj->kvp.count > 0 );
assert( obj->kvp.list[ndx] == kvp );
cson_kvp_free( kvp );
obj->kvp.list[ndx] = NULL;
{ /* if my brain were bigger i'd use memmove(). */
unsigned int i = ndx;
|
| ︙ | ︙ | |||
3107 3108 3109 3110 3111 3112 3113 |
#endif
return 0;
}
}
int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v )
{
| | | | 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 |
#endif
return 0;
}
}
int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v )
{
if( !obj || !key ) return CSON_RC_ArgError;
else if( NULL == v ) return cson_object_unset( obj, cson_string_cstr(key) );
else
{
char const * cKey;
cson_value * vKey;
cson_kvp * kvp;
vKey = cson_string_value(key);
assert(vKey && (key==CSON_STR(vKey)));
if( vKey == CSON_VCAST(obj) ){
return CSON_RC_ArgError;
}
cKey = cson_string_cstr(key);
kvp = cson_object_search_impl( obj, cKey, NULL );
if( kvp )
{ /* "I told 'em we've already got one!" */
if( kvp->key != vKey ){
cson_value_free( kvp->key );
|
| ︙ | ︙ | |||
3140 3141 3142 3143 3144 3145 3146 |
return 0;
}
if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1))
{ /* reserve space */
unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6;
if( n > cson_kvp_list_reserve( &obj->kvp, n ) )
{
| | | | 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 |
return 0;
}
if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1))
{ /* reserve space */
unsigned int const n = obj->kvp.count ? (obj->kvp.count*2) : 6;
if( n > cson_kvp_list_reserve( &obj->kvp, n ) )
{
return CSON_RC_AllocError;
}
}
{ /* insert new item... */
int rc = 0;
kvp = cson_kvp_alloc();
if( ! kvp )
{
return CSON_RC_AllocError;
}
rc = cson_kvp_list_append( &obj->kvp, kvp );
if( 0 != rc )
{
cson_kvp_free(kvp);
}
else
|
| ︙ | ︙ | |||
3172 3173 3174 3175 3176 3177 3178 |
return rc;
}
}
}
int cson_object_set( cson_object * obj, char const * key, cson_value * v )
{
| | | | 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 |
return rc;
}
}
}
int cson_object_set( cson_object * obj, char const * key, cson_value * v )
{
if( ! obj || !key || !*key ) return CSON_RC_ArgError;
else if( NULL == v )
{
return cson_object_unset( obj, key );
}
else
{
cson_string * cs = cson_new_string(key,strlen(key));
if(!cs) return CSON_RC_AllocError;
else
{
int const rc = cson_object_set_s(obj, cs, v);
if(rc) cson_value_free(cson_string_value(cs));
return rc;
}
}
|
| ︙ | ︙ | |||
3235 3236 3237 3238 3239 3240 3241 |
#endif
return rc;
}
}
/** @internal
If p->node is-a Object then value is inserted into the object
| | | | 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 |
#endif
return rc;
}
}
/** @internal
If p->node is-a Object then value is inserted into the object
using p->key. In any other case CSON_RC_InternalError is returned.
Returns CSON_RC_AllocError if an allocation fails.
Returns 0 on success. On error, parsing must be ceased immediately.
Ownership of val is ALWAYS TRANSFERED to this function. If this
function fails, val will be cleaned up and destroyed. (This
simplifies error handling in the core parser.)
*/
|
| ︙ | ︙ | |||
3265 3266 3267 3268 3269 3270 3271 |
extra alloc/strcpy of the key data.
*/
if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1))
{
if( obj->kvp.alloced > cson_kvp_list_reserve( &obj->kvp, obj->kvp.count ? (obj->kvp.count*2) : 5 ) )
{
cson_value_free(val);
| | | | 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 |
extra alloc/strcpy of the key data.
*/
if( !obj->kvp.alloced || (obj->kvp.count == obj->kvp.alloced-1))
{
if( obj->kvp.alloced > cson_kvp_list_reserve( &obj->kvp, obj->kvp.count ? (obj->kvp.count*2) : 5 ) )
{
cson_value_free(val);
return CSON_RC_AllocError;
}
}
kvp = cson_kvp_alloc();
if( ! kvp )
{
cson_value_free(val);
return CSON_RC_AllocError;
}
kvp->key = cson_string_value(p->ckey)/*transfer ownership*/;
assert(0 == kvp->key->refcount);
cson_refcount_incr(kvp->key);
p->ckey = NULL;
kvp->value = val;
cson_refcount_incr( val );
|
| ︙ | ︙ | |||
3294 3295 3296 3297 3298 3299 3300 |
++p->totalValueCount;
}
return rc;
}
else
{
if(val) cson_value_free(val);
| | | 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 |
++p->totalValueCount;
}
return rc;
}
else
{
if(val) cson_value_free(val);
return p->errNo = CSON_RC_InternalError;
}
}
/** @internal
Pushes val into the current object/array parent node, depending on the
|
| ︙ | ︙ | |||
3335 3336 3337 3338 3339 3340 3341 |
++p->totalValueCount;
}
return rc;
}
else
{ /* WTF? */
assert( 0 && "Internal error in cson_parser code" );
| | | | | 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 |
++p->totalValueCount;
}
return rc;
}
else
{ /* WTF? */
assert( 0 && "Internal error in cson_parser code" );
return p->errNo = CSON_RC_InternalError;
}
}
/**
Callback for JSON_parser API. Reminder: it returns 0 (meaning false)
on error!
*/
static int cson_parse_callback( void * cx, int type, JSON_value const * value )
{
cson_parser * p = (cson_parser *)cx;
int rc = 0;
#define ALLOC_V(T,V) cson_value * v = cson_value_new_##T(V); if( ! v ) { rc = CSON_RC_AllocError; break; }
switch(type) {
case JSON_T_ARRAY_BEGIN:
case JSON_T_OBJECT_BEGIN: {
cson_value * obja = (JSON_T_ARRAY_BEGIN == type)
? cson_value_new_array()
: cson_value_new_object();
if( ! obja )
{
p->errNo = CSON_RC_AllocError;
break;
}
if( 0 != rc ) break;
if( ! p->root )
{
p->root = p->node = obja;
rc = cson_array_append( &p->stack, obja );
|
| ︙ | ︙ | |||
3393 3394 3395 3396 3397 3398 3399 |
}
break;
}
case JSON_T_ARRAY_END:
case JSON_T_OBJECT_END: {
if( 0 == p->stack.list.count )
{
| | | 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 |
}
break;
}
case JSON_T_ARRAY_END:
case JSON_T_OBJECT_END: {
if( 0 == p->stack.list.count )
{
rc = CSON_RC_RangeError;
break;
}
#if CSON_OBJECT_PROPS_SORT
if( cson_value_is_object(p->node) )
{/* kludge: the parser uses custom cson_object property
insertion as a malloc/strcpy-reduction optimization.
Because of that, we have to sort the property list
|
| ︙ | ︙ | |||
3479 3480 3481 3482 3483 3484 3485 |
break;
}
case JSON_T_KEY: {
assert(!p->ckey);
p->ckey = cson_new_string( value->vu.str.value, value->vu.str.length );
if( ! p->ckey )
{
| | | | | | | | | | | | | | | | | 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 |
break;
}
case JSON_T_KEY: {
assert(!p->ckey);
p->ckey = cson_new_string( value->vu.str.value, value->vu.str.length );
if( ! p->ckey )
{
rc = CSON_RC_AllocError;
break;
}
++p->totalKeyCount;
break;
}
case JSON_T_STRING: {
cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length );
rc = ( NULL == v )
? CSON_RC_AllocError
: cson_parser_push_value( p, v );
break;
}
default:
assert(0);
rc = CSON_RC_InternalError;
break;
}
#undef ALLOC_V
return ((p->errNo = rc)) ? 0 : 1;
}
/**
Converts a JSON_error code to one of the cson_rc values.
*/
static int cson_json_err_to_rc( JSON_error jrc )
{
switch(jrc)
{
case JSON_E_NONE: return 0;
case JSON_E_INVALID_CHAR: return CSON_RC_Parse_INVALID_CHAR;
case JSON_E_INVALID_KEYWORD: return CSON_RC_Parse_INVALID_KEYWORD;
case JSON_E_INVALID_ESCAPE_SEQUENCE: return CSON_RC_Parse_INVALID_ESCAPE_SEQUENCE;
case JSON_E_INVALID_UNICODE_SEQUENCE: return CSON_RC_Parse_INVALID_UNICODE_SEQUENCE;
case JSON_E_INVALID_NUMBER: return CSON_RC_Parse_INVALID_NUMBER;
case JSON_E_NESTING_DEPTH_REACHED: return CSON_RC_Parse_NESTING_DEPTH_REACHED;
case JSON_E_UNBALANCED_COLLECTION: return CSON_RC_Parse_UNBALANCED_COLLECTION;
case JSON_E_EXPECTED_KEY: return CSON_RC_Parse_EXPECTED_KEY;
case JSON_E_EXPECTED_COLON: return CSON_RC_Parse_EXPECTED_COLON;
case JSON_E_OUT_OF_MEMORY: return CSON_RC_AllocError;
default:
return CSON_RC_InternalError;
}
}
/** @internal
Cleans up all contents of p but does not free p.
To properly take over ownership of the parser's root node on a
successful parse:
- Copy p->root's pointer and set p->root to NULL.
- Eventually free up p->root with cson_value_free().
If you do not set p->root to NULL, p->root will be freed along with
any other items inserted into it (or under it) during the parsing
process.
*/
static int cson_parser_clean( cson_parser * p )
{
if( ! p ) return CSON_RC_ArgError;
else
{
if( p->p )
{
delete_JSON_parser(p->p);
p->p = NULL;
}
|
| ︙ | ︙ | |||
3572 3573 3574 3575 3576 3577 3578 |
{
unsigned char ch[2] = {0,0};
cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty;
int rc = 0;
unsigned int len = 1;
cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
cson_parser p = cson_parser_empty;
| | | | | 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 |
{
unsigned char ch[2] = {0,0};
cson_parse_opt const opt = opt_ ? *opt_ : cson_parse_opt_empty;
int rc = 0;
unsigned int len = 1;
cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
cson_parser p = cson_parser_empty;
if( ! tgt || ! src ) return CSON_RC_ArgError;
{
JSON_config jopt = {0};
init_JSON_config( &jopt );
jopt.allow_comments = opt.allowComments;
jopt.depth = opt.maxDepth;
jopt.callback_ctx = &p;
jopt.handle_floats_manually = 0;
jopt.callback = cson_parse_callback;
p.p = new_JSON_parser(&jopt);
if( ! p.p )
{
return CSON_RC_AllocError;
}
}
do
{ /* FIXME: buffer the input in multi-kb chunks. */
len = 1;
ch[0] = 0;
rc = src( state, ch, &len );
if( 0 != rc ) break;
else if( !len /* EOF */ ) break;
++info.length;
if('\n' == ch[0])
{
++info.line;
info.col = 0;
}
if( ! JSON_parser_char(p.p, ch[0]) )
{
rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) );
if(0==rc) rc = p.errNo;
if(0==rc) rc = CSON_RC_InternalError;
info.errorCode = rc;
break;
}
if( '\n' != ch[0]) ++info.col;
} while(1);
if( info_ )
{
|
| ︙ | ︙ | |||
3628 3629 3630 3631 3632 3633 3634 |
return rc;
}
if( ! JSON_parser_done(p.p) )
{
rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) );
cson_parser_clean(&p);
if(0==rc) rc = p.errNo;
| | | 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 |
return rc;
}
if( ! JSON_parser_done(p.p) )
{
rc = cson_json_err_to_rc( JSON_parser_get_last_error(p.p) );
cson_parser_clean(&p);
if(0==rc) rc = p.errNo;
if(0==rc) rc = CSON_RC_InternalError;
}
else
{
cson_value * root = p.root;
p.root = NULL;
cson_parser_clean(&p);
if( root )
|
| ︙ | ︙ | |||
3650 3651 3652 3653 3654 3655 3656 |
root node to keep it from being cleaned up
prematurely.
*/;
*tgt = root;
}
else
{ /* then can happen on empty input. */
| | | 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 |
root node to keep it from being cleaned up
prematurely.
*/;
*tgt = root;
}
else
{ /* then can happen on empty input. */
rc = CSON_RC_UnknownError;
}
}
return rc;
}
/**
The UTF code was originally taken from sqlite3's public-domain
|
| ︙ | ︙ | |||
3769 3770 3771 3772 3773 3774 3775 |
here we would effectively change the data type from string to
object.
*/
static int cson_str_to_json( char const * str, unsigned int len,
char escapeFwdSlash,
cson_data_dest_f f, void * state )
{
| | | 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 |
here we would effectively change the data type from string to
object.
*/
static int cson_str_to_json( char const * str, unsigned int len,
char escapeFwdSlash,
cson_data_dest_f f, void * state )
{
if( NULL == f ) return CSON_RC_ArgError;
else if( !str || !*str || (0 == len) )
{ /* special case for 0-length strings. */
return f( state, "\"\"", 2 );
}
else
{
unsigned char const * pos = (unsigned char const *)str;
|
| ︙ | ︙ | |||
3875 3876 3877 3878 3879 3880 3881 |
else
{ /* UTF: transform it to \uXXXX */
#if defined(CSON_FOSSIL_MODE)
assume_latin1:
#endif
memset(ubuf,0,UBLen);
if(ch <= 0xFFFF){
| | | | | | | | | 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 |
else
{ /* UTF: transform it to \uXXXX */
#if defined(CSON_FOSSIL_MODE)
assume_latin1:
#endif
memset(ubuf,0,UBLen);
if(ch <= 0xFFFF){
rc = snprintf(ubuf, (size_t)UBLen, "\\u%04x",ch);
if( rc != 6 )
{
rc = CSON_RC_RangeError;
break;
}
rc = f( state, ubuf, 6 );
}else{ /* encode as a UTF16 surrogate pair */
/* http://unicodebook.readthedocs.org/en/latest/unicode_encodings.html#surrogates */
ch -= 0x10000;
rc = snprintf(ubuf, (size_t)UBLen, "\\u%04x\\u%04x",
(0xd800 | (ch>>10)),
(0xdc00 | (ch & 0x3ff)));
if( rc != 12 )
{
rc = CSON_RC_RangeError;
break;
}
rc = f( state, ubuf, 12 );
}
continue;
}
}
if( 0 == rc )
{
rc = f(state, "\"", 1 );
}
return rc;
}
}
int cson_object_iter_init( cson_object const * obj, cson_object_iterator * iter )
{
if( ! obj || !iter ) return CSON_RC_ArgError;
else
{
iter->obj = obj;
iter->pos = 0;
return 0;
}
}
|
| ︙ | ︙ | |||
3934 3935 3936 3937 3938 3939 3940 |
}
return rc;
}
}
static int cson_output_null( cson_data_dest_f f, void * state )
{
| | | | | > | < < | | | > | < < | | 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 |
}
return rc;
}
}
static int cson_output_null( cson_data_dest_f f, void * state )
{
if( !f ) return CSON_RC_ArgError;
else
{
return f(state, "null", 4);
}
}
static int cson_output_bool( cson_value const * src, cson_data_dest_f f, void * state )
{
if( !f ) return CSON_RC_ArgError;
else
{
char const v = cson_value_get_bool(src);
return f(state, v ? "true" : "false", v ? 4 : 5);
}
}
static int cson_output_integer( cson_value const * src, cson_data_dest_f f, void * state )
{
if( !f ) return CSON_RC_ArgError;
else if( !cson_value_is_integer(src) ) return CSON_RC_TypeError;
else
{
enum { BufLen = 100 };
char b[BufLen];
int rc;
memset( b, 0, BufLen );
rc = snprintf( b, (size_t)BufLen, "%"CSON_INT_T_PFMT,
cson_value_get_integer(src) );
return ( rc<=0 )
? CSON_RC_RangeError
: f( state, b, (unsigned int)rc )
;
}
}
static int cson_output_double( cson_value const * src, cson_data_dest_f f, void * state )
{
if( !f ) return CSON_RC_ArgError;
else if( !cson_value_is_double(src) ) return CSON_RC_TypeError;
else
{
enum { BufLen = 128 /* this must be relatively large or huge
doubles can cause us to overrun here,
resulting in stack-smashing errors.
*/};
char b[BufLen];
int rc;
memset( b, 0, BufLen );
rc = snprintf( b, (size_t)BufLen, "%"CSON_DOUBLE_T_PFMT,
cson_value_get_double(src) );
if( rc<=0 ) return CSON_RC_RangeError;
else if(1)
{ /* Strip trailing zeroes before passing it on... */
unsigned int urc = (unsigned int)rc;
char * pos = b + urc - 1;
for( ; ('0' == *pos) && urc && (*(pos-1) != '.'); --pos, --urc )
{
*pos = 0;
|
| ︙ | ︙ | |||
4010 4011 4012 4013 4014 4015 4016 |
}
return 0;
}
}
static int cson_output_string( cson_value const * src, char escapeFwdSlash, cson_data_dest_f f, void * state )
{
| | | | 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 |
}
return 0;
}
}
static int cson_output_string( cson_value const * src, char escapeFwdSlash, cson_data_dest_f f, void * state )
{
if( !f ) return CSON_RC_ArgError;
else if( ! cson_value_is_string(src) ) return CSON_RC_TypeError;
else
{
cson_string const * str = cson_value_get_string(src);
assert( NULL != str );
return cson_str_to_json(cson_string_cstr(str), str->length, escapeFwdSlash, f, state);
}
}
|
| ︙ | ︙ | |||
4073 4074 4075 4076 4077 4078 4079 |
on src->api->typeID.
Returns 0 on success.
*/
static int cson_output_impl( cson_value const * src, cson_data_dest_f f, void * state,
cson_output_opt const * fmt, unsigned int level )
{
| | | 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 |
on src->api->typeID.
Returns 0 on success.
*/
static int cson_output_impl( cson_value const * src, cson_data_dest_f f, void * state,
cson_output_opt const * fmt, unsigned int level )
{
if( ! src || !f || !src->api ) return CSON_RC_ArgError;
else
{
int rc = 0;
assert(fmt);
switch( src->api->typeID )
{
case CSON_TYPE_UNDEF:
|
| ︙ | ︙ | |||
4103 4104 4105 4106 4107 4108 4109 |
case CSON_TYPE_ARRAY:
rc = cson_output_array( src, f, state, fmt, level );
break;
case CSON_TYPE_OBJECT:
rc = cson_output_object( src, f, state, fmt, level );
break;
default:
| | | | | | 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 |
case CSON_TYPE_ARRAY:
rc = cson_output_array( src, f, state, fmt, level );
break;
case CSON_TYPE_OBJECT:
rc = cson_output_object( src, f, state, fmt, level );
break;
default:
rc = CSON_RC_TypeError;
break;
}
return rc;
}
}
static int cson_output_array( cson_value const * src, cson_data_dest_f f, void * state,
cson_output_opt const * fmt, unsigned int level )
{
if( !src || !f || !fmt ) return CSON_RC_ArgError;
else if( ! cson_value_is_array(src) ) return CSON_RC_TypeError;
else if( level > fmt->maxDepth ) return CSON_RC_RangeError;
else
{
int rc;
unsigned int i;
cson_value const * v;
char doIndent = fmt->indentation ? 1 : 0;
cson_array const * ar = cson_value_get_array(src);
|
| ︙ | ︙ | |||
4175 4176 4177 4178 4179 4180 4181 |
: rc;
}
}
static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state,
cson_output_opt const * fmt, unsigned int level )
{
| | | | | 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 |
: rc;
}
}
static int cson_output_object( cson_value const * src, cson_data_dest_f f, void * state,
cson_output_opt const * fmt, unsigned int level )
{
if( !src || !f || !fmt ) return CSON_RC_ArgError;
else if( ! cson_value_is_object(src) ) return CSON_RC_TypeError;
else if( level > fmt->maxDepth ) return CSON_RC_RangeError;
else
{
int rc;
unsigned int i;
cson_kvp const * kvp;
char doIndent = fmt->indentation ? 1 : 0;
cson_object const * obj = cson_value_get_object(src);
|
| ︙ | ︙ | |||
4265 4266 4267 4268 4269 4270 4271 |
rc = f(state, "\n", 1);
}
return rc;
}
int cson_data_dest_FILE( void * state, void const * src, unsigned int n )
{
| | | | 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 |
rc = f(state, "\n", 1);
}
return rc;
}
int cson_data_dest_FILE( void * state, void const * src, unsigned int n )
{
if( ! state ) return CSON_RC_ArgError;
else if( !src || !n ) return 0;
else
{
return ( 1 == fwrite( src, n, 1, (FILE*) state ) )
? 0
: CSON_RC_IOError;
}
}
int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * fmt )
{
int rc = 0;
if( fmt )
|
| ︙ | ︙ | |||
4298 4299 4300 4301 4302 4303 4304 |
fflush( dest );
}
return rc;
}
int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt )
{
| | | | | | 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 |
fflush( dest );
}
return rc;
}
int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt )
{
if( !src || !dest ) return CSON_RC_ArgError;
else
{
FILE * f = fopen(dest,"wb");
if( !f ) return CSON_RC_IOError;
else
{
int const rc = cson_output_FILE( src, f, fmt );
fclose(f);
return rc;
}
}
}
int cson_parse_filename( cson_value ** tgt, char const * src,
cson_parse_opt const * opt, cson_parse_info * err )
{
if( !src || !tgt ) return CSON_RC_ArgError;
else
{
FILE * f = fopen(src, "r");
if( !f ) return CSON_RC_IOError;
else
{
int const rc = cson_parse_FILE( tgt, f, opt, err );
fclose(f);
return rc;
}
}
|
| ︙ | ︙ | |||
4347 4348 4349 4350 4351 4352 4353 |
/**
A cson_data_source_f() implementation which requires the state argument
to be a properly populated (cson_data_source_StringSource_t*).
*/
static int cson_data_source_StringSource( void * state, void * dest, unsigned int * n )
{
| | | | | | | | 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 |
/**
A cson_data_source_f() implementation which requires the state argument
to be a properly populated (cson_data_source_StringSource_t*).
*/
static int cson_data_source_StringSource( void * state, void * dest, unsigned int * n )
{
if( !state || !n || !dest ) return CSON_RC_ArgError;
else if( !*n ) return 0 /* ignore this */;
else
{
unsigned int i;
cson_data_source_StringSource_t * ss = (cson_data_source_StringSource_t*) state;
unsigned char * tgt = (unsigned char *)dest;
for( i = 0; (i < *n) && (ss->pos < ss->end); ++i, ++ss->pos, ++tgt )
{
*tgt = *ss->pos;
}
*n = i;
return 0;
}
}
int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len,
cson_parse_opt const * opt, cson_parse_info * err )
{
if( ! tgt || !src ) return CSON_RC_ArgError;
else if( !*src || (len<2/*2==len of {} and []*/) ) return CSON_RC_RangeError;
else
{
cson_data_source_StringSource_t ss;
ss.str = ss.pos = src;
ss.end = src + len;
return cson_parse( tgt, cson_data_source_StringSource, &ss, opt, err );
}
}
int cson_parse_buffer( cson_value ** tgt,
cson_buffer const * buf,
cson_parse_opt const * opt,
cson_parse_info * err )
{
return ( !tgt || !buf || !buf->mem || !buf->used )
? CSON_RC_ArgError
: cson_parse_string( tgt, (char const *)buf->mem,
buf->used, opt, err );
}
int cson_buffer_reserve( cson_buffer * buf, cson_size_t n )
{
if( ! buf ) return CSON_RC_ArgError;
else if( 0 == n )
{
cson_free(buf->mem, "cson_buffer::mem");
*buf = cson_buffer_empty;
return 0;
}
else if( buf->capacity >= n )
{
return 0;
}
else
{
unsigned char * x = (unsigned char *)cson_realloc( buf->mem, n, "cson_buffer::mem" );
if( ! x ) return CSON_RC_AllocError;
memset( x + buf->used, 0, n - buf->used );
buf->mem = x;
buf->capacity = n;
++buf->timesExpanded;
return 0;
}
}
|
| ︙ | ︙ | |||
4432 4433 4434 4435 4436 4437 4438 |
cson_data_dest_f() implementation, used by cson_output_buffer().
arg MUST be a (cson_buffer*). This function appends n bytes at
position arg->used, expanding the buffer as necessary.
*/
static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n )
{
| | | | | 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 |
cson_data_dest_f() implementation, used by cson_output_buffer().
arg MUST be a (cson_buffer*). This function appends n bytes at
position arg->used, expanding the buffer as necessary.
*/
static int cson_data_dest_cson_buffer( void * arg, void const * data_, unsigned int n )
{
if( !arg ) return CSON_RC_ArgError;
else if( ! n ) return 0;
else
{
cson_buffer * sb = (cson_buffer*)arg;
char const * data = (char const *)data_;
cson_size_t npos = sb->used + n;
unsigned int i;
if( npos >= sb->capacity )
{
const cson_size_t oldCap = sb->capacity;
const cson_size_t asz = npos * 2;
if( asz < npos ) return CSON_RC_ArgError; /* overflow */
else if( 0 != cson_buffer_reserve( sb, asz ) ) return CSON_RC_AllocError;
assert( (sb->capacity > oldCap) && "Internal error in memory buffer management!" );
/* make sure it gets NUL terminated. */
memset( sb->mem + oldCap, 0, (sb->capacity - oldCap) );
}
for( i = 0; i < n; ++i, ++sb->used )
{
sb->mem[sb->used] = data[i];
|
| ︙ | ︙ | |||
4531 4532 4533 4534 4535 4536 4537 |
for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ }
*end = pos;
return (pos > *inp) ? 1 : 0;
}
int cson_object_fetch_sub2( cson_object const * obj, cson_value ** tgt, char const * path )
{
| | | | | | 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 |
for( ; *pos && (*pos != separator); ++pos) { /* find next splitter */ }
*end = pos;
return (pos > *inp) ? 1 : 0;
}
int cson_object_fetch_sub2( cson_object const * obj, cson_value ** tgt, char const * path )
{
if( ! obj || !path ) return CSON_RC_ArgError;
else if( !*path || !*(1+path) ) return CSON_RC_RangeError;
else return cson_object_fetch_sub(obj, tgt, path+1, *path);
}
int cson_object_fetch_sub( cson_object const * obj, cson_value ** tgt, char const * path, char sep )
{
if( ! obj || !path ) return CSON_RC_ArgError;
else if( !*path || !sep ) return CSON_RC_RangeError;
else
{
char const * beg = path;
char const * end = NULL;
int rc;
unsigned int i, len;
unsigned int tokenCount = 0;
|
| ︙ | ︙ | |||
4563 4564 4565 4566 4567 4568 4569 |
else
{
++tokenCount;
beg = end;
end = NULL;
}
}
| | | | | | | 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 |
else
{
++tokenCount;
beg = end;
end = NULL;
}
}
if( 0 == tokenCount ) return CSON_RC_RangeError;
beg = path;
end = NULL;
for( i = 0; i < tokenCount; ++i, beg=end, end=NULL )
{
rc = cson_next_token( &beg, sep, &end );
assert( 1 == rc );
assert( beg != end );
assert( end > beg );
len = end - beg;
if( len > (BufSize-1) ) return CSON_RC_RangeError;
memset( buf, 0, len + 1 );
memcpy( buf, beg, len );
buf[len] = 0;
cv = cson_object_get( curObj, buf );
if( NULL == cv ) return CSON_RC_NotFoundError;
else if( i == (tokenCount-1) )
{
if(tgt) *tgt = cv;
return 0;
}
else if( cson_value_is_object(cv) )
{
curObj = cson_value_get_object(cv);
assert((NULL != curObj) && "Detected mis-management of internal memory!");
}
/* TODO: arrays. Requires numeric parsing for the index. */
else
{
return CSON_RC_NotFoundError;
}
}
assert( i == tokenCount );
return CSON_RC_NotFoundError;
}
}
cson_value * cson_object_get_sub( cson_object const * obj, char const * path, char sep )
{
cson_value * v = NULL;
cson_object_fetch_sub( obj, &v, path, sep );
|
| ︙ | ︙ | |||
4836 4837 4838 4839 4840 4841 4842 |
v = cson_strdup( "null", 4 );
break;
}
case CSON_TYPE_STRING: {
cson_string const * jstr = cson_value_get_string(orig);
unsigned const int slen = cson_string_length_bytes( jstr );
assert( NULL != jstr );
| | > | > | | 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 |
v = cson_strdup( "null", 4 );
break;
}
case CSON_TYPE_STRING: {
cson_string const * jstr = cson_value_get_string(orig);
unsigned const int slen = cson_string_length_bytes( jstr );
assert( NULL != jstr );
v = cson_strdup( cson_string_cstr( jstr ), slen );
break;
}
case CSON_TYPE_INTEGER: {
char buf[BufSize] = {0};
if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_INT_T_PFMT,
cson_value_get_integer(orig)) )
{
v = cson_strdup( buf, strlen(buf) );
}
break;
}
case CSON_TYPE_DOUBLE: {
char buf[BufSize] = {0};
if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_DOUBLE_T_PFMT,
cson_value_get_double(orig)) )
{
v = cson_strdup( buf, strlen(buf) );
}
break;
}
default:
break;
|
| ︙ | ︙ | |||
4889 4890 4891 4892 4893 4894 4895 |
v = cson_strdup( "null", 4 );
break;
}
case CSON_TYPE_STRING: {
cson_string const * jstr = cson_value_get_string(orig);
unsigned const int slen = cson_string_length_bytes( jstr );
assert( NULL != jstr );
| | > | > | | 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 |
v = cson_strdup( "null", 4 );
break;
}
case CSON_TYPE_STRING: {
cson_string const * jstr = cson_value_get_string(orig);
unsigned const int slen = cson_string_length_bytes( jstr );
assert( NULL != jstr );
v = cson_strdup( cson_string_cstr( jstr ), slen );
break;
}
case CSON_TYPE_INTEGER: {
char buf[BufSize] = {0};
if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_INT_T_PFMT,
cson_value_get_integer(orig)) )
{
v = cson_strdup( buf, strlen(buf) );
}
break;
}
case CSON_TYPE_DOUBLE: {
char buf[BufSize] = {0};
if( 0 < snprintf( v, (size_t)BufSize, "%"CSON_DOUBLE_T_PFMT,
cson_value_get_double(orig)) )
{
v = cson_strdup( buf, strlen(buf) );
}
break;
}
default:
break;
|
| ︙ | ︙ | |||
4986 4987 4988 4989 4990 4991 4992 |
int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){
cson_object_iterator iter = cson_object_iterator_empty;
int rc;
char const replace = (flags & CSON_MERGE_REPLACE);
char const recurse = !(flags & CSON_MERGE_NO_RECURSE);
cson_kvp const * kvp;
| | | 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 |
int cson_object_merge( cson_object * dest, cson_object const * src, int flags ){
cson_object_iterator iter = cson_object_iterator_empty;
int rc;
char const replace = (flags & CSON_MERGE_REPLACE);
char const recurse = !(flags & CSON_MERGE_NO_RECURSE);
cson_kvp const * kvp;
if((!dest || !src) || (dest==src)) return CSON_RC_ArgError;
rc = cson_object_iter_init( src, &iter );
if(rc) return rc;
while( (kvp = cson_object_iter_next(&iter) ) )
{
cson_string * key = cson_kvp_key(kvp);
cson_value * val = cson_kvp_value(kvp);
cson_value * check = cson_object_get_s( dest, key );
|
| ︙ | ︙ | |||
5050 5051 5052 5053 5054 5055 5056 |
int cson_parse_argv_flags( int argc, char const * const * argv,
cson_object ** tgt, unsigned int * count ){
cson_object * o = NULL;
int rc = 0;
int i = 0;
| | | | 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 |
int cson_parse_argv_flags( int argc, char const * const * argv,
cson_object ** tgt, unsigned int * count ){
cson_object * o = NULL;
int rc = 0;
int i = 0;
if(argc<1 || !argc || !tgt) return CSON_RC_ArgError;
o = *tgt ? *tgt : cson_new_object();
if(count) *count = 0;
for( i = 0; i < argc; ++i ){
char const * arg = argv[i];
char const * key = arg;
char const * pos;
cson_string * k = NULL;
cson_value * v = NULL;
if('-' != *arg) continue;
while('-'==*key) ++key;
if(!*key) continue;
pos = key;
while( *pos && ('=' != *pos)) ++pos;
k = cson_new_string(key, pos-key);
if(!k){
rc = CSON_RC_AllocError;
break;
}
if(!*pos){ /** --key */
v = cson_value_true();
}else{ /** --key=...*/
assert('=' == *pos);
++pos /*skip '='*/;
|
| ︙ | ︙ | |||
5387 5388 5389 5390 5391 5392 5393 |
cson_value * rootV = NULL;
cson_object * root = NULL;
cson_string * colName = NULL;
int i = 0;
int rc = 0;
cson_value * currentValue = NULL;
int const colCount = sqlite3_column_count(st);
| | | 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 |
cson_value * rootV = NULL;
cson_object * root = NULL;
cson_string * colName = NULL;
int i = 0;
int rc = 0;
cson_value * currentValue = NULL;
int const colCount = sqlite3_column_count(st);
if( !colCount || (colCount>cson_array_length_get(colNames)) ) {
return NULL;
}
rootV = cson_value_new_object();
if(!rootV) return NULL;
root = cson_value_get_object(rootV);
for( i = 0; i < colCount; ++i )
{
|
| ︙ | ︙ |
Changes to extsrc/cson_amalgamation.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #ifdef FOSSIL_ENABLE_JSON #ifndef CSON_FOSSIL_MODE #define CSON_FOSSIL_MODE #endif /* auto-generated! Do not edit! */ /* begin file include/wh/cson/cson.h */ #if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED) #define WANDERINGHORSE_NET_CSON_H_INCLUDED 1 /*#include <stdint.h> C99: fixed-size int types. */ #include <stdio.h> /* FILE decl */ /** @page page_cson cson JSON API cson (pronounced "season") is an object-oriented C API for generating and consuming JSON (http://www.json.org) data. Its main claim to fame is that it can parse JSON from, and output it to, damned near anywhere. The i/o routines use a callback function to fetch/emit JSON data, allowing clients to easily plug in their own implementations. Implementations are provided for string- and FILE-based i/o. | > > | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#ifdef FOSSIL_ENABLE_JSON
#ifndef CSON_FOSSIL_MODE
#define CSON_FOSSIL_MODE
#endif
/* auto-generated! Do not edit! */
/* begin file include/wh/cson/cson.h */
#if !defined(WANDERINGHORSE_NET_CSON_H_INCLUDED)
#define WANDERINGHORSE_NET_CSON_H_INCLUDED 1
/*#include <stdint.h> C99: fixed-size int types. */
#include <stdio.h> /* FILE decl */
#include <stdarg.h>
/** @page page_cson cson JSON API
cson (pronounced "season") is an object-oriented C API for generating
and consuming JSON (http://www.json.org) data.
Its main claim to fame is that it can parse JSON from, and output it
to, damned near anywhere. The i/o routines use a callback function to
fetch/emit JSON data, allowing clients to easily plug in their own
implementations. Implementations are provided for string- and
FILE-based i/o.
Project home page: https://fossil.wanderinghorse.net/r/cson
Author: Stephan Beal (https://www.wanderinghorse.net/home/stephan/)
License: Dual Public Domain/MIT
The full license text is at the bottom of the main header file
(cson.h).
Examples of how to use the library are scattered throughout
the API documentation, in the test.c file in the source repo,
and in the wiki on the project's home page.
*/
#if defined(__cplusplus)
extern "C" {
#endif
#if defined(_WIN32) || defined(_WIN64)
|
| ︙ | ︙ | |||
294 295 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 |
@see cson_value_null()
@see cson_value_free()
@see cson_value_type_id()
*/
/** @var cson_rc
This object defines the error codes used by cson.
Library routines which return int values almost always return a
value from this structure. None of the members in this struct have
published values except for the OK member, which has the value 0.
All other values might be incidentally defined where clients
can see them, but the numbers might change from release to
release, so clients should only use the symbolic names.
Client code is expected to access these values via the shared
cson_rc object, and use them as demonstrated here:
@code
int rc = cson_some_func(...);
if( 0 == rc ) {...success...}
else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... }
else if( cson_rc.AllocError == rc ) { ... allocation error ... }
...
@endcode
The entries named Parse_XXX are generally only returned by
cson_parse() and friends.
*/
/** @struct cson_rc_
See \ref cson_rc for details.
*/
static const struct cson_rc_
{
/** The generic success value. Guaranteed to be 0. */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 294 295 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 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 |
@see cson_value_null()
@see cson_value_free()
@see cson_value_type_id()
*/
/** @var cson_rc
Deprecated: clients are encouraged to use the CSON_RC_xxx values
which correspond to cson_rc.xxx, as those are more efficient. Some
docs and code may still refer to cson_rc, though.
This object defines the error codes used by cson.
Library routines which return int values almost always return a
value from this structure. None of the members in this struct have
published values except for the OK member, which has the value 0.
All other values might be incidentally defined where clients
can see them, but the numbers might change from release to
release, so clients should only use the symbolic names.
Client code is expected to access these values via the shared
cson_rc object, and use them as demonstrated here:
@code
int rc = cson_some_func(...);
if( 0 == rc ) {...success...}
else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... }
else if( cson_rc.AllocError == rc ) { ... allocation error ... }
...
@endcode
Or with the preferred/newer method:
@code
int rc = cson_some_func(...);
switch(rc){
case 0: ...success...;
case CSON_RC_ArgError: ... some argument was wrong ...
case CSON_RC_AllocError: ... allocation error ...
...
}
@endcode
The entries named Parse_XXX are generally only returned by
cson_parse() and friends.
@deprecated
*/
/**
The CSON_RC_xxx values are intended to replace the older
cson_rc.xxx values.
*/
enum cson_rc_values {
/** The generic success value. Guaranteed to be 0. */
CSON_RC_OK = 0,
/** Signifies an error in one or more arguments (e.g. NULL where it is not allowed). */
CSON_RC_ArgError,
/** Signifies that some argument is not in a valid range. */
CSON_RC_RangeError,
/** Signifies that some argument is not of the correct logical cson type. */
CSON_RC_TypeError,
/** Signifies an input/ouput error. */
CSON_RC_IOError,
/** Signifies an out-of-memory error. */
CSON_RC_AllocError,
/** Signifies that the called code is "NYI" (Not Yet Implemented). */
CSON_RC_NYIError,
/** Signifies that an internal error was triggered. If it happens, please report this as a bug! */
CSON_RC_InternalError,
/** Signifies that the called operation is not supported in the
current environment. e.g. missing support from 3rd-party or
platform-specific code.
*/
CSON_RC_UnsupportedError,
/**
Signifies that the request resource could not be found.
*/
CSON_RC_NotFoundError,
/**
Signifies an unknown error, possibly because an underlying
3rd-party API produced an error and we have no other reasonable
error code to convert it to.
*/
CSON_RC_UnknownError,
/**
Signifies that the parser found an unexpected character.
*/
CSON_RC_Parse_INVALID_CHAR,
/**
Signifies that the parser found an invalid keyword (possibly
an unquoted string).
*/
CSON_RC_Parse_INVALID_KEYWORD,
/**
Signifies that the parser found an invalid escape sequence.
*/
CSON_RC_Parse_INVALID_ESCAPE_SEQUENCE,
/**
Signifies that the parser found an invalid Unicode character
sequence.
*/
CSON_RC_Parse_INVALID_UNICODE_SEQUENCE,
/**
Signifies that the parser found an invalid numeric token.
*/
CSON_RC_Parse_INVALID_NUMBER,
/**
Signifies that the parser reached its maximum defined
parsing depth before finishing the input.
*/
CSON_RC_Parse_NESTING_DEPTH_REACHED,
/**
Signifies that the parser found an unclosed object or array.
*/
CSON_RC_Parse_UNBALANCED_COLLECTION,
/**
Signifies that the parser found an key in an unexpected place.
*/
CSON_RC_Parse_EXPECTED_KEY,
/**
Signifies that the parser expected to find a colon but
found none (e.g. between keys and values in an object).
*/
CSON_RC_Parse_EXPECTED_COLON
};
/** @struct cson_rc_
See \ref cson_rc for details.
*/
static const struct cson_rc_
{
/** The generic success value. Guaranteed to be 0. */
|
| ︙ | ︙ | |||
395 396 397 398 399 400 401 |
const int Parse_EXPECTED_KEY;
/**
Signifies that the parser expected to find a colon but
found none (e.g. between keys and values in an object).
*/
const int Parse_EXPECTED_COLON;
} cson_rc = {
| | | | | | | | | | | | | | | | | | | | | | 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 |
const int Parse_EXPECTED_KEY;
/**
Signifies that the parser expected to find a colon but
found none (e.g. between keys and values in an object).
*/
const int Parse_EXPECTED_COLON;
} cson_rc = {
CSON_RC_OK,
CSON_RC_ArgError,
CSON_RC_RangeError,
CSON_RC_TypeError,
CSON_RC_IOError,
CSON_RC_AllocError,
CSON_RC_NYIError,
CSON_RC_InternalError,
CSON_RC_UnsupportedError,
CSON_RC_NotFoundError,
CSON_RC_UnknownError,
CSON_RC_Parse_INVALID_CHAR,
CSON_RC_Parse_INVALID_KEYWORD,
CSON_RC_Parse_INVALID_ESCAPE_SEQUENCE,
CSON_RC_Parse_INVALID_UNICODE_SEQUENCE,
CSON_RC_Parse_INVALID_NUMBER,
CSON_RC_Parse_NESTING_DEPTH_REACHED,
CSON_RC_Parse_UNBALANCED_COLLECTION,
CSON_RC_Parse_EXPECTED_KEY,
CSON_RC_Parse_EXPECTED_COLON
};
/**
Returns the string form of the cson_rc code corresponding to rc, or
some unspecified, non-NULL string if it is an unknown code.
The returned bytes are static and do not changing during the
|
| ︙ | ︙ | |||
669 670 671 672 673 674 675 |
The srcState argument is ignored by this function but is passed on to src,
so any output-destination-specific state can be stored there and accessed
via the src callback.
Non-parse error conditions include:
| | | | | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 |
The srcState argument is ignored by this function but is passed on to src,
so any output-destination-specific state can be stored there and accessed
via the src callback.
Non-parse error conditions include:
- (!tgt) or !src: CSON_RC_ArgError
- CSON_RC_AllocError can happen at any time during the input phase
Here's a complete example of using a custom input source:
@code
// Internal type to hold state for a JSON input string.
typedef struct
{
char const * str; // start of input string
char const * pos; // current internal cursor position
char const * end; // logical EOF (one-past-the-end)
} StringSource;
// cson_data_source_f() impl which uses StringSource.
static int cson_data_source_StringSource( void * state, void * dest,
unsigned int * n )
{
StringSource * ss = (StringSource*) state;
unsigned int i;
unsigned char * tgt = (unsigned char *)dest;
if( ! ss || ! n || !dest ) return CSON_RC_ArgError;
else if( !*n ) return CSON_RC_RangeError;
for( i = 0;
(i < *n) && (ss->pos < ss->end);
++i, ++ss->pos, ++tgt )
{
*tgt = *ss->pos;
}
*n = i;
|
| ︙ | ︙ | |||
746 747 748 749 750 751 752 |
*/
int cson_parse_FILE( cson_value ** tgt, FILE * src,
cson_parse_opt const * opt, cson_parse_info * info );
/**
Convenience wrapper around cson_parse_FILE() which opens the given filename.
| | | | | 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 |
*/
int cson_parse_FILE( cson_value ** tgt, FILE * src,
cson_parse_opt const * opt, cson_parse_info * info );
/**
Convenience wrapper around cson_parse_FILE() which opens the given filename.
Returns CSON_RC_IOError if the file cannot be opened.
@see cson_parse_FILE()
*/
int cson_parse_filename( cson_value ** tgt, char const * src,
cson_parse_opt const * opt, cson_parse_info * info );
/**
Uses an internal helper class to pass src through cson_parse().
See that function for the return value and argument semantics.
src must be a string containing JSON code, at least len bytes long,
and the parser will attempt to parse exactly len bytes from src.
If len is less than 2 (the minimum length of a legal top-node JSON
object) then CSON_RC_RangeError is returned.
*/
int cson_parse_string( cson_value ** tgt, char const * src, unsigned int len,
cson_parse_opt const * opt, cson_parse_info * info );
/**
Outputs the given value as a JSON-formatted string, sending all
output to the given callback function. It is intended for top-level
objects or arrays, but can be used with any cson_value.
If opt is NULL then default options (the values defined in
cson_output_opt_empty) are used.
If opt->maxDepth is exceeded while traversing the value tree,
CSON_RC_RangeError is returned.
The destState parameter is ignored by this function and is passed
on to the dest function.
Returns 0 on success. On error, any amount of output might have been
generated before the error was triggered.
|
| ︙ | ︙ | |||
814 815 816 817 818 819 820 | for FILE output. @see cson_output_filename() */ int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * opt ); /** Convenience wrapper around cson_output_FILE() which writes to the given filename, destroying | | | 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 | for FILE output. @see cson_output_filename() */ int cson_output_FILE( cson_value const * src, FILE * dest, cson_output_opt const * opt ); /** Convenience wrapper around cson_output_FILE() which writes to the given filename, destroying any existing contents. Returns CSON_RC_IOError if the file cannot be opened. @see cson_output_FILE() */ int cson_output_filename( cson_value const * src, char const * dest, cson_output_opt const * fmt ); /** Returns the virtual type of v, or CSON_TYPE_UNDEF if !v. |
| ︙ | ︙ | |||
945 946 947 948 949 950 951 | Similar to cson_value_fetch_bool(), but fetches an integer value. The conversion, if any, depends on the concrete type of val: NULL, null, undefined: *v is set to 0 and 0 is returned. string, object, array: *v is set to 0 and | | | 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | Similar to cson_value_fetch_bool(), but fetches an integer value. The conversion, if any, depends on the concrete type of val: NULL, null, undefined: *v is set to 0 and 0 is returned. string, object, array: *v is set to 0 and CSON_RC_TypeError is returned. The error may normally be safely ignored, but it is provided for those wanted to know whether a direct conversion was possible. integer: *v is set to the int value and 0 is returned. double: *v is set to the value truncated to int and 0 is returned. */ |
| ︙ | ︙ | |||
1176 1177 1178 1179 1180 1181 1182 | Ensures that ar has allocated space for at least the given number of entries. This never shrinks the array and never changes its logical size, but may pre-allocate space in the array for storing new (as-yet-unassigned) values. Returns 0 on success, or non-zero on error: | | | | | 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 | Ensures that ar has allocated space for at least the given number of entries. This never shrinks the array and never changes its logical size, but may pre-allocate space in the array for storing new (as-yet-unassigned) values. Returns 0 on success, or non-zero on error: - If ar is NULL: CSON_RC_ArgError - If allocation fails: CSON_RC_AllocError */ int cson_array_reserve( cson_array * ar, unsigned int size ); /** If ar is not NULL, sets *v (if v is not NULL) to the length of the array and returns 0. Returns CSON_RC_ArgError if ar is NULL. */ int cson_array_length_fetch( cson_array const * ar, unsigned int * v ); /** Simplified form of cson_array_length_fetch() which returns 0 if ar is NULL. */ |
| ︙ | ︙ | |||
1241 1242 1243 1244 1245 1246 1247 | This is functionally equivalent to cson_array_set(ar,cson_array_length_get(ar),v), but this implementation has slightly different array-preallocation policy (it grows more eagerly). Returns 0 on success, non-zero on error. Error cases include: | | | | | 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 | This is functionally equivalent to cson_array_set(ar,cson_array_length_get(ar),v), but this implementation has slightly different array-preallocation policy (it grows more eagerly). Returns 0 on success, non-zero on error. Error cases include: - ar or v are NULL: CSON_RC_ArgError - Array cannot be expanded to hold enough elements: CSON_RC_AllocError. - Appending would cause a numeric overlow in the array's size: CSON_RC_RangeError. (However, you'll get an AllocError long before that happens!) On error ownership of v is NOT modified, and the caller may still need to clean it up. See cson_array_set() for the details. */ int cson_array_append( cson_array * ar, cson_value * v ); |
| ︙ | ︙ | |||
1447 1448 1449 1450 1451 1452 1453 | The key may be encoded as ASCII or UTF8. Results are undefined with other encodings, and the errors won't show up here, but may show up later, e.g. during output. Returns 0 on success, non-0 on error. It has the following error cases: | | | | 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 | The key may be encoded as ASCII or UTF8. Results are undefined with other encodings, and the errors won't show up here, but may show up later, e.g. during output. Returns 0 on success, non-0 on error. It has the following error cases: - CSON_RC_ArgError: obj or key are NULL or strlen(key) is 0. - CSON_RC_AllocError: an out-of-memory error On error ownership of v is NOT modified, and the caller may still need to clean it up. For example, the following code will introduce a leak if this function fails: @code cson_object_set( myObj, "foo", cson_value_new_integer(42) ); |
| ︙ | ︙ | |||
1498 1499 1500 1501 1502 1503 1504 | */ int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v ); /** Removes a property from an object. If obj contains the given key, it is removed and 0 is returned. If | | | | 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 | */ int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v ); /** Removes a property from an object. If obj contains the given key, it is removed and 0 is returned. If it is not found, CSON_RC_NotFoundError is returned (which can normally be ignored by client code). CSON_RC_ArgError is returned if obj or key are NULL or key has a length of 0. Returns 0 if the given key is found and removed. This is functionally equivalent calling cson_object_set(obj,key,NULL). */ |
| ︙ | ︙ | |||
1561 1562 1563 1564 1565 1566 1567 |
obj is the object to search.
path is a delimited string, where the delimiter is the given
separator character.
This function searches for the given path, starting at the given object
and traversing its properties as the path specifies. If a given part of the
| | | | | | | 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 |
obj is the object to search.
path is a delimited string, where the delimiter is the given
separator character.
This function searches for the given path, starting at the given object
and traversing its properties as the path specifies. If a given part of the
path is not found, then this function fails with CSON_RC_NotFoundError.
If it finds the given path, it returns the value by assiging *tgt
to it. If tgt is NULL then this function has no side-effects but
will return 0 if the given path is found within the object, so it can be used
to test for existence without fetching it.
Returns 0 if it finds an entry, CSON_RC_NotFoundError if it finds
no item, and any other non-zero error code on a "real" error. Errors include:
- obj or path are NULL: CSON_RC_ArgError
- separator is 0, or path is an empty string or contains only
separator characters: CSON_RC_RangeError
- There is an upper limit on how long a single path component may
be (some "reasonable" internal size), and CSON_RC_RangeError is
returned if that length is violated.
Limitations:
- It has no way to fetch data from arrays this way. i could
imagine, e.g., a path of "subobj.subArray.0" for
|
| ︙ | ︙ | |||
1675 1676 1677 1678 1679 1680 1681 | Array properties in dest are NOT recursed for merging - they are either replaced or left as-is, depending on whether flags contains he CSON_MERGE_REPLACE bit. Returns 0 on success. The error conditions are: | | | 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 | Array properties in dest are NOT recursed for merging - they are either replaced or left as-is, depending on whether flags contains he CSON_MERGE_REPLACE bit. Returns 0 on success. The error conditions are: - dest or src are NULL or (dest==src) returns CSON_RC_ArgError. - dest or src contain cyclic references - this will likely cause a crash due to endless recursion. Potential TODOs: - Add a flag to copy clones, not the original values. |
| ︙ | ︙ | |||
1722 1723 1724 1725 1726 1727 1728 | /** Empty-initialized cson_object_iterator object. */ extern const cson_object_iterator cson_object_iterator_empty; /** Initializes the given iterator to point at the start of obj's | | | 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 | /** Empty-initialized cson_object_iterator object. */ extern const cson_object_iterator cson_object_iterator_empty; /** Initializes the given iterator to point at the start of obj's properties. Returns 0 on success or CSON_RC_ArgError if !obj or !iter. obj must outlive iter, or results are undefined. Results are also undefined if obj is modified while the iterator is active. @see cson_object_iter_next() */ |
| ︙ | ︙ | |||
1889 1890 1891 1892 1893 1894 1895 | On success 0 is returned and the contents of buf.mem are guaranteed to be NULL-terminated. On error the buffer might contain partial contents, and it should not be used except to free its contents. On error non-zero is returned. Errors include: | | | | 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 | On success 0 is returned and the contents of buf.mem are guaranteed to be NULL-terminated. On error the buffer might contain partial contents, and it should not be used except to free its contents. On error non-zero is returned. Errors include: - Invalid arguments: CSON_RC_ArgError - Buffer cannot be expanded (runs out of memory): CSON_RC_AllocError Example usage: @code cson_buffer buf = cson_buffer_empty; // optional: cson_buffer_reserve(&buf, 1024 * 10); int rc = cson_output_buffer( myValue, &buf, NULL ); |
| ︙ | ︙ | |||
1997 1998 1999 2000 2001 2002 2003 | invalidated by this call. On error non-0 is returned and dest has almost certainly been modified but its state must be considered incomplete. Errors include: | | | | 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 | invalidated by this call. On error non-0 is returned and dest has almost certainly been modified but its state must be considered incomplete. Errors include: - dest or src are NULL (CSON_RC_ArgError) - Allocation error (CSON_RC_AllocError) - src() returns an error code Whether or not the state parameter may be NULL depends on the src implementation requirements. On success dest will contain the contents read from the input |
| ︙ | ︙ | |||
2082 2083 2084 2085 2086 2087 2088 | cson_value_free(), after which the value must be considered, from the perspective of that client code, to be destroyed (though it will not be if there are still other live references to it). cson_value_free() will not _actually_ destroy the value until its reference count drops to 0. Returns 0 on success. The only error conditions are if v is NULL | | | | 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 | cson_value_free(), after which the value must be considered, from the perspective of that client code, to be destroyed (though it will not be if there are still other live references to it). cson_value_free() will not _actually_ destroy the value until its reference count drops to 0. Returns 0 on success. The only error conditions are if v is NULL (CSON_RC_ArgError) or if the reference increment would overflow (CSON_RC_RangeError). In theory a client would get allocation errors long before the reference count could overflow (assuming those reference counts come from container insertions, as opposed to via this function). Insider notes which clients really need to know: For shared/constant value instances, such as those returned by |
| ︙ | ︙ | |||
2236 2237 2238 2239 2240 2241 2242 | /** Calculates the approximate in-memory-allocated size of v, recursively if it is a container type, with the following caveats and limitations: | | | 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 | /** Calculates the approximate in-memory-allocated size of v, recursively if it is a container type, with the following caveats and limitations: If a given value is reference counted and encountered multiple times within a traversed container, each reference is counted at full cost. We have no way of knowing if a given reference has been visited already and whether it should or should not be counted, so we pessimistically count them even though the _might_ not really count for the given object tree (it depends on where the other open references live). |
| ︙ | ︙ | |||
2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 |
- If (*tgt!=NULL) (i.e., the caller provides his own object) then
it might contain partial results.
*/
int cson_parse_argv_flags( int argc, char const * const * argv,
cson_object ** tgt, unsigned int * count );
/* LICENSE
This software's source code, including accompanying documentation and
demonstration applications, are licensed under the following
conditions...
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 |
- If (*tgt!=NULL) (i.e., the caller provides his own object) then
it might contain partial results.
*/
int cson_parse_argv_flags( int argc, char const * const * argv,
cson_object ** tgt, unsigned int * count );
/**
Return values for the cson_pack() and cson_unpack() interfaces.
*/
enum cson_pack_retval {
/** Signals an out-of-memory error. */
CSON_PACK_ALLOC_ERROR = -1,
/** Signals a syntax error in the format string. */
CSON_PACK_ARG_ERROR = -2,
/**
Signals an that an internal error has occurred.
This indicates a bug in this library.
*/
CSON_PACK_INTERNAL_ERROR = -3,
/**
Signals that the JSON document does not validate agains the format
string passed to cson_unpack().
*/
CSON_PACK_VALIDATION_ERROR = -4
};
/**
Construct arbitrarily complex JSON documents from native C types.
Create a new object or array and add or merge the passed values and
properties to it according to the supplied format string.
fmt is a format string, it must at least contain an array or object
specifier as its root value. Format specifiers start with a percent sign '\%'
followed by one or more modifiers and a type character. Object properties
are specified as key-value pairs where the key is specified as a string and
passed as an argument of const char *. Any space, tab, carriage return, line
feed, colon and comma characters between format specifiers are ignored.
| Type | Description |
| :--: | :---------- |
| s | creates either a property name or a string value, in case of the former the corresponding argument is a pointer to const char which is a sequence of bytes specifying the name of the property that is to be created, in case of the latter the corresponding argument is a pointer to const char |
| d | creates an integer value, the corresponding argument is an int |
| i | ^ |
| f | creates a floating point value, the corresponding argument is a double |
| b | creates a boolean value, the corresponding argument is an int |
| N | creates a null value |
| [...] | creates an array, the corresponding argument is a pointer to a cson_array |
| {...} | creates an array, the corresponding argument is a pointer to a cson_object |
| Modifier | Description |
| :------: | :---------- |
| l | specifies that the following d or i specifier applies to an argument which is a pointer to long |
| ll | specifies that the following d or i specifier applies to an argument which is a pointer to cson_int_t |
| Short Form | Expands to
| :--------: | :--------- |
| {...} | %*{...} |
| [...] | %*[...] |
| \%D | \%lld |
Returns 0 on success. The error conditions are:
- CSON_PACK_ARG_ERROR: fmt contains a syntax error
- CSON_PACK_ALLOC_ERROR: a memory allocation failed
- CSON_PACK_INTERNAL_ERROR: an internal error has occurred, this is a bug in
cson
Example:
@code
cson_value * root_value;
cson_array * arr;
...
rc = cson_pack( root_value, "{%s: %d, %s: %[]}", "foo", 42, "bar", arr );
if( 0 != rc ) {
... error ...
}
@endcode
*/
int cson_pack( cson_value **root_valuep, const char *fmt, ... );
/**
Same as cson_pack() except that it takes a va_list instead of a variable
number of arguments.
*/
int cson_vpack( cson_value **root_valuep, const char *fmt, va_list args );
/**
Iterate over the given object or array and convert an arbitrary number of
JSON values into their native C types or validates them according to the
given format string fmt.
fmt is a format string, it must at least contain an array or object
specifier as its root value. Format specifiers start with a percent sign '\%'
followed by one or more modifiers and a type character. Object properties
are specified as key-value pairs where the key is specified as a string and
passed as an argument of const char *. Any space, tab, carriage return, line
feed, colon and comma characters between format specifiers are ignored.
| Type | Description |
| :--: | :---------- |
| s | matches a either a property name or a string value, in case of the former the corresponding argument is a pointer to const char which is a sequence of bytes specifying the name of the property that is to be matched, in case of the latter the corresponding argument is a pointer to a pointer to const char unless the 'm' modifier is specified where the the corresponding argument is a pointer to a pointer to char |
| d | matches an integer value and must be used in with the "ll" modifier, the corresponding argument is a pointer to cson_int_t |
| i | ^ |
| f | matches a floating point value, the corresponding argument is a pointer to double |
| b | matches a boolean value, the corresponding argument is a pointer to int |
| N | matches a null value |
| [...] | matches an array, the corresponding argument is a pointer to a pointer to a cson_array |
| {...} | matches an array, the corresponding argument is a pointer to a pointer to a cson_object |
| Modifier | Description |
| :------: | :---------- |
| ? | specifies that the property reffered to by the given property name is optional |
| * | suppresses assignment, only check for the presence and type of the specified value |
| m | allocates a memory buffer for the extracted string |
| ll | specifies that the following d or i specifier applies to an argument which is a pointer to cson_int_t |
| Short Form | Expands to
| :--------: | :--------- |
| {...} | %*{...} |
| [...] | %*[...] |
| \%D | \%lld |
Returns 0 on success. The error conditions are:
- CSON_PACK_ARG_ERROR: fmt contains a syntax error
- CSON_PACK_ALLOC_ERROR: a memory allocation failed
- CSON_PACK_VALIDATION_ERROR: validation failed, the JSON document structure
differs from that described by the format string
- CSON_PACK_INTERNAL_ERROR: an internal error has occurred, this
indicates a bug in this library.
Example:
@code
cson_value * root_value;
cson_int_t x = 0;
cson_array * arr = NULL;
const char *str = NULL;
...
rc = cson_unpack( root_value, "{%s: %d, %s: %[], %?s: %s}", "foo", &x, "bar", &arr, "baz", &str );
if( rc < 3 && rc >= 0 ) {
... optional property is missing ...
} else if ( CSON_PACK_ALLOC_ERROR == rc ) {
... out of memory error ...
} else if ( CSON_PACK_VALIDATION_ERROR == rc ) {
... unexpected JSON document structure ...
} else if ( rc ) {
... internal error ...
}
@endcode
*/
int cson_unpack( cson_value *root_value, const char *fmt, ... );
/**
Same as cson_unpack() except that it takes a va_list instead of a variable
number of arguments.
*/
int cson_vunpack( cson_value *root_value, const char *fmt, va_list args );
/* LICENSE
This software's source code, including accompanying documentation and
demonstration applications, are licensed under the following
conditions...
|
| ︙ | ︙ | |||
2414 2415 2416 2417 2418 2419 2420 | #define CSON_ENABLE_SQLITE3 1 # else #define CSON_ENABLE_SQLITE3 1 # endif #endif #if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */ | | | 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 |
#define CSON_ENABLE_SQLITE3 1
# else
#define CSON_ENABLE_SQLITE3 1
# endif
#endif
#if CSON_ENABLE_SQLITE3 /* we do this here for the sake of the amalgamation build */
#include "sqlite3.h"
#if defined(__cplusplus)
extern "C" {
#endif
/**
Converts a single value from a single 0-based column index to its JSON
|
| ︙ | ︙ |
Changes to extsrc/linenoise.c.
1 2 3 4 5 | /* linenoise.c -- guerrilla line editing library against the idea that a * line editing lib needs to be 20,000 lines of C code. * * You can find the latest source code at: * | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 134 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 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 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 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 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 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 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 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 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 |
#line 1 "utf8.h"
#ifndef UTF8_UTIL_H
#define UTF8_UTIL_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* UTF-8 utility functions
*
* (c) 2010-2019 Steve Bennett <steveb@workware.net.au>
*
* See utf8.c for licence details.
*/
#ifndef USE_UTF8
#include <ctype.h>
#define MAX_UTF8_LEN 1
/* No utf-8 support. 1 byte = 1 char */
#define utf8_strlen(S, B) ((B) < 0 ? (int)strlen(S) : (B))
#define utf8_strwidth(S, B) utf8_strlen((S), (B))
#define utf8_tounicode(S, CP) (*(CP) = (unsigned char)*(S), 1)
#define utf8_index(C, I) (I)
#define utf8_charlen(C) 1
#define utf8_width(C) 1
#else
#define MAX_UTF8_LEN 4
/**
* Converts the given unicode codepoint (0 - 0x1fffff) to utf-8
* and stores the result at 'p'.
*
* Returns the number of utf-8 characters
*/
int utf8_fromunicode(char *p, unsigned uc);
/**
* Returns the length of the utf-8 sequence starting with 'c'.
*
* Returns 1-4, or -1 if this is not a valid start byte.
*
* Note that charlen=4 is not supported by the rest of the API.
*/
int utf8_charlen(int c);
/**
* Returns the number of characters in the utf-8
* string of the given byte length.
*
* Any bytes which are not part of an valid utf-8
* sequence are treated as individual characters.
*
* The string *must* be null terminated.
*
* Does not support unicode code points > \u1fffff
*/
int utf8_strlen(const char *str, int bytelen);
/**
* Calculates the display width of the first 'charlen' characters in 'str'.
* See utf8_width()
*/
int utf8_strwidth(const char *str, int charlen);
/**
* Returns the byte index of the given character in the utf-8 string.
*
* The string *must* be null terminated.
*
* This will return the byte length of a utf-8 string
* if given the char length.
*/
int utf8_index(const char *str, int charindex);
/**
* Returns the unicode codepoint corresponding to the
* utf-8 sequence 'str'.
*
* Stores the result in *uc and returns the number of bytes
* consumed.
*
* If 'str' is null terminated, then an invalid utf-8 sequence
* at the end of the string will be returned as individual bytes.
*
* If it is not null terminated, the length *must* be checked first.
*
* Does not support unicode code points > \u1fffff
*/
int utf8_tounicode(const char *str, int *uc);
/**
* Returns the width (in characters) of the given unicode codepoint.
* This is 1 for normal letters and 0 for combining characters and 2 for wide characters.
*/
int utf8_width(int ch);
#endif
#ifdef __cplusplus
}
#endif
#endif
#line 1 "utf8.c"
/**
* UTF-8 utility functions
*
* (c) 2010-2019 Steve Bennett <steveb@workware.net.au>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef UTF8_UTIL_H
#include "utf8.h"
#endif
#ifdef USE_UTF8
int utf8_fromunicode(char *p, unsigned uc)
{
if (uc <= 0x7f) {
*p = uc;
return 1;
}
else if (uc <= 0x7ff) {
*p++ = 0xc0 | ((uc & 0x7c0) >> 6);
*p = 0x80 | (uc & 0x3f);
return 2;
}
else if (uc <= 0xffff) {
*p++ = 0xe0 | ((uc & 0xf000) >> 12);
*p++ = 0x80 | ((uc & 0xfc0) >> 6);
*p = 0x80 | (uc & 0x3f);
return 3;
}
/* Note: We silently truncate to 21 bits here: 0x1fffff */
else {
*p++ = 0xf0 | ((uc & 0x1c0000) >> 18);
*p++ = 0x80 | ((uc & 0x3f000) >> 12);
*p++ = 0x80 | ((uc & 0xfc0) >> 6);
*p = 0x80 | (uc & 0x3f);
return 4;
}
}
int utf8_charlen(int c)
{
if ((c & 0x80) == 0) {
return 1;
}
if ((c & 0xe0) == 0xc0) {
return 2;
}
if ((c & 0xf0) == 0xe0) {
return 3;
}
if ((c & 0xf8) == 0xf0) {
return 4;
}
/* Invalid sequence */
return -1;
}
int utf8_strlen(const char *str, int bytelen)
{
int charlen = 0;
if (bytelen < 0) {
bytelen = strlen(str);
}
while (bytelen > 0) {
int c;
int l = utf8_tounicode(str, &c);
charlen++;
str += l;
bytelen -= l;
}
return charlen;
}
int utf8_strwidth(const char *str, int charlen)
{
int width = 0;
while (charlen) {
int c;
int l = utf8_tounicode(str, &c);
width += utf8_width(c);
str += l;
charlen--;
}
return width;
}
int utf8_index(const char *str, int index)
{
const char *s = str;
while (index--) {
int c;
s += utf8_tounicode(s, &c);
}
return s - str;
}
int utf8_tounicode(const char *str, int *uc)
{
unsigned const char *s = (unsigned const char *)str;
if (s[0] < 0xc0) {
*uc = s[0];
return 1;
}
if (s[0] < 0xe0) {
if ((s[1] & 0xc0) == 0x80) {
*uc = ((s[0] & ~0xc0) << 6) | (s[1] & ~0x80);
if (*uc >= 0x80) {
return 2;
}
/* Otherwise this is an invalid sequence */
}
}
else if (s[0] < 0xf0) {
if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80)) {
*uc = ((s[0] & ~0xe0) << 12) | ((s[1] & ~0x80) << 6) | (s[2] & ~0x80);
if (*uc >= 0x800) {
return 3;
}
/* Otherwise this is an invalid sequence */
}
}
else if (s[0] < 0xf8) {
if (((str[1] & 0xc0) == 0x80) && ((str[2] & 0xc0) == 0x80) && ((str[3] & 0xc0) == 0x80)) {
*uc = ((s[0] & ~0xf0) << 18) | ((s[1] & ~0x80) << 12) | ((s[2] & ~0x80) << 6) | (s[3] & ~0x80);
if (*uc >= 0x10000) {
return 4;
}
/* Otherwise this is an invalid sequence */
}
}
/* Invalid sequence, so just return the byte */
*uc = *s;
return 1;
}
struct utf8range {
int lower; /* lower inclusive */
int upper; /* upper exclusive */
};
/* From http://unicode.org/Public/UNIDATA/UnicodeData.txt */
static const struct utf8range unicode_range_combining[] = {
{ 0x0300, 0x0370 }, { 0x0483, 0x048a }, { 0x0591, 0x05d0 }, { 0x0610, 0x061b },
{ 0x064b, 0x0660 }, { 0x0670, 0x0671 }, { 0x06d6, 0x06dd }, { 0x06df, 0x06e5 },
{ 0x06e7, 0x06ee }, { 0x0711, 0x0712 }, { 0x0730, 0x074d }, { 0x07a6, 0x07b1 },
{ 0x07eb, 0x07f4 }, { 0x0816, 0x0830 }, { 0x0859, 0x085e }, { 0x08d4, 0x0904 },
{ 0x093a, 0x0958 }, { 0x0962, 0x0964 }, { 0x0981, 0x0985 }, { 0x09bc, 0x09ce },
{ 0x09d7, 0x09dc }, { 0x09e2, 0x09e6 }, { 0x0a01, 0x0a05 }, { 0x0a3c, 0x0a59 },
{ 0x0a70, 0x0a72 }, { 0x0a75, 0x0a85 }, { 0x0abc, 0x0ad0 }, { 0x0ae2, 0x0ae6 },
{ 0x0afa, 0x0b05 }, { 0x0b3c, 0x0b5c }, { 0x0b62, 0x0b66 }, { 0x0b82, 0x0b83 },
{ 0x0bbe, 0x0bd0 }, { 0x0bd7, 0x0be6 }, { 0x0c00, 0x0c05 }, { 0x0c3e, 0x0c58 },
{ 0x0c62, 0x0c66 }, { 0x0c81, 0x0c85 }, { 0x0cbc, 0x0cde }, { 0x0ce2, 0x0ce6 },
{ 0x0d00, 0x0d05 }, { 0x0d3b, 0x0d4e }, { 0x0d57, 0x0d58 }, { 0x0d62, 0x0d66 },
{ 0x0d82, 0x0d85 }, { 0x0dca, 0x0de6 }, { 0x0df2, 0x0df4 }, { 0x0e31, 0x0e32 },
{ 0x0e34, 0x0e3f }, { 0x0e47, 0x0e4f }, { 0x0eb1, 0x0eb2 }, { 0x0eb4, 0x0ebd },
{ 0x0ec8, 0x0ed0 }, { 0x0f18, 0x0f1a }, { 0x0f35, 0x0f3a }, { 0x0f3e, 0x0f40 },
{ 0x0f71, 0x0f88 }, { 0x0f8d, 0x0fbe }, { 0x0fc6, 0x0fc7 }, { 0x102b, 0x103f },
{ 0x1056, 0x105a }, { 0x105e, 0x1065 }, { 0x1067, 0x106e }, { 0x1071, 0x1075 },
{ 0x1082, 0x1090 }, { 0x109a, 0x109e }, { 0x135d, 0x1360 }, { 0x1712, 0x1720 },
{ 0x1732, 0x1735 }, { 0x1752, 0x1760 }, { 0x1772, 0x1780 }, { 0x17b4, 0x17d4 },
{ 0x17dd, 0x17e0 }, { 0x180b, 0x180e }, { 0x1885, 0x1887 }, { 0x18a9, 0x18aa },
{ 0x1920, 0x1940 }, { 0x1a17, 0x1a1e }, { 0x1a55, 0x1a80 }, { 0x1ab0, 0x1b05 },
{ 0x1b34, 0x1b45 }, { 0x1b6b, 0x1b74 }, { 0x1b80, 0x1b83 }, { 0x1ba1, 0x1bae },
{ 0x1be6, 0x1bfc }, { 0x1c24, 0x1c3b }, { 0x1cd0, 0x1ce9 }, { 0x1ced, 0x1cee },
{ 0x1cf2, 0x1cf5 }, { 0x1cf7, 0x1d00 }, { 0x1dc0, 0x1e00 }, { 0x20d0, 0x2100 },
{ 0x2cef, 0x2cf2 }, { 0x2d7f, 0x2d80 }, { 0x2de0, 0x2e00 }, { 0x302a, 0x3030 },
{ 0x3099, 0x309b }, { 0xa66f, 0xa67e }, { 0xa69e, 0xa6a0 }, { 0xa6f0, 0xa6f2 },
{ 0xa802, 0xa803 }, { 0xa806, 0xa807 }, { 0xa80b, 0xa80c }, { 0xa823, 0xa828 },
{ 0xa880, 0xa882 }, { 0xa8b4, 0xa8ce }, { 0xa8e0, 0xa8f2 }, { 0xa926, 0xa92e },
{ 0xa947, 0xa95f }, { 0xa980, 0xa984 }, { 0xa9b3, 0xa9c1 }, { 0xa9e5, 0xa9e6 },
{ 0xaa29, 0xaa40 }, { 0xaa43, 0xaa44 }, { 0xaa4c, 0xaa50 }, { 0xaa7b, 0xaa7e },
{ 0xaab0, 0xaab5 }, { 0xaab7, 0xaab9 }, { 0xaabe, 0xaac2 }, { 0xaaeb, 0xaaf0 },
{ 0xaaf5, 0xab01 }, { 0xabe3, 0xabf0 }, { 0xfb1e, 0xfb1f }, { 0xfe00, 0xfe10 },
{ 0xfe20, 0xfe30 },
};
/* From http://unicode.org/Public/UNIDATA/EastAsianWidth.txt */
static const struct utf8range unicode_range_wide[] = {
{ 0x1100, 0x115f }, { 0x231a, 0x231b }, { 0x2329, 0x232a }, { 0x23e9, 0x23ec },
{ 0x23f0, 0x23f0 }, { 0x23f3, 0x23f3 }, { 0x25fd, 0x25fe }, { 0x2614, 0x2615 },
{ 0x2648, 0x2653 }, { 0x267f, 0x267f }, { 0x2693, 0x2693 }, { 0x26a1, 0x26a1 },
{ 0x26aa, 0x26ab }, { 0x26bd, 0x26be }, { 0x26c4, 0x26c5 }, { 0x26ce, 0x26ce },
{ 0x26d4, 0x26d4 }, { 0x26ea, 0x26ea }, { 0x26f2, 0x26f3 }, { 0x26f5, 0x26f5 },
{ 0x26fa, 0x26fa }, { 0x26fd, 0x26fd }, { 0x2705, 0x2705 }, { 0x270a, 0x270b },
{ 0x2728, 0x2728 }, { 0x274c, 0x274c }, { 0x274e, 0x274e }, { 0x2753, 0x2755 },
{ 0x2757, 0x2757 }, { 0x2795, 0x2797 }, { 0x27b0, 0x27b0 }, { 0x27bf, 0x27bf },
{ 0x2b1b, 0x2b1c }, { 0x2b50, 0x2b50 }, { 0x2b55, 0x2b55 }, { 0x2e80, 0x2e99 },
{ 0x2e9b, 0x2ef3 }, { 0x2f00, 0x2fd5 }, { 0x2ff0, 0x2ffb }, { 0x3001, 0x303e },
{ 0x3041, 0x3096 }, { 0x3099, 0x30ff }, { 0x3105, 0x312e }, { 0x3131, 0x318e },
{ 0x3190, 0x31ba }, { 0x31c0, 0x31e3 }, { 0x31f0, 0x321e }, { 0x3220, 0x3247 },
{ 0x3250, 0x32fe }, { 0x3300, 0x4dbf }, { 0x4e00, 0xa48c }, { 0xa490, 0xa4c6 },
{ 0xa960, 0xa97c }, { 0xac00, 0xd7a3 }, { 0xf900, 0xfaff }, { 0xfe10, 0xfe19 },
{ 0xfe30, 0xfe52 }, { 0xfe54, 0xfe66 }, { 0xfe68, 0xfe6b }, { 0x16fe0, 0x16fe1 },
{ 0x17000, 0x187ec }, { 0x18800, 0x18af2 }, { 0x1b000, 0x1b11e }, { 0x1b170, 0x1b2fb },
{ 0x1f004, 0x1f004 }, { 0x1f0cf, 0x1f0cf }, { 0x1f18e, 0x1f18e }, { 0x1f191, 0x1f19a },
{ 0x1f200, 0x1f202 }, { 0x1f210, 0x1f23b }, { 0x1f240, 0x1f248 }, { 0x1f250, 0x1f251 },
{ 0x1f260, 0x1f265 }, { 0x1f300, 0x1f320 }, { 0x1f32d, 0x1f335 }, { 0x1f337, 0x1f37c },
{ 0x1f37e, 0x1f393 }, { 0x1f3a0, 0x1f3ca }, { 0x1f3cf, 0x1f3d3 }, { 0x1f3e0, 0x1f3f0 },
{ 0x1f3f4, 0x1f3f4 }, { 0x1f3f8, 0x1f43e }, { 0x1f440, 0x1f440 }, { 0x1f442, 0x1f4fc },
{ 0x1f4ff, 0x1f53d }, { 0x1f54b, 0x1f54e }, { 0x1f550, 0x1f567 }, { 0x1f57a, 0x1f57a },
{ 0x1f595, 0x1f596 }, { 0x1f5a4, 0x1f5a4 }, { 0x1f5fb, 0x1f64f }, { 0x1f680, 0x1f6c5 },
{ 0x1f6cc, 0x1f6cc }, { 0x1f6d0, 0x1f6d2 }, { 0x1f6eb, 0x1f6ec }, { 0x1f6f4, 0x1f6f8 },
{ 0x1f910, 0x1f93e }, { 0x1f940, 0x1f94c }, { 0x1f950, 0x1f96b }, { 0x1f980, 0x1f997 },
{ 0x1f9c0, 0x1f9c0 }, { 0x1f9d0, 0x1f9e6 }, { 0x20000, 0x2fffd }, { 0x30000, 0x3fffd },
};
#define ARRAYSIZE(A) sizeof(A) / sizeof(*(A))
static int cmp_range(const void *key, const void *cm)
{
const struct utf8range *range = (const struct utf8range *)cm;
int ch = *(int *)key;
if (ch < range->lower) {
return -1;
}
if (ch >= range->upper) {
return 1;
}
return 0;
}
static int utf8_in_range(const struct utf8range *range, int num, int ch)
{
const struct utf8range *r =
bsearch(&ch, range, num, sizeof(*range), cmp_range);
if (r) {
return 1;
}
return 0;
}
int utf8_width(int ch)
{
/* short circuit for common case */
if (isascii(ch)) {
return 1;
}
if (utf8_in_range(unicode_range_combining, ARRAYSIZE(unicode_range_combining), ch)) {
return 0;
}
if (utf8_in_range(unicode_range_wide, ARRAYSIZE(unicode_range_wide), ch)) {
return 2;
}
return 1;
}
#endif
#line 1 "stringbuf.h"
#ifndef STRINGBUF_H
#define STRINGBUF_H
/**
* resizable string buffer
*
* (c) 2017-2020 Steve Bennett <steveb@workware.net.au>
*
* See utf8.c for licence details.
*/
#ifdef __cplusplus
extern "C" {
#endif
/** @file
* A stringbuf is a resizing, null terminated string buffer.
*
* The buffer is reallocated as necessary.
*
* In general it is *not* OK to call these functions with a NULL pointer
* unless stated otherwise.
*
* If USE_UTF8 is defined, supports utf8.
*/
/**
* The stringbuf structure should not be accessed directly.
* Use the functions below.
*/
typedef struct {
int remaining; /**< Allocated, but unused space */
int last; /**< Index of the null terminator (and thus the length of the string) */
#ifdef USE_UTF8
int chars; /**< Count of characters */
#endif
char *data; /**< Allocated memory containing the string or NULL for empty */
} stringbuf;
/**
* Allocates and returns a new stringbuf with no elements.
*/
stringbuf *sb_alloc(void);
/**
* Frees a stringbuf.
* It is OK to call this with NULL.
*/
void sb_free(stringbuf *sb);
/**
* Returns an allocated copy of the stringbuf
*/
stringbuf *sb_copy(stringbuf *sb);
/**
* Returns the byte length of the buffer.
*
* Returns 0 for both a NULL buffer and an empty buffer.
*/
static inline int sb_len(stringbuf *sb) {
return sb->last;
}
/**
* Returns the utf8 character length of the buffer.
*
* Returns 0 for both a NULL buffer and an empty buffer.
*/
static inline int sb_chars(stringbuf *sb) {
#ifdef USE_UTF8
return sb->chars;
#else
return sb->last;
#endif
}
/**
* Appends a null terminated string to the stringbuf
*/
void sb_append(stringbuf *sb, const char *str);
/**
* Like sb_append() except does not require a null terminated string.
* The length of 'str' is given as 'len'
*
* Note that in utf8 mode, characters will *not* be counted correctly
* if a partial utf8 sequence is added with sb_append_len()
*/
void sb_append_len(stringbuf *sb, const char *str, int len);
/**
* Returns a pointer to the null terminated string in the buffer.
*
* Note this pointer only remains valid until the next modification to the
* string buffer.
*
* The returned pointer can be used to update the buffer in-place
* as long as care is taken to not overwrite the end of the buffer.
*/
static inline char *sb_str(const stringbuf *sb)
{
return sb->data;
}
/**
* Inserts the given string *before* (zero-based) byte 'index' in the stringbuf.
* If index is past the end of the buffer, the string is appended,
* just like sb_append()
*/
void sb_insert(stringbuf *sb, int index, const char *str);
/**
* Delete 'len' bytes in the string at the given index.
*
* Any bytes past the end of the buffer are ignored.
* The buffer remains null terminated.
*
* If len is -1, deletes to the end of the buffer.
*/
void sb_delete(stringbuf *sb, int index, int len);
/**
* Clear to an empty buffer.
*/
void sb_clear(stringbuf *sb);
/**
* Return an allocated copy of buffer and frees 'sb'.
*
* If 'sb' is empty, returns an allocated copy of "".
*/
char *sb_to_string(stringbuf *sb);
#ifdef __cplusplus
}
#endif
#endif
#line 1 "stringbuf.c"
/**
* resizable string buffer
*
* (c) 2017-2020 Steve Bennett <steveb@workware.net.au>
*
* See utf8.c for licence details.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#ifndef STRINGBUF_H
#include "stringbuf.h"
#endif
#ifdef USE_UTF8
#ifndef UTF8_UTIL_H
#include "utf8.h"
#endif
#endif
#define SB_INCREMENT 200
stringbuf *sb_alloc(void)
{
stringbuf *sb = (stringbuf *)malloc(sizeof(*sb));
sb->remaining = 0;
sb->last = 0;
#ifdef USE_UTF8
sb->chars = 0;
#endif
sb->data = NULL;
return(sb);
}
void sb_free(stringbuf *sb)
{
if (sb) {
free(sb->data);
}
free(sb);
}
static void sb_realloc(stringbuf *sb, int newlen)
{
sb->data = (char *)realloc(sb->data, newlen);
sb->remaining = newlen - sb->last;
}
void sb_append(stringbuf *sb, const char *str)
{
sb_append_len(sb, str, strlen(str));
}
void sb_append_len(stringbuf *sb, const char *str, int len)
{
if (sb->remaining < len + 1) {
sb_realloc(sb, sb->last + len + 1 + SB_INCREMENT);
}
memcpy(sb->data + sb->last, str, len);
sb->data[sb->last + len] = 0;
sb->last += len;
sb->remaining -= len;
#ifdef USE_UTF8
sb->chars += utf8_strlen(str, len);
#endif
}
char *sb_to_string(stringbuf *sb)
{
if (sb->data == NULL) {
/* Return an allocated empty string, not null */
return strdup("");
}
else {
/* Just return the data and free the stringbuf structure */
char *pt = sb->data;
free(sb);
return pt;
}
}
/* Insert and delete operations */
/* Moves up all the data at position 'pos' and beyond by 'len' bytes
* to make room for new data
*
* Note: Does *not* update sb->chars
*/
static void sb_insert_space(stringbuf *sb, int pos, int len)
{
assert(pos <= sb->last);
/* Make sure there is enough space */
if (sb->remaining < len) {
sb_realloc(sb, sb->last + len + SB_INCREMENT);
}
/* Now move it up */
memmove(sb->data + pos + len, sb->data + pos, sb->last - pos);
sb->last += len;
sb->remaining -= len;
/* And null terminate */
sb->data[sb->last] = 0;
}
/**
* Move down all the data from pos + len, effectively
* deleting the data at position 'pos' of length 'len'
*/
static void sb_delete_space(stringbuf *sb, int pos, int len)
{
assert(pos < sb->last);
assert(pos + len <= sb->last);
#ifdef USE_UTF8
sb->chars -= utf8_strlen(sb->data + pos, len);
#endif
/* Now move it up */
memmove(sb->data + pos, sb->data + pos + len, sb->last - pos - len);
sb->last -= len;
sb->remaining += len;
/* And null terminate */
sb->data[sb->last] = 0;
}
void sb_insert(stringbuf *sb, int index, const char *str)
{
if (index >= sb->last) {
/* Inserting after the end of the list appends. */
sb_append(sb, str);
}
else {
int len = strlen(str);
sb_insert_space(sb, index, len);
memcpy(sb->data + index, str, len);
#ifdef USE_UTF8
sb->chars += utf8_strlen(str, len);
#endif
}
}
/**
* Delete the bytes at index 'index' for length 'len'
* Has no effect if the index is past the end of the list.
*/
void sb_delete(stringbuf *sb, int index, int len)
{
if (index < sb->last) {
char *pos = sb->data + index;
if (len < 0) {
len = sb->last;
}
sb_delete_space(sb, pos - sb->data, len);
}
}
void sb_clear(stringbuf *sb)
{
if (sb->data) {
/* Null terminate */
sb->data[0] = 0;
sb->last = 0;
#ifdef USE_UTF8
sb->chars = 0;
#endif
}
}
#line 1 "linenoise.c"
/* linenoise.c -- guerrilla line editing library against the idea that a
* line editing lib needs to be 20,000 lines of C code.
*
* You can find the latest source code at:
*
* http://github.com/msteveb/linenoise
* (forked from http://github.com/antirez/linenoise)
*
* Does a number of crazy assumptions that happen to be true in 99.9999% of
* the 2010 UNIX computers around.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* Copyright (c) 2011, Steve Bennett <steveb at workware dot net dot au>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
|
| ︙ | ︙ | |||
40 41 42 43 44 45 46 | * * ------------------------------------------------------------------------ * * References: * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html * | < < < < | > > | | | < < | | | | < < < | < < < < < | < < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | > > > > > > > > > > > > | | | | | | | > > > > > > > > > > > > | > > > > | < > | < < | > > | | | | > | > > > | > > > > | | > > > | > > > > | > > > > > > > > > > | > > > > > > > > > | > > > | > > > > > | > | < < < < < | | | | | | | | | | | | | | > > > > > > > > > > > | > > > > > > | > > > > | > > > > > > | > > > | | > > > > | | < | | < < < | | < > | > | | > > > | < > > > > > | > | | > > > > > > | > > | > | > > > | < | > > > > > > > | > | | > > | < > > | > | | > > > > > | > | > | > > > > > | > < < < | > | | | | | > > > | | | > > > > > > > > > < | | > | > | < < < | < | | > > > > > > > | > > > > > | > > > > > > > > | > > > > > > | > > > > > | | | > > > > > > | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > | > > > > | > > > > | > > > > > > > > > > > | > > > > > > > | > > > > > > > > > | | | > > > > > > > > | > > > > > > > > > > > > > > > | | > > > | > > > > > | > > | | > | | > > | > > > > > | > > > | > > | > | > > > > | > > > > | > > > > > > > | > > > > | > | | > > > > > > > > > | > | > | | > > > | > | | > > | > > > > | | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | > > > > > > | | > > > > > > > > > > > > > > > > > > > | | > > > | > > > | > > > > > > > > > > > > > > | > > > > > > > | | | > > > > > > > | | > > < < < < | < < < < < < | < | | | < < | < | < < < | | | < | | | | | > > | < | > > | > < | < < < < < | < < < < < < < < | | | | | < > | | > | > > | | | | | < > | | > > > > > > > | > | < < | | > > | > > > > > > > > > > > > > > > | | > | | | | > | | | > > > > | > > > | | | | > > > | > > > | < > > | > | | | < | > | | | | > > | > > > > | > | > > | | > > > > > > > | | | | | > | | > > > > > > > > > > > > > > > | > > | > | | > > > > | > > > > > | > > | | | > > | > > > > | < < > > | | > > | > | < > | > > > > > > > > > > > > > | > > > > | > > > > > | | > | > > > | > | | > | > | > > > > > | | > > | > > > | < < < < < < < > | > > > > > > > > | < | < < < | > | | < < > | | | < | > | | > > > > > > > > > > | > > | > > | | > | < < < > > > > > > | | | < < < > | > > > > > > > > | > | > | < > | > | > > > | | > > > > > > | > | > > > | < > | > > > | | > | | | < > | > | < > > | | | | | | | > > > > > > > | > | > | > > > | > > > | < | | | | | > > > | | > > > > | < > > | > > > | < < < > > | | | > > | > > > > | > > > > > > > > > > > > | > > > > > > > > | | | > | < > > > | | > > > | < > > | > > > | > > | > > < > > > | > | > | > | | < > | | > > | > > | > | | | | > > < > | | < | > > > > | > | > > | > > | > > > > | > | | < > | > > > | | | < < < < | > | | < < < < < | | > > | < > > | < > | | > > | > > | > | < < < < > | < > | | | < > | | | | | | < | < < > | > > | | | > > > > > > > > | > > | < | < < < > | < | | > | > > | | > | < < > > > > | > > | > > > > | | | > > > > > > | < > > > > | < | | | < | < | > > > | < > | > > > > > > | > | < < < < < < < > > | < < < > > > > > > | < < | < > | > | < < | > | < | < | < > > > | < > | > | < | > > | < | < > > | > | > > > > > | > > > | | | < < < > > | < | | > > | | > > > > > | > | | | | < < < < < < < | > | < | < < < | < | < < > | < > | > | > > > | | | | < < | < | | | | > | | > > > > > | > | | < > | | > | < | < < | < < | < < | < | < < | < < | < | | | < | | < | > | | > > > > > > > > > > > > > > > | | > | | > > | > | > | | < | | | > | | > > > > > | < < < < < < < | | < > > > | < > > > | < < | < < < | > > | < < | > | < < | < < < < < | > | < > > | > > | | > > > > | > > | > > > > | | | | < | > | | | | | | | | | > | < | | | < < < | | > > > | > > | > > > > > > | < > > > > > > > > | < > > | | > > > > | | | < | < < < > > | | > > > | | < < < < < < > > | < | | > > | > > > | > | | | | > | > > > > | > | | | < < < | < < < < | < < < | > | | > > > > > > > > | | < < < < | | | > | < | | > | < < | | | < | | > > | | > > < < < | | | > > > | > > > | < < < < < | < > | < < < < | > > > | > > | > > > | | | > > > > > > > > | > | | > | > > > > | > | > > | | < < < > > > | | | < | > > > | < | < < | | > | | < < < | | > > | > | > | | | < < | | < | < < < | | > > > > > > > > > > > > > > > > > > > | > | > > > > > > > > | | | > > | | > | | > > > | < > | > > | > | > | | | > > > > > > > > > > > | 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 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 934 935 936 937 938 939 940 941 942 943 944 945 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 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 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 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 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 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 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 1462 1463 1464 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 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 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 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 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 1860 1861 1862 1863 1864 1865 1866 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 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 |
*
* ------------------------------------------------------------------------
*
* References:
* - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
* - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
*
* Bloat:
* - Completion?
*
* Unix/termios
* ------------
* List of escape sequences used by this program, we do everything just
* a few sequences. In order to be so cheap we may have some
* flickering effect with some slow terminal, but the lesser sequences
* the more compatible.
*
* EL (Erase Line)
* Sequence: ESC [ 0 K
* Effect: clear from cursor to end of line
*
* CUF (CUrsor Forward)
* Sequence: ESC [ n C
* Effect: moves cursor forward n chars
*
* CR (Carriage Return)
* Sequence: \r
* Effect: moves cursor to column 1
*
* The following are used to clear the screen: ESC [ H ESC [ 2 J
* This is actually composed of two sequences:
*
* cursorhome
* Sequence: ESC [ H
* Effect: moves the cursor to upper left corner
*
* ED2 (Clear entire screen)
* Sequence: ESC [ 2 J
* Effect: clear the whole screen
*
* == For highlighting control characters, we also use the following two ==
* SO (enter StandOut)
* Sequence: ESC [ 7 m
* Effect: Uses some standout mode such as reverse video
*
* SE (Standout End)
* Sequence: ESC [ 0 m
* Effect: Exit standout mode
*
* == Only used if TIOCGWINSZ fails ==
* DSR/CPR (Report cursor position)
* Sequence: ESC [ 6 n
* Effect: reports current cursor position as ESC [ NNN ; MMM R
*
* == Only used in multiline mode ==
* CUU (Cursor Up)
* Sequence: ESC [ n A
* Effect: moves cursor up n chars.
*
* CUD (Cursor Down)
* Sequence: ESC [ n B
* Effect: moves cursor down n chars.
*
* win32/console
* -------------
* If __MINGW32__ is defined, the win32 console API is used.
* This could probably be made to work for the msvc compiler too.
* This support based in part on work by Jon Griffiths.
*/
#ifdef _WIN32 /* Windows platform, either MinGW or Visual Studio (MSVC) */
#include <windows.h>
#include <fcntl.h>
#define USE_WINCONSOLE
#ifdef __MINGW32__
#define HAVE_UNISTD_H
#endif
#else
#include <termios.h>
#include <sys/ioctl.h>
#include <poll.h>
#define USE_TERMIOS
#define HAVE_UNISTD_H
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#if defined(_WIN32) && !defined(__MINGW32__)
/* Microsoft headers don't like old POSIX names */
#define strdup _strdup
#define snprintf _snprintf
#endif
#include "linenoise.h"
#ifndef STRINGBUF_H
#include "stringbuf.h"
#endif
#ifndef UTF8_UTIL_H
#include "utf8.h"
#endif
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
/* ctrl('A') -> 0x01 */
#define ctrl(C) ((C) - '@')
/* meta('a') -> 0xe1 */
#define meta(C) ((C) | 0x80)
/* Use -ve numbers here to co-exist with normal unicode chars */
enum {
SPECIAL_NONE,
/* don't use -1 here since that indicates error */
SPECIAL_UP = -20,
SPECIAL_DOWN = -21,
SPECIAL_LEFT = -22,
SPECIAL_RIGHT = -23,
SPECIAL_DELETE = -24,
SPECIAL_HOME = -25,
SPECIAL_END = -26,
SPECIAL_INSERT = -27,
SPECIAL_PAGE_UP = -28,
SPECIAL_PAGE_DOWN = -29,
/* Some handy names for other special keycodes */
CHAR_ESCAPE = 27,
CHAR_DELETE = 127,
};
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
static int history_len = 0;
static int history_index = 0;
static char **history = NULL;
/* Structure to contain the status of the current (being edited) line */
struct current {
stringbuf *buf; /* Current buffer. Always null terminated */
int pos; /* Cursor position, measured in chars */
int cols; /* Size of the window, in chars */
int nrows; /* How many rows are being used in multiline mode (>= 1) */
int rpos; /* The current row containing the cursor - multiline mode only */
int colsright; /* refreshLine() cached cols for insert_char() optimisation */
int colsleft; /* refreshLine() cached cols for remove_char() optimisation */
const char *prompt;
stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */
stringbuf *output; /* used only during refreshLine() - output accumulator */
#if defined(USE_TERMIOS)
int fd; /* Terminal fd */
#elif defined(USE_WINCONSOLE)
HANDLE outh; /* Console output handle */
HANDLE inh; /* Console input handle */
int rows; /* Screen rows */
int x; /* Current column during output */
int y; /* Current row */
#ifdef USE_UTF8
#define UBUF_MAX_CHARS 132
WORD ubuf[UBUF_MAX_CHARS + 1]; /* Accumulates utf16 output - one extra for final surrogate pairs */
int ubuflen; /* length used in ubuf */
int ubufcols; /* how many columns are represented by the chars in ubuf? */
#endif
#endif
};
static int fd_read(struct current *current);
static int getWindowSize(struct current *current);
static void cursorDown(struct current *current, int n);
static void cursorUp(struct current *current, int n);
static void eraseEol(struct current *current);
static void refreshLine(struct current *current);
static void refreshLineAlt(struct current *current, const char *prompt, const char *buf, int cursor_pos);
static void setCursorPos(struct current *current, int x);
static void setOutputHighlight(struct current *current, const int *props, int nprops);
static void set_current(struct current *current, const char *str);
static int fd_isatty(struct current *current)
{
#ifdef USE_TERMIOS
return isatty(current->fd);
#else
(void)current;
return 0;
#endif
}
void linenoiseHistoryFree(void) {
if (history) {
int j;
for (j = 0; j < history_len; j++)
free(history[j]);
free(history);
history = NULL;
history_len = 0;
}
}
typedef enum {
EP_START, /* looking for ESC */
EP_ESC, /* looking for [ */
EP_DIGITS, /* parsing digits */
EP_PROPS, /* parsing digits or semicolons */
EP_END, /* ok */
EP_ERROR, /* error */
} ep_state_t;
struct esc_parser {
ep_state_t state;
int props[5]; /* properties are stored here */
int maxprops; /* size of the props[] array */
int numprops; /* number of properties found */
int termchar; /* terminator char, or 0 for any alpha */
int current; /* current (partial) property value */
};
/**
* Initialise the escape sequence parser at *parser.
*
* If termchar is 0 any alpha char terminates ok. Otherwise only the given
* char terminates successfully.
* Run the parser state machine with calls to parseEscapeSequence() for each char.
*/
static void initParseEscapeSeq(struct esc_parser *parser, int termchar)
{
parser->state = EP_START;
parser->maxprops = sizeof(parser->props) / sizeof(*parser->props);
parser->numprops = 0;
parser->current = 0;
parser->termchar = termchar;
}
/**
* Pass character 'ch' into the state machine to parse:
* 'ESC' '[' <digits> (';' <digits>)* <termchar>
*
* The first character must be ESC.
* Returns the current state. The state machine is done when it returns either EP_END
* or EP_ERROR.
*
* On EP_END, the "property/attribute" values can be read from parser->props[]
* of length parser->numprops.
*/
static int parseEscapeSequence(struct esc_parser *parser, int ch)
{
switch (parser->state) {
case EP_START:
parser->state = (ch == '\x1b') ? EP_ESC : EP_ERROR;
break;
case EP_ESC:
parser->state = (ch == '[') ? EP_DIGITS : EP_ERROR;
break;
case EP_PROPS:
if (ch == ';') {
parser->state = EP_DIGITS;
donedigits:
if (parser->numprops + 1 < parser->maxprops) {
parser->props[parser->numprops++] = parser->current;
parser->current = 0;
}
break;
}
/* fall through */
case EP_DIGITS:
if (ch >= '0' && ch <= '9') {
parser->current = parser->current * 10 + (ch - '0');
parser->state = EP_PROPS;
break;
}
/* must be terminator */
if (parser->termchar != ch) {
if (parser->termchar != 0 || !((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))) {
parser->state = EP_ERROR;
break;
}
}
parser->state = EP_END;
goto donedigits;
case EP_END:
parser->state = EP_ERROR;
break;
case EP_ERROR:
break;
}
return parser->state;
}
/*#define DEBUG_REFRESHLINE*/
#ifdef DEBUG_REFRESHLINE
#define DRL(ARGS...) fprintf(dfh, ARGS)
static FILE *dfh;
static void DRL_CHAR(int ch)
{
if (ch < ' ') {
DRL("^%c", ch + '@');
}
else if (ch > 127) {
DRL("\\u%04x", ch);
}
else {
DRL("%c", ch);
}
}
static void DRL_STR(const char *str)
{
while (*str) {
int ch;
int n = utf8_tounicode(str, &ch);
str += n;
DRL_CHAR(ch);
}
}
#else
#define DRL(...)
#define DRL_CHAR(ch)
#define DRL_STR(str)
#endif
#if defined(USE_WINCONSOLE)
#include "linenoise-win32.c"
#endif
#if defined(USE_TERMIOS)
static void linenoiseAtExit(void);
static struct termios orig_termios; /* in order to restore at exit */
static int rawmode = 0; /* for atexit() function to check if restore is needed*/
static int atexit_registered = 0; /* register atexit just 1 time */
static const char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
static int isUnsupportedTerm(void) {
char *term = getenv("TERM");
if (term) {
int j;
for (j = 0; unsupported_term[j]; j++) {
if (strcmp(term, unsupported_term[j]) == 0) {
return 1;
}
}
}
return 0;
}
static int enableRawMode(struct current *current) {
struct termios raw;
current->fd = STDIN_FILENO;
current->cols = 0;
if (!isatty(current->fd) || isUnsupportedTerm() ||
tcgetattr(current->fd, &orig_termios) == -1) {
fatal:
errno = ENOTTY;
return -1;
}
if (!atexit_registered) {
atexit(linenoiseAtExit);
atexit_registered = 1;
}
raw = orig_termios; /* modify the original mode */
/* input modes: no break, no CR to NL, no parity check, no strip char,
* no start/stop output control. */
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
/* output modes - actually, no need to disable post processing */
/*raw.c_oflag &= ~(OPOST);*/
/* control modes - set 8 bit chars */
raw.c_cflag |= (CS8);
/* local modes - choing off, canonical off, no extended functions,
* no signal chars (^Z,^C) */
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* control chars - set return condition: min number of bytes and timer.
* We want read to return every single byte, without timeout. */
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
/* put terminal in raw mode after flushing */
if (tcsetattr(current->fd,TCSADRAIN,&raw) < 0) {
goto fatal;
}
rawmode = 1;
return 0;
}
static void disableRawMode(struct current *current) {
/* Don't even check the return value as it's too late. */
if (rawmode && tcsetattr(current->fd,TCSADRAIN,&orig_termios) != -1)
rawmode = 0;
}
/* At exit we'll try to fix the terminal to the initial conditions. */
static void linenoiseAtExit(void) {
if (rawmode) {
tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
}
linenoiseHistoryFree();
}
/* gcc/glibc insists that we care about the return code of write!
* Clarification: This means that a void-cast like "(void) (EXPR)"
* does not work.
*/
#define IGNORE_RC(EXPR) if (EXPR) {}
/**
* Output bytes directly, or accumulate output (if current->output is set)
*/
static void outputChars(struct current *current, const char *buf, int len)
{
if (len < 0) {
len = strlen(buf);
}
if (current->output) {
sb_append_len(current->output, buf, len);
}
else {
IGNORE_RC(write(current->fd, buf, len));
}
}
/* Like outputChars, but using printf-style formatting
*/
static void outputFormatted(struct current *current, const char *format, ...)
{
va_list args;
char buf[64];
int n;
va_start(args, format);
n = vsnprintf(buf, sizeof(buf), format, args);
/* This will never happen because we are sure to use outputFormatted() only for short sequences */
assert(n < (int)sizeof(buf));
va_end(args);
outputChars(current, buf, n);
}
static void cursorToLeft(struct current *current)
{
outputChars(current, "\r", -1);
}
static void setOutputHighlight(struct current *current, const int *props, int nprops)
{
outputChars(current, "\x1b[", -1);
while (nprops--) {
outputFormatted(current, "%d%c", *props, (nprops == 0) ? 'm' : ';');
props++;
}
}
static void eraseEol(struct current *current)
{
outputChars(current, "\x1b[0K", -1);
}
static void setCursorPos(struct current *current, int x)
{
if (x == 0) {
cursorToLeft(current);
}
else {
outputFormatted(current, "\r\x1b[%dC", x);
}
}
static void cursorUp(struct current *current, int n)
{
if (n) {
outputFormatted(current, "\x1b[%dA", n);
}
}
static void cursorDown(struct current *current, int n)
{
if (n) {
outputFormatted(current, "\x1b[%dB", n);
}
}
void linenoiseClearScreen(void)
{
IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
}
/**
* Reads a char from 'fd', waiting at most 'timeout' milliseconds.
*
* A timeout of -1 means to wait forever.
*
* Returns -1 if no char is received within the time or an error occurs.
*/
static int fd_read_char(int fd, int timeout)
{
struct pollfd p;
unsigned char c;
p.fd = fd;
p.events = POLLIN;
if (poll(&p, 1, timeout) == 0) {
/* timeout */
return -1;
}
if (read(fd, &c, 1) != 1) {
return -1;
}
return c;
}
/**
* Reads a complete utf-8 character
* and returns the unicode value, or -1 on error.
*/
static int fd_read(struct current *current)
{
#ifdef USE_UTF8
char buf[MAX_UTF8_LEN];
int n;
int i;
int c;
if (read(current->fd, &buf[0], 1) != 1) {
return -1;
}
n = utf8_charlen(buf[0]);
if (n < 1) {
return -1;
}
for (i = 1; i < n; i++) {
if (read(current->fd, &buf[i], 1) != 1) {
return -1;
}
}
/* decode and return the character */
utf8_tounicode(buf, &c);
return c;
#else
return fd_read_char(current->fd, -1);
#endif
}
/**
* Stores the current cursor column in '*cols'.
* Returns 1 if OK, or 0 if failed to determine cursor pos.
*/
static int queryCursor(struct current *current, int* cols)
{
struct esc_parser parser;
int ch;
/* Should not be buffering this output, it needs to go immediately */
assert(current->output == NULL);
/* control sequence - report cursor location */
outputChars(current, "\x1b[6n", -1);
/* Parse the response: ESC [ rows ; cols R */
initParseEscapeSeq(&parser, 'R');
while ((ch = fd_read_char(current->fd, 100)) > 0) {
switch (parseEscapeSequence(&parser, ch)) {
default:
continue;
case EP_END:
if (parser.numprops == 2 && parser.props[1] < 1000) {
*cols = parser.props[1];
return 1;
}
break;
case EP_ERROR:
break;
}
/* failed */
break;
}
return 0;
}
/**
* Updates current->cols with the current window size (width)
*/
static int getWindowSize(struct current *current)
{
struct winsize ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0) {
current->cols = ws.ws_col;
return 0;
}
/* Failed to query the window size. Perhaps we are on a serial terminal.
* Try to query the width by sending the cursor as far to the right
* and reading back the cursor position.
* Note that this is only done once per call to linenoise rather than
* every time the line is refreshed for efficiency reasons.
*
* In more detail, we:
* (a) request current cursor position,
* (b) move cursor far right,
* (c) request cursor position again,
* (d) at last move back to the old position.
* This gives us the width without messing with the externally
* visible cursor position.
*/
if (current->cols == 0) {
int here;
/* If anything fails => default 80 */
current->cols = 80;
/* (a) */
if (queryCursor (current, &here)) {
/* (b) */
setCursorPos(current, 999);
/* (c). Note: If (a) succeeded, then (c) should as well.
* For paranoia we still check and have a fallback action
* for (d) in case of failure..
*/
if (queryCursor (current, ¤t->cols)) {
/* (d) Reset the cursor back to the original location. */
if (current->cols > here) {
setCursorPos(current, here);
}
}
}
}
return 0;
}
/**
* If CHAR_ESCAPE was received, reads subsequent
* chars to determine if this is a known special key.
*
* Returns SPECIAL_NONE if unrecognised, or -1 if EOF.
*
* If no additional char is received within a short time,
* CHAR_ESCAPE is returned.
*/
static int check_special(int fd)
{
int c = fd_read_char(fd, 50);
int c2;
if (c < 0) {
return CHAR_ESCAPE;
}
else if (c >= 'a' && c <= 'z') {
/* esc-a => meta-a */
return meta(c);
}
c2 = fd_read_char(fd, 50);
if (c2 < 0) {
return c2;
}
if (c == '[' || c == 'O') {
/* Potential arrow key */
switch (c2) {
case 'A':
return SPECIAL_UP;
case 'B':
return SPECIAL_DOWN;
case 'C':
return SPECIAL_RIGHT;
case 'D':
return SPECIAL_LEFT;
case 'F':
return SPECIAL_END;
case 'H':
return SPECIAL_HOME;
}
}
if (c == '[' && c2 >= '1' && c2 <= '8') {
/* extended escape */
c = fd_read_char(fd, 50);
if (c == '~') {
switch (c2) {
case '2':
return SPECIAL_INSERT;
case '3':
return SPECIAL_DELETE;
case '5':
return SPECIAL_PAGE_UP;
case '6':
return SPECIAL_PAGE_DOWN;
case '7':
return SPECIAL_HOME;
case '8':
return SPECIAL_END;
}
}
while (c != -1 && c != '~') {
/* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */
c = fd_read_char(fd, 50);
}
}
return SPECIAL_NONE;
}
#endif
static void clearOutputHighlight(struct current *current)
{
int nohighlight = 0;
setOutputHighlight(current, &nohighlight, 1);
}
static void outputControlChar(struct current *current, char ch)
{
int reverse = 7;
setOutputHighlight(current, &reverse, 1);
outputChars(current, "^", 1);
outputChars(current, &ch, 1);
clearOutputHighlight(current);
}
#ifndef utf8_getchars
static int utf8_getchars(char *buf, int c)
{
#ifdef USE_UTF8
return utf8_fromunicode(buf, c);
#else
*buf = c;
return 1;
#endif
}
#endif
/**
* Returns the unicode character at the given offset,
* or -1 if none.
*/
static int get_char(struct current *current, int pos)
{
if (pos >= 0 && pos < sb_chars(current->buf)) {
int c;
int i = utf8_index(sb_str(current->buf), pos);
(void)utf8_tounicode(sb_str(current->buf) + i, &c);
return c;
}
return -1;
}
static int char_display_width(int ch)
{
if (ch < ' ') {
/* control chars take two positions */
return 2;
}
else {
return utf8_width(ch);
}
}
#ifndef NO_COMPLETION
static linenoiseCompletionCallback *completionCallback = NULL;
static void *completionUserdata = NULL;
static int showhints = 1;
static linenoiseHintsCallback *hintsCallback = NULL;
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
static void *hintsUserdata = NULL;
static void beep(void) {
#ifdef USE_TERMIOS
fprintf(stderr, "\x7");
fflush(stderr);
#endif
}
static void freeCompletions(linenoiseCompletions *lc) {
size_t i;
for (i = 0; i < lc->len; i++)
free(lc->cvec[i]);
free(lc->cvec);
}
static int completeLine(struct current *current) {
linenoiseCompletions lc = { 0, NULL };
int c = 0;
completionCallback(sb_str(current->buf),&lc,completionUserdata);
if (lc.len == 0) {
beep();
} else {
size_t stop = 0, i = 0;
while(!stop) {
/* Show completion or original buffer */
if (i < lc.len) {
int chars = utf8_strlen(lc.cvec[i], -1);
refreshLineAlt(current, current->prompt, lc.cvec[i], chars);
} else {
refreshLine(current);
}
c = fd_read(current);
if (c == -1) {
break;
}
switch(c) {
case '\t': /* tab */
i = (i+1) % (lc.len+1);
if (i == lc.len) beep();
break;
case CHAR_ESCAPE: /* escape */
/* Re-show original buffer */
if (i < lc.len) {
refreshLine(current);
}
stop = 1;
break;
default:
/* Update buffer and return */
if (i < lc.len) {
set_current(current,lc.cvec[i]);
}
stop = 1;
break;
}
}
}
freeCompletions(&lc);
return c; /* Return last read character */
}
/* Register a callback function to be called for tab-completion.
Returns the prior callback so that the caller may (if needed)
restore it when done. */
linenoiseCompletionCallback * linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn, void *userdata) {
linenoiseCompletionCallback * old = completionCallback;
completionCallback = fn;
completionUserdata = userdata;
return old;
}
void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) {
lc->cvec = (char **)realloc(lc->cvec,sizeof(char*)*(lc->len+1));
lc->cvec[lc->len++] = strdup(str);
}
void linenoiseSetHintsCallback(linenoiseHintsCallback *callback, void *userdata)
{
hintsCallback = callback;
hintsUserdata = userdata;
}
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *callback)
{
freeHintsCallback = callback;
}
#endif
static const char *reduceSingleBuf(const char *buf, int availcols, int *cursor_pos)
{
/* We have availcols columns available.
* If necessary, strip chars off the front of buf until *cursor_pos
* fits within availcols
*/
int needcols = 0;
int pos = 0;
int new_cursor_pos = *cursor_pos;
const char *pt = buf;
DRL("reduceSingleBuf: availcols=%d, cursor_pos=%d\n", availcols, *cursor_pos);
while (*pt) {
int ch;
int n = utf8_tounicode(pt, &ch);
pt += n;
needcols += char_display_width(ch);
/* If we need too many cols, strip
* chars off the front of buf to make it fit.
* We keep 3 extra cols to the right of the cursor.
* 2 for possible wide chars, 1 for the last column that
* can't be used.
*/
while (needcols >= availcols - 3) {
n = utf8_tounicode(buf, &ch);
buf += n;
needcols -= char_display_width(ch);
DRL_CHAR(ch);
/* and adjust the apparent cursor position */
new_cursor_pos--;
if (buf == pt) {
/* can't remove more than this */
break;
}
}
if (pos++ == *cursor_pos) {
break;
}
}
DRL("<snip>");
DRL_STR(buf);
DRL("\nafter reduce, needcols=%d, new_cursor_pos=%d\n", needcols, new_cursor_pos);
/* Done, now new_cursor_pos contains the adjusted cursor position
* and buf points to he adjusted start
*/
*cursor_pos = new_cursor_pos;
return buf;
}
static int mlmode = 0;
void linenoiseSetMultiLine(int enableml)
{
mlmode = enableml;
}
/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
* to the right of the prompt.
* Returns 1 if a hint was shown, or 0 if not
* If 'display' is 0, does no output. Just returns the appropriate return code.
*/
static int refreshShowHints(struct current *current, const char *buf, int availcols, int display)
{
int rc = 0;
if (showhints && hintsCallback && availcols > 0) {
int bold = 0;
int color = -1;
char *hint = hintsCallback(buf, &color, &bold, hintsUserdata);
if (hint) {
rc = 1;
if (display) {
const char *pt;
if (bold == 1 && color == -1) color = 37;
if (bold || color > 0) {
int props[3] = { bold, color, 49 }; /* bold, color, fgnormal */
setOutputHighlight(current, props, 3);
}
DRL("<hint bold=%d,color=%d>", bold, color);
pt = hint;
while (*pt) {
int ch;
int n = utf8_tounicode(pt, &ch);
int width = char_display_width(ch);
if (width >= availcols) {
DRL("<hinteol>");
break;
}
DRL_CHAR(ch);
availcols -= width;
outputChars(current, pt, n);
pt += n;
}
if (bold || color > 0) {
clearOutputHighlight(current);
}
/* Call the function to free the hint returned. */
if (freeHintsCallback) freeHintsCallback(hint, hintsUserdata);
}
}
}
return rc;
}
#ifdef USE_TERMIOS
static void refreshStart(struct current *current)
{
/* We accumulate all output here */
assert(current->output == NULL);
current->output = sb_alloc();
}
static void refreshEnd(struct current *current)
{
/* Output everything at once */
IGNORE_RC(write(current->fd, sb_str(current->output), sb_len(current->output)));
sb_free(current->output);
current->output = NULL;
}
static void refreshStartChars(struct current *current)
{
(void)current;
}
static void refreshNewline(struct current *current)
{
DRL("<nl>");
outputChars(current, "\n", 1);
}
static void refreshEndChars(struct current *current)
{
(void)current;
}
#endif
static void refreshLineAlt(struct current *current, const char *prompt, const char *buf, int cursor_pos)
{
int i;
const char *pt;
int displaycol;
int displayrow;
int visible;
int currentpos;
int notecursor;
int cursorcol = 0;
int cursorrow = 0;
int hint;
struct esc_parser parser;
#ifdef DEBUG_REFRESHLINE
dfh = fopen("linenoise.debuglog", "a");
#endif
/* Should intercept SIGWINCH. For now, just get the size every time */
getWindowSize(current);
refreshStart(current);
DRL("wincols=%d, cursor_pos=%d, nrows=%d, rpos=%d\n", current->cols, cursor_pos, current->nrows, current->rpos);
/* Here is the plan:
* (a) move the the bottom row, going down the appropriate number of lines
* (b) move to beginning of line and erase the current line
* (c) go up one line and do the same, until we have erased up to the first row
* (d) output the prompt, counting cols and rows, taking into account escape sequences
* (e) output the buffer, counting cols and rows
* (e') when we hit the current pos, save the cursor position
* (f) move the cursor to the saved cursor position
* (g) save the current cursor row and number of rows
*/
/* (a) - The cursor is currently at row rpos */
cursorDown(current, current->nrows - current->rpos - 1);
DRL("<cud=%d>", current->nrows - current->rpos - 1);
/* (b), (c) - Erase lines upwards until we get to the first row */
for (i = 0; i < current->nrows; i++) {
if (i) {
DRL("<cup>");
cursorUp(current, 1);
}
DRL("<clearline>");
cursorToLeft(current);
eraseEol(current);
}
DRL("\n");
/* (d) First output the prompt. control sequences don't take up display space */
pt = prompt;
displaycol = 0; /* current display column */
displayrow = 0; /* current display row */
visible = 1;
refreshStartChars(current);
while (*pt) {
int width;
int ch;
int n = utf8_tounicode(pt, &ch);
if (visible && ch == CHAR_ESCAPE) {
/* The start of an escape sequence, so not visible */
visible = 0;
initParseEscapeSeq(&parser, 'm');
DRL("<esc-seq-start>");
}
if (ch == '\n' || ch == '\r') {
/* treat both CR and NL the same and force wrap */
refreshNewline(current);
displaycol = 0;
displayrow++;
}
else {
width = visible * utf8_width(ch);
displaycol += width;
if (displaycol >= current->cols) {
/* need to wrap to the next line because of newline or if it doesn't fit
* XXX this is a problem in single line mode
*/
refreshNewline(current);
displaycol = width;
displayrow++;
}
DRL_CHAR(ch);
#ifdef USE_WINCONSOLE
if (visible) {
outputChars(current, pt, n);
}
#else
outputChars(current, pt, n);
#endif
}
pt += n;
if (!visible) {
switch (parseEscapeSequence(&parser, ch)) {
case EP_END:
visible = 1;
setOutputHighlight(current, parser.props, parser.numprops);
DRL("<esc-seq-end,numprops=%d>", parser.numprops);
break;
case EP_ERROR:
DRL("<esc-seq-err>");
visible = 1;
break;
}
}
}
/* Now we are at the first line with all lines erased */
DRL("\nafter prompt: displaycol=%d, displayrow=%d\n", displaycol, displayrow);
/* (e) output the buffer, counting cols and rows */
if (mlmode == 0) {
/* In this mode we may need to trim chars from the start of the buffer until the
* cursor fits in the window.
*/
pt = reduceSingleBuf(buf, current->cols - displaycol, &cursor_pos);
}
else {
pt = buf;
}
currentpos = 0;
notecursor = -1;
while (*pt) {
int ch;
int n = utf8_tounicode(pt, &ch);
int width = char_display_width(ch);
if (currentpos == cursor_pos) {
/* (e') wherever we output this character is where we want the cursor */
notecursor = 1;
}
if (displaycol + width >= current->cols) {
if (mlmode == 0) {
/* In single line mode stop once we print as much as we can on one line */
DRL("<slmode>");
break;
}
/* need to wrap to the next line since it doesn't fit */
refreshNewline(current);
displaycol = 0;
displayrow++;
}
if (notecursor == 1) {
/* (e') Save this position as the current cursor position */
cursorcol = displaycol;
cursorrow = displayrow;
notecursor = 0;
DRL("<cursor>");
}
displaycol += width;
if (ch < ' ') {
outputControlChar(current, ch + '@');
}
else {
outputChars(current, pt, n);
}
DRL_CHAR(ch);
if (width != 1) {
DRL("<w=%d>", width);
}
pt += n;
currentpos++;
}
/* If we didn't see the cursor, it is at the current location */
if (notecursor) {
DRL("<cursor>");
cursorcol = displaycol;
cursorrow = displayrow;
}
DRL("\nafter buf: displaycol=%d, displayrow=%d, cursorcol=%d, cursorrow=%d\n", displaycol, displayrow, cursorcol, cursorrow);
/* (f) show hints */
hint = refreshShowHints(current, buf, current->cols - displaycol, 1);
/* Remember how many many cols are available for insert optimisation */
if (prompt == current->prompt && hint == 0) {
current->colsright = current->cols - displaycol;
current->colsleft = displaycol;
}
else {
/* Can't optimise */
current->colsright = 0;
current->colsleft = 0;
}
DRL("\nafter hints: colsleft=%d, colsright=%d\n\n", current->colsleft, current->colsright);
refreshEndChars(current);
/* (g) move the cursor to the correct place */
cursorUp(current, displayrow - cursorrow);
setCursorPos(current, cursorcol);
/* (h) Update the number of rows if larger, but never reduce this */
if (displayrow >= current->nrows) {
current->nrows = displayrow + 1;
}
/* And remember the row that the cursor is on */
current->rpos = cursorrow;
refreshEnd(current);
#ifdef DEBUG_REFRESHLINE
fclose(dfh);
#endif
}
static void refreshLine(struct current *current)
{
refreshLineAlt(current, current->prompt, sb_str(current->buf), current->pos);
}
static void set_current(struct current *current, const char *str)
{
sb_clear(current->buf);
sb_append(current->buf, str);
current->pos = sb_chars(current->buf);
}
/**
* Removes the char at 'pos'.
*
* Returns 1 if the line needs to be refreshed, 2 if not
* and 0 if nothing was removed
*/
static int remove_char(struct current *current, int pos)
{
if (pos >= 0 && pos < sb_chars(current->buf)) {
int offset = utf8_index(sb_str(current->buf), pos);
int nbytes = utf8_index(sb_str(current->buf) + offset, 1);
int rc = 1;
/* Now we try to optimise in the simple but very common case that:
* - outputChars() can be used directly (not win32)
* - we are removing the char at EOL
* - the buffer is not empty
* - there are columns available to the left
* - the char being deleted is not a wide or utf-8 character
* - no hints are being shown
*/
if (current->output && current->pos == pos + 1 && current->pos == sb_chars(current->buf) && pos > 0) {
#ifdef USE_UTF8
/* Could implement utf8_prev_len() but simplest just to not optimise this case */
char last = sb_str(current->buf)[offset];
#else
char last = 0;
#endif
if (current->colsleft > 0 && (last & 0x80) == 0) {
/* Have cols on the left and not a UTF-8 char or continuation */
/* Yes, can optimise */
current->colsleft--;
current->colsright++;
rc = 2;
}
}
sb_delete(current->buf, offset, nbytes);
if (current->pos > pos) {
current->pos--;
}
if (rc == 2) {
if (refreshShowHints(current, sb_str(current->buf), current->colsright, 0)) {
/* A hint needs to be shown, so can't optimise after all */
rc = 1;
}
else {
/* optimised output */
outputChars(current, "\b \b", 3);
}
}
return rc;
return 1;
}
return 0;
}
/**
* Insert 'ch' at position 'pos'
*
* Returns 1 if the line needs to be refreshed, 2 if not
* and 0 if nothing was inserted (no room)
*/
static int insert_char(struct current *current, int pos, int ch)
{
if (pos >= 0 && pos <= sb_chars(current->buf)) {
char buf[MAX_UTF8_LEN + 1];
int offset = utf8_index(sb_str(current->buf), pos);
int n = utf8_getchars(buf, ch);
int rc = 1;
/* null terminate since sb_insert() requires it */
buf[n] = 0;
/* Now we try to optimise in the simple but very common case that:
* - outputChars() can be used directly (not win32)
* - we are inserting at EOL
* - there are enough columns available
* - no hints are being shown
*/
if (current->output && pos == current->pos && pos == sb_chars(current->buf)) {
int width = char_display_width(ch);
if (current->colsright > width) {
/* Yes, can optimise */
current->colsright -= width;
current->colsleft -= width;
rc = 2;
}
}
sb_insert(current->buf, offset, buf);
if (current->pos >= pos) {
current->pos++;
}
if (rc == 2) {
if (refreshShowHints(current, sb_str(current->buf), current->colsright, 0)) {
/* A hint needs to be shown, so can't optimise after all */
rc = 1;
}
else {
/* optimised output */
outputChars(current, buf, n);
}
}
return rc;
}
return 0;
}
/**
* Captures up to 'n' characters starting at 'pos' for the cut buffer.
*
* This replaces any existing characters in the cut buffer.
*/
static void capture_chars(struct current *current, int pos, int nchars)
{
if (pos >= 0 && (pos + nchars - 1) < sb_chars(current->buf)) {
int offset = utf8_index(sb_str(current->buf), pos);
int nbytes = utf8_index(sb_str(current->buf) + offset, nchars);
if (nbytes > 0) {
if (current->capture) {
sb_clear(current->capture);
}
else {
current->capture = sb_alloc();
}
sb_append_len(current->capture, sb_str(current->buf) + offset, nbytes);
}
}
}
/**
* Removes up to 'n' characters at cursor position 'pos'.
*
* Returns 0 if no chars were removed or non-zero otherwise.
*/
static int remove_chars(struct current *current, int pos, int n)
{
int removed = 0;
/* First save any chars which will be removed */
capture_chars(current, pos, n);
while (n-- && remove_char(current, pos)) {
removed++;
}
return removed;
}
/**
* Inserts the characters (string) 'chars' at the cursor position 'pos'.
*
* Returns 0 if no chars were inserted or non-zero otherwise.
*/
static int insert_chars(struct current *current, int pos, const char *chars)
{
int inserted = 0;
while (*chars) {
int ch;
int n = utf8_tounicode(chars, &ch);
if (insert_char(current, pos, ch) == 0) {
break;
}
inserted++;
pos++;
chars += n;
}
return inserted;
}
static int skip_space_nonspace(struct current *current, int dir, int check_is_space)
{
int moved = 0;
int checkoffset = (dir < 0) ? -1 : 0;
int limit = (dir < 0) ? 0 : sb_chars(current->buf);
while (current->pos != limit && (get_char(current, current->pos + checkoffset) == ' ') == check_is_space) {
current->pos += dir;
moved++;
}
return moved;
}
static int skip_space(struct current *current, int dir)
{
return skip_space_nonspace(current, dir, 1);
}
static int skip_nonspace(struct current *current, int dir)
{
return skip_space_nonspace(current, dir, 0);
}
static void set_history_index(struct current *current, int new_index)
{
if (history_len > 1) {
/* Update the current history entry before to
* overwrite it with the next one. */
free(history[history_len - 1 - history_index]);
history[history_len - 1 - history_index] = strdup(sb_str(current->buf));
/* Show the new entry */
history_index = new_index;
if (history_index < 0) {
history_index = 0;
} else if (history_index >= history_len) {
history_index = history_len - 1;
} else {
set_current(current, history[history_len - 1 - history_index]);
refreshLine(current);
}
}
}
/**
* Returns the keycode to process, or 0 if none.
*/
static int reverseIncrementalSearch(struct current *current)
{
/* Display the reverse-i-search prompt and process chars */
char rbuf[50];
char rprompt[80];
int rchars = 0;
int rlen = 0;
int searchpos = history_len - 1;
int c;
rbuf[0] = 0;
while (1) {
int n = 0;
const char *p = NULL;
int skipsame = 0;
int searchdir = -1;
snprintf(rprompt, sizeof(rprompt), "(reverse-i-search)'%s': ", rbuf);
refreshLineAlt(current, rprompt, sb_str(current->buf), current->pos);
c = fd_read(current);
if (c == ctrl('H') || c == CHAR_DELETE) {
if (rchars) {
int p_ind = utf8_index(rbuf, --rchars);
rbuf[p_ind] = 0;
rlen = strlen(rbuf);
}
continue;
}
#ifdef USE_TERMIOS
if (c == CHAR_ESCAPE) {
c = check_special(current->fd);
}
#endif
if (c == ctrl('R')) {
/* Search for the previous (earlier) match */
if (searchpos > 0) {
searchpos--;
}
skipsame = 1;
}
else if (c == ctrl('S')) {
/* Search for the next (later) match */
if (searchpos < history_len) {
searchpos++;
}
searchdir = 1;
skipsame = 1;
}
else if (c == ctrl('P') || c == SPECIAL_UP) {
/* Exit Ctrl-R mode and go to the previous history line from the current search pos */
set_history_index(current, history_len - searchpos);
c = 0;
break;
}
else if (c == ctrl('N') || c == SPECIAL_DOWN) {
/* Exit Ctrl-R mode and go to the next history line from the current search pos */
set_history_index(current, history_len - searchpos - 2);
c = 0;
break;
}
else if (c >= ' ' && c <= '~') {
/* >= here to allow for null terminator */
if (rlen >= (int)sizeof(rbuf) - MAX_UTF8_LEN) {
continue;
}
n = utf8_getchars(rbuf + rlen, c);
rlen += n;
rchars++;
rbuf[rlen] = 0;
/* Adding a new char resets the search location */
searchpos = history_len - 1;
}
else {
/* Exit from incremental search mode */
break;
}
/* Now search through the history for a match */
for (; searchpos >= 0 && searchpos < history_len; searchpos += searchdir) {
p = strstr(history[searchpos], rbuf);
if (p) {
/* Found a match */
if (skipsame && strcmp(history[searchpos], sb_str(current->buf)) == 0) {
/* But it is identical, so skip it */
continue;
}
/* Copy the matching line and set the cursor position */
history_index = history_len - 1 - searchpos;
set_current(current,history[searchpos]);
current->pos = utf8_strlen(history[searchpos], p - history[searchpos]);
break;
}
}
if (!p && n) {
/* No match, so don't add it */
rchars--;
rlen -= n;
rbuf[rlen] = 0;
}
}
if (c == ctrl('G') || c == ctrl('C')) {
/* ctrl-g terminates the search with no effect */
set_current(current, "");
history_index = 0;
c = 0;
}
else if (c == ctrl('J')) {
/* ctrl-j terminates the search leaving the buffer in place */
history_index = 0;
c = 0;
}
/* Go process the char normally */
refreshLine(current);
return c;
}
static int linenoiseEdit(struct current *current) {
history_index = 0;
refreshLine(current);
while(1) {
int c = fd_read(current);
#ifndef NO_COMPLETION
/* Only autocomplete when the callback is set. It returns < 0 when
* there was an error reading from fd. Otherwise it will return the
* character that should be handled next. */
if (c == '\t' && current->pos == sb_chars(current->buf) && completionCallback != NULL) {
c = completeLine(current);
}
#endif
if (c == ctrl('R')) {
/* reverse incremental search will provide an alternative keycode or 0 for none */
c = reverseIncrementalSearch(current);
/* go on to process the returned char normally */
}
#ifdef USE_TERMIOS
if (c == CHAR_ESCAPE) { /* escape sequence */
c = check_special(current->fd);
}
#endif
if (c == -1) {
/* Return on errors */
return sb_len(current->buf);
}
switch(c) {
case SPECIAL_NONE:
break;
case '\r': /* enter/CR */
case '\n': /* LF */
history_len--;
free(history[history_len]);
current->pos = sb_chars(current->buf);
if (mlmode || hintsCallback) {
showhints = 0;
refreshLine(current);
showhints = 1;
}
return sb_len(current->buf);
case ctrl('C'): /* ctrl-c */
errno = EAGAIN;
return -1;
case ctrl('Z'): /* ctrl-z */
#ifdef SIGTSTP
/* send ourselves SIGSUSP */
disableRawMode(current);
raise(SIGTSTP);
/* and resume */
enableRawMode(current);
refreshLine(current);
#endif
continue;
case CHAR_DELETE: /* backspace */
case ctrl('H'):
if (remove_char(current, current->pos - 1) == 1) {
refreshLine(current);
}
break;
case ctrl('D'): /* ctrl-d */
if (sb_len(current->buf) == 0) {
/* Empty line, so EOF */
history_len--;
free(history[history_len]);
return -1;
}
/* Otherwise fall through to delete char to right of cursor */
/* fall-thru */
case SPECIAL_DELETE:
if (remove_char(current, current->pos) == 1) {
refreshLine(current);
}
break;
case SPECIAL_INSERT:
/* Ignore. Expansion Hook.
* Future possibility: Toggle Insert/Overwrite Modes
*/
break;
case meta('b'): /* meta-b, move word left */
if (skip_nonspace(current, -1)) {
refreshLine(current);
}
else if (skip_space(current, -1)) {
skip_nonspace(current, -1);
refreshLine(current);
}
break;
case meta('f'): /* meta-f, move word right */
if (skip_space(current, 1)) {
refreshLine(current);
}
else if (skip_nonspace(current, 1)) {
skip_space(current, 1);
refreshLine(current);
}
break;
case ctrl('W'): /* ctrl-w, delete word at left. save deleted chars */
/* eat any spaces on the left */
{
int pos = current->pos;
while (pos > 0 && get_char(current, pos - 1) == ' ') {
pos--;
}
/* now eat any non-spaces on the left */
while (pos > 0 && get_char(current, pos - 1) != ' ') {
pos--;
}
if (remove_chars(current, pos, current->pos - pos)) {
refreshLine(current);
}
}
break;
case ctrl('T'): /* ctrl-t */
if (current->pos > 0 && current->pos <= sb_chars(current->buf)) {
/* If cursor is at end, transpose the previous two chars */
int fixer = (current->pos == sb_chars(current->buf));
c = get_char(current, current->pos - fixer);
remove_char(current, current->pos - fixer);
insert_char(current, current->pos - 1, c);
refreshLine(current);
}
break;
case ctrl('V'): /* ctrl-v */
/* Insert the ^V first */
if (insert_char(current, current->pos, c)) {
refreshLine(current);
/* Now wait for the next char. Can insert anything except \0 */
c = fd_read(current);
/* Remove the ^V first */
remove_char(current, current->pos - 1);
if (c > 0) {
/* Insert the actual char, can't be error or null */
insert_char(current, current->pos, c);
}
refreshLine(current);
}
break;
case ctrl('B'):
case SPECIAL_LEFT:
if (current->pos > 0) {
current->pos--;
refreshLine(current);
}
break;
case ctrl('F'):
case SPECIAL_RIGHT:
if (current->pos < sb_chars(current->buf)) {
current->pos++;
refreshLine(current);
}
break;
case SPECIAL_PAGE_UP: /* move to start of history */
set_history_index(current, history_len - 1);
break;
case SPECIAL_PAGE_DOWN: /* move to 0 == end of history, i.e. current */
set_history_index(current, 0);
break;
case ctrl('P'):
case SPECIAL_UP:
set_history_index(current, history_index + 1);
break;
case ctrl('N'):
case SPECIAL_DOWN:
set_history_index(current, history_index - 1);
break;
case ctrl('A'): /* Ctrl+a, go to the start of the line */
case SPECIAL_HOME:
current->pos = 0;
refreshLine(current);
break;
case ctrl('E'): /* ctrl+e, go to the end of the line */
case SPECIAL_END:
current->pos = sb_chars(current->buf);
refreshLine(current);
break;
case ctrl('U'): /* Ctrl+u, delete to beginning of line, save deleted chars. */
if (remove_chars(current, 0, current->pos)) {
refreshLine(current);
}
break;
case ctrl('K'): /* Ctrl+k, delete from current to end of line, save deleted chars. */
if (remove_chars(current, current->pos, sb_chars(current->buf) - current->pos)) {
refreshLine(current);
}
break;
case ctrl('Y'): /* Ctrl+y, insert saved chars at current position */
if (current->capture && insert_chars(current, current->pos, sb_str(current->capture))) {
refreshLine(current);
}
break;
case ctrl('L'): /* Ctrl+L, clear screen */
linenoiseClearScreen();
/* Force recalc of window size for serial terminals */
current->cols = 0;
current->rpos = 0;
refreshLine(current);
break;
default:
if (c >= meta('a') && c <= meta('z')) {
/* Don't insert meta chars that are not bound */
break;
}
/* Only tab is allowed without ^V */
if (c == '\t' || c >= ' ') {
if (insert_char(current, current->pos, c) == 1) {
refreshLine(current);
}
}
break;
}
}
return sb_len(current->buf);
}
int linenoiseColumns(void)
{
struct current current;
current.output = NULL;
enableRawMode (¤t);
getWindowSize (¤t);
disableRawMode (¤t);
return current.cols;
}
/**
* Reads a line from the file handle (without the trailing NL or CRNL)
* and returns it in a stringbuf.
* Returns NULL if no characters are read before EOF or error.
*
* Note that the character count will *not* be correct for lines containing
* utf8 sequences. Do not rely on the character count.
*/
static stringbuf *sb_getline(FILE *fh)
{
stringbuf *sb = sb_alloc();
int c;
int n = 0;
while ((c = getc(fh)) != EOF) {
char ch;
n++;
if (c == '\r') {
/* CRLF -> LF */
continue;
}
if (c == '\n' || c == '\r') {
break;
}
ch = c;
/* ignore the effect of character count for partial utf8 sequences */
sb_append_len(sb, &ch, 1);
}
if (n == 0 || sb->data == NULL) {
sb_free(sb);
return NULL;
}
return sb;
}
char *linenoiseWithInitial(const char *prompt, const char *initial)
{
int count;
struct current current;
stringbuf *sb;
memset(¤t, 0, sizeof(current));
if (enableRawMode(¤t) == -1) {
printf("%s", prompt);
fflush(stdout);
sb = sb_getline(stdin);
if (sb && !fd_isatty(¤t)) {
printf("%s\n", sb_str(sb));
fflush(stdout);
}
}
else {
current.buf = sb_alloc();
current.pos = 0;
current.nrows = 1;
current.prompt = prompt;
/* The latest history entry is always our current buffer */
linenoiseHistoryAdd(initial);
set_current(¤t, initial);
count = linenoiseEdit(¤t);
disableRawMode(¤t);
printf("\n");
sb_free(current.capture);
if (count == -1) {
sb_free(current.buf);
return NULL;
}
sb = current.buf;
}
return sb ? sb_to_string(sb) : NULL;
}
char *linenoise(const char *prompt)
{
return linenoiseWithInitial(prompt, "");
}
/* Using a circular buffer is smarter, but a bit more complex to handle. */
static int linenoiseHistoryAddAllocated(char *line) {
if (history_max_len == 0) {
notinserted:
free(line);
return 0;
}
if (history == NULL) {
history = (char **)calloc(sizeof(char*), history_max_len);
}
/* do not insert duplicate lines into history */
if (history_len > 0 && strcmp(line, history[history_len - 1]) == 0) {
goto notinserted;
}
if (history_len == history_max_len) {
free(history[0]);
memmove(history,history+1,sizeof(char*)*(history_max_len-1));
history_len--;
}
history[history_len] = line;
history_len++;
return 1;
}
int linenoiseHistoryAdd(const char *line) {
return linenoiseHistoryAddAllocated(strdup(line));
}
int linenoiseHistoryGetMaxLen(void) {
return history_max_len;
}
int linenoiseHistorySetMaxLen(int len) {
char **newHistory;
if (len < 1) return 0;
if (history) {
int tocopy = history_len;
newHistory = (char **)calloc(sizeof(char*), len);
/* If we can't copy everything, free the elements we'll not use. */
if (len < tocopy) {
int j;
for (j = 0; j < tocopy-len; j++) free(history[j]);
tocopy = len;
}
memcpy(newHistory,history+(history_len-tocopy), sizeof(char*)*tocopy);
free(history);
history = newHistory;
}
history_max_len = len;
if (history_len > history_max_len)
history_len = history_max_len;
return 1;
}
/* Save the history in the specified file. On success 0 is returned
* otherwise -1 is returned. */
int linenoiseHistorySave(const char *filename) {
FILE *fp = fopen(filename,"w");
int j;
if (fp == NULL) return -1;
for (j = 0; j < history_len; j++) {
const char *str = history[j];
/* Need to encode backslash, nl and cr */
while (*str) {
if (*str == '\\') {
fputs("\\\\", fp);
}
else if (*str == '\n') {
fputs("\\n", fp);
}
else if (*str == '\r') {
fputs("\\r", fp);
}
else {
fputc(*str, fp);
}
str++;
}
fputc('\n', fp);
}
fclose(fp);
return 0;
}
/* Load the history from the specified file.
*
* If the file does not exist or can't be opened, no operation is performed
* and -1 is returned.
* Otherwise 0 is returned.
*/
int linenoiseHistoryLoad(const char *filename) {
FILE *fp = fopen(filename,"r");
stringbuf *sb;
if (fp == NULL) return -1;
while ((sb = sb_getline(fp)) != NULL) {
/* Take the stringbuf and decode backslash escaped values */
char *buf = sb_to_string(sb);
char *dest = buf;
const char *src;
for (src = buf; *src; src++) {
char ch = *src;
if (ch == '\\') {
src++;
if (*src == 'n') {
ch = '\n';
}
else if (*src == 'r') {
ch = '\r';
} else {
ch = *src;
}
}
*dest++ = ch;
}
*dest = 0;
linenoiseHistoryAddAllocated(buf);
}
fclose(fp);
return 0;
}
/* Provide access to the history buffer.
*
* If 'len' is not NULL, the length is stored in *len.
*/
char **linenoiseHistory(int *len) {
if (len) {
*len = history_len;
}
return history;
}
|
Changes to extsrc/linenoise.h.
|
| < < | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /* linenoise.h -- guerrilla line editing library against the idea that a * line editing lib needs to be 20,000 lines of C code. * * See linenoise.c for more information. * * ------------------------------------------------------------------------ * * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com> * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * |
| ︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 |
#ifndef __LINENOISE_H
#define __LINENOISE_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
| > > > > | > > > > > > > > > > > > > | | < | | | > > > > > > > > | > | < > | | > > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | | 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 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
#ifndef __LINENOISE_H
#define __LINENOISE_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef NO_COMPLETION
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
/*
* The callback type for tab completion handlers.
*/
typedef void(linenoiseCompletionCallback)(const char *prefix, linenoiseCompletions *comp, void *userdata);
/*
* Sets the current tab completion handler and returns the previous one, or NULL
* if no prior one has been set.
*/
linenoiseCompletionCallback * linenoiseSetCompletionCallback(linenoiseCompletionCallback *comp, void *userdata);
/*
* Adds a copy of the given string to the given completion list. The copy is owned
* by the linenoiseCompletions object.
*/
void linenoiseAddCompletion(linenoiseCompletions *comp, const char *str);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold, void *userdata);
typedef void(linenoiseFreeHintsCallback)(void *hint, void *userdata);
void linenoiseSetHintsCallback(linenoiseHintsCallback *callback, void *userdata);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *callback);
#endif
/*
* Prompts for input using the given string as the input
* prompt. Returns when the user has tapped ENTER or (on an empty
* line) EOF (Ctrl-D on Unix, Ctrl-Z on Windows). Returns either
* a copy of the entered string (for ENTER) or NULL (on EOF). The
* caller owns the returned string and must eventually free() it.
*/
char *linenoise(const char *prompt);
/**
* Like linenoise() but starts with an initial buffer.
*/
char *linenoiseWithInitial(const char *prompt, const char *initial);
/**
* Clear the screen.
*/
void linenoiseClearScreen(void);
/*
* Adds a copy of the given line of the command history.
*/
int linenoiseHistoryAdd(const char *line);
/*
* Sets the maximum length of the command history, in lines.
* If the history is currently longer, it will be trimmed,
* retaining only the most recent entries. If len is 0 or less
* then this function does nothing.
*/
int linenoiseHistorySetMaxLen(int len);
/*
* Returns the current maximum length of the history, in lines.
*/
int linenoiseHistoryGetMaxLen(void);
/*
* Saves the current contents of the history to the given file.
* Returns 0 on success.
*/
int linenoiseHistorySave(const char *filename);
/*
* Replaces the current history with the contents
* of the given file. Returns 0 on success.
*/
int linenoiseHistoryLoad(const char *filename);
/*
* Frees all history entries, clearing the history.
*/
void linenoiseHistoryFree(void);
/*
* Returns a pointer to the list of history entries, writing its
* length to *len if len is not NULL. The memory is owned by linenoise
* and must not be freed.
*/
char **linenoiseHistory(int *len);
/*
* Returns the number of display columns in the current terminal.
*/
int linenoiseColumns(void);
/**
* Enable or disable multiline mode (disabled by default)
*/
void linenoiseSetMultiLine(int enableml);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_H */
|
Changes to extsrc/pikchr.c.
1 | /* This file is automatically generated by Lemon from input grammar | | > | 1 2 3 4 5 6 7 8 9 10 | /* This file is automatically generated by Lemon from input grammar ** source file "pikchr.y". */ /* ** Zero-Clause BSD license: ** ** Copyright (C) 2020-09-01 by D. Richard Hipp <drh@sqlite.org> ** ** Permission to use, copy, modify, and/or distribute this software for ** any purpose with or without fee is hereby granted. |
| ︙ | ︙ | |||
264 265 266 267 268 269 270 | return c; } /* Extra token types not generated by LEMON but needed by the ** tokenizer */ #define T_PARAMETER 253 /* $1, $2, ..., $9 */ | | | 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 | return c; } /* Extra token types not generated by LEMON but needed by the ** tokenizer */ #define T_PARAMETER 253 /* $1, $2, ..., $9 */ #define T_WHITESPACE 254 /* Whitespace or comments */ #define T_ERROR 255 /* Any text that is not a valid token */ /* Directions of movement */ #define DIR_RIGHT 0 #define DIR_DOWN 1 #define DIR_LEFT 2 #define DIR_UP 3 |
| ︙ | ︙ | |||
317 318 319 320 321 322 323 324 325 326 327 328 329 330 | PPoint with; /* Position constraint from WITH clause */ char eWith; /* Type of heading point on WITH clause */ char cw; /* True for clockwise arc */ char larrow; /* Arrow at beginning (<- or <->) */ char rarrow; /* Arrow at end (-> or <->) */ char bClose; /* True if "close" is seen */ char bChop; /* True if "chop" is seen */ unsigned char nTxt; /* Number of text values */ unsigned mProp; /* Masks of properties set so far */ unsigned mCalc; /* Values computed from other constraints */ PToken aTxt[5]; /* Text with .eCode holding TP flags */ int iLayer; /* Rendering order */ int inDir, outDir; /* Entry and exit directions */ int nPath; /* Number of path points */ | > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | PPoint with; /* Position constraint from WITH clause */ char eWith; /* Type of heading point on WITH clause */ char cw; /* True for clockwise arc */ char larrow; /* Arrow at beginning (<- or <->) */ char rarrow; /* Arrow at end (-> or <->) */ char bClose; /* True if "close" is seen */ char bChop; /* True if "chop" is seen */ char bAltAutoFit; /* Always send both h and w into xFit() */ unsigned char nTxt; /* Number of text values */ unsigned mProp; /* Masks of properties set so far */ unsigned mCalc; /* Values computed from other constraints */ PToken aTxt[5]; /* Text with .eCode holding TP flags */ int iLayer; /* Rendering order */ int inDir, outDir; /* Entry and exit directions */ int nPath; /* Number of path points */ |
| ︙ | ︙ | |||
488 489 490 491 492 493 494 | static void pik_behind(Pik*,PObj*); static PObj *pik_assert(Pik*,PNum,PToken*,PNum); static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); static PNum pik_dist(PPoint*,PPoint*); static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); | | | 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | static void pik_behind(Pik*,PObj*); static PObj *pik_assert(Pik*,PNum,PToken*,PNum); static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); static PNum pik_dist(PPoint*,PPoint*); static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); #line 523 "pikchr.c" /**************** End of %include directives **********************************/ /* These constants specify the various numeric values for terminal symbols. ***************** Begin token definitions *************************************/ #ifndef T_ID #define T_ID 1 #define T_EDGEPT 2 #define T_OF 3 |
| ︙ | ︙ | |||
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 | ** zero the stack is dynamically sized using realloc() ** pik_parserARG_SDECL A static variable declaration for the %extra_argument ** pik_parserARG_PDECL A parameter declaration for the %extra_argument ** pik_parserARG_PARAM Code to pass %extra_argument as a subroutine parameter ** pik_parserARG_STORE Code to store %extra_argument into yypParser ** pik_parserARG_FETCH Code to extract %extra_argument from yypParser ** pik_parserCTX_* As pik_parserARG_ except for %extra_context ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. ** YYNRULE the number of rules in the grammar ** YYNTOKEN Number of terminal symbols ** YY_MAX_SHIFT Maximum value for shift actions ** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions ** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions ** YY_ERROR_ACTION The yy_action[] code for syntax error ** YY_ACCEPT_ACTION The yy_action[] code for accept ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned char #define YYNOCODE 136 | > > > > > | 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 664 665 | ** zero the stack is dynamically sized using realloc() ** pik_parserARG_SDECL A static variable declaration for the %extra_argument ** pik_parserARG_PDECL A parameter declaration for the %extra_argument ** pik_parserARG_PARAM Code to pass %extra_argument as a subroutine parameter ** pik_parserARG_STORE Code to store %extra_argument into yypParser ** pik_parserARG_FETCH Code to extract %extra_argument from yypParser ** pik_parserCTX_* As pik_parserARG_ except for %extra_context ** YYREALLOC Name of the realloc() function to use ** YYFREE Name of the free() function to use ** YYDYNSTACK True if stack space should be extended on heap ** YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. ** YYNSTATE the combined number of states. ** YYNRULE the number of rules in the grammar ** YYNTOKEN Number of terminal symbols ** YY_MAX_SHIFT Maximum value for shift actions ** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions ** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions ** YY_ERROR_ACTION The yy_action[] code for syntax error ** YY_ACCEPT_ACTION The yy_action[] code for accept ** YY_NO_ACTION The yy_action[] code for no-op ** YY_MIN_REDUCE Minimum value for reduce actions ** YY_MAX_REDUCE Maximum value for reduce actions ** YY_MIN_DSTRCTR Minimum symbol value that has a destructor ** YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned char #define YYNOCODE 136 |
| ︙ | ︙ | |||
672 673 674 675 676 677 678 679 680 681 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 | #define YYSTACKDEPTH 100 #endif #define pik_parserARG_SDECL #define pik_parserARG_PDECL #define pik_parserARG_PARAM #define pik_parserARG_FETCH #define pik_parserARG_STORE #define pik_parserCTX_SDECL Pik *p; #define pik_parserCTX_PDECL ,Pik *p #define pik_parserCTX_PARAM ,p #define pik_parserCTX_FETCH Pik *p=yypParser->p; #define pik_parserCTX_STORE yypParser->p=p; #define YYFALLBACK 1 #define YYNSTATE 164 #define YYNRULE 156 #define YYNRULE_WITH_ACTION 116 #define YYNTOKEN 100 #define YY_MAX_SHIFT 163 #define YY_MIN_SHIFTREDUCE 287 #define YY_MAX_SHIFTREDUCE 442 #define YY_ERROR_ACTION 443 #define YY_ACCEPT_ACTION 444 #define YY_NO_ACTION 445 #define YY_MIN_REDUCE 446 #define YY_MAX_REDUCE 601 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) /* Define the yytestcase() macro to be a no-op if is not already defined ** otherwise. ** ** Applications can choose to define yytestcase() in the %include section ** to a macro that can assist in verifying code coverage. For production ** code the yytestcase() macro should be turned off. But it is useful ** for testing. */ #ifndef yytestcase # define yytestcase(X) #endif /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an ** action integer. ** | > > > > > > > > > > > > > > > > > > > > > | 679 680 681 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 | #define YYSTACKDEPTH 100 #endif #define pik_parserARG_SDECL #define pik_parserARG_PDECL #define pik_parserARG_PARAM #define pik_parserARG_FETCH #define pik_parserARG_STORE #define YYREALLOC realloc #define YYFREE free #define YYDYNSTACK 0 #define pik_parserCTX_SDECL Pik *p; #define pik_parserCTX_PDECL ,Pik *p #define pik_parserCTX_PARAM ,p #define pik_parserCTX_FETCH Pik *p=yypParser->p; #define pik_parserCTX_STORE yypParser->p=p; #define YYFALLBACK 1 #define YYNSTATE 164 #define YYNRULE 156 #define YYNRULE_WITH_ACTION 116 #define YYNTOKEN 100 #define YY_MAX_SHIFT 163 #define YY_MIN_SHIFTREDUCE 287 #define YY_MAX_SHIFTREDUCE 442 #define YY_ERROR_ACTION 443 #define YY_ACCEPT_ACTION 444 #define YY_NO_ACTION 445 #define YY_MIN_REDUCE 446 #define YY_MAX_REDUCE 601 #define YY_MIN_DSTRCTR 100 #define YY_MAX_DSTRCTR 103 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) /* Define the yytestcase() macro to be a no-op if is not already defined ** otherwise. ** ** Applications can choose to define yytestcase() in the %include section ** to a macro that can assist in verifying code coverage. For production ** code the yytestcase() macro should be turned off. But it is useful ** for testing. */ #ifndef yytestcase # define yytestcase(X) #endif /* Macro to determine if stack space has the ability to grow using ** heap memory. */ #if YYSTACKDEPTH<=0 || YYDYNSTACK # define YYGROWABLESTACK 1 #else # define YYGROWABLESTACK 0 #endif /* Guarantee a minimum number of initial stack slots. */ #if YYSTACKDEPTH<=0 # undef YYSTACKDEPTH # define YYSTACKDEPTH 2 /* Need a minimum stack size */ #endif /* Next are the tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement ** functions that take a state number and lookahead value and return an ** action integer. ** |
| ︙ | ︙ | |||
1248 1249 1250 1251 1252 1253 1254 | int yyhwm; /* High-water mark of the stack */ #endif #ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ #endif pik_parserARG_SDECL /* A place to hold %extra_argument */ pik_parserCTX_SDECL /* A place to hold %extra_context */ | < | | | < < < < | 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 | int yyhwm; /* High-water mark of the stack */ #endif #ifndef YYNOERRORRECOVERY int yyerrcnt; /* Shifts left before out of the error */ #endif pik_parserARG_SDECL /* A place to hold %extra_argument */ pik_parserCTX_SDECL /* A place to hold %extra_context */ yyStackEntry *yystackEnd; /* Last entry in the stack */ yyStackEntry *yystack; /* The parser stack */ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */ }; typedef struct yyParser yyParser; #include <assert.h> #ifndef NDEBUG #include <stdio.h> static FILE *yyTraceFILE = 0; |
| ︙ | ︙ | |||
1599 1600 1601 1602 1603 1604 1605 | /* 153 */ "edge ::= RIGHT", /* 154 */ "edge ::= LEFT", /* 155 */ "object ::= objectname", }; #endif /* NDEBUG */ | | > | | | | | > | > < | | | | | | | > | < > | > > > > > < < | | < < < < < < < < | 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 |
/* 153 */ "edge ::= RIGHT",
/* 154 */ "edge ::= LEFT",
/* 155 */ "object ::= objectname",
};
#endif /* NDEBUG */
#if YYGROWABLESTACK
/*
** Try to increase the size of the parser stack. Return the number
** of errors. Return 0 on success.
*/
static int yyGrowStack(yyParser *p){
int oldSize = 1 + (int)(p->yystackEnd - p->yystack);
int newSize;
int idx;
yyStackEntry *pNew;
newSize = oldSize*2 + 100;
idx = (int)(p->yytos - p->yystack);
if( p->yystack==p->yystk0 ){
pNew = YYREALLOC(0, newSize*sizeof(pNew[0]));
if( pNew==0 ) return 1;
memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0]));
}else{
pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]));
if( pNew==0 ) return 1;
}
p->yystack = pNew;
p->yytos = &p->yystack[idx];
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
yyTracePrompt, oldSize, newSize);
}
#endif
p->yystackEnd = &p->yystack[newSize-1];
return 0;
}
#endif /* YYGROWABLESTACK */
#if !YYGROWABLESTACK
/* For builds that do no have a growable stack, yyGrowStack always
** returns an error.
*/
# define yyGrowStack(X) 1
#endif
/* Datatype of the argument to the memory allocated passed as the
** second argument to pik_parserAlloc() below. This can be changed by
** putting an appropriate #define in the %include section of the input
** grammar.
*/
#ifndef YYMALLOCARGTYPE
# define YYMALLOCARGTYPE size_t
#endif
/* Initialize a new parser that has already been allocated.
*/
void pik_parserInit(void *yypRawParser pik_parserCTX_PDECL){
yyParser *yypParser = (yyParser*)yypRawParser;
pik_parserCTX_STORE
#ifdef YYTRACKMAXSTACKDEPTH
yypParser->yyhwm = 0;
#endif
yypParser->yystack = yypParser->yystk0;
yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt = -1;
#endif
yypParser->yytos = yypParser->yystack;
yypParser->yystack[0].stateno = 0;
yypParser->yystack[0].major = 0;
}
#ifndef pik_parser_ENGINEALWAYSONSTACK
/*
** This function allocates a new parser.
** The only argument is a pointer to a function which works like
** malloc.
|
| ︙ | ︙ | |||
1722 1723 1724 1725 1726 1727 1728 |
** Note: during a reduce, the only symbols destroyed are those
** which appear on the RHS of the rule, but which are *not* used
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
case 100: /* statement_list */
{
| | | | | | 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 |
** Note: during a reduce, the only symbols destroyed are those
** which appear on the RHS of the rule, but which are *not* used
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
case 100: /* statement_list */
{
#line 511 "pikchr.y"
pik_elist_free(p,(yypminor->yy235));
#line 1777 "pikchr.c"
}
break;
case 101: /* statement */
case 102: /* unnamed_statement */
case 103: /* basetype */
{
#line 513 "pikchr.y"
pik_elem_free(p,(yypminor->yy162));
#line 1786 "pikchr.c"
}
break;
/********* End destructor definitions *****************************************/
default: break; /* If no destructor action specified: do nothing */
}
}
|
| ︙ | ︙ | |||
1767 1768 1769 1770 1771 1772 1773 |
}
/*
** Clear all secondary memory allocations from the parser
*/
void pik_parserFinalize(void *p){
yyParser *pParser = (yyParser*)p;
| > > > > | > > > > > > > > > > > > | > | | 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 |
}
/*
** Clear all secondary memory allocations from the parser
*/
void pik_parserFinalize(void *p){
yyParser *pParser = (yyParser*)p;
/* In-lined version of calling yy_pop_parser_stack() for each
** element left in the stack */
yyStackEntry *yytos = pParser->yytos;
while( yytos>pParser->yystack ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sPopping %s\n",
yyTracePrompt,
yyTokenName[yytos->major]);
}
#endif
if( yytos->major>=YY_MIN_DSTRCTR ){
yy_destructor(pParser, yytos->major, &yytos->minor);
}
yytos--;
}
#if YYGROWABLESTACK
if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack);
#endif
}
#ifndef pik_parser_ENGINEALWAYSONSTACK
/*
** Deallocate and destroy a parser. Destructors are called for
** all stack elements before shutting the parser down.
|
| ︙ | ︙ | |||
1951 1952 1953 1954 1955 1956 1957 |
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
}
#endif
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will execute if the parser
** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
| | | | 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 |
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
}
#endif
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will execute if the parser
** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
#line 545 "pikchr.y"
pik_error(p, 0, "parser stack overflow");
#line 2024 "pikchr.c"
/******** End %stack_overflow code ********************************************/
pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */
pik_parserCTX_STORE
}
/*
** Print tracing information for a SHIFT action
|
| ︙ | ︙ | |||
1998 1999 2000 2001 2002 2003 2004 |
yypParser->yytos++;
#ifdef YYTRACKMAXSTACKDEPTH
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
yypParser->yyhwm++;
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
}
#endif
| < < | < < < < | > > < < | 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 |
yypParser->yytos++;
#ifdef YYTRACKMAXSTACKDEPTH
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
yypParser->yyhwm++;
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
}
#endif
yytos = yypParser->yytos;
if( yytos>yypParser->yystackEnd ){
if( yyGrowStack(yypParser) ){
yypParser->yytos--;
yyStackOverflow(yypParser);
return;
}
yytos = yypParser->yytos;
assert( yytos <= yypParser->yystackEnd );
}
if( yyNewState > YY_MAX_SHIFT ){
yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
}
yytos->stateno = yyNewState;
yytos->major = yyMajor;
yytos->minor.yy0 = yyMinor;
yyTraceShift(yypParser, yyNewState, "Shift");
}
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
|
| ︙ | ︙ | |||
2385 2386 2387 2388 2389 2390 2391 |
** { ... } // User supplied code
** #line <lineno> <thisfile>
** break;
*/
/********** Begin reduce actions **********************************************/
YYMINORTYPE yylhsminor;
case 0: /* document ::= statement_list */
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 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 3031 3032 3033 3034 3035 3036 3037 3038 3039 |
** { ... } // User supplied code
** #line <lineno> <thisfile>
** break;
*/
/********** Begin reduce actions **********************************************/
YYMINORTYPE yylhsminor;
case 0: /* document ::= statement_list */
#line 549 "pikchr.y"
{pik_render(p,yymsp[0].minor.yy235);}
#line 2451 "pikchr.c"
break;
case 1: /* statement_list ::= statement */
#line 552 "pikchr.y"
{ yylhsminor.yy235 = pik_elist_append(p,0,yymsp[0].minor.yy162); }
#line 2456 "pikchr.c"
yymsp[0].minor.yy235 = yylhsminor.yy235;
break;
case 2: /* statement_list ::= statement_list EOL statement */
#line 554 "pikchr.y"
{ yylhsminor.yy235 = pik_elist_append(p,yymsp[-2].minor.yy235,yymsp[0].minor.yy162); }
#line 2462 "pikchr.c"
yymsp[-2].minor.yy235 = yylhsminor.yy235;
break;
case 3: /* statement ::= */
#line 557 "pikchr.y"
{ yymsp[1].minor.yy162 = 0; }
#line 2468 "pikchr.c"
break;
case 4: /* statement ::= direction */
#line 558 "pikchr.y"
{ pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy162=0; }
#line 2473 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 5: /* statement ::= lvalue ASSIGN rvalue */
#line 559 "pikchr.y"
{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy21,&yymsp[-1].minor.yy0); yylhsminor.yy162=0;}
#line 2479 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 6: /* statement ::= PLACENAME COLON unnamed_statement */
#line 561 "pikchr.y"
{ yylhsminor.yy162 = yymsp[0].minor.yy162; pik_elem_setname(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0); }
#line 2485 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 7: /* statement ::= PLACENAME COLON position */
#line 563 "pikchr.y"
{ yylhsminor.yy162 = pik_elem_new(p,0,0,0);
if(yylhsminor.yy162){ yylhsminor.yy162->ptAt = yymsp[0].minor.yy63; pik_elem_setname(p,yylhsminor.yy162,&yymsp[-2].minor.yy0); }}
#line 2492 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 8: /* statement ::= unnamed_statement */
#line 565 "pikchr.y"
{yylhsminor.yy162 = yymsp[0].minor.yy162;}
#line 2498 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 9: /* statement ::= print prlist */
#line 566 "pikchr.y"
{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy162=0;}
#line 2504 "pikchr.c"
break;
case 10: /* statement ::= ASSERT LP expr EQ expr RP */
#line 571 "pikchr.y"
{yymsp[-5].minor.yy162=pik_assert(p,yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy21);}
#line 2509 "pikchr.c"
break;
case 11: /* statement ::= ASSERT LP position EQ position RP */
#line 573 "pikchr.y"
{yymsp[-5].minor.yy162=pik_position_assert(p,&yymsp[-3].minor.yy63,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy63);}
#line 2514 "pikchr.c"
break;
case 12: /* statement ::= DEFINE ID CODEBLOCK */
#line 574 "pikchr.y"
{yymsp[-2].minor.yy162=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
#line 2519 "pikchr.c"
break;
case 13: /* rvalue ::= PLACENAME */
#line 585 "pikchr.y"
{yylhsminor.yy21 = pik_lookup_color(p,&yymsp[0].minor.yy0);}
#line 2524 "pikchr.c"
yymsp[0].minor.yy21 = yylhsminor.yy21;
break;
case 14: /* pritem ::= FILL */
case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15);
case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16);
#line 590 "pikchr.y"
{pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));}
#line 2532 "pikchr.c"
break;
case 17: /* pritem ::= rvalue */
#line 593 "pikchr.y"
{pik_append_num(p,"",yymsp[0].minor.yy21);}
#line 2537 "pikchr.c"
break;
case 18: /* pritem ::= STRING */
#line 594 "pikchr.y"
{pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);}
#line 2542 "pikchr.c"
break;
case 19: /* prsep ::= COMMA */
#line 595 "pikchr.y"
{pik_append(p, " ", 1);}
#line 2547 "pikchr.c"
break;
case 20: /* unnamed_statement ::= basetype attribute_list */
#line 598 "pikchr.y"
{yylhsminor.yy162 = yymsp[-1].minor.yy162; pik_after_adding_attributes(p,yylhsminor.yy162);}
#line 2552 "pikchr.c"
yymsp[-1].minor.yy162 = yylhsminor.yy162;
break;
case 21: /* basetype ::= CLASSNAME */
#line 600 "pikchr.y"
{yylhsminor.yy162 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); }
#line 2558 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 22: /* basetype ::= STRING textposition */
#line 602 "pikchr.y"
{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy188; yylhsminor.yy162 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); }
#line 2564 "pikchr.c"
yymsp[-1].minor.yy162 = yylhsminor.yy162;
break;
case 23: /* basetype ::= LB savelist statement_list RB */
#line 604 "pikchr.y"
{ p->list = yymsp[-2].minor.yy235; yymsp[-3].minor.yy162 = pik_elem_new(p,0,0,yymsp[-1].minor.yy235); if(yymsp[-3].minor.yy162) yymsp[-3].minor.yy162->errTok = yymsp[0].minor.yy0; }
#line 2570 "pikchr.c"
break;
case 24: /* savelist ::= */
#line 609 "pikchr.y"
{yymsp[1].minor.yy235 = p->list; p->list = 0;}
#line 2575 "pikchr.c"
break;
case 25: /* relexpr ::= expr */
#line 616 "pikchr.y"
{yylhsminor.yy72.rAbs = yymsp[0].minor.yy21; yylhsminor.yy72.rRel = 0;}
#line 2580 "pikchr.c"
yymsp[0].minor.yy72 = yylhsminor.yy72;
break;
case 26: /* relexpr ::= expr PERCENT */
#line 617 "pikchr.y"
{yylhsminor.yy72.rAbs = 0; yylhsminor.yy72.rRel = yymsp[-1].minor.yy21/100;}
#line 2586 "pikchr.c"
yymsp[-1].minor.yy72 = yylhsminor.yy72;
break;
case 27: /* optrelexpr ::= */
#line 619 "pikchr.y"
{yymsp[1].minor.yy72.rAbs = 0; yymsp[1].minor.yy72.rRel = 1.0;}
#line 2592 "pikchr.c"
break;
case 28: /* attribute_list ::= relexpr alist */
#line 621 "pikchr.y"
{pik_add_direction(p,0,&yymsp[-1].minor.yy72);}
#line 2597 "pikchr.c"
break;
case 29: /* attribute ::= numproperty relexpr */
#line 625 "pikchr.y"
{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72); }
#line 2602 "pikchr.c"
break;
case 30: /* attribute ::= dashproperty expr */
#line 626 "pikchr.y"
{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy21); }
#line 2607 "pikchr.c"
break;
case 31: /* attribute ::= dashproperty */
#line 627 "pikchr.y"
{ pik_set_dashed(p,&yymsp[0].minor.yy0,0); }
#line 2612 "pikchr.c"
break;
case 32: /* attribute ::= colorproperty rvalue */
#line 628 "pikchr.y"
{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21); }
#line 2617 "pikchr.c"
break;
case 33: /* attribute ::= go direction optrelexpr */
#line 629 "pikchr.y"
{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72);}
#line 2622 "pikchr.c"
break;
case 34: /* attribute ::= go direction even position */
#line 630 "pikchr.y"
{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63);}
#line 2627 "pikchr.c"
break;
case 35: /* attribute ::= CLOSE */
#line 631 "pikchr.y"
{ pik_close_path(p,&yymsp[0].minor.yy0); }
#line 2632 "pikchr.c"
break;
case 36: /* attribute ::= CHOP */
#line 632 "pikchr.y"
{ p->cur->bChop = 1; }
#line 2637 "pikchr.c"
break;
case 37: /* attribute ::= FROM position */
#line 633 "pikchr.y"
{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); }
#line 2642 "pikchr.c"
break;
case 38: /* attribute ::= TO position */
#line 634 "pikchr.y"
{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); }
#line 2647 "pikchr.c"
break;
case 39: /* attribute ::= THEN */
#line 635 "pikchr.y"
{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
#line 2652 "pikchr.c"
break;
case 40: /* attribute ::= THEN optrelexpr HEADING expr */
case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42);
#line 637 "pikchr.y"
{pik_move_hdg(p,&yymsp[-2].minor.yy72,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21,0,&yymsp[-3].minor.yy0);}
#line 2658 "pikchr.c"
break;
case 41: /* attribute ::= THEN optrelexpr EDGEPT */
case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43);
#line 638 "pikchr.y"
{pik_move_hdg(p,&yymsp[-1].minor.yy72,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);}
#line 2664 "pikchr.c"
break;
case 44: /* attribute ::= AT position */
#line 643 "pikchr.y"
{ pik_set_at(p,0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); }
#line 2669 "pikchr.c"
break;
case 45: /* attribute ::= SAME */
#line 645 "pikchr.y"
{pik_same(p,0,&yymsp[0].minor.yy0);}
#line 2674 "pikchr.c"
break;
case 46: /* attribute ::= SAME AS object */
#line 646 "pikchr.y"
{pik_same(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);}
#line 2679 "pikchr.c"
break;
case 47: /* attribute ::= STRING textposition */
#line 647 "pikchr.y"
{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy188);}
#line 2684 "pikchr.c"
break;
case 48: /* attribute ::= FIT */
#line 648 "pikchr.y"
{pik_size_to_fit(p,&yymsp[0].minor.yy0,3); }
#line 2689 "pikchr.c"
break;
case 49: /* attribute ::= BEHIND object */
#line 649 "pikchr.y"
{pik_behind(p,yymsp[0].minor.yy162);}
#line 2694 "pikchr.c"
break;
case 50: /* withclause ::= DOT_E edge AT position */
case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51);
#line 657 "pikchr.y"
{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); }
#line 2700 "pikchr.c"
break;
case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
#line 661 "pikchr.y"
{yylhsminor.yy0 = yymsp[0].minor.yy0;}
#line 2705 "pikchr.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 53: /* boolproperty ::= CW */
#line 672 "pikchr.y"
{p->cur->cw = 1;}
#line 2711 "pikchr.c"
break;
case 54: /* boolproperty ::= CCW */
#line 673 "pikchr.y"
{p->cur->cw = 0;}
#line 2716 "pikchr.c"
break;
case 55: /* boolproperty ::= LARROW */
#line 674 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=0; }
#line 2721 "pikchr.c"
break;
case 56: /* boolproperty ::= RARROW */
#line 675 "pikchr.y"
{p->cur->larrow=0; p->cur->rarrow=1; }
#line 2726 "pikchr.c"
break;
case 57: /* boolproperty ::= LRARROW */
#line 676 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=1; }
#line 2731 "pikchr.c"
break;
case 58: /* boolproperty ::= INVIS */
#line 677 "pikchr.y"
{p->cur->sw = -0.00001;}
#line 2736 "pikchr.c"
break;
case 59: /* boolproperty ::= THICK */
#line 678 "pikchr.y"
{p->cur->sw *= 1.5;}
#line 2741 "pikchr.c"
break;
case 60: /* boolproperty ::= THIN */
#line 679 "pikchr.y"
{p->cur->sw *= 0.67;}
#line 2746 "pikchr.c"
break;
case 61: /* boolproperty ::= SOLID */
#line 680 "pikchr.y"
{p->cur->sw = pik_value(p,"thickness",9,0);
p->cur->dotted = p->cur->dashed = 0.0;}
#line 2752 "pikchr.c"
break;
case 62: /* textposition ::= */
#line 683 "pikchr.y"
{yymsp[1].minor.yy188 = 0;}
#line 2757 "pikchr.c"
break;
case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
#line 686 "pikchr.y"
{yylhsminor.yy188 = (short int)pik_text_position(yymsp[-1].minor.yy188,&yymsp[0].minor.yy0);}
#line 2762 "pikchr.c"
yymsp[-1].minor.yy188 = yylhsminor.yy188;
break;
case 64: /* position ::= expr COMMA expr */
#line 689 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[0].minor.yy21;}
#line 2768 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 65: /* position ::= place PLUS expr COMMA expr */
#line 691 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x+yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y+yymsp[0].minor.yy21;}
#line 2774 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 66: /* position ::= place MINUS expr COMMA expr */
#line 692 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x-yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y-yymsp[0].minor.yy21;}
#line 2780 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 67: /* position ::= place PLUS LP expr COMMA expr RP */
#line 694 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x+yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y+yymsp[-1].minor.yy21;}
#line 2786 "pikchr.c"
yymsp[-6].minor.yy63 = yylhsminor.yy63;
break;
case 68: /* position ::= place MINUS LP expr COMMA expr RP */
#line 696 "pikchr.y"
{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x-yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y-yymsp[-1].minor.yy21;}
#line 2792 "pikchr.c"
yymsp[-6].minor.yy63 = yylhsminor.yy63;
break;
case 69: /* position ::= LP position COMMA position RP */
#line 697 "pikchr.y"
{yymsp[-4].minor.yy63.x=yymsp[-3].minor.yy63.x; yymsp[-4].minor.yy63.y=yymsp[-1].minor.yy63.y;}
#line 2798 "pikchr.c"
break;
case 70: /* position ::= LP position RP */
#line 698 "pikchr.y"
{yymsp[-2].minor.yy63=yymsp[-1].minor.yy63;}
#line 2803 "pikchr.c"
break;
case 71: /* position ::= expr between position AND position */
#line 700 "pikchr.y"
{yylhsminor.yy63 = pik_position_between(yymsp[-4].minor.yy21,yymsp[-2].minor.yy63,yymsp[0].minor.yy63);}
#line 2808 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 72: /* position ::= expr LT position COMMA position GT */
#line 702 "pikchr.y"
{yylhsminor.yy63 = pik_position_between(yymsp[-5].minor.yy21,yymsp[-3].minor.yy63,yymsp[-1].minor.yy63);}
#line 2814 "pikchr.c"
yymsp[-5].minor.yy63 = yylhsminor.yy63;
break;
case 73: /* position ::= expr ABOVE position */
#line 703 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y += yymsp[-2].minor.yy21;}
#line 2820 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 74: /* position ::= expr BELOW position */
#line 704 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y -= yymsp[-2].minor.yy21;}
#line 2826 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 75: /* position ::= expr LEFT OF position */
#line 705 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x -= yymsp[-3].minor.yy21;}
#line 2832 "pikchr.c"
yymsp[-3].minor.yy63 = yylhsminor.yy63;
break;
case 76: /* position ::= expr RIGHT OF position */
#line 706 "pikchr.y"
{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x += yymsp[-3].minor.yy21;}
#line 2838 "pikchr.c"
yymsp[-3].minor.yy63 = yylhsminor.yy63;
break;
case 77: /* position ::= expr ON HEADING EDGEPT OF position */
#line 708 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-5].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);}
#line 2844 "pikchr.c"
yymsp[-5].minor.yy63 = yylhsminor.yy63;
break;
case 78: /* position ::= expr HEADING EDGEPT OF position */
#line 710 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-4].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);}
#line 2850 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 79: /* position ::= expr EDGEPT OF position */
#line 712 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);}
#line 2856 "pikchr.c"
yymsp[-3].minor.yy63 = yylhsminor.yy63;
break;
case 80: /* position ::= expr ON HEADING expr FROM position */
#line 714 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_angle(yymsp[-5].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);}
#line 2862 "pikchr.c"
yymsp[-5].minor.yy63 = yylhsminor.yy63;
break;
case 81: /* position ::= expr HEADING expr FROM position */
#line 716 "pikchr.y"
{yylhsminor.yy63 = pik_position_at_angle(yymsp[-4].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);}
#line 2868 "pikchr.c"
yymsp[-4].minor.yy63 = yylhsminor.yy63;
break;
case 82: /* place ::= edge OF object */
#line 728 "pikchr.y"
{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);}
#line 2874 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 83: /* place2 ::= object */
#line 729 "pikchr.y"
{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,0);}
#line 2880 "pikchr.c"
yymsp[0].minor.yy63 = yylhsminor.yy63;
break;
case 84: /* place2 ::= object DOT_E edge */
#line 730 "pikchr.y"
{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);}
#line 2886 "pikchr.c"
yymsp[-2].minor.yy63 = yylhsminor.yy63;
break;
case 85: /* place2 ::= NTH VERTEX OF object */
#line 731 "pikchr.y"
{yylhsminor.yy63 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy162);}
#line 2892 "pikchr.c"
yymsp[-3].minor.yy63 = yylhsminor.yy63;
break;
case 86: /* object ::= nth */
#line 743 "pikchr.y"
{yylhsminor.yy162 = pik_find_nth(p,0,&yymsp[0].minor.yy0);}
#line 2898 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 87: /* object ::= nth OF|IN object */
#line 744 "pikchr.y"
{yylhsminor.yy162 = pik_find_nth(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);}
#line 2904 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 88: /* objectname ::= THIS */
#line 746 "pikchr.y"
{yymsp[0].minor.yy162 = p->cur;}
#line 2910 "pikchr.c"
break;
case 89: /* objectname ::= PLACENAME */
#line 747 "pikchr.y"
{yylhsminor.yy162 = pik_find_byname(p,0,&yymsp[0].minor.yy0);}
#line 2915 "pikchr.c"
yymsp[0].minor.yy162 = yylhsminor.yy162;
break;
case 90: /* objectname ::= objectname DOT_U PLACENAME */
#line 749 "pikchr.y"
{yylhsminor.yy162 = pik_find_byname(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);}
#line 2921 "pikchr.c"
yymsp[-2].minor.yy162 = yylhsminor.yy162;
break;
case 91: /* nth ::= NTH CLASSNAME */
#line 751 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); }
#line 2927 "pikchr.c"
yymsp[-1].minor.yy0 = yylhsminor.yy0;
break;
case 92: /* nth ::= NTH LAST CLASSNAME */
#line 752 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); }
#line 2933 "pikchr.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 93: /* nth ::= LAST CLASSNAME */
#line 753 "pikchr.y"
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
#line 2939 "pikchr.c"
break;
case 94: /* nth ::= LAST */
#line 754 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
#line 2944 "pikchr.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 95: /* nth ::= NTH LB RB */
#line 755 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);}
#line 2950 "pikchr.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 96: /* nth ::= NTH LAST LB RB */
#line 756 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);}
#line 2956 "pikchr.c"
yymsp[-3].minor.yy0 = yylhsminor.yy0;
break;
case 97: /* nth ::= LAST LB RB */
#line 757 "pikchr.y"
{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
#line 2962 "pikchr.c"
break;
case 98: /* expr ::= expr PLUS expr */
#line 759 "pikchr.y"
{yylhsminor.yy21=yymsp[-2].minor.yy21+yymsp[0].minor.yy21;}
#line 2967 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 99: /* expr ::= expr MINUS expr */
#line 760 "pikchr.y"
{yylhsminor.yy21=yymsp[-2].minor.yy21-yymsp[0].minor.yy21;}
#line 2973 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 100: /* expr ::= expr STAR expr */
#line 761 "pikchr.y"
{yylhsminor.yy21=yymsp[-2].minor.yy21*yymsp[0].minor.yy21;}
#line 2979 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 101: /* expr ::= expr SLASH expr */
#line 762 "pikchr.y"
{
if( yymsp[0].minor.yy21==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy21 = 0.0; }
else{ yylhsminor.yy21 = yymsp[-2].minor.yy21/yymsp[0].minor.yy21; }
}
#line 2988 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 102: /* expr ::= MINUS expr */
#line 766 "pikchr.y"
{yymsp[-1].minor.yy21=-yymsp[0].minor.yy21;}
#line 2994 "pikchr.c"
break;
case 103: /* expr ::= PLUS expr */
#line 767 "pikchr.y"
{yymsp[-1].minor.yy21=yymsp[0].minor.yy21;}
#line 2999 "pikchr.c"
break;
case 104: /* expr ::= LP expr RP */
#line 768 "pikchr.y"
{yymsp[-2].minor.yy21=yymsp[-1].minor.yy21;}
#line 3004 "pikchr.c"
break;
case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */
#line 769 "pikchr.y"
{yymsp[-2].minor.yy21=pik_get_var(p,&yymsp[-1].minor.yy0);}
#line 3009 "pikchr.c"
break;
case 106: /* expr ::= NUMBER */
#line 770 "pikchr.y"
{yylhsminor.yy21=pik_atof(&yymsp[0].minor.yy0);}
#line 3014 "pikchr.c"
yymsp[0].minor.yy21 = yylhsminor.yy21;
break;
case 107: /* expr ::= ID */
#line 771 "pikchr.y"
{yylhsminor.yy21=pik_get_var(p,&yymsp[0].minor.yy0);}
#line 3020 "pikchr.c"
yymsp[0].minor.yy21 = yylhsminor.yy21;
break;
case 108: /* expr ::= FUNC1 LP expr RP */
#line 772 "pikchr.y"
{yylhsminor.yy21 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy21,0.0);}
#line 3026 "pikchr.c"
yymsp[-3].minor.yy21 = yylhsminor.yy21;
break;
case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */
#line 773 "pikchr.y"
{yylhsminor.yy21 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy21,yymsp[-1].minor.yy21);}
#line 3032 "pikchr.c"
yymsp[-5].minor.yy21 = yylhsminor.yy21;
break;
case 110: /* expr ::= DIST LP position COMMA position RP */
#line 774 "pikchr.y"
{yymsp[-5].minor.yy21 = pik_dist(&yymsp[-3].minor.yy63,&yymsp[-1].minor.yy63);}
#line 3038 "pikchr.c"
break;
case 111: /* expr ::= place2 DOT_XY X */
#line 775 "pikchr.y"
{yylhsminor.yy21 = yymsp[-2].minor.yy63.x;}
#line 3043 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 112: /* expr ::= place2 DOT_XY Y */
#line 776 "pikchr.y"
{yylhsminor.yy21 = yymsp[-2].minor.yy63.y;}
#line 3049 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
case 113: /* expr ::= object DOT_L numproperty */
case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114);
case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115);
#line 777 "pikchr.y"
{yylhsminor.yy21=pik_property_of(yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);}
#line 3057 "pikchr.c"
yymsp[-2].minor.yy21 = yylhsminor.yy21;
break;
default:
/* (116) lvalue ::= ID */ yytestcase(yyruleno==116);
/* (117) lvalue ::= FILL */ yytestcase(yyruleno==117);
/* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118);
/* (119) lvalue ::= THICKNESS */ yytestcase(yyruleno==119);
|
| ︙ | ︙ | |||
3096 3097 3098 3099 3100 3101 3102 |
int yymajor, /* The major type of the error token */
pik_parserTOKENTYPE yyminor /* The minor type of the error token */
){
pik_parserARG_FETCH
pik_parserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
| | | | 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 |
int yymajor, /* The major type of the error token */
pik_parserTOKENTYPE yyminor /* The minor type of the error token */
){
pik_parserARG_FETCH
pik_parserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
#line 537 "pikchr.y"
if( TOKEN.z && TOKEN.z[0] ){
pik_error(p, &TOKEN, "syntax error");
}else{
pik_error(p, 0, "syntax error");
}
UNUSED_PARAMETER(yymajor);
#line 3168 "pikchr.c"
/************ End %syntax_error code ******************************************/
pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
pik_parserCTX_STORE
}
/*
** The following is executed when the parser accepts
|
| ︙ | ︙ | |||
3225 3226 3227 3228 3229 3230 3231 |
#ifdef YYTRACKMAXSTACKDEPTH
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
yypParser->yyhwm++;
assert( yypParser->yyhwm ==
(int)(yypParser->yytos - yypParser->yystack));
}
#endif
| < < < < < < < | 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 |
#ifdef YYTRACKMAXSTACKDEPTH
if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
yypParser->yyhwm++;
assert( yypParser->yyhwm ==
(int)(yypParser->yytos - yypParser->yystack));
}
#endif
if( yypParser->yytos>=yypParser->yystackEnd ){
if( yyGrowStack(yypParser) ){
yyStackOverflow(yypParser);
break;
}
}
}
yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor pik_parserCTX_PARAM);
}else if( yyact <= YY_MAX_SHIFTREDUCE ){
yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt--;
#endif
|
| ︙ | ︙ | |||
3380 3381 3382 3383 3384 3385 3386 | assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); return yyFallback[iToken]; #else (void)iToken; return 0; #endif } | | | 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 | assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); return yyFallback[iToken]; #else (void)iToken; return 0; #endif } #line 782 "pikchr.y" /* Chart of the 148 official CSS color names with their ** corresponding RGB values thru Color Module Level 4: ** https://developer.mozilla.org/en-US/docs/Web/CSS/color_value ** |
| ︙ | ︙ | |||
3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 |
{ "charwid", 0.08 },
{ "circlerad", 0.25 },
{ "color", 0.0 },
{ "cylht", 0.5 },
{ "cylrad", 0.075 },
{ "cylwid", 0.75 },
{ "dashwid", 0.05 },
{ "dotrad", 0.015 },
{ "ellipseht", 0.5 },
{ "ellipsewid", 0.75 },
{ "fileht", 0.75 },
{ "filerad", 0.15 },
{ "filewid", 0.5 },
{ "fill", -1.0 },
| > > | 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 |
{ "charwid", 0.08 },
{ "circlerad", 0.25 },
{ "color", 0.0 },
{ "cylht", 0.5 },
{ "cylrad", 0.075 },
{ "cylwid", 0.75 },
{ "dashwid", 0.05 },
{ "diamondht", 0.75 },
{ "diamondwid", 1.0 },
{ "dotrad", 0.015 },
{ "ellipseht", 0.5 },
{ "ellipsewid", 0.75 },
{ "fileht", 0.75 },
{ "filerad", 0.15 },
{ "filewid", 0.5 },
{ "fill", -1.0 },
|
| ︙ | ︙ | |||
3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 |
pik_append_dis(p," r=\"", r, "\"");
pik_append_style(p,pObj,2);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pObj, 0);
}
/* Methods for the "ellipse" class */
static void ellipseInit(Pik *p, PObj *pObj){
pObj->w = pik_value(p, "ellipsewid",10,0);
pObj->h = pik_value(p, "ellipseht",9,0);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 |
pik_append_dis(p," r=\"", r, "\"");
pik_append_style(p,pObj,2);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pObj, 0);
}
/* Methods for the "diamond" class */
static void diamondInit(Pik *p, PObj *pObj){
pObj->w = pik_value(p, "diamondwid",10,0);
pObj->h = pik_value(p, "diamondht",9,0);
pObj->bAltAutoFit = 1;
}
/* Return offset from the center of the box to the compass point
** given by parameter cp */
static PPoint diamondOffset(Pik *p, PObj *pObj, int cp){
PPoint pt = cZeroPoint;
PNum w2 = 0.5*pObj->w;
PNum w4 = 0.25*pObj->w;
PNum h2 = 0.5*pObj->h;
PNum h4 = 0.25*pObj->h;
switch( cp ){
case CP_C: break;
case CP_N: pt.x = 0.0; pt.y = h2; break;
case CP_NE: pt.x = w4; pt.y = h4; break;
case CP_E: pt.x = w2; pt.y = 0.0; break;
case CP_SE: pt.x = w4; pt.y = -h4; break;
case CP_S: pt.x = 0.0; pt.y = -h2; break;
case CP_SW: pt.x = -w4; pt.y = -h4; break;
case CP_W: pt.x = -w2; pt.y = 0.0; break;
case CP_NW: pt.x = -w4; pt.y = h4; break;
default: assert(0);
}
UNUSED_PARAMETER(p);
return pt;
}
static void diamondFit(Pik *p, PObj *pObj, PNum w, PNum h){
if( pObj->w<=0 ) pObj->w = w*1.5;
if( pObj->h<=0 ) pObj->h = h*1.5;
if( pObj->w>0 && pObj->h>0 ){
PNum x = pObj->w*h/pObj->h + w;
PNum y = pObj->h*x/pObj->w;
pObj->w = x;
pObj->h = y;
}
UNUSED_PARAMETER(p);
}
static void diamondRender(Pik *p, PObj *pObj){
PNum w2 = 0.5*pObj->w;
PNum h2 = 0.5*pObj->h;
PPoint pt = pObj->ptAt;
if( pObj->sw>=0.0 ){
pik_append_xy(p,"<path d=\"M", pt.x-w2,pt.y);
pik_append_xy(p,"L", pt.x,pt.y-h2);
pik_append_xy(p,"L", pt.x+w2,pt.y);
pik_append_xy(p,"L", pt.x,pt.y+h2);
pik_append(p,"Z\" ",-1);
pik_append_style(p,pObj,3);
pik_append(p,"\" />\n", -1);
}
pik_append_txt(p, pObj, 0);
}
/* Methods for the "ellipse" class */
static void ellipseInit(Pik *p, PObj *pObj){
pObj->w = pik_value(p, "ellipsewid",10,0);
pObj->h = pik_value(p, "ellipseht",9,0);
}
|
| ︙ | ︙ | |||
4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 |
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ cylinderOffset,
/* xFit */ cylinderFit,
/* xRender */ cylinderRender
},
{ /* name */ "dot",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ dotInit,
/* xNumProp */ dotNumProp,
/* xCheck */ dotCheck,
/* xChop */ circleChop,
| > > > > > > > > > > > | 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 |
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ cylinderOffset,
/* xFit */ cylinderFit,
/* xRender */ cylinderRender
},
{ /* name */ "diamond",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ diamondInit,
/* xNumProp */ 0,
/* xCheck */ 0,
/* xChop */ boxChop,
/* xOffset */ diamondOffset,
/* xFit */ diamondFit,
/* xRender */ diamondRender
},
{ /* name */ "dot",
/* isline */ 0,
/* eJust */ 0,
/* xInit */ dotInit,
/* xNumProp */ dotNumProp,
/* xCheck */ dotCheck,
/* xChop */ circleChop,
|
| ︙ | ︙ | |||
6027 6028 6029 6030 6031 6032 6033 |
if( pObj->mProp & A_AT ){
pik_error(p, pErrTok, "location fixed by prior \"at\"");
return;
}
pObj->mProp |= A_AT;
pObj->eWith = pEdge ? pEdge->eEdge : CP_C;
if( pObj->eWith>=CP_END ){
| | | 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 |
if( pObj->mProp & A_AT ){
pik_error(p, pErrTok, "location fixed by prior \"at\"");
return;
}
pObj->mProp |= A_AT;
pObj->eWith = pEdge ? pEdge->eEdge : CP_C;
if( pObj->eWith>=CP_END ){
int dir = pObj->eWith==CP_END ? pObj->outDir : (pObj->inDir+2)%4;
pObj->eWith = eDirToCp[dir];
}
pObj->with = *pAt;
}
/*
** Try to add a text attribute to an object
|
| ︙ | ︙ | |||
6267 6268 6269 6270 6271 6272 6273 |
pik_error(0, pFit, "no text to fit to");
return;
}
if( pObj->type->xFit==0 ) return;
pik_bbox_init(&bbox);
pik_compute_layout_settings(p);
pik_append_txt(p, pObj, &bbox);
| > | > > > | | 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 |
pik_error(0, pFit, "no text to fit to");
return;
}
if( pObj->type->xFit==0 ) return;
pik_bbox_init(&bbox);
pik_compute_layout_settings(p);
pik_append_txt(p, pObj, &bbox);
if( (eWhich & 1)!=0 || pObj->bAltAutoFit ){
w = (bbox.ne.x - bbox.sw.x) + p->charWidth;
}else{
w = 0;
}
if( (eWhich & 2)!=0 || pObj->bAltAutoFit ){
PNum h1, h2;
h1 = (bbox.ne.y - pObj->ptAt.y);
h2 = (pObj->ptAt.y - bbox.sw.y);
h = 2.0*( h1<h2 ? h2 : h1 ) + 0.5*p->charHeight;
}else{
h = 0;
}
|
| ︙ | ︙ | |||
7114 7115 7116 7117 7118 7119 7120 |
** and the optional "margin = EXPR" setting. */
p->bbox.ne.x += margin + pik_value(p,"rightmargin",11,0);
p->bbox.ne.y += margin + pik_value(p,"topmargin",9,0);
p->bbox.sw.x -= margin + pik_value(p,"leftmargin",10,0);
p->bbox.sw.y -= margin + pik_value(p,"bottommargin",12,0);
/* Output the SVG */
| | > | 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 |
** and the optional "margin = EXPR" setting. */
p->bbox.ne.x += margin + pik_value(p,"rightmargin",11,0);
p->bbox.ne.y += margin + pik_value(p,"topmargin",9,0);
p->bbox.sw.x -= margin + pik_value(p,"leftmargin",10,0);
p->bbox.sw.y -= margin + pik_value(p,"bottommargin",12,0);
/* Output the SVG */
pik_append(p, "<svg xmlns='http://www.w3.org/2000/svg'"
" style='font-size:initial;'",-1);
if( p->zClass ){
pik_append(p, " class=\"", -1);
pik_append(p, p->zClass, -1);
pik_append(p, "\"", 1);
}
w = p->bbox.ne.x - p->bbox.sw.x;
h = p->bbox.ne.y - p->bbox.sw.y;
|
| ︙ | ︙ | |||
7337 7338 7339 7340 7341 7342 7343 |
pToken->eType = T_ERROR;
return i;
}
case ' ':
case '\t':
case '\f':
case '\r': {
| | | 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 |
pToken->eType = T_ERROR;
return i;
}
case ' ':
case '\t':
case '\f':
case '\r': {
for(i=1; (c = z[i])==' ' || c=='\t' || c=='\r' || c=='\f'; i++){}
pToken->eType = T_WHITESPACE;
return i;
}
case '#': {
for(i=1; (c = z[i])!=0 && c!='\n'; i++){}
pToken->eType = T_WHITESPACE;
/* If the comment is "#breakpoint" then invoke the pik_breakpoint()
|
| ︙ | ︙ | |||
7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 |
/* Print a usage comment for the shell and exit. */
static void usage(const char *argv0){
fprintf(stderr, "usage: %s [OPTIONS] FILE ...\n", argv0);
fprintf(stderr,
"Convert Pikchr input files into SVG. Filename \"-\" means stdin.\n"
"All output goes to stdout.\n"
"Options:\n"
" --dont-stop Process all files even if earlier files have errors\n"
" --svg-only Emit raw SVG without the HTML wrapper\n"
);
exit(1);
}
/* Send text to standard output, but escape HTML markup */
| > | 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 |
/* Print a usage comment for the shell and exit. */
static void usage(const char *argv0){
fprintf(stderr, "usage: %s [OPTIONS] FILE ...\n", argv0);
fprintf(stderr,
"Convert Pikchr input files into SVG. Filename \"-\" means stdin.\n"
"All output goes to stdout.\n"
"Options:\n"
" --dark-mode Generate \"dark mode\" output\n"
" --dont-stop Process all files even if earlier files have errors\n"
" --svg-only Emit raw SVG without the HTML wrapper\n"
);
exit(1);
}
/* Send text to standard output, but escape HTML markup */
|
| ︙ | ︙ | |||
8038 8039 8040 8041 8042 8043 8044 |
usage(argv[0]);
}
continue;
}
zIn = readFile(argv[i]);
if( zIn==0 ) continue;
zOut = pikchr(zIn, "pikchr", mFlags, &w, &h);
| | | 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 |
usage(argv[0]);
}
continue;
}
zIn = readFile(argv[i]);
if( zIn==0 ) continue;
zOut = pikchr(zIn, "pikchr", mFlags, &w, &h);
if( w<0 && !bDontStop ) exitCode = 1;
if( zOut==0 ){
fprintf(stderr, "pikchr() returns NULL. Out of memory?\n");
if( !bDontStop ) exit(1);
}else if( bSvgOnly ){
printf("%s\n", zOut);
}else{
if( zHtmlHdr ){
|
| ︙ | ︙ | |||
8141 8142 8143 8144 8145 8146 8147 | return TCL_OK; } #endif /* PIKCHR_TCL */ | | | 8240 8241 8242 8243 8244 8245 8246 8247 | return TCL_OK; } #endif /* PIKCHR_TCL */ #line 8272 "pikchr.c" |
Changes to extsrc/pikchr.wasm.
cannot compute difference between binary files
Changes to extsrc/shell.c.
| ︙ | ︙ | |||
260 261 262 263 264 265 266 267 268 269 270 271 272 273 | #ifdef SQLITE_SHELL_FIDDLE /* Deselect most features from the console I/O package for Fiddle. */ # define SQLITE_CIO_NO_REDIRECT # define SQLITE_CIO_NO_CLASSIFY # define SQLITE_CIO_NO_TRANSLATE # define SQLITE_CIO_NO_SETMODE #endif /************************* Begin ../ext/consio/console_io.h ******************/ /* ** 2023 November 1 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: | > | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | #ifdef SQLITE_SHELL_FIDDLE /* Deselect most features from the console I/O package for Fiddle. */ # define SQLITE_CIO_NO_REDIRECT # define SQLITE_CIO_NO_CLASSIFY # define SQLITE_CIO_NO_TRANSLATE # define SQLITE_CIO_NO_SETMODE # define SQLITE_CIO_NO_FLUSH #endif /************************* Begin ../ext/consio/console_io.h ******************/ /* ** 2023 November 1 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: |
| ︙ | ︙ | |||
440 441 442 443 444 445 446 447 448 | oPutbUtf8(const char *cBuf, int nAccept); /* Like fPutbUtf8 except stream is always the designated error. */ #ifdef CONSIO_EPUTB SQLITE_INTERNAL_LINKAGE int ePutbUtf8(const char *cBuf, int nAccept); #endif /* ** Collect input like fgets(...) with special provisions for input | > > > > > > > > | < | | | 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 |
oPutbUtf8(const char *cBuf, int nAccept);
/* Like fPutbUtf8 except stream is always the designated error. */
#ifdef CONSIO_EPUTB
SQLITE_INTERNAL_LINKAGE int
ePutbUtf8(const char *cBuf, int nAccept);
#endif
/*
** Flush the given output stream. Return non-zero for success, else 0.
*/
#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE)
SQLITE_INTERNAL_LINKAGE int
fFlushBuffer(FILE *pfOut);
#endif
/*
** Collect input like fgets(...) with special provisions for input
** from the console on such platforms as require same. Newline
** translation may be done as set by set{Binary,Text}Mode().
** As a convenience, pfIn==NULL is treated as stdin.
*/
SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn);
/* Like fGetsUtf8 except stream is always the designated input. */
/* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
|
| ︙ | ︙ | |||
576 577 578 579 580 581 582 583 584 585 586 587 588 589 | # include <limits.h> # include <assert.h> /* # include "sqlite3.h" */ #endif #ifndef HAVE_CONSOLE_IO_H # include "console_io.h" #endif #ifndef SQLITE_CIO_NO_TRANSLATE # if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT # ifndef SHELL_NO_SYSINC # include <io.h> # include <fcntl.h> # undef WIN32_LEAN_AND_MEAN | > > > | 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 | # include <limits.h> # include <assert.h> /* # include "sqlite3.h" */ #endif #ifndef HAVE_CONSOLE_IO_H # include "console_io.h" #endif #if defined(_MSC_VER) # pragma warning(disable : 4204) #endif #ifndef SQLITE_CIO_NO_TRANSLATE # if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT # ifndef SHELL_NO_SYSINC # include <io.h> # include <fcntl.h> # undef WIN32_LEAN_AND_MEAN |
| ︙ | ︙ | |||
597 598 599 600 601 602 603 | # endif # define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */ # endif #else # define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */ #endif | < < < < < | 608 609 610 611 612 613 614 615 616 617 618 619 620 621 |
# endif
# define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */
# endif
#else
# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */
#endif
#if CIO_WIN_WC_XLATE
static HANDLE handleOfFile(FILE *pf){
int fileDesc = _fileno(pf);
union { intptr_t osfh; HANDLE fh; } fid = {
(fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE
};
return fid.fh;
|
| ︙ | ︙ | |||
673 674 675 676 677 678 679 680 681 682 683 684 685 686 | return rv; # else ppst->pf = pf; ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); return ppst->reachesConsole; # endif } # if CIO_WIN_WC_XLATE /* Define console modes for use with the Windows Console API. */ # define SHELL_CONI_MODE \ (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) # define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ | > > > > | 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 | return rv; # else ppst->pf = pf; ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); return ppst->reachesConsole; # endif } # ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING # define ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x4) # endif # if CIO_WIN_WC_XLATE /* Define console modes for use with the Windows Console API. */ # define SHELL_CONI_MODE \ (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) # define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ |
| ︙ | ︙ | |||
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 |
# endif
return (int)fwrite(cBuf, 1, nAccept, pfOut);
# if CIO_WIN_WC_XLATE
}
# endif
}
# ifdef CONSIO_EPUTB
SQLITE_INTERNAL_LINKAGE int
ePutbUtf8(const char *cBuf, int nAccept){
FILE *pfErr;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ){
return conZstrEmit(ppst, cBuf, nAccept);
}else {
# endif
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 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 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 |
# endif
return (int)fwrite(cBuf, 1, nAccept, pfOut);
# if CIO_WIN_WC_XLATE
}
# endif
}
/*
** Flush the given output stream. Return non-zero for success, else 0.
*/
#if !defined(SQLITE_CIO_NO_FLUSH) && !defined(SQLITE_CIO_NO_SETMODE)
SQLITE_INTERNAL_LINKAGE int
fFlushBuffer(FILE *pfOut){
# if CIO_WIN_WC_XLATE && !defined(SHELL_OMIT_FIO_DUPE)
return FlushFileBuffers(handleOfFile(pfOut))? 1 : 0;
# else
return fflush(pfOut);
# endif
}
#endif
#if CIO_WIN_WC_XLATE \
&& !defined(SHELL_OMIT_FIO_DUPE) \
&& defined(SQLITE_USE_ONLY_WIN32)
static struct FileAltIds {
int fd;
HANDLE fh;
} altIdsOfFile(FILE *pf){
struct FileAltIds rv = { _fileno(pf) };
union { intptr_t osfh; HANDLE fh; } fid = {
(rv.fd>=0)? _get_osfhandle(rv.fd) : (intptr_t)INVALID_HANDLE_VALUE
};
rv.fh = fid.fh;
return rv;
}
SQLITE_INTERNAL_LINKAGE size_t
cfWrite(const void *buf, size_t osz, size_t ocnt, FILE *pf){
size_t rv = 0;
struct FileAltIds fai = altIdsOfFile(pf);
int fmode = _setmode(fai.fd, _O_BINARY);
_setmode(fai.fd, fmode);
while( rv < ocnt ){
size_t nbo = osz;
while( nbo > 0 ){
DWORD dwno = (nbo>(1L<<24))? 1L<<24 : (DWORD)nbo;
BOOL wrc = TRUE;
BOOL genCR = (fmode & _O_TEXT)!=0;
if( genCR ){
const char *pnl = (const char*)memchr(buf, '\n', nbo);
if( pnl ) nbo = pnl - (const char*)buf;
else genCR = 0;
}
if( dwno>0 ) wrc = WriteFile(fai.fh, buf, dwno, 0,0);
if( genCR && wrc ){
wrc = WriteFile(fai.fh, "\r\n", 2, 0,0);
++dwno; /* Skip over the LF */
}
if( !wrc ) return rv;
buf = (const char*)buf + dwno;
nbo += dwno;
}
++rv;
}
return rv;
}
SQLITE_INTERNAL_LINKAGE char *
cfGets(char *cBuf, int n, FILE *pf){
int nci = 0;
struct FileAltIds fai = altIdsOfFile(pf);
int fmode = _setmode(fai.fd, _O_BINARY);
BOOL eatCR = (fmode & _O_TEXT)!=0;
_setmode(fai.fd, fmode);
while( nci < n-1 ){
DWORD nr;
if( !ReadFile(fai.fh, cBuf+nci, 1, &nr, 0) || nr==0 ) break;
if( nr>0 && (!eatCR || cBuf[nci]!='\r') ) nci += nr;
}
if( nci < n ) cBuf[nci] = 0;
return (nci>0)? cBuf : 0;
}
# else
# define cfWrite(b,os,no,f) fwrite(b,os,no,f)
# define cfGets(b,n,f) fgets(b,n,f)
# endif
# ifdef CONSIO_EPUTB
SQLITE_INTERNAL_LINKAGE int
ePutbUtf8(const char *cBuf, int nAccept){
FILE *pfErr;
PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
# if CIO_WIN_WC_XLATE
if( pstReachesConsole(ppst) ){
return conZstrEmit(ppst, cBuf, nAccept);
}else {
# endif
return (int)cfWrite(cBuf, 1, nAccept, pfErr);
# if CIO_WIN_WC_XLATE
}
# endif
}
# endif /* defined(CONSIO_EPUTB) */
SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
|
| ︙ | ︙ | |||
1217 1218 1219 1220 1221 1222 1223 |
if( noc > 0 ){
cBuf[noc] = 0;
return cBuf;
}else return 0;
# endif
}else{
# endif
| | > > > > | 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 |
if( noc > 0 ){
cBuf[noc] = 0;
return cBuf;
}else return 0;
# endif
}else{
# endif
return cfGets(cBuf, ncMax, pfIn);
# if CIO_WIN_WC_XLATE
}
# endif
}
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
#if defined(_MSC_VER)
# pragma warning(default : 4204)
#endif
#undef SHELL_INVALID_FILE_PTR
/************************* End ../ext/consio/console_io.c ********************/
#ifndef SQLITE_SHELL_FIDDLE
|
| ︙ | ︙ | |||
1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 | * eputf(f, ...) => emit varargs per format f to error stream * oputb(b, n) => emit char buffer b[0..n-1] to default stream * * Note that the default stream is whatever has been last set via: * setOutputStream(FILE *pf) * This is normally the stream that CLI normal output goes to. * For the stand-alone CLI, it is stdout with no .output redirect. */ # define sputz(s,z) fPutsUtf8(z,s) # define sputf fPrintfUtf8 # define oputz(z) oPutsUtf8(z) # define oputf oPrintfUtf8 # define eputz(z) ePutsUtf8(z) # define eputf ePrintfUtf8 # define oputb(buf,na) oPutbUtf8(buf,na) #else /* For Fiddle, all console handling and emit redirection is omitted. */ | > > > > > > > > > | > > < | > | 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 |
* eputf(f, ...) => emit varargs per format f to error stream
* oputb(b, n) => emit char buffer b[0..n-1] to default stream
*
* Note that the default stream is whatever has been last set via:
* setOutputStream(FILE *pf)
* This is normally the stream that CLI normal output goes to.
* For the stand-alone CLI, it is stdout with no .output redirect.
*
* The ?putz(z) forms are required for the Fiddle builds for string literal
* output, in aid of enforcing format string to argument correspondence.
*/
# define sputz(s,z) fPutsUtf8(z,s)
# define sputf fPrintfUtf8
# define oputz(z) oPutsUtf8(z)
# define oputf oPrintfUtf8
# define eputz(z) ePutsUtf8(z)
# define eputf ePrintfUtf8
# define oputb(buf,na) oPutbUtf8(buf,na)
# define fflush(s) fFlushBuffer(s);
#else
/* For Fiddle, all console handling and emit redirection is omitted. */
/* These next 3 macros are for emitting formatted output. When complaints
* from the WASM build are issued for non-formatted output, when a mere
* string literal is to be emitted, the ?putz(z) forms should be used.
* (This permits compile-time checking of format string / argument mismatch.)
*/
# define oputf(fmt, ...) printf(fmt,__VA_ARGS__)
# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__)
# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__)
/* These next 3 macros are for emitting simple string literals. */
# define oputz(z) fputs(z,stdout)
# define eputz(z) fputs(z,stderr)
# define sputz(fp,z) fputs(z,fp)
# define oputb(buf,na) fwrite(buf,1,na,stdout)
# undef fflush
#endif
/* True if the timer is enabled */
static int enableTimer = 0;
/* A version of strcmp() that works with NULL values */
static int cli_strcmp(const char *a, const char *b){
|
| ︙ | ︙ | |||
1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 |
*/
static char *shell_strncpy(char *dest, const char *src, size_t n){
size_t i;
for(i=0; i<n-1 && src[i]!=0; i++) dest[i] = src[i];
dest[i] = 0;
return dest;
}
/*
** Optionally disable dynamic continuation prompt.
** Unless disabled, the continuation prompt shows open SQL lexemes if any,
** or open parentheses level if non-zero, or continuation prompt as set.
** This facility interacts with the scanner and process_input() where the
** below 5 macros are used.
| > > > > > > > > | 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 |
*/
static char *shell_strncpy(char *dest, const char *src, size_t n){
size_t i;
for(i=0; i<n-1 && src[i]!=0; i++) dest[i] = src[i];
dest[i] = 0;
return dest;
}
/*
** strcpy() workalike to squelch an unwarranted link-time warning
** from OpenBSD.
*/
static void shell_strcpy(char *dest, const char *src){
while( (*(dest++) = *(src++))!=0 ){}
}
/*
** Optionally disable dynamic continuation prompt.
** Unless disabled, the continuation prompt shows open SQL lexemes if any,
** or open parentheses level if non-zero, or continuation prompt as set.
** This facility interacts with the scanner and process_input() where the
** below 5 macros are used.
|
| ︙ | ︙ | |||
1568 1569 1570 1571 1572 1573 1574 |
|| (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){
return continuePrompt;
}else{
if( dynPrompt.zScannerAwaits ){
size_t ncp = strlen(continuePrompt);
size_t ndp = strlen(dynPrompt.zScannerAwaits);
if( ndp > ncp-3 ) return continuePrompt;
| | | 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 |
|| (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){
return continuePrompt;
}else{
if( dynPrompt.zScannerAwaits ){
size_t ncp = strlen(continuePrompt);
size_t ndp = strlen(dynPrompt.zScannerAwaits);
if( ndp > ncp-3 ) return continuePrompt;
shell_strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits);
while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' ';
shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
PROMPT_LEN_MAX-4);
}else{
if( dynPrompt.inParenLevel>9 ){
shell_strncpy(dynPrompt.dynamicPrompt, "(..", 4);
}else if( dynPrompt.inParenLevel<0 ){
|
| ︙ | ︙ | |||
2829 2830 2831 2832 2833 2834 2835 | ****************************************************************************** ** ** This SQLite extension implements functions that compute SHA3 hashes ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. ** Two SQL functions are implemented: ** ** sha3(X,SIZE) | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 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 3031 3032 3033 3034 3035 3036 3037 3038 |
******************************************************************************
**
** This SQLite extension implements functions that compute SHA3 hashes
** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard.
** Two SQL functions are implemented:
**
** sha3(X,SIZE)
** sha3_agg(Y,SIZE)
** sha3_query(Z,SIZE)
**
** The sha3(X) function computes the SHA3 hash of the input X, or NULL if
** X is NULL. If inputs X is text, the UTF-8 rendering of that text is
** used to compute the hash. If X is a BLOB, then the binary data of the
** blob is used to compute the hash. If X is an integer or real number,
** then that number if converted into UTF-8 text and the hash is computed
** over the text.
**
** The sha3_agg(Y) function computes the SHA3 hash of all Y inputs. Since
** order is important for the hash, it is recommended that the Y expression
** by followed by an ORDER BY clause to guarantee that the inputs occur
** in the desired order.
**
** The sha3_query(Y) function evaluates all queries in the SQL statements of Y
** and returns a hash of their results.
**
** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm
** is used. If SIZE is included it must be one of the integers 224, 256,
** 384, or 512, to determine SHA3 hash variant that is computed.
**
** Because the sha3_agg() and sha3_query() functions compute a hash over
** multiple values, the values are encode to use include type information.
**
** In sha3_agg(), the sequence of bytes that gets hashed for each input
** Y depends on the datatype of Y:
**
** typeof(Y)='null' A single "N" is hashed. (One byte)
**
** typeof(Y)='integer' The data hash is the character "I" followed
** by an 8-byte big-endian binary of the
** 64-bit signed integer. (Nine bytes total.)
**
** typeof(Y)='real' The character "F" followed by an 8-byte
** big-ending binary of the double. (Nine
** bytes total.)
**
** typeof(Y)='text' The hash is over prefix "Tnnn:" followed
** by the UTF8 encoding of the text. The "nnn"
** in the prefix is the minimum-length decimal
** representation of the octet_length of the text.
** Notice the ":" at the end of the prefix, which
** is needed to separate the prefix from the
** content in cases where the content starts
** with a digit.
**
** typeof(Y)='blob' The hash is taken over prefix "Bnnn:" followed
** by the binary content of the blob. The "nnn"
** in the prefix is the mimimum-length decimal
** representation of the byte-length of the blob.
**
** According to the rules above, all of the following SELECT statements
** should return TRUE:
**
** SELECT sha3(1) = sha3('1');
**
** SELECT sha3('hello') = sha3(x'68656c6c6f');
**
** WITH a(x) AS (VALUES('xyzzy'))
** SELECT sha3_agg(x) = sha3('T5:xyzzy') FROM a;
**
** WITH a(x) AS (VALUES(x'010203'))
** SELECT sha3_agg(x) = sha3(x'42333a010203') FROM a;
**
** WITH a(x) AS (VALUES(0x123456))
** SELECT sha3_agg(x) = sha3(x'490000000000123456') FROM a;
**
** WITH a(x) AS (VALUES(100.015625))
** SELECT sha3_agg(x) = sha3(x'464059010000000000') FROM a;
**
** WITH a(x) AS (VALUES(NULL))
** SELECT sha3_agg(x) = sha3('N') FROM a;
**
**
** In sha3_query(), individual column values are encoded as with
** sha3_agg(), but with the addition that a single "R" character is
** inserted at the start of each row.
**
** Note that sha3_agg() hashes rows for which Y is NULL. Add a FILTER
** clause if NULL rows should be excluded:
**
** SELECT sha3_agg(x ORDER BY rowid) FILTER(WHERE x NOT NULL) FROM t1;
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <stdarg.h>
|
| ︙ | ︙ | |||
2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 |
union {
u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */
unsigned char x[1600]; /* ... or 1600 bytes */
} u;
unsigned nRate; /* Bytes of input accepted per Keccak iteration */
unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */
unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */
};
/*
** A single step of the Keccak mixing function for a 1600-bit state
*/
static void KeccakF1600Step(SHA3Context *p){
int i;
| > | 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 |
union {
u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */
unsigned char x[1600]; /* ... or 1600 bytes */
} u;
unsigned nRate; /* Bytes of input accepted per Keccak iteration */
unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */
unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */
unsigned iSize; /* 224, 256, 358, or 512 */
};
/*
** A single step of the Keccak mixing function for a 1600-bit state
*/
static void KeccakF1600Step(SHA3Context *p){
int i;
|
| ︙ | ︙ | |||
3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 |
/*
** Initialize a new hash. iSize determines the size of the hash
** in bits and should be one of 224, 256, 384, or 512. Or iSize
** can be zero to use the default hash size of 256 bits.
*/
static void SHA3Init(SHA3Context *p, int iSize){
memset(p, 0, sizeof(*p));
if( iSize>=128 && iSize<=512 ){
p->nRate = (1600 - ((iSize + 31)&~31)*2)/8;
}else{
p->nRate = (1600 - 2*256)/8;
}
#if SHA3_BYTEORDER==1234
/* Known to be little-endian at compile-time. No-op */
| > | 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 |
/*
** Initialize a new hash. iSize determines the size of the hash
** in bits and should be one of 224, 256, 384, or 512. Or iSize
** can be zero to use the default hash size of 256 bits.
*/
static void SHA3Init(SHA3Context *p, int iSize){
memset(p, 0, sizeof(*p));
p->iSize = iSize;
if( iSize>=128 && iSize<=512 ){
p->nRate = (1600 - ((iSize + 31)&~31)*2)/8;
}else{
p->nRate = (1600 - 2*256)/8;
}
#if SHA3_BYTEORDER==1234
/* Known to be little-endian at compile-time. No-op */
|
| ︙ | ︙ | |||
3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 | char zBuf[50]; va_start(ap, zFormat); sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap); va_end(ap); n = (int)strlen(zBuf); SHA3Update(p, (unsigned char*)zBuf, n); } /* ** Implementation of the sha3_query(SQL,SIZE) function. ** ** This function compiles and runs the SQL statement(s) given in the ** argument. The results are hashed using a SIZE-bit SHA3. The default ** size is 256. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 |
char zBuf[50];
va_start(ap, zFormat);
sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap);
va_end(ap);
n = (int)strlen(zBuf);
SHA3Update(p, (unsigned char*)zBuf, n);
}
/*
** Update a SHA3Context using a single sqlite3_value.
*/
static void sha3UpdateFromValue(SHA3Context *p, sqlite3_value *pVal){
switch( sqlite3_value_type(pVal) ){
case SQLITE_NULL: {
SHA3Update(p, (const unsigned char*)"N",1);
break;
}
case SQLITE_INTEGER: {
sqlite3_uint64 u;
int j;
unsigned char x[9];
sqlite3_int64 v = sqlite3_value_int64(pVal);
memcpy(&u, &v, 8);
for(j=8; j>=1; j--){
x[j] = u & 0xff;
u >>= 8;
}
x[0] = 'I';
SHA3Update(p, x, 9);
break;
}
case SQLITE_FLOAT: {
sqlite3_uint64 u;
int j;
unsigned char x[9];
double r = sqlite3_value_double(pVal);
memcpy(&u, &r, 8);
for(j=8; j>=1; j--){
x[j] = u & 0xff;
u >>= 8;
}
x[0] = 'F';
SHA3Update(p,x,9);
break;
}
case SQLITE_TEXT: {
int n2 = sqlite3_value_bytes(pVal);
const unsigned char *z2 = sqlite3_value_text(pVal);
sha3_step_vformat(p,"T%d:",n2);
SHA3Update(p, z2, n2);
break;
}
case SQLITE_BLOB: {
int n2 = sqlite3_value_bytes(pVal);
const unsigned char *z2 = sqlite3_value_blob(pVal);
sha3_step_vformat(p,"B%d:",n2);
SHA3Update(p, z2, n2);
break;
}
}
}
/*
** Implementation of the sha3_query(SQL,SIZE) function.
**
** This function compiles and runs the SQL statement(s) given in the
** argument. The results are hashed using a SIZE-bit SHA3. The default
** size is 256.
|
| ︙ | ︙ | |||
3450 3451 3452 3453 3454 3455 3456 |
SHA3Update(&cx,(unsigned char*)z,n);
}
/* Compute a hash over the result of the query */
while( SQLITE_ROW==sqlite3_step(pStmt) ){
SHA3Update(&cx,(const unsigned char*)"R",1);
for(i=0; i<nCol; i++){
| | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 |
SHA3Update(&cx,(unsigned char*)z,n);
}
/* Compute a hash over the result of the query */
while( SQLITE_ROW==sqlite3_step(pStmt) ){
SHA3Update(&cx,(const unsigned char*)"R",1);
for(i=0; i<nCol; i++){
sha3UpdateFromValue(&cx, sqlite3_column_value(pStmt,i));
}
}
sqlite3_finalize(pStmt);
}
sqlite3_result_blob(context, SHA3Final(&cx), iSize/8, SQLITE_TRANSIENT);
}
/*
** xStep function for sha3_agg().
*/
static void sha3AggStep(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
SHA3Context *p;
p = (SHA3Context*)sqlite3_aggregate_context(context, sizeof(*p));
if( p==0 ) return;
if( p->nRate==0 ){
int sz = 256;
if( argc==2 ){
sz = sqlite3_value_int(argv[1]);
if( sz!=224 && sz!=384 && sz!=512 ){
sz = 256;
}
}
SHA3Init(p, sz);
}
sha3UpdateFromValue(p, argv[0]);
}
/*
** xFinal function for sha3_agg().
*/
static void sha3AggFinal(sqlite3_context *context){
SHA3Context *p;
p = (SHA3Context*)sqlite3_aggregate_context(context, sizeof(*p));
if( p==0 ) return;
if( p->iSize ){
sqlite3_result_blob(context, SHA3Final(p), p->iSize/8, SQLITE_TRANSIENT);
}
}
#ifdef _WIN32
#endif
int sqlite3_shathree_init(
sqlite3 *db,
|
| ︙ | ︙ | |||
3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 |
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0, sha3Func, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3", 2,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0, sha3Func, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3_query", 1,
SQLITE_UTF8 | SQLITE_DIRECTONLY,
0, sha3QueryFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3_query", 2,
| > > > > > > > > > > | 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 |
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0, sha3Func, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3", 2,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0, sha3Func, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3_agg", 1,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0, 0, sha3AggStep, sha3AggFinal);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3_agg", 2,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0, 0, sha3AggStep, sha3AggFinal);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3_query", 1,
SQLITE_UTF8 | SQLITE_DIRECTONLY,
0, sha3QueryFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "sha3_query", 2,
|
| ︙ | ︙ | |||
4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 |
rc = sqlite3_create_collation(db, "decimal", SQLITE_UTF8,
0, decimalCollFunc);
}
return rc;
}
/************************* End ../ext/misc/decimal.c ********************/
#undef sqlite3_base_init
#define sqlite3_base_init sqlite3_base64_init
/************************* Begin ../ext/misc/base64.c ******************/
/*
** 2022-11-18
**
** The author disclaims copyright to this source code. In place of
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 |
rc = sqlite3_create_collation(db, "decimal", SQLITE_UTF8,
0, decimalCollFunc);
}
return rc;
}
/************************* End ../ext/misc/decimal.c ********************/
/************************* Begin ../ext/misc/percentile.c ******************/
/*
** 2013-05-28
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** This file contains code to implement the percentile(Y,P) SQL function
** and similar as described below:
**
** (1) The percentile(Y,P) function is an aggregate function taking
** exactly two arguments.
**
** (2) If the P argument to percentile(Y,P) is not the same for every
** row in the aggregate then an error is thrown. The word "same"
** in the previous sentence means that the value differ by less
** than 0.001.
**
** (3) If the P argument to percentile(Y,P) evaluates to anything other
** than a number in the range of 0.0 to 100.0 inclusive then an
** error is thrown.
**
** (4) If any Y argument to percentile(Y,P) evaluates to a value that
** is not NULL and is not numeric then an error is thrown.
**
** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
** infinity then an error is thrown. (SQLite always interprets NaN
** values as NULL.)
**
** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
** including CASE WHEN expressions.
**
** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
** one million (1,000,000) rows.
**
** (8) If there are no non-NULL values for Y, then percentile(Y,P)
** returns NULL.
**
** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
** returns the one Y value.
**
** (10) If there N non-NULL values of Y where N is two or more and
** the Y values are ordered from least to greatest and a graph is
** drawn from 0 to N-1 such that the height of the graph at J is
** the J-th Y value and such that straight lines are drawn between
** adjacent Y values, then the percentile(Y,P) function returns
** the height of the graph at P*(N-1)/100.
**
** (11) The percentile(Y,P) function always returns either a floating
** point number or NULL.
**
** (12) The percentile(Y,P) is implemented as a single C99 source-code
** file that compiles into a shared-library or DLL that can be loaded
** into SQLite using the sqlite3_load_extension() interface.
**
** (13) A separate median(Y) function is the equivalent percentile(Y,50).
**
** (14) A separate percentile_cont(Y,P) function is equivalent to
** percentile(Y,P/100.0). In other words, the fraction value in
** the second argument is in the range of 0 to 1 instead of 0 to 100.
**
** (15) A separate percentile_disc(Y,P) function is like
** percentile_cont(Y,P) except that instead of returning the weighted
** average of the nearest two input values, it returns the next lower
** value. So the percentile_disc(Y,P) will always return a value
** that was one of the inputs.
**
** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and
** percentile_disc(Y,P) can be used as window functions.
**
** Differences from standard SQL:
**
** * The percentile_cont(X,P) function is equivalent to the following in
** standard SQL:
**
** (percentile_cont(P) WITHIN GROUP (ORDER BY X))
**
** The SQLite syntax is much more compact. The standard SQL syntax
** is also supported if SQLite is compiled with the
** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option.
**
** * No median(X) function exists in the SQL standard. App developers
** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)".
**
** * No percentile(Y,P) function exists in the SQL standard. Instead of
** percential(Y,P), developers must write this:
** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that
** the fraction parameter to percentile() goes from 0 to 100 whereas
** the fraction parameter in SQL standard percentile_cont() goes from
** 0 to 1.
**
** Implementation notes as of 2024-08-31:
**
** * The regular aggregate-function versions of these routines work
** by accumulating all values in an array of doubles, then sorting
** that array using quicksort before computing the answer. Thus
** the runtime is O(NlogN) where N is the number of rows of input.
**
** * For the window-function versions of these routines, the array of
** inputs is sorted as soon as the first value is computed. Thereafter,
** the array is kept in sorted order using an insert-sort. This
** results in O(N*K) performance where K is the size of the window.
** One can imagine alternative implementations that give O(N*logN*logK)
** performance, but they require more complex logic and data structures.
** The developers have elected to keep the asymptotically slower
** algorithm for now, for simplicity, under the theory that window
** functions are seldom used and when they are, the window size K is
** often small. The developers might revisit that decision later,
** should the need arise.
*/
#if defined(SQLITE3_H)
/* no-op */
#elif defined(SQLITE_STATIC_PERCENTILE)
/* # include "sqlite3.h" */
#else
/* # include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#endif
#include <assert.h>
#include <string.h>
#include <stdlib.h>
/* The following object is the group context for a single percentile()
** aggregate. Remember all input Y values until the very end.
** Those values are accumulated in the Percentile.a[] array.
*/
typedef struct Percentile Percentile;
struct Percentile {
unsigned nAlloc; /* Number of slots allocated for a[] */
unsigned nUsed; /* Number of slots actually used in a[] */
char bSorted; /* True if a[] is already in sorted order */
char bKeepSorted; /* True if advantageous to keep a[] sorted */
char bPctValid; /* True if rPct is valid */
double rPct; /* Fraction. 0.0 to 1.0 */
double *a; /* Array of Y values */
};
/* Details of each function in the percentile family */
typedef struct PercentileFunc PercentileFunc;
struct PercentileFunc {
const char *zName; /* Function name */
char nArg; /* Number of arguments */
char mxFrac; /* Maximum value of the "fraction" input */
char bDiscrete; /* True for percentile_disc() */
};
static const PercentileFunc aPercentFunc[] = {
{ "median", 1, 1, 0 },
{ "percentile", 2, 100, 0 },
{ "percentile_cont", 2, 1, 0 },
{ "percentile_disc", 2, 1, 1 },
};
/*
** Return TRUE if the input floating-point number is an infinity.
*/
static int percentIsInfinity(double r){
sqlite3_uint64 u;
assert( sizeof(u)==sizeof(r) );
memcpy(&u, &r, sizeof(u));
return ((u>>52)&0x7ff)==0x7ff;
}
/*
** Return TRUE if two doubles differ by 0.001 or less.
*/
static int percentSameValue(double a, double b){
a -= b;
return a>=-0.001 && a<=0.001;
}
/*
** Search p (which must have p->bSorted) looking for an entry with
** value y. Return the index of that entry.
**
** If bExact is true, return -1 if the entry is not found.
**
** If bExact is false, return the index at which a new entry with
** value y should be insert in order to keep the values in sorted
** order. The smallest return value in this case will be 0, and
** the largest return value will be p->nUsed.
*/
static int percentBinarySearch(Percentile *p, double y, int bExact){
int iFirst = 0; /* First element of search range */
int iLast = p->nUsed - 1; /* Last element of search range */
while( iLast>=iFirst ){
int iMid = (iFirst+iLast)/2;
double x = p->a[iMid];
if( x<y ){
iFirst = iMid + 1;
}else if( x>y ){
iLast = iMid - 1;
}else{
return iMid;
}
}
if( bExact ) return -1;
return iFirst;
}
/*
** Generate an error for a percentile function.
**
** The error format string must have exactly one occurrance of "%%s()"
** (with two '%' characters). That substring will be replaced by the name
** of the function.
*/
static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
char *zMsg1;
char *zMsg2;
va_list ap;
va_start(ap, zFormat);
zMsg1 = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, pFunc->zName) : 0;
sqlite3_result_error(pCtx, zMsg2, -1);
sqlite3_free(zMsg1);
sqlite3_free(zMsg2);
}
/*
** The "step" function for percentile(Y,P) is called once for each
** input row.
*/
static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
Percentile *p;
double rPct;
int eType;
double y;
assert( argc==2 || argc==1 );
if( argc==1 ){
/* Requirement 13: median(Y) is the same as percentile(Y,50). */
rPct = 0.5;
}else{
/* Requirement 3: P must be a number between 0 and 100 */
PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
eType = sqlite3_value_numeric_type(argv[1]);
rPct = sqlite3_value_double(argv[1])/(double)pFunc->mxFrac;
if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
|| rPct<0.0 || rPct>1.0
){
percentError(pCtx, "the fraction argument to %%s()"
" is not between 0.0 and %.1f",
(double)pFunc->mxFrac);
return;
}
}
/* Allocate the session context. */
p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p==0 ) return;
/* Remember the P value. Throw an error if the P value is different
** from any prior row, per Requirement (2). */
if( !p->bPctValid ){
p->rPct = rPct;
p->bPctValid = 1;
}else if( !percentSameValue(p->rPct,rPct) ){
percentError(pCtx, "the fraction argument to %%s()"
" is not the same for all input rows");
return;
}
/* Ignore rows for which Y is NULL */
eType = sqlite3_value_type(argv[0]);
if( eType==SQLITE_NULL ) return;
/* If not NULL, then Y must be numeric. Otherwise throw an error.
** Requirement 4 */
if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
percentError(pCtx, "input to %%s() is not numeric");
return;
}
/* Throw an error if the Y value is infinity or NaN */
y = sqlite3_value_double(argv[0]);
if( percentIsInfinity(y) ){
percentError(pCtx, "Inf input to %%s()");
return;
}
/* Allocate and store the Y */
if( p->nUsed>=p->nAlloc ){
unsigned n = p->nAlloc*2 + 250;
double *a = sqlite3_realloc64(p->a, sizeof(double)*n);
if( a==0 ){
sqlite3_free(p->a);
memset(p, 0, sizeof(*p));
sqlite3_result_error_nomem(pCtx);
return;
}
p->nAlloc = n;
p->a = a;
}
if( p->nUsed==0 ){
p->a[p->nUsed++] = y;
p->bSorted = 1;
}else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
p->a[p->nUsed++] = y;
}else if( p->bKeepSorted ){
int i;
i = percentBinarySearch(p, y, 0);
if( i<p->nUsed ){
memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
}
p->a[i] = y;
p->nUsed++;
}else{
p->a[p->nUsed++] = y;
p->bSorted = 0;
}
}
/*
** Interchange two doubles.
*/
#define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;}
/*
** Sort an array of doubles.
**
** Algorithm: quicksort
**
** This is implemented separately rather than using the qsort() routine
** from the standard library because:
**
** (1) To avoid a dependency on qsort()
** (2) To avoid the function call to the comparison routine for each
** comparison.
*/
static void percentSort(double *a, unsigned int n){
int iLt; /* Entries before a[iLt] are less than rPivot */
int iGt; /* Entries at or after a[iGt] are greater than rPivot */
int i; /* Loop counter */
double rPivot; /* The pivot value */
assert( n>=2 );
if( a[0]>a[n-1] ){
SWAP_DOUBLE(a[0],a[n-1])
}
if( n==2 ) return;
iGt = n-1;
i = n/2;
if( a[0]>a[i] ){
SWAP_DOUBLE(a[0],a[i])
}else if( a[i]>a[iGt] ){
SWAP_DOUBLE(a[i],a[iGt])
}
if( n==3 ) return;
rPivot = a[i];
iLt = i = 1;
do{
if( a[i]<rPivot ){
if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
iLt++;
i++;
}else if( a[i]>rPivot ){
do{
iGt--;
}while( iGt>i && a[iGt]>rPivot );
SWAP_DOUBLE(a[i],a[iGt])
}else{
i++;
}
}while( i<iGt );
if( iLt>=2 ) percentSort(a, iLt);
if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
/* Uncomment for testing */
#if 0
for(i=0; i<n-1; i++){
assert( a[i]<=a[i+1] );
}
#endif
}
/*
** The "inverse" function for percentile(Y,P) is called to remove a
** row that was previously inserted by "step".
*/
static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){
Percentile *p;
int eType;
double y;
int i;
assert( argc==2 || argc==1 );
/* Allocate the session context. */
p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
assert( p!=0 );
/* Ignore rows for which Y is NULL */
eType = sqlite3_value_type(argv[0]);
if( eType==SQLITE_NULL ) return;
/* If not NULL, then Y must be numeric. Otherwise throw an error.
** Requirement 4 */
if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
return;
}
/* Ignore the Y value if it is infinity or NaN */
y = sqlite3_value_double(argv[0]);
if( percentIsInfinity(y) ){
return;
}
if( p->bSorted==0 ){
assert( p->nUsed>1 );
percentSort(p->a, p->nUsed);
p->bSorted = 1;
}
p->bKeepSorted = 1;
/* Find and remove the row */
i = percentBinarySearch(p, y, 1);
if( i>=0 ){
p->nUsed--;
if( i<p->nUsed ){
memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
}
}
}
/*
** Compute the final output of percentile(). Clean up all allocated
** memory if and only if bIsFinal is true.
*/
static void percentCompute(sqlite3_context *pCtx, int bIsFinal){
Percentile *p;
PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
unsigned i1, i2;
double v1, v2;
double ix, vx;
p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
if( p==0 ) return;
if( p->a==0 ) return;
if( p->nUsed ){
if( p->bSorted==0 ){
assert( p->nUsed>1 );
percentSort(p->a, p->nUsed);
p->bSorted = 1;
}
ix = p->rPct*(p->nUsed-1);
i1 = (unsigned)ix;
if( pFunc->bDiscrete ){
vx = p->a[i1];
}else{
i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
v1 = p->a[i1];
v2 = p->a[i2];
vx = v1 + (v2-v1)*(ix-i1);
}
sqlite3_result_double(pCtx, vx);
}
if( bIsFinal ){
sqlite3_free(p->a);
memset(p, 0, sizeof(*p));
}else{
p->bKeepSorted = 1;
}
}
static void percentFinal(sqlite3_context *pCtx){
percentCompute(pCtx, 1);
}
static void percentValue(sqlite3_context *pCtx){
percentCompute(pCtx, 0);
}
#if defined(_WIN32) && !defined(SQLITE3_H) && !defined(SQLITE_STATIC_PERCENTILE)
#endif
int sqlite3_percentile_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
int i;
#if defined(SQLITE3_H) || defined(SQLITE_STATIC_PERCENTILE)
(void)pApi; /* Unused parameter */
#else
SQLITE_EXTENSION_INIT2(pApi);
#endif
(void)pzErrMsg; /* Unused parameter */
for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){
rc = sqlite3_create_window_function(db,
aPercentFunc[i].zName,
aPercentFunc[i].nArg,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_SELFORDER1,
(void*)&aPercentFunc[i],
percentStep, percentFinal, percentValue, percentInverse, 0);
if( rc ) break;
}
return rc;
}
/************************* End ../ext/misc/percentile.c ********************/
#undef sqlite3_base_init
#define sqlite3_base_init sqlite3_base64_init
/************************* Begin ../ext/misc/base64.c ******************/
/*
** 2022-11-18
**
** The author disclaims copyright to this source code. In place of
|
| ︙ | ︙ | |||
5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 | ** start, stop, and step columns, and if present, it uses those constraints ** to bound the sequence of generated values. If the equality constraints ** are missing, it uses 0 for start, 4294967295 for stop, and 1 for step. ** xBestIndex returns a small cost when both start and stop are available, ** and a very large cost if either start or stop are unavailable. This ** encourages the query planner to order joins such that the bounds of the ** series are well-defined. */ /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <limits.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Return that member of a generate_series(...) sequence whose 0-based ** index is ix. The 0th member is given by smBase. The sequence members ** progress per ix increment by smStep. */ | > > > > > > > > > > > > > > > > > > > > | > | | > | > > | | | > > | | | 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 |
** start, stop, and step columns, and if present, it uses those constraints
** to bound the sequence of generated values. If the equality constraints
** are missing, it uses 0 for start, 4294967295 for stop, and 1 for step.
** xBestIndex returns a small cost when both start and stop are available,
** and a very large cost if either start or stop are unavailable. This
** encourages the query planner to order joins such that the bounds of the
** series are well-defined.
**
** Update on 2024-08-22:
** xBestIndex now also looks for equality and inequality constraints against
** the value column and uses those constraints as additional bounds against
** the sequence range. Thus, a query like this:
**
** SELECT value FROM generate_series($SA,$EA)
** WHERE value BETWEEN $SB AND $EB;
**
** Is logically the same as:
**
** SELECT value FROM generate_series(max($SA,$SB),min($EA,$EB));
**
** Constraints on the value column can server as substitutes for constraints
** on the hidden start and stop columns. So, the following two queries
** are equivalent:
**
** SELECT value FROM generate_series($S,$E);
** SELECT value FROM generate_series WHERE value BETWEEN $S and $E;
**
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <limits.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Return that member of a generate_series(...) sequence whose 0-based
** index is ix. The 0th member is given by smBase. The sequence members
** progress per ix increment by smStep.
*/
static sqlite3_int64 genSeqMember(
sqlite3_int64 smBase,
sqlite3_int64 smStep,
sqlite3_uint64 ix
){
static const sqlite3_uint64 mxI64 =
((sqlite3_uint64)0x7fffffff)<<32 | 0xffffffff;
if( ix>=mxI64 ){
/* Get ix into signed i64 range. */
ix -= mxI64;
/* With 2's complement ALU, this next can be 1 step, but is split into
* 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
smBase += (mxI64/2) * smStep;
smBase += (mxI64 - mxI64/2) * smStep;
}
/* Under UBSAN (or on 1's complement machines), must do this last term
* in steps to avoid the dreaded (and harmless) signed multiply overlow. */
if( ix>=2 ){
sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
smBase += ix2*smStep;
ix -= ix2;
}
return smBase + ((sqlite3_int64)ix)*smStep;
}
/* typedef unsigned char u8; */
typedef struct SequenceSpec {
sqlite3_int64 iOBase; /* Original starting value ("start") */
sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
sqlite3_int64 iBase; /* Starting value to actually use */
sqlite3_int64 iTerm; /* Terminal value to actually use */
sqlite3_int64 iStep; /* Increment ("step") */
sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */
sqlite3_uint64 uSeqIndexNow; /* Current index during generation */
sqlite3_int64 iValueNow; /* Current value during generation */
u8 isNotEOF; /* Sequence generation not exhausted */
u8 isReversing; /* Sequence is being reverse generated */
} SequenceSpec;
|
| ︙ | ︙ | |||
5925 5926 5927 5928 5929 5930 5931 |
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
series_cursor *pCur = (series_cursor*)cur;
sqlite3_int64 x = 0;
switch( i ){
| | | | > > | 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 |
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
series_cursor *pCur = (series_cursor*)cur;
sqlite3_int64 x = 0;
switch( i ){
case SERIES_COLUMN_START: x = pCur->ss.iOBase; break;
case SERIES_COLUMN_STOP: x = pCur->ss.iOTerm; break;
case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break;
default: x = pCur->ss.iValueNow; break;
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
}
#ifndef LARGEST_UINT64
#define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
#define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
#define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
#endif
/*
** Return the rowid for the current row, logically equivalent to n+1 where
** "n" is the ascending integer in the aforesaid production definition.
*/
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
| ︙ | ︙ | |||
5976 5977 5978 5979 5980 5981 5982 | ** once prior to any call to seriesColumn() or seriesRowid() or ** seriesEof(). ** ** The query plan selected by seriesBestIndex is passed in the idxNum ** parameter. (idxStr is not used in this implementation.) idxNum ** is a bitmask showing which constraints are available: ** | | | | < < | | > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < > > > > > | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > | | > | | | > > > > > > | 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 |
** once prior to any call to seriesColumn() or seriesRowid() or
** seriesEof().
**
** The query plan selected by seriesBestIndex is passed in the idxNum
** parameter. (idxStr is not used in this implementation.) idxNum
** is a bitmask showing which constraints are available:
**
** 0x0001: start=VALUE
** 0x0002: stop=VALUE
** 0x0004: step=VALUE
** 0x0008: descending order
** 0x0010: ascending order
** 0x0020: LIMIT VALUE
** 0x0040: OFFSET VALUE
** 0x0080: value=VALUE
** 0x0100: value>=VALUE
** 0x0200: value>VALUE
** 0x1000: value<=VALUE
** 0x2000: value<VALUE
**
** This routine should initialize the cursor and position it so that it
** is pointing at the first row, or pointing off the end of the table
** (so that seriesEof() will return true) if the table is empty.
*/
static int seriesFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStrUnused,
int argc, sqlite3_value **argv
){
series_cursor *pCur = (series_cursor *)pVtabCursor;
int i = 0;
int returnNoRows = 0;
sqlite3_int64 iMin = SMALLEST_INT64;
sqlite3_int64 iMax = LARGEST_INT64;
sqlite3_int64 iLimit = 0;
sqlite3_int64 iOffset = 0;
(void)idxStrUnused;
if( idxNum & 0x01 ){
pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
}else{
pCur->ss.iBase = 0;
}
if( idxNum & 0x02 ){
pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
}else{
pCur->ss.iTerm = 0xffffffff;
}
if( idxNum & 0x04 ){
pCur->ss.iStep = sqlite3_value_int64(argv[i++]);
if( pCur->ss.iStep==0 ){
pCur->ss.iStep = 1;
}else if( pCur->ss.iStep<0 ){
if( (idxNum & 0x10)==0 ) idxNum |= 0x08;
}
}else{
pCur->ss.iStep = 1;
}
/* If there are constraints on the value column but there are
** no constraints on the start, stop, and step columns, then
** initialize the default range to be the entire range of 64-bit signed
** integers. This range will contracted by the value column constraints
** further below.
*/
if( (idxNum & 0x05)==0 && (idxNum & 0x0380)!=0 ){
pCur->ss.iBase = SMALLEST_INT64;
}
if( (idxNum & 0x06)==0 && (idxNum & 0x3080)!=0 ){
pCur->ss.iTerm = LARGEST_INT64;
}
pCur->ss.iOBase = pCur->ss.iBase;
pCur->ss.iOTerm = pCur->ss.iTerm;
/* Extract the LIMIT and OFFSET values, but do not apply them yet.
** The range must first be constrained by the limits on value.
*/
if( idxNum & 0x20 ){
iLimit = sqlite3_value_int64(argv[i++]);
if( idxNum & 0x40 ){
iOffset = sqlite3_value_int64(argv[i++]);
}
}
if( idxNum & 0x3380 ){
/* Extract the maximum range of output values determined by
** constraints on the "value" column.
*/
if( idxNum & 0x0080 ){
iMin = iMax = sqlite3_value_int64(argv[i++]);
}else{
if( idxNum & 0x0300 ){
iMin = sqlite3_value_int64(argv[i++]);
if( idxNum & 0x0200 ){
if( iMin==LARGEST_INT64 ){
returnNoRows = 1;
}else{
iMin++;
}
}
}
if( idxNum & 0x3000 ){
iMax = sqlite3_value_int64(argv[i++]);
if( idxNum & 0x2000 ){
if( iMax==SMALLEST_INT64 ){
returnNoRows = 1;
}else{
iMax--;
}
}
}
if( iMin>iMax ){
returnNoRows = 1;
}
}
/* Try to reduce the range of values to be generated based on
** constraints on the "value" column.
*/
if( pCur->ss.iStep>0 ){
sqlite3_int64 szStep = pCur->ss.iStep;
if( pCur->ss.iBase<iMin ){
sqlite3_uint64 d = iMin - pCur->ss.iBase;
pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
}
if( pCur->ss.iTerm>iMax ){
sqlite3_uint64 d = pCur->ss.iTerm - iMax;
pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep;
}
}else{
sqlite3_int64 szStep = -pCur->ss.iStep;
assert( szStep>0 );
if( pCur->ss.iBase>iMax ){
sqlite3_uint64 d = pCur->ss.iBase - iMax;
pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
}
if( pCur->ss.iTerm<iMin ){
sqlite3_uint64 d = iMin - pCur->ss.iTerm;
pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep;
}
}
}
/* Apply LIMIT and OFFSET constraints, if any */
if( idxNum & 0x20 ){
if( iOffset>0 ){
pCur->ss.iBase += pCur->ss.iStep*iOffset;
}
if( iLimit>=0 ){
sqlite3_int64 iTerm;
iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep;
if( pCur->ss.iStep<0 ){
if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
}else{
if( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
}
}
}
for(i=0; i<argc; i++){
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
/* If any of the constraints have a NULL value, then return no rows.
** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
returnNoRows = 1;
break;
}
}
if( returnNoRows ){
pCur->ss.iBase = 1;
pCur->ss.iTerm = 0;
pCur->ss.iStep = 1;
}
if( idxNum & 0x08 ){
pCur->ss.isReversing = pCur->ss.iStep > 0;
}else{
pCur->ss.isReversing = pCur->ss.iStep < 0;
}
setupSequence( &pCur->ss );
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the generate_series virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
**
** In this implementation idxNum is used to represent the
** query plan. idxStr is unused.
**
** The query plan is represented by bits in idxNum:
**
** 0x0001 start = $num
** 0x0002 stop = $num
** 0x0004 step = $num
** 0x0008 output is in descending order
** 0x0010 output is in ascending order
** 0x0020 LIMIT $num
** 0x0040 OFFSET $num
** 0x0080 value = $num
** 0x0100 value >= $num
** 0x0200 value > $num
** 0x1000 value <= $num
** 0x2000 value < $num
**
** Only one of 0x0100 or 0x0200 will be returned. Similarly, only
** one of 0x1000 or 0x2000 will be returned. If the 0x0080 is set, then
** none of the 0xff00 bits will be set.
**
** The order of parameters passed to xFilter is as follows:
**
** * The argument to start= if bit 0x0001 is in the idxNum mask
** * The argument to stop= if bit 0x0002 is in the idxNum mask
** * The argument to step= if bit 0x0004 is in the idxNum mask
** * The argument to LIMIT if bit 0x0020 is in the idxNum mask
** * The argument to OFFSET if bit 0x0040 is in the idxNum mask
** * The argument to value=, or value>= or value> if any of
** bits 0x0380 are in the idxNum mask
** * The argument to value<= or value< if either of bits 0x3000
** are in the mask
**
*/
static int seriesBestIndex(
sqlite3_vtab *pVTab,
sqlite3_index_info *pIdxInfo
){
int i, j; /* Loop over constraints */
int idxNum = 0; /* The query plan bitmask */
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
int bStartSeen = 0; /* EQ constraint seen on the START column */
#endif
int unusableMask = 0; /* Mask of unusable constraints */
int nArg = 0; /* Number of arguments that seriesFilter() expects */
int aIdx[7]; /* Constraints on start, stop, step, LIMIT, OFFSET,
** and value. aIdx[5] covers value=, value>=, and
** value>, aIdx[6] covers value<= and value< */
const struct sqlite3_index_constraint *pConstraint;
/* This implementation assumes that the start, stop, and step columns
** are the last three columns in the virtual table. */
assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
aIdx[0] = aIdx[1] = aIdx[2] = aIdx[3] = aIdx[4] = aIdx[5] = aIdx[6] = -1;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
int iCol; /* 0 for start, 1 for stop, 2 for step */
int iMask; /* bitmask for those column */
int op = pConstraint->op;
if( op>=SQLITE_INDEX_CONSTRAINT_LIMIT
&& op<=SQLITE_INDEX_CONSTRAINT_OFFSET
){
if( pConstraint->usable==0 ){
/* do nothing */
}else if( op==SQLITE_INDEX_CONSTRAINT_LIMIT ){
aIdx[3] = i;
idxNum |= 0x20;
}else{
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
aIdx[4] = i;
idxNum |= 0x40;
}
continue;
}
if( pConstraint->iColumn==SERIES_COLUMN_VALUE ){
switch( op ){
case SQLITE_INDEX_CONSTRAINT_EQ:
case SQLITE_INDEX_CONSTRAINT_IS: {
idxNum |= 0x0080;
idxNum &= ~0x3300;
aIdx[5] = i;
aIdx[6] = -1;
bStartSeen = 1;
break;
}
case SQLITE_INDEX_CONSTRAINT_GE: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x0100;
idxNum &= ~0x0200;
aIdx[5] = i;
bStartSeen = 1;
break;
}
case SQLITE_INDEX_CONSTRAINT_GT: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x0200;
idxNum &= ~0x0100;
aIdx[5] = i;
bStartSeen = 1;
break;
}
case SQLITE_INDEX_CONSTRAINT_LE: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x1000;
idxNum &= ~0x2000;
aIdx[6] = i;
break;
}
case SQLITE_INDEX_CONSTRAINT_LT: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x2000;
idxNum &= ~0x1000;
aIdx[6] = i;
break;
}
}
continue;
}
iCol = pConstraint->iColumn - SERIES_COLUMN_START;
assert( iCol>=0 && iCol<=2 );
iMask = 1 << iCol;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
if( iCol==0 && op==SQLITE_INDEX_CONSTRAINT_EQ ){
bStartSeen = 1;
}
#endif
if( pConstraint->usable==0 ){
unusableMask |= iMask;
continue;
}else if( op==SQLITE_INDEX_CONSTRAINT_EQ ){
idxNum |= iMask;
aIdx[iCol] = i;
}
}
if( aIdx[3]==0 ){
/* Ignore OFFSET if LIMIT is omitted */
idxNum &= ~0x60;
aIdx[4] = 0;
}
for(i=0; i<7; i++){
if( (j = aIdx[i])>=0 ){
pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[j].omit =
!SQLITE_SERIES_CONSTRAINT_VERIFY || i>=3;
}
}
/* The current generate_column() implementation requires at least one
** argument (the START value). Legacy versions assumed START=0 if the
** first argument was omitted. Compile with -DZERO_ARGUMENT_GENERATE_SERIES
** to obtain the legacy behavior */
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
if( !bStartSeen ){
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg = sqlite3_mprintf(
"first argument to \"generate_series()\" missing or unusable");
return SQLITE_ERROR;
}
#endif
if( (unusableMask & ~idxNum)!=0 ){
/* The start, stop, and step columns are inputs. Therefore if there
** are unusable constraints on any of start, stop, or step then
** this plan is unusable */
return SQLITE_CONSTRAINT;
}
if( (idxNum & 0x03)==0x03 ){
/* Both start= and stop= boundaries are available. This is the
** the preferred case */
pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0));
pIdxInfo->estimatedRows = 1000;
if( pIdxInfo->nOrderBy>=1 && pIdxInfo->aOrderBy[0].iColumn==0 ){
if( pIdxInfo->aOrderBy[0].desc ){
idxNum |= 0x08;
}else{
idxNum |= 0x10;
}
pIdxInfo->orderByConsumed = 1;
}
}else if( (idxNum & 0x21)==0x21 ){
/* We have start= and LIMIT */
pIdxInfo->estimatedRows = 2500;
}else{
/* If either boundary is missing, we have to generate a huge span
** of numbers. Make this case very expensive so that the query
** planner will work hard to avoid it. */
pIdxInfo->estimatedRows = 2147483647;
}
pIdxInfo->idxNum = idxNum;
#ifdef SQLITE_INDEX_SCAN_HEX
pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_HEX;
#endif
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** generate_series virtual table.
*/
|
| ︙ | ︙ | |||
7449 7450 7451 7452 7453 7454 7455 |
mode_t mode, /* MODE parameter passed to writefile() */
sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */
){
if( zFile==0 ) return 1;
#if !defined(_WIN32) && !defined(WIN32)
if( S_ISLNK(mode) ){
const char *zTo = (const char*)sqlite3_value_text(pData);
| > > | | 8445 8446 8447 8448 8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 |
mode_t mode, /* MODE parameter passed to writefile() */
sqlite3_int64 mtime /* MTIME parameter (or -1 to not set time) */
){
if( zFile==0 ) return 1;
#if !defined(_WIN32) && !defined(WIN32)
if( S_ISLNK(mode) ){
const char *zTo = (const char*)sqlite3_value_text(pData);
if( zTo==0 ) return 1;
unlink(zFile);
if( symlink(zTo, zFile)<0 ) return 1;
}else
#endif
{
if( S_ISDIR(mode) ){
if( mkdir(zFile, mode) ){
/* The mkdir() call to create the directory failed. This might not
** be an error though - if there is already a directory at the same
|
| ︙ | ︙ | |||
7535 7536 7537 7538 7539 7540 7541 |
times[0].tv_nsec = times[1].tv_nsec = 0;
times[0].tv_sec = time(0);
times[1].tv_sec = mtime;
if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){
return 1;
}
#else
| | > > > > > | | | | | | > | 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 |
times[0].tv_nsec = times[1].tv_nsec = 0;
times[0].tv_sec = time(0);
times[1].tv_sec = mtime;
if( utimensat(AT_FDCWD, zFile, times, AT_SYMLINK_NOFOLLOW) ){
return 1;
}
#else
/* Legacy unix.
**
** Do not use utimes() on a symbolic link - it sees through the link and
** modifies the timestamps on the target. Or fails if the target does
** not exist. */
if( 0==S_ISLNK(mode) ){
struct timeval times[2];
times[0].tv_usec = times[1].tv_usec = 0;
times[0].tv_sec = time(0);
times[1].tv_sec = mtime;
if( utimes(zFile, times) ){
return 1;
}
}
#endif
}
return 0;
}
|
| ︙ | ︙ | |||
8362 8363 8364 8365 8366 8367 8368 |
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
zSql = sqlite3_mprintf(
"%z%s"
"SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
| | | 9366 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 |
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
zSql = sqlite3_mprintf(
"%z%s"
"SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
" JOIN pragma_table_xinfo(sm.name,%Q) AS pti"
" WHERE sm.type='table'",
zSql, zSep, zDb, zDb
);
if( zSql==0 ) return SQLITE_NOMEM;
zSep = " UNION ";
}
sqlite3_finalize(pS2);
|
| ︙ | ︙ | |||
9322 9323 9324 9325 9326 9327 9328 | ** * Only the "inflate/deflate" (zlib) compression method is supported */ /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #include <stdio.h> #include <string.h> #include <assert.h> | > | > | 10326 10327 10328 10329 10330 10331 10332 10333 10334 10335 10336 10337 10338 10339 10340 10341 10342 | ** * Only the "inflate/deflate" (zlib) compression method is supported */ /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #include <stdio.h> #include <string.h> #include <assert.h> #ifndef SQLITE_NO_STDINT # include <stdint.h> #endif #include <zlib.h> #ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_AMALGAMATION |
| ︙ | ︙ | |||
11613 11614 11615 11616 11617 11618 11619 |
*/
static void sqlarUncompressFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
uLong nData;
| | > | | | 12619 12620 12621 12622 12623 12624 12625 12626 12627 12628 12629 12630 12631 12632 12633 12634 12635 12636 12637 12638 12639 12640 12641 12642 12643 12644 12645 12646 12647 12648 12649 |
*/
static void sqlarUncompressFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
uLong nData;
sqlite3_int64 sz;
assert( argc==2 );
sz = sqlite3_value_int(argv[1]);
if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){
sqlite3_result_value(context, argv[0]);
}else{
uLongf szf = sz;
const Bytef *pData= sqlite3_value_blob(argv[0]);
Bytef *pOut = sqlite3_malloc(sz);
if( pOut==0 ){
sqlite3_result_error_nomem(context);
}else if( Z_OK!=uncompress(pOut, &szf, pData, nData) ){
sqlite3_result_error(context, "error in uncompress()", -1);
}else{
sqlite3_result_blob(context, pOut, szf, SQLITE_TRANSIENT);
}
sqlite3_free(pOut);
}
}
#ifdef _WIN32
|
| ︙ | ︙ | |||
12458 12459 12460 12461 12462 12463 12464 |
(void)idxStr;
(void)argc;
(void)argv;
rc = sqlite3_finalize(pCsr->pData);
pCsr->pData = 0;
if( rc==SQLITE_OK ){
rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg,
| | | 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 13476 13477 13478 13479 |
(void)idxStr;
(void)argc;
(void)argv;
rc = sqlite3_finalize(pCsr->pData);
pCsr->pData = 0;
if( rc==SQLITE_OK ){
rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg,
"SELECT * FROM main.%Q WHERE sqlite_expert_sample()", pVtab->pTab->zName
);
}
if( rc==SQLITE_OK ){
rc = expertNext(cur);
}
return rc;
|
| ︙ | ︙ | |||
13332 13333 13334 13335 13336 13337 13338 |
int nByte; /* Bytes of space allocated at z */
int n; /* Size of buffer z */
char *z; /* SQLITE_TEXT/BLOB value */
} aSlot[1];
};
/*
| | | | 14339 14340 14341 14342 14343 14344 14345 14346 14347 14348 14349 14350 14351 14352 14353 14354 14355 14356 14357 14358 14359 14360 14361 14362 14363 14364 14365 14366 |
int nByte; /* Bytes of space allocated at z */
int n; /* Size of buffer z */
char *z; /* SQLITE_TEXT/BLOB value */
} aSlot[1];
};
/*
** Implementation of scalar function sqlite_expert_rem().
*/
static void idxRemFunc(
sqlite3_context *pCtx,
int argc,
sqlite3_value **argv
){
struct IdxRemCtx *p = (struct IdxRemCtx*)sqlite3_user_data(pCtx);
struct IdxRemSlot *pSlot;
int iSlot;
assert( argc==2 );
iSlot = sqlite3_value_int(argv[0]);
assert( iSlot<p->nSlot );
pSlot = &p->aSlot[iSlot];
switch( pSlot->eType ){
case SQLITE_NULL:
/* no-op */
break;
|
| ︙ | ︙ | |||
13456 13457 13458 13459 13460 13461 13462 |
/* Formulate the query text */
sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC);
while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){
const char *zComma = zCols==0 ? "" : ", ";
const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
zCols = idxAppendText(&rc, zCols,
| > | | 14463 14464 14465 14466 14467 14468 14469 14470 14471 14472 14473 14474 14475 14476 14477 14478 |
/* Formulate the query text */
sqlite3_bind_text(pIndexXInfo, 1, zIdx, -1, SQLITE_STATIC);
while( SQLITE_OK==rc && SQLITE_ROW==sqlite3_step(pIndexXInfo) ){
const char *zComma = zCols==0 ? "" : ", ";
const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
zCols = idxAppendText(&rc, zCols,
"%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s",
zComma, zName, nCol, zName, zColl
);
zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
}
sqlite3_reset(pIndexXInfo);
if( rc==SQLITE_OK ){
if( p->iSample==100 ){
zQuery = sqlite3_mprintf(
|
| ︙ | ︙ | |||
13589 13590 13591 13592 13593 13594 13595 |
if( rc==SQLITE_OK ){
int nByte = sizeof(struct IdxRemCtx) + (sizeof(struct IdxRemSlot) * nMax);
pCtx = (struct IdxRemCtx*)idxMalloc(&rc, nByte);
}
if( rc==SQLITE_OK ){
sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
| | | | | | 14597 14598 14599 14600 14601 14602 14603 14604 14605 14606 14607 14608 14609 14610 14611 14612 14613 14614 14615 14616 14617 |
if( rc==SQLITE_OK ){
int nByte = sizeof(struct IdxRemCtx) + (sizeof(struct IdxRemSlot) * nMax);
pCtx = (struct IdxRemCtx*)idxMalloc(&rc, nByte);
}
if( rc==SQLITE_OK ){
sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
rc = sqlite3_create_function(dbrem, "sqlite_expert_rem",
2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(p->db, "sqlite_expert_sample",
0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
pCtx->nSlot = nMax+1;
rc = idxPrepareStmt(p->dbm, &pAllIndex, pzErr, zAllIndex);
}
|
| ︙ | ︙ | |||
13647 13648 13649 13650 13651 13652 13653 13654 13655 13656 13657 13658 13659 13660 |
sqlite3_free(pCtx);
}
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
}
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
return rc;
}
/*
** Define and possibly pretend to use a useless collation sequence.
** This pretense allows expert to accept SQL using custom collations.
| > > > | 14655 14656 14657 14658 14659 14660 14661 14662 14663 14664 14665 14666 14667 14668 14669 14670 14671 |
sqlite3_free(pCtx);
}
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
}
sqlite3_create_function(p->db, "sqlite_expert_rem", 2, SQLITE_UTF8, 0,0,0,0);
sqlite3_create_function(p->db, "sqlite_expert_sample", 0,SQLITE_UTF8,0,0,0,0);
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
return rc;
}
/*
** Define and possibly pretend to use a useless collation sequence.
** This pretense allows expert to accept SQL using custom collations.
|
| ︙ | ︙ | |||
13780 13781 13782 13783 13784 13785 13786 |
#endif
/* Copy the entire schema of database [db] into [dbm]. */
if( rc==SQLITE_OK ){
sqlite3_stmt *pSql = 0;
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
"SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
| | | 14791 14792 14793 14794 14795 14796 14797 14798 14799 14800 14801 14802 14803 14804 14805 |
#endif
/* Copy the entire schema of database [db] into [dbm]. */
if( rc==SQLITE_OK ){
sqlite3_stmt *pSql = 0;
rc = idxPrintfPrepareStmt(pNew->db, &pSql, pzErrmsg,
"SELECT sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%%'"
" AND sql NOT LIKE 'CREATE VIRTUAL %%' ORDER BY rowid"
);
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
if( zSql ) rc = sqlite3_exec(pNew->dbm, zSql, 0, 0, pzErrmsg);
}
idxFinalize(&rc, pSql);
}
|
| ︙ | ︙ | |||
13981 13982 13983 13984 13985 13986 13987 13988 13989 13990 13991 13992 13993 13994 |
sqlite3_free(p);
}
}
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
/************************* End ../ext/expert/sqlite3expert.c ********************/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
#define SQLITE_SHELL_HAVE_RECOVER 1
#else
#define SQLITE_SHELL_HAVE_RECOVER 0
#endif
#if SQLITE_SHELL_HAVE_RECOVER
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 14992 14993 14994 14995 14996 14997 14998 14999 15000 15001 15002 15003 15004 15005 15006 15007 15008 15009 15010 15011 15012 15013 15014 15015 15016 15017 15018 15019 15020 15021 15022 15023 15024 15025 15026 15027 15028 15029 15030 15031 15032 15033 15034 15035 15036 15037 15038 15039 15040 15041 15042 15043 15044 15045 15046 15047 15048 15049 15050 15051 15052 15053 15054 15055 15056 15057 15058 15059 15060 15061 15062 15063 15064 15065 15066 15067 15068 15069 15070 15071 15072 15073 15074 15075 15076 15077 15078 15079 15080 15081 15082 15083 15084 15085 15086 15087 15088 15089 15090 15091 15092 15093 15094 15095 15096 15097 15098 15099 15100 15101 15102 15103 15104 15105 15106 15107 15108 15109 15110 15111 15112 15113 15114 15115 15116 15117 15118 15119 15120 15121 15122 15123 15124 15125 15126 15127 15128 15129 15130 15131 15132 15133 15134 15135 15136 15137 15138 15139 15140 15141 15142 15143 15144 15145 15146 15147 15148 15149 15150 15151 15152 15153 15154 15155 15156 15157 15158 15159 15160 15161 15162 15163 15164 15165 15166 15167 15168 15169 15170 15171 15172 15173 15174 15175 15176 15177 15178 15179 15180 15181 15182 15183 15184 15185 15186 15187 15188 15189 15190 15191 15192 15193 15194 15195 15196 15197 15198 15199 15200 15201 15202 15203 15204 15205 15206 15207 15208 15209 15210 15211 15212 15213 15214 15215 15216 15217 15218 15219 15220 15221 15222 15223 15224 15225 15226 15227 15228 15229 15230 15231 15232 15233 15234 15235 15236 15237 15238 15239 15240 15241 15242 15243 15244 15245 15246 15247 15248 15249 15250 15251 15252 15253 15254 15255 15256 15257 15258 15259 15260 15261 15262 15263 15264 15265 15266 15267 15268 15269 15270 15271 15272 15273 15274 15275 15276 15277 15278 15279 15280 15281 15282 15283 15284 15285 15286 15287 15288 15289 15290 15291 15292 15293 15294 15295 15296 15297 15298 15299 15300 15301 15302 15303 15304 15305 15306 15307 15308 15309 15310 15311 15312 15313 15314 15315 15316 15317 15318 15319 15320 15321 15322 15323 15324 15325 15326 15327 15328 15329 15330 15331 15332 15333 15334 15335 15336 15337 15338 15339 15340 15341 15342 15343 15344 15345 15346 15347 15348 15349 15350 15351 15352 15353 15354 15355 15356 15357 15358 15359 15360 15361 15362 15363 15364 15365 15366 15367 15368 15369 15370 15371 15372 15373 15374 15375 15376 15377 15378 15379 15380 15381 15382 15383 15384 15385 15386 15387 15388 15389 15390 15391 15392 15393 15394 15395 15396 15397 15398 15399 15400 15401 15402 15403 15404 15405 15406 15407 15408 15409 15410 15411 15412 15413 15414 15415 15416 15417 15418 15419 15420 15421 15422 15423 15424 15425 15426 15427 15428 15429 15430 15431 15432 15433 15434 15435 15436 15437 15438 15439 15440 15441 15442 15443 15444 15445 15446 15447 15448 15449 15450 15451 15452 15453 15454 15455 15456 15457 15458 15459 15460 15461 15462 15463 15464 15465 15466 15467 15468 15469 15470 15471 15472 15473 15474 15475 15476 15477 15478 15479 15480 15481 15482 15483 15484 15485 15486 15487 15488 15489 15490 15491 15492 15493 15494 15495 15496 15497 15498 15499 15500 15501 15502 15503 15504 15505 15506 15507 15508 15509 15510 15511 15512 15513 15514 15515 15516 15517 15518 15519 15520 15521 15522 15523 15524 15525 15526 15527 15528 15529 15530 15531 15532 15533 15534 15535 15536 15537 15538 15539 15540 15541 15542 15543 15544 15545 15546 15547 15548 15549 15550 15551 15552 15553 15554 15555 15556 15557 15558 15559 15560 15561 15562 15563 15564 15565 15566 15567 15568 15569 15570 15571 15572 15573 15574 15575 15576 15577 15578 15579 15580 15581 15582 15583 15584 15585 15586 15587 15588 15589 15590 15591 15592 15593 15594 15595 15596 15597 15598 15599 15600 15601 15602 15603 15604 15605 15606 15607 15608 15609 15610 15611 15612 15613 15614 15615 15616 15617 15618 15619 15620 15621 15622 15623 15624 15625 15626 15627 15628 15629 15630 15631 15632 15633 15634 15635 15636 15637 15638 15639 15640 15641 15642 15643 15644 15645 15646 15647 15648 15649 15650 15651 15652 15653 15654 15655 15656 15657 15658 15659 15660 15661 15662 15663 15664 15665 15666 15667 15668 15669 15670 15671 15672 15673 15674 15675 15676 15677 15678 15679 15680 15681 15682 15683 15684 15685 15686 15687 15688 15689 15690 15691 15692 15693 15694 15695 15696 15697 15698 15699 15700 15701 15702 15703 15704 15705 15706 15707 15708 15709 15710 15711 15712 15713 15714 15715 15716 15717 15718 15719 15720 15721 15722 15723 15724 15725 15726 15727 15728 15729 15730 15731 15732 15733 15734 15735 15736 15737 15738 15739 15740 15741 15742 15743 15744 15745 15746 15747 15748 15749 15750 15751 15752 15753 15754 15755 15756 15757 15758 15759 15760 15761 15762 15763 15764 15765 15766 15767 15768 15769 15770 15771 15772 15773 15774 15775 15776 15777 15778 15779 15780 15781 15782 15783 15784 15785 15786 15787 15788 15789 15790 15791 15792 15793 15794 15795 15796 15797 15798 15799 15800 15801 15802 15803 15804 15805 15806 15807 15808 15809 15810 15811 15812 15813 15814 15815 15816 15817 15818 15819 15820 15821 15822 15823 15824 15825 15826 15827 15828 15829 15830 15831 15832 15833 15834 15835 15836 15837 15838 15839 15840 15841 15842 15843 15844 15845 15846 15847 15848 15849 15850 15851 15852 15853 15854 15855 15856 15857 15858 15859 15860 15861 15862 15863 15864 15865 15866 15867 15868 15869 15870 15871 15872 15873 15874 15875 15876 15877 15878 15879 15880 15881 15882 15883 15884 15885 15886 15887 15888 15889 15890 15891 15892 15893 15894 15895 15896 15897 15898 15899 15900 15901 15902 15903 15904 15905 15906 15907 15908 15909 15910 15911 15912 15913 15914 15915 15916 15917 15918 15919 15920 15921 15922 15923 15924 15925 15926 15927 15928 15929 15930 15931 15932 15933 15934 15935 15936 15937 15938 15939 15940 15941 15942 15943 15944 15945 15946 15947 15948 15949 15950 15951 15952 15953 15954 15955 15956 15957 15958 15959 15960 15961 15962 15963 15964 15965 15966 15967 15968 15969 15970 15971 15972 15973 15974 15975 15976 15977 15978 15979 15980 15981 15982 15983 15984 15985 15986 15987 15988 15989 15990 15991 15992 15993 15994 15995 15996 15997 15998 15999 16000 16001 16002 16003 16004 16005 16006 16007 16008 16009 16010 16011 16012 16013 16014 16015 16016 16017 16018 16019 16020 16021 16022 16023 16024 16025 16026 16027 16028 16029 16030 16031 16032 16033 16034 16035 16036 16037 16038 16039 16040 16041 16042 16043 16044 16045 16046 16047 16048 16049 16050 16051 16052 16053 16054 16055 16056 16057 16058 16059 16060 16061 16062 16063 16064 16065 16066 16067 16068 16069 16070 16071 16072 16073 16074 16075 16076 16077 16078 16079 16080 16081 16082 16083 16084 16085 16086 16087 16088 16089 16090 16091 16092 16093 16094 16095 16096 16097 16098 16099 16100 16101 16102 16103 16104 16105 16106 16107 16108 16109 16110 16111 16112 16113 16114 16115 16116 16117 16118 16119 16120 16121 16122 16123 16124 16125 16126 16127 16128 16129 16130 16131 16132 16133 16134 16135 16136 16137 16138 16139 16140 16141 16142 16143 16144 16145 16146 16147 16148 16149 16150 16151 16152 16153 16154 16155 16156 16157 16158 16159 16160 16161 16162 16163 16164 16165 16166 16167 16168 16169 16170 16171 16172 16173 16174 16175 16176 16177 16178 16179 16180 16181 16182 16183 16184 16185 16186 16187 16188 16189 16190 16191 16192 16193 16194 16195 16196 16197 16198 16199 16200 16201 16202 16203 16204 16205 16206 16207 16208 16209 16210 16211 16212 16213 16214 16215 16216 16217 16218 16219 16220 16221 16222 |
sqlite3_free(p);
}
}
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
/************************* End ../ext/expert/sqlite3expert.c ********************/
/************************* Begin ../ext/intck/sqlite3intck.h ******************/
/*
** 2024-02-08
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
*/
/*
** Incremental Integrity-Check Extension
** -------------------------------------
**
** This module contains code to check whether or not an SQLite database
** is well-formed or corrupt. This is the same task as performed by SQLite's
** built-in "PRAGMA integrity_check" command. This module differs from
** "PRAGMA integrity_check" in that:
**
** + It is less thorough - this module does not detect certain types
** of corruption that are detected by the PRAGMA command. However,
** it does detect all kinds of corruption that are likely to cause
** errors in SQLite applications.
**
** + It is slower. Sometimes up to three times slower.
**
** + It allows integrity-check operations to be split into multiple
** transactions, so that the database does not need to be read-locked
** for the duration of the integrity-check.
**
** One way to use the API to run integrity-check on the "main" database
** of handle db is:
**
** int rc = SQLITE_OK;
** sqlite3_intck *p = 0;
**
** sqlite3_intck_open(db, "main", &p);
** while( SQLITE_OK==sqlite3_intck_step(p) ){
** const char *zMsg = sqlite3_intck_message(p);
** if( zMsg ) printf("corruption: %s\n", zMsg);
** }
** rc = sqlite3_intck_error(p, &zErr);
** if( rc!=SQLITE_OK ){
** printf("error occured (rc=%d), (errmsg=%s)\n", rc, zErr);
** }
** sqlite3_intck_close(p);
**
** Usually, the sqlite3_intck object opens a read transaction within the
** first call to sqlite3_intck_step() and holds it open until the
** integrity-check is complete. However, if sqlite3_intck_unlock() is
** called, the read transaction is ended and a new read transaction opened
** by the subsequent call to sqlite3_intck_step().
*/
#ifndef _SQLITE_INTCK_H
#define _SQLITE_INTCK_H
/* #include "sqlite3.h" */
#ifdef __cplusplus
extern "C" {
#endif
/*
** An ongoing incremental integrity-check operation is represented by an
** opaque pointer of the following type.
*/
typedef struct sqlite3_intck sqlite3_intck;
/*
** Open a new incremental integrity-check object. If successful, populate
** output variable (*ppOut) with the new object handle and return SQLITE_OK.
** Or, if an error occurs, set (*ppOut) to NULL and return an SQLite error
** code (e.g. SQLITE_NOMEM).
**
** The integrity-check will be conducted on database zDb (which must be "main",
** "temp", or the name of an attached database) of database handle db. Once
** this function has been called successfully, the caller should not use
** database handle db until the integrity-check object has been destroyed
** using sqlite3_intck_close().
*/
int sqlite3_intck_open(
sqlite3 *db, /* Database handle */
const char *zDb, /* Database name ("main", "temp" etc.) */
sqlite3_intck **ppOut /* OUT: New sqlite3_intck handle */
);
/*
** Close and release all resources associated with a handle opened by an
** earlier call to sqlite3_intck_open(). The results of using an
** integrity-check handle after it has been passed to this function are
** undefined.
*/
void sqlite3_intck_close(sqlite3_intck *pCk);
/*
** Do the next step of the integrity-check operation specified by the handle
** passed as the only argument. This function returns SQLITE_DONE if the
** integrity-check operation is finished, or an SQLite error code if
** an error occurs, or SQLITE_OK if no error occurs but the integrity-check
** is not finished. It is not considered an error if database corruption
** is encountered.
**
** Following a successful call to sqlite3_intck_step() (one that returns
** SQLITE_OK), sqlite3_intck_message() returns a non-NULL value if
** corruption was detected in the db.
**
** If an error occurs and a value other than SQLITE_OK or SQLITE_DONE is
** returned, then the integrity-check handle is placed in an error state.
** In this state all subsequent calls to sqlite3_intck_step() or
** sqlite3_intck_unlock() will immediately return the same error. The
** sqlite3_intck_error() method may be used to obtain an English language
** error message in this case.
*/
int sqlite3_intck_step(sqlite3_intck *pCk);
/*
** If the previous call to sqlite3_intck_step() encountered corruption
** within the database, then this function returns a pointer to a buffer
** containing a nul-terminated string describing the corruption in
** English. If the previous call to sqlite3_intck_step() did not encounter
** corruption, or if there was no previous call, this function returns
** NULL.
*/
const char *sqlite3_intck_message(sqlite3_intck *pCk);
/*
** Close any read-transaction opened by an earlier call to
** sqlite3_intck_step(). Any subsequent call to sqlite3_intck_step() will
** open a new transaction. Return SQLITE_OK if successful, or an SQLite error
** code otherwise.
**
** If an error occurs, then the integrity-check handle is placed in an error
** state. In this state all subsequent calls to sqlite3_intck_step() or
** sqlite3_intck_unlock() will immediately return the same error. The
** sqlite3_intck_error() method may be used to obtain an English language
** error message in this case.
*/
int sqlite3_intck_unlock(sqlite3_intck *pCk);
/*
** If an error has occurred in an earlier call to sqlite3_intck_step()
** or sqlite3_intck_unlock(), then this method returns the associated
** SQLite error code. Additionally, if pzErr is not NULL, then (*pzErr)
** may be set to point to a nul-terminated string containing an English
** language error message. Or, if no error message is available, to
** NULL.
**
** If no error has occurred within sqlite3_intck_step() or
** sqlite_intck_unlock() calls on the handle passed as the first argument,
** then SQLITE_OK is returned and (*pzErr) set to NULL.
*/
int sqlite3_intck_error(sqlite3_intck *pCk, const char **pzErr);
/*
** This API is used for testing only. It returns the full-text of an SQL
** statement used to test object zObj, which may be a table or index.
** The returned buffer is valid until the next call to either this function
** or sqlite3_intck_close() on the same sqlite3_intck handle.
*/
const char *sqlite3_intck_test_sql(sqlite3_intck *pCk, const char *zObj);
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
#endif
#endif /* ifndef _SQLITE_INTCK_H */
/************************* End ../ext/intck/sqlite3intck.h ********************/
/************************* Begin ../ext/intck/sqlite3intck.c ******************/
/*
** 2024-02-08
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
*/
/* #include "sqlite3intck.h" */
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
/*
** nKeyVal:
** The number of values that make up the 'key' for the current pCheck
** statement.
**
** rc:
** Error code returned by most recent sqlite3_intck_step() or
** sqlite3_intck_unlock() call. This is set to SQLITE_DONE when
** the integrity-check operation is finished.
**
** zErr:
** If the object has entered the error state, this is the error message.
** Is freed using sqlite3_free() when the object is deleted.
**
** zTestSql:
** The value returned by the most recent call to sqlite3_intck_testsql().
** Each call to testsql() frees the previous zTestSql value (using
** sqlite3_free()) and replaces it with the new value it will return.
*/
struct sqlite3_intck {
sqlite3 *db;
const char *zDb; /* Copy of zDb parameter to _open() */
char *zObj; /* Current object. Or NULL. */
sqlite3_stmt *pCheck; /* Current check statement */
char *zKey;
int nKeyVal;
char *zMessage;
int bCorruptSchema;
int rc; /* Error code */
char *zErr; /* Error message */
char *zTestSql; /* Returned by sqlite3_intck_test_sql() */
};
/*
** Some error has occurred while using database p->db. Save the error message
** and error code currently held by the database handle in p->rc and p->zErr.
*/
static void intckSaveErrmsg(sqlite3_intck *p){
p->rc = sqlite3_errcode(p->db);
sqlite3_free(p->zErr);
p->zErr = sqlite3_mprintf("%s", sqlite3_errmsg(p->db));
}
/*
** If the handle passed as the first argument is already in the error state,
** then this function is a no-op (returns NULL immediately). Otherwise, if an
** error occurs within this function, it leaves an error in said handle.
**
** Otherwise, this function attempts to prepare SQL statement zSql and
** return the resulting statement handle to the user.
*/
static sqlite3_stmt *intckPrepare(sqlite3_intck *p, const char *zSql){
sqlite3_stmt *pRet = 0;
if( p->rc==SQLITE_OK ){
p->rc = sqlite3_prepare_v2(p->db, zSql, -1, &pRet, 0);
if( p->rc!=SQLITE_OK ){
intckSaveErrmsg(p);
assert( pRet==0 );
}
}
return pRet;
}
/*
** If the handle passed as the first argument is already in the error state,
** then this function is a no-op (returns NULL immediately). Otherwise, if an
** error occurs within this function, it leaves an error in said handle.
**
** Otherwise, this function treats argument zFmt as a printf() style format
** string. It formats it according to the trailing arguments and then
** attempts to prepare the results and return the resulting prepared
** statement.
*/
static sqlite3_stmt *intckPrepareFmt(sqlite3_intck *p, const char *zFmt, ...){
sqlite3_stmt *pRet = 0;
va_list ap;
char *zSql = 0;
va_start(ap, zFmt);
zSql = sqlite3_vmprintf(zFmt, ap);
if( p->rc==SQLITE_OK && zSql==0 ){
p->rc = SQLITE_NOMEM;
}
pRet = intckPrepare(p, zSql);
sqlite3_free(zSql);
va_end(ap);
return pRet;
}
/*
** Finalize SQL statement pStmt. If an error occurs and the handle passed
** as the first argument does not already contain an error, store the
** error in the handle.
*/
static void intckFinalize(sqlite3_intck *p, sqlite3_stmt *pStmt){
int rc = sqlite3_finalize(pStmt);
if( p->rc==SQLITE_OK && rc!=SQLITE_OK ){
intckSaveErrmsg(p);
}
}
/*
** If there is already an error in handle p, return it. Otherwise, call
** sqlite3_step() on the statement handle and return that value.
*/
static int intckStep(sqlite3_intck *p, sqlite3_stmt *pStmt){
if( p->rc ) return p->rc;
return sqlite3_step(pStmt);
}
/*
** Execute SQL statement zSql. There is no way to obtain any results
** returned by the statement. This function uses the sqlite3_intck error
** code convention.
*/
static void intckExec(sqlite3_intck *p, const char *zSql){
sqlite3_stmt *pStmt = 0;
pStmt = intckPrepare(p, zSql);
intckStep(p, pStmt);
intckFinalize(p, pStmt);
}
/*
** A wrapper around sqlite3_mprintf() that uses the sqlite3_intck error
** code convention.
*/
static char *intckMprintf(sqlite3_intck *p, const char *zFmt, ...){
va_list ap;
char *zRet = 0;
va_start(ap, zFmt);
zRet = sqlite3_vmprintf(zFmt, ap);
if( p->rc==SQLITE_OK ){
if( zRet==0 ){
p->rc = SQLITE_NOMEM;
}
}else{
sqlite3_free(zRet);
zRet = 0;
}
return zRet;
}
/*
** This is used by sqlite3_intck_unlock() to save the vector key value
** required to restart the current pCheck query as a nul-terminated string
** in p->zKey.
*/
static void intckSaveKey(sqlite3_intck *p){
int ii;
char *zSql = 0;
sqlite3_stmt *pStmt = 0;
sqlite3_stmt *pXinfo = 0;
const char *zDir = 0;
assert( p->pCheck );
assert( p->zKey==0 );
pXinfo = intckPrepareFmt(p,
"SELECT group_concat(desc, '') FROM %Q.sqlite_schema s, "
"pragma_index_xinfo(%Q, %Q) "
"WHERE s.type='index' AND s.name=%Q",
p->zDb, p->zObj, p->zDb, p->zObj
);
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXinfo) ){
zDir = (const char*)sqlite3_column_text(pXinfo, 0);
}
if( zDir==0 ){
/* Object is a table, not an index. This is the easy case,as there are
** no DESC columns or NULL values in a primary key. */
const char *zSep = "SELECT '(' || ";
for(ii=0; ii<p->nKeyVal; ii++){
zSql = intckMprintf(p, "%z%squote(?)", zSql, zSep);
zSep = " || ', ' || ";
}
zSql = intckMprintf(p, "%z || ')'", zSql);
}else{
/* Object is an index. */
assert( p->nKeyVal>1 );
for(ii=p->nKeyVal; ii>0; ii--){
int bLastIsDesc = zDir[ii-1]=='1';
int bLastIsNull = sqlite3_column_type(p->pCheck, ii)==SQLITE_NULL;
const char *zLast = sqlite3_column_name(p->pCheck, ii);
char *zLhs = 0;
char *zRhs = 0;
char *zWhere = 0;
if( bLastIsNull ){
if( bLastIsDesc ) continue;
zWhere = intckMprintf(p, "'%s IS NOT NULL'", zLast);
}else{
const char *zOp = bLastIsDesc ? "<" : ">";
zWhere = intckMprintf(p, "'%s %s ' || quote(?%d)", zLast, zOp, ii);
}
if( ii>1 ){
const char *zLhsSep = "";
const char *zRhsSep = "";
int jj;
for(jj=0; jj<ii-1; jj++){
const char *zAlias = (const char*)sqlite3_column_name(p->pCheck,jj+1);
zLhs = intckMprintf(p, "%z%s%s", zLhs, zLhsSep, zAlias);
zRhs = intckMprintf(p, "%z%squote(?%d)", zRhs, zRhsSep, jj+1);
zLhsSep = ",";
zRhsSep = " || ',' || ";
}
zWhere = intckMprintf(p,
"'(%z) IS (' || %z || ') AND ' || %z",
zLhs, zRhs, zWhere);
}
zWhere = intckMprintf(p, "'WHERE ' || %z", zWhere);
zSql = intckMprintf(p, "%z%s(quote( %z ) )",
zSql,
(zSql==0 ? "VALUES" : ",\n "),
zWhere
);
}
zSql = intckMprintf(p,
"WITH wc(q) AS (\n%z\n)"
"SELECT 'VALUES' || group_concat('(' || q || ')', ',\n ') FROM wc"
, zSql
);
}
pStmt = intckPrepare(p, zSql);
if( p->rc==SQLITE_OK ){
for(ii=0; ii<p->nKeyVal; ii++){
sqlite3_bind_value(pStmt, ii+1, sqlite3_column_value(p->pCheck, ii+1));
}
if( SQLITE_ROW==sqlite3_step(pStmt) ){
p->zKey = intckMprintf(p,"%s",(const char*)sqlite3_column_text(pStmt, 0));
}
intckFinalize(p, pStmt);
}
sqlite3_free(zSql);
intckFinalize(p, pXinfo);
}
/*
** Find the next database object (table or index) to check. If successful,
** set sqlite3_intck.zObj to point to a nul-terminated buffer containing
** the object's name before returning.
*/
static void intckFindObject(sqlite3_intck *p){
sqlite3_stmt *pStmt = 0;
char *zPrev = p->zObj;
p->zObj = 0;
assert( p->rc==SQLITE_OK );
assert( p->pCheck==0 );
pStmt = intckPrepareFmt(p,
"WITH tables(table_name) AS ("
" SELECT name"
" FROM %Q.sqlite_schema WHERE (type='table' OR type='index') AND rootpage"
" UNION ALL "
" SELECT 'sqlite_schema'"
")"
"SELECT table_name FROM tables "
"WHERE ?1 IS NULL OR table_name%s?1 "
"ORDER BY 1"
, p->zDb, (p->zKey ? ">=" : ">")
);
if( p->rc==SQLITE_OK ){
sqlite3_bind_text(pStmt, 1, zPrev, -1, SQLITE_TRANSIENT);
if( sqlite3_step(pStmt)==SQLITE_ROW ){
p->zObj = intckMprintf(p,"%s",(const char*)sqlite3_column_text(pStmt, 0));
}
}
intckFinalize(p, pStmt);
/* If this is a new object, ensure the previous key value is cleared. */
if( sqlite3_stricmp(p->zObj, zPrev) ){
sqlite3_free(p->zKey);
p->zKey = 0;
}
sqlite3_free(zPrev);
}
/*
** Return the size in bytes of the first token in nul-terminated buffer z.
** For the purposes of this call, a token is either:
**
** * a quoted SQL string,
* * a contiguous series of ascii alphabet characters, or
* * any other single byte.
*/
static int intckGetToken(const char *z){
char c = z[0];
int iRet = 1;
if( c=='\'' || c=='"' || c=='`' ){
while( 1 ){
if( z[iRet]==c ){
iRet++;
if( z[iRet]!=c ) break;
}
iRet++;
}
}
else if( c=='[' ){
while( z[iRet++]!=']' && z[iRet] );
}
else if( (c>='A' && c<='Z') || (c>='a' && c<='z') ){
while( (z[iRet]>='A' && z[iRet]<='Z') || (z[iRet]>='a' && z[iRet]<='z') ){
iRet++;
}
}
return iRet;
}
/*
** Return true if argument c is an ascii whitespace character.
*/
static int intckIsSpace(char c){
return (c==' ' || c=='\t' || c=='\n' || c=='\r');
}
/*
** Argument z points to the text of a CREATE INDEX statement. This function
** identifies the part of the text that contains either the index WHERE
** clause (if iCol<0) or the iCol'th column of the index.
**
** If (iCol<0), the identified fragment does not include the "WHERE" keyword,
** only the expression that follows it. If (iCol>=0) then the identified
** fragment does not include any trailing sort-order keywords - "ASC" or
** "DESC".
**
** If the CREATE INDEX statement does not contain the requested field or
** clause, NULL is returned and (*pnByte) is set to 0. Otherwise, a pointer to
** the identified fragment is returned and output parameter (*pnByte) set
** to its size in bytes.
*/
static const char *intckParseCreateIndex(const char *z, int iCol, int *pnByte){
int iOff = 0;
int iThisCol = 0;
int iStart = 0;
int nOpen = 0;
const char *zRet = 0;
int nRet = 0;
int iEndOfCol = 0;
/* Skip forward until the first "(" token */
while( z[iOff]!='(' ){
iOff += intckGetToken(&z[iOff]);
if( z[iOff]=='\0' ) return 0;
}
assert( z[iOff]=='(' );
nOpen = 1;
iOff++;
iStart = iOff;
while( z[iOff] ){
const char *zToken = &z[iOff];
int nToken = 0;
/* Check if this is the end of the current column - either a "," or ")"
** when nOpen==1. */
if( nOpen==1 ){
if( z[iOff]==',' || z[iOff]==')' ){
if( iCol==iThisCol ){
int iEnd = iEndOfCol ? iEndOfCol : iOff;
nRet = (iEnd - iStart);
zRet = &z[iStart];
break;
}
iStart = iOff+1;
while( intckIsSpace(z[iStart]) ) iStart++;
iThisCol++;
}
if( z[iOff]==')' ) break;
}
if( z[iOff]=='(' ) nOpen++;
if( z[iOff]==')' ) nOpen--;
nToken = intckGetToken(zToken);
if( (nToken==3 && 0==sqlite3_strnicmp(zToken, "ASC", nToken))
|| (nToken==4 && 0==sqlite3_strnicmp(zToken, "DESC", nToken))
){
iEndOfCol = iOff;
}else if( 0==intckIsSpace(zToken[0]) ){
iEndOfCol = 0;
}
iOff += nToken;
}
/* iStart is now the byte offset of 1 byte passed the final ')' in the
** CREATE INDEX statement. Try to find a WHERE clause to return. */
while( zRet==0 && z[iOff] ){
int n = intckGetToken(&z[iOff]);
if( n==5 && 0==sqlite3_strnicmp(&z[iOff], "where", 5) ){
zRet = &z[iOff+5];
nRet = (int)strlen(zRet);
}
iOff += n;
}
/* Trim any whitespace from the start and end of the returned string. */
if( zRet ){
while( intckIsSpace(zRet[0]) ){
nRet--;
zRet++;
}
while( nRet>0 && intckIsSpace(zRet[nRet-1]) ) nRet--;
}
*pnByte = nRet;
return zRet;
}
/*
** User-defined SQL function wrapper for intckParseCreateIndex():
**
** SELECT parse_create_index(<sql>, <icol>);
*/
static void intckParseCreateIndexFunc(
sqlite3_context *pCtx,
int nVal,
sqlite3_value **apVal
){
const char *zSql = (const char*)sqlite3_value_text(apVal[0]);
int idx = sqlite3_value_int(apVal[1]);
const char *zRes = 0;
int nRes = 0;
assert( nVal==2 );
if( zSql ){
zRes = intckParseCreateIndex(zSql, idx, &nRes);
}
sqlite3_result_text(pCtx, zRes, nRes, SQLITE_TRANSIENT);
}
/*
** Return true if sqlite3_intck.db has automatic indexes enabled, false
** otherwise.
*/
static int intckGetAutoIndex(sqlite3_intck *p){
int bRet = 0;
sqlite3_stmt *pStmt = 0;
pStmt = intckPrepare(p, "PRAGMA automatic_index");
if( SQLITE_ROW==intckStep(p, pStmt) ){
bRet = sqlite3_column_int(pStmt, 0);
}
intckFinalize(p, pStmt);
return bRet;
}
/*
** Return true if zObj is an index, or false otherwise.
*/
static int intckIsIndex(sqlite3_intck *p, const char *zObj){
int bRet = 0;
sqlite3_stmt *pStmt = 0;
pStmt = intckPrepareFmt(p,
"SELECT 1 FROM %Q.sqlite_schema WHERE name=%Q AND type='index'",
p->zDb, zObj
);
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
bRet = 1;
}
intckFinalize(p, pStmt);
return bRet;
}
/*
** Return a pointer to a nul-terminated buffer containing the SQL statement
** used to check database object zObj (a table or index) for corruption.
** If parameter zPrev is not NULL, then it must be a string containing the
** vector key required to restart the check where it left off last time.
** If pnKeyVal is not NULL, then (*pnKeyVal) is set to the number of
** columns in the vector key value for the specified object.
**
** This function uses the sqlite3_intck error code convention.
*/
static char *intckCheckObjectSql(
sqlite3_intck *p, /* Integrity check object */
const char *zObj, /* Object (table or index) to scan */
const char *zPrev, /* Restart key vector, if any */
int *pnKeyVal /* OUT: Number of key-values for this scan */
){
char *zRet = 0;
sqlite3_stmt *pStmt = 0;
int bAutoIndex = 0;
int bIsIndex = 0;
const char *zCommon =
/* Relation without_rowid also contains just one row. Column "b" is
** set to true if the table being examined is a WITHOUT ROWID table,
** or false otherwise. */
", without_rowid(b) AS ("
" SELECT EXISTS ("
" SELECT 1 FROM tabname, pragma_index_list(tab, db) AS l"
" WHERE origin='pk' "
" AND NOT EXISTS (SELECT 1 FROM sqlite_schema WHERE name=l.name)"
" )"
")"
""
/* Table idx_cols contains 1 row for each column in each index on the
** table being checked. Columns are:
**
** idx_name: Name of the index.
** idx_ispk: True if this index is the PK of a WITHOUT ROWID table.
** col_name: Name of indexed column, or NULL for index on expression.
** col_expr: Indexed expression, including COLLATE clause.
** col_alias: Alias used for column in 'intck_wrapper' table.
*/
", idx_cols(idx_name, idx_ispk, col_name, col_expr, col_alias) AS ("
" SELECT l.name, (l.origin=='pk' AND w.b), i.name, COALESCE(("
" SELECT parse_create_index(sql, i.seqno) FROM "
" sqlite_schema WHERE name = l.name"
" ), format('\"%w\"', i.name) || ' COLLATE ' || quote(i.coll)),"
" 'c' || row_number() OVER ()"
" FROM "
" tabname t,"
" without_rowid w,"
" pragma_index_list(t.tab, t.db) l,"
" pragma_index_xinfo(l.name) i"
" WHERE i.key"
" UNION ALL"
" SELECT '', 1, '_rowid_', '_rowid_', 'r1' FROM without_rowid WHERE b=0"
")"
""
""
/*
** For a PK declared as "PRIMARY KEY(a, b) ... WITHOUT ROWID", where
** the intck_wrapper aliases of "a" and "b" are "c1" and "c2":
**
** o_pk: "o.c1, o.c2"
** i_pk: "i.'a', i.'b'"
** ...
** n_pk: 2
*/
", tabpk(db, tab, idx, o_pk, i_pk, q_pk, eq_pk, ps_pk, pk_pk, n_pk) AS ("
" WITH pkfields(f, a) AS ("
" SELECT i.col_name, i.col_alias FROM idx_cols i WHERE i.idx_ispk"
" )"
" SELECT t.db, t.tab, t.idx, "
" group_concat(a, ', '), "
" group_concat('i.'||quote(f), ', '), "
" group_concat('quote(o.'||a||')', ' || '','' || '), "
" format('(%s)==(%s)',"
" group_concat('o.'||a, ', '), "
" group_concat(format('\"%w\"', f), ', ')"
" ),"
" group_concat('%s', ','),"
" group_concat('quote('||a||')', ', '), "
" count(*)"
" FROM tabname t, pkfields"
")"
""
", idx(name, match_expr, partial, partial_alias, idx_ps, idx_idx) AS ("
" SELECT idx_name,"
" format('(%s,%s) IS (%s,%s)', "
" group_concat(i.col_expr, ', '), i_pk,"
" group_concat('o.'||i.col_alias, ', '), o_pk"
" ), "
" parse_create_index("
" (SELECT sql FROM sqlite_schema WHERE name=idx_name), -1"
" ),"
" 'cond' || row_number() OVER ()"
" , group_concat('%s', ',')"
" , group_concat('quote('||i.col_alias||')', ', ')"
" FROM tabpk t, "
" without_rowid w,"
" idx_cols i"
" WHERE i.idx_ispk==0 "
" GROUP BY idx_name"
")"
""
", wrapper_with(s) AS ("
" SELECT 'intck_wrapper AS (\n SELECT\n ' || ("
" WITH f(a, b) AS ("
" SELECT col_expr, col_alias FROM idx_cols"
" UNION ALL "
" SELECT partial, partial_alias FROM idx WHERE partial IS NOT NULL"
" )"
" SELECT group_concat(format('%s AS %s', a, b), ',\n ') FROM f"
" )"
" || format('\n FROM %Q.%Q ', t.db, t.tab)"
/* If the object being checked is a table, append "NOT INDEXED".
** Otherwise, append "INDEXED BY <index>", and then, if the index
** is a partial index " WHERE <condition>". */
" || CASE WHEN t.idx IS NULL THEN "
" 'NOT INDEXED'"
" ELSE"
" format('INDEXED BY %Q%s', t.idx, ' WHERE '||i.partial)"
" END"
" || '\n)'"
" FROM tabname t LEFT JOIN idx i ON (i.name=t.idx)"
")"
""
;
bAutoIndex = intckGetAutoIndex(p);
if( bAutoIndex ) intckExec(p, "PRAGMA automatic_index = 0");
bIsIndex = intckIsIndex(p, zObj);
if( bIsIndex ){
pStmt = intckPrepareFmt(p,
/* Table idxname contains a single row. The first column, "db", contains
** the name of the db containing the table (e.g. "main") and the second,
** "tab", the name of the table itself. */
"WITH tabname(db, tab, idx) AS ("
" SELECT %Q, (SELECT tbl_name FROM %Q.sqlite_schema WHERE name=%Q), %Q "
")"
""
", whereclause(w_c) AS (%s)"
""
"%s" /* zCommon */
""
", case_statement(c) AS ("
" SELECT "
" 'CASE WHEN (' || group_concat(col_alias, ', ') || ', 1) IS (\n' "
" || ' SELECT ' || group_concat(col_expr, ', ') || ', 1 FROM '"
" || format('%%Q.%%Q NOT INDEXED WHERE %%s\n', t.db, t.tab, p.eq_pk)"
" || ' )\n THEN NULL\n '"
" || 'ELSE format(''surplus entry ('"
" || group_concat('%%s', ',') || ',' || p.ps_pk"
" || ') in index ' || t.idx || ''', ' "
" || group_concat('quote('||i.col_alias||')', ', ') || ', ' || p.pk_pk"
" || ')'"
" || '\n END AS error_message'"
" FROM tabname t, tabpk p, idx_cols i WHERE i.idx_name=t.idx"
")"
""
", thiskey(k, n) AS ("
" SELECT group_concat(i.col_alias, ', ') || ', ' || p.o_pk, "
" count(*) + p.n_pk "
" FROM tabpk p, idx_cols i WHERE i.idx_name=p.idx"
")"
""
", main_select(m, n) AS ("
" SELECT format("
" 'WITH %%s\n' ||"
" ', idx_checker AS (\n' ||"
" ' SELECT %%s,\n' ||"
" ' %%s\n' || "
" ' FROM intck_wrapper AS o\n' ||"
" ')\n',"
" ww.s, c, t.k"
" ), t.n"
" FROM case_statement, wrapper_with ww, thiskey t"
")"
"SELECT m || "
" group_concat('SELECT * FROM idx_checker ' || w_c, ' UNION ALL '), n"
" FROM "
"main_select, whereclause "
, p->zDb, p->zDb, zObj, zObj
, zPrev ? zPrev : "VALUES('')", zCommon
);
}else{
pStmt = intckPrepareFmt(p,
/* Table tabname contains a single row. The first column, "db", contains
** the name of the db containing the table (e.g. "main") and the second,
** "tab", the name of the table itself. */
"WITH tabname(db, tab, idx, prev) AS (SELECT %Q, %Q, NULL, %Q)"
""
"%s" /* zCommon */
/* expr(e) contains one row for each index on table zObj. Value e
** is set to an expression that evaluates to NULL if the required
** entry is present in the index, or an error message otherwise. */
", expr(e, p) AS ("
" SELECT format('CASE WHEN EXISTS \n"
" (SELECT 1 FROM %%Q.%%Q AS i INDEXED BY %%Q WHERE %%s%%s)\n"
" THEN NULL\n"
" ELSE format(''entry (%%s,%%s) missing from index %%s'', %%s, %%s)\n"
" END\n'"
" , t.db, t.tab, i.name, i.match_expr, ' AND (' || partial || ')',"
" i.idx_ps, t.ps_pk, i.name, i.idx_idx, t.pk_pk),"
" CASE WHEN partial IS NULL THEN NULL ELSE i.partial_alias END"
" FROM tabpk t, idx i"
")"
", numbered(ii, cond, e) AS ("
" SELECT 0, 'n.ii=0', 'NULL'"
" UNION ALL "
" SELECT row_number() OVER (),"
" '(n.ii='||row_number() OVER ()||COALESCE(' AND '||p||')', ')'), e"
" FROM expr"
")"
", counter_with(w) AS ("
" SELECT 'WITH intck_counter(ii) AS (\n ' || "
" group_concat('SELECT '||ii, ' UNION ALL\n ') "
" || '\n)' FROM numbered"
")"
""
", case_statement(c) AS ("
" SELECT 'CASE ' || "
" group_concat(format('\n WHEN %%s THEN (%%s)', cond, e), '') ||"
" '\nEND AS error_message'"
" FROM numbered"
")"
""
/* This table contains a single row consisting of a single value -
** the text of an SQL expression that may be used by the main SQL
** statement to output an SQL literal that can be used to resume
** the scan if it is suspended. e.g. for a rowid table, an expression
** like:
**
** format('(%d,%d)', _rowid_, n.ii)
*/
", thiskey(k, n) AS ("
" SELECT o_pk || ', ii', n_pk+1 FROM tabpk"
")"
""
", whereclause(w_c) AS ("
" SELECT CASE WHEN prev!='' THEN "
" '\nWHERE (' || o_pk ||', n.ii) > ' || prev"
" ELSE ''"
" END"
" FROM tabpk, tabname"
")"
""
", main_select(m, n) AS ("
" SELECT format("
" '%%s, %%s\nSELECT %%s,\n%%s\nFROM intck_wrapper AS o"
", intck_counter AS n%%s\nORDER BY %%s', "
" w, ww.s, c, thiskey.k, whereclause.w_c, t.o_pk"
" ), thiskey.n"
" FROM case_statement, tabpk t, counter_with, "
" wrapper_with ww, thiskey, whereclause"
")"
"SELECT m, n FROM main_select",
p->zDb, zObj, zPrev, zCommon
);
}
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
zRet = intckMprintf(p, "%s", (const char*)sqlite3_column_text(pStmt, 0));
if( pnKeyVal ){
*pnKeyVal = sqlite3_column_int(pStmt, 1);
}
}
intckFinalize(p, pStmt);
if( bAutoIndex ) intckExec(p, "PRAGMA automatic_index = 1");
return zRet;
}
/*
** Open a new integrity-check object.
*/
int sqlite3_intck_open(
sqlite3 *db, /* Database handle to operate on */
const char *zDbArg, /* "main", "temp" etc. */
sqlite3_intck **ppOut /* OUT: New integrity-check handle */
){
sqlite3_intck *pNew = 0;
int rc = SQLITE_OK;
const char *zDb = zDbArg ? zDbArg : "main";
int nDb = (int)strlen(zDb);
pNew = (sqlite3_intck*)sqlite3_malloc(sizeof(*pNew) + nDb + 1);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pNew, 0, sizeof(*pNew));
pNew->db = db;
pNew->zDb = (const char*)&pNew[1];
memcpy(&pNew[1], zDb, nDb+1);
rc = sqlite3_create_function(db, "parse_create_index",
2, SQLITE_UTF8, 0, intckParseCreateIndexFunc, 0, 0
);
if( rc!=SQLITE_OK ){
sqlite3_intck_close(pNew);
pNew = 0;
}
}
*ppOut = pNew;
return rc;
}
/*
** Free the integrity-check object.
*/
void sqlite3_intck_close(sqlite3_intck *p){
if( p ){
sqlite3_finalize(p->pCheck);
sqlite3_create_function(
p->db, "parse_create_index", 1, SQLITE_UTF8, 0, 0, 0, 0
);
sqlite3_free(p->zObj);
sqlite3_free(p->zKey);
sqlite3_free(p->zTestSql);
sqlite3_free(p->zErr);
sqlite3_free(p->zMessage);
sqlite3_free(p);
}
}
/*
** Step the integrity-check object.
*/
int sqlite3_intck_step(sqlite3_intck *p){
if( p->rc==SQLITE_OK ){
if( p->zMessage ){
sqlite3_free(p->zMessage);
p->zMessage = 0;
}
if( p->bCorruptSchema ){
p->rc = SQLITE_DONE;
}else
if( p->pCheck==0 ){
intckFindObject(p);
if( p->rc==SQLITE_OK ){
if( p->zObj ){
char *zSql = 0;
zSql = intckCheckObjectSql(p, p->zObj, p->zKey, &p->nKeyVal);
p->pCheck = intckPrepare(p, zSql);
sqlite3_free(zSql);
sqlite3_free(p->zKey);
p->zKey = 0;
}else{
p->rc = SQLITE_DONE;
}
}else if( p->rc==SQLITE_CORRUPT ){
p->rc = SQLITE_OK;
p->zMessage = intckMprintf(p, "%s",
"corruption found while reading database schema"
);
p->bCorruptSchema = 1;
}
}
if( p->pCheck ){
assert( p->rc==SQLITE_OK );
if( sqlite3_step(p->pCheck)==SQLITE_ROW ){
/* Normal case, do nothing. */
}else{
intckFinalize(p, p->pCheck);
p->pCheck = 0;
p->nKeyVal = 0;
if( p->rc==SQLITE_CORRUPT ){
p->rc = SQLITE_OK;
p->zMessage = intckMprintf(p,
"corruption found while scanning database object %s", p->zObj
);
}
}
}
}
return p->rc;
}
/*
** Return a message describing the corruption encountered by the most recent
** call to sqlite3_intck_step(), or NULL if no corruption was encountered.
*/
const char *sqlite3_intck_message(sqlite3_intck *p){
assert( p->pCheck==0 || p->zMessage==0 );
if( p->zMessage ){
return p->zMessage;
}
if( p->pCheck ){
return (const char*)sqlite3_column_text(p->pCheck, 0);
}
return 0;
}
/*
** Return the error code and message.
*/
int sqlite3_intck_error(sqlite3_intck *p, const char **pzErr){
if( pzErr ) *pzErr = p->zErr;
return (p->rc==SQLITE_DONE ? SQLITE_OK : p->rc);
}
/*
** Close any read transaction the integrity-check object is holding open
** on the database.
*/
int sqlite3_intck_unlock(sqlite3_intck *p){
if( p->rc==SQLITE_OK && p->pCheck ){
assert( p->zKey==0 && p->nKeyVal>0 );
intckSaveKey(p);
intckFinalize(p, p->pCheck);
p->pCheck = 0;
}
return p->rc;
}
/*
** Return the SQL statement used to check object zObj. Or, if zObj is
** NULL, the current SQL statement.
*/
const char *sqlite3_intck_test_sql(sqlite3_intck *p, const char *zObj){
sqlite3_free(p->zTestSql);
if( zObj ){
p->zTestSql = intckCheckObjectSql(p, zObj, 0, 0);
}else{
if( p->zObj ){
p->zTestSql = intckCheckObjectSql(p, p->zObj, p->zKey, 0);
}else{
sqlite3_free(p->zTestSql);
p->zTestSql = 0;
}
}
return p->zTestSql;
}
/************************* End ../ext/intck/sqlite3intck.c ********************/
/************************* Begin ../ext/misc/stmtrand.c ******************/
/*
** 2024-05-24
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
******************************************************************************
**
** An SQL function that return pseudo-random non-negative integers.
**
** SELECT stmtrand(123);
**
** A special feature of this function is that the same sequence of random
** integers is returned for each invocation of the statement. This makes
** the results repeatable, and hence useful for testing. The argument is
** an integer which is the seed for the random number sequence. The seed
** is used by the first invocation of this function only and is ignored
** for all subsequent calls within the same statement.
**
** Resetting a statement (sqlite3_reset()) also resets the random number
** sequence.
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
/* State of the pseudo-random number generator */
typedef struct Stmtrand {
unsigned int x, y;
} Stmtrand;
/* auxdata key */
#define STMTRAND_KEY (-4418371)
/*
** Function: stmtrand(SEED)
**
** Return a pseudo-random number.
*/
static void stmtrandFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
Stmtrand *p;
p = (Stmtrand*)sqlite3_get_auxdata(context, STMTRAND_KEY);
if( p==0 ){
unsigned int seed;
p = sqlite3_malloc( sizeof(*p) );
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
}
if( argc>=1 ){
seed = (unsigned int)sqlite3_value_int(argv[0]);
}else{
seed = 0;
}
p->x = seed | 1;
p->y = seed;
sqlite3_set_auxdata(context, STMTRAND_KEY, p, sqlite3_free);
p = (Stmtrand*)sqlite3_get_auxdata(context, STMTRAND_KEY);
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
}
}
p->x = (p->x>>1) ^ ((1+~(p->x&1)) & 0xd0000001);
p->y = p->y*1103515245 + 12345;
sqlite3_result_int(context, (int)((p->x ^ p->y)&0x7fffffff));
}
#ifdef _WIN32
#endif
int sqlite3_stmtrand_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErrMsg; /* Unused parameter */
rc = sqlite3_create_function(db, "stmtrand", 1, SQLITE_UTF8, 0,
stmtrandFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "stmtrand", 0, SQLITE_UTF8, 0,
stmtrandFunc, 0, 0);
}
return rc;
}
/************************* End ../ext/misc/stmtrand.c ********************/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
#define SQLITE_SHELL_HAVE_RECOVER 1
#else
#define SQLITE_SHELL_HAVE_RECOVER 0
#endif
#if SQLITE_SHELL_HAVE_RECOVER
|
| ︙ | ︙ | |||
14332 14333 14334 14335 14336 14337 14338 14339 14340 14341 14342 14343 14344 14345 14346 14347 14348 14349 14350 14351 14352 14353 14354 |
#ifndef SQLITE_OMIT_VIRTUALTABLE
#define DBDATA_PADDING_BYTES 100
typedef struct DbdataTable DbdataTable;
typedef struct DbdataCursor DbdataCursor;
/* Cursor object */
struct DbdataCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
sqlite3_stmt *pStmt; /* For fetching database pages */
int iPgno; /* Current page number */
u8 *aPage; /* Buffer containing page */
int nPage; /* Size of aPage[] in bytes */
int nCell; /* Number of cells on aPage[] */
int iCell; /* Current cell number */
int bOnePage; /* True to stop after one page */
int szDb;
sqlite3_int64 iRowid;
/* Only for the sqlite_dbdata table */
| > > > > > > > > > | | 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569 16570 16571 16572 16573 16574 16575 16576 16577 16578 16579 16580 16581 16582 16583 16584 16585 16586 16587 16588 16589 16590 16591 16592 16593 16594 16595 16596 16597 16598 16599 |
#ifndef SQLITE_OMIT_VIRTUALTABLE
#define DBDATA_PADDING_BYTES 100
typedef struct DbdataTable DbdataTable;
typedef struct DbdataCursor DbdataCursor;
typedef struct DbdataBuffer DbdataBuffer;
/*
** Buffer type.
*/
struct DbdataBuffer {
u8 *aBuf;
sqlite3_int64 nBuf;
};
/* Cursor object */
struct DbdataCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
sqlite3_stmt *pStmt; /* For fetching database pages */
int iPgno; /* Current page number */
u8 *aPage; /* Buffer containing page */
int nPage; /* Size of aPage[] in bytes */
int nCell; /* Number of cells on aPage[] */
int iCell; /* Current cell number */
int bOnePage; /* True to stop after one page */
int szDb;
sqlite3_int64 iRowid;
/* Only for the sqlite_dbdata table */
DbdataBuffer rec;
sqlite3_int64 nRec; /* Size of pRec[] in bytes */
sqlite3_int64 nHdr; /* Size of header in bytes */
int iField; /* Current field number */
u8 *pHdrPtr;
u8 *pPtr;
u32 enc; /* Text encoding */
|
| ︙ | ︙ | |||
14392 14393 14394 14395 14396 14397 14398 14399 14400 14401 14402 14403 14404 14405 |
#define DBPTR_COLUMN_SCHEMA 2
#define DBPTR_SCHEMA \
"CREATE TABLE x(" \
" pgno INTEGER," \
" child INTEGER," \
" schema TEXT HIDDEN" \
")"
/*
** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual
** table.
*/
static int dbdataConnect(
sqlite3 *db,
| > > > > > > > > > > > > > > > > > > > > > > > > > | 16629 16630 16631 16632 16633 16634 16635 16636 16637 16638 16639 16640 16641 16642 16643 16644 16645 16646 16647 16648 16649 16650 16651 16652 16653 16654 16655 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665 16666 16667 |
#define DBPTR_COLUMN_SCHEMA 2
#define DBPTR_SCHEMA \
"CREATE TABLE x(" \
" pgno INTEGER," \
" child INTEGER," \
" schema TEXT HIDDEN" \
")"
/*
** Ensure the buffer passed as the first argument is at least nMin bytes
** in size. If an error occurs while attempting to resize the buffer,
** SQLITE_NOMEM is returned. Otherwise, SQLITE_OK.
*/
static int dbdataBufferSize(DbdataBuffer *pBuf, sqlite3_int64 nMin){
if( nMin>pBuf->nBuf ){
sqlite3_int64 nNew = nMin+16384;
u8 *aNew = (u8*)sqlite3_realloc64(pBuf->aBuf, nNew);
if( aNew==0 ) return SQLITE_NOMEM;
pBuf->aBuf = aNew;
pBuf->nBuf = nNew;
}
return SQLITE_OK;
}
/*
** Release the allocation managed by buffer pBuf.
*/
static void dbdataBufferFree(DbdataBuffer *pBuf){
sqlite3_free(pBuf->aBuf);
memset(pBuf, 0, sizeof(*pBuf));
}
/*
** Connect to an sqlite_dbdata (pAux==0) or sqlite_dbptr (pAux!=0) virtual
** table.
*/
static int dbdataConnect(
sqlite3 *db,
|
| ︙ | ︙ | |||
14533 14534 14535 14536 14537 14538 14539 | } pCsr->pStmt = 0; pCsr->iPgno = 1; pCsr->iCell = 0; pCsr->iField = 0; pCsr->bOnePage = 0; sqlite3_free(pCsr->aPage); | < | > | 16795 16796 16797 16798 16799 16800 16801 16802 16803 16804 16805 16806 16807 16808 16809 16810 16811 |
}
pCsr->pStmt = 0;
pCsr->iPgno = 1;
pCsr->iCell = 0;
pCsr->iField = 0;
pCsr->bOnePage = 0;
sqlite3_free(pCsr->aPage);
dbdataBufferFree(&pCsr->rec);
pCsr->aPage = 0;
pCsr->nRec = 0;
}
/*
** Close an sqlite_dbdata or sqlite_dbptr cursor.
*/
static int dbdataClose(sqlite3_vtab_cursor *pCursor){
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
|
| ︙ | ︙ | |||
14677 14678 14679 14680 14681 14682 14683 |
static void dbdataValue(
sqlite3_context *pCtx,
u32 enc,
int eType,
u8 *pData,
sqlite3_int64 nData
){
| > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < | < < | | < < < < | < < < < < < < < < < < < < < < | < < | | | | > > > > | > > > | 16939 16940 16941 16942 16943 16944 16945 16946 16947 16948 16949 16950 16951 16952 16953 16954 16955 16956 16957 16958 16959 16960 16961 16962 16963 16964 16965 16966 16967 16968 16969 16970 16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 16985 16986 16987 16988 16989 16990 16991 16992 16993 16994 16995 16996 16997 16998 16999 17000 17001 17002 17003 17004 17005 17006 17007 17008 17009 17010 17011 17012 17013 17014 17015 17016 17017 17018 17019 17020 17021 17022 17023 17024 17025 17026 17027 17028 17029 17030 17031 17032 17033 |
static void dbdataValue(
sqlite3_context *pCtx,
u32 enc,
int eType,
u8 *pData,
sqlite3_int64 nData
){
if( eType>=0 ){
if( dbdataValueBytes(eType)<=nData ){
switch( eType ){
case 0:
case 10:
case 11:
sqlite3_result_null(pCtx);
break;
case 8:
sqlite3_result_int(pCtx, 0);
break;
case 9:
sqlite3_result_int(pCtx, 1);
break;
case 1: case 2: case 3: case 4: case 5: case 6: case 7: {
sqlite3_uint64 v = (signed char)pData[0];
pData++;
switch( eType ){
case 7:
case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
case 4: v = (v<<8) + pData[0]; pData++;
case 3: v = (v<<8) + pData[0]; pData++;
case 2: v = (v<<8) + pData[0]; pData++;
}
if( eType==7 ){
double r;
memcpy(&r, &v, sizeof(r));
sqlite3_result_double(pCtx, r);
}else{
sqlite3_result_int64(pCtx, (sqlite3_int64)v);
}
break;
}
default: {
int n = ((eType-12) / 2);
if( eType % 2 ){
switch( enc ){
#ifndef SQLITE_OMIT_UTF16
case SQLITE_UTF16BE:
sqlite3_result_text16be(pCtx, (void*)pData, n, SQLITE_TRANSIENT);
break;
case SQLITE_UTF16LE:
sqlite3_result_text16le(pCtx, (void*)pData, n, SQLITE_TRANSIENT);
break;
#endif
default:
sqlite3_result_text(pCtx, (char*)pData, n, SQLITE_TRANSIENT);
break;
}
}else{
sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT);
}
}
}
}else{
if( eType==7 ){
sqlite3_result_double(pCtx, 0.0);
}else if( eType<7 ){
sqlite3_result_int(pCtx, 0);
}else if( eType%2 ){
sqlite3_result_text(pCtx, "", 0, SQLITE_STATIC);
}else{
sqlite3_result_blob(pCtx, "", 0, SQLITE_STATIC);
}
}
}
}
/* This macro is a copy of the MX_CELL() macro in the SQLite core. Given
** a page-size, it returns the maximum number of cells that may be present
** on the page. */
#define DBDATA_MX_CELL(pgsz) ((pgsz-8)/6)
/* Maximum number of fields that may appear in a single record. This is
** the "hard-limit", according to comments in sqliteLimit.h. */
#define DBDATA_MX_FIELD 32676
/*
** Move an sqlite_dbdata or sqlite_dbptr cursor to the next entry.
*/
static int dbdataNext(sqlite3_vtab_cursor *pCursor){
DbdataCursor *pCsr = (DbdataCursor*)pCursor;
DbdataTable *pTab = (DbdataTable*)pCursor->pVtab;
|
| ︙ | ︙ | |||
14766 14767 14768 14769 14770 14771 14772 14773 14774 14775 14776 14777 14778 14779 14780 14781 14782 14783 14784 14785 14786 14787 14788 14789 |
if( pCsr->bOnePage ) return SQLITE_OK;
pCsr->iPgno++;
}
assert( iOff+3+2<=pCsr->nPage );
pCsr->iCell = pTab->bPtr ? -2 : 0;
pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]);
}
if( pTab->bPtr ){
if( pCsr->aPage[iOff]!=0x02 && pCsr->aPage[iOff]!=0x05 ){
pCsr->iCell = pCsr->nCell;
}
pCsr->iCell++;
if( pCsr->iCell>=pCsr->nCell ){
sqlite3_free(pCsr->aPage);
pCsr->aPage = 0;
if( pCsr->bOnePage ) return SQLITE_OK;
pCsr->iPgno++;
}else{
return SQLITE_OK;
}
}else{
/* If there is no record loaded, load it now. */
| > > > > | | 17049 17050 17051 17052 17053 17054 17055 17056 17057 17058 17059 17060 17061 17062 17063 17064 17065 17066 17067 17068 17069 17070 17071 17072 17073 17074 17075 17076 17077 17078 17079 17080 17081 17082 17083 17084 |
if( pCsr->bOnePage ) return SQLITE_OK;
pCsr->iPgno++;
}
assert( iOff+3+2<=pCsr->nPage );
pCsr->iCell = pTab->bPtr ? -2 : 0;
pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]);
if( pCsr->nCell>DBDATA_MX_CELL(pCsr->nPage) ){
pCsr->nCell = DBDATA_MX_CELL(pCsr->nPage);
}
}
if( pTab->bPtr ){
if( pCsr->aPage[iOff]!=0x02 && pCsr->aPage[iOff]!=0x05 ){
pCsr->iCell = pCsr->nCell;
}
pCsr->iCell++;
if( pCsr->iCell>=pCsr->nCell ){
sqlite3_free(pCsr->aPage);
pCsr->aPage = 0;
if( pCsr->bOnePage ) return SQLITE_OK;
pCsr->iPgno++;
}else{
return SQLITE_OK;
}
}else{
/* If there is no record loaded, load it now. */
assert( pCsr->rec.aBuf!=0 || pCsr->nRec==0 );
if( pCsr->nRec==0 ){
int bHasRowid = 0;
int nPointer = 0;
sqlite3_int64 nPayload = 0;
sqlite3_int64 nHdr = 0;
int iHdr;
int U, X;
int nLocal;
|
| ︙ | ︙ | |||
14810 14811 14812 14813 14814 14815 14816 14817 |
pCsr->iCell = pCsr->nCell;
break;
}
if( pCsr->iCell>=pCsr->nCell ){
bNextPage = 1;
}else{
| > < | | | > | 17097 17098 17099 17100 17101 17102 17103 17104 17105 17106 17107 17108 17109 17110 17111 17112 17113 17114 17115 17116 17117 17118 17119 17120 17121 17122 17123 17124 17125 17126 17127 17128 |
pCsr->iCell = pCsr->nCell;
break;
}
if( pCsr->iCell>=pCsr->nCell ){
bNextPage = 1;
}else{
int iCellPtr = iOff + 8 + nPointer + pCsr->iCell*2;
if( iCellPtr>pCsr->nPage ){
bNextPage = 1;
}else{
iOff = get_uint16(&pCsr->aPage[iCellPtr]);
}
/* For an interior node cell, skip past the child-page number */
iOff += nPointer;
/* Load the "byte of payload including overflow" field */
if( bNextPage || iOff>pCsr->nPage || iOff<=iCellPtr ){
bNextPage = 1;
}else{
iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload);
if( nPayload>0x7fffff00 ) nPayload &= 0x3fff;
if( nPayload==0 ) nPayload = 1;
}
/* If this is a leaf intkey cell, load the rowid */
if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){
iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey);
}
|
| ︙ | ︙ | |||
14861 14862 14863 14864 14865 14866 14867 |
if( bNextPage || nLocal+iOff>pCsr->nPage ){
bNextPage = 1;
}else{
/* Allocate space for payload. And a bit more to catch small buffer
** overruns caused by attempting to read a varint or similar from
** near the end of a corrupt record. */
| | | | | | | > > > | | | | > > | | < | | < | | 17149 17150 17151 17152 17153 17154 17155 17156 17157 17158 17159 17160 17161 17162 17163 17164 17165 17166 17167 17168 17169 17170 17171 17172 17173 17174 17175 17176 17177 17178 17179 17180 17181 17182 17183 17184 17185 17186 17187 17188 17189 17190 17191 17192 17193 17194 17195 17196 17197 17198 17199 17200 17201 17202 17203 17204 17205 17206 17207 17208 17209 17210 17211 17212 17213 17214 17215 17216 17217 17218 17219 17220 17221 17222 17223 17224 17225 17226 17227 17228 17229 17230 17231 17232 17233 17234 17235 17236 17237 17238 17239 17240 |
if( bNextPage || nLocal+iOff>pCsr->nPage ){
bNextPage = 1;
}else{
/* Allocate space for payload. And a bit more to catch small buffer
** overruns caused by attempting to read a varint or similar from
** near the end of a corrupt record. */
rc = dbdataBufferSize(&pCsr->rec, nPayload+DBDATA_PADDING_BYTES);
if( rc!=SQLITE_OK ) return rc;
assert( pCsr->rec.aBuf!=0 );
assert( nPayload!=0 );
/* Load the nLocal bytes of payload */
memcpy(pCsr->rec.aBuf, &pCsr->aPage[iOff], nLocal);
iOff += nLocal;
/* Load content from overflow pages */
if( nPayload>nLocal ){
sqlite3_int64 nRem = nPayload - nLocal;
u32 pgnoOvfl = get_uint32(&pCsr->aPage[iOff]);
while( nRem>0 ){
u8 *aOvfl = 0;
int nOvfl = 0;
int nCopy;
rc = dbdataLoadPage(pCsr, pgnoOvfl, &aOvfl, &nOvfl);
assert( rc!=SQLITE_OK || aOvfl==0 || nOvfl==pCsr->nPage );
if( rc!=SQLITE_OK ) return rc;
if( aOvfl==0 ) break;
nCopy = U-4;
if( nCopy>nRem ) nCopy = nRem;
memcpy(&pCsr->rec.aBuf[nPayload-nRem], &aOvfl[4], nCopy);
nRem -= nCopy;
pgnoOvfl = get_uint32(aOvfl);
sqlite3_free(aOvfl);
}
nPayload -= nRem;
}
memset(&pCsr->rec.aBuf[nPayload], 0, DBDATA_PADDING_BYTES);
pCsr->nRec = nPayload;
iHdr = dbdataGetVarintU32(pCsr->rec.aBuf, &nHdr);
if( nHdr>nPayload ) nHdr = 0;
pCsr->nHdr = nHdr;
pCsr->pHdrPtr = &pCsr->rec.aBuf[iHdr];
pCsr->pPtr = &pCsr->rec.aBuf[pCsr->nHdr];
pCsr->iField = (bHasRowid ? -1 : 0);
}
}
}else{
pCsr->iField++;
if( pCsr->iField>0 ){
sqlite3_int64 iType;
if( pCsr->pHdrPtr>=&pCsr->rec.aBuf[pCsr->nRec]
|| pCsr->iField>=DBDATA_MX_FIELD
){
bNextPage = 1;
}else{
int szField = 0;
pCsr->pHdrPtr += dbdataGetVarintU32(pCsr->pHdrPtr, &iType);
szField = dbdataValueBytes(iType);
if( (pCsr->nRec - (pCsr->pPtr - pCsr->rec.aBuf))<szField ){
pCsr->pPtr = &pCsr->rec.aBuf[pCsr->nRec];
}else{
pCsr->pPtr += szField;
}
}
}
}
if( bNextPage ){
sqlite3_free(pCsr->aPage);
pCsr->aPage = 0;
pCsr->nRec = 0;
if( pCsr->bOnePage ) return SQLITE_OK;
pCsr->iPgno++;
}else{
if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->rec.aBuf[pCsr->nHdr] ){
return SQLITE_OK;
}
/* Advance to the next cell. The next iteration of the loop will load
** the record and so on. */
pCsr->nRec = 0;
pCsr->iCell++;
}
}
}
assert( !"can't get here" );
return SQLITE_OK;
|
| ︙ | ︙ | |||
15125 15126 15127 15128 15129 15130 15131 |
break;
case DBDATA_COLUMN_FIELD:
sqlite3_result_int(ctx, pCsr->iField);
break;
case DBDATA_COLUMN_VALUE: {
if( pCsr->iField<0 ){
sqlite3_result_int64(ctx, pCsr->iIntkey);
| | | | 17416 17417 17418 17419 17420 17421 17422 17423 17424 17425 17426 17427 17428 17429 17430 17431 17432 17433 17434 17435 |
break;
case DBDATA_COLUMN_FIELD:
sqlite3_result_int(ctx, pCsr->iField);
break;
case DBDATA_COLUMN_VALUE: {
if( pCsr->iField<0 ){
sqlite3_result_int64(ctx, pCsr->iIntkey);
}else if( &pCsr->rec.aBuf[pCsr->nRec] >= pCsr->pPtr ){
sqlite3_int64 iType;
dbdataGetVarintU32(pCsr->pHdrPtr, &iType);
dbdataValue(
ctx, pCsr->enc, iType, pCsr->pPtr,
&pCsr->rec.aBuf[pCsr->nRec] - pCsr->pPtr
);
}
break;
}
}
}
return SQLITE_OK;
|
| ︙ | ︙ | |||
15567 15568 15569 15570 15571 15572 15573 |
const char *zFmt, ...
){
char *z = 0;
va_list ap;
va_start(ap, zFmt);
if( zFmt ){
z = sqlite3_vmprintf(zFmt, ap);
| < > | 17858 17859 17860 17861 17862 17863 17864 17865 17866 17867 17868 17869 17870 17871 17872 17873 |
const char *zFmt, ...
){
char *z = 0;
va_list ap;
va_start(ap, zFmt);
if( zFmt ){
z = sqlite3_vmprintf(zFmt, ap);
}
va_end(ap);
sqlite3_free(p->zErrMsg);
p->zErrMsg = z;
p->errCode = errCode;
return errCode;
}
|
| ︙ | ︙ | |||
16393 16394 16395 16396 16397 16398 16399 |
}
rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0);
if( rc==SQLITE_OK ){
recoverSqlCallback(p, zSql);
if( bTable && !bVirtual ){
if( SQLITE_ROW==sqlite3_step(pTblname) ){
const char *zTbl = (const char*)sqlite3_column_text(pTblname, 0);
| | | 18684 18685 18686 18687 18688 18689 18690 18691 18692 18693 18694 18695 18696 18697 18698 |
}
rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0);
if( rc==SQLITE_OK ){
recoverSqlCallback(p, zSql);
if( bTable && !bVirtual ){
if( SQLITE_ROW==sqlite3_step(pTblname) ){
const char *zTbl = (const char*)sqlite3_column_text(pTblname, 0);
if( zTbl ) recoverAddTable(p, zTbl, iRoot);
}
recoverReset(p, pTblname);
}
}else if( rc!=SQLITE_ERROR ){
recoverDbError(p, p->dbOut);
}
sqlite3_free(zFree);
|
| ︙ | ︙ | |||
19259 19260 19261 19262 19263 19264 19265 |
}
break;
}
case MODE_ScanExp:
case MODE_Explain: {
static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13};
static const int aExplainMap[] = {0, 1, 2, 3, 4, 5, 6, 7 };
| | | 21550 21551 21552 21553 21554 21555 21556 21557 21558 21559 21560 21561 21562 21563 21564 |
}
break;
}
case MODE_ScanExp:
case MODE_Explain: {
static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13};
static const int aExplainMap[] = {0, 1, 2, 3, 4, 5, 6, 7 };
static const int aScanExpWidth[] = {4, 15, 6, 13, 4, 4, 4, 13, 2, 13};
static const int aScanExpMap[] = {0, 9, 8, 1, 2, 3, 4, 5, 6, 7 };
const int *aWidth = aExplainWidth;
const int *aMap = aExplainMap;
int nWidth = ArraySize(aExplainWidth);
int iIndent = 1;
|
| ︙ | ︙ | |||
20269 20270 20271 20272 20273 20274 20275 |
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pArg);
#else
if( pArg->scanstatsOn==3 ){
const char *zSql =
" SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec,"
| > > > > > | > | 22560 22561 22562 22563 22564 22565 22566 22567 22568 22569 22570 22571 22572 22573 22574 22575 22576 22577 22578 22579 22580 |
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pArg);
#else
if( pArg->scanstatsOn==3 ){
const char *zSql =
" SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec,"
" format('% 6s (%.2f%%)',"
" CASE WHEN ncycle<100_000 THEN ncycle || ' '"
" WHEN ncycle<100_000_000 THEN (ncycle/1_000) || 'K'"
" WHEN ncycle<100_000_000_000 THEN (ncycle/1_000_000) || 'M'"
" ELSE (ncycle/1000_000_000) || 'G' END,"
" ncycle*100.0/(sum(ncycle) OVER ())"
" ) AS cycles"
" FROM bytecode(?)";
int rc = SQLITE_OK;
sqlite3_stmt *pStmt = 0;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc==SQLITE_OK ){
sqlite3_stmt *pSave = pArg->pStmt;
|
| ︙ | ︙ | |||
20377 20378 20379 20380 20381 20382 20383 20384 20385 20386 20387 20388 20389 20390 |
}else if( sqlite3_strlike("_NAN", zVar, 0)==0 ){
sqlite3_bind_double(pStmt, i, NAN);
#endif
#ifdef INFINITY
}else if( sqlite3_strlike("_INF", zVar, 0)==0 ){
sqlite3_bind_double(pStmt, i, INFINITY);
#endif
}else{
sqlite3_bind_null(pStmt, i);
}
sqlite3_reset(pQ);
}
sqlite3_finalize(pQ);
}
| > > > > > > > > > | 22674 22675 22676 22677 22678 22679 22680 22681 22682 22683 22684 22685 22686 22687 22688 22689 22690 22691 22692 22693 22694 22695 22696 |
}else if( sqlite3_strlike("_NAN", zVar, 0)==0 ){
sqlite3_bind_double(pStmt, i, NAN);
#endif
#ifdef INFINITY
}else if( sqlite3_strlike("_INF", zVar, 0)==0 ){
sqlite3_bind_double(pStmt, i, INFINITY);
#endif
}else if( strncmp(zVar, "$int_", 5)==0 ){
sqlite3_bind_int(pStmt, i, atoi(&zVar[5]));
}else if( strncmp(zVar, "$text_", 6)==0 ){
size_t szVar = strlen(zVar);
char *zBuf = sqlite3_malloc64( szVar-5 );
if( zBuf ){
memcpy(zBuf, &zVar[6], szVar-5);
sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8);
}
}else{
sqlite3_bind_null(pStmt, i);
}
sqlite3_reset(pQ);
}
sqlite3_finalize(pQ);
}
|
| ︙ | ︙ | |||
20615 20616 20617 20618 20619 20620 20621 20622 20623 20624 20625 20626 20627 20628 | int bw = p->cmOpts.bWordWrap; const char *zEmpty = ""; const char *zShowNull = p->nullValue; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ) return; nColumn = sqlite3_column_count(pStmt); nAlloc = nColumn*4; if( nAlloc<=0 ) nAlloc = 1; azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); shell_check_oom(azData); azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); shell_check_oom(azNextLine); memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); | > | 22921 22922 22923 22924 22925 22926 22927 22928 22929 22930 22931 22932 22933 22934 22935 | int bw = p->cmOpts.bWordWrap; const char *zEmpty = ""; const char *zShowNull = p->nullValue; rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ) return; nColumn = sqlite3_column_count(pStmt); if( nColumn==0 ) goto columnar_end; nAlloc = nColumn*4; if( nAlloc<=0 ) nAlloc = 1; azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); shell_check_oom(azData); azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); shell_check_oom(azNextLine); memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); |
| ︙ | ︙ | |||
20700 20701 20702 20703 20704 20705 20706 |
z = azData[i];
if( z==0 ) z = (char*)zEmpty;
n = strlenChar(z);
j = i%nColumn;
if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
}
if( seenInterrupt ) goto columnar_end;
| < | 23007 23008 23009 23010 23011 23012 23013 23014 23015 23016 23017 23018 23019 23020 |
z = azData[i];
if( z==0 ) z = (char*)zEmpty;
n = strlenChar(z);
j = i%nColumn;
if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
}
if( seenInterrupt ) goto columnar_end;
switch( p->cMode ){
case MODE_Column: {
colSep = " ";
rowSep = "\n";
if( p->showHeader ){
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
|
| ︙ | ︙ | |||
21092 21093 21094 21095 21096 21097 21098 21099 21100 21101 21102 21103 21104 21105 21106 21107 21108 21109 21110 21111 21112 21113 21114 21115 21116 21117 21118 21119 21120 21121 21122 |
if( pArg->autoEQP>=AUTOEQP_trigger ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0);
}
pExplain = pStmt;
sqlite3_reset(pExplain);
rc = sqlite3_stmt_explain(pExplain, 2);
if( rc==SQLITE_OK ){
while( sqlite3_step(pExplain)==SQLITE_ROW ){
const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
int iEqpId = sqlite3_column_int(pExplain, 0);
int iParentId = sqlite3_column_int(pExplain, 1);
if( zEQPLine==0 ) zEQPLine = "";
if( zEQPLine[0]=='-' ) eqp_render(pArg, 0);
eqp_append(pArg, iEqpId, iParentId, zEQPLine);
}
eqp_render(pArg, 0);
}
if( pArg->autoEQP>=AUTOEQP_full ){
/* Also do an EXPLAIN for ".eqp full" mode */
sqlite3_reset(pExplain);
rc = sqlite3_stmt_explain(pExplain, 1);
if( rc==SQLITE_OK ){
pArg->cMode = MODE_Explain;
assert( sqlite3_stmt_isexplain(pExplain)==1 );
explain_data_prepare(pArg, pExplain);
exec_prepared_stmt(pArg, pExplain);
explain_data_delete(pArg);
}
}
if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0);
| > > | 23398 23399 23400 23401 23402 23403 23404 23405 23406 23407 23408 23409 23410 23411 23412 23413 23414 23415 23416 23417 23418 23419 23420 23421 23422 23423 23424 23425 23426 23427 23428 23429 23430 |
if( pArg->autoEQP>=AUTOEQP_trigger ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0);
}
pExplain = pStmt;
sqlite3_reset(pExplain);
rc = sqlite3_stmt_explain(pExplain, 2);
if( rc==SQLITE_OK ){
bind_prepared_stmt(pArg, pExplain);
while( sqlite3_step(pExplain)==SQLITE_ROW ){
const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
int iEqpId = sqlite3_column_int(pExplain, 0);
int iParentId = sqlite3_column_int(pExplain, 1);
if( zEQPLine==0 ) zEQPLine = "";
if( zEQPLine[0]=='-' ) eqp_render(pArg, 0);
eqp_append(pArg, iEqpId, iParentId, zEQPLine);
}
eqp_render(pArg, 0);
}
if( pArg->autoEQP>=AUTOEQP_full ){
/* Also do an EXPLAIN for ".eqp full" mode */
sqlite3_reset(pExplain);
rc = sqlite3_stmt_explain(pExplain, 1);
if( rc==SQLITE_OK ){
pArg->cMode = MODE_Explain;
assert( sqlite3_stmt_isexplain(pExplain)==1 );
bind_prepared_stmt(pArg, pExplain);
explain_data_prepare(pArg, pExplain);
exec_prepared_stmt(pArg, pExplain);
explain_data_delete(pArg);
}
}
if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0);
|
| ︙ | ︙ | |||
21583 21584 21585 21586 21587 21588 21589 21590 21591 21592 21593 21594 21595 21596 | #endif #ifndef SQLITE_OMIT_TEST_CONTROL ",imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", #ifdef SQLITE_ENABLE_IOTRACE ",iotrace FILE Enable I/O diagnostic logging to FILE", #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", ".lint OPTIONS Report potential schema issues.", " Options:", " fkey-indexes Find missing foreign key indexes", | > | 23891 23892 23893 23894 23895 23896 23897 23898 23899 23900 23901 23902 23903 23904 23905 | #endif #ifndef SQLITE_OMIT_TEST_CONTROL ",imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", ".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db", #ifdef SQLITE_ENABLE_IOTRACE ",iotrace FILE Enable I/O diagnostic logging to FILE", #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", ".lint OPTIONS Report potential schema issues.", " Options:", " fkey-indexes Find missing foreign key indexes", |
| ︙ | ︙ | |||
22202 22203 22204 22205 22206 22207 22208 22209 22210 22211 22212 22213 22214 22215 22216 |
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
sqlite3_shathree_init(p->db, 0, 0);
sqlite3_uint_init(p->db, 0, 0);
sqlite3_decimal_init(p->db, 0, 0);
sqlite3_base64_init(p->db, 0, 0);
sqlite3_base85_init(p->db, 0, 0);
sqlite3_regexp_init(p->db, 0, 0);
sqlite3_ieee_init(p->db, 0, 0);
sqlite3_series_init(p->db, 0, 0);
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_fileio_init(p->db, 0, 0);
| > > | 24511 24512 24513 24514 24515 24516 24517 24518 24519 24520 24521 24522 24523 24524 24525 24526 24527 |
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
sqlite3_shathree_init(p->db, 0, 0);
sqlite3_uint_init(p->db, 0, 0);
sqlite3_stmtrand_init(p->db, 0, 0);
sqlite3_decimal_init(p->db, 0, 0);
sqlite3_percentile_init(p->db, 0, 0);
sqlite3_base64_init(p->db, 0, 0);
sqlite3_base85_init(p->db, 0, 0);
sqlite3_regexp_init(p->db, 0, 0);
sqlite3_ieee_init(p->db, 0, 0);
sqlite3_series_init(p->db, 0, 0);
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_fileio_init(p->db, 0, 0);
|
| ︙ | ︙ | |||
22355 22356 22357 22358 22359 22360 22361 | (void)iEnd; rl_attempted_completion_over = 1; return rl_completion_matches(zText, readline_completion_generator); } #elif HAVE_LINENOISE /* | | > | > > | 24666 24667 24668 24669 24670 24671 24672 24673 24674 24675 24676 24677 24678 24679 24680 24681 24682 24683 24684 24685 24686 24687 24688 24689 24690 24691 |
(void)iEnd;
rl_attempted_completion_over = 1;
return rl_completion_matches(zText, readline_completion_generator);
}
#elif HAVE_LINENOISE
/*
** Linenoise completion callback. Note that the 3rd argument is from
** the "msteveb" version of linenoise, not the "antirez" version.
*/
static void linenoise_completion(const char *zLine, linenoiseCompletions *lc,
void *pUserData){
i64 nLine = strlen(zLine);
i64 i, iStart;
sqlite3_stmt *pStmt = 0;
char *zSql;
char zBuf[1000];
UNUSED_PARAMETER(pUserData);
if( nLine>(i64)sizeof(zBuf)-30 ) return;
if( zLine[0]=='.' || zLine[0]=='#') return;
for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
if( i==nLine-1 ) return;
iStart = i+1;
memcpy(zBuf, zLine, iStart);
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
|
| ︙ | ︙ | |||
23159 23160 23161 23162 23163 23164 23165 23166 23167 23168 23169 |
sqlite3_free(zSchemaTab);
sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
oputf("%-20s %u\n", "data version", iDataVersion);
return 0;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
/*
** Print the current sqlite3_errmsg() value to stderr and return 1.
*/
static int shellDatabaseError(sqlite3 *db){
| > > > > > > | < | 25473 25474 25475 25476 25477 25478 25479 25480 25481 25482 25483 25484 25485 25486 25487 25488 25489 25490 25491 25492 25493 25494 25495 25496 25497 |
sqlite3_free(zSchemaTab);
sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
oputf("%-20s %u\n", "data version", iDataVersion);
return 0;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
/*
** Print the given string as an error message.
*/
static void shellEmitError(const char *zErr){
eputf("Error: %s\n", zErr);
}
/*
** Print the current sqlite3_errmsg() value to stderr and return 1.
*/
static int shellDatabaseError(sqlite3 *db){
shellEmitError(sqlite3_errmsg(db));
return 1;
}
/*
** Compare the pattern in zGlob[] against the text in z[]. Return TRUE
** if they match and FALSE (0) if they do not match.
**
|
| ︙ | ︙ | |||
23709 23710 23711 23712 23713 23714 23715 |
*/
static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){
va_list ap;
char *z;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
| | | 26028 26029 26030 26031 26032 26033 26034 26035 26036 26037 26038 26039 26040 26041 26042 |
*/
static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){
va_list ap;
char *z;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
shellEmitError(z);
if( pAr->fromCmdLine ){
eputz("Use \"-A\" for more help\n");
}else{
eputz("Use \".archive --help\" for more help\n");
}
sqlite3_free(z);
return SQLITE_ERROR;
|
| ︙ | ︙ | |||
24492 24493 24494 24495 24496 24497 24498 24499 24500 24501 24502 24503 24504 24505 |
eputf("sql error: %s (%d)\n", zErr, errCode);
}
rc = sqlite3_recover_finish(p);
return rc;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
/*
* zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it.
* zAutoColumn(0, &db, ?) => (db!=0) Form columns spec for CREATE TABLE,
* close db and set it to 0, and return the columns spec, to later
* be sqlite3_free()'ed by the caller.
* The return is 0 when either:
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 26811 26812 26813 26814 26815 26816 26817 26818 26819 26820 26821 26822 26823 26824 26825 26826 26827 26828 26829 26830 26831 26832 26833 26834 26835 26836 26837 26838 26839 26840 26841 26842 26843 26844 26845 26846 26847 26848 26849 26850 26851 26852 26853 26854 26855 26856 26857 26858 |
eputf("sql error: %s (%d)\n", zErr, errCode);
}
rc = sqlite3_recover_finish(p);
return rc;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
/*
** Implementation of ".intck STEPS_PER_UNLOCK" command.
*/
static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){
sqlite3_intck *p = 0;
int rc = SQLITE_OK;
rc = sqlite3_intck_open(pState->db, "main", &p);
if( rc==SQLITE_OK ){
i64 nStep = 0;
i64 nError = 0;
const char *zErr = 0;
while( SQLITE_OK==sqlite3_intck_step(p) ){
const char *zMsg = sqlite3_intck_message(p);
if( zMsg ){
oputf("%s\n", zMsg);
nError++;
}
nStep++;
if( nStepPerUnlock && (nStep % nStepPerUnlock)==0 ){
sqlite3_intck_unlock(p);
}
}
rc = sqlite3_intck_error(p, &zErr);
if( zErr ){
eputf("%s\n", zErr);
}
sqlite3_intck_close(p);
oputf("%lld steps, %lld errors\n", nStep, nError);
}
return rc;
}
/*
* zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it.
* zAutoColumn(0, &db, ?) => (db!=0) Form columns spec for CREATE TABLE,
* close db and set it to 0, and return the columns spec, to later
* be sqlite3_free()'ed by the caller.
* The return is 0 when either:
|
| ︙ | ︙ | |||
24734 24735 24736 24737 24738 24739 24740 24741 24742 24743 24744 24745 24746 24747 |
oputz("/* WARNING: "
"Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n"
);
}
shellFinalize(&rc, pStmt);
return rc;
}
/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 27087 27088 27089 27090 27091 27092 27093 27094 27095 27096 27097 27098 27099 27100 27101 27102 27103 27104 27105 27106 27107 27108 27109 27110 27111 27112 27113 27114 27115 27116 27117 27118 27119 27120 27121 27122 27123 27124 27125 27126 27127 27128 27129 27130 27131 27132 27133 27134 27135 27136 27137 27138 27139 |
oputz("/* WARNING: "
"Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n"
);
}
shellFinalize(&rc, pStmt);
return rc;
}
/*
** Fault-Simulator state and logic.
*/
static struct {
int iId; /* ID that triggers a simulated fault. -1 means "any" */
int iErr; /* The error code to return on a fault */
int iCnt; /* Trigger the fault only if iCnt is already zero */
int iInterval; /* Reset iCnt to this value after each fault */
int eVerbose; /* When to print output */
int nHit; /* Number of hits seen so far */
int nRepeat; /* Turn off after this many hits. 0 for never */
int nSkip; /* Skip this many before first fault */
} faultsim_state = {-1, 0, 0, 0, 0, 0, 0, 0};
/*
** This is the fault-sim callback
*/
static int faultsim_callback(int iArg){
if( faultsim_state.iId>0 && faultsim_state.iId!=iArg ){
return SQLITE_OK;
}
if( faultsim_state.iCnt ){
if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--;
if( faultsim_state.eVerbose>=2 ){
oputf("FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt);
}
return SQLITE_OK;
}
if( faultsim_state.eVerbose>=1 ){
oputf("FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr);
}
faultsim_state.iCnt = faultsim_state.iInterval;
faultsim_state.nHit++;
if( faultsim_state.nRepeat>0 && faultsim_state.nRepeat<=faultsim_state.nHit ){
faultsim_state.iCnt = -1;
}
return faultsim_state.iErr;
}
/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
|
| ︙ | ︙ | |||
24867 24868 24869 24870 24871 24872 24873 |
if( bAsync ){
sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;",
0, 0, 0);
}
open_db(p, 0);
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
if( pBackup==0 ){
| | | | 27259 27260 27261 27262 27263 27264 27265 27266 27267 27268 27269 27270 27271 27272 27273 27274 27275 27276 27277 27278 27279 27280 27281 27282 |
if( bAsync ){
sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;",
0, 0, 0);
}
open_db(p, 0);
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
if( pBackup==0 ){
shellDatabaseError(pDest);
close_db(pDest);
return 1;
}
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){}
sqlite3_backup_finish(pBackup);
if( rc==SQLITE_DONE ){
rc = 0;
}else{
shellDatabaseError(pDest);
rc = 1;
}
close_db(pDest);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
if( c=='b' && n>=3 && cli_strncmp(azArg[0], "bail", n)==0 ){
|
| ︙ | ︙ | |||
25052 25053 25054 25055 25056 25057 25058 |
char **azName = 0;
int nName = 0;
sqlite3_stmt *pStmt;
int i;
open_db(p, 0);
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
if( rc ){
| | | 27444 27445 27446 27447 27448 27449 27450 27451 27452 27453 27454 27455 27456 27457 27458 |
char **azName = 0;
int nName = 0;
sqlite3_stmt *pStmt;
int i;
open_db(p, 0);
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
if( rc ){
shellDatabaseError(p->db);
rc = 1;
}else{
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zSchema = (const char *)sqlite3_column_text(pStmt,1);
const char *zFile = (const char*)sqlite3_column_text(pStmt,2);
if( zSchema==0 || zFile==0 ) continue;
azName = sqlite3_realloc(azName, (nName+1)*2*sizeof(char*));
|
| ︙ | ︙ | |||
25226 25227 25228 25229 25230 25231 25232 |
);
run_schema_dump_query(p,zSql);
sqlite3_free(zSql);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
zSql = sqlite3_mprintf(
"SELECT sql FROM sqlite_schema AS o "
"WHERE (%s) AND sql NOT NULL"
| | > | 27618 27619 27620 27621 27622 27623 27624 27625 27626 27627 27628 27629 27630 27631 27632 27633 |
);
run_schema_dump_query(p,zSql);
sqlite3_free(zSql);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
zSql = sqlite3_mprintf(
"SELECT sql FROM sqlite_schema AS o "
"WHERE (%s) AND sql NOT NULL"
" AND type IN ('index','trigger','view') "
"ORDER BY type COLLATE NOCASE DESC",
zLike
);
run_table_dump_query(p, zSql);
sqlite3_free(zSql);
}
sqlite3_free(zLike);
if( p->writableSchema ){
|
| ︙ | ︙ | |||
25549 25550 25551 25552 25553 25554 25555 |
showHelp(p->out, 0);
}
}else
#ifndef SQLITE_SHELL_FIDDLE
if( c=='i' && cli_strncmp(azArg[0], "import", n)==0 ){
char *zTable = 0; /* Insert data into this table */
| | | | < | 27942 27943 27944 27945 27946 27947 27948 27949 27950 27951 27952 27953 27954 27955 27956 27957 27958 27959 27960 27961 27962 27963 27964 |
showHelp(p->out, 0);
}
}else
#ifndef SQLITE_SHELL_FIDDLE
if( c=='i' && cli_strncmp(azArg[0], "import", n)==0 ){
char *zTable = 0; /* Insert data into this table */
char *zSchema = 0; /* Schema of zTable */
char *zFile = 0; /* Name of file to extra content from */
sqlite3_stmt *pStmt = NULL; /* A statement */
int nCol; /* Number of columns in the table */
i64 nByte; /* Number of bytes in an SQL string */
int i, j; /* Loop counters */
int needCommit; /* True to COMMIT or ROLLBACK at end */
int nSep; /* Number of bytes in p->colSeparator[] */
char *zSql = 0; /* An SQL statement */
ImportCtx sCtx; /* Reader context */
char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
int eVerbose = 0; /* Larger for more console output */
int nSkip = 0; /* Initial lines to skip */
int useOutputMode = 1; /* Use output mode to determine separators */
char *zCreate = 0; /* CREATE TABLE statement text */
|
| ︙ | ︙ | |||
25692 25693 25694 25695 25696 25697 25698 |
import_cleanup(&sCtx);
shell_out_of_memory();
}
/* Below, resources must be freed before exit. */
while( (nSkip--)>0 ){
while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
}
| < < < < < < < < < < < < | > | > < < < < > > > > > > > | > > > > > > > > > > | | < < | | > > | | > > > > > > > > | > | > > > > > > > | | > > < < | 28084 28085 28086 28087 28088 28089 28090 28091 28092 28093 28094 28095 28096 28097 28098 28099 28100 28101 28102 28103 28104 28105 28106 28107 28108 28109 28110 28111 28112 28113 28114 28115 28116 28117 28118 28119 28120 28121 28122 28123 28124 28125 28126 28127 28128 28129 28130 28131 28132 28133 28134 28135 28136 28137 28138 28139 28140 28141 28142 28143 28144 28145 28146 28147 28148 28149 28150 28151 28152 28153 28154 28155 28156 28157 28158 28159 28160 28161 28162 28163 28164 28165 28166 28167 28168 28169 28170 28171 28172 28173 28174 28175 28176 28177 28178 28179 28180 28181 28182 28183 28184 28185 28186 28187 28188 28189 28190 28191 28192 28193 28194 28195 28196 28197 28198 28199 28200 28201 28202 28203 |
import_cleanup(&sCtx);
shell_out_of_memory();
}
/* Below, resources must be freed before exit. */
while( (nSkip--)>0 ){
while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
}
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) ){
/* Table does not exist. Create it. */
sqlite3 *dbCols = 0;
char *zRenames = 0;
char *zColDefs;
zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
zSchema ? zSchema : "main", zTable);
while( xRead(&sCtx) ){
zAutoColumn(sCtx.z, &dbCols, 0);
if( sCtx.cTerm!=sCtx.cColSep ) break;
}
zColDefs = zAutoColumn(0, &dbCols, &zRenames);
if( zRenames!=0 ){
sputf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
"Columns renamed during .import %s due to duplicates:\n"
"%s\n", sCtx.zFile, zRenames);
sqlite3_free(zRenames);
}
assert(dbCols==0);
if( zColDefs==0 ){
eputf("%s: empty file\n", sCtx.zFile);
import_cleanup(&sCtx);
rc = 1;
sqlite3_free(zCreate);
goto meta_command_exit;
}
zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
if( zCreate==0 ){
import_cleanup(&sCtx);
shell_out_of_memory();
}
if( eVerbose>=1 ){
oputf("%s\n", zCreate);
}
rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
sqlite3_free(zCreate);
zCreate = 0;
if( rc ){
eputf("%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
import_cleanup(&sCtx);
rc = 1;
goto meta_command_exit;
}
}
zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);",
zTable, zSchema);
if( zSql==0 ){
import_cleanup(&sCtx);
shell_out_of_memory();
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
zSql = 0;
if( rc ){
if (pStmt) sqlite3_finalize(pStmt);
shellDatabaseError(p->db);
import_cleanup(&sCtx);
rc = 1;
goto meta_command_exit;
}
if( sqlite3_step(pStmt)==SQLITE_ROW ){
nCol = sqlite3_column_int(pStmt, 0);
}else{
nCol = 0;
}
sqlite3_finalize(pStmt);
pStmt = 0;
if( nCol==0 ) return 0; /* no columns, no error */
nByte = 64 /* space for "INSERT INTO", "VALUES(", ")\0" */
+ (zSchema ? strlen(zSchema)*2 + 2: 0) /* Quoted schema name */
+ strlen(zTable)*2 + 2 /* Quoted table name */
+ nCol*2; /* Space for ",?" for each column */
zSql = sqlite3_malloc64( nByte );
if( zSql==0 ){
import_cleanup(&sCtx);
shell_out_of_memory();
}
if( zSchema ){
sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?",
zSchema, zTable);
}else{
sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
}
j = strlen30(zSql);
for(i=1; i<nCol; i++){
zSql[j++] = ',';
zSql[j++] = '?';
}
zSql[j++] = ')';
zSql[j] = 0;
assert( j<nByte );
if( eVerbose>=2 ){
oputf("Insert using: %s\n", zSql);
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
zSql = 0;
if( rc ){
shellDatabaseError(p->db);
if (pStmt) sqlite3_finalize(pStmt);
import_cleanup(&sCtx);
rc = 1;
goto meta_command_exit;
}
needCommit = sqlite3_get_autocommit(p->db);
if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
do{
int startLine = sCtx.nLine;
for(i=0; i<nCol; i++){
char *z = xRead(&sCtx);
/*
|
| ︙ | ︙ | |||
25948 25949 25950 25951 25952 25953 25954 25955 25956 25957 25958 25959 25960 25961 |
}else{
eputf("SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
rc = 1;
}
sqlite3_free(zSql);
}else
#endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */
#ifdef SQLITE_ENABLE_IOTRACE
if( c=='i' && cli_strncmp(azArg[0], "iotrace", n)==0 ){
SQLITE_API extern void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...);
if( iotrace && iotrace!=stdout ) fclose(iotrace);
iotrace = 0;
if( nArg<2 ){
| > > > > > > > > > > > > > > > | 28359 28360 28361 28362 28363 28364 28365 28366 28367 28368 28369 28370 28371 28372 28373 28374 28375 28376 28377 28378 28379 28380 28381 28382 28383 28384 28385 28386 28387 |
}else{
eputf("SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
rc = 1;
}
sqlite3_free(zSql);
}else
#endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */
if( c=='i' && cli_strncmp(azArg[0], "intck", n)==0 ){
i64 iArg = 0;
if( nArg==2 ){
iArg = integerValue(azArg[1]);
if( iArg==0 ) iArg = -1;
}
if( (nArg!=1 && nArg!=2) || iArg<0 ){
eputf("%s","Usage: .intck STEPS_PER_UNLOCK\n");
rc = 1;
goto meta_command_exit;
}
open_db(p, 0);
rc = intckDatabaseCmd(p, iArg);
}else
#ifdef SQLITE_ENABLE_IOTRACE
if( c=='i' && cli_strncmp(azArg[0], "iotrace", n)==0 ){
SQLITE_API extern void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...);
if( iotrace && iotrace!=stdout ) fclose(iotrace);
iotrace = 0;
if( nArg<2 ){
|
| ︙ | ︙ | |||
26052 26053 26054 26055 26056 26057 26058 |
goto meta_command_exit;
}
zFile = azArg[1];
zProc = nArg>=3 ? azArg[2] : 0;
open_db(p, 0);
rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
if( rc!=SQLITE_OK ){
| | | 28478 28479 28480 28481 28482 28483 28484 28485 28486 28487 28488 28489 28490 28491 28492 |
goto meta_command_exit;
}
zFile = azArg[1];
zProc = nArg>=3 ? azArg[2] : 0;
open_db(p, 0);
rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
if( rc!=SQLITE_OK ){
shellEmitError(zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}
}else
#endif
if( c=='l' && cli_strncmp(azArg[0], "log", n)==0 ){
|
| ︙ | ︙ | |||
26674 26675 26676 26677 26678 26679 26680 |
eputf("Error: cannot open \"%s\"\n", zSrcFile);
close_db(pSrc);
return 1;
}
open_db(p, 0);
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
if( pBackup==0 ){
| | | | 29100 29101 29102 29103 29104 29105 29106 29107 29108 29109 29110 29111 29112 29113 29114 29115 29116 29117 29118 29119 29120 29121 29122 29123 29124 29125 29126 29127 29128 29129 29130 29131 29132 |
eputf("Error: cannot open \"%s\"\n", zSrcFile);
close_db(pSrc);
return 1;
}
open_db(p, 0);
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
if( pBackup==0 ){
shellDatabaseError(p->db);
close_db(pSrc);
return 1;
}
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
|| rc==SQLITE_BUSY ){
if( rc==SQLITE_BUSY ){
if( nTimeout++ >= 3 ) break;
sqlite3_sleep(100);
}
}
sqlite3_backup_finish(pBackup);
if( rc==SQLITE_DONE ){
rc = 0;
}else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){
eputz("Error: source database is busy\n");
rc = 1;
}else{
shellDatabaseError(p->db);
rc = 1;
}
close_db(pSrc);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){
|
| ︙ | ︙ | |||
26789 26790 26791 26792 26793 26794 26795 |
}
}
if( zDiv ){
sqlite3_stmt *pStmt = 0;
rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list",
-1, &pStmt, 0);
if( rc ){
| | | 29215 29216 29217 29218 29219 29220 29221 29222 29223 29224 29225 29226 29227 29228 29229 |
}
}
if( zDiv ){
sqlite3_stmt *pStmt = 0;
rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list",
-1, &pStmt, 0);
if( rc ){
shellDatabaseError(p->db);
sqlite3_finalize(pStmt);
rc = 1;
goto meta_command_exit;
}
appendText(&sSelect, "SELECT sql FROM", 0);
iSchema = 0;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
| ︙ | ︙ | |||
26858 26859 26860 26861 26862 26863 26864 |
oputf("SQL: %s;\n", sSelect.z);
}else{
rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
}
freeText(&sSelect);
}
if( zErrMsg ){
| | | 29284 29285 29286 29287 29288 29289 29290 29291 29292 29293 29294 29295 29296 29297 29298 |
oputf("SQL: %s;\n", sSelect.z);
}else{
rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
}
freeText(&sSelect);
}
if( zErrMsg ){
shellEmitError(zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}else if( rc != SQLITE_OK ){
eputz("Error: querying schema information\n");
rc = 1;
}else{
rc = 0;
|
| ︙ | ︙ | |||
27622 27623 27624 27625 27626 27627 27628 |
} aCtrl[] = {
{"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" },
{"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" },
/*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/
/*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
{"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
{"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
| | | | | 30048 30049 30050 30051 30052 30053 30054 30055 30056 30057 30058 30059 30060 30061 30062 30063 30064 30065 30066 30067 30068 30069 30070 30071 30072 30073 |
} aCtrl[] = {
{"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" },
{"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" },
/*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/
/*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
{"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
{"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
{"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." },
{"fk_no_action", SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN" },
{"imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"},
{"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" },
{"json_selfcheck", SQLITE_TESTCTRL_JSON_SELFCHECK ,0,"BOOLEAN" },
{"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" },
{"never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" },
{"optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK ..."},
#ifdef YYCOVERAGE
{"parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" },
#endif
{"pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,1, "OFFSET " },
{"prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" },
{"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" },
{"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" },
{"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" },
{"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" },
{"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" },
{"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"},
|
| ︙ | ︙ | |||
27692 27693 27694 27695 27696 27697 27698 27699 |
}
}
if( testctrl<0 ){
eputf("Error: unknown test-control: %s\n"
"Use \".testctrl --help\" for help\n", zCmd);
}else{
switch(testctrl){
| | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 30118 30119 30120 30121 30122 30123 30124 30125 30126 30127 30128 30129 30130 30131 30132 30133 30134 30135 30136 30137 30138 30139 30140 30141 30142 30143 30144 30145 30146 30147 30148 30149 30150 30151 30152 30153 30154 30155 30156 30157 30158 30159 30160 30161 30162 30163 30164 30165 30166 30167 30168 30169 30170 30171 30172 30173 30174 30175 30176 30177 30178 30179 30180 30181 30182 30183 30184 30185 30186 30187 30188 30189 30190 30191 30192 30193 30194 30195 30196 30197 30198 30199 30200 30201 30202 30203 30204 30205 30206 30207 30208 30209 30210 30211 30212 30213 30214 30215 30216 30217 30218 30219 30220 30221 30222 30223 30224 30225 30226 30227 30228 30229 30230 30231 30232 30233 30234 30235 30236 30237 30238 30239 30240 30241 30242 30243 30244 |
}
}
if( testctrl<0 ){
eputf("Error: unknown test-control: %s\n"
"Use \".testctrl --help\" for help\n", zCmd);
}else{
switch(testctrl){
/* Special processing for .testctrl opt MASK ...
** Each MASK argument can be one of:
**
** +LABEL Enable the named optimization
**
** -LABEL Disable the named optimization
**
** INTEGER Mask of optimizations to disable
*/
case SQLITE_TESTCTRL_OPTIMIZATIONS: {
static const struct {
unsigned int mask; /* Mask for this optimization */
unsigned int bDsply; /* Display this on output */
const char *zLabel; /* Name of optimization */
} aLabel[] = {
{ 0x00000001, 1, "QueryFlattener" },
{ 0x00000001, 0, "Flatten" },
{ 0x00000002, 1, "WindowFunc" },
{ 0x00000004, 1, "GroupByOrder" },
{ 0x00000008, 1, "FactorOutConst" },
{ 0x00000010, 1, "DistinctOpt" },
{ 0x00000020, 1, "CoverIdxScan" },
{ 0x00000040, 1, "OrderByIdxJoin" },
{ 0x00000080, 1, "Transitive" },
{ 0x00000100, 1, "OmitNoopJoin" },
{ 0x00000200, 1, "CountOfView" },
{ 0x00000400, 1, "CurosrHints" },
{ 0x00000800, 1, "Stat4" },
{ 0x00001000, 1, "PushDown" },
{ 0x00002000, 1, "SimplifyJoin" },
{ 0x00004000, 1, "SkipScan" },
{ 0x00008000, 1, "PropagateConst" },
{ 0x00010000, 1, "MinMaxOpt" },
{ 0x00020000, 1, "SeekScan" },
{ 0x00040000, 1, "OmitOrderBy" },
{ 0x00080000, 1, "BloomFilter" },
{ 0x00100000, 1, "BloomPulldown" },
{ 0x00200000, 1, "BalancedMerge" },
{ 0x00400000, 1, "ReleaseReg" },
{ 0x00800000, 1, "FlttnUnionAll" },
{ 0x01000000, 1, "IndexedEXpr" },
{ 0x02000000, 1, "Coroutines" },
{ 0x04000000, 1, "NullUnusedCols" },
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;
unsigned int newOpt;
int ii;
sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt);
newOpt = curOpt;
for(ii=2; ii<nArg; ii++){
const char *z = azArg[ii];
int useLabel = 0;
const char *zLabel = 0;
if( (z[0]=='+'|| z[0]=='-') && !IsDigit(z[1]) ){
useLabel = z[0];
zLabel = &z[1];
}else if( !IsDigit(z[0]) && z[0]!=0 && !IsDigit(z[1]) ){
useLabel = '+';
zLabel = z;
}else{
newOpt = (unsigned int)strtol(z,0,0);
}
if( useLabel ){
int jj;
for(jj=0; jj<ArraySize(aLabel); jj++){
if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break;
}
if( jj>=ArraySize(aLabel) ){
eputf("Error: no such optimization: \"%s\"\n", zLabel);
eputz("Should be one of:");
for(jj=0; jj<ArraySize(aLabel); jj++){
eputf(" %s", aLabel[jj].zLabel);
}
eputz("\n");
rc = 1;
goto meta_command_exit;
}
if( useLabel=='+' ){
newOpt &= ~aLabel[jj].mask;
}else{
newOpt |= aLabel[jj].mask;
}
}
}
if( curOpt!=newOpt ){
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt);
}else if( nArg<3 ){
curOpt = ~newOpt;
}
if( newOpt==0 ){
oputz("+All\n");
}else if( newOpt==0xffffffff ){
oputz("-All\n");
}else{
int jj;
for(jj=0; jj<ArraySize(aLabel); jj++){
unsigned int m = aLabel[jj].mask;
if( !aLabel[jj].bDsply ) continue;
if( (curOpt&m)!=(newOpt&m) ){
oputf("%c%s\n", (newOpt & m)==0 ? '+' : '-',
aLabel[jj].zLabel);
}
}
}
rc2 = isOk = 3;
break;
}
/* sqlite3_test_control(int, db, int) */
case SQLITE_TESTCTRL_FK_NO_ACTION:
if( nArg==3 ){
unsigned int opt = (unsigned int)strtol(azArg[2], 0, 0);
rc2 = sqlite3_test_control(testctrl, p->db, opt);
isOk = 3;
}
break;
|
| ︙ | ︙ | |||
27855 27856 27857 27858 27859 27860 27861 27862 27863 27864 27865 27866 27867 27868 |
isOk = 1;
}else{
rc2 = booleanValue(azArg[2]);
isOk = 3;
}
sqlite3_test_control(testctrl, &rc2);
break;
}
}
if( isOk==0 && iCtrl>=0 ){
oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
oputf("%d\n", rc2);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 30391 30392 30393 30394 30395 30396 30397 30398 30399 30400 30401 30402 30403 30404 30405 30406 30407 30408 30409 30410 30411 30412 30413 30414 30415 30416 30417 30418 30419 30420 30421 30422 30423 30424 30425 30426 30427 30428 30429 30430 30431 30432 30433 30434 30435 30436 30437 30438 30439 30440 30441 30442 30443 30444 30445 30446 30447 30448 30449 30450 30451 30452 30453 30454 30455 30456 30457 30458 30459 30460 30461 30462 30463 30464 30465 30466 30467 30468 30469 30470 30471 30472 30473 30474 |
isOk = 1;
}else{
rc2 = booleanValue(azArg[2]);
isOk = 3;
}
sqlite3_test_control(testctrl, &rc2);
break;
case SQLITE_TESTCTRL_FAULT_INSTALL: {
int kk;
int bShowHelp = nArg<=2;
isOk = 3;
for(kk=2; kk<nArg; kk++){
const char *z = azArg[kk];
if( z[0]=='-' && z[1]=='-' ) z++;
if( cli_strcmp(z,"off")==0 ){
sqlite3_test_control(testctrl, 0);
}else if( cli_strcmp(z,"on")==0 ){
faultsim_state.iCnt = faultsim_state.nSkip;
if( faultsim_state.iErr==0 ) faultsim_state.iErr = 1;
faultsim_state.nHit = 0;
sqlite3_test_control(testctrl, faultsim_callback);
}else if( cli_strcmp(z,"reset")==0 ){
faultsim_state.iCnt = faultsim_state.nSkip;
faultsim_state.nHit = 0;
sqlite3_test_control(testctrl, faultsim_callback);
}else if( cli_strcmp(z,"status")==0 ){
oputf("faultsim.iId: %d\n", faultsim_state.iId);
oputf("faultsim.iErr: %d\n", faultsim_state.iErr);
oputf("faultsim.iCnt: %d\n", faultsim_state.iCnt);
oputf("faultsim.nHit: %d\n", faultsim_state.nHit);
oputf("faultsim.iInterval: %d\n", faultsim_state.iInterval);
oputf("faultsim.eVerbose: %d\n", faultsim_state.eVerbose);
oputf("faultsim.nRepeat: %d\n", faultsim_state.nRepeat);
oputf("faultsim.nSkip: %d\n", faultsim_state.nSkip);
}else if( cli_strcmp(z,"-v")==0 ){
if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++;
}else if( cli_strcmp(z,"-q")==0 ){
if( faultsim_state.eVerbose>0 ) faultsim_state.eVerbose--;
}else if( cli_strcmp(z,"-id")==0 && kk+1<nArg ){
faultsim_state.iId = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-errcode")==0 && kk+1<nArg ){
faultsim_state.iErr = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-interval")==0 && kk+1<nArg ){
faultsim_state.iInterval = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-repeat")==0 && kk+1<nArg ){
faultsim_state.nRepeat = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-skip")==0 && kk+1<nArg ){
faultsim_state.nSkip = atoi(azArg[++kk]);
}else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){
bShowHelp = 1;
}else{
eputf("Unrecognized fault_install argument: \"%s\"\n",
azArg[kk]);
rc = 1;
bShowHelp = 1;
break;
}
}
if( bShowHelp ){
oputz(
"Usage: .testctrl fault_install ARGS\n"
"Possible arguments:\n"
" off Disable faultsim\n"
" on Activate faultsim\n"
" reset Reset the trigger counter\n"
" status Show current status\n"
" -v Increase verbosity\n"
" -q Decrease verbosity\n"
" --errcode N When triggered, return N as error code\n"
" --id ID Trigger only for the ID specified\n"
" --interval N Trigger only after every N-th call\n"
" --repeat N Turn off after N hits. 0 means never\n"
" --skip N Skip the first N encounters\n"
);
}
break;
}
}
}
if( isOk==0 && iCtrl>=0 ){
oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
oputf("%d\n", rc2);
|
| ︙ | ︙ | |||
28275 28276 28277 28278 28279 28280 28281 | ** 3. The database is empty, and ** 4. The shell is not running in --safe mode. ** ** The implementation uses the ShellState.eRestoreState to maintain state: ** ** 0: Have not seen any SQL. ** 1: Have seen "PRAGMA foreign_keys=OFF;". | | | | > | | < | | | > | > | | | | | | | | > | | | | | | < | > | | | | | > | > > > > > > | < | | | | | > | > > > > | | | | | < < < | 30881 30882 30883 30884 30885 30886 30887 30888 30889 30890 30891 30892 30893 30894 30895 30896 30897 30898 30899 30900 30901 30902 30903 30904 30905 30906 30907 30908 30909 30910 30911 30912 30913 30914 30915 30916 30917 30918 30919 30920 30921 30922 30923 30924 30925 30926 30927 30928 30929 30930 30931 30932 30933 30934 30935 30936 30937 30938 30939 30940 30941 30942 30943 30944 30945 30946 30947 30948 30949 30950 30951 30952 30953 30954 30955 30956 30957 30958 30959 30960 |
** 3. The database is empty, and
** 4. The shell is not running in --safe mode.
**
** The implementation uses the ShellState.eRestoreState to maintain state:
**
** 0: Have not seen any SQL.
** 1: Have seen "PRAGMA foreign_keys=OFF;".
** 2-6: Currently running .dump transaction. If the "2" bit is set,
** disable DEFENSIVE when done. If "4" is set, disable DQS_DDL.
** 7: Nothing left to do. This function becomes a no-op.
*/
static int doAutoDetectRestore(ShellState *p, const char *zSql){
int rc = SQLITE_OK;
if( p->eRestoreState<7 ){
switch( p->eRestoreState ){
case 0: {
const char *zExpect = "PRAGMA foreign_keys=OFF;";
assert( strlen(zExpect)==24 );
if( p->bSafeMode==0
&& strlen(zSql)>=24
&& memcmp(zSql, zExpect, 25)==0
){
p->eRestoreState = 1;
}else{
p->eRestoreState = 7;
}
break;
};
case 1: {
int bIsDump = 0;
const char *zExpect = "BEGIN TRANSACTION;";
assert( strlen(zExpect)==18 );
if( memcmp(zSql, zExpect, 19)==0 ){
/* Now check if the database is empty. */
const char *zQuery = "SELECT 1 FROM sqlite_schema LIMIT 1";
sqlite3_stmt *pStmt = 0;
bIsDump = 1;
shellPrepare(p->db, &rc, zQuery, &pStmt);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
bIsDump = 0;
}
shellFinalize(&rc, pStmt);
}
if( bIsDump && rc==SQLITE_OK ){
int bDefense = 0;
int bDqsDdl = 0;
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDefense);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, -1, &bDqsDdl);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 1, 0);
p->eRestoreState = (bDefense ? 2 : 0) + (bDqsDdl ? 4 : 0);
}else{
p->eRestoreState = 7;
}
break;
}
default: {
if( sqlite3_get_autocommit(p->db) ){
if( (p->eRestoreState & 2) ){
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0);
}
if( (p->eRestoreState & 4) ){
sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 0, 0);
}
p->eRestoreState = 7;
}
break;
}
}
}
return rc;
}
/*
** Run a single line of SQL. Return the number of errors.
|
| ︙ | ︙ | |||
28615 28616 28617 28618 28619 28620 28621 |
return home_dir;
}
/*
** On non-Windows platforms, look for $XDG_CONFIG_HOME.
** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
| | > > | > > | < | < < < < > | > | | > | 31232 31233 31234 31235 31236 31237 31238 31239 31240 31241 31242 31243 31244 31245 31246 31247 31248 31249 31250 31251 31252 31253 31254 31255 31256 31257 31258 31259 31260 31261 31262 31263 31264 31265 31266 31267 31268 |
return home_dir;
}
/*
** On non-Windows platforms, look for $XDG_CONFIG_HOME.
** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
** the path to it. If there is no $(XDG_CONFIG_HOME) then
** look for $(HOME)/.config/sqlite3/sqliterc and if found
** return that. If none of these are found, return 0.
**
** The string returned is obtained from sqlite3_malloc() and
** should be freed by the caller.
*/
static char *find_xdg_config(void){
#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
|| defined(__RTP__) || defined(_WRS_KERNEL)
return 0;
#else
char *zConfig = 0;
const char *zXdgHome;
zXdgHome = getenv("XDG_CONFIG_HOME");
if( zXdgHome==0 ){
const char *zHome = getenv("HOME");
if( zHome==0 ) return 0;
zConfig = sqlite3_mprintf("%s/.config/sqlite3/sqliterc", zHome);
}else{
zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
}
shell_check_oom(zConfig);
if( access(zConfig,0)!=0 ){
sqlite3_free(zConfig);
zConfig = 0;
}
return zConfig;
#endif
|
| ︙ | ︙ | |||
28663 28664 28665 28666 28667 28668 28669 |
char *home_dir = NULL;
const char *sqliterc = sqliterc_override;
char *zBuf = 0;
FILE *inSaved = p->in;
int savedLineno = p->lineno;
if( sqliterc == NULL ){
| | | 31282 31283 31284 31285 31286 31287 31288 31289 31290 31291 31292 31293 31294 31295 31296 |
char *home_dir = NULL;
const char *sqliterc = sqliterc_override;
char *zBuf = 0;
FILE *inSaved = p->in;
int savedLineno = p->lineno;
if( sqliterc == NULL ){
sqliterc = zBuf = find_xdg_config();
}
if( sqliterc == NULL ){
home_dir = find_home_dir(0);
if( home_dir==0 ){
eputz("-- warning: cannot find home directory;"
" cannot read ~/.sqliterc\n");
return;
|
| ︙ | ︙ | |||
28736 28737 28738 28739 28740 28741 28742 28743 28744 28745 28746 28747 28748 28749 | " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nofollow refuse to open symbolic links to database files\n" " -nonce STRING set the safe-mode escape nonce\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -pcachetrace trace all page cache operations\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" " -safe enable safe-mode\n" " -separator SEP set output column separator. Default: '|'\n" | > | 31355 31356 31357 31358 31359 31360 31361 31362 31363 31364 31365 31366 31367 31368 31369 | " -mmap N default mmap size set to N\n" #ifdef SQLITE_ENABLE_MULTIPLEX " -multiplex enable the multiplexor VFS\n" #endif " -newline SEP set output row separator. Default: '\\n'\n" " -nofollow refuse to open symbolic links to database files\n" " -nonce STRING set the safe-mode escape nonce\n" " -no-rowid-in-view Disable rowid-in-view using sqlite3_config()\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" " -pcachetrace trace all page cache operations\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" " -safe enable safe-mode\n" " -separator SEP set output column separator. Default: '|'\n" |
| ︙ | ︙ | |||
28768 28769 28770 28771 28772 28773 28774 |
"FILENAME is the name of an SQLite database. A new database is created\n"
"if the file does not previously exist. Defaults to :memory:.\n", Argv0);
if( showDetail ){
eputf("OPTIONS include:\n%s", zOptions);
}else{
eputz("Use the -help option for additional information\n");
}
| | | 31388 31389 31390 31391 31392 31393 31394 31395 31396 31397 31398 31399 31400 31401 31402 |
"FILENAME is the name of an SQLite database. A new database is created\n"
"if the file does not previously exist. Defaults to :memory:.\n", Argv0);
if( showDetail ){
eputf("OPTIONS include:\n%s", zOptions);
}else{
eputz("Use the -help option for additional information\n");
}
exit(0);
}
/*
** Internal check: Verify that the SQLite is uninitialized. Print a
** error message if it is initialized.
*/
static void verify_uninitialized(void){
|
| ︙ | ︙ | |||
29026 29027 29028 29029 29030 29031 29032 29033 29034 29035 29036 29037 29038 29039 |
/* Need to check for batch mode here to so we can avoid printing
** informational messages (like from process_sqliterc) before
** we do the actual processing of arguments later in a second pass.
*/
stdin_is_interactive = 0;
}else if( cli_strcmp(z,"-utf8")==0 ){
}else if( cli_strcmp(z,"-no-utf8")==0 ){
}else if( cli_strcmp(z,"-heap")==0 ){
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
const char *zSize;
sqlite3_int64 szHeap;
zSize = cmdline_option_value(argc, argv, ++i);
szHeap = integerValue(zSize);
| > > > > | 31646 31647 31648 31649 31650 31651 31652 31653 31654 31655 31656 31657 31658 31659 31660 31661 31662 31663 |
/* Need to check for batch mode here to so we can avoid printing
** informational messages (like from process_sqliterc) before
** we do the actual processing of arguments later in a second pass.
*/
stdin_is_interactive = 0;
}else if( cli_strcmp(z,"-utf8")==0 ){
}else if( cli_strcmp(z,"-no-utf8")==0 ){
}else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){
int val = 0;
sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW, &val);
assert( val==0 );
}else if( cli_strcmp(z,"-heap")==0 ){
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
const char *zSize;
sqlite3_int64 szHeap;
zSize = cmdline_option_value(argc, argv, ++i);
szHeap = integerValue(zSize);
|
| ︙ | ︙ | |||
29301 29302 29303 29304 29305 29306 29307 29308 29309 29310 29311 29312 29313 29314 |
stdin_is_interactive = 1;
}else if( cli_strcmp(z,"-batch")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-utf8")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-no-utf8")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-heap")==0 ){
i++;
}else if( cli_strcmp(z,"-pagecache")==0 ){
i+=2;
}else if( cli_strcmp(z,"-lookaside")==0 ){
i+=2;
}else if( cli_strcmp(z,"-threadsafe")==0 ){
| > > | 31925 31926 31927 31928 31929 31930 31931 31932 31933 31934 31935 31936 31937 31938 31939 31940 |
stdin_is_interactive = 1;
}else if( cli_strcmp(z,"-batch")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-utf8")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-no-utf8")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-heap")==0 ){
i++;
}else if( cli_strcmp(z,"-pagecache")==0 ){
i+=2;
}else if( cli_strcmp(z,"-lookaside")==0 ){
i+=2;
}else if( cli_strcmp(z,"-threadsafe")==0 ){
|
| ︙ | ︙ | |||
29347 29348 29349 29350 29351 29352 29353 |
if( z[0]=='.' ){
rc = do_meta_command(z, &data);
if( rc && bail_on_error ) return rc==2 ? 0 : rc;
}else{
open_db(&data, 0);
rc = shell_exec(&data, z, &zErrMsg);
if( zErrMsg!=0 ){
| | | 31973 31974 31975 31976 31977 31978 31979 31980 31981 31982 31983 31984 31985 31986 31987 |
if( z[0]=='.' ){
rc = do_meta_command(z, &data);
if( rc && bail_on_error ) return rc==2 ? 0 : rc;
}else{
open_db(&data, 0);
rc = shell_exec(&data, z, &zErrMsg);
if( zErrMsg!=0 ){
shellEmitError(zErrMsg);
if( bail_on_error ) return rc!=0 ? rc : 1;
}else if( rc!=0 ){
eputf("Error: unable to process SQL \"%s\"\n", z);
if( bail_on_error ) return rc;
}
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
|
| ︙ | ︙ | |||
29392 29393 29394 29395 29396 29397 29398 |
** command-line inputs, except for the argToSkip argument which contains
** the database filename.
*/
for(i=0; i<nCmd; i++){
if( azCmd[i][0]=='.' ){
rc = do_meta_command(azCmd[i], &data);
if( rc ){
| < | > | < | > | 32018 32019 32020 32021 32022 32023 32024 32025 32026 32027 32028 32029 32030 32031 32032 32033 32034 32035 32036 32037 32038 32039 32040 32041 32042 32043 32044 32045 32046 32047 |
** command-line inputs, except for the argToSkip argument which contains
** the database filename.
*/
for(i=0; i<nCmd; i++){
if( azCmd[i][0]=='.' ){
rc = do_meta_command(azCmd[i], &data);
if( rc ){
if( rc==2 ) rc = 0;
goto shell_main_exit;
}
}else{
open_db(&data, 0);
echo_group_input(&data, azCmd[i]);
rc = shell_exec(&data, azCmd[i], &zErrMsg);
if( zErrMsg || rc ){
if( zErrMsg!=0 ){
shellEmitError(zErrMsg);
}else{
eputf("Error: unable to process SQL: %s\n", azCmd[i]);
}
sqlite3_free(zErrMsg);
if( rc==0 ) rc = 1;
goto shell_main_exit;
}
}
}
}else{
/* Run commands received from standard input
*/
if( stdin_is_interactive ){
|
| ︙ | ︙ | |||
29445 29446 29447 29448 29449 29450 29451 |
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
}
}
if( zHistory ){ shell_read_history(zHistory); }
#if HAVE_READLINE || HAVE_EDITLINE
rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE
| | > > > > > > | 32071 32072 32073 32074 32075 32076 32077 32078 32079 32080 32081 32082 32083 32084 32085 32086 32087 32088 32089 32090 32091 32092 32093 32094 32095 32096 32097 32098 32099 32100 32101 32102 32103 32104 32105 32106 32107 |
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
}
}
if( zHistory ){ shell_read_history(zHistory); }
#if HAVE_READLINE || HAVE_EDITLINE
rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE
linenoiseSetCompletionCallback(linenoise_completion, NULL);
#endif
data.in = 0;
rc = process_input(&data);
if( zHistory ){
shell_stifle_history(2000);
shell_write_history(zHistory);
free(zHistory);
}
}else{
data.in = stdin;
rc = process_input(&data);
}
}
#ifndef SQLITE_SHELL_FIDDLE
/* In WASM mode we have to leave the db state in place so that
** client code can "push" SQL into it after this call returns. */
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( data.expert.pExpert ){
expertFinish(&data, 1, 0);
}
#endif
shell_main_exit:
free(azCmd);
set_table_name(&data, 0);
if( data.db ){
session_close_all(&data, -1);
close_db(data.db);
}
for(i=0; i<ArraySize(data.aAuxDb); i++){
|
| ︙ | ︙ | |||
29494 29495 29496 29497 29498 29499 29500 |
memset(&data, 0, sizeof(data));
#ifdef SQLITE_DEBUG
if( sqlite3_memory_used()>mem_main_enter ){
eputf("Memory leaked: %u bytes\n",
(unsigned int)(sqlite3_memory_used()-mem_main_enter));
}
#endif
| | > > | 32126 32127 32128 32129 32130 32131 32132 32133 32134 32135 32136 32137 32138 32139 32140 32141 32142 |
memset(&data, 0, sizeof(data));
#ifdef SQLITE_DEBUG
if( sqlite3_memory_used()>mem_main_enter ){
eputf("Memory leaked: %u bytes\n",
(unsigned int)(sqlite3_memory_used()-mem_main_enter));
}
#endif
#else /* SQLITE_SHELL_FIDDLE... */
shell_main_exit:
#endif
return rc;
}
#ifdef SQLITE_SHELL_FIDDLE
/* Only for emcc experimentation purposes. */
int fiddle_experiment(int a,int b){
|
| ︙ | ︙ | |||
29528 29529 29530 29531 29532 29533 29534 |
SQLITE_FCNTL_VFS_POINTER, &pVfs);
}
return pVfs;
}
/* Only for emcc experimentation purposes. */
sqlite3 * fiddle_db_arg(sqlite3 *arg){
| | | 32162 32163 32164 32165 32166 32167 32168 32169 32170 32171 32172 32173 32174 32175 32176 |
SQLITE_FCNTL_VFS_POINTER, &pVfs);
}
return pVfs;
}
/* Only for emcc experimentation purposes. */
sqlite3 * fiddle_db_arg(sqlite3 *arg){
oputf("fiddle_db_arg(%p)\n", (const void*)arg);
return arg;
}
/*
** Intended to be called via a SharedWorker() while a separate
** SharedWorker() (which manages the wasm module) is performing work
** which should be interrupted. Unfortunately, SharedWorker is not
|
| ︙ | ︙ | |||
29554 29555 29556 29557 29558 29559 29560 |
return globalDb
? sqlite3_db_filename(globalDb, zDbName ? zDbName : "main")
: NULL;
}
/*
** Completely wipes out the contents of the currently-opened database
| | > > > > > > > > > > | | | 32188 32189 32190 32191 32192 32193 32194 32195 32196 32197 32198 32199 32200 32201 32202 32203 32204 32205 32206 32207 32208 32209 32210 32211 32212 32213 32214 32215 32216 32217 |
return globalDb
? sqlite3_db_filename(globalDb, zDbName ? zDbName : "main")
: NULL;
}
/*
** Completely wipes out the contents of the currently-opened database
** but leaves its storage intact for reuse. If any transactions are
** active, they are forcibly rolled back.
*/
void fiddle_reset_db(void){
if( globalDb ){
int rc;
while( sqlite3_txn_state(globalDb,0)>0 ){
/*
** Resolve problem reported in
** https://sqlite.org/forum/forumpost/0b41a25d65
*/
oputz("Rolling back in-progress transaction.\n");
sqlite3_exec(globalDb,"ROLLBACK", 0, 0, 0);
}
rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
if( 0==rc ) sqlite3_exec(globalDb, "VACUUM", 0, 0, 0);
sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
}
}
/*
** Uses the current database's VFS xRead to stream the db file's
** contents out to the given callback. The callback gets a single
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.c.
more than 10,000 changes
Changes to extsrc/sqlite3.h.
| ︙ | ︙ | |||
142 143 144 145 146 147 148 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.47.0" #define SQLITE_VERSION_NUMBER 3047000 #define SQLITE_SOURCE_ID "2024-09-02 21:59:31 7891a266c4425722ae8b9231397ef9e42e2432be9e6b70632dfaf9ff15300d2c" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
416 417 418 419 420 421 422 423 424 425 426 427 428 429 | ** <ul> ** <li> The application must ensure that the 1st parameter to sqlite3_exec() ** is a valid and open [database connection]. ** <li> The application must not close the [database connection] specified by ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. ** <li> The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ** </ul> */ SQLITE_API int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ void *, /* 1st argument to callback */ | > > | 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | ** <ul> ** <li> The application must ensure that the 1st parameter to sqlite3_exec() ** is a valid and open [database connection]. ** <li> The application must not close the [database connection] specified by ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. ** <li> The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. ** <li> The application must not dereference the arrays or string pointers ** passed as the 3rd and 4th callback parameters after it returns. ** </ul> */ SQLITE_API int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ void *, /* 1st argument to callback */ |
| ︙ | ︙ | |||
758 759 760 761 762 763 764 | ** <li> [SQLITE_LOCK_SHARED], ** <li> [SQLITE_LOCK_RESERVED], ** <li> [SQLITE_LOCK_PENDING], or ** <li> [SQLITE_LOCK_EXCLUSIVE]. ** </ul> ** xLock() upgrades the database file lock. In other words, xLock() moves the ** database file lock in the direction NONE toward EXCLUSIVE. The argument to | | | | | | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | ** <li> [SQLITE_LOCK_SHARED], ** <li> [SQLITE_LOCK_RESERVED], ** <li> [SQLITE_LOCK_PENDING], or ** <li> [SQLITE_LOCK_EXCLUSIVE]. ** </ul> ** xLock() upgrades the database file lock. In other words, xLock() moves the ** database file lock in the direction NONE toward EXCLUSIVE. The argument to ** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never ** SQLITE_LOCK_NONE. If the database file lock is already at or above the ** requested lock, then the call to xLock() is a no-op. ** xUnlock() downgrades the database file lock to either SHARED or NONE. ** If the lock is already at or below the requested lock state, then the call ** to xUnlock() is a no-op. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, ** PENDING, or EXCLUSIVE lock on the file. It returns, via its output ** pointer parameter, true if such a lock exists and false otherwise. ** ** The xFileControl() method is a generic interface that allows custom ** VFS implementations to directly control an open file using the ** [sqlite3_file_control()] interface. The second "op" argument is an ** integer opcode. The third argument is a generic pointer intended to ** point to a structure that may contain arguments or space in which to ** write return values. Potential uses for xFileControl() might be |
| ︙ | ︙ | |||
2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 | ** [sqlite3_int64] parameter which is the default maximum size for an in-memory ** database created using [sqlite3_deserialize()]. This default maximum ** size can be adjusted up or down for individual databases using the ** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ | > > > > > > > > > > > > > > > > | 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 | ** [sqlite3_int64] parameter which is the default maximum size for an in-memory ** database created using [sqlite3_deserialize()]. This default maximum ** size can be adjusted up or down for individual databases using the ** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. ** ** [[SQLITE_CONFIG_ROWID_IN_VIEW]] ** <dt>SQLITE_CONFIG_ROWID_IN_VIEW ** <dd>The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability ** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is ** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability ** defaults to on. This configuration option queries the current setting or ** changes the setting to off or on. The argument is a pointer to an integer. ** If that integer initially holds a value of 1, then the ability for VIEWs to ** have ROWIDs is activated. If the integer initially holds zero, then the ** ability is deactivated. Any other initial value for the integer leaves the ** setting unchanged. After changes, if any, the integer is written with ** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite ** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and ** recommended case) then the integer is always filled with zero, regardless ** if its initial value. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ |
| ︙ | ︙ | |||
2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** | > | 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ #define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** |
| ︙ | ︙ | |||
3282 3283 3284 3285 3286 3287 3288 | #define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ #define SQLITE_RECURSIVE 33 /* NULL NULL */ /* | | | | 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 | #define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ #define SQLITE_RECURSIVE 33 /* NULL NULL */ /* ** CAPI3REF: Deprecated Tracing And Profiling Functions ** DEPRECATED ** ** These routines are deprecated. Use the [sqlite3_trace_v2()] interface ** instead of the routines described here. ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. ** |
| ︙ | ︙ | |||
3547 3548 3549 3550 3551 3552 3553 | ** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt> ** <dd>The database is opened [shared cache] disabled, overriding ** the default shared cache setting provided by ** [sqlite3_enable_shared_cache()].)^ ** ** [[OPEN_EXRESCODE]] ^(<dt>[SQLITE_OPEN_EXRESCODE]</dt> ** <dd>The database connection comes up in "extended result code mode". | | | | 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 | ** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt> ** <dd>The database is opened [shared cache] disabled, overriding ** the default shared cache setting provided by ** [sqlite3_enable_shared_cache()].)^ ** ** [[OPEN_EXRESCODE]] ^(<dt>[SQLITE_OPEN_EXRESCODE]</dt> ** <dd>The database connection comes up in "extended result code mode". ** In other words, the database behaves as if ** [sqlite3_extended_result_codes(db,1)] were called on the database ** connection as soon as the connection is created. In addition to setting ** the extended result code mode, this flag also causes [sqlite3_open_v2()] ** to return an extended result code.</dd> ** ** [[OPEN_NOFOLLOW]] ^(<dt>[SQLITE_OPEN_NOFOLLOW]</dt> ** <dd>The database filename is not allowed to contain a symbolic link</dd> ** </dl>)^ |
| ︙ | ︙ | |||
5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 | ** Every function that invokes [sqlite3_result_subtype()] should have this ** property. If it does not, then the call to [sqlite3_result_subtype()] ** might become a no-op if the function is used as term in an ** [expression index]. On the other hand, SQL functions that never invoke ** [sqlite3_result_subtype()] should avoid setting this property, as the ** purpose of this property is to disable certain optimizations that are ** incompatible with subtypes. ** </dd> ** </dl> */ #define SQLITE_DETERMINISTIC 0x000000800 #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 #define SQLITE_RESULT_SUBTYPE 0x001000000 /* ** CAPI3REF: Deprecated Functions ** DEPRECATED ** ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue | > > > > > > > > > > | 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 | ** Every function that invokes [sqlite3_result_subtype()] should have this ** property. If it does not, then the call to [sqlite3_result_subtype()] ** might become a no-op if the function is used as term in an ** [expression index]. On the other hand, SQL functions that never invoke ** [sqlite3_result_subtype()] should avoid setting this property, as the ** purpose of this property is to disable certain optimizations that are ** incompatible with subtypes. ** ** [[SQLITE_SELFORDER1]] <dt>SQLITE_SELFORDER1</dt><dd> ** The SQLITE_SELFORDER1 flag indicates that the function is an aggregate ** that internally orders the values provided to the first argument. The ** ordered-set aggregate SQL notation with a single ORDER BY term can be ** used to invoke this function. If the ordered-set aggregate notation is ** used on a function that lacks this flag, then an error is raised. Note ** that the ordered-set aggregate syntax is only available if SQLite is ** built using the -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES compile-time option. ** </dd> ** </dl> */ #define SQLITE_DETERMINISTIC 0x000000800 #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 #define SQLITE_RESULT_SUBTYPE 0x001000000 #define SQLITE_SELFORDER1 0x002000000 /* ** CAPI3REF: Deprecated Functions ** DEPRECATED ** ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue |
| ︙ | ︙ | |||
5797 5798 5799 5800 5801 5802 5803 | ** ** The sqlite3_value_subtype(V) function returns the subtype for ** an [application-defined SQL function] argument V. The subtype ** information can be used to pass a limited amount of context from ** one SQL function to another. Use the [sqlite3_result_subtype()] ** routine to set the subtype for the return value of an SQL function. ** | | | 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 | ** ** The sqlite3_value_subtype(V) function returns the subtype for ** an [application-defined SQL function] argument V. The subtype ** information can be used to pass a limited amount of context from ** one SQL function to another. Use the [sqlite3_result_subtype()] ** routine to set the subtype for the return value of an SQL function. ** ** Every [application-defined SQL function] that invokes this interface ** should include the [SQLITE_SUBTYPE] property in the text ** encoding argument when the function is [sqlite3_create_function|registered]. ** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() ** might return zero instead of the upstream subtype in some corner cases. */ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); |
| ︙ | ︙ | |||
6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 | ** ** ^In the current implementation, the update hook ** is not invoked when conflicting rows are deleted because of an ** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook ** invoked when rows are deleted using the [truncate optimization]. ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions ** to modify the database connection must be deferred until after the ** completion of the [sqlite3_step()] call that triggered the update hook. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. | > > > > > > | 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 | ** ** ^In the current implementation, the update hook ** is not invoked when conflicting rows are deleted because of an ** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook ** invoked when rows are deleted using the [truncate optimization]. ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** ** Whether the update hook is invoked before or after the ** corresponding change is currently unspecified and may differ ** depending on the type of change. Do not rely on the order of the ** hook call with regards to the final result of the operation which ** triggers the hook. ** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions ** to modify the database connection must be deferred until after the ** completion of the [sqlite3_step()] call that triggered the update hook. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. |
| ︙ | ︙ | |||
7398 7399 7400 7401 7402 7403 7404 | ** indicates that the expense of the operation is similar to that of a ** binary search on a unique indexed field of an SQLite table with N rows. ** ** ^The estimatedRows value is an estimate of the number of rows that ** will be returned by the strategy. ** ** The xBestIndex method may optionally populate the idxFlags field with a | | > > | | | 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 | ** indicates that the expense of the operation is similar to that of a ** binary search on a unique indexed field of an SQLite table with N rows. ** ** ^The estimatedRows value is an estimate of the number of rows that ** will be returned by the strategy. ** ** The xBestIndex method may optionally populate the idxFlags field with a ** mask of SQLITE_INDEX_SCAN_* flags. One such flag is ** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN] ** output to show the idxNum has hex instead of as decimal. Another flag is ** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will ** return at most one row. ** ** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then ** SQLite also assumes that if a call to the xUpdate() method is made as ** part of the same statement to delete or update a virtual table row and the ** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback ** any database changes. In other words, if the xUpdate() returns ** SQLITE_CONSTRAINT, the database contents must be exactly as they were |
| ︙ | ︙ | |||
7464 7465 7466 7467 7468 7469 7470 | /* ** CAPI3REF: Virtual Table Scan Flags ** ** Virtual table implementations are allowed to set the ** [sqlite3_index_info].idxFlags field to some combination of ** these bits. */ | | > > | 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 |
/*
** CAPI3REF: Virtual Table Scan Flags
**
** Virtual table implementations are allowed to set the
** [sqlite3_index_info].idxFlags field to some combination of
** these bits.
*/
#define SQLITE_INDEX_SCAN_UNIQUE 0x00000001 /* Scan visits at most 1 row */
#define SQLITE_INDEX_SCAN_HEX 0x00000002 /* Display idxNum as hex */
/* in EXPLAIN QUERY PLAN */
/*
** CAPI3REF: Virtual Table Constraint Operator Codes
**
** These macros define the allowed values for the
** [sqlite3_index_info].aConstraint[].op field. Each value represents
** an operator that is part of a constraint term in the WHERE clause of
|
| ︙ | ︙ | |||
8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 | > | 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */ #define SQLITE_TESTCTRL_JSON_SELFCHECK 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_GETOPT 16 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 |
| ︙ | ︙ | |||
8334 8335 8336 8337 8338 8339 8340 | ** recognized by SQLite. Applications can uses these routines to determine ** whether or not a specific identifier needs to be escaped (for example, ** by enclosing in double-quotes) so as not to confuse the parser. ** ** The sqlite3_keyword_count() interface returns the number of distinct ** keywords understood by SQLite. ** | | | 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 | ** recognized by SQLite. Applications can uses these routines to determine ** whether or not a specific identifier needs to be escaped (for example, ** by enclosing in double-quotes) so as not to confuse the parser. ** ** The sqlite3_keyword_count() interface returns the number of distinct ** keywords understood by SQLite. ** ** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and ** makes *Z point to that keyword expressed as UTF8 and writes the number ** of bytes in the keyword into *L. The string that *Z points to is not ** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns ** SQLITE_OK if N is within bounds and SQLITE_ERROR if not. If either Z ** or L are NULL or invalid pointers then calls to ** sqlite3_keyword_name(N,Z,L) result in undefined behavior. ** |
| ︙ | ︙ | |||
9913 9914 9915 9916 9917 9918 9919 | ** that the query planner does not need the rows to be returned in sorted order ** as long as all rows with the same values in all columns identified by the ** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ** is doing a GROUP BY. ** <li value="2"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular | | | | < | | < > | | < | > > > > | > > | > > > > > > > > > > > > > > > > > | 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 9985 9986 9987 9988 9989 9990 9991 9992 9993 9994 9995 9996 9997 9998 9999 10000 10001 10002 10003 10004 | ** that the query planner does not need the rows to be returned in sorted order ** as long as all rows with the same values in all columns identified by the ** "aOrderBy" field are adjacent.)^ This mode is used when the query planner ** is doing a GROUP BY. ** <li value="2"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 2, that means ** that the query planner does not need the rows returned in any particular ** order, as long as rows with the same values in all columns identified ** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows ** contain the same values for all columns identified by "colUsed", all but ** one such row may optionally be omitted from the result.)^ ** The virtual table is not required to omit rows that are duplicates ** over the "colUsed" columns, but if the virtual table can do that without ** too much extra effort, it could potentially help the query to run faster. ** This mode is used for a DISTINCT query. ** <li value="3"><p> ** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the ** virtual table must return rows in the order defined by "aOrderBy" as ** if the sqlite3_vtab_distinct() interface had returned 0. However if ** two or more rows in the result have the same values for all columns ** identified by "colUsed", then all but one such row may optionally be ** omitted.)^ Like when the return value is 2, the virtual table ** is not required to omit rows that are duplicates over the "colUsed" ** columns, but if the virtual table can do that without ** too much extra effort, it could potentially help the query to run faster. ** This mode is used for queries ** that have both DISTINCT and ORDER BY clauses. ** </ol> ** ** <p>The following table summarizes the conditions under which the ** virtual table is allowed to set the "orderByConsumed" flag based on ** the value returned by sqlite3_vtab_distinct(). This table is a ** restatement of the previous four paragraphs: ** ** <table border=1 cellspacing=0 cellpadding=10 width="90%"> ** <tr> ** <td valign="top">sqlite3_vtab_distinct() return value ** <td valign="top">Rows are returned in aOrderBy order ** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent ** <td valign="top">Duplicates over all colUsed columns may be omitted ** <tr><td>0<td>yes<td>yes<td>no ** <tr><td>1<td>no<td>yes<td>no ** <tr><td>2<td>no<td>yes<td>yes ** <tr><td>3<td>yes<td>yes<td>yes ** </table> ** ** ^For the purposes of comparing virtual table output values to see if the ** values are same value for sorting purposes, two NULL values are considered ** to be the same. In other words, the comparison operator is "IS" ** (or "IS NOT DISTINCT FROM") and not "==". ** ** If a virtual table implementation is unable to meet the requirements |
| ︙ | ︙ | |||
10782 10783 10784 10785 10786 10787 10788 | #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #if defined(__wasi__) # undef SQLITE_WASI # define SQLITE_WASI 1 | < < | 10843 10844 10845 10846 10847 10848 10849 10850 10851 10852 10853 10854 10855 10856 | #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #if defined(__wasi__) # undef SQLITE_WASI # define SQLITE_WASI 1 # ifndef SQLITE_OMIT_LOAD_EXTENSION # define SQLITE_OMIT_LOAD_EXTENSION # endif # ifndef SQLITE_THREADSAFE # define SQLITE_THREADSAFE 0 # endif #endif |
| ︙ | ︙ | |||
11974 11975 11976 11977 11978 11979 11980 11981 11982 11983 11984 11985 11986 11987 | ** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ** occurs during processing, this function returns SQLITE_NOMEM. ** ** In all cases, if an error occurs the state of the final contents of the ** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup | > > > > > > > > > > > > > > > > > > > > > > > > | 12033 12034 12035 12036 12037 12038 12039 12040 12041 12042 12043 12044 12045 12046 12047 12048 12049 12050 12051 12052 12053 12054 12055 12056 12057 12058 12059 12060 12061 12062 12063 12064 12065 12066 12067 12068 12069 12070 | ** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition ** occurs during processing, this function returns SQLITE_NOMEM. ** ** In all cases, if an error occurs the state of the final contents of the ** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); /* ** CAPI3REF: Add A Single Change To A Changegroup ** METHOD: sqlite3_changegroup ** ** This function adds the single change currently indicated by the iterator ** passed as the second argument to the changegroup object. The rules for ** adding the change are just as described for [sqlite3changegroup_add()]. ** ** If the change is successfully added to the changegroup, SQLITE_OK is ** returned. Otherwise, an SQLite error code is returned. ** ** The iterator must point to a valid entry when this function is called. ** If it does not, SQLITE_ERROR is returned and no change is added to the ** changegroup. Additionally, the iterator must not have been opened with ** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also ** returned. */ SQLITE_API int sqlite3changegroup_add_change( sqlite3_changegroup*, sqlite3_changeset_iter* ); /* ** CAPI3REF: Obtain A Composite Changeset From A Changegroup ** METHOD: sqlite3_changegroup ** ** Obtain a buffer containing a changeset (or patchset) representing the ** current contents of the changegroup. If the inputs to the changegroup |
| ︙ | ︙ | |||
12779 12780 12781 12782 12783 12784 12785 | const unsigned char *b; }; /* ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): | | | | 12862 12863 12864 12865 12866 12867 12868 12869 12870 12871 12872 12873 12874 12875 12876 12877 | const unsigned char *b; }; /* ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): ** Return a copy of the pUserData pointer passed to the xCreateFunction() ** API when the extension function was registered. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken ** to the total number of tokens in the FTS5 table. Or, if iCol is ** non-negative but less than the number of columns in the table, return ** the total number of tokens in column iCol, considering all rows in ** the FTS5 table. |
| ︙ | ︙ | |||
12962 12963 12964 12965 12966 12967 12968 12969 12970 12971 12972 12973 12974 12975 | ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. If the FTS5 table is created ** with either "detail=none" or "detail=column" and "content=" option ** (i.e. if it is a contentless table), then this API always iterates ** through an empty set (all calls to xPhraseFirst() set iCol to -1). ** ** xPhraseNext() ** See xPhraseFirst above. ** ** xPhraseFirstColumn() ** This function and xPhraseNextColumn() are similar to the xPhraseFirst() ** and xPhraseNext() APIs described above. The difference is that instead ** of iterating through all instances of a phrase in the current row, these | > > > > | 13045 13046 13047 13048 13049 13050 13051 13052 13053 13054 13055 13056 13057 13058 13059 13060 13061 13062 | ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. If the FTS5 table is created ** with either "detail=none" or "detail=column" and "content=" option ** (i.e. if it is a contentless table), then this API always iterates ** through an empty set (all calls to xPhraseFirst() set iCol to -1). ** ** In all cases, matches are visited in (column ASC, offset ASC) order. ** i.e. all those in column 0, sorted by offset, followed by those in ** column 1, etc. ** ** xPhraseNext() ** See xPhraseFirst above. ** ** xPhraseFirstColumn() ** This function and xPhraseNextColumn() are similar to the xPhraseFirst() ** and xPhraseNext() APIs described above. The difference is that instead ** of iterating through all instances of a phrase in the current row, these |
| ︙ | ︙ | |||
13028 13029 13030 13031 13032 13033 13034 13035 13036 |
**
** The output text is not a copy of the document text that was tokenized.
** It is the output of the tokenizer module. For tokendata=1 tables, this
** includes any embedded 0x00 and trailing data.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
| > > > > > > > > > > > > > > > > > > > > > > > | | 13115 13116 13117 13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 13128 13129 13130 13131 13132 13133 13134 13135 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 13153 13154 |
**
** The output text is not a copy of the document text that was tokenized.
** It is the output of the tokenizer module. For tokendata=1 tables, this
** includes any embedded 0x00 and trailing data.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
**
** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale)
** If parameter iCol is less than zero, or greater than or equal to the
** number of columns in the table, SQLITE_RANGE is returned.
**
** Otherwise, this function attempts to retrieve the locale associated
** with column iCol of the current row. Usually, there is no associated
** locale, and output parameters (*pzLocale) and (*pnLocale) are set
** to NULL and 0, respectively. However, if the fts5_locale() function
** was used to associate a locale with the value when it was inserted
** into the fts5 table, then (*pzLocale) is set to point to a nul-terminated
** buffer containing the name of the locale in utf-8 encoding. (*pnLocale)
** is set to the size in bytes of the buffer, not including the
** nul-terminator.
**
** If successful, SQLITE_OK is returned. Or, if an error occurs, an
** SQLite error code is returned. The final value of the output parameters
** is undefined in this case.
**
** xTokenize_v2:
** Tokenize text using the tokenizer belonging to the FTS5 table. This
** API is the same as the xTokenize() API, except that it allows a tokenizer
** locale to be specified.
*/
struct Fts5ExtensionApi {
int iVersion; /* Currently always set to 4 */
void *(*xUserData)(Fts5Context*);
int (*xColumnCount)(Fts5Context*);
int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
|
| ︙ | ︙ | |||
13072 13073 13074 13075 13076 13077 13078 13079 13080 13081 13082 13083 13084 13085 13086 13087 13088 13089 13090 13091 13092 13093 13094 13095 13096 13097 13098 |
/* Below this point are iVersion>=3 only */
int (*xQueryToken)(Fts5Context*,
int iPhrase, int iToken,
const char **ppToken, int *pnToken
);
int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/
/*************************************************************************
** CUSTOM TOKENIZERS
**
** Applications may also register custom tokenizer types. A tokenizer
** is registered by providing fts5 with a populated instance of the
** following structure. All structure methods must be defined, setting
** any member of the fts5_tokenizer struct to NULL leads to undefined
** behaviour. The structure methods are expected to function as follows:
**
** xCreate:
** This function is used to allocate and initialize a tokenizer instance.
** A tokenizer instance is required to actually tokenize text.
**
** The first argument passed to this function is a copy of the (void*)
| > > > > > > > > > > | | 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 13192 13193 13194 13195 13196 13197 13198 13199 13200 13201 13202 13203 13204 13205 13206 13207 13208 13209 13210 13211 13212 13213 13214 13215 13216 13217 13218 13219 13220 13221 13222 13223 13224 13225 13226 |
/* Below this point are iVersion>=3 only */
int (*xQueryToken)(Fts5Context*,
int iPhrase, int iToken,
const char **ppToken, int *pnToken
);
int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
/* Below this point are iVersion>=4 only */
int (*xColumnLocale)(Fts5Context*, int iCol, const char **pz, int *pn);
int (*xTokenize_v2)(Fts5Context*,
const char *pText, int nText, /* Text to tokenize */
const char *pLocale, int nLocale, /* Locale to pass to tokenizer */
void *pCtx, /* Context passed to xToken() */
int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
);
};
/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/
/*************************************************************************
** CUSTOM TOKENIZERS
**
** Applications may also register custom tokenizer types. A tokenizer
** is registered by providing fts5 with a populated instance of the
** following structure. All structure methods must be defined, setting
**
** any member of the fts5_tokenizer struct to NULL leads to undefined
** behaviour. The structure methods are expected to function as follows:
**
** xCreate:
** This function is used to allocate and initialize a tokenizer instance.
** A tokenizer instance is required to actually tokenize text.
**
** The first argument passed to this function is a copy of the (void*)
** pointer provided by the application when the fts5_tokenizer_v2 object
** was registered with FTS5 (the third argument to xCreateTokenizer()).
** The second and third arguments are an array of nul-terminated strings
** containing the tokenizer arguments, if any, specified following the
** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
** to create the FTS5 table.
**
** The final argument is an output variable. If successful, (*ppOut)
|
| ︙ | ︙ | |||
13116 13117 13118 13119 13120 13121 13122 | ** ** xTokenize: ** This function is expected to tokenize the nText byte string indicated ** by argument pText. pText may or may not be nul-terminated. The first ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** | | | 13236 13237 13238 13239 13240 13241 13242 13243 13244 13245 13246 13247 13248 13249 13250 | ** ** xTokenize: ** This function is expected to tokenize the nText byte string indicated ** by argument pText. pText may or may not be nul-terminated. The first ** argument passed to this function is a pointer to an Fts5Tokenizer object ** returned by an earlier call to xCreate(). ** ** The third argument indicates the reason that FTS5 is requesting ** tokenization of the supplied text. This is always one of the following ** four values: ** ** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into ** or removed from the FTS table. The tokenizer is being invoked to ** determine the set of tokens to add to (or delete from) the ** FTS index. |
| ︙ | ︙ | |||
13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 13151 13152 | ** returned by the tokenizer will be treated as a token prefix. ** ** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to ** satisfy an fts5_api.xTokenize() request made by an auxiliary ** function. Or an fts5_api.xColumnSize() request made by the same ** on a columnsize=0 database. ** </ul> ** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth ** arguments are a pointer to a buffer containing the token text, and the ** size of the token in bytes. The 4th and 5th arguments are the byte offsets ** of the first byte of and first byte immediately following the text from | > > > > > > > | 13259 13260 13261 13262 13263 13264 13265 13266 13267 13268 13269 13270 13271 13272 13273 13274 13275 13276 13277 13278 13279 | ** returned by the tokenizer will be treated as a token prefix. ** ** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to ** satisfy an fts5_api.xTokenize() request made by an auxiliary ** function. Or an fts5_api.xColumnSize() request made by the same ** on a columnsize=0 database. ** </ul> ** ** The sixth and seventh arguments passed to xTokenize() - pLocale and ** nLocale - are a pointer to a buffer containing the locale to use for ** tokenization (e.g. "en_US") and its size in bytes, respectively. The ** pLocale buffer is not nul-terminated. pLocale may be passed NULL (in ** which case nLocale is always 0) to indicate that the tokenizer should ** use its default locale. ** ** For each token in the input string, the supplied callback xToken() must ** be invoked. The first argument to it should be a copy of the pointer ** passed as the second argument to xTokenize(). The third and fourth ** arguments are a pointer to a buffer containing the token text, and the ** size of the token in bytes. The 4th and 5th arguments are the byte offsets ** of the first byte of and first byte immediately following the text from |
| ︙ | ︙ | |||
13162 13163 13164 13165 13166 13167 13168 13169 13170 13171 13172 13173 13174 13175 | ** If an xToken() callback returns any value other than SQLITE_OK, then ** the tokenization should be abandoned and the xTokenize() method should ** immediately return a copy of the xToken() return value. Or, if the ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, ** if an error occurs with the xTokenize() implementation itself, it ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a ** user wishes to query for a phrase such as "first place". Using the ** built-in tokenizers, the FTS5 query 'first + place' will match instances ** of "first place" within the document set, but not alternative forms | > > > > > > > > > > > > > > > > > > > > > > > > | 13289 13290 13291 13292 13293 13294 13295 13296 13297 13298 13299 13300 13301 13302 13303 13304 13305 13306 13307 13308 13309 13310 13311 13312 13313 13314 13315 13316 13317 13318 13319 13320 13321 13322 13323 13324 13325 13326 | ** If an xToken() callback returns any value other than SQLITE_OK, then ** the tokenization should be abandoned and the xTokenize() method should ** immediately return a copy of the xToken() return value. Or, if the ** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally, ** if an error occurs with the xTokenize() implementation itself, it ** may abandon the tokenization and return any error code other than ** SQLITE_OK or SQLITE_DONE. ** ** If the tokenizer is registered using an fts5_tokenizer_v2 object, ** then the xTokenize() method has two additional arguments - pLocale ** and nLocale. These specify the locale that the tokenizer should use ** for the current request. If pLocale and nLocale are both 0, then the ** tokenizer should use its default locale. Otherwise, pLocale points to ** an nLocale byte buffer containing the name of the locale to use as utf-8 ** text. pLocale is not nul-terminated. ** ** FTS5_TOKENIZER ** ** There is also an fts5_tokenizer object. This is an older, deprecated, ** version of fts5_tokenizer_v2. It is similar except that: ** ** <ul> ** <li> There is no "iVersion" field, and ** <li> The xTokenize() method does not take a locale argument. ** </ul> ** ** Legacy fts5_tokenizer tokenizers must be registered using the ** legacy xCreateTokenizer() function, instead of xCreateTokenizer_v2(). ** ** Tokenizer implementations registered using either API may be retrieved ** using both xFindTokenizer() and xFindTokenizer_v2(). ** ** SYNONYM SUPPORT ** ** Custom tokenizers may also support synonyms. Consider a case in which a ** user wishes to query for a phrase such as "first place". Using the ** built-in tokenizers, the FTS5 query 'first + place' will match instances ** of "first place" within the document set, but not alternative forms |
| ︙ | ︙ | |||
13271 13272 13273 13274 13275 13276 13277 13278 13279 13280 13281 13282 13283 13284 13285 13286 13287 13288 13289 13290 13291 13292 13293 13294 13295 13296 13297 13298 13299 13300 13301 13302 13303 13304 13305 13306 13307 13308 13309 13310 13311 13312 13313 13314 13315 |
**
** When using methods (2) or (3), it is important that the tokenizer only
** provide synonyms when tokenizing document text (method (3)) or query
** text (method (2)), not both. Doing so will not cause any errors, but is
** inefficient.
*/
typedef struct Fts5Tokenizer Fts5Tokenizer;
typedef struct fts5_tokenizer fts5_tokenizer;
struct fts5_tokenizer {
int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
void (*xDelete)(Fts5Tokenizer*);
int (*xTokenize)(Fts5Tokenizer*,
void *pCtx,
int flags, /* Mask of FTS5_TOKENIZE_* flags */
const char *pText, int nText,
int (*xToken)(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
const char *pToken, /* Pointer to buffer containing token */
int nToken, /* Size of token in bytes */
int iStart, /* Byte offset of token within input text */
int iEnd /* Byte offset of end of token within input text */
)
);
};
/* Flags that may be passed as the third argument to xTokenize() */
#define FTS5_TOKENIZE_QUERY 0x0001
#define FTS5_TOKENIZE_PREFIX 0x0002
#define FTS5_TOKENIZE_DOCUMENT 0x0004
#define FTS5_TOKENIZE_AUX 0x0008
/* Flags that may be passed by the tokenizer implementation back to FTS5
** as the third argument to the supplied xToken callback. */
#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */
/*
** END OF CUSTOM TOKENIZERS
*************************************************************************/
/*************************************************************************
** FTS5 EXTENSION REGISTRATION API
*/
typedef struct fts5_api fts5_api;
struct fts5_api {
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 13422 13423 13424 13425 13426 13427 13428 13429 13430 13431 13432 13433 13434 13435 13436 13437 13438 13439 13440 13441 13442 13443 13444 13445 13446 13447 13448 13449 13450 13451 13452 13453 13454 13455 13456 13457 13458 13459 13460 13461 13462 13463 13464 13465 13466 13467 13468 13469 13470 13471 13472 13473 13474 13475 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 |
**
** When using methods (2) or (3), it is important that the tokenizer only
** provide synonyms when tokenizing document text (method (3)) or query
** text (method (2)), not both. Doing so will not cause any errors, but is
** inefficient.
*/
typedef struct Fts5Tokenizer Fts5Tokenizer;
typedef struct fts5_tokenizer_v2 fts5_tokenizer_v2;
struct fts5_tokenizer_v2 {
int iVersion; /* Currently always 2 */
int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
void (*xDelete)(Fts5Tokenizer*);
int (*xTokenize)(Fts5Tokenizer*,
void *pCtx,
int flags, /* Mask of FTS5_TOKENIZE_* flags */
const char *pText, int nText,
const char *pLocale, int nLocale,
int (*xToken)(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
const char *pToken, /* Pointer to buffer containing token */
int nToken, /* Size of token in bytes */
int iStart, /* Byte offset of token within input text */
int iEnd /* Byte offset of end of token within input text */
)
);
};
/*
** New code should use the fts5_tokenizer_v2 type to define tokenizer
** implementations. The following type is included for legacy applications
** that still use it.
*/
typedef struct fts5_tokenizer fts5_tokenizer;
struct fts5_tokenizer {
int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
void (*xDelete)(Fts5Tokenizer*);
int (*xTokenize)(Fts5Tokenizer*,
void *pCtx,
int flags, /* Mask of FTS5_TOKENIZE_* flags */
const char *pText, int nText,
int (*xToken)(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
const char *pToken, /* Pointer to buffer containing token */
int nToken, /* Size of token in bytes */
int iStart, /* Byte offset of token within input text */
int iEnd /* Byte offset of end of token within input text */
)
);
};
/* Flags that may be passed as the third argument to xTokenize() */
#define FTS5_TOKENIZE_QUERY 0x0001
#define FTS5_TOKENIZE_PREFIX 0x0002
#define FTS5_TOKENIZE_DOCUMENT 0x0004
#define FTS5_TOKENIZE_AUX 0x0008
/* Flags that may be passed by the tokenizer implementation back to FTS5
** as the third argument to the supplied xToken callback. */
#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */
/*
** END OF CUSTOM TOKENIZERS
*************************************************************************/
/*************************************************************************
** FTS5 EXTENSION REGISTRATION API
*/
typedef struct fts5_api fts5_api;
struct fts5_api {
int iVersion; /* Currently always set to 3 */
/* Create a new tokenizer */
int (*xCreateTokenizer)(
fts5_api *pApi,
const char *zName,
void *pUserData,
fts5_tokenizer *pTokenizer,
|
| ︙ | ︙ | |||
13336 13337 13338 13339 13340 13341 13342 13343 13344 13345 13346 13347 13348 13349 13350 13351 13352 13353 13354 13355 |
int (*xCreateFunction)(
fts5_api *pApi,
const char *zName,
void *pUserData,
fts5_extension_function xFunction,
void (*xDestroy)(void*)
);
};
/*
** END OF REGISTRATION API
*************************************************************************/
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
#endif
#endif /* _FTS5_H */
/******** End of fts5.h *********/
| > > > > > > > > > > > > > > > > > > > | 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 13530 13531 13532 13533 13534 13535 13536 13537 13538 13539 13540 13541 13542 13543 13544 13545 13546 13547 13548 13549 13550 13551 13552 13553 |
int (*xCreateFunction)(
fts5_api *pApi,
const char *zName,
void *pUserData,
fts5_extension_function xFunction,
void (*xDestroy)(void*)
);
/* APIs below this point are only available if iVersion>=3 */
/* Create a new tokenizer */
int (*xCreateTokenizer_v2)(
fts5_api *pApi,
const char *zName,
void *pUserData,
fts5_tokenizer_v2 *pTokenizer,
void (*xDestroy)(void*)
);
/* Find an existing tokenizer */
int (*xFindTokenizer_v2)(
fts5_api *pApi,
const char *zName,
void **ppUserData,
fts5_tokenizer_v2 **ppTokenizer
);
};
/*
** END OF REGISTRATION API
*************************************************************************/
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
#endif
#endif /* _FTS5_H */
/******** End of fts5.h *********/
|
Changes to skins/ardoise/css.txt.
| ︙ | ︙ | |||
614 615 616 617 618 619 620 |
ol,
p,
pre,
table,
ul {
margin-bottom: 1.5rem
}
| | | | | | | | | | | | 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 |
ol,
p,
pre,
table,
ul {
margin-bottom: 1.5rem
}
header {
color: #888;
font-weight: 400;
padding-top: 10px;
border-width: 0
}
.filetree li > ul:before,
.filetree li li:before {
border-left: 2px solid #888;
content: '';
position: absolute
}
.filetree>ul,
header .logo,
header .logo h1 {
display: inline-block
}
header .login {
padding-top: 2px;
text-align: right
}
header .login .button {
margin: 0
}
header h1 {
margin: 0;
color: #888;
display: inline-block
}
header .title h1 {
padding-bottom: 10px
}
header .login,
header h1 small,
header h2 small {
color: #777
}
.middle {
background-color: #1d2021;
padding-bottom: 20px;
max-width: 100%;
box-sizing: border-box
|
| ︙ | ︙ | |||
682 683 684 685 686 687 688 |
}
.artifact_content blockquote:first-of-type {
padding: 1px 20px;
margin: 0 0 20px;
background: #000;
border-radius: 5px
}
| | | | | 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 |
}
.artifact_content blockquote:first-of-type {
padding: 1px 20px;
margin: 0 0 20px;
background: #000;
border-radius: 5px
}
footer {
padding: 10px 0 60px;
border-top: 0;
color: #888
}
footer a {
color: #527b8f;
background-repeat: no-repeat;
background-position: center top 10px
}
footer a:hover {
color: #eef8ff
}
.mainmenu {
background-color: #161819;
border-top-right-radius: 15px;
border-top-left-radius: 15px;
clear: both
|
| ︙ | ︙ | |||
731 732 733 734 735 736 737 |
.mainmenu li:hover {
background-color: #ff8000;
border-radius: 5px
}
.mainmenu li:hover a {
color: #000
}
| | | 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 |
.mainmenu li:hover {
background-color: #ff8000;
border-radius: 5px
}
.mainmenu li:hover a {
color: #000
}
nav#hbdrop {
background-color: #161819;
border-radius: 15px;
display: none;
width: 100%;
position: absolute;
z-index: 20;
}
|
| ︙ | ︙ |
Changes to skins/ardoise/footer.txt.
|
| | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<th1>
if {[string first artifact $current_page] == 0 || [string first hexdump $current_page] == 0} {
html "</div>"
}
</th1>
</div> <!-- end div container -->
</div> <!-- end div middle max-full-width -->
<footer>
<div class="container">
<div class="pull-right">
<a href="https://fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
</div>
This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
</div>
</footer>
|
Changes to skins/ardoise/header.txt.
|
| | | 1 2 3 4 5 6 7 8 |
<header>
<div class="container">
<div class="login pull-right">
<th1>
if {[info exists login]} {
html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
} else {
html "<a class='button' href='$home/login'>Login</a>\n"
|
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
html "<a class='rss' href='$home/timeline.rss'></a>"
}
</th1>
<small> $<title></small></h1>
</div>
<!-- Main Menu -->
| | | | | | | | | | | | | | | | | > | | | | | | | | | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
html "<a class='rss' href='$home/timeline.rss'></a>"
}
</th1>
<small> $<title></small></h1>
</div>
<!-- Main Menu -->
<nav class="mainmenu" title="Main Menu">
<ul>
<th1>
html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n"
builtin_request_js hbmenu.js
set once 1
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {$once && [string match $url\[/?#\]* /$current_page/]} {
set class "$class active"
set once 0
}
html "<li class='$class'>"
if {[string match /* $url]} {set url $home$url}
html "<a href='$url'>$name</a></li>\n"
}
</th1>
</ul>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>
</div> <!-- end div container -->
</header>
<div class="middle max-full-width">
<div class="container">
<th1>
if {[string first artifact $current_page] == 0 || [string first hexdump $current_page] == 0} {
html "<div class=\"artifact_content\">"
}
</th1>
|
Changes to skins/black_and_white/css.txt.
| ︙ | ︙ | |||
47 48 49 50 51 52 53 | color: #333; font-size: 0.8em; font-weight: bold; white-space: nowrap; } /* The header across the top of the page */ | | | | | | | 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 87 88 89 90 91 92 93 94 |
color: #333;
font-size: 0.8em;
font-weight: bold;
white-space: nowrap;
}
/* The header across the top of the page */
header {
margin:10px 0px 10px 0px;
padding:1px 0px 0px 20px;
border-style:solid;
border-color:black;
border-width:1px 0px;
background-color:#eee;
}
/* The main menu bar that appears at the top left of the page beneath
** the header. Width must be co-ordinated with the container below */
nav.mainmenu {
float: left;
margin-left: 10px;
margin-right: 20px;
font-size: 0.9em;
font-weight: bold;
padding:5px;
background-color:#eee;
border:1px solid #999;
width:6em;
}
/* Main menu is now a list */
nav.mainmenu ul {
padding: 0;
list-style:none;
}
nav.mainmenu a, nav.mainmenu a:visited{
padding: 1px 10px 1px 10px;
color: #333;
text-decoration: none;
}
nav.mainmenu a:hover {
color: #eee;
background-color: #333;
}
/* Container for the sub-menu and content so they don't spread
** out underneath the main menu */
#container {
|
| ︙ | ︙ | |||
147 148 149 150 151 152 153 | float: left; clear: left; color: #333; white-space: nowrap; } /* The footer at the very bottom of the page */ | | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
float: left;
clear: left;
color: #333;
white-space: nowrap;
}
/* The footer at the very bottom of the page */
footer {
font-size: 0.8em;
margin-top: 12px;
padding: 5px 10px 5px 10px;
text-align: right;
background-color: #eee;
color: #555;
}
|
| ︙ | ︙ |
Changes to skins/black_and_white/footer.txt.
|
| | | | 1 2 3 | <footer> Fossil $release_version $manifest_version $manifest_date </footer> |
Changes to skins/black_and_white/header.txt.
|
| | | | | | | | | | | | | | | | | | | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<header>
<div class="logo">
<img src="$logo_image_url" alt="logo">
<br />$<project_name>
</div>
<div class="title">$<title></div>
<div class="status"><th1>
if {[info exists login]} {
puts "Logged in as $login"
} else {
puts "Not logged in"
}
</th1></div>
</header>
<nav class="mainmenu" title="Main Menu">
<th1>
set sitemap 0
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {set url $home$url}
html "<a href='$url'>$name</a><br/>\n"
if {[string match /sitemap $url]} {set sitemap 1}
}
if {!$sitemap} {
html "<a href='$home/sitemap'>Sitemap</a>\n"
}
</th1>
</nav>
|
Changes to skins/blitz/css.txt.
| ︙ | ︙ | |||
753 754 755 756 757 758 759 | box-sizing: border-box; } /* Header * Div displayed at the top of every page. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ | | | | | | | | | | | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 |
box-sizing: border-box;
}
/* Header
* Div displayed at the top of every page.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
header {
color: #666;
font-weight: 400;
padding-top: 10px;
border-width: 0px;
border-top: 4px solid #446979;
border-bottom: 1px solid #ccc;
}
header .logo {
display: inline-block;
}
header .login {
padding-top: 2px;
text-align: right;
}
header .login .button {
margin: 0;
}
header h1 {
margin: 0px;
color: #666;
display: inline-block;
}
header .logo h1 {
display: inline-block;
}
header .title h1 {
padding-bottom: 10px;
}
header h1 small, header h2 small {
color: #888;
}
header a.rss {
display: inline-block;
padding: 10px 15px;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wMNDhwn05VjawAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAGlSURBVDjLrdPfb8xREAXwT7tIl+paVNaPJghCKC8kXv0XXvyNXsRfwYPQJqVKiqykWFVZXd12vcxNJtduUtJJvrm7984998ycMxxwNGI9jPs4j7nY+/U/gIdiPYO71dk21rCE7r8ybOHGmMfmcRNnsbEf1gXwNzqYSXs5WljEMXzAaBLg1Ji9Js7hOi6OeeAznqC/X8AcMyHWYpX7E4/Rm1QyHMdefCWGeI/VcMDR2D8S7Fci5y/AeTzCPVyLi1sYJAut4BTaiX0n9kc14MmkcjPY3I5LXezGtxqKtyJ3Lir6VAM2AmCq6m8Hl6PsQTB5hyvxmMhZxk4G3MZLfAwLtdNZM9rwOs528TVVNB3ga7UoQ2wGmyWciFaU0VwIJiP8iL6Xfp7GK+w0JthliDep8UKonTSGvbBTaU8f3QzYxgPcCsBvWK9E6OBFCNGPVjTTqC430p+H6fLVGLGtmIw7SbwevqT+XkgVPJ9Otpmtyl6I9XswLXEp/d6oPN0ugJu14xMLob4kgPRYjtkCOMDTUG+AZ3ibEtfDLorfEmAB3UuTdXDxBzUUZV+B82aLAAAAAElFTkSuQmCC);
background-position: center center;
background-repeat: no-repeat;
}
|
| ︙ | ︙ | |||
828 829 830 831 832 833 834 | color: #002060; } /* Footer * Displayed after the middle div and forms the page bottom. ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ | | | | 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 |
color: #002060;
}
/* Footer
* Displayed after the middle div and forms the page bottom.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
footer {
padding: 10px 0 60px;
border-top: 1px solid #ccc;
background-color: #f8f8f8;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABGCAQAAADxl9ugAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQffAwkQBRPw+yfrAAAAPHRFWHRDb21tZW50ACBJbWFnZSBnZW5lcmF0ZWQgYnkgRVNQIEdob3N0c2NyaXB0IChkZXZpY2U9cG5tcmF3KQqV01S1AAANg0lEQVRo3s2aa3RVRZbH/3tXnftIbhJCeJMXyEsNCsgrSBzAYXyt0YVj2zSOonbLqNNO+25FB2kdoBVdrp6l09P2NNpOL/ExSrtUdGRU5JEQgggCggIhQIBAIIGQ5N57TlXt+ZAICY+AGCT76zlV91dVe+/a+38uDQ7dYmrxw41QocJuni28vWTedEa7Gd9iatplos7Y2mfDRED6A2G0I2At6AdPIiA84HgbLhx1o9QX9Zoh7QjYHpOkYJYrfNyOKH0O/f2FJpfkSRmMCNqDk9rDXwTAuxK7h3uFf7vXxC6QVO8w19taNOT4Y0wu1sCdW0AA6IZ73bCUaI/GHuqnsJqcEqVrzSo+wNtLdgAPsz6jeVURtQ9gA8bQT8xztW7X2u3I4G6UdA0cohxcSAN7nt939/zDQzl0LnfwaLpxqFT/i7w7XLYXhoUQ+YFW3pJlC0/nt6jZZb4zjXY2AaGXvQOz/gMY09NM4C6ixRL7OU3PTm4ORdiIjyLsJvq1Z28Hj5rBM82xMTqPuhHznmU72vq12W5M3+LykQWUW9pirzXOmmk8wjE87kb8HPmSKlY+wY62R9jhI69xf/AmYiEfiXs+e4CCAZiDUc9wP6cRIJn8tq23PQClbwap9Ivk7usiGj/CDs5xl3Qe/EiQCEWsgXLlq8sfamM73osMna43ef/pfm731Hsxv+Z00ozFzaJpF+gMrsNB2BwL3cGpSsNRWHbmvTQNiTbev9q4xXszMLzqpdgDNjxv/YjmvHlSQIc5brl8nrqT5pllMpHM9wRc4gU3oC8JyDLt8P78ZLyM2lrmAeTg5YrKdf1S1N9QTs6At1YM49DJo1gwWy7uE5kKBQHbler9K8R+L8API2qmFoDY7ejxpxGJxtMctyZcfYvJDYX4i5LXp/MJAS2edkM6edNUDuLNOdMDmz913bIw8RidKO/1w+3NQfePbHQjdbd/NKOjbq7UqQOmYtXb/+BdbGc4AJjPX53CXe6UGweYWxUR8N8rNkzn4wAFc9yYPvGbI+mttoycpo14fWbD0hZv+njOAcBl6Yl8jiEMbSKhqGPypZ5N0B2kDxrjOjF0QuI2oRqj2z6rAebx5pNiRrEo0jhV9wFkP//xpkPHAU6UGdnBLyR8jMOQs6xwcMAznVys+Uqa5YCCnilFkuVSkeKlGKfDED8JaI+VifMWR9LPC/u+UkoZA6edOezVu83pny4yD6qQnCxhDzk/MhUED+8PWHJMkAgWROvvQoxOcEdayynVY9/5tIgAwsfIvaD3XaFx6M6ddMx3WlurCGICiAIhMLxB1VM+wQRMDOeYjHCUMum85OW909/acOlJgqaI3qrulU9dYGlgzVI1tsVrETzlOt3MeUGCudVoCgJmAhFU78F+6Usu0V39iylSHCRZMROM0do5pUDWQamEPZTy5oqyXVt77bG94HGEyTkCE8BsjLOcl11Uvf7V+qvIP86jgWH8zhc9LpKYU7qRhH5NTRknwFw3+jpMQCIItG69PCLfZ1YKAMlS/7PQDCc2qbRzSjET4slINBAvhsPxjarKKy/9FhjZG8Ol4tINi/uFzkcfl8sEnwExATGRY73Ze2VpAHxHoPFki7r2iljN3a6LqqOxnZ+vXUBNwTF6uL0e0Nr3tSYWpqNhQtaIaA0A1oZftuf5Y1xmCMRO2IM1VXqf3aYr5IDXODTx7+43PAjPFcit1MANeHfFxuFpSFW9TbbORpaNEqt6/5tIoxuBOO049NHXNTN5lFwlE3o09JUMlWKdSmJ/UC8TvRwqjNzoVwNIw3upcluyq4hSAq3Idyt5HOyRIDGuGdA42un+6wv/Cq7sFA1F/B4N/5MEgL/yOsSPlFSMOzD5JhkMEQ8l8u41Job7hVoExsiRNMU1Biac4oqD92Np8Xt0BgUiEOeIHLyobbQb9U/8fSAItsFdJv1wECASB5L92xb2yefcE3Q+jgsof//m3+PO2i7YhgV0HisAK48UnE03EaT67axMyjMNXpFkf/za8r3VPP2I46Ri+aYDW0z3SMT6NJb62wPiJEFhEoFCYIhNoyS4VF1CBKArPu6MKWysBZhFVFrD7yri2VkuR4QZAIkTMAOAiDIY9KvFQ3gVPqXVFD9JSbSS3vP7b/QzKY8SlO6G5e2fv/dozvDxciKvCqOYrIURT/XU2+0SKrdVqtpVojz4Sq1Vi1duV0UE+PSEy33AhRRZAzDDc4vXrB1xKU0gRUR0LCCRi/be8XZ12/0M41Ka5K/5JjA80PlQKMjfeVVN9ZG0NZbmH8xf5Y8nJxIKmYC7SmLlexeVp2wKb+y++f6KZ3c/03g+qSISPONGDqHRZOk7QGde6XsrjTP+kXTTCpCVGErZteZUDRdhD3Ls65tz64J85ZGyQ0tXDAzckadFlOtv+ZQLoYMgHHaGcrJ7fb56inR3WVJJRVTdVM3kQSh5r1hjlbIGIKVX6ammE1vnmImh4Y4BZHGcvmvx5XQ6BcRIfnPHxV82ZnFUYsh+bWXLzMuwquGwCnvdBUTkqHcPmb+l5cJZcKervlwc8N04sbYwSJC1tsnneemJqh32LksZflr1SUh+pT6q+/ov/krztitDq1s4hP1p7mbfuFfkEKJgSejLh/Wa7Y5GJsdQmEYDoVocDCvnLCvnwPDMzOK/uiVoPS8IxoQi409D2xAU4Hd2xE2DZ3gjvcpVZY+0iqgAbFh7F9grp84xf5D6QFOE/rYo1vto4z6ZVg/COHKAiGJrACaljNHaiaqTp0NeXlr37Ydz/ahSLY9YzLIPMzlxSrxMzM/ochfnO8UHvJJpgTnm+UE5GKMeOu3LrLJPd32+Z1FamfczKli36jzTtHplvPg46o2WgNwEaOqoTk8Jxsu4+vGUruDkCCCJkg171g2ntqMkjrmuYUBwG7LglIq/U7Zr1HEjJrtiay9imIzsqt37gb4/RSZlSsP88iZf5T3kX2xOuBEck2yp13F32NbDuZbHqSjwFrzJbeP5eN6NHOJuoQyXRIZ7Y+3Gh08wohjpW/VOkFMyqagHILnOSAOuflh1bqKggew524Zs1ex+R6cmz1W7Z6clvzzF8T7rLsvnSfBgJVO9cN+ax4/IRwazZLZrqmOSWJjAR0hCdFc/F2AtToTos34POQHAkVw2Sp2m7EKwOEyLVj59w6EtpyjdFYSCCYhaC0n9fcmmr5pb8QBrQl0wqn/hNc82J8Rfc8lWVwVGwLeNDVPTdW30+KZN0Sab5JTBqOBBowIHzeZVJcBjXHdKXfQp93+DgwuIgy9QtqJ8evPhOtRRzZi30iKfBZPxQVNhofDP6sUXh05zA5MH+WKdpAgAJ/lAChqhyQmd5HgFChFoMvar0AaKq8qSGmA6o03lVFCEqx1Q0Ce0075hkgvW93EPNeMZ3Cl93eRl5Q8GVjoBs1tUN1l/rvkn1VOuNXUIAyATv6zz3bXPEo3t7N/L6bBirfV0MimktOclgnDUJHiTWxo9oJOZQT/TDUk0nIYfFODFaMMN4WFqbnEl8BzXtlBX++ATr2KW3eC9kbg/9d2l6/qmdxrq9bdRkBwMtrgd+lpkpxwyWSwAkvRKSfl0phf4l+6acGVeak6QQbCJYH/Gwci+zPi/+oMFEJpLdTDfQ8IpTTl8r/lkQuniX3oR98mjX65sJQZ0wYNu1NU0wZ8RjSUf0Qo22aCU1oAoCazTFe5bdZ1pVIqcv2B12XSm6awxCFNaichCr1I1auDOQFvKxzQHFD4mexGXNfds/PoYB+iMhxzwIC8vcJe7HKVIrBEoLRQkuVL+Ii70lDlMUOR/uHrJdNaAwXo82orksR8gfVUAGFkgy5JfhKdEKncelwhq8SinwMeidX+303VHuvOgCGSRRF2o6vb6FyKmQTGRNTraQt1qJ6EaAPA4V22MYH20wXx+aCyfKFvFAcymiYfCh/YhSQZACFHpBB/l6On2l0s/AgTBWdIHHbpIulzfWDbvAW4r3gPUIwWZ0lW6SoaE0AgDYG6Six0R4OjgWRMwGUAVHuUz+SS2iNQ+XSMAvKqzrLCemdtUo3ifWhpoTi7b/RSfVRH9zBc2y43umXVgtImf3R08c7zCIeYpVoPOtoh+ZjYEhd3kWtV4IHSldEjAV7U/ATHxig89Tx0QUFCtvUJisxnNknuHArSY4+xtNuk4ZQWai4wOBThJRqZJvhYXtptnNTVAHQnQYbT4k7QI6/Vd6w6f/U9h39cUgEg+BCG3rk/AHQ9wlhsz1IUCI3tRPrDVtdlhzO9rxPNke+nuXR0TUIdAMOrDlkJshwEM4QId98C8vKTmUUJHAwww03VNT8ny9hd/cJ9qCaU7QnrpjXvcsKuT6W7VdcvHc+sq95wDCuZILxp9t+TZJWVL/56PPdJzDhjDhG75UyUrsXXtBw9z/PjAObd3729leA++mTOldO07dyl9ghbmnALmYpjybndhO3/VV/dx9IQdlj53YQEAo2din3p1cv1VfDIF8EfvSdJQSZvU3ljGJJOvNuH94kOPs22jwWq3P5edTryG8G/OdtsxOPiZd6O1ppTeK0n43Hb/9yPuYBhPuFFXumEq3231S+ibL/e+yhtP2Zz+iD74hCu83vblr+W1yJ5LgguxmzedRu/8//AUSaqMTR1xAAAAAElFTkSuQmCC);
background-repeat: no-repeat;
background-position: center top 10px;
}
footer a {
color: #3b5c6b;
}
/* Main Menu
* Displayed in header, contains repository links.
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */
|
| ︙ | ︙ | |||
869 870 871 872 873 874 875 |
.mainmenu li.active {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB90FDxEXAZ2XRzAAAABJSURBVCjPY2CgBzhz5sx/QmoYiTXAxMSEkWRDsLkAl0GMpHoBm0EoAlu3bmUQFxcnGAboBjEhc4gxAJtLGUmJBVwuYiTXAGSDAIx5IBObnuVxAAAAAElFTkSuQmCC);
background-repeat: no-repeat;
background-position: center bottom;
}
.mainmenu li a,
| | | | | 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 |
.mainmenu li.active {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEQAACxEBf2RfkQAAAAd0SU1FB90FDxEXAZ2XRzAAAABJSURBVCjPY2CgBzhz5sx/QmoYiTXAxMSEkWRDsLkAl0GMpHoBm0EoAlu3bmUQFxcnGAboBjEhc4gxAJtLGUmJBVwuYiTXAGSDAIx5IBObnuVxAAAAAElFTkSuQmCC);
background-repeat: no-repeat;
background-position: center bottom;
}
.mainmenu li a,
nav#hbdrop a {
color: #3b5c6b;
padding: 10px 15px;
}
.mainmenu li.active a {
font-weight: bold;
}
.mainmenu li:hover
nav#hbdrop a:hover {
background-color: #eee;
}
nav#hbdrop {
background-color: white;
border: 2px solid #ccc;
display: none;
width: 100%;
position: absolute;
z-index: 20;
}
|
| ︙ | ︙ |
Changes to skins/blitz/footer.txt.
1 2 | </div> <!-- end div container --> </div> <!-- end div middle max-full-width --> | | | | 1 2 3 4 5 6 7 8 9 10 |
</div> <!-- end div container -->
</div> <!-- end div middle max-full-width -->
<footer>
<div class="container">
<div class="pull-right">
<a href="https://www.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
</div>
This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
</div>
</footer>
|
Changes to skins/blitz/header.txt.
|
| | | 1 2 3 4 5 6 7 8 |
<header>
<div class="container">
<!-- Header -->
<div class="login pull-right">
<th1>
if {[info exists login]} {
html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
|
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
html "<a class='rss' href='$home/timeline.rss'></a>"
}
</th1>
<small> $<title></small></h1>
</div>
<!-- Main Menu -->
| | > | | | | | | | | | | | | | | | | > | | | | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
html "<a class='rss' href='$home/timeline.rss'></a>"
}
</th1>
<small> $<title></small></h1>
</div>
<!-- Main Menu -->
<nav class="mainmenu" title="Main Menu">
<ul>
<th1>
html "<li><a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a></li>\n"
builtin_request_js hbmenu.js
set once 1
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {$once && [string match $url\[/?#\]* /$current_page/]} {
set class "active $class"
set once 0
}
html "<li class='$class'>"
if {[string match /* $url]} {set url $home$url}
html "<a href='$url'>$name</a></li>\n"
}
</th1>
</ul>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>
</div> <!-- end div container -->
</header>
<div class="middle max-full-width">
<div class="container">
|
Changes to skins/darkmode/css.txt.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 |
** the area to show as blank. The purpose is to cause the
** title to be exactly centered. */
div.leftoftitle {
visibility: hidden;
}
/* The header across the top of the page */
| | | | | | | | | 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 87 88 89 90 91 92 93 94 95 96 97 |
** the area to show as blank. The purpose is to cause the
** title to be exactly centered. */
div.leftoftitle {
visibility: hidden;
}
/* The header across the top of the page */
header {
display: table;
width: 100%;
}
/* The main menu bar that appears at the top of the page beneath
** the header */
nav.mainmenu {
padding: 0.25em 0.5em;
font-size: 0.9em;
font-weight: bold;
text-align: center;
border-top-left-radius: 0.5em;
border-top-right-radius: 0.5em;
border-bottom: 1px dotted rgba(200,200,200,0.3);
z-index: 21; /* just above hbdrop */
}
nav#hbdrop {
background-color: #1f1f1f;
border: 2px solid #303536;
border-radius: 0 0 0.5em 0.5em;
display: none;
left: 2em;
width: calc(100% - 4em);
position: absolute;
z-index: 20; /* just below mainmenu, but above timeline bubbles */
}
nav.mainmenu, div.submenu, div.sectionmenu {
color: #ffffffcc;
background-color: #303536/*#0000ff60*/;
}
/* The submenu bar that *sometimes* appears below the main menu */
div.submenu, div.sectionmenu {
padding: 0.15em 0.5em 0.15em 0;
font-size: 0.9em;
text-align: center;
border-bottom-left-radius: 0.5em;
border-bottom-right-radius: 0.5em;
}
a, a:visited {
color: rgba(127, 201, 255, 0.9);
display: inline;
text-decoration: none;
}
a:visited {opacity: 0.8}
nav.mainmenu a, div.submenu a,
div.sectionmenu>a.button, div.submenu label,
footer a {
padding: 0.15em 0.5em;
}
nav.mainmenu a.active {
border-bottom: 1px solid #FF4500f0;
}
a:hover,
a:visited:hover {
background-color: #FF4500f0;
color: rgba(24,24,24,0.8);
border-radius: 0.1em;
|
| ︙ | ︙ | |||
170 171 172 173 174 175 176 | margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ | | | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
margin: .2em 0 .2em 0;
float: left;
clear: left;
white-space: nowrap;
}
/* The footer at the very bottom of the page */
footer {
clear: both;
font-size: 0.8em;
padding: 0.15em 0.5em;
text-align: right;
background-color: #303536/*#0000ff60*/;
border-top: 1px dotted rgba(200,200,200,0.3);
border-bottom-left-radius: 0.5em;
|
| ︙ | ︙ | |||
597 598 599 600 601 602 603 |
body.tkt td.tktDspValue { color: black }
body.tkt td.tktDspValue a { color: blue }
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #442800;
}
| > > > > | 597 598 599 600 601 602 603 604 605 606 607 |
body.tkt td.tktDspValue { color: black }
body.tkt td.tktDspValue a { color: blue }
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #442800;
}
p.noMoreShun {
color: #e5e500;
}
|
Changes to skins/darkmode/footer.txt.
|
| | | | 1 2 3 4 5 6 7 8 |
<footer>
<div class="container">
<div class="pull-right">
<a href="https://www.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
</div>
This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
</div>
</footer>
|
Changes to skins/darkmode/header.txt.
|
| | | | | | | | | | | | | | | | | | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<header>
<div class="status leftoftitle"><th1>
if {[info exists login]} {
set logintext "<a href='$home/login'>$login</a>\n"
} else {
set logintext "<a href='$home/login'>Login</a>\n"
}
html $logintext
</th1></div>
<div class="title">$<title></div>
<div class="status"><nobr><th1>
html $logintext
</th1></nobr></div>
</header>
<nav class="mainmenu" title="Main Menu">
<th1>
html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>"
builtin_request_js hbmenu.js
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {
if {[string match $url\[/?#\]* /$current_page/]} {
set class "active $class"
}
set url $home$url
}
html "<a href='$url' class='$class'>$name</a>\n"
}
</th1>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>
|
Changes to skins/default/README.md.
|
| | > | > | > > > | | 1 2 3 4 5 6 7 8 9 10 | This skin was originally contributed by Étienne Deparis on 2015-02-22, promoted to the default on 2015-03-14, and subsequently changed by many: https://fossil-scm.org/home/finfo/skins/default/css.txt https://fossil-scm.org/home/blame?filename=skins/default/css.txt&checkin=trunk In February 2024, a sufficiently large set of changes were made to the skin that we forked the old version for the benefit of those who needed to reference the old one — as when migrating custom skin changes to work atop the new default — or who simply preferred it. See ../etienne. |
Changes to skins/default/css.txt.
|
| | > < < < < > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 |
/* Overall page style; vi: filetype=css
*/
body {
margin: 0 auto;
background-color: white;
font-family: sans-serif;
}
a {
/* Unvisited links are a lightness-adjusted version of this skin's
* header blue, balancing contrast between the body text and the
* background in order to meet the goals specified by the WCAG 2
* accessbility standard, earning us an "AA" grade according to
* the calculator result here:
*
* https://webaim.org/resources/linkcontrastchecker/?fcolor=2E2E2E&bcolor=FFFFFF&lcolor=3779BF
*
* It is for this same reason that our not-quite-black body text
* color is the shade of dark gray that it is. It can't be any
* lighter and still allow us to meet both targets. */
color: #3779BF;
text-decoration: none;
}
a:hover {
color: #4183C4;
text-decoration: underline;
}
/* Page title, above menu bars */
.title {
color: #4183C4;
float: left;
}
h1.page-title {
font-size: 1.60em; /* match content > h1 */
margin-bottom: 0; /* div.content top margin suffices */
display: none; /* don't use body-area h1 except… */
}
.artifact h1.page-title,
.dir h1.page-title,
.doc h1.page-title,
.wiki h1.page-title {
display: block; /* …for potentially long doc titles… */
}
.artifact .title > .page-title,
.dir .title > .page-title,
.doc .title > .page-title,
.wiki .title > .page-title {
display: none; /* …where we suppress the title area h1 instead */
}
.title h1 {
display: inline;
font-size: 2.20em;
}
.title h1:after {
content: " / ";
color: #777;
font-weight: normal;
}
.artifact .title h1:after,
.dir .title h1:after,
.doc .title h1:after,
.wiki .title h1:after {
content: ""; /* hide solidus for docs along with title h1 */
}
.status {
float: right;
font-size: 0.8em;
}
div.logo {
float: left;
padding-right: 10px;
}
div.logo img {
max-height: 2em; /* smaller than title to keep it above the baseline */
}
/* Main menu and optional sub-menu */
.mainmenu {
clear: both;
background: #eaeaea linear-gradient(#fafafa, #eaeaea) repeat-x;
border: 1px solid #eaeaea;
border-radius: 5px;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
z-index: 21; /* just above hbdrop */
}
.mainmenu a {
text-decoration: none;
color: #777;
border-right: 1px solid #eaeaea;
}
.mainmenu a.active,
.mainmenu a:hover {
color: #000;
border-bottom: 2px solid #D26911;
}
nav#hbdrop {
background-color: white;
border: 1px solid black;
border-top: white;
border-radius: 0 0 0.5em 0.5em;
display: none;
font-size: 80%;
left: 2em;
width: 90%;
padding-right: 1em;
position: absolute;
z-index: 20; /* just below mainmenu, but above timeline bubbles */
}
.submenu {
font-size: 0.8em;
padding: 10px;
border-bottom: 1px solid #ccc;
}
.submenu a, .submenu label {
padding: 10px 11px;
text-decoration: none;
color: #777;
|
| ︙ | ︙ | |||
103 104 105 106 107 108 109 |
white-space: nowrap;
}
/* Main document area; elements common to most pages. */
.content {
| | < | < < < | < < | | < | | < | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
white-space: nowrap;
}
/* Main document area; elements common to most pages. */
.content {
padding: 1ex;
color: #2e2e2e; /* justified above in "WCAG 2" comment */
}
.content h1 { font-size: 1.60em; color: #444; }
.content h2 { font-size: 1.45em; color: #444; }
.content h3 { font-size: 1.15em; color: #444; }
.content h4 { font-size: 1.05em; color: #444; }
.content h5 { font-size: 1.00em; color: #444; }
.section {
font-size: 1em;
font-weight: bold;
background-color: #f5f5f5;
border: 1px solid #d8d8d8;
border-radius: 3px 3px 0 0;
|
| ︙ | ︙ | |||
149 150 151 152 153 154 155 |
hr {
color: #eee;
}
/* Page footer */
| | | | > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > | 179 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 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 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 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 440 |
hr {
color: #eee;
}
/* Page footer */
footer {
border-top: 1px solid #ccc;
padding: 10px;
font-size: 0.8em;
margin-top: 10px;
color: #ccc;
}
/* Forum */
.forum a:visited {
color: #6A7F94;
}
div.forumSel {
animation: 1s linear 0s sel-fade;
background-color: white; /* animation end state */
border-left: 4px solid black; /* after-animation selection indicator */
}
@keyframes sel-fade {
from { background-color: #cef; }
to { background-color: white; }
}
.forum form input {
margin: 0.5em 0;
}
/* Markdown and Wiki-formatted pages: /wiki, /doc, /file... */
.markdown blockquote, p.blockquote, .sidebar {
/* Override default.css version with our accent colors. Background is
* the solid version of rgba(65, 131, 196, 0.1) on white, needed to
* avoid tinting pre block backgrounds going "under" them. */
background-color: #ebf2f9;
border-left-color: #4183c4;
}
div.sidebar {
/* Add extra whitespace between sidebar and content, both for spacing
* and to put a gap between it and any <pre> blocks that happen to run
* up against it. */
outline: 1em solid white;
}
/* Mark inline code fragments in the near-universal manner pioneered by
* Stack Overflow, then picked up by approximately everyone, including
* us, now.
*
* This combinatorial selector explosion results from a need to apply
* these stylings inside multiple page container types, multiplied by
* the surprisingly large number of tags HTML defines for semantically
* differentiated monospaced inline markup. If we do not target the
* elements we want to affect carefully, we'll end up overreaching,
* styling Fossil UI elements that use these tags for local purposes.
*
* HTML generated and emitted by Fossil UI does not always fall under
* the skin's generic rules; we must avoid intruding on its domain.
* Our limited intent here is to style user content only, where it is
* unreasonable to expect its author to take the time to hand-craft
* per-document styling. Contrast Fossil UI, which often does exactly
* that in order to get particular results.
*
* Its rough equivalent in Sass syntax is far more compact, thus clearer:
*
* .artifact, .dir, .doc, .forum, .wiki // the page types we target
* > .content // hands off header & footer
* &, > .fossil-doc, > .markdown // wiki, HTML & MD emb docs
* > p // in top-level paras only
* > code, > kbd, > samp, > tt, > var // monospaced tag types
* background-color: #f4f4f4 // pale gray box which…
* padding: 0 4px // …extends around the sides
*
* We then need something similar for the block-level pre elements.
*
* The CSS below is based on feeding that Sass code through this:
*
* $ sassc code.sass | sed -e 's/, /,\n/g'
*
* …then hand-cleansing it to make it _somewhat_ more understandable.
* That largely amounts to whitespace tweaks, but we've also done things
* like trim back the forum-specific styling to apply to the default MD
* markup only; direct HTML formatting isn't even an option there, and
* while wiki markup _is_ supported, MD was the default from day 1.
* Another quirk of the forum post handling is that the .markdown class
* gets applied per-post, not up at the top level as with the wiki,
* embedded docs, etc.
*/
.artifact > .content > p > code,
.artifact > .content > p > kbd,
.artifact > .content > p > samp,
.artifact > .content > p > tt,
.artifact > .content > p > var,
.artifact > .content > .fossil-doc > p > code,
.artifact > .content > .fossil-doc > p > kbd,
.artifact > .content > .fossil-doc > p > samp,
.artifact > .content > .fossil-doc > p > tt,
.artifact > .content > .fossil-doc > p > var,
.artifact > .content > .markdown > p > code,
.artifact > .content > .markdown > p > kbd,
.artifact > .content > .markdown > p > samp,
.artifact > .content > .markdown > p > tt,
.artifact > .content > .markdown > p > var,
.dir > .content > p > code,
.dir > .content > p > kbd,
.dir > .content > p > samp,
.dir > .content > p > tt,
.dir > .content > p > var,
.dir > .content > .fossil-doc > p > code,
.dir > .content > .fossil-doc > p > kbd,
.dir > .content > .fossil-doc > p > samp,
.dir > .content > .fossil-doc > p > tt,
.dir > .content > .fossil-doc > p > var,
.dir > .content > .markdown > p > code,
.dir > .content > .markdown > p > kbd,
.dir > .content > .markdown > p > samp,
.dir > .content > .markdown > p > tt,
.dir > .content > .markdown > p > var,
.doc > .content > p > code,
.doc > .content > p > kbd,
.doc > .content > p > samp,
.doc > .content > p > tt,
.doc > .content > p > var,
.doc > .content > .fossil-doc > p > code,
.doc > .content > .fossil-doc > p > kbd,
.doc > .content > .fossil-doc > p > samp,
.doc > .content > .fossil-doc > p > tt,
.doc > .content > .fossil-doc > p > var,
.doc > .content > .markdown > p > code,
.doc > .content > .markdown > p > kbd,
.doc > .content > .markdown > p > samp,
.doc > .content > .markdown > p > tt,
.doc > .content > .markdown > p > var,
.forum > .content .markdown > p > code,
.forum > .content .markdown > p > kbd,
.forum > .content .markdown > p > samp,
.forum > .content .markdown > p > tt,
.forum > .content .markdown > p > var,
.wiki > .content > p > code,
.wiki > .content > p > kbd,
.wiki > .content > p > samp,
.wiki > .content > p > tt,
.wiki > .content > p > var,
.wiki > .content > .fossil-doc > p > code,
.wiki > .content > .fossil-doc > p > kbd,
.wiki > .content > .fossil-doc > p > samp,
.wiki > .content > .fossil-doc > p > tt,
.wiki > .content > .fossil-doc > p > var,
.wiki > .content > .markdown > p > code,
.wiki > .content > .markdown > p > kbd,
.wiki > .content > .markdown > p > samp,
.wiki > .content > .markdown > p > tt,
.wiki > .content > .markdown > p > var,
.artifact > .content > pre,
.artifact > .content > .fossil-doc > pre,
.artifact > .content > .markdown > pre,
.dir > .content > pre,
.dir > .content > .fossil-doc > pre,
.dir > .content > .markdown > pre,
.doc > .content > pre,
.doc > .content > .fossil-doc > pre,
.doc > .content > .markdown > pre,
.forum > .content .markdown > pre,
.wiki > .content > pre,
.wiki > .content > .fossil-doc > pre,
.wiki > .content > .markdown > pre {
background-color: #f4f4f4;
padding: 0 4px;
}
.content pre, table.numbered-lines > tbody > tr {
hyphens: none;
line-height: 1.25;
}
.content ul:not(.browser) li {
list-style-type: disc;
}
div.filetree ul li.dir,
div.filetree ul li.subdir,
div.filetree ul li.file{
list-style-type: none;
}
.artifact > .content table,
.dir > .content table,
.doc > .content table,
.wiki > .content table {
background-color: #f0f5f9;
border: 1px solid #a7c2dc;
border-radius: 0.5em;
border-spacing: 0;
padding: 6px;
}
.artifact > .content th,
.dir > .content th,
.doc > .content th,
.wiki > .content th {
border-bottom: 1px solid #dee8f2;
padding-bottom: 4px;
padding-right: 6px;
}
.artifact > .content tr > th,
.dir > .content tr > th,
.doc > .content tr > th,
.wiki > .content tr > th {
background-color: #dee8f0;
}
.artifact > .content tr:nth-child(odd),
.dir > .content tr:nth-child(odd),
.doc > .content tr:nth-child(odd),
.wiki > .content tr:nth-child(odd) {
background-color: #e0e8ee;
}
.artifact > .content td,
.dir > .content td,
.doc > .content td,
.wiki > .content td {
padding-bottom: 4px;
padding-right: 6px;
}
th {
/* Special rule at high level to override default centering of table
header cell text. If it isn't at this level, it can't be
overridden in the HTML, as by the MD table generator's handling
of `:` alignment markers. */
text-align: left;
}
/* Wiki adjustments */
pre.verbatim {
/* keep code examples from crashing into sidebars, etc. */
white-space: pre-wrap;
}
textarea.wikiedit {
/* Monospace fonts tend to have smaller x-heights; compensate.
* Can't do this generally because not all fonts have this problem.
* A textarea stands alone, whereas inline <code> has to work with
* the browser's choice of sans-serif proportional font. */
font-size: 1.1em;
}
/* Tickets */
table.report {
cursor: auto;
border: 1px solid #ccc;
border-radius: 0.5em;
margin: 1em 0;
}
.report td, .report th {
border: 0;
font-size: .8em;
padding: 10px;
}
|
| ︙ | ︙ | |||
225 226 227 228 229 230 231 |
white-space: pre-wrap;
}
/* Timeline */
span.timelineDetail {
| | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 |
white-space: pre-wrap;
}
/* Timeline */
span.timelineDetail {
font-size: 90%;
}
div.timelineDate {
font-weight: bold;
white-space: nowrap;
}
/* Extend default.css comment cell rounding to the whole row for the
* various types of "selected" rows, making them "hang" into the left
* margin, distinguishing them from the coloring used for branch cells.
* Care must be taken to avoid having the box-shadow rounded but the
* background squared-off. */
table.timelineTable {
padding: 0 3px; /* leave space to sides for box shadow; can clip otherwise */
}
table.timelineTable tr {
border-radius: 1em;
}
tr.timelineSelected, tr.timelineSecondary { background-color: unset; }
tr.timelineSelected td, span.timelineSelected {
background-color: #fbe8d5;
}
tr.timelineSecondary td, span.timelineSecondary {
background-color: #d5e8fb;
}
tr.timelineCurrent td:first-child,
tr.timelineSecondary td:first-child,
tr.timelineSelected td:first-child {
border-top-left-radius: 1em;
border-bottom-left-radius: 1em;
}
tr.timelineCurrent td:last-child,
tr.timelineSecondary td:last-child,
tr.timelineSelected td:last-child {
border-top-right-radius: 1em;
border-bottom-right-radius: 1em;
}
tr.timelineCurrent td {
border-top: 1px dashed #446979;
border-bottom: 1px dashed #446979;
}
tr.timelineCurrent td:first-child {
border-left: 1px dashed #446979;
}
tr.timelineCurrent td:last-child {
border-right: 1px dashed #446979;
}
/* Miscellaneous UI elements */
.fossil-tooltip.help-buttonlet-content {
background-color: lightyellow;
}
/* Exceptions for specific screen sizes */
@media screen and (max-width: 600px) {
/* Spacing for mobile */
body {
padding-left: 4px;
padding-right: 4px;
}
.content {
font-size: 0.9em;
}
.title {
padding-top: 0px;
padding-bottom: 0px;
}
.title > .page-title {
display: inline; /* show page titles above menu bar… */
}
.artifact .title > .page-title,
.dir .title > .page-title,
.doc .title > .page-title,
.wiki .title > .page-title {
display: none; /* …except for docs, where it may force wrapping */
}
.status {padding-top: 0px;}
.mainmenu a {
padding: 8px 10px;
}
.mainmenu {
padding: 10px;
}
}
@media screen and (min-width: 600px) {
/* Spacing for desktop */
body {
padding-left: 20px;
padding-right: 20px;
}
.title {
padding-top: 10px;
padding-bottom: 10px;
}
span.page-title {
font-size: 18px;
}
div.logo {
padding-top: 10px;
}
.status {padding-top: 30px;}
.mainmenu a {
padding: 8px 20px;
}
.mainmenu {
padding: 10px;
}
/* Wide screens mean long lines. Add extra leading to give the eye a
* "gutter" to follow from the end of one to the start of the next. */
.content dd,
.content dt,
.content div,
.content li,
.content p,
.content table {
line-height: 1.4em;
}
/* This horror show has the same cause that informed our handling of
* <code> and friends above; see "combinatorial selector explosion."
* Without this careful targeting, we'd not only overreach into areas
* of Fossil UI where our meddling is not wanted, we would mistakenly
* apply double indents to nested formatting in MD forum posts, p
* within td tags, and more.
*
* Rather than give the equivalent Sass code here, see the SCSS file
* that the [Inskinerator](https://tangentsoft.com/inskinerator/)
* project ships as override/modern/media.scss. Rendering that
* through sassc gives substantially identical output, modulo the
* hand-polishing we've done here. */
.artifact > .content > p,
.artifact > .content > .markdown > p,
.artifact > .content > .fossil-doc > p,
.artifact > .content > ol, .artifact > .content > ul,
.artifact > .content > .markdown > ol, .artifact > .content > .markdown > ul,
.artifact > .content > .fossil-doc > ol, .artifact > .content > .fossil-doc > ul,
.artifact > .content > table,
.artifact > .content > .markdown > table,
.artifact > .content > .fossil-doc > table,
.dir > .content > p,
.dir > .content > .markdown > p,
.dir > .content > .fossil-doc > p,
.dir > .content > ol, .dir > .content > ul,
.dir > .content > .markdown > ol, .dir > .content > .markdown > ul,
.dir > .content > .fossil-doc > ol, .dir > .content > .fossil-doc > ul,
.dir > .content > table,
.dir > .content > .markdown > table,
.dir > .content > .fossil-doc > table,
.doc > .content > p,
.doc > .content > .markdown > p,
.doc > .content > .fossil-doc > p,
.doc > .content > ol, .doc > .content > ul,
.doc > .content > .markdown > ol, .doc > .content > .markdown > ul,
.doc > .content > .fossil-doc > ol, .doc > .content > .fossil-doc > ul,
.doc > .content > table,
.doc > .content > .markdown > table,
.doc > .content > .fossil-doc > table,
.wiki > .content > p,
.wiki > .content > .markdown > p,
.wiki > .content > .fossil-doc > p,
.wiki > .content > ol, .wiki > .content > ul,
.wiki > .content > .markdown > ol, .wiki > .content > .markdown > ul,
.wiki > .content > .fossil-doc > ol, .wiki > .content > .fossil-doc > ul,
.wiki > .content > table,
.wiki > .content > .markdown > table,
.wiki > .content > .fossil-doc > table,
#fileedit-tab-preview-wrapper > p,
#fileedit-tab-preview-wrapper > ol,
#fileedit-tab-preview-wrapper > ul,
#fileedit-tab-preview-wrapper > table,
#fileedit-tab-preview-wrapper > .markdown > p,
#fileedit-tab-preview-wrapper > .markdown > ol,
#fileedit-tab-preview-wrapper > .markdown > ul,
#fileedit-tab-preview-wrapper > .markdown > table,
#wikiedit-tab-preview-wrapper > p,
#wikiedit-tab-preview-wrapper > ol,
#wikiedit-tab-preview-wrapper > ul,
#wikiedit-tab-preview-wrapper > table,
#wikiedit-tab-preview-wrapper > .markdown > p,
#wikiedit-tab-preview-wrapper > .markdown > ol,
#wikiedit-tab-preview-wrapper > .markdown > ul,
#wikiedit-tab-preview-wrapper > .markdown > table {
margin-left: 50pt;
margin-right: 50pt;
}
/* Code blocks get extra indenting. We need a selector explosion
* equally powerful to the one above for inline <code> fragments and
* similar elements, for essentially the same reason: Fossil UI also
* uses <pre>, and we want to affect user content only.
*
* The equivalent Sass code is:
*
* .artifact, .dir, .doc, .wiki // doc types we target
* > .content // hands off header & footer
* @import 'pre-doc-margins.sass'
*
* #fileedit-tab-preview-wrapper, // include /fileedit previews
* #wikiedit-tab-preview-wrapper // ditto /wikiedit
* @import 'pre-doc-margins.sass'
*
* …where pre-doc-margins.sass contains the elements common to both:
*
* &, > .fossil-doc, > .markdown // wiki, HTML & MD doc types
* > pre // direct pre descendants only
* margin-left: 70pt;
* margin-right: 50pt;
*
* This is a technical overreach since /wiki & /wikiedit lack support
* for Fossil's HTML embedded doc markup capability, but we prefer to
* draw the /fileedit parallel in our Sass example over the dubious
* pleasure of being nit-picky on this point. Instead, we've chosen
* to back that overreach out by hand below.
*/
.artifact > .content > pre,
.artifact > .content > .fossil-doc > pre,
.artifact > .content > .markdown > pre,
.dir > .content > pre,
.dir > .content > .fossil-doc > pre,
.dir > .content > .markdown > pre,
.doc > .content > pre,
.doc > .content > .fossil-doc > pre,
.doc > .content > .markdown > pre,
.wiki > .content > pre,
.wiki > .content > .markdown > pre {
margin-left: 70pt;
margin-right: 50pt;
}
#fileedit-tab-preview-wrapper > pre,
#wikiedit-tab-preview-wrapper > pre,
#fileedit-tab-preview-wrapper > .fossil-doc > pre,
#fileedit-tab-preview-wrapper > .markdown > pre,
#wikiedit-tab-preview-wrapper > .markdown > pre {
margin-left: 70pt;
margin-right: 50pt;
}
.forum > .content .markdown > pre {
margin-left: 20pt; /* special case for MD in forum; need less indent */
}
/* Fossil UI uses these, but in sufficiently constrained ways that we
* don't have to be nearly as careful to avoid an overreach. */
.doc > .content h1, .artifact h1, .dir h1, .fileedit h1, .wiki h1 { margin-left: 10pt; }
.doc > .content h2, .artifact h2, .dir h2, .fileedit h2, .wiki h2 { margin-left: 20pt; }
.doc > .content h3, .artifact h3, .dir h3, .fileedit h3, .wiki h3 { margin-left: 30pt; }
.doc > .content h4, .artifact h4, .dir h4, .fileedit h4, .wiki h4 { margin-left: 40pt; }
.doc > .content h5, .artifact h5, .dir h5, .fileedit h5, .wiki h5 { margin-left: 50pt; }
.doc > .content hr, .artifact hr, .dir hr, .fileedit hr, .wiki hr { margin-left: 10pt; }
/* Don't need to be nearly as careful with tags Fossil UI doesn't use. */
.doc dd, .artifact dd, .dir dd, .fileedit dd, .wikiedit dd { margin-left: 30pt; margin-bottom: 1em; }
.doc dl, .artifact dl, .dir dl, .fileedit dl, .wikiedit dl { margin-left: 60pt; }
.doc dt, .artifact dt, .dir dt, .fileedit dt, .wikiedit dt { margin-left: 10pt; }
/* Fossil UI doesn't use Pikchr at all (yet?) so we can be quite loose
* with these selectors. */
.content .pikchr-wrapper { margin-left: 70pt; }
div.pikchr-wrapper.indent:not(.source) {
/* Selector naming scheme mismatch is intentional: it must match the
* way it's given in default.css exactly if it is to override it. */
margin-left: 70pt;
margin-right: 50pt;
}
div.pikchr-wrapper.center:not(.source),
div.pikchr-wrapper.float-right:not(.source) {
margin-left: 0;
}
/* Special treatment for backward compatibility. */
.indent, /* clean alternative to misusing <blockquote> */
.artifact > .content > blockquote:not(.file-content),
.dir > .content > blockquote,
.doc > .content > blockquote,
.fileedit > .content > blockquote,
.wiki > .content > blockquote {
/* We must apply extra indent relative to "p" since Fossil's wiki
* generator misuses the blockquote tag against HTML and MD norms
* to mean "indented paragraph." Skip it for file content retrieved
* by /dir URLs. */
margin-left: 80pt;
}
.artifact > .content > .markdown > blockquote,
.dir > .content > .markdown > blockquote,
.doc > .content > .markdown > blockquote,
.fileedit > .content > .markdown > blockquote,
.wiki > .content > .markdown > blockquote {
/* Fossil MD didn't inherit that bug; its HTML generator emits
* blockquote tags only for _block quotes_! A moderate indent
* suffices due to the visual styling applied above. */
margin-left: 60pt;
}
/* Alternative to BLOCK.indent when wrapped in something that is
* itself indented. The value is the delta between p and blockquote
* above, expressed as padding instead of margin so it adds to the
* outer margin instead of forcing the browser into picking one. */
.local-indent {
padding-left: 30pt;
}
}
|
Changes to skins/default/details.txt.
1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 | > > | 1 2 3 4 5 6 | pikchr-fontscale: "0.9" pikchr-scale: "1.1" timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 |
Changes to skins/default/footer.txt.
|
| | | | 1 2 3 4 5 |
<footer>
This page was generated in about
<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
Fossil $release_version $manifest_version $manifest_date
</footer>
|
Changes to skins/default/header.txt.
|
| > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > | | | | | | | > | | | | | | | | | | | | | | | > | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 |
<header>
<div class="logo">
<th1>
## See skins/original/header.txt for commentary; not repeated here.
proc getLogoUrl { baseurl } {
set idx(first) [string first // $baseurl]
if {$idx(first) != -1} {
set idx(first+1) [expr {$idx(first) + 2}]
set idx(nextRange) [string range $baseurl $idx(first+1) end]
set idx(next) [string first / $idx(nextRange)]
if {$idx(next) != -1} {
set idx(next) [expr {$idx(next) + $idx(first+1)}]
set idx(next-1) [expr {$idx(next) - 1}]
set scheme [string range $baseurl 0 $idx(first)]
set host [string range $baseurl $idx(first+1) $idx(next-1)]
if {[string compare $scheme http:/] == 0} {
set scheme http://
} else {
set scheme https://
}
set logourl $scheme$host/
} else {
set logourl $baseurl
}
} else {
set logourl $baseurl
}
return $logourl
}
set logourl [getLogoUrl $baseurl]
</th1>
<a href="$logourl">
<img src="$logo_image_url" border="0" alt="$project_name">
</a>
</div>
<div class="title">
<h1>$<project_name></h1>
<span class="page-title">$<title></span>
</div>
<div class="status">
<th1>
if {[info exists login]} {
html "<a href='$home/login'>$login</a>\n"
} else {
html "<a href='$home/login'>Login</a>\n"
}
</th1>
</div>
</header>
<nav class="mainmenu" title="Main Menu">
<th1>
html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>"
builtin_request_js hbmenu.js
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {
if {[string match $url\[/?#\]* /$current_page/]} {
set class "active $class"
}
set url $home$url
}
html "<a href='$url' class='$class'>$name</a>\n"
}
</th1>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>
<h1 class="page-title">$<title></h1>
|
Changes to skins/eagle/css.txt.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 | color: white; font-size: 0.8em; font-weight: bold; white-space: nowrap; } /* The header across the top of the page */ | | | | | | | 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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
color: white;
font-size: 0.8em;
font-weight: bold;
white-space: nowrap;
}
/* The header across the top of the page */
header {
display: table;
width: 100%;
}
/* The main menu bar that appears at the top of the page beneath
** the header */
nav.mainmenu {
padding: 5px 10px 5px 10px;
font-size: 0.9em;
font-weight: bold;
text-align: center;
letter-spacing: 1px;
background-color: #76869D;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
color: white;
}
nav#hbdrop {
background-color: #485D7B;
border-radius: 0 0 15px 15px;
border-left: 0.5em solid #76869d;
border-bottom: 1.2em solid #76869d;
display: none;
width: 98%;
position: absolute;
z-index: 20;
}
/* The submenu bar that *sometimes* appears below the main menu */
div.submenu, div.sectionmenu {
padding: 3px 10px 3px 0px;
font-size: 0.9em;
font-weight: bold;
text-align: center;
background-color: #485D7B;
color: white;
}
nav.mainmenu a, nav.mainmenu a:visited, div.submenu a, div.submenu a:visited,
div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited,
div.submenu label {
padding: 3px 10px 3px 10px;
color: white;
text-decoration: none;
}
nav.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover,
div.submenu label:hover {
text-decoration: underline;
}
/* All page content from the bottom of the menu or submenu down to
** the footer */
div.content {
|
| ︙ | ︙ | |||
129 130 131 132 133 134 135 | margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
margin: .2em 0 .2em 0;
float: left;
clear: left;
white-space: nowrap;
}
/* The footer at the very bottom of the page */
footer {
clear: both;
font-size: 0.8em;
margin-top: 12px;
padding: 5px 10px 5px 10px;
text-align: right;
background-color: #485D7B;
border-bottom-left-radius: 8px;
|
| ︙ | ︙ | |||
450 451 452 453 454 455 456 |
background-color: #e5e500;
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #7EA2D9;
}
| > > > > | 450 451 452 453 454 455 456 457 458 459 460 |
background-color: #e5e500;
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #7EA2D9;
}
p.noMoreShun {
color: #e5e500;
}
|
Changes to skins/eagle/footer.txt.
|
| | | 1 2 3 4 5 6 7 8 |
<footer>
<th1>
proc getTclVersion {} {
if {[catch {tclEval info patchlevel} tclVersion] == 0} {
return "<a href=\"https://www.tcl.tk/\">Tcl</a> version $tclVersion"
}
return ""
}
|
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
</th1>
This page was generated in about
<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
<a href="$fossilUrl/">Fossil</a>
version $release_version $tclVersion
<a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
<a href="$fossilUrl/index.html/timeline?c=$fossilDate&y=ci">$manifest_date</a>
| | | 17 18 19 20 21 22 23 24 |
</th1>
This page was generated in about
<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
<a href="$fossilUrl/">Fossil</a>
version $release_version $tclVersion
<a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
<a href="$fossilUrl/index.html/timeline?c=$fossilDate&y=ci">$manifest_date</a>
</footer>
|
Changes to skins/eagle/header.txt.
|
| | | 1 2 3 4 5 6 7 8 |
<header>
<div class="logo">
<th1>
##
## NOTE: The purpose of this procedure is to take the base URL of the
## Fossil project and return the root of the entire web site using
## the same URI scheme as the base URL (e.g. http or https).
##
|
| ︙ | ︙ | |||
74 75 76 77 78 79 80 |
<div class="status"><nobr><th1>
if {[info exists login]} {
puts "Logged in as $login"
} else {
puts "Not logged in"
}
</th1></nobr><small><div id="clock"></div></small></div>
| | | | | | | | | | | | | | | | | | | > | | | | | | | | > | | 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 |
<div class="status"><nobr><th1>
if {[info exists login]} {
puts "Logged in as $login"
} else {
puts "Not logged in"
}
</th1></nobr><small><div id="clock"></div></small></div>
</header>
<th1>html "<script nonce='$nonce'>"</th1>
(function updateClock(){
var e = document.getElementById("clock");
if(!e) return;
if(!updateClock.fmt){
updateClock.fmt = function(n){
return n < 10 ? '0' + n : n;
};
}
var d = new Date();
e.innerHTML = d.getUTCFullYear()+ '-' +
updateClock.fmt(d.getUTCMonth() + 1) + '-' +
updateClock.fmt(d.getUTCDate()) + ' ' +
updateClock.fmt(d.getUTCHours()) + ':' +
updateClock.fmt(d.getUTCMinutes());
setTimeout(updateClock,(60-d.getUTCSeconds())*1000);
})();
</script>
<nav class="mainmenu" title="Main Menu">
<th1>
html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>\n"
builtin_request_js hbmenu.js
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {set url $home$url}
html "<a href='$url' class='$class'>$name</a>\n"
}
</th1>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>
|
Added skins/etienne/README.md.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | This skin was contributed by Étienne Deparis. It was promoted to the default from 2015-03-14 until February 2024, when it was forked into this location for use by those who do not want the large number of changes merged into trunk at that time. Even if you agree with us that the changes improve readability, you may prefer to pack more information onto the screen at the expense of readability. Other reasons to choose this fork are to migrate custom skin changes to work atop the new base, or to make a comparative design evaluation. A bare minimum of changes have been made to this fork, primarily to allow this skin to render the Fossil documentation in a readable fashion. The intent is that you be able to toggle between these two skins at will. |
Added skins/etienne/css.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 134 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 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
/* Overall page style; vi: filetype=css
*/
body {
margin: 0 auto;
background-color: white;
font-family: sans-serif;
font-size: 14pt;
}
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
color: #4183C4;
text-decoration: underline;
}
/* Page title, above menu bars */
.title {
color: #4183C4;
float: left;
}
.title h1 {
display: inline;
}
.title h1:after {
content: " / ";
color: #777;
font-weight: normal;
}
.status {
float: right;
font-size: 0.7em;
}
/* Main menu and optional sub-menu */
.mainmenu {
font-size: 0.8em;
clear: both;
background: #eaeaea linear-gradient(#fafafa, #eaeaea) repeat-x;
border: 1px solid #eaeaea;
border-radius: 5px;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
z-index: 21; /* just above hbdrop */
}
.mainmenu a {
text-decoration: none;
color: #777;
border-right: 1px solid #eaeaea;
}
.mainmenu a.active,
.mainmenu a:hover {
color: #000;
border-bottom: 2px solid #D26911;
}
nav#hbdrop {
background-color: white;
border: 1px solid black;
border-top: white;
border-radius: 0 0 0.5em 0.5em;
display: none;
font-size: 80%;
left: 2em;
width: 90%;
padding-right: 1em;
position: absolute;
z-index: 20; /* just below mainmenu, but above timeline bubbles */
}
.submenu {
font-size: .7em;
padding: 10px;
border-bottom: 1px solid #ccc;
}
.submenu a, .submenu label {
padding: 10px 11px;
text-decoration: none;
color: #777;
}
.submenu label {
white-space: nowrap;
}
.submenu a:hover, .submenu label:hover {
padding: 6px 10px;
border: 1px solid #ccc;
border-radius: 5px;
color: #000;
}
span.submenuctrl, span.submenuctrl input, select.submenuctrl {
color: #777;
}
span.submenuctrl {
white-space: nowrap;
}
/* Main document area; elements common to most pages. */
.content {
padding-top: 10px;
font-size: 0.8em;
color: #444;
}
.content blockquote {
padding: 0 15px;
}
.content h1 {
font-size: 1.25em;
}
.content h2 {
font-size: 1.15em;
}
.content h3 {
font-size: 1.05em;
}
.section {
font-size: 1em;
font-weight: bold;
background-color: #f5f5f5;
border: 1px solid #d8d8d8;
border-radius: 3px 3px 0 0;
padding: 9px 10px 10px;
margin: 10px 0;
}
.sectionmenu {
border: 1px solid #d8d8d8;
border-radius: 0 0 3px 3px;
border-top: 0;
margin-top: -10px;
margin-bottom: 10px;
padding: 10px;
}
.sectionmenu a {
display: inline-block;
margin-right: 1em;
}
hr {
color: #eee;
}
/* Page footer */
footer {
border-top: 1px solid #ccc;
padding: 10px;
font-size: 0.7em;
margin-top: 10px;
color: #ccc;
}
/* Forum */
.forum a:visited {
color: #6A7F94;
}
.forum blockquote {
background-color: rgba(65, 131, 196, 0.1);
border-left: 3px solid #254769;
padding: .1em 1em;
}
/* Markdown and Wiki-formatted pages: /wiki, /doc, /file... */
.doc > .content table {
background-color: rgba(0, 0, 0, 0.05);
border: 1px solid #aaa;
border-radius: 0.5em;
border-spacing: 0;
padding: 6px;
}
.doc > .content th {
border-bottom: 1px solid #ddd;
padding-bottom: 4px;
padding-right: 6px;
text-align: left;
}
.doc > .content tr > th {
background-color: #eee;
}
.doc > .content tr:nth-child(odd) {
background-color: #e8e8e8;
}
.doc > .content td {
padding-bottom: 4px;
padding-right: 6px;
text-align: left;
}
/* Tickets */
table.report {
cursor: auto;
border-radius: 5px;
border: 1px solid #ccc;
margin: 1em 0;
}
.report td, .report th {
border: 0;
font-size: .8em;
padding: 10px;
}
.report td:first-child {
border-top-left-radius: 5px;
}
.report tbody tr:last-child td:first-child {
border-bottom-left-radius: 5px;
}
.report td:last-child {
border-top-right-radius: 5px;
}
.report tbody tr:last-child {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
.report tbody tr:last-child td:last-child {
border-bottom-right-radius: 5px;
}
.report th {
cursor: pointer;
}
.report thead+tbody tr:hover {
background-color: #f5f9fc !important;
}
td.tktDspLabel {
width: 70px;
text-align: right;
overflow: hidden;
}
td.tktDspValue {
text-align: left;
vertical-align: top;
background-color: #f8f8f8;
border: 1px solid #ccc;
}
td.tktDspValue pre {
white-space: pre-wrap;
}
/* Timeline */
span.timelineDetail {
font-size: 90%;
}
div.timelineDate {
font-weight: bold;
white-space: nowrap;
}
/* Miscellaneous UI elements */
.fossil-tooltip.help-buttonlet-content {
background-color: lightyellow;
}
/* Exceptions for specific screen sizes */
@media screen and (max-width: 600px) {
/* Spacing for mobile */
body {
padding-left: 4px;
padding-right: 4px;
}
.title {
padding-top: 0px;
padding-bottom: 0px;
}
.status {padding-top: 0px;}
.mainmenu a {
padding: 8px 10px;
}
.mainmenu {
padding: 10px;
}
}
@media screen and (min-width: 600px) {
/* Spacing for desktop */
body {
padding-left: 20px;
padding-right: 20px;
}
.title {
padding-top: 10px;
padding-bottom: 10px;
}
.status {padding-top: 30px;}
.mainmenu a {
padding: 8px 20px;
}
.mainmenu {
padding: 10px;
}
}
|
Added skins/etienne/details.txt.
> > > > | 1 2 3 4 | timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 |
Added skins/etienne/footer.txt.
> > > > > | 1 2 3 4 5 |
<footer>
This page was generated in about
<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
Fossil $release_version $manifest_version $manifest_date
</footer>
|
Added skins/etienne/header.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<header>
<div class="title"><h1>$<project_name></h1>$<title></div>
<div class="status">
<th1>
if {[info exists login]} {
html "<a href='$home/login'>$login</a>\n"
} else {
html "<a href='$home/login'>Login</a>\n"
}
</th1>
</div>
</header>
<nav class="mainmenu" title="Main Menu">
<th1>
html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>"
builtin_request_js hbmenu.js
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {
if {[string match $url\[/?#\]* /$current_page/]} {
set class "active $class"
}
set url $home$url
}
html "<a href='$url' class='$class'>$name</a>\n"
}
</th1>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>
|
Changes to skins/khaki/css.txt.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 | padding: 5px 5px 0 0; font-size: 0.8em; font-weight: bold; white-space: nowrap; } /* The header across the top of the page */ | | | | | | | | | | | | 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 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 |
padding: 5px 5px 0 0;
font-size: 0.8em;
font-weight: bold;
white-space: nowrap;
}
/* The header across the top of the page */
header {
display: table;
width: 100%;
}
/* The main menu bar that appears at the top of the page beneath
** the header */
nav.mainmenu {
padding: 5px 10px 5px 10px;
font-size: 0.9em;
font-weight: bold;
text-align: center;
letter-spacing: 1px;
background-color: #a09048;
color: black;
z-index: 21; /* just above hbdrop */
}
nav#hbdrop {
background-color: #fef3bc;
border: 2px solid #a09048;
border-radius: 0 0 0.5em 0.5em;
display: none;
left: 2em;
width: 90%;
padding-right: 1em;
position: absolute;
z-index: 20; /* just below mainmenu, but above timeline bubbles */
}
/* The submenu bar that *sometimes* appears below the main menu */
div.submenu, div.sectionmenu {
padding: 3px 10px 3px 0px;
font-size: 0.9em;
text-align: center;
background-color: #c0af58;
color: white;
}
nav.mainmenu a, nav.mainmenu a:visited, div.submenu a, div.submenu a:visited,
div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited,
div.submenu label {
padding: 3px 10px 3px 10px;
color: white;
text-decoration: none;
}
nav.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover,
div.submenu label:hover, nav#hbdrop a:hover {
color: #a09048;
background-color: white;
}
/* All page content from the bottom of the menu or submenu down to
** the footer */
div.content {
padding: 1ex 5px;
}
div.content a, nav#hbdrop a { color: #706532; }
div.content a:link, nav#hbdrop a:link { color: #706532; }
div.content a:visited, nav#hbdrop a:visited { color: #704032; }
div.content a:hover, nav#hbdrop a:hover {
background-color: white; color: #706532;
}
a, a:visited {
text-decoration: none;
}
|
| ︙ | ︙ | |||
131 132 133 134 135 136 137 | margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ | | | | | | | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
margin: .2em 0 .2em 0;
float: left;
clear: left;
white-space: nowrap;
}
/* The footer at the very bottom of the page */
footer {
font-size: 0.8em;
margin-top: 12px;
padding: 5px 10px 5px 10px;
text-align: right;
background-color: #a09048;
color: white;
}
/* Hyperlink colors */
footer a { color: white; }
footer a:link { color: white; }
footer a:visited { color: white; }
footer a:hover { background-color: white; color: #558195; }
/* <verbatim> blocks */
pre.verbatim {
background-color: #f5f5f5;
padding: 0.5em;
white-space: pre-wrap;
}
|
| ︙ | ︙ |
Changes to skins/khaki/footer.txt.
|
| | | | 1 2 3 | <footer> Fossil $release_version $manifest_version $manifest_date </footer> |
Changes to skins/khaki/header.txt.
|
| | | | | | | | | > | > | | | | | | | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<header>
<div class="title">$<title></div>
<div class="status">
<div class="logo">$<project_name></div><br/>
<th1>
if {[info exists login]} {
puts "Logged in as $login"
} else {
puts "Not logged in"
}
</th1>
</div>
</header>
<nav class="mainmenu" title="Main Menu">
<th1>
html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>"
builtin_request_js hbmenu.js
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {set url $home$url}
html "<a href='$url' class='$class'>$name</a>\n"
}
</th1>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>
|
Changes to skins/original/css.txt.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 | color: #558195; font-size: 0.8em; font-weight: bold; white-space: nowrap; } /* The header across the top of the page */ | | | | | | 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 87 88 |
color: #558195;
font-size: 0.8em;
font-weight: bold;
white-space: nowrap;
}
/* The header across the top of the page */
header {
display: table;
width: 100%;
}
/* The main menu bar that appears at the top of the page beneath
** the header */
nav.mainmenu {
padding: 5px;
font-size: 0.9em;
font-weight: bold;
text-align: center;
letter-spacing: 1px;
background-color: #558195;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
color: white;
}
/* The submenu bar that *sometimes* appears below the main menu */
div.submenu, div.sectionmenu {
padding: 3px 10px 3px 0px;
font-size: 0.9em;
text-align: center;
background-color: #456878;
color: white;
}
nav.mainmenu a, nav.mainmenu a:visited, div.submenu a, div.submenu a:visited,
div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited,
div.submenu label {
padding: 3px 10px 3px 10px;
color: white;
text-decoration: none;
}
nav.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover,
div.submenu label:hover {
color: #558195;
background-color: white;
}
/* All page content from the bottom of the menu or submenu down to
** the footer */
|
| ︙ | ︙ | |||
113 114 115 116 117 118 119 | margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ | | | | | | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
margin: .2em 0 .2em 0;
float: left;
clear: left;
white-space: nowrap;
}
/* The footer at the very bottom of the page */
footer {
clear: both;
font-size: 0.8em;
padding: 5px 10px 5px 10px;
text-align: right;
background-color: #558195;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
color: white;
}
/* Hyperlink colors in the footer */
footer a { color: white; }
footer a:link { color: white; }
footer a:visited { color: white; }
footer a:hover { background-color: white; color: #558195; }
/* verbatim blocks */
pre.verbatim {
background-color: #f5f5f5;
padding: 0.5em;
white-space: pre-wrap;
}
|
| ︙ | ︙ |
Changes to skins/original/footer.txt.
|
| | | 1 2 3 4 5 6 7 8 |
<footer>
<th1>
proc getTclVersion {} {
if {[catch {tclEval info patchlevel} tclVersion] == 0} {
return "<a href=\"https://www.tcl.tk/\">Tcl</a> version $tclVersion"
}
return ""
}
|
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
</th1>
This page was generated in about
<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
<a href="$fossilUrl/">Fossil</a>
version $release_version $tclVersion
<a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
<a href="$fossilUrl/index.html/timeline?c=$fossilDate&y=ci">$manifest_date</a>
| | | 17 18 19 20 21 22 23 24 |
</th1>
This page was generated in about
<th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by
<a href="$fossilUrl/">Fossil</a>
version $release_version $tclVersion
<a href="$fossilUrl/index.html/info/$version">$manifest_version</a>
<a href="$fossilUrl/index.html/timeline?c=$fossilDate&y=ci">$manifest_date</a>
</footer>
|
Changes to skins/original/header.txt.
|
| | | 1 2 3 4 5 6 7 8 |
<header>
<div class="logo">
<th1>
##
## NOTE: The purpose of this procedure is to take the base URL of the
## Fossil project and return the root of the entire web site using
## the same URI scheme as the base URL (e.g. http or https).
##
|
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
<div class="status"><nobr><th1>
if {[info exists login]} {
puts "Logged in as $login"
} else {
puts "Not logged in"
}
</th1></nobr><small><div id="clock"></div></small></div>
| | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | > | 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 |
<div class="status"><nobr><th1>
if {[info exists login]} {
puts "Logged in as $login"
} else {
puts "Not logged in"
}
</th1></nobr><small><div id="clock"></div></small></div>
</header>
<th1>html "<script nonce='$nonce'>"</th1>
function updateClock(){
var e = document.getElementById("clock");
if(e){
var d = new Date();
function f(n) {
return n < 10 ? '0' + n : n;
}
e.innerHTML = d.getUTCFullYear()+ '-' +
f(d.getUTCMonth() + 1) + '-' +
f(d.getUTCDate()) + ' ' +
f(d.getUTCHours()) + ':' +
f(d.getUTCMinutes());
setTimeout(updateClock,(60-d.getUTCSeconds())*1000);
}
}
updateClock();
</script>
<nav class="mainmenu" title="Main Menu">
<th1>
set sitemap 0
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {set url $home$url}
html "<a href='$url' class='$class'>$name</a>\n"
if {[string match */sitemap $url]} {set sitemap 1}
}
if {!$sitemap} {
html "<a href='$home/sitemap'>...</a>"
}
</th1>
</nav>
|
Changes to skins/plain_gray/css.txt.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 | vertical-align: bottom; color: #404040; font-weight: bold; white-space: nowrap; } /* The header across the top of the page */ | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
vertical-align: bottom;
color: #404040;
font-weight: bold;
white-space: nowrap;
}
/* The header across the top of the page */
header {
display: table;
width: 100%;
}
/* The main menu bar that appears at the top of the page beneath
** the header */
nav.mainmenu {
padding: 5px 10px 5px 10px;
font-size: 0.9em;
font-weight: bold;
text-align: center;
letter-spacing: 1px;
background-color: #404040;
color: white;
|
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
div.submenu, div.sectionmenu {
padding: 3px 10px 3px 0px;
font-size: 0.9em;
text-align: center;
background-color: #606060;
color: white;
}
| | | | | 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 |
div.submenu, div.sectionmenu {
padding: 3px 10px 3px 0px;
font-size: 0.9em;
text-align: center;
background-color: #606060;
color: white;
}
nav.mainmenu a,
nav.mainmenu a:visited,
div.submenu a,
div.submenu a:visited,
div.sectionmenu>a.button:link,
div.sectionmenu>a.button:visited,
div.submenu label {
padding: 3px 10px 3px 10px;
color: white;
text-decoration: none;
}
nav.mainmenu a:hover,
div.submenu a:hover,
div.sectionmenu>a.button:hover,
div.submenu label:hover {
color: #404040;
background-color: white;
}
a, a:visited {
|
| ︙ | ︙ | |||
129 130 131 132 133 134 135 | margin: .2em 0 .2em 0; float: left; clear: left; white-space: nowrap; } /* The footer at the very bottom of the page */ | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
margin: .2em 0 .2em 0;
float: left;
clear: left;
white-space: nowrap;
}
/* The footer at the very bottom of the page */
footer {
font-size: 0.8em;
margin-top: 12px;
padding: 5px 10px 5px 10px;
text-align: right;
background-color: #404040;
color: white;
}
|
| ︙ | ︙ |
Changes to skins/plain_gray/footer.txt.
|
| | | | 1 2 3 | <footer> Fossil $release_version $manifest_version $manifest_date </footer> |
Changes to skins/plain_gray/header.txt.
|
| | | | | | | | | | | | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<header>
<div class="title">$<project_name>: $<title></div>
</header>
<nav class="mainmenu" title="Main Menu">
<th1>
html "<a id='hbbtn' href='$home/sitemap' aria-label='Site Map'>☰</a>"
builtin_request_js hbmenu.js
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {set url $home$url}
html "<a href='$url' class='$class'>$name</a>\n"
}
</th1>
</nav>
<nav id="hbdrop" class='hbdrop' title="sitemap"></nav>
|
Changes to skins/xekri/css.txt.
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.25rem;
}
| < < < < < < < < < | | | 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 |
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.25rem;
}
/**************************************
* Main Area
*/
header, nav.mainmenu, div.submenu, div.content, footer {
clear: both;
margin: 0 auto;
max-width: 90%;
padding: 0.25rem 1rem;
}
/**************************************
* Main Area: Header
*/
header {
margin: 0.5rem auto 0 auto;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
}
div.logo {
|
| ︙ | ︙ | |||
144 145 146 147 148 149 150 | } /************************************** * Main Area: Global Menu */ | | | | | | 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 |
}
/**************************************
* Main Area: Global Menu
*/
nav.mainmenu, div.submenu {
background-color: #080;
border-radius: 1rem 1rem 0 0;
box-shadow: 3px 4px 1px #000;
color: #000;
font-weight: bold;
font-size: 1.1rem;
text-align: center;
}
nav.mainmenu {
padding-top: 0.33rem;
padding-bottom: 0.25rem;
}
div.submenu {
border-top: 1px solid #0a0;
border-radius: 0;
display: block;
}
nav.mainmenu a, div.submenu a, div.submenu label {
color: #000;
padding: 0 0.75rem;
text-decoration: none;
}
nav.mainmenu a:hover, div.submenu a:hover, div.submenu label:hover {
color: #fff;
text-shadow: 0px 0px 6px #0f0;
}
div.submenu * {
margin: 0 0.5rem;
vertical-align: middle;
|
| ︙ | ︙ | |||
221 222 223 224 225 226 227 | stroke: white; } /************************************** * Main Area: Footer */ | | | | | | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
stroke: white;
}
/**************************************
* Main Area: Footer
*/
footer {
color: #ee0;
font-size: 0.75rem;
padding: 0;
text-align: right;
width: 75%;
}
footer div {
background-color: #222;
box-shadow: 3px 3px 1px #000;
border-radius: 0 0 1rem 1rem;
margin: 0 0 10px 0;
padding: 0.25rem 0.75rem;
}
footer div.page-time {
float: left;
}
footer div.fossil-info {
float: right;
}
footer a, footer a:link, footer a:visited {
color: #ee0;
}
footer a:hover {
color: #fff;
text-shadow: 0px 0px 6px #ee0;
}
/**************************************
* Check-in
|
| ︙ | ︙ | |||
571 572 573 574 575 576 577 |
margin: 1.2rem auto 0.75rem auto;
padding: 0.2rem;
text-align: center;
}
div.sectionmenu {
border-radius: 0 0 3rem 3rem;
| | | 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
margin: 1.2rem auto 0.75rem auto;
padding: 0.2rem;
text-align: center;
}
div.sectionmenu {
border-radius: 0 0 3rem 3rem;
margin-top: auto;
width: 75%;
}
div.sectionmenu > a:link, div.sectionmenu > a:visited {
color: #000;
text-decoration: none;
}
|
| ︙ | ︙ | |||
1082 1083 1084 1085 1086 1087 1088 |
/* format for report configuration errors */
blockquote.reportError {
color: #f00;
font-weight: bold;
}
/* format for artifact lines, no longer shunned */
p.noMoreShun {
| | | | 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 |
/* format for report configuration errors */
blockquote.reportError {
color: #f00;
font-weight: bold;
}
/* format for artifact lines, no longer shunned */
p.noMoreShun {
color: yellow;
}
/* format for artifact lines being shunned */
p.shunned {
color: yellow;
}
/* a broken hyperlink */
span.brokenlink {
color: #f00;
}
/* List of files in a timeline */
ul.filelist {
|
| ︙ | ︙ | |||
1162 1163 1164 1165 1166 1167 1168 |
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #444;
}
| | | | 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 |
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #444;
}
body.chat header, body.chat footer,
body.chat nav.mainmenu, body.chat div.submenu,
body.chat div.content {
margin-left: 0.5em;
margin-right: 0.5em;
margin-top: auto/*eliminates unnecessary scrollbars*/;
}
body.chat.chat-only-mode div.content {
max-width: revert;
}
body.chat #chat-user-list .chat-user{
color: white;
}
|
Changes to skins/xekri/footer.txt.
1 | </div> | | | | | | | | | | 1 2 3 4 5 6 7 8 9 |
</div>
<footer>
<div class="page-time">
Generated in <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s
</div>
<div class="fossil-info">
Fossil v$release_version $manifest_version
</div>
</footer>
|
Changes to skins/xekri/header.txt.
|
| | | 1 2 3 4 5 6 7 8 |
<header>
<div class="logo">
<th1>
##
## NOTE: The purpose of this procedure is to take the base URL of the
## Fossil project and return the root of the entire web site using
## the same URI scheme as the base URL (e.g. http or https).
##
|
| ︙ | ︙ | |||
67 68 69 70 71 72 73 |
}
</th1>
<a href="$logourl">
<img src="$logo_image_url" border="0" alt="$project_name">
</a>
</div>
<div class="title">$<title></div>
| | > | | | | | > | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | > | 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 |
}
</th1>
<a href="$logourl">
<img src="$logo_image_url" border="0" alt="$project_name">
</a>
</div>
<div class="title">$<title></div>
<div class="status"><nobr>
<th1>
if {[info exists login]} {
puts "Logged in as $login"
} else {
puts "Not logged in"
}
</th1>
</nobr><small><div id="clock"></div></small></div>
</header>
<th1>html "<script nonce='$nonce'>"</th1>
function updateClock(){
var e = document.getElementById("clock");
if(e){
var d = new Date();
function f(n) {
return n < 10 ? '0' + n : n;
}
e.innerHTML = d.getUTCFullYear()+ '-' +
f(d.getUTCMonth() + 1) + '-' +
f(d.getUTCDate()) + ' ' +
f(d.getUTCHours()) + ':' +
f(d.getUTCMinutes());
setTimeout(updateClock,(60-d.getUTCSeconds())*1000);
}
}
updateClock();
</script>
<nav class="mainmenu" title="Main Menu">
<th1>
set sitemap 0
foreach {name url expr class} $mainmenu {
if {![capexpr $expr]} continue
if {[string match /* $url]} {
if {[string match $url\[/?#\]* /$current_page/]} {
set class "active $class"
}
set url $home$url
}
html "<a href='$url' class='$class'>$name</a>\n"
if {[string match */sitemap $url]} {set sitemap 1}
}
if {!$sitemap} {
html "<a href='$home/sitemap'>...</a>\n"
}
</th1>
</nav>
|
Changes to src/add.c.
| ︙ | ︙ | |||
445 446 447 448 449 450 451 |
zName = blob_str(&fullName);
isDir = file_isdir(zName, RepoFILE);
if( isDir==1 ){
vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
}else if( isDir==0 ){
fossil_warning("not found: %s", zName);
}else{
| | > | 445 446 447 448 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 |
zName = blob_str(&fullName);
isDir = file_isdir(zName, RepoFILE);
if( isDir==1 ){
vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
}else if( isDir==0 ){
fossil_warning("not found: %s", zName);
}else{
char *zTreeName = file_case_preferred_name(g.zLocalRoot,&zName[nRoot]);
if( !forceFlag && glob_match(pIgnore, zTreeName) ){
Blob ans;
char cReply;
char *prompt = mprintf("file \"%s\" matches \"ignore-glob\". "
"Add it (a=all/y/N)? ", zTreeName);
prompt_user(prompt, &ans);
fossil_free(prompt);
cReply = blob_str(&ans)[0];
blob_reset(&ans);
if( cReply=='a' || cReply=='A' ){
forceFlag = 1;
}else if( cReply!='y' && cReply!='Y' ){
blob_reset(&fullName);
continue;
}
}
db_multi_exec(
"INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)",
zTreeName
);
fossil_free(zTreeName);
}
blob_reset(&fullName);
}
glob_free(pIgnore);
glob_free(pClean);
/** Check for Windows-reserved names and warn or exit, as
|
| ︙ | ︙ | |||
1054 1055 1056 1057 1058 1059 1060 |
vid = db_lget_int("checkout", 0);
if( vid==0 ){
fossil_fatal("no check-out in which to rename files");
}
if( g.argc<4 ){
usage("OLDNAME NEWNAME");
}
| | | 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 |
vid = db_lget_int("checkout", 0);
if( vid==0 ){
fossil_fatal("no check-out in which to rename files");
}
if( g.argc<4 ){
usage("OLDNAME NEWNAME");
}
zDest = file_case_preferred_name(".",g.argv[g.argc-1]);
db_begin_transaction();
if( g.argv[1][0]=='r' ){ /* i.e. "rename" */
moveFiles = 0;
}else if( softFlag ){
moveFiles = 0;
}else if( hardFlag ){
moveFiles = 1;
|
| ︙ | ︙ | |||
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 |
mv_one_file(vid, zFrom, zTo, dryRunFlag);
if( moveFiles ) add_file_to_move(zFrom, zTo);
}
db_finalize(&q);
undo_reset();
db_end_transaction(0);
if( moveFiles ) process_files_to_move(dryRunFlag);
}
/*
** Function for stash_apply to be able to restore a file and indicate
** newly ADDED state.
*/
int stash_add_files_in_sfile(int vid){
return add_files_in_sfile(vid);
}
| > | 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 |
mv_one_file(vid, zFrom, zTo, dryRunFlag);
if( moveFiles ) add_file_to_move(zFrom, zTo);
}
db_finalize(&q);
undo_reset();
db_end_transaction(0);
if( moveFiles ) process_files_to_move(dryRunFlag);
fossil_free(zDest);
}
/*
** Function for stash_apply to be able to restore a file and indicate
** newly ADDED state.
*/
int stash_add_files_in_sfile(int vid){
return add_files_in_sfile(vid);
}
|
Changes to src/alerts.c.
| ︙ | ︙ | |||
325 326 327 328 329 330 331 |
@ <form action="%R/setup_notification" method="post"><div>
@ <input type="submit" name="submit" value="Apply Changes"><hr>
login_insert_csrf_secret();
entry_attribute("Canonical Server URL", 40, "email-url",
"eurl", "", 0);
@ <p><b>Required.</b>
| | | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
@ <form action="%R/setup_notification" method="post"><div>
@ <input type="submit" name="submit" value="Apply Changes"><hr>
login_insert_csrf_secret();
entry_attribute("Canonical Server URL", 40, "email-url",
"eurl", "", 0);
@ <p><b>Required.</b>
@ This URL is used as the basename for hyperlinks included in
@ email alert text. Omit the trailing "/".
@ Suggested value: "%h(g.zBaseURL)"
@ (Property: "email-url")</p>
@ <hr>
entry_attribute("Administrator email address", 40, "email-admin",
"eadmin", "", 0);
|
| ︙ | ︙ | |||
1458 1459 1460 1461 1462 1463 1464 | /* If we reach this point, all is well */ return 1; } /* ** Text of email message sent in order to confirm a subscription. */ | | | 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 | /* If we reach this point, all is well */ return 1; } /* ** Text of email message sent in order to confirm a subscription. */ static const char zConfirmMsg[] = @ Someone has signed you up for email alerts on the Fossil repository @ at %s. @ @ To confirm your subscription and begin receiving alerts, click on @ the following hyperlink: @ @ %s/alerts/%s |
| ︙ | ︙ | |||
1647 1648 1649 1650 1651 1652 1653 |
const char *zInit = "";
if( P("captchaseed")!=0 && eErr!=2 ){
uSeed = strtoul(P("captchaseed"),0,10);
zInit = P("captcha");
}else{
uSeed = captcha_seed();
}
| | | 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 |
const char *zInit = "";
if( P("captchaseed")!=0 && eErr!=2 ){
uSeed = strtoul(P("captchaseed"),0,10);
zInit = P("captcha");
}else{
uSeed = captcha_seed();
}
zDecoded = captcha_decode(uSeed, 0);
zCaptcha = captcha_render(zDecoded);
@ <tr>
@ <td class="form_label">Security Code:</td>
@ <td><input type="text" name="captcha" value="%h(zInit)" size="30">
captcha_speakit_button(uSeed, "Speak the code");
@ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td>
@ </tr>
|
| ︙ | ︙ | |||
2351 2352 2353 2354 2355 2356 2357 |
@ <td class="form_label">Email Address:</td>
@ <td><input type="text" name="e" value="%h(zEAddr)" size="30"></td>
if( eErr==1 ){
@ <td><span class="loginError">← %h(zErr)</span></td>
}
@ </tr>
uSeed = captcha_seed();
| | | 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 |
@ <td class="form_label">Email Address:</td>
@ <td><input type="text" name="e" value="%h(zEAddr)" size="30"></td>
if( eErr==1 ){
@ <td><span class="loginError">← %h(zErr)</span></td>
}
@ </tr>
uSeed = captcha_seed();
zDecoded = captcha_decode(uSeed, 0);
zCaptcha = captcha_render(zDecoded);
@ <tr>
@ <td class="form_label">Security Code:</td>
@ <td><input type="text" name="captcha" value="" size="30">
captcha_speakit_button(uSeed, "Speak the code");
@ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td>
if( eErr==2 ){
|
| ︙ | ︙ | |||
2561 2562 2563 2564 2565 2566 2567 | } /* ** Compute a string that is appropriate for the EmailEvent.zPriors field ** for a particular forum post. ** ** This string is an encode list of sender names and rids for all ancestors | | | 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 |
}
/*
** Compute a string that is appropriate for the EmailEvent.zPriors field
** for a particular forum post.
**
** This string is an encode list of sender names and rids for all ancestors
** of the fpdi post - the post that fpid answer, the post that that parent
** post answers, and so forth back up to the root post. Duplicates sender
** names are omitted.
**
** The EmailEvent.zPriors field is used to screen events for people who
** only want to see replies to their own posts or to specific posts.
*/
static char *alert_compute_priors(int fpid){
|
| ︙ | ︙ | |||
3221 3222 3223 3224 3225 3226 3227 |
** alerts that have been completely sent.
*/
db_multi_exec("DELETE FROM pending_alert WHERE sentDigest AND sentSep;");
/* Send renewal messages to subscribers whose subscriptions are about
** to expire. Only do this if:
**
| | | 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 |
** alerts that have been completely sent.
*/
db_multi_exec("DELETE FROM pending_alert WHERE sentDigest AND sentSep;");
/* Send renewal messages to subscribers whose subscriptions are about
** to expire. Only do this if:
**
** (1) email-renew-interval is 14 or greater (or in other words if
** subscription expiration is enabled).
**
** (2) The SENDALERT_RENEWAL flag is set
*/
send_alert_expiration_warnings:
if( (flags & SENDALERT_RENEWAL)!=0
&& (iInterval = db_get_int("email-renew-interval",0))>=14
|
| ︙ | ︙ | |||
3354 3355 3356 3357 3358 3359 3360 |
}
alert_sender_free(pSender);
style_finish_page();
return;
}
if( captcha_needed() ){
uSeed = captcha_seed();
| | | 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 |
}
alert_sender_free(pSender);
style_finish_page();
return;
}
if( captcha_needed() ){
uSeed = captcha_seed();
zDecoded = captcha_decode(uSeed, 0);
zCaptcha = captcha_render(zDecoded);
}
style_set_current_feature("alerts");
style_header("Message To Administrator");
form_begin(0, "%R/contact_admin");
@ <p>Enter a message to the repository administrator below:</p>
@ <table class="subscribe">
|
| ︙ | ︙ |
Changes to src/allrepo.c.
| ︙ | ︙ | |||
116 117 118 119 120 121 122 | ** remote Show remote hosts for all repositories. ** ** repack Look for extra compression in all repositories. ** ** sync Run a "sync" on all repositories. Only the --verbose ** and --unversioned and --share-links options are supported. ** | | | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | ** remote Show remote hosts for all repositories. ** ** repack Look for extra compression in all repositories. ** ** sync Run a "sync" on all repositories. Only the --verbose ** and --unversioned and --share-links options are supported. ** ** set[tings] Run the "settings" command on all repositories. ** This command is useful for settings like "max-loadavg" which ** you usually want to be the same across all repositories ** on a server. ** ** unset Run the "unset" command on all repositories ** ** server Run the "server" commands on all repositories. ** The root URI gives a listing of all repos. ** ** ui Run the "ui" command on all repositories. Like "server" ** but bind to the loopback TCP address only, enable ** the --localauth option and automatically launch a ** web-browser ** ** whatis Run the "whatis" command on all repositories. Only ** show output for repositories that have a match. ** ** ** In addition, the following maintenance operations are supported: ** ** add Add all the repositories named to the set of repositories ** tracked by Fossil. Normally Fossil is able to keep up with ** this list by itself, but sometimes it can benefit from this |
| ︙ | ︙ | |||
311 312 313 314 315 316 317 |
}else if( fossil_strcmp(g.argv[3],"config-data")==0 ){
zCmd = "remote config-data -R";
}else{
usage("remote ?config-data|list|ls?");
}
}else if( fossil_strcmp(zCmd, "repack")==0 ){
zCmd = "repack";
| | > > | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
}else if( fossil_strcmp(g.argv[3],"config-data")==0 ){
zCmd = "remote config-data -R";
}else{
usage("remote ?config-data|list|ls?");
}
}else if( fossil_strcmp(zCmd, "repack")==0 ){
zCmd = "repack";
}else if( fossil_strcmp(zCmd, "set")==0
|| fossil_strcmp(zCmd, "setting")==0
|| fossil_strcmp(zCmd, "settings")==0 ){
zCmd = "settings -R";
collect_argv(&extra, 3);
}else if( fossil_strcmp(zCmd, "unset")==0 ){
zCmd = "unset -R";
collect_argv(&extra, 3);
}else if( fossil_strcmp(zCmd, "fts-config")==0 ){
zCmd = "fts-config -R";
collect_argv(&extra, 3);
|
| ︙ | ︙ | |||
422 423 424 425 426 427 428 |
collect_argument(&extra, "file", "f");
collect_argument_value(&extra, "type", 0);
collect_argv(&extra, 3);
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config git ignore "
"info list ls pull push rebuild remote "
| | | 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
collect_argument(&extra, "file", "f");
collect_argument_value(&extra, "type", 0);
collect_argv(&extra, 3);
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config git ignore "
"info list ls pull push rebuild remote "
"server settings sync ui unset whatis");
}
verify_all_options();
db_multi_exec("CREATE TEMP TABLE repolist(name,tag);");
if( useCheckouts ){
db_multi_exec(
"INSERT INTO repolist "
"SELECT DISTINCT substr(name, 7), name COLLATE nocase"
|
| ︙ | ︙ |
Changes to src/attach.c.
| ︙ | ︙ | |||
748 749 750 751 752 753 754 |
if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){
zBody = pWiki->zWiki;
}
if( zBody==0 ){
fossil_fatal("technote [%s] not found",zETime);
}
zTarget = db_text(0,
| | > | 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){
zBody = pWiki->zWiki;
}
if( zBody==0 ){
fossil_fatal("technote [%s] not found",zETime);
}
zTarget = db_text(0,
"SELECT substr(tagname,7) FROM tag "
" WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')",
rid
);
zFile = g.argv[3];
}
blob_read_from_file(&content, zFile, ExtFILE);
user_select();
attach_commit(
|
| ︙ | ︙ |
Changes to src/backlink.c.
| ︙ | ︙ | |||
247 248 249 250 251 252 253 |
void *opaque
){
Backlink *p = (Backlink*)opaque;
char *zTarget = blob_buffer(target);
int nTarget = blob_size(target);
backlink_create(p, zTarget, nTarget);
| | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
void *opaque
){
Backlink *p = (Backlink*)opaque;
char *zTarget = blob_buffer(target);
int nTarget = blob_size(target);
backlink_create(p, zTarget, nTarget);
return 1;
}
/* No-op routines for the rendering callbacks that we do not need */
static void mkdn_noop_prolog(Blob *b, void *v){ return; }
static void (*mkdn_noop_epilog)(Blob*, void*) = mkdn_noop_prolog;
static void mkdn_noop_footnotes(Blob *b1, const Blob *b2, void *v){ return; }
static void mkdn_noop_blockcode(Blob *b1, Blob *b2, void *v){ return; }
|
| ︙ | ︙ |
Changes to src/backoffice.c.
| ︙ | ︙ | |||
313 314 315 316 317 318 319 |
** we cannot prove that the process is dead, return true.
*/
static int backofficeProcessExists(sqlite3_uint64 pid){
#if defined(_WIN32)
return pid>0 && backofficeWin32ProcessExists((DWORD)pid)!=0;
#else
return pid>0 && kill((pid_t)pid, 0)==0;
| | | | 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 |
** we cannot prove that the process is dead, return true.
*/
static int backofficeProcessExists(sqlite3_uint64 pid){
#if defined(_WIN32)
return pid>0 && backofficeWin32ProcessExists((DWORD)pid)!=0;
#else
return pid>0 && kill((pid_t)pid, 0)==0;
#endif
}
/*
** Check to see if the process identified by pid has finished. If
** we cannot prove that the process is still running, return true.
*/
static int backofficeProcessDone(sqlite3_uint64 pid){
#if defined(_WIN32)
return pid<=0 || backofficeWin32ProcessExists((DWORD)pid)==0;
#else
return pid<=0 || kill((pid_t)pid, 0)!=0;
#endif
}
/*
** Return a process id number for the current process
*/
static sqlite3_uint64 backofficeProcessId(void){
return (sqlite3_uint64)GETPID();
|
| ︙ | ︙ | |||
482 483 484 485 486 487 488 489 490 491 492 493 494 495 |
sqlite3_uint64 tmNow;
sqlite3_uint64 idSelf;
int lastWarning = 0;
int warningDelay = 30;
static int once = 0;
if( sqlite3_db_readonly(g.db, 0) ) return;
g.zPhase = "backoffice";
backoffice_error_check_one(&once);
idSelf = backofficeProcessId();
while(1){
tmNow = time(0);
db_begin_write();
backofficeReadLease(&x);
| > | 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 |
sqlite3_uint64 tmNow;
sqlite3_uint64 idSelf;
int lastWarning = 0;
int warningDelay = 30;
static int once = 0;
if( sqlite3_db_readonly(g.db, 0) ) return;
if( db_is_protected(PROTECT_READONLY) ) return;
g.zPhase = "backoffice";
backoffice_error_check_one(&once);
idSelf = backofficeProcessId();
while(1){
tmNow = time(0);
db_begin_write();
backofficeReadLease(&x);
|
| ︙ | ︙ | |||
673 674 675 676 677 678 679 | ** This might be done by a cron job or similar to make sure backoffice ** processing happens periodically. Or, the --poll option can be used ** to run this command as a daemon that will periodically invoke backoffice ** on a collection of repositories. ** ** If only a single repository is named and --poll is omitted, then the ** backoffice work is done in-process. But if there are multiple repositories | | | 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 | ** This might be done by a cron job or similar to make sure backoffice ** processing happens periodically. Or, the --poll option can be used ** to run this command as a daemon that will periodically invoke backoffice ** on a collection of repositories. ** ** If only a single repository is named and --poll is omitted, then the ** backoffice work is done in-process. But if there are multiple repositories ** or if --poll is used, a separate sub-process is started for each poll of ** each repository. ** ** Standard options: ** ** --debug Show what this command is doing ** ** --logfile FILE Append a log of backoffice actions onto FILE |
| ︙ | ︙ |
Changes to src/bag.c.
| ︙ | ︙ | |||
72 73 74 75 76 77 78 | free(p->a); bag_init(p); } /* ** The hash function */ | | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | free(p->a); bag_init(p); } /* ** The hash function */ #define bag_hash(i) (((u64)(i))*101) /* ** Change the size of the hash table on a bag so that ** it contains N slots ** ** Completely reconstruct the hash table from scratch. Deleted ** entries (indicated by a -1) are removed. When finished, it |
| ︙ | ︙ |
Changes to src/bisect.c.
| ︙ | ︙ | |||
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
** fossil bisect run [OPTIONS] COMMAND
**
** Invoke COMMAND (with arguments) repeatedly to perform the bisect.
**
** Options:
** -i|--interactive Prompt user for decisions rather than
** using the return code from COMMAND
*/
static void bisect_run(void){
const char *zCmd;
int isInteractive = 0;
int i;
if( g.argc<4 ){
fossil_fatal("Usage: fossil bisect run [OPTIONS] COMMAND\n");
}
for(i=3; i<g.argc-1; i++){
const char *zArg = g.argv[i];
if( zArg[0]=='-' && zArg[1]=='-' && zArg[2]!=0 ) zArg++;
if( strcmp(zArg, "-i")==0 || strcmp(zArg, "-interactive")==0 ){
isInteractive = 1;
continue;
}
fossil_fatal("unknown command-line option: \"%s\"\n", g.argv[i]);
}
zCmd = g.argv[i];
if( db_int(0, "SELECT count(*) FROM vvar"
" WHERE name IN ('bisect-good','bisect-bad')")!=2 ){
fossil_fatal("need good/bad boundaries to use \"fossil bisect run\"");
| > > > > > > | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
** fossil bisect run [OPTIONS] COMMAND
**
** Invoke COMMAND (with arguments) repeatedly to perform the bisect.
**
** Options:
** -i|--interactive Prompt user for decisions rather than
** using the return code from COMMAND
** --ii Like -i but also pause after showing
** the status after each step.
*/
static void bisect_run(void){
const char *zCmd;
int isInteractive = 0;
int i;
if( g.argc<4 ){
fossil_fatal("Usage: fossil bisect run [OPTIONS] COMMAND\n");
}
for(i=3; i<g.argc-1; i++){
const char *zArg = g.argv[i];
if( zArg[0]=='-' && zArg[1]=='-' && zArg[2]!=0 ) zArg++;
if( strcmp(zArg, "-i")==0 || strcmp(zArg, "-interactive")==0 ){
isInteractive = 1;
continue;
}
if( strcmp(zArg, "-ii")==0 ){
isInteractive = 2;
continue;
}
fossil_fatal("unknown command-line option: \"%s\"\n", g.argv[i]);
}
zCmd = g.argv[i];
if( db_int(0, "SELECT count(*) FROM vvar"
" WHERE name IN ('bisect-good','bisect-bad')")!=2 ){
fossil_fatal("need good/bad boundaries to use \"fossil bisect run\"");
|
| ︙ | ︙ | |||
458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
blob_append(&cmd, " bisect skip", -1);
}else{
blob_append(&cmd, " bisect bad", -1);
}
fossil_print("%s\n", blob_str(&cmd));
fossil_system(blob_str(&cmd));
blob_reset(&cmd);
}
}
/*
** COMMAND: bisect
**
** Usage: %fossil bisect SUBCOMMAND ...
| > > > > > > > > > > > > | 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 |
blob_append(&cmd, " bisect skip", -1);
}else{
blob_append(&cmd, " bisect bad", -1);
}
fossil_print("%s\n", blob_str(&cmd));
fossil_system(blob_str(&cmd));
blob_reset(&cmd);
if( isInteractive>=2 && db_lget_int("bisect-complete", 0)==0 ){
int n;
char *z;
Blob in;
int bContinue = 1;
prompt_user("Run testcase again? (Y)es or No: ", &in);
n = blob_size(&in);
z = blob_str(&in);
if( n>0 && sqlite3_strnicmp("no", z, n)==0 ) bContinue = 0;
blob_reset(&in);
if( !bContinue ) break;
}
}
}
/*
** COMMAND: bisect
**
** Usage: %fossil bisect SUBCOMMAND ...
|
| ︙ | ︙ |
Changes to src/blob.c.
| ︙ | ︙ | |||
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
/*
** Other replacements for ctype.h functions.
*/
int fossil_islower(char c){ return c>='a' && c<='z'; }
int fossil_isupper(char c){ return c>='A' && c<='Z'; }
int fossil_isdigit(char c){ return c>='0' && c<='9'; }
int fossil_isxdigit(char c){ return (c>='0' && c<='9') || (c>='a' && c<='f'); }
int fossil_tolower(char c){
return fossil_isupper(c) ? c - 'A' + 'a' : c;
}
int fossil_toupper(char c){
return fossil_islower(c) ? c - 'a' + 'A' : c;
}
int fossil_isalpha(char c){
| > > > | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
/*
** Other replacements for ctype.h functions.
*/
int fossil_islower(char c){ return c>='a' && c<='z'; }
int fossil_isupper(char c){ return c>='A' && c<='Z'; }
int fossil_isdigit(char c){ return c>='0' && c<='9'; }
int fossil_isxdigit(char c){ return (c>='0' && c<='9') || (c>='a' && c<='f'); }
int fossil_isXdigit(char c){
return (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f');
}
int fossil_tolower(char c){
return fossil_isupper(c) ? c - 'A' + 'a' : c;
}
int fossil_toupper(char c){
return fossil_islower(c) ? c - 'a' + 'A' : c;
}
int fossil_isalpha(char c){
|
| ︙ | ︙ | |||
1549 1550 1551 1552 1553 1554 1555 |
z[--j] = z[i];
}
}
}
/*
** ASCII (for reference):
| | | | | | | | | | | | 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 |
z[--j] = z[i];
}
}
}
/*
** ASCII (for reference):
** x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf
** 0x ^` ^a ^b ^c ^d ^e ^f ^g \b \t \n () \f \r ^n ^o
** 1x ^p ^q ^r ^s ^t ^u ^v ^w ^x ^y ^z ^{ ^| ^} ^~ ^
** 2x () ! " # $ % & ' ( ) * + , - . /
** 3x 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
** 4x @ A B C D E F G H I J K L M N O
** 5x P Q R S T U V W X Y Z [ \ ] ^ _
** 6x ` a b c d e f g h i j k l m n o
** 7x p q r s t u v w x y z { | } ~ ^_
*/
/*
** Meanings for bytes in a filename:
**
** 0 Ordinary character. No encoding required
** 1 Needs to be escaped
** 2 Illegal character. Do not allow in a filename
** 3 First byte of a 2-byte UTF-8
** 4 First byte of a 3-byte UTF-8
** 5 First byte of a 4-byte UTF-8
*/
static const char aSafeChar[256] = {
#ifdef _WIN32
/* Windows
** Prohibit: all control characters, including tab, \r and \n.
** Escape: (space) " # $ % & ' ( ) * ; < > ? [ ] ^ ` { | }
*/
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x */
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 1x */
1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 2x */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, /* 3x */
|
| ︙ | ︙ | |||
1663 1664 1665 1666 1667 1668 1669 |
blob_token(pBlob, &bad);
fossil_fatal("the [%s] argument to the \"%s\" command contains "
"an illegal UTF-8 character",
zIn, blob_str(&bad));
}
i += x-2;
}
| | | 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 |
blob_token(pBlob, &bad);
fossil_fatal("the [%s] argument to the \"%s\" command contains "
"an illegal UTF-8 character",
zIn, blob_str(&bad));
}
i += x-2;
}
}
}
/* Separate from the previous argument by a space */
if( n>0 && !fossil_isspace(z[n-1]) ){
blob_append_char(pBlob, ' ');
}
|
| ︙ | ︙ | |||
1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 |
blob_append_char(pBlob, '\\');
}else if( zIn[0]=='/' ){
blob_append_char(pBlob, '.');
}
for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
blob_append_char(pBlob, (char)c);
if( c=='"' ) blob_append_char(pBlob, '"');
}
blob_append_char(pBlob, '"');
#else
/* Quoting strategy for unix:
** If the name does not contain ', then surround the whole thing
** with '...'. If there is one or more ' characters within the
** name, then put \ before each special character.
| > > | 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 |
blob_append_char(pBlob, '\\');
}else if( zIn[0]=='/' ){
blob_append_char(pBlob, '.');
}
for(i=0; (c = (unsigned char)zIn[i])!=0; i++){
blob_append_char(pBlob, (char)c);
if( c=='"' ) blob_append_char(pBlob, '"');
if( c=='\\' ) blob_append_char(pBlob, '\\');
if( c=='%' && isFilename ) blob_append(pBlob, "%cd:~,%", 7);
}
blob_append_char(pBlob, '"');
#else
/* Quoting strategy for unix:
** If the name does not contain ', then surround the whole thing
** with '...'. If there is one or more ' characters within the
** name, then put \ before each special character.
|
| ︙ | ︙ | |||
1793 1794 1795 1796 1797 1798 1799 |
}
#ifdef _WIN32
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='\\' ) zArg += 2;
#else
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='/' ) zArg += 2;
#endif
if( strcmp(zBuf, zArg)!=0 ){
| | | 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 |
}
#ifdef _WIN32
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='\\' ) zArg += 2;
#else
if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='/' ) zArg += 2;
#endif
if( strcmp(zBuf, zArg)!=0 ){
fossil_fatal("argument disagree: \"%s\" (%s) versus \"%s\"",
zBuf, g.argv[i-1], zArg);
}
continue;
}else if( fossil_strcmp(zArg, "--fuzz")==0 && i+1<g.argc ){
int n = atoi(g.argv[++i]);
int j;
for(j=0; j<n; j++){
|
| ︙ | ︙ |
Changes to src/branch.c.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 | const char *zColor; /* Color of the new branch */ Blob branch; /* manifest for the new branch */ Manifest *pParent; /* Parsed parent manifest */ Blob mcksum; /* Self-checksum on the manifest */ const char *zDateOvrd; /* Override date string */ const char *zUserOvrd; /* Override user name */ int isPrivate = 0; /* True if the branch should be private */ | < > < < < < | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
const char *zColor; /* Color of the new branch */
Blob branch; /* manifest for the new branch */
Manifest *pParent; /* Parsed parent manifest */
Blob mcksum; /* Self-checksum on the manifest */
const char *zDateOvrd; /* Override date string */
const char *zUserOvrd; /* Override user name */
int isPrivate = 0; /* True if the branch should be private */
noSign = find_option("nosign","",0)!=0;
if( find_option("nosync",0,0) ) g.fNoSync = 1;
zColor = find_option("bgcolor","c",1);
isPrivate = find_option("private",0,0)!=0;
zDateOvrd = find_option("date-override",0,1);
zUserOvrd = find_option("user-override",0,1);
verify_all_options();
if( g.argc<5 ){
usage("new BRANCH-NAME BASIS ?OPTIONS?");
}
|
| ︙ | ︙ | |||
150 151 152 153 154 155 156 |
blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
}
manifest_destroy(pParent);
/* Add the symbolic branch name and the "branch" tag to identify
** this as a new branch */
if( content_is_private(rootid) ) isPrivate = 1;
| < | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
}
manifest_destroy(pParent);
/* Add the symbolic branch name and the "branch" tag to identify
** this as a new branch */
if( content_is_private(rootid) ) isPrivate = 1;
if( zColor!=0 ){
blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
}
blob_appendf(&branch, "T *branch * %F\n", zBranch);
blob_appendf(&branch, "T *sym-%F *\n", zBranch);
if( isPrivate ){
noSign = 1;
|
| ︙ | ︙ | |||
338 339 340 341 342 343 344 |
blob_append_sql(&sql,
"SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE 1"
);
break;
}
case BRL_OPEN_ONLY: {
blob_append_sql(&sql,
| | > | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
blob_append_sql(&sql,
"SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE 1"
);
break;
}
case BRL_OPEN_ONLY: {
blob_append_sql(&sql,
"SELECT name, isprivate, mtime, mergeto FROM tmp_brlist "
" WHERE NOT isclosed"
);
break;
}
}
if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate");
if( brFlags & BRL_MERGED ) blob_append_sql(&sql, " AND mergeto IS NOT NULL");
if( zBrNameGlob ) blob_append_sql(&sql, " AND (name GLOB %Q)", zBrNameGlob);
|
| ︙ | ︙ | |||
662 663 664 665 666 667 668 | ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS? ** ** Create a new branch BRANCH-NAME off of check-in BASIS. ** ** Options: ** --private Branch is private (i.e., remains local) ** --bgcolor COLOR Use COLOR instead of automatic background | < < > | 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 | ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS? ** ** Create a new branch BRANCH-NAME off of check-in BASIS. ** ** Options: ** --private Branch is private (i.e., remains local) ** --bgcolor COLOR Use COLOR instead of automatic background ** --nosign Do not sign contents on this branch ** --nosync Do not auto-sync prior to creating the branch ** --date-override DATE DATE to use instead of 'now' ** --user-override USER USER to use instead of the current default ** ** DATE may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be ** replaced by a space, and it may also name a timezone offset ** from UTC as "-HH:MM" (westward) or "+HH:MM" (eastward). |
| ︙ | ︙ | |||
770 771 772 773 774 775 776 |
int isPriv = db_column_int(&q, 1)==1;
const char *zMergeTo = db_column_text(&q, 2);
int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
const char *zUsers = db_column_text(&q, 3);
if( (brFlags & BRL_MERGED) && fossil_strcmp(zCurrent,zMergeTo)!=0 ){
continue;
}
| | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 |
int isPriv = db_column_int(&q, 1)==1;
const char *zMergeTo = db_column_text(&q, 2);
int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
const char *zUsers = db_column_text(&q, 3);
if( (brFlags & BRL_MERGED) && fossil_strcmp(zCurrent,zMergeTo)!=0 ){
continue;
}
if( (brFlags & BRL_UNMERGED) && (fossil_strcmp(zCurrent,zMergeTo)==0
|| isCur) ){
continue;
}
blob_appendf(&txt, "%s%s%s",
( (brFlags & BRL_PRIVATE) ? " " : ( isPriv ? "#" : " ") ),
(isCur ? "* " : " "), zBr);
if( nUsers ){
|
| ︙ | ︙ | |||
884 885 886 887 888 889 890 |
}
}
if( zBgClr && zBgClr[0] && show_colors ){
@ <tr style="background-color:%s(zBgClr)">
}else{
@ <tr>
}
| | | 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 |
}
}
if( zBgClr && zBgClr[0] && show_colors ){
@ <tr style="background-color:%s(zBgClr)">
}else{
@ <tr>
}
@ <td>%z(href("%R/timeline?r=%T",zBranch))%h(zBranch)</a><input
@ type="checkbox" disabled="disabled"/></td>
@ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td>
@ <td>%d(nCkin)</td>
fossil_free(zAge);
@ <td>%s(isClosed?"closed":"")</td>
if( zMergeTo ){
@ <td>merged into
|
| ︙ | ︙ |
Changes to src/browse.c.
| ︙ | ︙ | |||
356 357 358 359 360 361 362 | /* Generate a multi-column table listing the contents of zD[] ** directory. */ mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/"); if( mxLen<12 ) mxLen = 12; mxLen += (mxLen+9)/10; | | | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
/* Generate a multi-column table listing the contents of zD[]
** directory.
*/
mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
if( mxLen<12 ) mxLen = 12;
mxLen += (mxLen+9)/10;
db_prepare(&q,
"SELECT x, u FROM localfiles ORDER BY x COLLATE uintnocase /*scan*/");
@ <div class="columns files" style="columns: %d(mxLen)ex auto">
@ <ul class="browser">
while( db_step(&q)==SQLITE_ROW ){
const char *zFN;
zFN = db_column_text(&q, 0);
if( zFN[0]=='/' ){
|
| ︙ | ︙ | |||
469 470 471 472 473 474 475 | FileTreeNode *pSibling; /* Next element in the same subdirectory */ FileTreeNode *pChild; /* List of child nodes */ FileTreeNode *pLastChild; /* Last child on the pChild list */ char *zName; /* Name of this entry. The "tail" */ char *zFullName; /* Full pathname of this entry */ char *zUuid; /* Artifact hash of this file. May be NULL. */ double mtime; /* Modification time for this entry */ | | > | 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 |
FileTreeNode *pSibling; /* Next element in the same subdirectory */
FileTreeNode *pChild; /* List of child nodes */
FileTreeNode *pLastChild; /* Last child on the pChild list */
char *zName; /* Name of this entry. The "tail" */
char *zFullName; /* Full pathname of this entry */
char *zUuid; /* Artifact hash of this file. May be NULL. */
double mtime; /* Modification time for this entry */
double sortBy; /* Either mtime or size, depending on desired
sort order */
int iSize; /* Size for this entry */
unsigned nFullName; /* Length of zFullName */
unsigned iLevel; /* Levels of parent directories */
};
/*
** A complete file hierarchy
|
| ︙ | ︙ | |||
506 507 508 509 510 511 512 |
const char *zUuid, /* Hash of the file. Might be NULL. */
double mtime, /* Modification time for this entry */
int size, /* Size for this entry */
int sortOrder /* 0: filename, 1: mtime, 2: size */
){
int i;
FileTreeNode *pParent; /* Parent (directory) of the next node to insert */
| | | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 |
const char *zUuid, /* Hash of the file. Might be NULL. */
double mtime, /* Modification time for this entry */
int size, /* Size for this entry */
int sortOrder /* 0: filename, 1: mtime, 2: size */
){
int i;
FileTreeNode *pParent; /* Parent (directory) of the next node to insert */
/* Make pParent point to the most recent ancestor of zPath, or
** NULL if there are no prior entires that are a container for zPath.
*/
pParent = pTree->pLast;
while( pParent!=0 &&
( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
|| zPath[pParent->nFullName]!='/' )
|
| ︙ | ︙ | |||
857 858 859 860 861 862 863 864 |
if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue;
tree_add_node(&sTree, zFile, zUuid, mtime, size, sortOrder);
}
db_finalize(&q);
}else{
Stmt q;
db_prepare(&q,
"SELECT\n"
| > > > > > > | | | | | | > | 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 |
if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue;
tree_add_node(&sTree, zFile, zUuid, mtime, size, sortOrder);
}
db_finalize(&q);
}else{
Stmt q;
db_prepare(&q,
"WITH mx(fnid,fid,mtime) AS (\n"
" SELECT fnid, fid, max(event.mtime)\n"
" FROM mlink, event\n"
" WHERE event.objid=mlink.mid\n"
" GROUP BY 1\n"
")\n"
"SELECT\n"
" filename.name,\n"
" blob.uuid,\n"
" blob.size,\n"
" mx.mtime\n"
"FROM mx\n"
" LEFT JOIN filename ON filename.fnid=mx.fnid\n"
" LEFT JOIN blob ON blob.rid=mx.fid\n"
" ORDER BY 1 COLLATE uintnocase;");
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const char *zUuid = db_column_text(&q,1);
int size = db_column_int(&q,2);
double mtime = db_column_double(&q,3);
if( nD>0 && (fossil_strncmp(zName, zD, nD-1)!=0 || zName[nD-1]!='/') ){
|
| ︙ | ︙ | |||
1152 1153 1154 1155 1156 1157 1158 |
const char *zNow; /* Time of check-in */
int isBranchCI; /* name= is a branch name */
int showId = PB("showid");
Stmt q1, q2;
double baseTime;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
| | | 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 |
const char *zNow; /* Time of check-in */
int isBranchCI; /* name= is a branch name */
int showId = PB("showid");
Stmt q1, q2;
double baseTime;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( exclude_spiders(0) ) return;
zName = P("name");
if( zName==0 ) zName = "tip";
rid = symbolic_name_to_rid(zName, "ci");
if( rid==0 ){
fossil_fatal("not a valid check-in: %s", zName);
}
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
|
| ︙ | ︙ |
Changes to src/builtin.c.
| ︙ | ︙ | |||
519 520 521 522 523 524 525 | builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur; return pCur->iRowid>count(aBuiltinFiles); } /* ** This method is called to "rewind" the builtinVtab_cursor object back ** to the first row of output. This method is always called at least | | | | 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur;
return pCur->iRowid>count(aBuiltinFiles);
}
/*
** This method is called to "rewind" the builtinVtab_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to builtinVtabColumn() or builtinVtabRowid() or
** builtinVtabEof().
*/
static int builtinVtabFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
builtinVtab_cursor *pCur = (builtinVtab_cursor *)pVtabCursor;
pCur->iRowid = 1;
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
548 549 550 551 552 553 554 |
){
pIdxInfo->estimatedCost = (double)count(aBuiltinFiles);
pIdxInfo->estimatedRows = count(aBuiltinFiles);
return SQLITE_OK;
}
/*
| | | 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
){
pIdxInfo->estimatedCost = (double)count(aBuiltinFiles);
pIdxInfo->estimatedRows = count(aBuiltinFiles);
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module builtinVtabModule = {
/* iVersion */ 0,
/* xCreate */ 0, /* The builtin vtab is eponymous and read-only */
/* xConnect */ builtinVtabConnect,
/* xBestIndex */ builtinVtabBestIndex,
|
| ︙ | ︙ | |||
814 815 816 817 818 819 820 | ** per-page basis. In this case, all arguments are ignored! ** ** This function has an internal mapping of the dependencies for each ** of the known fossil.XYZ.js modules and ensures that the ** dependencies also get queued (recursively) and that each module is ** queued only once. ** | | | 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 | ** per-page basis. In this case, all arguments are ignored! ** ** This function has an internal mapping of the dependencies for each ** of the known fossil.XYZ.js modules and ensures that the ** dependencies also get queued (recursively) and that each module is ** queued only once. ** ** If passed a name which is not a base fossil module name then it ** will fail fatally! ** ** DO NOT use this for loading fossil.page.*.js: use ** builtin_request_js() for those. ** ** If the current JS delivery mode is *not* JS_BUNDLED then this ** function queues up a request for each given module and its known |
| ︙ | ︙ |
Changes to src/bundle.c.
| ︙ | ︙ | |||
144 145 146 147 148 149 150 |
db_finalize(&q);
}
}
/*
** Implement the "fossil bundle append BUNDLE FILE..." command. Add
** the named files into the BUNDLE. Create the BUNDLE if it does not
| | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
db_finalize(&q);
}
}
/*
** Implement the "fossil bundle append BUNDLE FILE..." command. Add
** the named files into the BUNDLE. Create the BUNDLE if it does not
** already exist.
*/
static void bundle_append_cmd(void){
Blob content, hash;
int i;
Stmt q;
verify_all_options();
|
| ︙ | ︙ | |||
537 538 539 540 541 542 543 |
** Write elements of a bundle on standard output
*/
static void bundle_cat_cmd(void){
int i;
Blob x;
verify_all_options();
if( g.argc<5 ) usage("cat BUNDLE HASH...");
| | | 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 |
** Write elements of a bundle on standard output
*/
static void bundle_cat_cmd(void){
int i;
Blob x;
verify_all_options();
if( g.argc<5 ) usage("cat BUNDLE HASH...");
bundle_attach_file(g.argv[3], "b1", 0);
blob_zero(&x);
for(i=4; i<g.argc; i++){
int blobid = db_int(0,"SELECT blobid FROM bblob WHERE uuid LIKE '%q%%'",
g.argv[i]);
if( blobid==0 ){
fossil_fatal("no such artifact in bundle: %s", g.argv[i]);
}
|
| ︙ | ︙ | |||
567 568 569 570 571 572 573 |
*/
static void bundle_import_cmd(void){
int forceFlag = find_option("force","f",0)!=0;
int isPriv = find_option("publish",0,0)==0;
char *zMissingDeltas;
verify_all_options();
if ( g.argc!=4 ) usage("import BUNDLE ?OPTIONS?");
| | | 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 |
*/
static void bundle_import_cmd(void){
int forceFlag = find_option("force","f",0)!=0;
int isPriv = find_option("publish",0,0)==0;
char *zMissingDeltas;
verify_all_options();
if ( g.argc!=4 ) usage("import BUNDLE ?OPTIONS?");
bundle_attach_file(g.argv[3], "b1", 0);
/* Only import a bundle that was generated from a repo with the same
** project code, unless the --force flag is true */
if( !forceFlag ){
if( !db_exists("SELECT 1 FROM config, bconfig"
" WHERE config.name='project-code'"
" AND bconfig.bcname='project-code'"
|
| ︙ | ︙ |
Changes to src/cache.c.
| ︙ | ︙ | |||
311 312 313 314 315 316 317 |
sqlite3_exec(db, "DELETE FROM cache; DELETE FROM blob; VACUUM;",0,0,0);
sqlite3_close(db);
fossil_print("cache cleared\n");
}else{
fossil_print("nothing to clear; cache does not exist\n");
}
}else if( strncmp(zCmd, "list", nCmd)==0
| | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
sqlite3_exec(db, "DELETE FROM cache; DELETE FROM blob; VACUUM;",0,0,0);
sqlite3_close(db);
fossil_print("cache cleared\n");
}else{
fossil_print("nothing to clear; cache does not exist\n");
}
}else if( strncmp(zCmd, "list", nCmd)==0
|| strncmp(zCmd, "ls", nCmd)==0
|| strncmp(zCmd, "status", nCmd)==0
){
db = cacheOpen(0);
if( db==0 ){
fossil_print("cache does not exist\n");
}else{
int nEntry = 0;
|
| ︙ | ︙ | |||
430 431 432 433 434 435 436 |
@ hit-count: %d(sqlite3_column_int(pStmt,2))
@ last-access: %s(sqlite3_column_text(pStmt,3)) \
if( zHash ){
@ %z(href("%R/timeline?c=%S",zHash))check-in</a>\
fossil_free(zHash);
}
@ </p></li>
| | | 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
@ hit-count: %d(sqlite3_column_int(pStmt,2))
@ last-access: %s(sqlite3_column_text(pStmt,3)) \
if( zHash ){
@ %z(href("%R/timeline?c=%S",zHash))check-in</a>\
fossil_free(zHash);
}
@ </p></li>
}
sqlite3_finalize(pStmt);
@ </ol>
}
zDbName = cacheName();
bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE));
@ <p>
|
| ︙ | ︙ |
Changes to src/capabilities.c.
| ︙ | ︙ | |||
399 400 401 402 403 404 405 |
@ <th>Unversioned Content</th></tr>
while( db_step(&q)==SQLITE_ROW ){
const char *zId = db_column_text(&q, 0);
const char *zCap = db_column_text(&q, 1);
int n = db_column_int(&q, 3);
int eType;
static const char *const azType[] = { "off", "read", "write" };
| | | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
@ <th>Unversioned Content</th></tr>
while( db_step(&q)==SQLITE_ROW ){
const char *zId = db_column_text(&q, 0);
const char *zCap = db_column_text(&q, 1);
int n = db_column_int(&q, 3);
int eType;
static const char *const azType[] = { "off", "read", "write" };
static const char *const azClass[] =
{ "capsumOff", "capsumRead", "capsumWrite" };
if( n==0 ) continue;
/* Code */
if( db_column_int(&q,2)<10 ){
@ <tr><th align="right"><tt>"%h(zId)"</tt></th>
|
| ︙ | ︙ |
Changes to src/captcha.c.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 | ** scripting attacks more difficult. */ #include "config.h" #include <assert.h> #include "captcha.h" #if INTERFACE | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
** scripting attacks more difficult.
*/
#include "config.h"
#include <assert.h>
#include "captcha.h"
#if INTERFACE
#define CAPTCHA 2 /* Which captcha rendering to use */
#endif
/*
** Convert a hex digit into a value between 0 and 15
*/
int hex_digit_value(char c){
if( c>='0' && c<='9' ){
|
| ︙ | ︙ | |||
67 68 69 70 71 72 73 |
/*
** Render an 8-character hexadecimal string as ascii art.
** Space to hold the result is obtained from malloc() and should be freed
** by the caller.
*/
char *captcha_render(const char *zPw){
| | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | 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 134 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 |
/*
** Render an 8-character hexadecimal string as ascii art.
** Space to hold the result is obtained from malloc() and should be freed
** by the caller.
*/
char *captcha_render(const char *zPw){
char *z = fossil_malloc( 9*12*3*strlen(zPw) + 8 );
int i, j, k, m;
k = 0;
for(i=0; i<6; i++){
for(j=0; zPw[j]; j++){
unsigned char v = hex_digit_value(zPw[j]);
v = (aFont1[v] >> ((5-i)*4)) & 0xf;
for(m=8; m>=1; m = m>>1){
if( v & m ){
z[k++] = 0xe2;
z[k++] = 0x96;
z[k++] = 0x88;
z[k++] = 0xe2;
z[k++] = 0x96;
z[k++] = 0x88;
}else{
z[k++] = ' ';
z[k++] = ' ';
}
}
z[k++] = ' ';
z[k++] = ' ';
}
z[k++] = '\n';
}
z[k] = 0;
return z;
}
#endif /* CAPTCHA==1 */
#if CAPTCHA==2
/*
** A 5x7 pixel bitmap font for hexadecimal digits
*/
static const unsigned char aFont2[] = {
/* 0 */ 0x0e, 0x13, 0x15, 0x19, 0x11, 0x11, 0x0e,
/* 1 */ 0x02, 0x06, 0x0A, 0x02, 0x02, 0x02, 0x02,
/* 2 */ 0x0e, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1f,
/* 3 */ 0x0e, 0x11, 0x01, 0x06, 0x01, 0x11, 0x0e,
/* 4 */ 0x02, 0x06, 0x0A, 0x12, 0x1f, 0x02, 0x02,
/* 5 */ 0x1f, 0x10, 0x1e, 0x01, 0x01, 0x11, 0x0e,
/* 6 */ 0x0e, 0x11, 0x10, 0x1e, 0x11, 0x11, 0x0e,
/* 7 */ 0x1f, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08,
/* 8 */ 0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e,
/* 9 */ 0x0e, 0x11, 0x11, 0x0f, 0x01, 0x11, 0x0e,
/* A */ 0x0e, 0x11, 0x11, 0x11, 0x1f, 0x11, 0x11,
/* B */ 0x1e, 0x11, 0x11, 0x1e, 0x11, 0x11, 0x1e,
/* C */ 0x0e, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0e,
/* D */ 0x1c, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1c,
/* E */ 0x1f, 0x10, 0x10, 0x1c, 0x10, 0x10, 0x1f,
/* F */ 0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x10,
};
/*
** Render an 8-character hexadecimal string as ascii art.
** Space to hold the result is obtained from malloc() and should be freed
** by the caller.
*/
char *captcha_render(const char *zPw){
char *z = fossil_malloc( 160*strlen(zPw) + 9 );
int i, j, k, m;
k = 0;
for(i=0; i<7; i++){
for(j=0; zPw[j]; j++){
unsigned char v = hex_digit_value(zPw[j]);
v = aFont2[v*7+i];
for(m=16; m>=1; m = m>>1){
if( v & m ){
z[k++] = 0xe2;
z[k++] = 0x96;
z[k++] = 0x88;
z[k++] = 0xe2;
z[k++] = 0x96;
z[k++] = 0x88;
}else{
z[k++] = ' ';
z[k++] = ' ';
}
}
z[k++] = ' ';
z[k++] = ' ';
}
z[k++] = '\n';
}
z[k] = 0;
return z;
}
#endif /* CAPTCHA==2 */
#if CAPTCHA==3
static const char *const azFont3[] = {
/* 0 */
" __ ",
" / \\ ",
"| () |",
" \\__/ ",
/* 1 */
|
| ︙ | ︙ | |||
208 209 210 211 212 213 214 |
int i, j, k, m;
const char *zChar;
k = 0;
for(i=0; i<4; i++){
for(j=0; zPw[j]; j++){
unsigned char v = hex_digit_value(zPw[j]);
| | | | | | 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
int i, j, k, m;
const char *zChar;
k = 0;
for(i=0; i<4; i++){
for(j=0; zPw[j]; j++){
unsigned char v = hex_digit_value(zPw[j]);
zChar = azFont3[4*v + i];
for(m=0; zChar[m]; m++){
z[k++] = zChar[m];
}
}
z[k++] = '\n';
}
z[k] = 0;
return z;
}
#endif /* CAPTCHA==3 */
#if CAPTCHA==4
static const char *const azFont4[] = {
/* 0 */
" ___ ",
" / _ \\ ",
"| | | |",
"| | | |",
"| |_| |",
" \\___/ ",
|
| ︙ | ︙ | |||
396 397 398 399 400 401 402 |
case 0x4a:
y = 1;
break;
default:
y = 0;
break;
}
| | | | 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 |
case 0x4a:
y = 1;
break;
default:
y = 0;
break;
}
zChar = azFont4[6*v + i];
while( y && zChar[0]==' ' ){ y--; zChar++; }
while( y && z[k-1]==' ' ){ y--; k--; }
for(m=0; zChar[m]; m++){
z[k++] = zChar[m];
}
}
z[k++] = '\n';
}
z[k] = 0;
return z;
}
#endif /* CAPTCHA==4 */
/*
** COMMAND: test-captcha
**
** Render an ASCII-art captcha for numbers given on the command line.
*/
void test_captcha(void){
|
| ︙ | ︙ | |||
442 443 444 445 446 447 448 449 450 451 452 453 454 |
*/
unsigned int captcha_seed(void){
unsigned int x;
sqlite3_randomness(sizeof(x), &x);
x &= 0x7fffffff;
return x;
}
/*
** Translate a captcha seed value into the captcha password string.
** The returned string is static and overwritten on each call to
** this function.
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | > | 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 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 |
*/
unsigned int captcha_seed(void){
unsigned int x;
sqlite3_randomness(sizeof(x), &x);
x &= 0x7fffffff;
return x;
}
/* The SQL that will rotate the the captcha-secret. */
static const char captchaSecretRotationSql[] =
@ SAVEPOINT rotate;
@ DELETE FROM config
@ WHERE name GLOB 'captcha-secret-*'
@ AND mtime<unixepoch('now','-6 hours');
@ UPDATE config
@ SET name=format('captcha-secret-%%d',substr(name,16)+1)
@ WHERE name GLOB 'captcha-secret-*';
@ UPDATE config
@ SET name='captcha-secret-1', mtime=unixepoch()
@ WHERE name='captcha-secret';
@ REPLACE INTO config(name,value,mtime)
@ VALUES('captcha-secret',%Q,unixepoch());
@ RELEASE rotate;
;
/*
** Create a new random captcha-secret. Rotate the old one into
** the captcha-secret-N backups. Purge captch-secret-N backups
** older than 6 hours.
**
** Do this on the current database and in all other databases of
** the same login group.
*/
void captcha_secret_rotate(void){
char *zNew = db_text(0, "SELECT lower(hex(randomblob(20)))");
char *zSql = mprintf(captchaSecretRotationSql/*works-like:"%Q"*/, zNew);
char *zErrs = 0;
fossil_free(zNew);
db_unprotect(PROTECT_CONFIG);
db_begin_transaction();
sqlite3_exec(g.db, zSql, 0, 0, &zErrs);
db_protect_pop();
if( zErrs && zErrs[0] ){
db_rollback_transaction();
fossil_fatal("Unable to rotate captcha-secret\n%s\nERROR: %s\n",
zSql, zErrs);
}
db_end_transaction(0);
login_group_sql(zSql, "", "", &zErrs);
if( zErrs ){
sqlite3_free(zErrs); /* Silently ignore errors on other repos */
}
fossil_free(zSql);
}
/*
** Return the value of the N-th more recent captcha-secret. The
** most recent captch-secret is 0. Others are prior captcha-secrets
** that have expired, but are retained for a limited period of time
** so that pending anonymous login cookies and/or captcha dialogs
** don't malfunction when the captcha-secret changes.
**
** Clients should start by using the 0-th captcha-secret. Only if
** that one does not work should they advance to 1 and 2 and so forth,
** until this routine returns a NULL pointer.
**
** The value returned is a string obtained from fossil_malloc() and
** should be freed by the caller.
**
** The 0-th captcha secret is the value of Config.Name='captcha-secret'.
** For N>0, the value is in Config.Name='captcha-secret-$N'.
*/
char *captcha_secret(int N){
if( N==0 ){
return db_text(0, "SELECT value FROM config WHERE name='captcha-secret'");
}else{
return db_text(0,
"SELECT value FROM config"
" WHERE name='captcha-secret-%d'"
" AND mtime>unixepoch('now','-6 hours')", N);
}
}
/*
** Translate a captcha seed value into the captcha password string.
** The returned string is static and overwritten on each call to
** this function.
**
** Use the N-th captcha secret to compute the password. When N==0,
** a valid password is always returned. A new captcha-secret will
** be created if necessary. But for N>0, the return value might
** be NULL to indicate that there is no N-th captcha-secret.
*/
const char *captcha_decode(unsigned int seed, int N){
char *zSecret;
const char *z;
Blob b;
static char zRes[20];
zSecret = captcha_secret(N);
if( zSecret==0 ){
if( N>0 ) return 0;
db_unprotect(PROTECT_CONFIG);
db_multi_exec(
"REPLACE INTO config(name,value)"
" VALUES('captcha-secret', lower(hex(randomblob(20))));"
);
db_protect_pop();
zSecret = captcha_secret(0);
assert( zSecret!=0 );
}
blob_init(&b, 0, 0);
blob_appendf(&b, "%s-%x", zSecret, seed);
sha1sum_blob(&b, &b);
z = blob_buffer(&b);
memcpy(zRes, z, 8);
zRes[8] = 0;
fossil_free(zSecret);
return zRes;
}
/*
** Return true if a CAPTCHA is required for editing wiki or tickets or for
** adding attachments.
**
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
*/
int captcha_is_correct(int bAlwaysNeeded){
const char *zSeed;
const char *zEntered;
const char *zDecode;
char z[30];
int i;
if( !bAlwaysNeeded && !captcha_needed() ){
return 1; /* No captcha needed */
}
zSeed = P("captchaseed");
if( zSeed==0 ) return 0;
zEntered = P("captcha");
if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
| > > | > | | | | | | | | > > > > > | | | | | | 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 |
*/
int captcha_is_correct(int bAlwaysNeeded){
const char *zSeed;
const char *zEntered;
const char *zDecode;
char z[30];
int i;
int n = 0;
if( !bAlwaysNeeded && !captcha_needed() ){
return 1; /* No captcha needed */
}
zSeed = P("captchaseed");
if( zSeed==0 ) return 0;
zEntered = P("captcha");
if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
do{
zDecode = captcha_decode((unsigned int)atoi(zSeed), n++);
if( zDecode==0 ) return 0;
assert( strlen(zDecode)==8 );
for(i=0; i<8; i++){
char c = zEntered[i];
if( c>='A' && c<='F' ) c += 'a' - 'A';
if( c=='O' ) c = '0';
z[i] = c;
}
}while( strncmp(zDecode,z,8)!=0 );
return 1;
}
/*
** Generate a captcha display together with the necessary hidden parameter
** for the seed and the entry box into which the user will type the text of
** the captcha. This is typically done at the very bottom of a form.
**
** This routine is a no-op if no captcha is required.
**
** Flag values:
**
** 0x01 Show the "Submit" button in the form.
** 0x02 Always generate the captcha, even if not required
*/
void captcha_generate(int mFlags){
unsigned int uSeed;
const char *zDecoded;
char *zCaptcha;
if( !captcha_needed() && (mFlags & 0x02)==0 ) return;
uSeed = captcha_seed();
zDecoded = captcha_decode(uSeed, 0);
zCaptcha = captcha_render(zDecoded);
@ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
@ %h(zCaptcha)
@ </pre>
@ Enter security code shown above:
@ <input type="hidden" name="captchaseed" value="%u(uSeed)">
@ <input type="text" name="captcha" size="8" autofocus>
if( mFlags & 0x01 ){
@ <input type="submit" value="Submit">
}
@ <br/>\
captcha_speakit_button(uSeed, 0);
@ </td></tr></table></div>
}
|
| ︙ | ︙ | |||
572 573 574 575 576 577 578 | @ audio.play(); @ } @ </script> } /* ** WEBPAGE: test-captcha | > | | | > > > > > > | < | > > > > > | | | | | | | > | > > > | > | | | > > > | | < | > | < < < > < < < < < < < | < | > | < > > | > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 |
@ audio.play();
@ }
@ </script>
}
/*
** WEBPAGE: test-captcha
**
** If the name query parameter is provided, then render the hex value of
** the name using the captcha font.
**
** Otherwise render the captcha screen. The "show-button" parameter causes
** the submit button to be rendered.
*/
void captcha_test(void){
const char *zPw = P("name");
if( zPw==0 || zPw[0]==0 ){
(void)exclude_spiders(1);
@ <hr><p>The captcha is shown above. Add a name=HEX query parameter
@ to see how HEX would be rendered in the current captcha font.
@ <h2>Debug/Testing Values:</h2>
@ <ul>
@ <li> g.isHuman = %d(g.isHuman)
@ <li> g.zLogin = %h(g.zLogin)
@ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
@ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
@ </ul>
style_finish_page();
}else{
style_set_current_feature("test");
style_header("Captcha Test");
@ <pre class="captcha">
@ %s(captcha_render(zPw))
@ </pre>
style_finish_page();
}
}
/*
** Check to see if the current request is coming from an agent that
** self-identifies as a spider.
**
** If the agent does not claim to be a spider or if the user has logged
** in (even as anonymous), then return 0 without doing anything.
**
** But if the user agent does self-identify as a spider and there is
** no login, offer a captcha challenge to allow the user agent to prove
** that he is human and return non-zero.
**
** If the bTest argument is non-zero, then show the captcha regardless of
** how the agent identifies. This is used for testing only.
*/
int exclude_spiders(int bTest){
if( !bTest ){
if( g.isHuman ) return 0; /* This user has already proven human */
if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
if( login_cookie_wellformed() ){
/* Logged into another member of the login group */
return 0;
}
}
/* This appears to be a spider. Offer the captcha */
style_set_current_feature("captcha");
style_header("I think you are a robot");
style_submenu_enable(0);
@ <form method='POST' action='%R/ityaar'>
@ <p>You seem like a robot.
@
@ <p>If you are human, you can prove that by solving the captcha below,
@ after which you will be allowed to proceed.
if( bTest ){
@ <input type="hidden" name="istest" value="1">
}
captcha_generate(3);
@ </form>
if( !bTest ){
if( P("fossil-goto")==0 ){
cgi_set_cookie("fossil-goto", cgi_reconstruct_original_url(), 0, 600);
}
cgi_append_header("X-Robot: 1\r\n");
style_finish_page();
}
return 1;
}
/*
** WEBPAGE: ityaar
**
** This is the action for the form that is the captcha. Not intended
** for external use. "ityaar" is an acronym "I Think You Are A Robot".
**
** If the captcha is correctly solved, then an anonymous login cookie
** is set. Regardless of whether or not the captcha was solved, this
** page always redirects to the fossil-goto cookie.
*/
void captcha_callback(void){
int bTest = atoi(PD("istest","0"));
if( captcha_is_correct(1) ){
if( bTest==0 ){
if( !login_cookie_wellformed() ){
/* ^^^^--- Don't overwrite a valid login on another repo! */
login_set_anon_cookie(0, 0);
}
cgi_append_header("X-Robot: 0\r\n");
}
login_redirect_to_g();
}else{
g.isHuman = 0;
(void)exclude_spiders(bTest);
if( bTest ){
@ <hr><p>Wrong code. Try again
style_finish_page();
}
}
}
/*
** Generate a WAV file that reads aloud the hex digits given by
** zHex.
*/
static void captcha_wav(const char *zHex, Blob *pOut){
int i;
|
| ︙ | ︙ | |||
684 685 686 687 688 689 690 |
** WEBPAGE: /captcha-audio
**
** Return a WAV file that pronounces the digits of the captcha that
** is determined by the seed given in the name= query parameter.
*/
void captcha_wav_page(void){
const char *zSeed = PD("name","0");
| | | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 |
** WEBPAGE: /captcha-audio
**
** Return a WAV file that pronounces the digits of the captcha that
** is determined by the seed given in the name= query parameter.
*/
void captcha_wav_page(void){
const char *zSeed = PD("name","0");
const char *zDecode = captcha_decode((unsigned int)atoi(zSeed), 0);
Blob audio;
captcha_wav(zDecode, &audio);
cgi_set_content_type("audio/wav");
cgi_set_content(&audio);
}
/*
|
| ︙ | ︙ |
Changes to src/cgi.c.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 | ** So, even though the name of this file implies that it only deals with ** CGI, in fact, the code in this file is used to interpret webpage requests ** received by a variety of means, and to generate well-formatted replies ** to those requests. ** ** The code in this file abstracts the web-request so that downstream ** modules that generate the body of the reply (based on the requested page) | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | ** So, even though the name of this file implies that it only deals with ** CGI, in fact, the code in this file is used to interpret webpage requests ** received by a variety of means, and to generate well-formatted replies ** to those requests. ** ** The code in this file abstracts the web-request so that downstream ** modules that generate the body of the reply (based on the requested page) ** do not need to know if the request is coming from CGI, direct HTTP, ** SCGI, or some other means. ** ** This module gathers information about web page request into a key/value ** store. Keys and values come from: ** ** * Query parameters ** * POST parameter |
| ︙ | ︙ | |||
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0501 # endif # include <winsock2.h> # include <ws2tcpip.h> #else # include <sys/socket.h> # include <netinet/in.h> # include <arpa/inet.h> # include <sys/times.h> # include <sys/time.h> # include <sys/wait.h> # include <sys/select.h> #endif #ifdef __EMX__ typedef int socklen_t; #endif #include <time.h> #include <stdio.h> #include <stdlib.h> | > > | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | # if !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0501 # endif # include <winsock2.h> # include <ws2tcpip.h> #else # include <sys/socket.h> # include <sys/un.h> # include <netinet/in.h> # include <arpa/inet.h> # include <sys/times.h> # include <sys/time.h> # include <sys/wait.h> # include <sys/select.h> # include <errno.h> #endif #ifdef __EMX__ typedef int socklen_t; #endif #include <time.h> #include <stdio.h> #include <stdlib.h> |
| ︙ | ︙ | |||
479 480 481 482 483 484 485 |
if( iReplyStatus<=0 ){
iReplyStatus = 200;
zReplyStatus = "OK";
}
if( g.fullHttpReply ){
if( rangeEnd>0
| | | 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 |
if( iReplyStatus<=0 ){
iReplyStatus = 200;
zReplyStatus = "OK";
}
if( g.fullHttpReply ){
if( rangeEnd>0
&& iReplyStatus==200
&& fossil_strcmp(P("REQUEST_METHOD"),"GET")==0
){
iReplyStatus = 206;
zReplyStatus = "Partial Content";
}
blob_appendf(&hdr, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus);
blob_appendf(&hdr, "Date: %s\r\n", cgi_rfc822_datestamp(time(0)));
|
| ︙ | ︙ | |||
560 561 562 563 564 565 566 |
blob_appendf(&hdr, "Content-Encoding: gzip\r\n");
blob_appendf(&hdr, "Vary: Accept-Encoding\r\n");
}
total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
if( iReplyStatus==206 ){
blob_appendf(&hdr, "Content-Range: bytes %d-%d/%d\r\n",
rangeStart, rangeEnd-1, total_size);
| | | 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
blob_appendf(&hdr, "Content-Encoding: gzip\r\n");
blob_appendf(&hdr, "Vary: Accept-Encoding\r\n");
}
total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
if( iReplyStatus==206 ){
blob_appendf(&hdr, "Content-Range: bytes %d-%d/%d\r\n",
rangeStart, rangeEnd-1, total_size);
total_size = rangeEnd - rangeStart;
}
blob_appendf(&hdr, "Content-Length: %d\r\n", total_size);
}else{
total_size = 0;
}
blob_appendf(&hdr, "\r\n");
cgi_fwrite(blob_buffer(&hdr), blob_size(&hdr));
|
| ︙ | ︙ | |||
895 896 897 898 899 900 901 902 903 904 905 906 907 908 |
if( i<nUsedQP ){
memmove(aParamQP+i, aParamQP+i+1, sizeof(*aParamQP)*(nUsedQP-i));
}
return;
}
}
}
/*
** Add an environment varaible value to the parameter set. The zName
** portion is fixed but a copy is be made of zValue.
*/
void cgi_setenv(const char *zName, const char *zValue){
cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
| > > > > > > > > > > > > > | 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 |
if( i<nUsedQP ){
memmove(aParamQP+i, aParamQP+i+1, sizeof(*aParamQP)*(nUsedQP-i));
}
return;
}
}
}
/*
** Return the number of query parameters. Cookies and environment variables
** do not count. Also, do not count the special QP "name".
*/
int cgi_qp_count(void){
int cnt = 0;
int i;
for(i=0; i<nUsedQP; i++){
if( aParamQP[i].isQP && fossil_strcmp(aParamQP[i].zName,"name")!=0 ) cnt++;
}
return cnt;
}
/*
** Add an environment varaible value to the parameter set. The zName
** portion is fixed but a copy is be made of zValue.
*/
void cgi_setenv(const char *zName, const char *zValue){
cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
|
| ︙ | ︙ | |||
1236 1237 1238 1239 1240 1241 1242 |
return;
}
}
fputs(z, pLog);
}
/* Forward declaration */
| | | | 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 |
return;
}
}
fputs(z, pLog);
}
/* Forward declaration */
static NORETURN void malformed_request(const char *zMsg, ...);
/*
** Checks the QUERY_STRING environment variable, sets it up
** via add_param_list() and, if found, applies its "skin"
** setting. Returns 0 if no QUERY_STRING is set, 1 if it is,
** and 2 if it sets the skin (in which case the cookie may
** still need flushing by the page, via cookie_render()).
*/
int cgi_setup_query_string(void){
int rc = 0;
char * z = (char*)P("QUERY_STRING");
if( z ){
++rc;
z = fossil_strdup(z);
add_param_list(z, '&');
z = (char*)P("skin");
if( z ){
char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
++rc;
if( !zErr && P("once")==0 ){
cookie_write_parameter("skin","skin",z);
/* Per /chat discussion, passing ?skin=... without "once"
** implies the "udc" argument, so we force that into the
** environment here. */
cgi_set_parameter_nocopy("udc", "1", 1);
|
| ︙ | ︙ | |||
1309 1310 1311 1312 1313 1314 1315 | ** / \ ** https://fossil-scm.org/forum/info/12736b30c072551a?t=c ** \___/ \____________/\____/\____________________/ \_/ ** | | | | | ** | HTTP_HOST | PATH_INFO QUERY_STRING ** | | ** REQUEST_SCHEMA SCRIPT_NAME | | > | 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 |
** / \
** https://fossil-scm.org/forum/info/12736b30c072551a?t=c
** \___/ \____________/\____/\____________________/ \_/
** | | | | |
** | HTTP_HOST | PATH_INFO QUERY_STRING
** | |
** REQUEST_SCHEMA SCRIPT_NAME
**
*/
void cgi_init(void){
char *z;
const char *zType;
char *zSemi;
int len;
const char *zRequestUri = cgi_parameter("REQUEST_URI",0);
const char *zScriptName = cgi_parameter("SCRIPT_NAME",0);
const char *zPathInfo = cgi_parameter("PATH_INFO",0);
const char *zContentLength = 0;
#ifdef _WIN32
const char *zServerSoftware = cgi_parameter("SERVER_SOFTWARE",0);
#endif
#ifdef FOSSIL_ENABLE_JSON
const int noJson = P("no_json")!=0;
#endif
|
| ︙ | ︙ | |||
1346 1347 1348 1349 1350 1351 1352 |
zScriptName = fossil_strndup(zRequestUri,(int)(z-zRequestUri));
cgi_set_parameter("SCRIPT_NAME", zScriptName);
}
#ifdef _WIN32
/* The Microsoft IIS web server does not define REQUEST_URI, instead it uses
** PATH_INFO for virtually the same purpose. Define REQUEST_URI the same as
| | | 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 |
zScriptName = fossil_strndup(zRequestUri,(int)(z-zRequestUri));
cgi_set_parameter("SCRIPT_NAME", zScriptName);
}
#ifdef _WIN32
/* The Microsoft IIS web server does not define REQUEST_URI, instead it uses
** PATH_INFO for virtually the same purpose. Define REQUEST_URI the same as
** PATH_INFO and redefine PATH_INFO with SCRIPT_NAME removed from the
** beginning. */
if( zServerSoftware && strstr(zServerSoftware, "Microsoft-IIS") ){
int i, j;
cgi_set_parameter("REQUEST_URI", zPathInfo);
for(i=0; zPathInfo[i]==zScriptName[i] && zPathInfo[i]; i++){}
for(j=i; zPathInfo[j] && zPathInfo[j]!='?'; j++){}
zPathInfo = fossil_strndup(zPathInfo+i, j-i);
|
| ︙ | ︙ | |||
1405 1406 1407 1408 1409 1410 1411 |
#endif
z = (char*)P("HTTP_COOKIE");
if( z ){
z = fossil_strdup(z);
add_param_list(z, ';');
z = (char*)cookie_value("skin",0);
if(z){
| | | > > > > > > > > | 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 |
#endif
z = (char*)P("HTTP_COOKIE");
if( z ){
z = fossil_strdup(z);
add_param_list(z, ';');
z = (char*)cookie_value("skin",0);
if(z){
skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
}
}
cgi_setup_query_string();
z = (char*)P("REMOTE_ADDR");
if( z ){
g.zIpAddr = fossil_strdup(z);
}
zContentLength = P("CONTENT_LENGTH");
if( zContentLength==0 ){
len = 0;
if( sqlite3_stricmp(PD("REQUEST_METHOD",""),"POST")==0 ){
malformed_request("missing CONTENT_LENGTH on a POST method");
}
}else{
len = atoi(zContentLength);
}
zType = P("CONTENT_TYPE");
zSemi = zType ? strchr(zType, ';') : 0;
if( zSemi ){
g.zContentType = fossil_strndup(zType, (int)(zSemi-zType));
zType = g.zContentType;
}else{
g.zContentType = zType;
|
| ︙ | ︙ | |||
1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 |
"REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_NAME",
"SERVER_PROTOCOL", "HOME", "FOSSIL_HOME", "USERNAME", "USER",
"FOSSIL_USER", "SQLITE_TMPDIR", "TMPDIR",
"TEMP", "TMP", "FOSSIL_VFS",
"FOSSIL_FORCE_TICKET_MODERATION", "FOSSIL_FORCE_WIKI_MODERATION",
"FOSSIL_TCL_PATH", "TH1_DELETE_INTERP", "TH1_ENABLE_DOCS",
"TH1_ENABLE_HOOKS", "TH1_ENABLE_TCL", "REMOTE_HOST",
};
int i;
for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]);
}
/*
** Print all query parameters on standard output.
| > | 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 |
"REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_NAME",
"SERVER_PROTOCOL", "HOME", "FOSSIL_HOME", "USERNAME", "USER",
"FOSSIL_USER", "SQLITE_TMPDIR", "TMPDIR",
"TEMP", "TMP", "FOSSIL_VFS",
"FOSSIL_FORCE_TICKET_MODERATION", "FOSSIL_FORCE_WIKI_MODERATION",
"FOSSIL_TCL_PATH", "TH1_DELETE_INTERP", "TH1_ENABLE_DOCS",
"TH1_ENABLE_HOOKS", "TH1_ENABLE_TCL", "REMOTE_HOST",
"CONTENT_TYPE", "CONTENT_LENGTH",
};
int i;
for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]);
}
/*
** Print all query parameters on standard output.
|
| ︙ | ︙ | |||
1809 1810 1811 1812 1813 1814 1815 |
break;
}
case 2: {
cgi_debug("%s = %s\n", zName, zValue);
break;
}
case 3: {
| | | 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 |
break;
}
case 2: {
cgi_debug("%s = %s\n", zName, zValue);
break;
}
case 3: {
if( zValue!=0 && strlen(zValue)>100 ){
fprintf(out,"%s = %.100s...\n", zName, zValue);
}else{
fprintf(out,"%s = %s\n", zName, zValue);
}
break;
}
}
|
| ︙ | ︙ | |||
1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 |
void cgi_query_parameters_to_url(HQuery *p){
int i;
for(i=0; i<nUsedQP; i++){
if( aParamQP[i].isQP==0 || aParamQP[i].cTag ) continue;
url_add_parameter(p, aParamQP[i].zName, aParamQP[i].zValue);
}
}
/*
** Tag query parameter zName so that it is not exported by
** cgi_query_parameters_to_hidden(). Or if zName==0, then
** untag all query parameters.
*/
void cgi_tag_query_parameter(const char *zName){
| > > > > > > > > > > > > > > > > > > > > > > > > | 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 |
void cgi_query_parameters_to_url(HQuery *p){
int i;
for(i=0; i<nUsedQP; i++){
if( aParamQP[i].isQP==0 || aParamQP[i].cTag ) continue;
url_add_parameter(p, aParamQP[i].zName, aParamQP[i].zValue);
}
}
/*
** Reconstruct the URL into memory obtained from fossil_malloc() and
** return a pointer to that URL.
*/
char *cgi_reconstruct_original_url(void){
int i;
char cSep = '?';
Blob url;
blob_init(&url, 0, 0);
blob_appendf(&url, "%s/%s", g.zBaseURL, g.zPath);
for(i=0; i<nUsedQP; i++){
if( aParamQP[i].isQP ){
struct QParam *p = &aParamQP[i];
if( p->zValue && p->zValue[0] ){
blob_appendf(&url, "%c%t=%t", cSep, p->zName, p->zValue);
}else{
blob_appendf(&url, "%c%t", cSep, p->zName);
}
cSep = '&';
}
}
return blob_str(&url);
}
/*
** Tag query parameter zName so that it is not exported by
** cgi_query_parameters_to_hidden(). Or if zName==0, then
** untag all query parameters.
*/
void cgi_tag_query_parameter(const char *zName){
|
| ︙ | ︙ | |||
1909 1910 1911 1912 1913 1914 1915 | vxprintf(pContent,zFormat,ap); } /* ** Send a reply indicating that the HTTP request was malformed */ | | > > > > > | > > > > | > | > | | 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 |
vxprintf(pContent,zFormat,ap);
}
/*
** Send a reply indicating that the HTTP request was malformed
*/
static NORETURN void malformed_request(const char *zMsg, ...){
va_list ap;
char *z;
va_start(ap, zMsg);
z = vmprintf(zMsg, ap);
va_end(ap);
cgi_set_status(400, "Bad Request");
zContentType = "text/plain";
if( g.zReqType==0 ) g.zReqType = "WWW";
if( g.zReqType[0]=='C' && PD("SERVER_SOFTWARE",0)!=0 ){
const char *zServer = PD("SERVER_SOFTWARE","");
cgi_printf("Bad CGI Request from \"%s\": %s\n",zServer,z);
}else{
cgi_printf("Bad %s Request: %s\n", g.zReqType, z);
}
fossil_free(z);
cgi_reply();
fossil_exit(0);
}
/*
** Panic and die while processing a webpage.
*/
|
| ︙ | ︙ | |||
2030 2031 2032 2033 2034 2035 2036 2037 |
*/
void cgi_handle_http_request(const char *zIpAddr){
char *z, *zToken;
int i;
const char *zScheme = "http";
char zLine[2000]; /* A single line of input. */
g.fullHttpReply = 1;
if( cgi_fgets(zLine, sizeof(zLine))==0 ){
| > | | > | | | 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 |
*/
void cgi_handle_http_request(const char *zIpAddr){
char *z, *zToken;
int i;
const char *zScheme = "http";
char zLine[2000]; /* A single line of input. */
g.fullHttpReply = 1;
g.zReqType = "HTTP";
if( cgi_fgets(zLine, sizeof(zLine))==0 ){
malformed_request("missing header");
}
blob_append(&g.httpHeader, zLine, -1);
cgi_trace(zLine);
zToken = extract_token(zLine, &z);
if( zToken==0 ){
malformed_request("malformed HTTP header");
}
if( fossil_strcmp(zToken,"GET")!=0
&& fossil_strcmp(zToken,"POST")!=0
&& fossil_strcmp(zToken,"HEAD")!=0
){
malformed_request("unsupported HTTP method: \"%s\" - Fossil only supports"
"GET, POST, and HEAD", zToken);
}
cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
cgi_setenv("REQUEST_METHOD",zToken);
zToken = extract_token(z, &z);
if( zToken==0 ){
malformed_request("malformed URI in the HTTP header");
}
cgi_setenv("REQUEST_URI", zToken);
cgi_setenv("SCRIPT_NAME", "");
for(i=0; zToken[i] && zToken[i]!='?'; i++){}
if( zToken[i] ) zToken[i++] = 0;
cgi_setenv("PATH_INFO", zToken);
cgi_setenv("QUERY_STRING", &zToken[i]);
if( zIpAddr==0 ){
zIpAddr = cgi_remote_ip(fossil_fileno(g.httpIn));
}
if( zIpAddr ){
cgi_setenv("REMOTE_ADDR", zIpAddr);
g.zIpAddr = fossil_strdup(zIpAddr);
}
|
| ︙ | ︙ | |||
2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 |
if( nCycles==0 ){
cgi_setenv("REMOTE_ADDR", zIpAddr);
g.zIpAddr = fossil_strdup(zIpAddr);
}
}else{
fossil_fatal("missing SSH IP address");
}
if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
malformed_request("missing HTTP header");
}
cgi_trace(zLine);
zToken = extract_token(zLine, &z);
if( zToken==0 ){
malformed_request("malformed HTTP header");
| > | 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 |
if( nCycles==0 ){
cgi_setenv("REMOTE_ADDR", zIpAddr);
g.zIpAddr = fossil_strdup(zIpAddr);
}
}else{
fossil_fatal("missing SSH IP address");
}
g.zReqType = "HTTP";
if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
malformed_request("missing HTTP header");
}
cgi_trace(zLine);
zToken = extract_token(zLine, &z);
if( zToken==0 ){
malformed_request("malformed HTTP header");
|
| ︙ | ︙ | |||
2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 | */ #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ #define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */ #define HTTP_SERVER_HAD_REPOSITORY 0x0004 /* Was the repository open? */ #define HTTP_SERVER_HAD_CHECKOUT 0x0008 /* Was a checkout open? */ #define HTTP_SERVER_REPOLIST 0x0010 /* Allow repo listing */ #define HTTP_SERVER_NOFORK 0x0020 /* Do not call fork() */ #endif /* INTERFACE */ /* ** Maximum number of child processes that we can have running ** at one time. Set this to 0 for "no limit". */ | > | 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 | */ #define HTTP_SERVER_LOCALHOST 0x0001 /* Bind to 127.0.0.1 only */ #define HTTP_SERVER_SCGI 0x0002 /* SCGI instead of HTTP */ #define HTTP_SERVER_HAD_REPOSITORY 0x0004 /* Was the repository open? */ #define HTTP_SERVER_HAD_CHECKOUT 0x0008 /* Was a checkout open? */ #define HTTP_SERVER_REPOLIST 0x0010 /* Allow repo listing */ #define HTTP_SERVER_NOFORK 0x0020 /* Do not call fork() */ #define HTTP_SERVER_UNIXSOCKET 0x0040 /* Use a unix-domain socket */ #endif /* INTERFACE */ /* ** Maximum number of child processes that we can have running ** at one time. Set this to 0 for "no limit". */ |
| ︙ | ︙ | |||
2452 2453 2454 2455 2456 2457 2458 2459 | int nRequest = 0; /* Number of requests handled so far */ fd_set readfds; /* Set of file descriptors for select() */ socklen_t lenaddr; /* Length of the inaddr structure */ int child; /* PID of the child process */ int nchildren = 0; /* Number of child processes */ struct timeval delay; /* How long to wait inside select() */ struct sockaddr_in inaddr; /* The socket address */ int opt = 1; /* setsockopt flag */ | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | > | > > > > > > > > > > | > > > > | > > > > > | | | > | | 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 |
int nRequest = 0; /* Number of requests handled so far */
fd_set readfds; /* Set of file descriptors for select() */
socklen_t lenaddr; /* Length of the inaddr structure */
int child; /* PID of the child process */
int nchildren = 0; /* Number of child processes */
struct timeval delay; /* How long to wait inside select() */
struct sockaddr_in inaddr; /* The socket address */
struct sockaddr_un uxaddr; /* The address for unix-domain sockets */
int opt = 1; /* setsockopt flag */
int rc; /* Result code from system calls */
int iPort = mnPort; /* Port to try to use */
while( iPort<=mxPort ){
if( flags & HTTP_SERVER_UNIXSOCKET ){
/* Initialize a Unix socket named g.zSockName */
assert( g.zSockName!=0 );
memset(&uxaddr, 0, sizeof(uxaddr));
if( strlen(g.zSockName)>sizeof(uxaddr.sun_path) ){
fossil_fatal("name of unix socket too big: %s\nmax size: %d\n",
g.zSockName, (int)sizeof(uxaddr.sun_path));
}
if( file_isdir(g.zSockName, ExtFILE)!=0 ){
if( !file_issocket(g.zSockName) ){
fossil_fatal("cannot name socket \"%s\" because another object"
" with that name already exists", g.zSockName);
}else{
unlink(g.zSockName);
}
}
uxaddr.sun_family = AF_UNIX;
strncpy(uxaddr.sun_path, g.zSockName, sizeof(uxaddr.sun_path)-1);
listener = socket(AF_UNIX, SOCK_STREAM, 0);
if( listener<0 ){
fossil_fatal("unable to create a unix socket named %s",
g.zSockName);
}
/* Set the access permission for the new socket. Default to 0660.
** But use an alternative specified by --socket-mode if available.
** Do this before bind() to avoid a race condition. */
if( g.zSockMode ){
file_set_mode(g.zSockName, listener, g.zSockMode, 0);
}else{
file_set_mode(g.zSockName, listener, "0660", 1);
}
}else{
/* Initialize a TCP/IP socket on port iPort */
memset(&inaddr, 0, sizeof(inaddr));
inaddr.sin_family = AF_INET;
if( zIpAddr ){
inaddr.sin_addr.s_addr = inet_addr(zIpAddr);
if( inaddr.sin_addr.s_addr == INADDR_NONE ){
fossil_fatal("not a valid IP address: %s", zIpAddr);
}
}else if( flags & HTTP_SERVER_LOCALHOST ){
inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}else{
inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
}
inaddr.sin_port = htons(iPort);
listener = socket(AF_INET, SOCK_STREAM, 0);
if( listener<0 ){
iPort++;
continue;
}
}
/* if we can't terminate nicely, at least allow the socket to be reused */
setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
if( flags & HTTP_SERVER_UNIXSOCKET ){
rc = bind(listener, (struct sockaddr*)&uxaddr, sizeof(uxaddr));
/* Set the owner of the socket if requested by --socket-owner. This
** must wait until after bind(), after the filesystem object has been
** created. See https://lkml.org/lkml/2004/11/1/84 and
** https://fossil-scm.org/forum/forumpost/7517680ef9684c57 */
if( g.zSockOwner ){
file_set_owner(g.zSockName, listener, g.zSockOwner);
}
}else{
rc = bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr));
}
if( rc<0 ){
close(listener);
iPort++;
continue;
}
break;
}
if( iPort>mxPort ){
if( flags & HTTP_SERVER_UNIXSOCKET ){
fossil_fatal("unable to listen on unix socket %s", zIpAddr);
}else if( mnPort==mxPort ){
fossil_fatal("unable to open listening socket on port %d", mnPort);
}else{
fossil_fatal("unable to open listening socket on any"
" port in the range %d..%d", mnPort, mxPort);
}
}
if( iPort>mxPort ) return 1;
listen(listener,10);
if( flags & HTTP_SERVER_UNIXSOCKET ){
fossil_print("Listening for %s requests on unix socket %s\n",
(flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" :
g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP", g.zSockName);
}else{
fossil_print("Listening for %s requests on TCP port %d\n",
(flags & HTTP_SERVER_SCGI)!=0 ? "SCGI" :
g.httpUseSSL?"TLS-encrypted HTTPS":"HTTP", iPort);
}
fflush(stdout);
if( zBrowser && (flags & HTTP_SERVER_UNIXSOCKET)==0 ){
assert( strstr(zBrowser,"%d")!=0 );
zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
#if defined(__CYGWIN__)
/* On Cygwin, we can do better than "echo" */
if( fossil_strncmp(zBrowser, "echo ", 5)==0 ){
wchar_t *wUrl = fossil_utf8_to_unicode(zBrowser+5);
wUrl[wcslen(wUrl)-2] = 0; /* Strip terminating " &" */
|
| ︙ | ︙ | |||
2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 |
if( child>0 ){
nchildren++;
nRequest++;
}
close(connection);
}else{
int nErr = 0, fd;
close(0);
fd = dup(connection);
if( fd!=0 ) nErr++;
close(1);
fd = dup(connection);
if( fd!=1 ) nErr++;
if( 0 && !g.fAnyTrace ){
| > | 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 |
if( child>0 ){
nchildren++;
nRequest++;
}
close(connection);
}else{
int nErr = 0, fd;
g.zSockName = 0 /* avoid deleting the socket via atexit() */;
close(0);
fd = dup(connection);
if( fd!=0 ) nErr++;
close(1);
fd = dup(connection);
if( fd!=1 ) nErr++;
if( 0 && !g.fAnyTrace ){
|
| ︙ | ︙ | |||
2772 2773 2774 2775 2776 2777 2778 |
** implementation as possible, ideally just before it begins doing
** potentially CPU-intensive computations and after all query parameters
** have been consulted.
*/
void cgi_check_for_malice(void){
struct QParam * pParam;
int i;
| | | > > | > | 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 |
** implementation as possible, ideally just before it begins doing
** potentially CPU-intensive computations and after all query parameters
** have been consulted.
*/
void cgi_check_for_malice(void){
struct QParam * pParam;
int i;
for(i=0; i<nUsedQP; ++i){
pParam = &aParamQP[i];
if( 0==pParam->isFetched
&& pParam->zValue!=0
&& pParam->zName!=0
&& fossil_islower(pParam->zName[0])
){
cgi_value_spider_check(pParam->zValue, pParam->zName);
}
}
}
|
Changes to src/chat.c.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 | ** * Chat content lives in a single repository. It is never synced. ** Content expires and is deleted after a set interval (a week or so). ** ** Notification is accomplished using the "hanging GET" or "long poll" design ** in which a GET request is issued but the server does not send a reply until ** new content arrives. Newer Web Sockets and Server Sent Event protocols are ** more elegant, but are not compatible with CGI, and would thus complicate | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ** * Chat content lives in a single repository. It is never synced. ** Content expires and is deleted after a set interval (a week or so). ** ** Notification is accomplished using the "hanging GET" or "long poll" design ** in which a GET request is issued but the server does not send a reply until ** new content arrives. Newer Web Sockets and Server Sent Event protocols are ** more elegant, but are not compatible with CGI, and would thus complicate ** configuration. */ #include "config.h" #include <assert.h> #include "chat.h" /* ** Outputs JS code to initialize a list of chat alert audio files for |
| ︙ | ︙ | |||
145 146 147 148 149 150 151 | /* ** WEBPAGE: chat loadavg-exempt ** ** Start up a browser-based chat session. ** ** This is the main page that humans use to access the chatroom. Simply ** point a web-browser at /chat and the screen fills with the latest | | < < < < < < < < < < < < < < < < < | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
/*
** WEBPAGE: chat loadavg-exempt
**
** Start up a browser-based chat session.
**
** This is the main page that humans use to access the chatroom. Simply
** point a web-browser at /chat and the screen fills with the latest
** chat messages, and waits for new ones.
**
** Other /chat-OP pages are used by XHR requests from this page to
** send new chat message, delete older messages, or poll for changes.
*/
void chat_webpage(void){
char *zAlert;
char *zProjectName;
char * zInputPlaceholder0; /* Common text input placeholder value */
login_check_credentials();
if( !g.perm.Chat ){
login_needed(g.anon.Chat);
return;
}
zAlert = mprintf("%s/builtin/%s", g.zBaseURL,
|
| ︙ | ︙ | |||
201 202 203 204 205 206 207 208 | @ <div contenteditable id="chat-input-field-x" \ @ data-placeholder0="%h(zInputPlaceholder0)" \ @ data-placeholder="%h(zInputPlaceholder0)" \ @ class="chat-input-field hidden"></div> @ <div id='chat-buttons-wrapper'> @ <span class='cbutton' id="chat-button-preview" \ @ title="Preview message (Shift-Enter)">👁</span> @ <span class='cbutton' id="chat-button-attach" \ | > > | | 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | @ <div contenteditable id="chat-input-field-x" \ @ data-placeholder0="%h(zInputPlaceholder0)" \ @ data-placeholder="%h(zInputPlaceholder0)" \ @ class="chat-input-field hidden"></div> @ <div id='chat-buttons-wrapper'> @ <span class='cbutton' id="chat-button-preview" \ @ title="Preview message (Shift-Enter)">👁</span> @ <span class='cbutton' id="chat-button-search" \ @ title="Search chat history">🔍</span> @ <span class='cbutton' id="chat-button-attach" \ @ title="Attach file to message">📎</span> @ <span class='cbutton' id="chat-button-settings" \ @ title="Configure chat">⚙</span> @ <span class='cbutton' id="chat-button-submit" \ @ title="Send message (Ctrl-Enter)">📤</span> @ </div> @ </div> @ <div id='chat-input-file-area'> |
| ︙ | ︙ | |||
231 232 233 234 235 236 237 | @ </span> @ <span>Active users (sorted by last message time)</span> @ </div> @ <div id='chat-user-list'></div> @ </div> @ <div id='chat-preview' class='hidden chat-view'> @ <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header> | | | | > > > > > > > > | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
@ </span>
@ <span>Active users (sorted by last message time)</span>
@ </div>
@ <div id='chat-user-list'></div>
@ </div>
@ <div id='chat-preview' class='hidden chat-view'>
@ <header>Preview: (<a href='%R/md_rules' target='_blank'>markdown reference</a>)</header>
@ <div id='chat-preview-content'></div>
@ <div class='button-bar'><button class='action-close'>Close Preview</button></div>
@ </div>
@ <div id='chat-config' class='hidden chat-view'>
@ <div id='chat-config-options'></div>
/* ^^^populated client-side */
@ <div class='button-bar'><button class='action-close'>Close Settings</button></div>
@ </div>
@ <div id='chat-search' class='hidden chat-view'>
@ <div id='chat-search-content'></div>
/* ^^^populated client-side */
@ <div class='button-bar'>
@ <button class='action-clear'>Clear results</button>
@ <button class='action-close'>Close Search</button>
@ </div>
@ </div>
@ <div id='chat-messages-wrapper' class='chat-view'>
/* New chat messages get inserted immediately after this element */
@ <span id='message-inject-point'></span>
@ </div>
fossil_free(zProjectName);
fossil_free(zInputPlaceholder0);
|
| ︙ | ︙ | |||
269 270 271 272 273 274 275 |
chat_emit_alert_list();
@ }, false);
@ </script>
builtin_request_js("fossil.page.chat.js");
style_finish_page();
}
| > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > | | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 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 360 361 362 363 364 365 |
chat_emit_alert_list();
@ }, false);
@ </script>
builtin_request_js("fossil.page.chat.js");
style_finish_page();
}
/*
** Definition of repository tables used by chat
*/
static const char zChatSchema1[] =
@ CREATE TABLE repository.chat(
@ msgid INTEGER PRIMARY KEY AUTOINCREMENT,
@ mtime JULIANDAY, -- Time for this entry - Julianday Zulu
@ lmtime TEXT, -- Client YYYY-MM-DDZHH:MM:SS when message originally sent
@ xfrom TEXT, -- Login of the sender
@ xmsg TEXT, -- Raw, unformatted text of the message
@ fname TEXT, -- Filename of the uploaded file, or NULL
@ fmime TEXT, -- MIMEType of the upload file, or NULL
@ mdel INT, -- msgid of another message to delete
@ file BLOB -- Text of the uploaded file, or NULL
@ );
;
/*
** Create or rebuild the /chat search index. Requires that the
** repository.chat table exists. If bForce is true, it will drop the
** chatfts1 table and recreate/reindex it. If bForce is 0, it will
** only index the chat content if the chatfts1 table does not already
** exist.
*/
void chat_rebuild_index(int bForce){
if( bForce!=0 ){
db_multi_exec("DROP TABLE IF EXISTS chatfts1");
}
if( bForce!=0 || !db_table_exists("repository", "chatfts1") ){
const int tokType = search_tokenizer_type(0);
const char *zTokenizer = search_tokenize_arg_for_type(
tokType==FTS5TOK_NONE ? FTS5TOK_PORTER : tokType
/* Special case: if fts search is disabled for the main repo
** content, use a default tokenizer here. */
);
assert( zTokenizer && zTokenizer[0] );
db_multi_exec(
"CREATE VIRTUAL TABLE repository.chatfts1 USING fts5("
" xmsg, content=chat, content_rowid=msgid%s"
");"
"INSERT INTO repository.chatfts1(chatfts1) VALUES('rebuild');",
zTokenizer/*safe-for-%s*/
);
}
}
/*
** Make sure the repository data tables used by chat exist. Create
** them if they do not. Set up TEMP triggers (if needed) to update the
** chatfts1 table as the chat table is updated.
*/
static void chat_create_tables(void){
if( !db_table_exists("repository","chat") ){
db_multi_exec(zChatSchema1/*works-like:""*/);
}else if( !db_table_has_column("repository","chat","lmtime") ){
if( !db_table_has_column("repository","chat","mdel") ){
db_multi_exec("ALTER TABLE chat ADD COLUMN mdel INT");
}
db_multi_exec("ALTER TABLE chat ADD COLUMN lmtime TEXT");
}
chat_rebuild_index(0);
db_multi_exec(
"CREATE TEMP TRIGGER IF NOT EXISTS chat_ai AFTER INSERT ON chat BEGIN "
" INSERT INTO chatfts1(rowid, xmsg) VALUES(new.msgid, new.xmsg);"
"END;"
"CREATE TEMP TRIGGER IF NOT EXISTS chat_ad AFTER DELETE ON chat BEGIN "
" INSERT INTO chatfts1(chatfts1, rowid, xmsg) "
" VALUES('delete', old.msgid, old.xmsg);"
"END;"
);
}
/*
** Delete old content from the chat table.
*/
static void chat_purge(void){
int mxCnt = db_get_int("chat-keep-count",50);
double mxDays = atof(db_get("chat-keep-days","7"));
double rAge;
int msgid;
rAge = db_double(0.0, "SELECT julianday('now')-mtime FROM chat"
" ORDER BY msgid LIMIT 1");
if( rAge>mxDays ){
msgid = db_int(0, "SELECT msgid FROM chat"
" ORDER BY msgid DESC LIMIT 1 OFFSET %d", mxCnt);
if( msgid>0 ){
Stmt s;
db_multi_exec("PRAGMA secure_delete=ON;");
db_prepare(&s,
"DELETE FROM chat WHERE mtime<julianday('now')-:mxage"
" AND msgid<%d", msgid);
db_bind_double(&s, ":mxage", mxDays);
db_step(&s);
db_finalize(&s);
}
}
|
| ︙ | ︙ | |||
386 387 388 389 390 391 392 |
const char *zMsg;
const char *zUserName;
login_check_credentials();
if( 0==g.perm.Chat ) {
chat_emit_permissions_error(0);
return;
}
| < > | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
const char *zMsg;
const char *zUserName;
login_check_credentials();
if( 0==g.perm.Chat ) {
chat_emit_permissions_error(0);
return;
}
zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody";
nByte = atoi(PD("file:bytes","0"));
zMsg = PD("msg","");
db_begin_write();
db_unprotect(PROTECT_READONLY);
chat_create_tables();
chat_purge();
if( nByte==0 ){
if( zMsg[0] ){
db_multi_exec(
"INSERT INTO chat(mtime,lmtime,xfrom,xmsg)"
"VALUES(julianday('now'),%Q,%Q,%Q)",
P("lmtime"), zUserName, zMsg
|
| ︙ | ︙ | |||
415 416 417 418 419 420 421 |
PD("file:mimetype","application/octet-stream"));
blob_init(&b, P("file"), nByte);
db_bind_blob(&q, ":file", &b);
db_step(&q);
db_finalize(&q);
blob_reset(&b);
}
| < > | 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
PD("file:mimetype","application/octet-stream"));
blob_init(&b, P("file"), nByte);
db_bind_blob(&q, ":file", &b);
db_step(&q);
db_finalize(&q);
blob_reset(&b);
}
db_commit_transaction();
db_protect_pop();
}
/*
** This routine receives raw (user-entered) message text and
** transforms it into HTML that is safe to insert using innerHTML. As
** of 2021-09-19, it does so by using wiki_convert() or
** markdown_to_html() to convert wiki/markdown-formatted zMsg to HTML.
|
| ︙ | ︙ | |||
450 451 452 453 454 455 456 | } return blob_str(&out); } /* ** COMMAND: test-chat-formatter ** | | > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 |
}
return blob_str(&out);
}
/*
** COMMAND: test-chat-formatter
**
** Usage: %fossil test-chat-formatter ?OPTIONS? STRING ...
**
** Transform each argument string into HTML that will display the
** chat message. This is used to test the formatter and to verify
** that a malicious message text will not cause HTML or JS injection
** into the chat display in a browser.
**
** Options:
**
** -w|--wiki Assume fossil wiki format instead of markdown
*/
void chat_test_formatter_cmd(void){
int i;
char *zOut;
int const isWiki = find_option("w","wiki",0)!=0;
db_find_and_open_repository(0,0);
g.perm.Hyperlink = 1;
for(i=2; i<g.argc; i++){
zOut = chat_format_to_html(g.argv[i], isWiki);
fossil_print("[%d]: %s\n", i-1, zOut);
fossil_free(zOut);
}
}
/*
** The SQL statement passed as the first argument should return zero or
** more rows of data, each of which represents a single message from the
** "chat" table. The rows returned should be similar to those returned
** by:
**
** SELECT msgid,
** datetime(mtime),
** xfrom,
** xmsg,
** octet_length(file),"
** fname,
** fmime,
** mdel,
** lmtime
** FROM chat;
**
** This function loops through all rows returned by statement p, adding
** a record to the JSON stored in argument pJson for each. See comments
** above function chat_poll_webpage() for a description of the JSON records
** added to pJson.
*/
static int chat_poll_rowstojson(
Stmt *p, /* Statement to read rows from */
int bRaw, /* True to return raw format xmsg */
Blob *pJson /* Append json array entries here */
){
int cnt = 0;
const char *zChatUser = db_get("chat-timeline-user",0);
while( db_step(p)==SQLITE_ROW ){
int isWiki = 0; /* True if chat message is x-fossil-wiki */
int id = db_column_int(p, 0);
const char *zDate = db_column_text(p, 1);
const char *zFrom = db_column_text(p, 2);
const char *zRawMsg = db_column_text(p, 3);
int nByte = db_column_int(p, 4);
const char *zFName = db_column_text(p, 5);
const char *zFMime = db_column_text(p, 6);
int iToDel = db_column_int(p, 7);
const char *zLMtime = db_column_text(p, 8);
char *zMsg;
if(cnt++){
blob_append(pJson, ",\n", 2);
}
blob_appendf(pJson, "{\"msgid\":%d,", id);
blob_appendf(pJson, "\"mtime\":\"%.10sT%sZ\",", zDate, zDate+11);
if( zLMtime && zLMtime[0] ){
blob_appendf(pJson, "\"lmtime\":%!j,", zLMtime);
}
blob_append(pJson, "\"xfrom\":", -1);
if(zFrom){
blob_appendf(pJson, "%!j,", zFrom);
isWiki = fossil_strcmp(zFrom,zChatUser)==0;
}else{
/* see https://fossil-scm.org/forum/forumpost/e0be0eeb4c */
blob_appendf(pJson, "null,");
isWiki = 0;
}
blob_appendf(pJson, "\"uclr\":%!j,",
isWiki ? "transparent" : user_color(zFrom ? zFrom : "nobody"));
if(bRaw){
blob_appendf(pJson, "\"xmsg\":%!j,", zRawMsg);
}else{
zMsg = chat_format_to_html(zRawMsg ? zRawMsg : "", isWiki);
blob_appendf(pJson, "\"xmsg\":%!j,", zMsg);
fossil_free(zMsg);
}
if( nByte==0 ){
blob_appendf(pJson, "\"fsize\":0");
}else{
blob_appendf(pJson, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
nByte, zFName, zFMime);
}
if( iToDel ){
blob_appendf(pJson, ",\"mdel\":%d}", iToDel);
}else{
blob_append(pJson, "}", 1);
}
}
db_reset(p);
return cnt;
}
/*
** WEBPAGE: chat-poll hidden loadavg-exempt
**
** The chat page generated by /chat using an XHR to this page to
** request new chat content. A typical invocation is:
**
|
| ︙ | ︙ | |||
566 567 568 569 570 571 572 |
** in a prominent manner and then stop polling for new messages.
*/
void chat_poll_webpage(void){
Blob json; /* The json to be constructed and returned */
sqlite3_int64 dataVersion; /* Data version. Used for polling. */
const int iDelay = 1000; /* Delay until next poll (milliseconds) */
int nDelay; /* Maximum delay.*/
| < < < | 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 |
** in a prominent manner and then stop polling for new messages.
*/
void chat_poll_webpage(void){
Blob json; /* The json to be constructed and returned */
sqlite3_int64 dataVersion; /* Data version. Used for polling. */
const int iDelay = 1000; /* Delay until next poll (milliseconds) */
int nDelay; /* Maximum delay.*/
int msgid = atoi(PD("name","0"));
const int msgBefore = atoi(PD("before","0"));
int nLimit = msgBefore>0 ? atoi(PD("n","0")) : 0;
const int bRaw = P("raw")!=0;
Blob sql = empty_blob;
Stmt q1;
nDelay = db_get_int("chat-poll-timeout",420); /* Default about 7 minutes */
login_check_credentials();
if( !g.perm.Chat ) {
chat_emit_permissions_error(1);
return;
}
chat_create_tables();
cgi_set_content_type("application/json");
dataVersion = db_int64(0, "PRAGMA data_version");
blob_append_sql(&sql,
"SELECT msgid, datetime(mtime), xfrom, xmsg, octet_length(file),"
" fname, fmime, %s, lmtime"
" FROM chat ",
|
| ︙ | ︙ | |||
621 622 623 624 625 626 627 |
msgid
);
}
db_prepare(&q1, "%s", blob_sql_text(&sql));
blob_reset(&sql);
blob_init(&json, "{\"msgs\":[\n", -1);
while( nDelay>0 ){
| | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 |
msgid
);
}
db_prepare(&q1, "%s", blob_sql_text(&sql));
blob_reset(&sql);
blob_init(&json, "{\"msgs\":[\n", -1);
while( nDelay>0 ){
int cnt = chat_poll_rowstojson(&q1, bRaw, &json);
if( cnt || msgBefore>0 ){
break;
}
sqlite3_sleep(iDelay); nDelay--;
while( nDelay>0 ){
sqlite3_int64 newDataVers = db_int64(0,"PRAGMA repository.data_version");
if( newDataVers!=dataVersion ){
dataVersion = newDataVers;
break;
}
sqlite3_sleep(iDelay); nDelay--;
}
} /* Exit by "break" */
db_finalize(&q1);
blob_append(&json, "\n]}", 3);
cgi_set_content(&json);
return;
}
/*
** WEBPAGE: chat-query hidden loadavg-exempt
*/
void chat_query_webpage(void){
Blob json; /* The json to be constructed and returned */
Blob sql = empty_blob;
Stmt q1;
int nLimit = atoi(PD("n","500"));
int iFirst = atoi(PD("i","0"));
const char *zQuery = PD("q", "");
i64 iMin = 0;
i64 iMax = 0;
login_check_credentials();
if( !g.perm.Chat ) {
chat_emit_permissions_error(1);
return;
}
chat_create_tables();
cgi_set_content_type("application/json");
if( zQuery[0] ){
iMax = db_int64(0, "SELECT max(msgid) FROM chat");
iMin = db_int64(0, "SELECT min(msgid) FROM chat");
if( '#'==zQuery[0] ){
/* Assume we're looking for an exact msgid match. */
++zQuery;
blob_append_sql(&sql,
"SELECT msgid, datetime(mtime), xfrom, "
" xmsg, octet_length(file), fname, fmime, mdel, lmtime "
" FROM chat WHERE msgid=+%Q",
zQuery
);
}else{
char * zPat = search_simplify_pattern(zQuery);
blob_append_sql(&sql,
"SELECT * FROM ("
"SELECT c.msgid, datetime(c.mtime), c.xfrom, "
" highlight(chatfts1, 0, '<span class=\"match\">', '</span>'), "
" octet_length(c.file), c.fname, c.fmime, c.mdel, c.lmtime "
" FROM chatfts1(%Q) f, chat c "
" WHERE f.rowid=c.msgid"
" ORDER BY f.rowid DESC LIMIT %d"
") ORDER BY 1 ASC", zPat, nLimit
);
fossil_free(zPat);
}
}else{
blob_append_sql(&sql,
"SELECT msgid, datetime(mtime), xfrom, "
" xmsg, octet_length(file), fname, fmime, mdel, lmtime"
" FROM chat WHERE msgid>=%d LIMIT %d",
iFirst, nLimit
);
}
db_prepare(&q1, "%s", blob_sql_text(&sql));
blob_reset(&sql);
blob_init(&json, "{\"msgs\":[\n", -1);
chat_poll_rowstojson(&q1, 0, &json);
db_finalize(&q1);
blob_appendf(&json, "\n], \"first\":%lld, \"last\":%lld}", iMin, iMax);
cgi_set_content(&json);
return;
}
/*
** WEBPAGE: chat-fetch-one hidden loadavg-exempt
**
** /chat-fetch-one/N
**
|
| ︙ | ︙ | |||
724 725 726 727 728 729 730 |
if( !g.perm.Chat ) {
chat_emit_permissions_error(0);
return;
}
zChatUser = db_get("chat-timeline-user",0);
chat_create_tables();
cgi_set_content_type("application/json");
| | | 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 |
if( !g.perm.Chat ) {
chat_emit_permissions_error(0);
return;
}
zChatUser = db_get("chat-timeline-user",0);
chat_create_tables();
cgi_set_content_type("application/json");
db_prepare(&q,
"SELECT datetime(mtime), xfrom, xmsg, octet_length(file),"
" fname, fmime, lmtime"
" FROM chat WHERE msgid=%d AND mdel IS NULL",
msgid);
if(SQLITE_ROW==db_step(&q)){
const char *zDate = db_column_text(&q, 0);
const char *zFrom = db_column_text(&q, 1);
|
| ︙ | ︙ | |||
767 768 769 770 771 772 773 |
fossil_free(zMsg);
}
if( nByte==0 ){
blob_appendf(&json, "\"fsize\":0");
}else{
blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
nByte, zFName, zFMime);
| | | 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 |
fossil_free(zMsg);
}
if( nByte==0 ){
blob_appendf(&json, "\"fsize\":0");
}else{
blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
nByte, zFName, zFMime);
}
blob_append(&json,"}",1);
cgi_set_content(&json);
}else{
ajax_route_error(404,"Chat message #%d not found.", msgid);
}
db_finalize(&q);
}
|
| ︙ | ︙ | |||
803 804 805 806 807 808 809 |
**
** - Mimetype is text/x-pikchr or P("name") ends with ".pikchr": emit
** image/svg+xml if rendering succeeds or text/html if rendering
** fails.
*/
void chat_download_webpage(void){
int msgid;
| > > | | | 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 |
**
** - Mimetype is text/x-pikchr or P("name") ends with ".pikchr": emit
** image/svg+xml if rendering succeeds or text/html if rendering
** fails.
*/
void chat_download_webpage(void){
int msgid;
int bCheckedMimetype = 0; /* true to bypass the text/... mimetype
** check at the end */
Blob r; /* file content */
const char *zMime; /* file mimetype */
const char *zName = PD("name","0");
login_check_credentials();
if( !g.perm.Chat ){
style_header("Chat Not Authorized");
@ <h1>Not Authorized</h1>
@ <p>You do not have permission to use the chatroom on this
@ repository.</p>
|
| ︙ | ︙ | |||
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 |
const char * zMime2 = 0; /* adjusted response mimetype */
if(fossil_strcmp(zMime, "text/x-markdown")==0
/* Firefox uploads md files with the mimetype text/markdown */
|| fossil_strcmp(zMime, "text/markdown")==0){
markdown_to_html(&r, 0, &r2);
safe_html(&r2);
zMime2 = "text/html";
}else if(fossil_strcmp(zMime, "text/x-fossil-wiki")==0
|| sqlite3_strglob("*.wiki", zName)==0){
/* .wiki files get uploaded as application/octet-stream */
wiki_convert(&r, &r2, 0);
zMime2 = "text/html";
}else if(fossil_strcmp(zMime, "text/x-pikchr")==0
|| sqlite3_strglob("*.pikchr",zName)==0){
/* .pikchr files get uploaded as application/octet-stream */
const char *zPikchr = blob_str(&r);
int w = 0, h = 0;
char *zOut = pikchr(zPikchr, "pikchr", 0, &w, &h);
if(zOut){
blob_append(&r2, zOut, -1);
}
zMime2 = w>0 ? "image/svg+xml" : "text/html";
free(zOut);
}
if(r2.aData!=0){
blob_swap(&r, &r2);
blob_reset(&r2);
zMime = zMime2;
}
}
cgi_set_content_type(zMime);
cgi_set_content(&r);
}
/*
| > > > > > > > > > > > > > > > | 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 |
const char * zMime2 = 0; /* adjusted response mimetype */
if(fossil_strcmp(zMime, "text/x-markdown")==0
/* Firefox uploads md files with the mimetype text/markdown */
|| fossil_strcmp(zMime, "text/markdown")==0){
markdown_to_html(&r, 0, &r2);
safe_html(&r2);
zMime2 = "text/html";
bCheckedMimetype = 1;
}else if(fossil_strcmp(zMime, "text/x-fossil-wiki")==0
|| sqlite3_strglob("*.wiki", zName)==0){
/* .wiki files get uploaded as application/octet-stream */
wiki_convert(&r, &r2, 0);
zMime2 = "text/html";
bCheckedMimetype = 1;
}else if(fossil_strcmp(zMime, "text/x-pikchr")==0
|| sqlite3_strglob("*.pikchr",zName)==0){
/* .pikchr files get uploaded as application/octet-stream */
const char *zPikchr = blob_str(&r);
int w = 0, h = 0;
char *zOut = pikchr(zPikchr, "pikchr", 0, &w, &h);
if(zOut){
blob_append(&r2, zOut, -1);
}
zMime2 = w>0 ? "image/svg+xml" : "text/html";
free(zOut);
bCheckedMimetype = 1;
}
if(r2.aData!=0){
blob_swap(&r, &r2);
blob_reset(&r2);
zMime = zMime2;
}
}
if( bCheckedMimetype==0 && sqlite3_strglob("text/*", zMime)==0 ){
/* The problem: both Chrome and Firefox upload *.patch with
** the mimetype text/x-patch, whereas we very often use that
** name glob for fossil-format patches. That causes such files
** to attempt to render in the browser when clicked via
** download links in chat.
**
** The workaround: */
if( looks_like_binary(&r) ){
zMime = "application/octet-stream";
}
}
cgi_set_content_type(zMime);
cgi_set_content(&r);
}
/*
|
| ︙ | ︙ | |||
955 956 957 958 959 960 961 |
sqlite3_value **argv
){
const char *zType = (const char*)sqlite3_value_text(argv[0]);
int rid = sqlite3_value_int(argv[1]);
const char *zUser = (const char*)sqlite3_value_text(argv[2]);
const char *zMsg = (const char*)sqlite3_value_text(argv[3]);
char *zRes = 0;
| | | 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 |
sqlite3_value **argv
){
const char *zType = (const char*)sqlite3_value_text(argv[0]);
int rid = sqlite3_value_int(argv[1]);
const char *zUser = (const char*)sqlite3_value_text(argv[2]);
const char *zMsg = (const char*)sqlite3_value_text(argv[3]);
char *zRes = 0;
if( zType==0 || zUser==0 || zMsg==0 ) return;
if( zType[0]=='c' ){
/* Check-ins */
char *zBranch;
char *zUuid;
zBranch = db_text(0,
|
| ︙ | ︙ | |||
1107 1108 1109 1110 1111 1112 1113 |
#ifdef _WIN32
zCmd = mprintf("%s %s/chat?cli &", zBrowser, zUrl);
#else
zCmd = mprintf("%s \"%s/chat?cli\" &", zBrowser, zUrl);
#endif
fossil_system(zCmd);
}else if( strcmp(g.argv[2],"send")==0 ){
| | | 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 |
#ifdef _WIN32
zCmd = mprintf("%s %s/chat?cli &", zBrowser, zUrl);
#else
zCmd = mprintf("%s \"%s/chat?cli\" &", zBrowser, zUrl);
#endif
fossil_system(zCmd);
}else if( strcmp(g.argv[2],"send")==0 ){
const char *zFilename = find_option("file","f",1);
const char *zAs = find_option("as",0,1);
const char *zMsg = find_option("message","m",1);
int allowUnsafe = find_option("unsafe",0,0)!=0;
const int mFlags = HTTP_GENERIC | HTTP_QUIET | HTTP_NOCOMPRESS;
int i;
const char *zPw;
char *zLMTime;
|
| ︙ | ︙ | |||
1215 1216 1217 1218 1219 1220 1221 |
blob_appendf(&reqUri, "/chat-backup?msgid=%d", msgid);
if( g.url.user && g.url.user[0] ){
zObs = obscure(g.url.user);
blob_appendf(&reqUri, "&resid=%t", zObs);
fossil_free(zObs);
}
zPw = g.url.passwd;
| | > > > > > > > > | 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 |
blob_appendf(&reqUri, "/chat-backup?msgid=%d", msgid);
if( g.url.user && g.url.user[0] ){
zObs = obscure(g.url.user);
blob_appendf(&reqUri, "&resid=%t", zObs);
fossil_free(zObs);
}
zPw = g.url.passwd;
if( zPw==0 && isDefaultUrl ){
zPw = unobscure(db_get("last-sync-pw", 0));
if( zPw==0 ){
/* Can happen if "remember password" is not used. */
g.url.flags |= URL_PROMPT_PW;
url_prompt_for_password();
zPw = g.url.passwd;
}
}
if( zPw && zPw[0] ){
zObs = obscure(zPw);
blob_appendf(&reqUri, "&token=%t", zObs);
fossil_free(zObs);
}
g.url.path = blob_str(&reqUri);
if( bDebug ){
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 |
blob_reset(&report);
/* The status command ends with warnings about ambiguous leaves (forks). */
if( command==STATUS ){
leaf_ambiguity_warning(vid, vid);
}
}
/*
** Take care of -r version of ls command
*/
static void ls_cmd_rev(
const char *zRev, /* Revision string given */
int verboseFlag, /* Verbose flag given */
int showAge, /* Age flag given */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | 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 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 |
blob_reset(&report);
/* The status command ends with warnings about ambiguous leaves (forks). */
if( command==STATUS ){
leaf_ambiguity_warning(vid, vid);
}
}
/* zIn is a string that is guaranteed to be followed by \n. Return
** a pointer to the next line after the \n. The returned value might
** point to the \000 string terminator.
*/
static const char *next_line(const char *zIn){
const char *z = strchr(zIn, '\n');
assert( z!=0 );
return z+1;
}
/* zIn is a non-empty list of filenames in sorted order and separated
** by \n. There might be a cluster of lines that have the same n-character
** prefix. Return a pointer to the start of the last line of that
** cluster. The return value might be zIn if the first line of zIn is
** unique in its first n character.
*/
static const char *last_line(const char *zIn, int n){
const char *zLast = zIn;
const char *z;
while( 1 ){
z = next_line(zLast);
if( z[0]==0 || (n>0 && strncmp(zIn, z, n)!=0) ) break;
zLast = z;
}
return zLast;
}
/*
** Print a section of a filelist hierarchy graph. This is a helper
** routine for print_filelist_as_tree() below.
*/
static const char *print_filelist_section(
const char *zIn, /* List of filenames, separated by \n */
const char *zLast, /* Last filename in the list to print */
const char *zPrefix, /* Prefix so put before each output line */
int nDir /* Ignore this many characters of directory name */
){
/* Unicode box-drawing characters: U+251C, U+2514, U+2502 */
const char *zENTRY = "\342\224\234\342\224\200\342\224\200 ";
const char *zLASTE = "\342\224\224\342\224\200\342\224\200 ";
const char *zCONTU = "\342\224\202 ";
const char *zBLANK = " ";
while( zIn<=zLast ){
int i;
for(i=nDir; zIn[i]!='\n' && zIn[i]!='/'; i++){}
if( zIn[i]=='/' ){
char *zSubPrefix;
const char *zSubLast = last_line(zIn, i+1);
zSubPrefix = mprintf("%s%s", zPrefix, zSubLast==zLast ? zBLANK : zCONTU);
fossil_print("%s%s%.*s\n", zPrefix, zSubLast==zLast ? zLASTE : zENTRY,
i-nDir, &zIn[nDir]);
zIn = print_filelist_section(zIn, zSubLast, zSubPrefix, i+1);
fossil_free(zSubPrefix);
}else{
fossil_print("%s%s%.*s\n", zPrefix, zIn==zLast ? zLASTE : zENTRY,
i-nDir, &zIn[nDir]);
zIn = next_line(zIn);
}
}
return zIn;
}
/*
** Input blob pList is a list of filenames, one filename per line,
** in sorted order and with / directory separators. Output this list
** as a tree in a manner similar to the "tree" command on Linux.
*/
static void print_filelist_as_tree(Blob *pList){
char *zAll;
const char *zLast;
fossil_print("%s\n", g.zLocalRoot);
zAll = blob_str(pList);
if( zAll[0] ){
zLast = last_line(zAll, 0);
print_filelist_section(zAll, zLast, "", 0);
}
}
/*
** Take care of -r version of ls command
*/
static void ls_cmd_rev(
const char *zRev, /* Revision string given */
int verboseFlag, /* Verbose flag given */
int showAge, /* Age flag given */
int timeOrder, /* Order by time flag given */
int treeFmt /* Show output in the tree format */
){
Stmt q;
char *zOrderBy = "pathname COLLATE nocase";
char *zName;
Blob where;
int rid;
int i;
Blob out;
/* Handle given file names */
blob_zero(&where);
for(i=2; i<g.argc; i++){
Blob fname;
file_tree_name(g.argv[i], &fname, 0, 1);
zName = blob_str(&fname);
|
| ︙ | ︙ | |||
647 648 649 650 651 652 653 654 655 656 657 658 |
"SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
" blob.size\n"
" FROM fileage, blob\n"
" WHERE blob.rid=fileage.fid %s\n"
" ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/
);
blob_reset(&where);
while( db_step(&q)==SQLITE_ROW ){
const char *zTime = db_column_text(&q,0);
const char *zFile = db_column_text(&q,1);
int size = db_column_int(&q,2);
| > > > | > > > > | 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 756 757 758 759 760 761 762 |
"SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
" blob.size\n"
" FROM fileage, blob\n"
" WHERE blob.rid=fileage.fid %s\n"
" ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/
);
blob_reset(&where);
if( treeFmt ) blob_init(&out, 0, 0);
while( db_step(&q)==SQLITE_ROW ){
const char *zTime = db_column_text(&q,0);
const char *zFile = db_column_text(&q,1);
int size = db_column_int(&q,2);
if( treeFmt ){
blob_appendf(&out, "%s\n", zFile);
}else if( verboseFlag ){
fossil_print("%s %7d %s\n", zTime, size, zFile);
}else if( showAge ){
fossil_print("%s %s\n", zTime, zFile);
}else{
fossil_print("%s\n", zFile);
}
}
db_finalize(&q);
if( treeFmt ){
print_filelist_as_tree(&out);
blob_reset(&out);
}
}
/*
** COMMAND: ls
**
** Usage: %fossil ls ?OPTIONS? ?PATHS ...?
**
|
| ︙ | ︙ | |||
690 691 692 693 694 695 696 | ** ** The -t option changes the sort order. Without -t, files are sorted by ** path and name (case insensitive sort if -r). If neither --age nor -r ** are used, -t sorts by modification time, otherwise by commit time. ** ** Options: ** --age Show when each file was committed | | | | | > | > > > > > | | 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 |
**
** The -t option changes the sort order. Without -t, files are sorted by
** path and name (case insensitive sort if -r). If neither --age nor -r
** are used, -t sorts by modification time, otherwise by commit time.
**
** Options:
** --age Show when each file was committed
** --hash With -v, verify file status using hashing
** rather than relying on file sizes and mtimes
** -r VERSION The specific check-in to list
** -R|--repository REPO Extract info from repository REPO
** -t Sort output in time order
** --tree Tree format
** -v|--verbose Provide extra information about each file
**
** See also: [[changes]], [[extras]], [[status]], [[tree]]
*/
void ls_cmd(void){
int vid;
Stmt q;
int verboseFlag;
int showAge;
int treeFmt;
int timeOrder;
char *zOrderBy = "pathname";
Blob where;
int i;
int useHash = 0;
const char *zName;
const char *zRev;
verboseFlag = find_option("verbose","v", 0)!=0;
if( !verboseFlag ){
verboseFlag = find_option("l","l", 0)!=0; /* deprecated */
}
showAge = find_option("age",0,0)!=0;
zRev = find_option("r","r",1);
timeOrder = find_option("t","t",0)!=0;
if( verboseFlag ){
useHash = find_option("hash",0,0)!=0;
}
treeFmt = find_option("tree",0,0)!=0;
if( treeFmt ){
if( zRev==0 ) zRev = "current";
}
if( zRev!=0 ){
db_find_and_open_repository(0, 0);
verify_all_options();
ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder,treeFmt);
return;
}else if( find_option("R",0,1)!=0 ){
fossil_fatal("the -r is required in addition to -R");
}
db_must_be_within_tree();
vid = db_lget_int("checkout", 0);
|
| ︙ | ︙ | |||
826 827 828 829 830 831 832 833 834 835 836 837 838 839 |
}else{
fossil_print("%s%s\n", type, zPathname);
}
free(zFullName);
}
db_finalize(&q);
}
/*
** COMMAND: extras
**
** Usage: %fossil extras ?OPTIONS? ?PATH1 ...?
**
** Print a list of all files in the source tree that are not part of the
| > > > > > > > > > > > > > > > > > > > > > > > > > | 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 949 950 951 952 953 954 955 956 957 958 |
}else{
fossil_print("%s%s\n", type, zPathname);
}
free(zFullName);
}
db_finalize(&q);
}
/*
** COMMAND: tree
**
** Usage: %fossil tree ?OPTIONS? ?PATHS ...?
**
** List all files in the current check-out in after the fashion of the
** "tree" command. If PATHS is included, only the named files
** (or their children if directories) are shown.
**
** Options:
** -r VERSION The specific check-in to list
** -R|--repository REPO Extract info from repository REPO
**
** See also: [[ls]]
*/
void tree_cmd(void){
const char *zRev;
zRev = find_option("r","r",1);
if( zRev==0 ) zRev = "current";
db_find_and_open_repository(0, 0);
verify_all_options();
ls_cmd_rev(zRev,0,0,0,1);
}
/*
** COMMAND: extras
**
** Usage: %fossil extras ?OPTIONS? ?PATH1 ...?
**
** Print a list of all files in the source tree that are not part of the
|
| ︙ | ︙ | |||
854 855 856 857 858 859 860 861 862 863 864 865 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 |
** --abs-paths Display absolute pathnames
** --case-sensitive BOOL Override case-sensitive setting
** --dotfiles Include files beginning with a dot (".")
** --header Identify the repository if there are extras
** --ignore CSG Ignore files matching patterns from the argument
** --rel-paths Display pathnames relative to the current working
** directory
**
** See also: [[changes]], [[clean]], [[status]]
*/
void extras_cmd(void){
Blob report = BLOB_INITIALIZER;
const char *zIgnoreFlag = find_option("ignore",0,1);
unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0;
unsigned flags = C_EXTRA;
int showHdr = find_option("header",0,0)!=0;
Glob *pIgnore;
if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
db_must_be_within_tree();
if( determine_cwd_relative_option() ){
flags |= C_RELPATH;
}
if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL;
/* We should be done with options.. */
verify_all_options();
if( zIgnoreFlag==0 ){
zIgnoreFlag = db_get("ignore-glob", 0);
}
pIgnore = glob_create(zIgnoreFlag);
locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore);
glob_free(pIgnore);
blob_zero(&report);
status_report(&report, flags);
if( blob_size(&report) ){
if( showHdr ){
fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"),
g.zLocalRoot);
}
| > > > > > > > > > | > | 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 |
** --abs-paths Display absolute pathnames
** --case-sensitive BOOL Override case-sensitive setting
** --dotfiles Include files beginning with a dot (".")
** --header Identify the repository if there are extras
** --ignore CSG Ignore files matching patterns from the argument
** --rel-paths Display pathnames relative to the current working
** directory
** --tree Show output in the tree format
**
** See also: [[changes]], [[clean]], [[status]]
*/
void extras_cmd(void){
Blob report = BLOB_INITIALIZER;
const char *zIgnoreFlag = find_option("ignore",0,1);
unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0;
unsigned flags = C_EXTRA;
int showHdr = find_option("header",0,0)!=0;
int treeFmt = find_option("tree",0,0)!=0;
Glob *pIgnore;
if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
db_must_be_within_tree();
if( determine_cwd_relative_option() ){
flags |= C_RELPATH;
}
if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL;
if( treeFmt ){
flags &= ~C_RELPATH;
}
/* We should be done with options.. */
verify_all_options();
if( zIgnoreFlag==0 ){
zIgnoreFlag = db_get("ignore-glob", 0);
}
pIgnore = glob_create(zIgnoreFlag);
locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore);
glob_free(pIgnore);
blob_zero(&report);
status_report(&report, flags);
if( blob_size(&report) ){
if( showHdr ){
fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"),
g.zLocalRoot);
}
if( treeFmt ){
print_filelist_as_tree(&report);
}else{
blob_write_to_file(&report, "-");
}
}
blob_reset(&report);
}
/*
** COMMAND: clean
**
|
| ︙ | ︙ | |||
1371 1372 1373 1374 1375 1376 1377 |
Blob sql = BLOB_INITIALIZER;
FileDirList *diffFiles;
int i;
for(i=0; g.aCommitFile[i]!=0; ++i){}
diffFiles = fossil_malloc_zero((i+1) * sizeof(*diffFiles));
for(i=0; g.aCommitFile[i]!=0; ++i){
blob_append_sql(&sql,
| | > | 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 |
Blob sql = BLOB_INITIALIZER;
FileDirList *diffFiles;
int i;
for(i=0; g.aCommitFile[i]!=0; ++i){}
diffFiles = fossil_malloc_zero((i+1) * sizeof(*diffFiles));
for(i=0; g.aCommitFile[i]!=0; ++i){
blob_append_sql(&sql,
"SELECT pathname, deleted, rid "
"FROM vfile WHERE id=%d",
g.aCommitFile[i]);
db_prepare(&q, "%s", blob_sql_text(&sql));
blob_reset(&sql);
assert( db_step(&q)==SQLITE_ROW );
diffFiles[i].zName = fossil_strdup(db_column_text(&q, 0));
DCfg.diffFlags &= (~DIFF_FILE_MASK);
if( db_column_int(&q, 1) ){
|
| ︙ | ︙ | |||
2178 2179 2180 2181 2182 2183 2184 | ** --allow-empty Allow a commit with no changes ** --allow-fork Allow the commit to fork ** --allow-older Allow a commit older than its ancestor ** --baseline Use a baseline manifest in the commit process ** --bgcolor COLOR Apply COLOR to this one check-in only ** --branch NEW-BRANCH-NAME Check in to this new branch ** --branchcolor COLOR Apply given COLOR to the branch | < < | 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 | ** --allow-empty Allow a commit with no changes ** --allow-fork Allow the commit to fork ** --allow-older Allow a commit older than its ancestor ** --baseline Use a baseline manifest in the commit process ** --bgcolor COLOR Apply COLOR to this one check-in only ** --branch NEW-BRANCH-NAME Check in to this new branch ** --branchcolor COLOR Apply given COLOR to the branch ** --close Close the branch being committed ** --date-override DATETIME DATE to use instead of 'now' ** --delta Use a delta manifest in the commit process ** --hash Verify file status using hashing rather ** than relying on file mtimes ** --ignore-clock-skew If a clock skew is detected, ignore it and ** behave as if the user had entered 'yes' to |
| ︙ | ︙ | |||
2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 | ** -v|--verbose Show a diff in the commit message prompt ** --no-prompt This option disables prompting the user for ** input and assumes an answer of 'No' for every ** question. ** --no-warnings Omit all warnings about file contents ** --no-verify Do not run before-commit hooks ** --nosign Do not attempt to sign this commit with gpg ** --override-lock Allow a check-in even though parent is locked ** --private Do not sync changes and their descendants ** --tag TAG-NAME Assign given tag TAG-NAME to the check-in ** --trace Debug tracing ** --user-override USER USER to use instead of the current default ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in | > | 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 | ** -v|--verbose Show a diff in the commit message prompt ** --no-prompt This option disables prompting the user for ** input and assumes an answer of 'No' for every ** question. ** --no-warnings Omit all warnings about file contents ** --no-verify Do not run before-commit hooks ** --nosign Do not attempt to sign this commit with gpg ** --nosync Do not auto-sync prior to committing ** --override-lock Allow a check-in even though parent is locked ** --private Do not sync changes and their descendants ** --tag TAG-NAME Assign given tag TAG-NAME to the check-in ** --trace Debug tracing ** --user-override USER USER to use instead of the current default ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| ︙ | ︙ | |||
2262 2263 2264 2265 2266 2267 2268 | int szD; /* Size of the delta manifest */ int szB; /* Size of the baseline manifest */ int nConflict = 0; /* Number of unresolved merge conflicts */ int abortCommit = 0; /* Abort the commit due to text format conversions */ Blob ans; /* Answer to continuation prompts */ char cReply; /* First character of ans */ int bRecheck = 0; /* Repeat fork and closed-branch checks*/ | < > | 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 |
int szD; /* Size of the delta manifest */
int szB; /* Size of the baseline manifest */
int nConflict = 0; /* Number of unresolved merge conflicts */
int abortCommit = 0; /* Abort the commit due to text format conversions */
Blob ans; /* Answer to continuation prompts */
char cReply; /* First character of ans */
int bRecheck = 0; /* Repeat fork and closed-branch checks*/
int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
int mxSize;
memset(&sCiInfo, 0, sizeof(sCiInfo));
url_proxy_options();
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
noSign = find_option("nosign",0,0)!=0;
if( find_option("nosync",0,0) ) g.fNoSync = 1;
privateFlag = find_option("private",0,0)!=0;
forceDelta = find_option("delta",0,0)!=0;
forceBaseline = find_option("baseline",0,0)!=0;
db_must_be_within_tree();
if( db_get_boolean("dont-commit",0) ){
fossil_fatal("committing is prohibited: the 'dont-commit' option is set");
}
|
| ︙ | ︙ | |||
2304 2305 2306 2307 2308 2309 2310 |
noPrompt = find_option("no-prompt", 0, 0)!=0;
noWarningFlag = find_option("no-warnings", 0, 0)!=0;
noVerify = find_option("no-verify",0,0)!=0;
bTrace = find_option("trace",0,0)!=0;
sCiInfo.zBranch = find_option("branch","b",1);
sCiInfo.zColor = find_option("bgcolor",0,1);
sCiInfo.zBrClr = find_option("branchcolor",0,1);
| < < < < | 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 |
noPrompt = find_option("no-prompt", 0, 0)!=0;
noWarningFlag = find_option("no-warnings", 0, 0)!=0;
noVerify = find_option("no-verify",0,0)!=0;
bTrace = find_option("trace",0,0)!=0;
sCiInfo.zBranch = find_option("branch","b",1);
sCiInfo.zColor = find_option("bgcolor",0,1);
sCiInfo.zBrClr = find_option("branchcolor",0,1);
sCiInfo.closeFlag = find_option("close",0,0)!=0;
sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
sCiInfo.zMimetype = find_option("mimetype",0,1);
sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0;
while( (zTag = find_option("tag",0,1))!=0 ){
if( zTag[0]==0 ) continue;
sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag,
|
| ︙ | ︙ | |||
2349 2350 2351 2352 2353 2354 2355 |
/* Track the "private" status */
g.markPrivate = privateFlag || privateParent;
if( privateFlag && !privateParent ){
/* Apply default branch name ("private") and color ("orange") if not
** specified otherwise on the command-line, and if the parent is not
** already private. */
if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
| < < < | 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 |
/* Track the "private" status */
g.markPrivate = privateFlag || privateParent;
if( privateFlag && !privateParent ){
/* Apply default branch name ("private") and color ("orange") if not
** specified otherwise on the command-line, and if the parent is not
** already private. */
if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
}
/* Do not allow the creation of a new branch using an existing open
** branch name unless the --force flag is used */
if( sCiInfo.zBranch!=0
&& !forceFlag
&& fossil_strcmp(sCiInfo.zBranch,"private")!=0
|
| ︙ | ︙ | |||
2533 2534 2535 2536 2537 2538 2539 |
"use --override-lock",
g.ckinLockFail);
}else{
fossil_fatal("Would fork. \"update\" first or use --branch or "
"--allow-fork.");
}
}
| | | | 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 |
"use --override-lock",
g.ckinLockFail);
}else{
fossil_fatal("Would fork. \"update\" first or use --branch or "
"--allow-fork.");
}
}
/*
** Do not allow a commit against a closed leaf unless the commit
** ends up on a different branch.
*/
if(
/* parent check-in has the "closed" tag... */
leaf_is_closed(vid)
/* ... and the new check-in has no --branch option or the --branch
** option does not actually change the branch */
&& (sCiInfo.zBranch==0
|| db_exists("SELECT 1 FROM tagxref"
" WHERE tagid=%d AND rid=%d AND tagtype>0"
" AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch))
){
fossil_fatal("cannot commit against a closed leaf");
}
/* Always exit the loop on the second pass */
if( bRecheck ) break;
/* Get the check-in comment. This might involve prompting the
** user for the check-in comment, in which case we should resync
** to renew the check-in lock and repeat the checks for conflicts.
*/
if( zComment ){
blob_zero(&comment);
blob_append(&comment, zComment, -1);
|
| ︙ | ︙ |
Changes to src/clone.c.
| ︙ | ︙ | |||
134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
** --save-http-password Remember the HTTP password without asking
** -c|--ssh-command SSH Use SSH as the "ssh" command
** --ssl-identity FILENAME Use the SSL identity if requested by the server
** --transport-command CMD Use CMD to move messages to the server and back
** -u|--unversioned Also sync unversioned content
** -v|--verbose Show more statistics in output
** --workdir DIR Also open a check-out in DIR
**
** See also: [[init]], [[open]]
*/
void clone_cmd(void){
char *zPassword;
const char *zDefaultUser; /* Optional name of the default user */
const char *zHttpAuth; /* HTTP Authorization user:pass information */
| > | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
** --save-http-password Remember the HTTP password without asking
** -c|--ssh-command SSH Use SSH as the "ssh" command
** --ssl-identity FILENAME Use the SSL identity if requested by the server
** --transport-command CMD Use CMD to move messages to the server and back
** -u|--unversioned Also sync unversioned content
** -v|--verbose Show more statistics in output
** --workdir DIR Also open a check-out in DIR
** --xverbose Extra debugging output
**
** See also: [[init]], [[open]]
*/
void clone_cmd(void){
char *zPassword;
const char *zDefaultUser; /* Optional name of the default user */
const char *zHttpAuth; /* HTTP Authorization user:pass information */
|
| ︙ | ︙ | |||
159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE;
if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER;
if( find_option("save-http-password",0,0)!=0 ){
urlFlags &= ~URL_PROMPT_PW;
urlFlags |= URL_REMEMBER_PW;
}
if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE;
if( find_option("unversioned","u",0)!=0 ){
syncFlags |= SYNC_UNVERSIONED;
if( syncFlags & SYNC_VERBOSE ){
syncFlags |= SYNC_UV_TRACE;
}
}
zHttpAuth = find_option("httpauth","B",1);
| > | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE;
if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER;
if( find_option("save-http-password",0,0)!=0 ){
urlFlags &= ~URL_PROMPT_PW;
urlFlags |= URL_REMEMBER_PW;
}
if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE;
if( find_option("xverbose",0,0)!=0) syncFlags |= SYNC_XVERBOSE;
if( find_option("unversioned","u",0)!=0 ){
syncFlags |= SYNC_UNVERSIONED;
if( syncFlags & SYNC_VERBOSE ){
syncFlags |= SYNC_UV_TRACE;
}
}
zHttpAuth = find_option("httpauth","B",1);
|
| ︙ | ︙ | |||
194 195 196 197 198 199 200 |
g.argv[2]);
}
zRepo = mprintf("./%s.fossil", zBase);
if( zWorkDir==0 ){
zWorkDir = mprintf("./%s", zBase);
}
fossil_free(zBase);
| | | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
g.argv[2]);
}
zRepo = mprintf("./%s.fossil", zBase);
if( zWorkDir==0 ){
zWorkDir = mprintf("./%s", zBase);
}
fossil_free(zBase);
}
if( -1 != file_size(zRepo, ExtFILE) ){
fossil_fatal("file already exists: %s", zRepo);
}
/* Fail before clone if open will fail because inside an open check-out */
if( zWorkDir!=0 && zWorkDir[0]!=0 && !noOpen ){
if( db_open_local_v2(0, allowNested) ){
fossil_fatal("there is already an open tree at %s", g.zLocalRoot);
|
| ︙ | ︙ | |||
258 259 260 261 262 263 264 |
"DELETE FROM config WHERE name='project-code';"
);
db_protect_pop();
url_enable_proxy(0);
clone_ssh_db_set_options();
url_get_password_if_needed();
g.xlinkClusterOnly = 1;
| | > > > > > > > | > > > | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
"DELETE FROM config WHERE name='project-code';"
);
db_protect_pop();
url_enable_proxy(0);
clone_ssh_db_set_options();
url_get_password_if_needed();
g.xlinkClusterOnly = 1;
nErr = client_sync(syncFlags,CONFIGSET_ALL,0,0,0);
g.xlinkClusterOnly = 0;
verify_cancel();
db_end_transaction(0);
db_close(1);
if( nErr ){
file_delete(zRepo);
if( g.fHttpTrace ){
fossil_fatal(
"server returned an error - clone aborted\n\n%s",
http_last_trace_reply()
);
}else{
fossil_fatal(
"server returned an error - clone aborted\n"
"Rerun using --httptrace for more detail"
);
}
}
db_open_repository(zRepo);
}
db_begin_transaction();
if( db_exists("SELECT 1 FROM delta WHERE srcId IN phantom") ){
fossil_fatal("there are unresolved deltas -"
" the clone is probably incomplete and unusable.");
|
| ︙ | ︙ |
Changes to src/configure.c.
| ︙ | ︙ | |||
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 |
} aConfig[] = {
{ "css", CONFIGSET_CSS },
{ "header", CONFIGSET_SKIN },
{ "mainmenu", CONFIGSET_SKIN },
{ "footer", CONFIGSET_SKIN },
{ "details", CONFIGSET_SKIN },
{ "js", CONFIGSET_SKIN },
{ "logo-mimetype", CONFIGSET_SKIN },
{ "logo-image", CONFIGSET_SKIN },
{ "background-mimetype", CONFIGSET_SKIN },
{ "background-image", CONFIGSET_SKIN },
{ "icon-mimetype", CONFIGSET_SKIN },
{ "icon-image", CONFIGSET_SKIN },
{ "timeline-block-markup", CONFIGSET_SKIN },
{ "timeline-date-format", CONFIGSET_SKIN },
{ "timeline-default-style", CONFIGSET_SKIN },
{ "timeline-dwelltime", CONFIGSET_SKIN },
{ "timeline-closetime", CONFIGSET_SKIN },
{ "timeline-max-comment", CONFIGSET_SKIN },
{ "timeline-plaintext", CONFIGSET_SKIN },
{ "timeline-truncate-at-blank", CONFIGSET_SKIN },
{ "timeline-tslink-info", CONFIGSET_SKIN },
{ "timeline-utc", CONFIGSET_SKIN },
{ "adunit", CONFIGSET_SKIN },
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
| > > | 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 |
} aConfig[] = {
{ "css", CONFIGSET_CSS },
{ "header", CONFIGSET_SKIN },
{ "mainmenu", CONFIGSET_SKIN },
{ "footer", CONFIGSET_SKIN },
{ "details", CONFIGSET_SKIN },
{ "js", CONFIGSET_SKIN },
{ "default-skin", CONFIGSET_SKIN },
{ "logo-mimetype", CONFIGSET_SKIN },
{ "logo-image", CONFIGSET_SKIN },
{ "background-mimetype", CONFIGSET_SKIN },
{ "background-image", CONFIGSET_SKIN },
{ "icon-mimetype", CONFIGSET_SKIN },
{ "icon-image", CONFIGSET_SKIN },
{ "timeline-block-markup", CONFIGSET_SKIN },
{ "timeline-date-format", CONFIGSET_SKIN },
{ "timeline-default-style", CONFIGSET_SKIN },
{ "timeline-dwelltime", CONFIGSET_SKIN },
{ "timeline-closetime", CONFIGSET_SKIN },
{ "timeline-hard-newlines", CONFIGSET_SKIN },
{ "timeline-max-comment", CONFIGSET_SKIN },
{ "timeline-plaintext", CONFIGSET_SKIN },
{ "timeline-truncate-at-blank", CONFIGSET_SKIN },
{ "timeline-tslink-info", CONFIGSET_SKIN },
{ "timeline-utc", CONFIGSET_SKIN },
{ "adunit", CONFIGSET_SKIN },
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
|
| ︙ | ︙ | |||
868 869 870 871 872 873 874 |
}
url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG);
if( g.url.protocol==0 ) fossil_fatal("no server URL specified");
user_select();
url_enable_proxy("via proxy: ");
if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
if( strncmp(zMethod, "push", n)==0 ){
| | | | | 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 |
}
url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG);
if( g.url.protocol==0 ) fossil_fatal("no server URL specified");
user_select();
url_enable_proxy("via proxy: ");
if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
if( strncmp(zMethod, "push", n)==0 ){
client_sync(0,0,(unsigned)mask,0,0);
}else if( strncmp(zMethod, "pull", n)==0 ){
if( overwriteFlag ) db_unprotect(PROTECT_USER);
client_sync(0,(unsigned)mask,0,0,0);
if( overwriteFlag ) db_protect_pop();
}else{
client_sync(0,(unsigned)mask,(unsigned)mask,0,0);
}
}else
if( strncmp(zMethod, "reset", n)==0 ){
int mask, i;
char *zBackup;
if( g.argc!=4 ) usage("reset AREA");
mask = configure_name_to_mask(g.argv[3], 1);
|
| ︙ | ︙ |
Changes to src/cookies.c.
| ︙ | ︙ | |||
209 210 211 212 213 214 215 216 217 |
const char *cookie_value(const char *zPName, const char *zDefault){
int i;
assert( zPName!=0 );
cookie_parse();
for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){}
return i<cookies.nParam ? cookies.aParam[i].zPValue : zDefault;
}
/*
| > > > > > > > > > > > > > > > | > > > > > | > > | | > > > > > > > > > > > > > > > > > > > | > > > | 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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
const char *cookie_value(const char *zPName, const char *zDefault){
int i;
assert( zPName!=0 );
cookie_parse();
for(i=0; i<cookies.nParam && strcmp(zPName,cookies.aParam[i].zPName); i++){}
return i<cookies.nParam ? cookies.aParam[i].zPValue : zDefault;
}
/* Return the number of characters of hex in the prefix to the
** given string.
*/
static int hex_prefix_length(const char *z){
int i;
for(i=0; fossil_isXdigit(z[i]); i++){}
return i;
}
/*
** WEBPAGE: cookies
**
** Show all cookies associated with Fossil. This shows the text of the
** login cookie and is hence dangerous if an adversary is looking over
** your shoulder and is able to read and reproduce that cookie.
**
** WEBPAGE: fdscookie
**
** Show the current display settings contained in the
** "fossil_display_settings" cookie.
*/
void cookie_page(void){
int i;
int nCookie = 0;
const char *zName = 0;
const char *zValue = 0;
const char *zLoginCookie = login_cookie_name();
int isQP = 0;
int bFDSonly = strstr(g.zPath, "fdscookie")!=0;
cookie_parse();
if( bFDSonly ){
style_header("Display Preferences Cookie");
}else{
style_header("All Cookies");
}
@ <form method="POST">
@ <ol>
for(i=0; cgi_param_info(i, &zName, &zValue, &isQP); i++){
char *zDel;
if( isQP ) continue;
if( fossil_isupper(zName[0]) ) continue;
if( bFDSonly && strcmp(zName, "fossil_display_settings")!=0 ) continue;
zDel = mprintf("del%s",zName);
if( P(zDel)!=0 ){
cgi_set_cookie(zName, "", 0, -1);
cgi_redirect(g.zPath);
}
nCookie++;
@ <li><p><b>%h(zName)</b>: %h(zValue)
@ <input type="submit" name="%h(zDel)" value="Delete">
if( fossil_strcmp(zName, DISPLAY_SETTINGS_COOKIE)==0 && cookies.nParam>0 ){
int j;
@ <p>This cookie remembers your Fossil display preferences.
@ <ul>
for(j=0; j<cookies.nParam; j++){
@ <li>%h(cookies.aParam[j].zPName): "%h(cookies.aParam[j].zPValue)"
}
@ </ul>
}else
if( fossil_strcmp(zName, zLoginCookie)==0 ){
@ <p>This is your login cookie. If you delete this cookie, you will
@ be logged out.
}else
if( fossil_strncmp(zName, "fossil-", 7)==0
&& strlen(zName)==23
&& hex_prefix_length(&zName[7])==16
&& hex_prefix_length(zValue)>24
){
@ <p>This appears to be a login cookie for another Fossil repository
@ in the same website.
}
else {
@ <p>This cookie was not generated by Fossil. It might be something
@ from another program on the same website.
}
fossil_free(zDel);
}
@ </ol>
@ </form>
if( nCookie==0 ){
if( bFDSonly ){
@ <p><i>Your browser is not holding a "fossil_display_setting" cookie
@ for this website</i></p>
}else{
@ <p><i>Your browser is not holding any cookies for this website</i></p>
}
}
style_finish_page();
}
|
Changes to src/db.c.
| ︙ | ︙ | |||
455 456 457 458 459 460 461 |
** be compromised by an attack.
*/
void db_protect_only(unsigned flags){
if( db.nProtect>=count(db.aProtect)-2 ){
fossil_panic("too many db_protect() calls");
}
db.aProtect[db.nProtect++] = db.protectMask;
| | | 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 |
** be compromised by an attack.
*/
void db_protect_only(unsigned flags){
if( db.nProtect>=count(db.aProtect)-2 ){
fossil_panic("too many db_protect() calls");
}
db.aProtect[db.nProtect++] = db.protectMask;
if( (flags & PROTECT_SENSITIVE)!=0
&& db.bProtectTriggers==0
&& g.repositoryOpen
){
/* Create the triggers needed to protect sensitive settings from
** being created or modified the first time that PROTECT_SENSITIVE
** is enabled. Deleting a sensitive setting is harmless, so there
** is not trigger to block deletes. After being created once, the
|
| ︙ | ︙ | |||
894 895 896 897 898 899 900 |
const char *db_column_name(Stmt *pStmt, int N){
return (char*)sqlite3_column_name(pStmt->pStmt, N);
}
int db_column_count(Stmt *pStmt){
return sqlite3_column_count(pStmt->pStmt);
}
char *db_column_malloc(Stmt *pStmt, int N){
| | | 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 |
const char *db_column_name(Stmt *pStmt, int N){
return (char*)sqlite3_column_name(pStmt->pStmt, N);
}
int db_column_count(Stmt *pStmt){
return sqlite3_column_count(pStmt->pStmt);
}
char *db_column_malloc(Stmt *pStmt, int N){
return fossil_strdup_nn(db_column_text(pStmt, N));
}
void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){
blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N),
sqlite3_column_bytes(pStmt->pStmt, N));
}
Blob db_column_text_as_blob(Stmt *pStmt, int N){
Blob x;
|
| ︙ | ︙ | |||
1190 1191 1192 1193 1194 1195 1196 |
va_list ap;
Stmt s;
char *z;
va_start(ap, zSql);
db_vprepare(&s, 0, zSql, ap);
va_end(ap);
if( db_step(&s)==SQLITE_ROW ){
| | < < | | 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 |
va_list ap;
Stmt s;
char *z;
va_start(ap, zSql);
db_vprepare(&s, 0, zSql, ap);
va_end(ap);
if( db_step(&s)==SQLITE_ROW ){
z = fossil_strdup_nn((const char*)sqlite3_column_text(s.pStmt, 0));
}else{
z = fossil_strdup(zDefault);
}
db_finalize(&s);
return z;
}
/*
** Initialize a new database file with the given schema. If anything
|
| ︙ | ︙ | |||
1435 1436 1437 1438 1439 1440 1441 |
return;
}
if( sqlite3_user_data(context)==0 ){
zTemp = obscure((char*)zIn);
}else{
zTemp = unobscure((char*)zIn);
}
| | | 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 |
return;
}
if( sqlite3_user_data(context)==0 ){
zTemp = obscure((char*)zIn);
}else{
zTemp = unobscure((char*)zIn);
}
fossil_strcpy(zOut, zTemp);
fossil_free(zTemp);
sqlite3_result_text(context, zOut, strlen(zOut), sqlite3_free);
}
/*
** Return True if zName is a protected (a.k.a. "sensitive") setting.
*/
|
| ︙ | ︙ | |||
1555 1556 1557 1558 1559 1560 1561 |
sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0,
db_protected_setting_func, 0, 0);
sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0,
db_win_reserved_func,0,0);
sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
url_nouser_func,0,0);
sqlite3_create_function(db, "chat_msg_from_event", 4,
| | | | 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 |
sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0,
db_protected_setting_func, 0, 0);
sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0,
db_win_reserved_func,0,0);
sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
url_nouser_func,0,0);
sqlite3_create_function(db, "chat_msg_from_event", 4,
SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
chat_msg_from_event, 0, 0);
}
#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/
static char *zSavedKey = 0;
|
| ︙ | ︙ | |||
2487 2488 2489 2490 2491 2492 2493 |
db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
}
if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
}
}
| | | 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 |
db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
}
if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
}
}
/* The design of the check-out database changed on 2019-01-19 adding the mhash
** column to vfile and vmerge and changing the UNIQUE index on vmerge into
** a PRIMARY KEY that includes the new mhash column. However, we must have
** the repository database at hand in order to do the migration, so that
** step is deferred. */
return 1;
}
|
| ︙ | ︙ | |||
2532 2533 2534 2535 2536 2537 2538 |
for(i=0; i<count(aDbName); i++){
sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "/%s", aDbName[i]);
if( isValidLocalDb(zPwd) ){
if( db_open_config(0, 1)==0 ){
return 0; /* Configuration could not be opened */
}
/* Found a valid check-out database file */
| | | 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 |
for(i=0; i<count(aDbName); i++){
sqlite3_snprintf(sizeof(zPwd)-n, &zPwd[n], "/%s", aDbName[i]);
if( isValidLocalDb(zPwd) ){
if( db_open_config(0, 1)==0 ){
return 0; /* Configuration could not be opened */
}
/* Found a valid check-out database file */
g.zLocalDbName = fossil_strdup(zPwd);
zPwd[n] = 0;
while( n>0 && zPwd[n-1]=='/' ){
n--;
zPwd[n] = 0;
}
g.zLocalRoot = mprintf("%s/", zPwd);
g.localOpen = 1;
|
| ︙ | ︙ | |||
2604 2605 2606 2607 2608 2609 2610 | sqlite3_stmt *pStmt = 0; sz = file_size(zDbName, ExtFILE); if( sz<16834 ) return 0; db = db_open(zDbName); if( !db ) return 0; if( !g.zVfsName && sz%512 ) return 0; | | | 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 |
sqlite3_stmt *pStmt = 0;
sz = file_size(zDbName, ExtFILE);
if( sz<16834 ) return 0;
db = db_open(zDbName);
if( !db ) return 0;
if( !g.zVfsName && sz%512 ) return 0;
rc = sqlite3_prepare_v2(db,
"SELECT count(*) FROM sqlite_schema"
" WHERE name COLLATE nocase IN"
"('blob','delta','rcvfrom','user','config','mlink','plink');",
-1, &pStmt, 0);
if( rc ) goto is_repo_end;
rc = sqlite3_step(pStmt);
if( rc!=SQLITE_ROW ) goto is_repo_end;
|
| ︙ | ︙ | |||
2668 2669 2670 2671 2672 2673 2674 |
}else{
#ifdef FOSSIL_ENABLE_JSON
g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
#endif
fossil_fatal("not a valid repository: %s", zDbName);
}
}
| | | 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 |
}else{
#ifdef FOSSIL_ENABLE_JSON
g.json.resultCode = FSL_JSON_E_DB_NOT_VALID;
#endif
fossil_fatal("not a valid repository: %s", zDbName);
}
}
g.zRepositoryName = fossil_strdup(zDbName);
db_open_or_attach(g.zRepositoryName, "repository");
g.repositoryOpen = 1;
sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION,
&g.iRepoDataVers);
/* Cache "allow-symlinks" option, because we'll need it on every stat call */
g.allowSymlinks = db_get_boolean("allow-symlinks",0);
|
| ︙ | ︙ | |||
3228 3229 3230 3231 3232 3233 3234 | ** By default, all settings will be initialized to their default values. ** This can be overridden using the --template parameter to specify a ** repository file from which to copy the initial settings. When a template ** repository is used, almost all of the settings accessible from the setup ** page, either directly or indirectly, will be copied. Normal users and ** their associated permissions will not be copied; however, the system ** default users "anonymous", "nobody", "reader", "developer", and their | | > | 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 | ** By default, all settings will be initialized to their default values. ** This can be overridden using the --template parameter to specify a ** repository file from which to copy the initial settings. When a template ** repository is used, almost all of the settings accessible from the setup ** page, either directly or indirectly, will be copied. Normal users and ** their associated permissions will not be copied; however, the system ** default users "anonymous", "nobody", "reader", "developer", and their ** associated permissions will be copied. In case of SQL errors, rebuild the ** template repository and try again. ** ** Options: ** --template FILE Copy settings from repository file ** -A|--admin-user USERNAME Select given USERNAME as admin user ** --date-override DATETIME Use DATETIME as time of the initial check-in ** --sha1 Use an initial hash policy of "sha1" ** --project-name STRING The name of the project "project name in |
| ︙ | ︙ | |||
3507 3508 3509 3510 3511 3512 3513 |
char *zOut;
if( g.perm.RdAddr ){
zOut = db_text(0, "SELECT content FROM concealed WHERE hash=%Q", zKey);
}else{
zOut = 0;
}
if( zOut==0 ){
| | | 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 |
char *zOut;
if( g.perm.RdAddr ){
zOut = db_text(0, "SELECT content FROM concealed WHERE hash=%Q", zKey);
}else{
zOut = 0;
}
if( zOut==0 ){
zOut = fossil_strdup_nn(zKey);
}
return zOut;
}
/*
** Return true if the string zVal represents "true" (or "false").
*/
|
| ︙ | ︙ | |||
3714 3715 3716 3717 3718 3719 3720 |
z = fossil_strdup(pSetting->def);
}else{
z = fossil_strdup(zDefault);
}
}
return z;
}
| | > | 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 |
z = fossil_strdup(pSetting->def);
}else{
z = fossil_strdup(zDefault);
}
}
return z;
}
char *db_get_mtime(const char *zName, const char *zFormat,
const char *zDefault){
char *z = 0;
if( g.repositoryOpen ){
z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName);
}
if( z==0 ){
z = fossil_strdup(zDefault);
}else if( zFormat!=0 ){
|
| ︙ | ︙ | |||
4019 4020 4021 4022 4023 4024 4025 |
if( !g.localOpen ) return;
zName = db_repository_filename();
}
file_canonical_name(zName, &full, 0);
(void)filename_collation(); /* Initialize before connection swap */
db_swap_connections();
zRepoSetting = mprintf("repo:%q", blob_str(&full));
| | | 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 |
if( !g.localOpen ) return;
zName = db_repository_filename();
}
file_canonical_name(zName, &full, 0);
(void)filename_collation(); /* Initialize before connection swap */
db_swap_connections();
zRepoSetting = mprintf("repo:%q", blob_str(&full));
db_unprotect(PROTECT_CONFIG);
db_multi_exec(
"DELETE FROM global_config WHERE name %s = %Q;",
filename_collation(), zRepoSetting
);
db_multi_exec(
"INSERT OR IGNORE INTO global_config(name,value)"
|
| ︙ | ︙ | |||
4183 4184 4185 4186 4187 4188 4189 |
if( keepFlag==0
&& bForce==0
&& (nLocal = file_directory_size(".", 0, 1))>0
&& (nLocal>1 || isUri || !file_in_cwd(zRepo))
){
fossil_fatal("directory %s is not empty\n"
"use the -f (--force) option to override\n"
| | | 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 |
if( keepFlag==0
&& bForce==0
&& (nLocal = file_directory_size(".", 0, 1))>0
&& (nLocal>1 || isUri || !file_in_cwd(zRepo))
){
fossil_fatal("directory %s is not empty\n"
"use the -f (--force) option to override\n"
"or the -k (--keep) option to keep local files unchanged",
file_getcwd(0,0));
}
if( db_open_local_v2(0, allowNested) ){
fossil_fatal("there is already an open tree at %s", g.zLocalRoot);
}
|
| ︙ | ︙ | |||
4205 4206 4207 4208 4209 4210 4211 |
zUri = zRepo;
zNewBase = url_to_repo_basename(zUri);
if( zNewBase==0 ){
fossil_fatal("unable to deduce a repository name from the url \"%s\"",
zUri);
}
| | > > | > | 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 |
zUri = zRepo;
zNewBase = url_to_repo_basename(zUri);
if( zNewBase==0 ){
fossil_fatal("unable to deduce a repository name from the url \"%s\"",
zUri);
}
if( zRepoDir==0 ){
zRepo = mprintf("%s.fossil", zNewBase);
}else{
zRepo = mprintf("%s/%s.fossil", zRepoDir, zNewBase);
}
fossil_free(zNewBase);
blob_init(&cmd, 0, 0);
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
blob_append(&cmd, " clone", -1);
if(0!=bVerbose){
blob_append(&cmd, " --verbose", -1);
}
|
| ︙ | ︙ | |||
4389 4390 4391 4392 4393 4394 4395 | ** ** When the admin-log setting is enabled, configuration changes are recorded ** in the "admin_log" table of the repository. */ /* ** SETTING: allow-symlinks boolean default=off sensitive ** | | | 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 | ** ** When the admin-log setting is enabled, configuration changes are recorded ** in the "admin_log" table of the repository. */ /* ** SETTING: allow-symlinks boolean default=off sensitive ** ** When allow-symlinks is OFF, Fossil does not see symbolic links ** (a.k.a "symlinks") on disk as a separate class of object. Instead Fossil ** sees the object that the symlink points to. Fossil will only manage files ** and directories, not symlinks. When a symlink is added to a repository, ** the object that the symlink points to is added, not the symlink itself. ** ** When allow-symlinks is ON, Fossil sees symlinks on disk as a separate ** object class that is distinct from files and directories. When a symlink |
| ︙ | ︙ | |||
4447 4448 4449 4450 4451 4452 4453 | ** When the auto-hyperlink setting is 1, the javascript that runs to set ** the href= attributes of hyperlinks delays by this many milliseconds ** after the page load. Suggested values: 50 to 200. */ /* ** SETTING: auto-hyperlink-mouseover boolean default=off ** | | | 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 | ** When the auto-hyperlink setting is 1, the javascript that runs to set ** the href= attributes of hyperlinks delays by this many milliseconds ** after the page load. Suggested values: 50 to 200. */ /* ** SETTING: auto-hyperlink-mouseover boolean default=off ** ** When the auto-hyperlink setting is 1 and this setting is on, the ** javascript that runs to set the href= attributes of hyperlinks waits ** until either a mousedown or mousemove event is seen. This helps ** to distinguish real users from robots. For maximum robot defense, ** the recommended setting is ON. */ /* ** SETTING: auto-shun boolean default=on |
| ︙ | ︙ | |||
4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 | ** off,commit=pullonly Do not autosync, except do a pull before each ** "commit", presumably to avoid undesirable ** forks. ** ** The syntax is a comma-separated list of VALUE and COMMAND=VALUE entries. ** A plain VALUE entry is the default that is used if no COMMAND matches. ** Otherwise, the VALUE of the matching command is used. */ /* ** SETTING: autosync-tries width=16 default=1 ** If autosync is enabled setting this to a value greater ** than zero will cause autosync to try no more than this ** number of attempts if there is a sync failure. */ | > > > | 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 | ** off,commit=pullonly Do not autosync, except do a pull before each ** "commit", presumably to avoid undesirable ** forks. ** ** The syntax is a comma-separated list of VALUE and COMMAND=VALUE entries. ** A plain VALUE entry is the default that is used if no COMMAND matches. ** Otherwise, the VALUE of the matching command is used. ** ** The "all" value is special in that it applies to the "sync" command in ** addition to "commit", "merge", "open", and "update". */ /* ** SETTING: autosync-tries width=16 default=1 ** If autosync is enabled setting this to a value greater ** than zero will cause autosync to try no more than this ** number of attempts if there is a sync failure. */ |
| ︙ | ︙ | |||
4668 4669 4670 4671 4672 4673 4674 | ** Note that /fileedit cannot edit binary files, so the list should not ** contain any globs for, e.g., images or PDFs. */ /* ** SETTING: forbid-delta-manifests boolean default=off ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts | | | 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 | ** Note that /fileedit cannot edit binary files, so the list should not ** contain any globs for, e.g., images or PDFs. */ /* ** SETTING: forbid-delta-manifests boolean default=off ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts ** to obtain a check-in lock during auto-sync, the server will ** send the "pragma avoid-delta-manifests" statement in its reply, ** which will cause the client to avoid generating a delta ** manifest. */ /* ** SETTING: forum-close-policy boolean default=off ** If true, forum moderators may close/re-open forum posts, and reply |
| ︙ | ︙ | |||
5009 5010 5011 5012 5013 5014 5015 | ** Defaults to "start" on windows, "open" on Mac, ** and "firefox" on Unix. */ /* ** SETTING: large-file-size width=10 default=200000000 ** Fossil considers any file whose size is greater than this value ** to be a "large file". Fossil might issue warnings if you try to | | | 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 | ** Defaults to "start" on windows, "open" on Mac, ** and "firefox" on Unix. */ /* ** SETTING: large-file-size width=10 default=200000000 ** Fossil considers any file whose size is greater than this value ** to be a "large file". Fossil might issue warnings if you try to ** "add" or "commit" a "large file". Set this value to 0 or less ** to disable all such warnings. */ /* ** Look up a control setting by its name. Return a pointer to the Setting ** object, or NULL if there is no such setting. ** |
| ︙ | ︙ | |||
5234 5235 5236 5237 5238 5239 5240 | ** optimization. FILENAME can also be the configuration database file ** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file. ** ** The purpose of this command is for testing the WITHOUT ROWID capabilities ** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil. ** ** Options: | | | 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 |
** optimization. FILENAME can also be the configuration database file
** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file.
**
** The purpose of this command is for testing the WITHOUT ROWID capabilities
** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil.
**
** Options:
** -n|--dry-run No changes. Just print what would happen.
*/
void test_without_rowid(void){
int i, j;
Stmt q;
Blob allSql;
int dryRun = find_option("dry-run", "n", 0)!=0;
for(i=2; i<g.argc; i++){
|
| ︙ | ︙ |
Changes to src/default.css.
| ︙ | ︙ | |||
508 509 510 511 512 513 514 |
padding: 0;
width: 125px;
text-align: center;
border-collapse: collapse;
border-spacing: 0;
}
table.report {
| < | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 |
padding: 0;
width: 125px;
text-align: center;
border-collapse: collapse;
border-spacing: 0;
}
table.report {
border: 1px solid #999;
margin: 1em 0 1em 0;
cursor: pointer;
}
td.rpteditex {
border-width: thin;
border-color: #000000;
|
| ︙ | ︙ | |||
594 595 596 597 598 599 600 |
line-height: 1.275/*for mobile: forum post e6f4ee7de98b55c0*/;
text-size-adjust: none
/* ^^^ attempt to keep mobile from inflating some text */;
}
table.diff pre > ins,
table.diff pre > del {
/* Fill platform-dependent color gaps caused by
| | | 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 |
line-height: 1.275/*for mobile: forum post e6f4ee7de98b55c0*/;
text-size-adjust: none
/* ^^^ attempt to keep mobile from inflating some text */;
}
table.diff pre > ins,
table.diff pre > del {
/* Fill platform-dependent color gaps caused by
inflated line-height */
padding: 0.062em 0 0.062em 0;
}
table.diff pre > ins > *,
table.diff pre > del > *{
/* Avoid odd-looking color swatches in conjunction with
(table.diff pre > ins/del) padding */
padding: inherit;
|
| ︙ | ︙ | |||
630 631 632 633 634 635 636 |
}
tr.diffskip.jchunk:hover {
/*background-color: rgba(127,127,127,0.5);
cursor: pointer;*/
}
tr.diffskip > td.chunkctrl {
text-align: left;
| < | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 |
}
tr.diffskip.jchunk:hover {
/*background-color: rgba(127,127,127,0.5);
cursor: pointer;*/
}
tr.diffskip > td.chunkctrl {
text-align: left;
}
tr.diffskip > td.chunkctrl > div {
display: flex;
align-items: center;
}
tr.diffskip > td.chunkctrl > div > span.error {
padding: 0.25em 0.5em;
|
| ︙ | ︙ | |||
677 678 679 680 681 682 683 |
}
tr.diffskip > td.chunkctrl .jcbutton.down > span::before {
content: '⇣';
}
tr.diffskip > td.chunkctrl .jcbutton.up.down > span::before {
content: '⇡⇣';
}
| < > > > > > > > > > > > > > > > > > > | | > | 675 676 677 678 679 680 681 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 |
}
tr.diffskip > td.chunkctrl .jcbutton.down > span::before {
content: '⇣';
}
tr.diffskip > td.chunkctrl .jcbutton.up.down > span::before {
content: '⇡⇣';
}
tr.diffskip > td.chunkctrl .jcbutton:hover {
cursor: pointer;
opacity: 1;
filter: contrast(1);
}
tr.diffchunk {
display: grid;
gap: 0px 0px;
grid-template-rows: 1fr;
}
table.splitdiff tr.diffchunk {
grid-template-columns: auto 1fr auto auto 1fr;
grid-template-areas: "difflnl difftxtl diffsep difflnr difftxtr";
}
table.udiff tr.diffchunk {
grid-template-columns: auto auto auto 1fr;
grid-template-areas: "difflnl difflnr diffsep difftxtu";
}
td.difflnl { grid-area: difflnl; }
td.difflnr { grid-area: difflnr; }
td.difftxtu { grid-area: difftxtu; }
td.difftxtl { grid-area: difftxtl; }
td.difftxtr { grid-area: difftxtr; }
td.diffln {
width: fit-content;
text-align: right;
padding: 0 1em 0 0;
}
td.difflne {
padding-bottom: 0.4em;
}
td.diffsep {
width: fit-content;
padding: 0 0.3em 0 0.5em;
grid-area: diffsep;
}
td.difftxt pre {
overflow-x: auto;
}
td.diffln ins {
background-color: #a0e4b2;
text-decoration: none;
|
| ︙ | ︙ | |||
1306 1307 1308 1309 1310 1311 1312 |
margin: 0;
}
.flex-container.child-gap-small > * {
margin: 0.25em;
}
#fossil-status-bar {
display: block;
| < | 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 |
margin: 0;
}
.flex-container.child-gap-small > * {
margin: 0.25em;
}
#fossil-status-bar {
display: block;
border-width: 1px;
border-style: inset;
border-color: inherit;
min-height: 1.5em;
font-size: 1.2em;
padding: 0.2em;
margin: 0.25em 0;
|
| ︙ | ︙ | |||
1348 1349 1350 1351 1352 1353 1354 |
(e.g. DIV) so that certain nesting constructs are legal.
*/
.input-with-label {
border: 1px inset rgba(128, 128, 128, 0.5);
border-radius: 0.25em;
padding: 0.1em;
margin: 0 0.5em;
| | > > > > > > > > | 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 |
(e.g. DIV) so that certain nesting constructs are legal.
*/
.input-with-label {
border: 1px inset rgba(128, 128, 128, 0.5);
border-radius: 0.25em;
padding: 0.1em;
margin: 0 0.5em;
display: inline-block
/* We would really like flex layout but changing that
currently introduces a good deal of UI breakage
to chase down. The advantage would be better alignment
of the contained elements. */;
cursor: default;
white-space: nowrap;
}
.submenu .input-with-label {
border: none;
}
.input-with-label > * {
vertical-align: middle;
}
.input-with-label > label {
display: inline; /* some skins set label display to block! */
cursor: pointer;
white-space: nowrap;
}
.input-with-label > input {
margin: 0;
}
.input-with-label > button {
margin: 0;
}
|
| ︙ | ︙ | |||
1397 1398 1399 1400 1401 1402 1403 |
table.numbered-lines {
width: 100%;
table-layout: fixed /* required to keep ultra-wide code from exceeding
window width, and instead force a scrollbar
on them. */;
}
table.numbered-lines > tbody > tr {
| < | 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 |
table.numbered-lines {
width: 100%;
table-layout: fixed /* required to keep ultra-wide code from exceeding
window width, and instead force a scrollbar
on them. */;
}
table.numbered-lines > tbody > tr {
line-height: 1.35;
white-space: pre;
}
table.numbered-lines > tbody > tr > td {
font-family: inherit;
font-size: inherit;
line-height: inherit;
|
| ︙ | ︙ | |||
1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 |
color: black;
}
blockquote.file-content {
/* file content block in the /file page */
margin: 0 1em;
}
/**
Circular "help" buttons intended to be placed to the right of
another element and hold text text for it. These typically get
initialized automatically at page startup via
fossil.popupwidget.js, and can be manually initialized/created
| > > > > > > > > > > > > > > > > > > > > > | 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 |
color: black;
}
blockquote.file-content {
/* file content block in the /file page */
margin: 0 1em;
}
/* Generic sidebar styling inherited by skins that don't make their own
* arrangements. */
.markdown blockquote, p.blockquote, .sidebar {
background-color: rgba(0, 0, 0, 0.05);
border-left: 3px solid #777;
padding: 0.1em 1em;
}
.sidebar {
/* Generic form that can be applied to any block element. */
font-size: 90%;
}
div.sidebar {
/* Special exception for div-type sidebars, where there is no p
* wrapper inside to give us the extra padding we want. */
padding: 1em;
}
div.sidebar:not(.no-label):before {
content: "Sidebar: ";
font-weight: bold;
}
/**
Circular "help" buttons intended to be placed to the right of
another element and hold text text for it. These typically get
initialized automatically at page startup via
fossil.popupwidget.js, and can be manually initialized/created
|
| ︙ | ︙ | |||
1773 1774 1775 1776 1777 1778 1779 |
body.branch .submenu > a.timeline-link {
display: none;
}
body.branch .submenu > a.timeline-link.selected {
display: inline;
}
| > > > > > > > | > > > > | < | 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 |
body.branch .submenu > a.timeline-link {
display: none;
}
body.branch .submenu > a.timeline-link.selected {
display: inline;
}
/* Candidate fonts for various forms of monospaced text. Collected here
* to avoid repeating this long list of fonts. */
code, kbd, pre, samp, tt, var,
div.markdown ol.footnotes > li.fn-joined > sup.fn-joined,
table.numbered-lines > tbody > tr,
tr.diffskip > td.chunkctrl,
#fossil-status-bar,
.monospace {
font-family: "Source Code Pro", "Menlo", "Monaco", "Consolas",
"Andale Mono", "Ubuntu Mono", "Deja Vu Sans Mono",
"Letter Gothic", "Letter Gothic Std", "Prestige Elite Std",
"Courier", "Courier New",
monospace;
}
div.markdown > ol.footnotes {
font-size: 90%;
}
div.markdown > ol.footnotes > li {
margin-bottom: 0.5em;
}
div.markdown ol.footnotes > li.fn-joined > sup.fn-joined {
color: gray;
}
div.markdown ol.footnotes > li.fn-joined > sup.fn-joined::after {
content: "(joined from multiple locations) ";
}
div.markdown ol.footnotes > li.fn-misreference {
margin-top: 0.75em;
margin-bottom: 0.75em;
|
| ︙ | ︙ | |||
1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 |
}
/* Objects in the "desktoponly" class are invisible on mobile */
@media screen and (max-width: 600px) {
.desktoponly {
display: none;
}
}
/* Objects in the "wideonly" class are invisible only on wide-screen desktops */
@media screen and (max-width: 1200px) {
.wideonly {
display: none;
}
}
| > > > > > > > > | 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 |
}
/* Objects in the "desktoponly" class are invisible on mobile */
@media screen and (max-width: 600px) {
.desktoponly {
display: none;
}
}
/* Float sidebars to the right of the main content only if there's room. */
@media screen and (min-width: 600px) {
.sidebar {
float: right;
max-width: 33%;
margin-left: 1em;
}
}
/* Objects in the "wideonly" class are invisible only on wide-screen desktops */
@media screen and (max-width: 1200px) {
.wideonly {
display: none;
}
}
|
Changes to src/descendants.c.
| ︙ | ︙ | |||
247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
" AND event.objid=plink.pid"
" ORDER BY mtime DESC"
" )"
" SELECT ancestor.rid FROM ancestor"
" WHERE EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagxref.rid=ancestor.rid"
" AND value=%Q AND tagtype>0)"
" LIMIT 1",
rid, rid, TAG_BRANCH, zBranch
);
}
/*
** Compute all direct ancestors (merge ancestors do not count)
| > | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
" AND event.objid=plink.pid"
" ORDER BY mtime DESC"
" )"
" SELECT ancestor.rid FROM ancestor"
" WHERE EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagxref.rid=ancestor.rid"
" AND value=%Q AND tagtype>0)"
" ORDER BY mtime DESC"
" LIMIT 1",
rid, rid, TAG_BRANCH, zBranch
);
}
/*
** Compute all direct ancestors (merge ancestors do not count)
|
| ︙ | ︙ |
Changes to src/diff.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** ** This file contains code used to compute a "diff" between two ** text files. */ #include "config.h" #include "diff.h" #include <assert.h> #if INTERFACE /* ** Flag parameters to the text_diff() routine used to control the formatting ** of the diff output. */ | > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ** ** This file contains code used to compute a "diff" between two ** text files. */ #include "config.h" #include "diff.h" #include <assert.h> #include <errno.h> #if INTERFACE /* ** Flag parameters to the text_diff() routine used to control the formatting ** of the diff output. */ |
| ︙ | ︙ | |||
88 89 90 91 92 93 94 | ** Conceptually, this object is as an encoding of the command-line options ** for the "fossil diff" command. That is not a precise description, though, ** because not all diff operations are started from the command-line. But ** the idea is sound. ** ** Information encoded by this object includes but is not limited to: ** | | | | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
** Conceptually, this object is as an encoding of the command-line options
** for the "fossil diff" command. That is not a precise description, though,
** because not all diff operations are started from the command-line. But
** the idea is sound.
**
** Information encoded by this object includes but is not limited to:
**
** * The desired output format (unified vs. side-by-side,
** TCL, JSON, HTML vs. plain-text).
**
** * Number of lines of context surrounding each difference block
**
** * Width of output columns for text side-by-side diffop
*/
struct DiffConfig {
u64 diffFlags; /* Diff flags */
int nContext; /* Number of lines of context */
int wColumn; /* Column width in -y mode */
u32 nFile; /* Number of files diffed so far */
const char *zDiffCmd; /* External diff command to use instead of builtin */
|
| ︙ | ︙ | |||
913 914 915 916 917 918 919 | /* ** This is an abstract superclass for an object that accepts difference ** lines and formats them for display. Subclasses of this object format ** the diff output in different ways. ** ** To subclass, create an instance of the DiffBuilder object and fill ** in appropriate method implementations. | | | 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 |
/*
** This is an abstract superclass for an object that accepts difference
** lines and formats them for display. Subclasses of this object format
** the diff output in different ways.
**
** To subclass, create an instance of the DiffBuilder object and fill
** in appropriate method implementations.
*/
typedef struct DiffBuilder DiffBuilder;
struct DiffBuilder {
void (*xSkip)(DiffBuilder*, unsigned int, int);
void (*xCommon)(DiffBuilder*,const DLine*);
void (*xInsert)(DiffBuilder*,const DLine*);
void (*xDelete)(DiffBuilder*,const DLine*);
void (*xReplace)(DiffBuilder*,const DLine*,const DLine*);
|
| ︙ | ︙ | |||
1098 1099 1100 1101 1102 1103 1104 |
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ' ');
| | | 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 |
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut,
pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
}
if( x<pX->n ){
blob_append_char(p->pOut, ' ');
blob_append_tcl_literal(p->pOut, pX->z + x, pX->n - x);
}
blob_append_char(p->pOut, '\n');
|
| ︙ | ︙ | |||
1184 1185 1186 1187 1188 1189 1190 |
}
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ',');
| | | 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 |
}
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
x = chng.a[i].iStart1;
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
x += chng.a[i].iLen1;
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut,
pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
}
blob_append_char(p->pOut, ',');
blob_append_json_literal(p->pOut, pX->z + x, pX->n - x);
blob_append(p->pOut, "],\n",3);
}
static void dfjsonEnd(DiffBuilder *p){
|
| ︙ | ︙ | |||
1266 1267 1268 1269 1270 1271 1272 | /* "+" marks for the separator on inserted lines */ for(i=0; i<p->nPending; i++) blob_append(&p->aCol[1], "+\n", 2); /* Text of the inserted lines */ blob_append(&p->aCol[2], "<ins>", 5); blob_append_xfer(&p->aCol[2], &p->aCol[4]); blob_append(&p->aCol[2], "</ins>", 6); | | | | 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 |
/* "+" marks for the separator on inserted lines */
for(i=0; i<p->nPending; i++) blob_append(&p->aCol[1], "+\n", 2);
/* Text of the inserted lines */
blob_append(&p->aCol[2], "<ins>", 5);
blob_append_xfer(&p->aCol[2], &p->aCol[4]);
blob_append(&p->aCol[2], "</ins>", 6);
p->nPending = 0;
}
static void dfunifiedFinishRow(DiffBuilder *p){
dfunifiedFinishDelete(p);
dfunifiedFinishInsert(p);
if( blob_size(&p->aCol[0])==0 ) return;
blob_append(p->pOut, "</pre></td><td class=\"diffln difflnr\"><pre>\n", -1);
blob_append_xfer(p->pOut, &p->aCol[0]);
blob_append(p->pOut, "</pre></td><td class=\"diffsep\"><pre>\n", -1);
blob_append_xfer(p->pOut, &p->aCol[1]);
blob_append(p->pOut, "</pre></td><td class=\"difftxt difftxtu\"><pre>\n",-1);
blob_append_xfer(p->pOut, &p->aCol[2]);
blob_append(p->pOut, "</pre></td></tr>\n", -1);
}
static void dfunifiedStartRow(DiffBuilder *p){
if( blob_size(&p->aCol[0])>0 ) return;
blob_appendf(p->pOut,"<tr id=\"chunk%d\" class=\"diffchunk\">"
"<td class=\"diffln difflnl\"><pre>\n", ++nChunk);
}
static void dfunifiedSkip(DiffBuilder *p, unsigned int n, int isFinal){
dfunifiedFinishRow(p);
if( p->pCfg && p->pCfg->zLeftHash ){
blob_appendf(p->pOut,
"<tr class=\"diffskip\" data-startln=\"%d\" data-endln=\"%d\""
|
| ︙ | ︙ | |||
1509 1510 1511 1512 1513 1514 1515 |
blob_append_xfer(p->pOut, &p->aCol[2]);
blob_append(p->pOut, "</pre></td><td class=\"difftxt difftxtr\"><pre>\n",-1);
blob_append_xfer(p->pOut, &p->aCol[3]);
blob_append(p->pOut, "</pre></td></tr>\n", -1);
}
static void dfsplitStartRow(DiffBuilder *p){
if( blob_size(&p->aCol[0])>0 ) return;
| | | 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 |
blob_append_xfer(p->pOut, &p->aCol[2]);
blob_append(p->pOut, "</pre></td><td class=\"difftxt difftxtr\"><pre>\n",-1);
blob_append_xfer(p->pOut, &p->aCol[3]);
blob_append(p->pOut, "</pre></td></tr>\n", -1);
}
static void dfsplitStartRow(DiffBuilder *p){
if( blob_size(&p->aCol[0])>0 ) return;
blob_appendf(p->pOut,"<tr id=\"chunk%d\" class=\"diffchunk\">"
"<td class=\"diffln difflnl\"><pre>\n", ++nChunk);
p->eState = 0;
}
static void dfsplitSkip(DiffBuilder *p, unsigned int n, int isFinal){
dfsplitFinishRow(p);
if( p->pCfg && p->pCfg->zLeftHash ){
blob_appendf(p->pOut,
|
| ︙ | ︙ | |||
2003 2004 2005 2006 2007 2008 2009 |
aBig = aRight;
nBig = nRight;
}
iDivBig = nBig/2;
iDivSmall = nSmall/2;
if( pCfg->diffFlags & DIFF_DEBUG ){
| | | 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 |
aBig = aRight;
nBig = nRight;
}
iDivBig = nBig/2;
iDivSmall = nSmall/2;
if( pCfg->diffFlags & DIFF_DEBUG ){
fossil_print(" Divide at [%.*s]\n",
aBig[iDivBig].n, aBig[iDivBig].z);
}
bestScore = 10000;
for(i=0; i<nSmall; i++){
score = match_dline(aBig+iDivBig, aSmall+i) + abs(i-nSmall/2)*2;
if( score<bestScore ){
|
| ︙ | ︙ | |||
3155 3156 3157 3158 3159 3160 3161 |
}
/* Undocumented and unsupported flags used for development
** debugging and analysis: */
if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
}
| | > > > | | > | 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 |
}
/* Undocumented and unsupported flags used for development
** debugging and analysis: */
if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
}
if( (z = find_option("context","c",1))!=0 ){
char *zEnd;
f = (int)strtol(z, &zEnd, 10);
if( zEnd[0]==0 && errno!=ERANGE ){
pCfg->nContext = f;
diffFlags |= DIFF_CONTEXT_EX;
}
}
if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
pCfg->wColumn = f;
}
if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT;
|
| ︙ | ︙ | |||
3491 3492 3493 3494 3495 3496 3497 |
}
p->nVers++;
cnt++;
}
if( p->nVers==0 ){
if( zRevision ){
| | > | 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 |
}
p->nVers++;
cnt++;
}
if( p->nVers==0 ){
if( zRevision ){
fossil_fatal("file %s does not exist in check-in %s",
zFilename, zRevision);
}else{
fossil_fatal("no history for file: %s", zFilename);
}
}
db_finalize(&q);
db_end_transaction(0);
|
| ︙ | ︙ | |||
3580 3581 3582 3583 3584 3585 3586 |
struct AnnVers *p;
unsigned clr1, clr2, clr;
int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
/* Gather query parameters */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
| | | 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 |
struct AnnVers *p;
unsigned clr1, clr2, clr;
int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
/* Gather query parameters */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( exclude_spiders(0) ) return;
fossil_nice_default();
zFilename = P("filename");
zRevision = PD("checkin",0);
zOrigin = P("origin");
zLimit = P("limit");
showLog = PB("log");
fileVers = PB("filevers");
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
111 112 113 114 115 116 117 | } return 0; } /* ** Print details about the compared versions - possibly the working directory ** or the undo buffer. For check-ins, show hash and commit time. | | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
}
return 0;
}
/*
** Print details about the compared versions - possibly the working directory
** or the undo buffer. For check-ins, show hash and commit time.
**
** This is intended primarily to go into the "header garbage" that is ignored
** by patch(1).
**
** zFrom and zTo are interpreted as symbolic version names, unless they
** start with '(', in which case they are printed directly.
*/
void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){
|
| ︙ | ︙ | |||
216 217 218 219 220 221 222 | } } /* ** Default header texts for diff with --webpage */ | | | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
}
}
/*
** Default header texts for diff with --webpage
*/
static const char zWebpageHdr[] =
@ <!DOCTYPE html>
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <style>
@ body {
@ background-color: white;
|
| ︙ | ︙ | |||
340 341 342 343 344 345 346 |
@ background-color: #a2dbb2;
@ color: #000000;
@ }
@ td.difftxt ins > ins {
@ background-color: #559855;
@ }
@ }
| | | | | 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 |
@ background-color: #a2dbb2;
@ color: #000000;
@ }
@ td.difftxt ins > ins {
@ background-color: #559855;
@ }
@ }
@
@ </style>
@ </head>
@ <body>
;
static const char zWebpageHdrDark[] =
@ <!DOCTYPE html>
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <style>
@ body {
@ background-color: #353535;
@ color: #ffffff;
@ }
@ h1 {
@ font-size: 150%;
@ }
@
@ table.diff {
@ width: 100%;
@ border-spacing: 0;
@ border: 1px solid black;
@ line-height: inherit;
@ font-size: inherit;
@ }
|
| ︙ | ︙ | |||
446 447 448 449 450 451 452 |
@ font-weight: bold;
@ }
@ td.difftxt ins > ins.edit {
@ background-color: #c0c0ff;
@ text-decoration: none;
@ font-weight: bold;
@ }
| | | | 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
@ font-weight: bold;
@ }
@ td.difftxt ins > ins.edit {
@ background-color: #c0c0ff;
@ text-decoration: none;
@ font-weight: bold;
@ }
@
@ </style>
@ </head>
@ <body>
;
const char zWebpageEnd[] =
@ </body>
@ </html>
;
/*
** State variables used by the --browser option for diff. These must
** be static variables, not elements of DiffConfig, since they are
|
| ︙ | ︙ | |||
514 515 516 517 518 519 520 |
#ifndef _WIN32
signal(SIGINT, diff_www_interrupt);
#else
SetConsoleCtrlHandler(diff_console_ctrl_handler, TRUE);
#endif
}
if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){
| | | | 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
#ifndef _WIN32
signal(SIGINT, diff_www_interrupt);
#else
SetConsoleCtrlHandler(diff_console_ctrl_handler, TRUE);
#endif
}
if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){
fossil_print("%s",(pCfg->diffFlags & DIFF_DARKMODE)!=0 ? zWebpageHdrDark :
zWebpageHdr);
fflush(stdout);
}
}
/* Do any final output required by a diff and complete the diff
** process.
**
** For --browser and --webpage, output any javascript required by
** the diff. (Currently JS is only needed for side-by-side diffs).
**
** For --browser, close the connection to the temporary file, then
** launch a web browser to view the file. After a delay
** of FOSSIL_BROWSER_DIFF_DELAY milliseconds, delete the temp file.
*/
void diff_end(DiffConfig *pCfg, int nErr){
|
| ︙ | ︙ | |||
945 946 947 948 949 950 951 |
){
Stmt q;
Blob content;
db_prepare(&q, "SELECT pathname, content FROM undo");
blob_init(&content, 0, 0);
if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
diff_print_versions("(undo)", "(workdir)", pCfg);
| | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 |
){
Stmt q;
Blob content;
db_prepare(&q, "SELECT pathname, content FROM undo");
blob_init(&content, 0, 0);
if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
diff_print_versions("(undo)", "(workdir)", pCfg);
}
while( db_step(&q)==SQLITE_ROW ){
char *zFullName;
const char *zFile = (const char*)db_column_text(&q, 0);
if( !file_dir_match(pFileDir, zFile) ) continue;
zFullName = mprintf("%s%s", g.zLocalRoot, zFile);
db_column_blob(&q, 1, &content);
diff_file(&content, zFullName, zFile, pCfg, 0);
|
| ︙ | ︙ | |||
1032 1033 1034 1035 1036 1037 1038 |
manifest_file_rewind(pFrom);
pFromFile = manifest_file_next(pFrom,0);
pTo = manifest_get_by_name(zTo, 0);
manifest_file_rewind(pTo);
pToFile = manifest_file_next(pTo,0);
if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
diff_print_versions(zFrom, zTo, pCfg);
| | | 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 |
manifest_file_rewind(pFrom);
pFromFile = manifest_file_next(pFrom,0);
pTo = manifest_get_by_name(zTo, 0);
manifest_file_rewind(pTo);
pToFile = manifest_file_next(pTo,0);
if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
diff_print_versions(zFrom, zTo, pCfg);
}
while( pFromFile || pToFile ){
int cmp;
if( pFromFile==0 ){
cmp = +1;
}else if( pToFile==0 ){
cmp = -1;
}else{
|
| ︙ | ︙ | |||
1248 1249 1250 1251 1252 1253 1254 | ** as binary ** --branch BRANCH Show diff of all changes on BRANCH ** --brief Show filenames only ** -b|--browser Show the diff output in a web-browser ** --by Shorthand for "--browser -y" ** -ci|--checkin VERSION Show diff of all changes in VERSION ** --command PROG External diff program. Overrides "diff-command" | | | | 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 | ** as binary ** --branch BRANCH Show diff of all changes on BRANCH ** --brief Show filenames only ** -b|--browser Show the diff output in a web-browser ** --by Shorthand for "--browser -y" ** -ci|--checkin VERSION Show diff of all changes in VERSION ** --command PROG External diff program. Overrides "diff-command" ** -c|--context N Show N lines of context around each change, ** with negative N meaning show all content ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML ** --diff-binary BOOL Include binary files with external commands ** --exec-abs-paths Force absolute path names on external commands ** --exec-rel-paths Force relative path names on external commands ** -r|--from VERSION Select VERSION as source for the diff ** -w|--ignore-all-space Ignore white space when comparing lines ** -i|--internal Use internal diff logic |
| ︙ | ︙ | |||
1381 1382 1383 1384 1385 1386 1387 |
}
fossil_free(pFileDir[i].zName);
}
fossil_free(pFileDir);
}
diff_end(&DCfg, 0);
if ( DCfg.diffFlags & DIFF_NUMSTAT ){
| | | 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 |
}
fossil_free(pFileDir[i].zName);
}
fossil_free(pFileDir);
}
diff_end(&DCfg, 0);
if ( DCfg.diffFlags & DIFF_NUMSTAT ){
fossil_print("%10d %10d TOTAL over %d changed files\n",
g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]);
}
}
/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO
|
| ︙ | ︙ |
Changes to src/dispatch.c.
| ︙ | ︙ | |||
451 452 453 454 455 456 457 |
aIndent[iLevel] = nIndent;
azEnd[iLevel] = zEndUL;
if( wantP ){
blob_append(pHtml,"<p>", 3);
wantP = 0;
}
blob_append(pHtml, "<ul>\n", 5);
| | | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
aIndent[iLevel] = nIndent;
azEnd[iLevel] = zEndUL;
if( wantP ){
blob_append(pHtml,"<p>", 3);
wantP = 0;
}
blob_append(pHtml, "<ul>\n", 5);
}else if( isDT
|| zHelp[nIndent]=='-'
|| hasGap(zHelp+nIndent,i-nIndent) ){
iLevel++;
aIndent[iLevel] = nIndent;
azEnd[iLevel] = zEndDL;
wantP = 0;
blob_append(pHtml, "<blockquote><dl>\n", -1);
|
| ︙ | ︙ | |||
545 546 547 548 549 550 551 |
if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){
if( i>0 ) blob_append(pText, zHelp, i);
zHelp += i+2;
blob_append(pText, zHelp, x-3);
zHelp += x-1;
i = -1;
continue;
| | | | 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){
if( i>0 ) blob_append(pText, zHelp, i);
zHelp += i+2;
blob_append(pText, zHelp, x-3);
zHelp += x-1;
i = -1;
continue;
}
}
if( i>0 ){
blob_append(pText, zHelp, i);
}
}
/*
** Display help for all commands based on provided flags.
*/
static void display_all_help(int mask, int useHtml, int rawOut){
int i;
|
| ︙ | ︙ | |||
633 634 635 636 637 638 639 | ** ** Show help text for commands and pages. Useful for proof-reading. ** Defaults to just the CLI commands. Specify --www to see only the ** web pages, or --everything to see both commands and pages. ** ** Options: ** -a|--aliases Show aliases | | | 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 | ** ** Show help text for commands and pages. Useful for proof-reading. ** Defaults to just the CLI commands. Specify --www to see only the ** web pages, or --everything to see both commands and pages. ** ** Options: ** -a|--aliases Show aliases ** -e|--everything Show all commands and pages. Omit aliases to ** avoid duplicates. ** -h|--html Transform output to HTML ** -o|--options Show global options ** -r|--raw No output formatting ** -s|--settings Show settings ** -t|--test Include test- commands ** -w|--www Show WWW pages |
| ︙ | ︙ | |||
659 660 661 662 663 664 665 |
CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST;
}
if( find_option("settings","s",0) ){
mask = CMDFLAG_SETTING;
}
if( find_option("aliases","a",0) ){
mask = CMDFLAG_ALIAS;
| | | 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 |
CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST;
}
if( find_option("settings","s",0) ){
mask = CMDFLAG_SETTING;
}
if( find_option("aliases","a",0) ){
mask = CMDFLAG_ALIAS;
}
if( find_option("test","t",0) ){
mask |= CMDFLAG_TEST;
}
display_all_help(mask, useHtml, rawOut);
}
/*
|
| ︙ | ︙ | |||
766 767 768 769 770 771 772 |
iLast = FOSSIL_FIRST_CMD-1;
}else{
iFirst = FOSSIL_FIRST_CMD;
iLast = MX_COMMAND-1;
}
while( n<nArray ){
| | | 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 |
iLast = FOSSIL_FIRST_CMD-1;
}else{
iFirst = FOSSIL_FIRST_CMD;
iLast = MX_COMMAND-1;
}
while( n<nArray ){
bestScore = mxScore;
for(i=iFirst; i<=iLast; i++){
m = edit_distance(zIn, aCommand[i].zName);
if( m<mnScore ) continue;
if( m==mnScore ){
azArray[n++] = aCommand[i].zName;
if( n>=nArray ) return n;
}else if( m<bestScore ){
|
| ︙ | ︙ | |||
867 868 869 870 871 872 873 874 875 876 877 878 879 |
@ <div class="helpPage">
help_to_html(pCmd->zHelp, cgi_output_blob());
@ </div>
}
}
}else{
int i;
unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */
int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */
style_header("Help");
@ <a name='commands'></a>
@ <h1>Available commands:</h1>
| > | | 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 |
@ <div class="helpPage">
help_to_html(pCmd->zHelp, cgi_output_blob());
@ </div>
}
}
}else{
int i;
const char *zWidth = "28ex";
unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */
int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */
style_header("Help");
@ <a name='commands'></a>
@ <h1>Available commands:</h1>
@ <div class="columns" style="column-width: %s(zWidth);">
@ <ul>
/* Fill in help string buckets */
for(i=0; i<MX_COMMAND; i++){
if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue;
bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i;
}
for(i=0; i<MX_COMMAND; i++){
|
| ︙ | ︙ | |||
895 896 897 898 899 900 901 |
@ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
/* Output aliases */
if( occHelp[aCommand[i].iHelp] > 1 ){
int j;
int aliases[MX_HELP_DUP], nAliases=0;
for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
if( bktHelp[aCommand[i].iHelp][j] != i ){
| | > | 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 |
@ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
/* Output aliases */
if( occHelp[aCommand[i].iHelp] > 1 ){
int j;
int aliases[MX_HELP_DUP], nAliases=0;
for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
if( bktHelp[aCommand[i].iHelp][j] != i ){
if( aCommand[bktHelp[aCommand[i].iHelp][j]].eCmdFlags
& CMDFLAG_ALIAS ){
aliases[nAliases++] = bktHelp[aCommand[i].iHelp][j];
}
}
}
if( nAliases>0 ){
int k;
@(\
|
| ︙ | ︙ | |||
917 918 919 920 921 922 923 |
@ </li>
}
@ </ul></div>
@ <a name='webpages'></a>
@ <h1>Available web UI pages:</h1>
| | | | | 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 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 |
@ </li>
}
@ </ul></div>
@ <a name='webpages'></a>
@ <h1>Available web UI pages:</h1>
@ <div class="columns" style="column-width: %s(zWidth);">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( '/'!=*z ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help?cmd=%s(z)">%s(z+1)</a></li>
}else{
@ <li>%s(z+1)</li>
}
}
@ </ul></div>
@ <a name='unsupported'></a>
@ <h1>Unsupported commands:</h1>
@ <div class="columns" style="column-width: %s(zWidth);">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( strncmp(z,"test",4)!=0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
}else{
@ <li>%s(z)</li>
}
}
@ </ul></div>
@ <a name='settings'></a>
@ <h1>Settings:</h1>
@ <div class="columns" style="column-width: %s(zWidth);">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help?cmd=%s(z)">%s(z)</a></li>
|
| ︙ | ︙ | |||
985 986 987 988 989 990 991 |
style_set_current_feature("test");
style_header("All Help Text");
@ <dl>
/* Fill in help string buckets */
for(i=0; i<MX_COMMAND; i++){
if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue;
bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i;
| | | 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 |
style_set_current_feature("test");
style_header("All Help Text");
@ <dl>
/* Fill in help string buckets */
for(i=0; i<MX_COMMAND; i++){
if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue;
bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i;
}
for(i=0; i<MX_COMMAND; i++){
const char *zDesc;
unsigned int e = aCommand[i].eCmdFlags;
if( e & CMDFLAG_1ST_TIER ){
zDesc = "1st tier command";
}else if( e & CMDFLAG_2ND_TIER ){
zDesc = "2nd tier command";
|
| ︙ | ︙ | |||
1037 1038 1039 1040 1041 1042 1043 |
}else if( e & CMDFLAG_WEBPAGE ){
if( e & CMDFLAG_RAWCONTENT ){
zDesc = "raw-content web page";
}else{
zDesc = "web page";
}
}
| | | 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 |
}else if( e & CMDFLAG_WEBPAGE ){
if( e & CMDFLAG_RAWCONTENT ){
zDesc = "raw-content web page";
}else{
zDesc = "web page";
}
}
@ <dt><big><b>%s(aCommand[bktHelp[aCommand[i].iHelp][j]].zName)</b>
@</big> (%s(zDesc))</dt>
}
@ <p><dd>
help_to_html(aCommand[i].zHelp, cgi_output_blob());
@ </dd><p>
occHelp[aCommand[i].iHelp] = 0;
|
| ︙ | ︙ | |||
1116 1117 1118 1119 1120 1121 1122 | /* ** Documentation on universal command-line options. */ /* @-comment: # */ static const char zOptions[] = @ Command-line options common to all commands: | | | | | 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 | /* ** Documentation on universal command-line options. */ /* @-comment: # */ static const char zOptions[] = @ Command-line options common to all commands: @ @ --args FILENAME Read additional arguments and options from FILENAME @ --case-sensitive BOOL Set case sensitivity for file names @ --cgitrace Active CGI tracing @ --chdir PATH Change to PATH before performing any operations @ --comfmtflags VALUE Set comment formatting flags to VALUE @ --comment-format VALUE Alias for --comfmtflags @ --errorlog FILENAME Log errors to FILENAME @ --help Show help on the command rather than running it @ --httptrace Trace outbound HTTP requests @ --localtime Display times using the local timezone @ --nocgi Do not act as CGI @ --no-th-hook Do not run TH1 hooks @ --quiet Reduce the amount of output @ --sqlstats Show SQL usage statistics when done |
| ︙ | ︙ | |||
1485 1486 1487 1488 1489 1490 1491 | helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur; return pCur->iRowid>=MX_COMMAND; } /* ** This method is called to "rewind" the helptextVtab_cursor object back ** to the first row of output. This method is always called at least | | | | 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 |
helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur;
return pCur->iRowid>=MX_COMMAND;
}
/*
** This method is called to "rewind" the helptextVtab_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to helptextVtabColumn() or helptextVtabRowid() or
** helptextVtabEof().
*/
static int helptextVtabFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
helptextVtab_cursor *pCur = (helptextVtab_cursor *)pVtabCursor;
pCur->iRowid = 1;
return SQLITE_OK;
}
|
| ︙ | ︙ | |||
1514 1515 1516 1517 1518 1519 1520 |
){
pIdxInfo->estimatedCost = (double)MX_COMMAND;
pIdxInfo->estimatedRows = MX_COMMAND;
return SQLITE_OK;
}
/*
| | | 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 |
){
pIdxInfo->estimatedCost = (double)MX_COMMAND;
pIdxInfo->estimatedRows = MX_COMMAND;
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module helptextVtabModule = {
/* iVersion */ 0,
/* xCreate */ 0, /* Helptext is eponymous and read-only */
/* xConnect */ helptextVtabConnect,
/* xBestIndex */ helptextVtabBestIndex,
|
| ︙ | ︙ |
Changes to src/doc.c.
| ︙ | ︙ | |||
151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
{ "ief", 3, "image/ief" },
{ "iges", 4, "model/iges" },
{ "igs", 3, "model/iges" },
{ "ips", 3, "application/x-ipscript" },
{ "ipx", 3, "application/x-ipix" },
{ "jad", 3, "text/vnd.sun.j2me.app-descriptor" },
{ "jar", 3, "application/java-archive" },
{ "jpe", 3, "image/jpeg" },
{ "jpeg", 4, "image/jpeg" },
{ "jpg", 3, "image/jpeg" },
{ "js", 2, "text/javascript" },
/* application/javascript is commonly used for JS, but the
** spec says text/javascript is correct:
** https://html.spec.whatwg.org/multipage/scripting.html
| > | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
{ "ief", 3, "image/ief" },
{ "iges", 4, "model/iges" },
{ "igs", 3, "model/iges" },
{ "ips", 3, "application/x-ipscript" },
{ "ipx", 3, "application/x-ipix" },
{ "jad", 3, "text/vnd.sun.j2me.app-descriptor" },
{ "jar", 3, "application/java-archive" },
{ "jp2", 3, "image/jp2" },
{ "jpe", 3, "image/jpeg" },
{ "jpeg", 4, "image/jpeg" },
{ "jpg", 3, "image/jpeg" },
{ "js", 2, "text/javascript" },
/* application/javascript is commonly used for JS, but the
** spec says text/javascript is correct:
** https://html.spec.whatwg.org/multipage/scripting.html
|
| ︙ | ︙ | |||
250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
{ "skp", 3, "application/x-koan" },
{ "skt", 3, "application/x-koan" },
{ "smi", 3, "application/smil" },
{ "smil", 4, "application/smil" },
{ "snd", 3, "audio/basic" },
{ "sol", 3, "application/solids" },
{ "spl", 3, "application/x-futuresplash" },
{ "src", 3, "application/x-wais-source" },
{ "step", 4, "application/STEP" },
{ "stl", 3, "application/SLA" },
{ "stp", 3, "application/STEP" },
{ "sv4cpio", 7, "application/x-sv4cpio" },
{ "sv4crc", 6, "application/x-sv4crc" },
{ "svg", 3, "image/svg+xml" },
| > | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
{ "skp", 3, "application/x-koan" },
{ "skt", 3, "application/x-koan" },
{ "smi", 3, "application/smil" },
{ "smil", 4, "application/smil" },
{ "snd", 3, "audio/basic" },
{ "sol", 3, "application/solids" },
{ "spl", 3, "application/x-futuresplash" },
{ "sql", 3, "application/sql" },
{ "src", 3, "application/x-wais-source" },
{ "step", 4, "application/STEP" },
{ "stl", 3, "application/SLA" },
{ "stp", 3, "application/STEP" },
{ "sv4cpio", 7, "application/x-sv4cpio" },
{ "sv4crc", 6, "application/x-sv4crc" },
{ "svg", 3, "image/svg+xml" },
|
| ︙ | ︙ | |||
339 340 341 342 343 344 345 |
static char * zList = 0;
static char const * zEnd = 0;
static int once = 0;
char * z;
int tokenizerState /* 0=expecting a key, 1=skip next token,
** 2=accept next token */;
if(once==0){
| | | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
static char * zList = 0;
static char const * zEnd = 0;
static int once = 0;
char * z;
int tokenizerState /* 0=expecting a key, 1=skip next token,
** 2=accept next token */;
if(once==0){
once = 1;
zList = db_get("mimetypes",0);
if(zList==0){
return 0;
}
/* Transform zList to simplify the main loop:
replace non-newline spaces with NUL bytes. */
zEnd = zList + strlen(zList);
|
| ︙ | ︙ | |||
727 728 729 730 731 732 733 | ** Transfer content to the output. During the transfer, when text of ** the following form is seen: ** ** href="$ROOT/..." ** action="$ROOT/..." ** href=".../doc/$CURRENT/..." ** | | | | 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 |
** Transfer content to the output. During the transfer, when text of
** the following form is seen:
**
** href="$ROOT/..."
** action="$ROOT/..."
** href=".../doc/$CURRENT/..."
**
** Convert $ROOT to the root URI of the repository, and $CURRENT to the
** version number of the /doc/ document currently being displayed (if any).
** Allow ' in place of " and any case for href or action.
**
** Efforts are made to limit this translation to cases where the text is
** fully contained with an HTML markup element.
*/
void convert_href_and_output(Blob *pIn){
int i, base;
int n = blob_size(pIn);
|
| ︙ | ︙ | |||
828 829 830 831 832 833 834 |
convert_href_and_output(pBody);
if( !isPopup ){
document_emit_js();
style_finish_page();
}
}else if( fossil_strcmp(zMime, "text/x-pikchr")==0 ){
style_adunit_config(ADUNIT_RIGHT_OK);
| | | | 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 |
convert_href_and_output(pBody);
if( !isPopup ){
document_emit_js();
style_finish_page();
}
}else if( fossil_strcmp(zMime, "text/x-pikchr")==0 ){
style_adunit_config(ADUNIT_RIGHT_OK);
if( !isPopup ) style_header("%s", zDefaultTitle);
wiki_render_by_mimetype(pBody, zMime);
if( !isPopup ) style_finish_page();
#ifdef FOSSIL_ENABLE_TH1_DOCS
}else if( Th_AreDocsEnabled() &&
fossil_strcmp(zMime, "application/x-th1")==0 ){
int raw = P("raw")!=0;
if( !raw ){
Blob tail;
blob_zero(&tail);
|
| ︙ | ︙ | |||
1209 1210 1211 1212 1213 1214 1215 | ** ** The intended use case here is to supply an icon for the "fossil ui" ** command. For a permanent website, the recommended process is for ** the admin to set up a project-specific icon and reference that icon ** in the HTML header using a line like: ** ** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/> | | | 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 |
**
** The intended use case here is to supply an icon for the "fossil ui"
** command. For a permanent website, the recommended process is for
** the admin to set up a project-specific icon and reference that icon
** in the HTML header using a line like:
**
** <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/>
**
*/
void favicon_page(void){
Blob icon;
char *zMime;
etag_check(ETAG_CONFIG, 0);
zMime = db_get("icon-mimetype", "image/gif");
|
| ︙ | ︙ |
Changes to src/etag.c.
| ︙ | ︙ | |||
98 99 100 101 102 103 104 | char zBuf[50]; assert( zETag[0]==0 ); /* Only call this routine once! */ if( etagCancelled ) return; /* By default, ETagged URLs never expire since the ETag will change * when the content changes. Approximate this policy as 10 years. */ | | | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
char zBuf[50];
assert( zETag[0]==0 ); /* Only call this routine once! */
if( etagCancelled ) return;
/* By default, ETagged URLs never expire since the ETag will change
* when the content changes. Approximate this policy as 10 years. */
iMaxAge = 10 * 365 * 24 * 60 * 60;
md5sum_init();
/* Always include the executable ID as part of the hash */
md5sum_step_text("exe-id: ", -1);
md5sum_step_text(fossil_exe_id(), -1);
md5sum_step_text("\n", 1);
if( (eFlags & ETAG_HASH)!=0 && zHash ){
md5sum_step_text("hash: ", -1);
md5sum_step_text(zHash, -1);
md5sum_step_text("\n", 1);
iMaxAge = 0;
}
if( eFlags & ETAG_DATA ){
|
| ︙ | ︙ | |||
208 209 210 211 212 213 214 |
/* Check to see the If-Modified-Since constraint is satisfied */
zIfModifiedSince = P("HTTP_IF_MODIFIED_SINCE");
if( zIfModifiedSince==0 ) return;
x = cgi_rfc822_parsedate(zIfModifiedSince);
if( x<mtime ) return;
| | | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
/* Check to see the If-Modified-Since constraint is satisfied */
zIfModifiedSince = P("HTTP_IF_MODIFIED_SINCE");
if( zIfModifiedSince==0 ) return;
x = cgi_rfc822_parsedate(zIfModifiedSince);
if( x<mtime ) return;
#if 0
/* If the Fossil executable is more recent than If-Modified-Since,
** go ahead and regenerate the resource. */
if( file_mtime(g.nameOfExe, ExtFILE)>x ) return;
#endif
/* If we reach this point, it means that the resource has not changed
** and that we should generate a 304 Not Modified reply */
|
| ︙ | ︙ | |||
242 243 244 245 246 247 248 |
/* Return the last-modified time in seconds since 1970. Or return 0 if
** there is no last-modified time.
*/
sqlite3_int64 etag_mtime(void){
return iEtagMtime;
}
| | | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
/* Return the last-modified time in seconds since 1970. Or return 0 if
** there is no last-modified time.
*/
sqlite3_int64 etag_mtime(void){
return iEtagMtime;
}
/*
** COMMAND: test-etag
**
** Usage: fossil test-etag -key KEY-NUMBER -hash HASH
**
** Generate an etag given a KEY-NUMBER and/or a HASH.
**
** KEY-NUMBER is some combination of:
|
| ︙ | ︙ |
Changes to src/export.c.
| ︙ | ︙ | |||
447 448 449 450 451 452 453 |
}while( (rid = bag_next(vers, rid))!=0 );
}
}
}
/* This is the original header command (and hence documentation) for
** the "fossil export" command:
| | | 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
}while( (rid = bag_next(vers, rid))!=0 );
}
}
}
/* This is the original header command (and hence documentation) for
** the "fossil export" command:
**
** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY?
**
** Write an export of all check-ins to standard output. The export is
** written in the git-fast-export file format assuming the --git option is
** provided. The git-fast-export format is currently the only VCS
** interchange format supported, though other formats may be added in
** the future.
|
| ︙ | ︙ | |||
1002 1003 1004 1005 1006 1007 1008 |
db_bind_int(&sIns, ":isfile", isFile!=0);
db_step(&sIns);
db_reset(&sIns);
return mprintf(":%d", db_last_insert_rowid());
}
/* This is the SHA3-256 hash of an empty file */
| | | 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 |
db_bind_int(&sIns, ":isfile", isFile!=0);
db_step(&sIns);
db_reset(&sIns);
return mprintf(":%d", db_last_insert_rowid());
}
/* This is the SHA3-256 hash of an empty file */
static const char zEmptySha3[] =
"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a";
/*
** Export a single file named by zUuid.
**
** Return 0 on success and non-zero on any failure.
**
|
| ︙ | ︙ | |||
1035 1036 1037 1038 1039 1040 1041 |
}else{
rc = content_get(rid, &data);
if( rc==0 ){
if( bPhantomOk ){
blob_init(&data, 0, 0);
gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid);
zUuid = zEmptySha3;
| | | 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 |
}else{
rc = content_get(rid, &data);
if( rc==0 ){
if( bPhantomOk ){
blob_init(&data, 0, 0);
gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid);
zUuid = zEmptySha3;
}else{
return 1;
}
}
}
zMark = gitmirror_find_mark(zUuid, 1, 1);
if( zMark[0]==':' ){
fprintf(xCmd, "blob\nmark %s\ndata %d\n", zMark, blob_size(&data));
|
| ︙ | ︙ | |||
1277 1278 1279 1280 1281 1282 1283 |
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
blob_strlen(&manifest), blob_str(&manifest));
blob_reset(&manifest);
}
if( fManifest & MFESTFLG_UUID ){
int n = (int)strlen(zUuid);
| | | 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 |
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
blob_strlen(&manifest), blob_str(&manifest));
blob_reset(&manifest);
}
if( fManifest & MFESTFLG_UUID ){
int n = (int)strlen(zUuid);
fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid);
}
if( fManifest & MFESTFLG_TAGS ){
Blob tagslist;
blob_init(&tagslist, 0, 0);
get_checkin_taglist(rid, &tagslist);
fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
blob_strlen(&tagslist), blob_str(&tagslist));
|
| ︙ | ︙ | |||
1348 1349 1350 1351 1352 1353 1354 |
int i;
zCmd = "git symbolic-ref --short HEAD";
gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
xCmd = popen(zCmd, "r");
if( xCmd==0 ){
fossil_fatal("git command failed: %s", zCmd);
}
| | | | 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 |
int i;
zCmd = "git symbolic-ref --short HEAD";
gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
xCmd = popen(zCmd, "r");
if( xCmd==0 ){
fossil_fatal("git command failed: %s", zCmd);
}
z = fgets(zLine, sizeof(zLine), xCmd);
pclose(xCmd);
if( z==0 ){
fossil_fatal("no output from \"%s\"", zCmd);
}
for(i=0; z[i] && !fossil_isspace(z[i]); i++){}
z[i] = 0;
zMainBr = fossil_strdup(z);
}
return zMainBr;
}
/*
** Implementation of the "fossil git export" command.
*/
void gitmirror_export_command(void){
const char *zLimit; /* Text of the --limit flag */
int nLimit = 0x7fffffff; /* Numeric value of the --limit flag */
|
| ︙ | ︙ | |||
1434 1435 1436 1437 1438 1439 1440 |
/* Make sure GIT has been initialized */
z = mprintf("%s/.git", zMirror);
if( !file_isdir(z, ExtFILE) ){
zMainBr = gitmirror_init(zMirror, zMainBr);
bNeedRepack = 1;
}
fossil_free(z);
| | | 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 |
/* Make sure GIT has been initialized */
z = mprintf("%s/.git", zMirror);
if( !file_isdir(z, ExtFILE) ){
zMainBr = gitmirror_init(zMirror, zMainBr);
bNeedRepack = 1;
}
fossil_free(z);
/* Make sure the .mirror_state subdirectory exists */
z = mprintf("%s/.mirror_state", zMirror);
rc = file_mkdir(z, ExtFILE, 0);
if( rc ) fossil_fatal("cannot create directory \"%s\"", z);
fossil_free(z);
/* Attach the .mirror_state/db database */
|
| ︙ | ︙ | |||
1741 1742 1743 1744 1745 1746 1747 |
char *zSql;
int bQuiet = 0;
int bByAll = 0; /* Undocumented option meaning this command was invoked
** from "fossil all" and should modify output accordingly */
db_find_and_open_repository(0, 0);
bQuiet = find_option("quiet","q",0)!=0;
| | | 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 |
char *zSql;
int bQuiet = 0;
int bByAll = 0; /* Undocumented option meaning this command was invoked
** from "fossil all" and should modify output accordingly */
db_find_and_open_repository(0, 0);
bQuiet = find_option("quiet","q",0)!=0;
bByAll = find_option("by-all",0,0)!=0;
verify_all_options();
zMirror = db_get("last-git-export-repo", 0);
if( zMirror==0 ){
if( bQuiet ) return;
if( bByAll ) return;
fossil_print("Git mirror: none\n");
return;
|
| ︙ | ︙ | |||
1785 1786 1787 1788 1789 1790 1791 |
}
}
z = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'");
if( z==0 ){
fossil_print("Autopush: off\n");
}else{
UrlData url;
| > | | > > > | 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 |
}
}
z = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'");
if( z==0 ){
fossil_print("Autopush: off\n");
}else{
UrlData url;
if( sqlite3_strglob("http*", z)==0 ){
url_parse_local(z, 0, &url);
fossil_print("Autopush: %s\n", url.canonical);
}else{
fossil_print("Autopush: %s\n", z);
}
fossil_free(z);
}
n = db_int(0,
"SELECT count(*) FROM event"
" WHERE type='ci'"
" AND mtime>coalesce((SELECT value FROM mconfig"
" WHERE key='start'),0.0)"
|
| ︙ | ︙ | |||
1854 1855 1856 1857 1858 1859 1860 | ** mapped into this name. "master" is used if ** this option is omitted. ** -q|--quiet Reduce output. Repeat for even less output. ** -v|--verbose More output ** ** > fossil git import MIRROR ** | | | 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 | ** mapped into this name. "master" is used if ** this option is omitted. ** -q|--quiet Reduce output. Repeat for even less output. ** -v|--verbose More output ** ** > fossil git import MIRROR ** ** TBD... ** ** > fossil git status ** ** Show the status of the current Git mirror, if there is one. ** ** -q|--quiet No output if there is nothing to report */ |
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 39 40 41 42 43 44 45 | */ #ifdef _WIN32 # include <direct.h> # include <windows.h> # include <sys/utime.h> #else # include <sys/time.h> #endif #if INTERFACE /* Many APIs take an eFType argument which must be one of ExtFILE, RepoFILE, ** or SymFILE. ** | > > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | */ #ifdef _WIN32 # include <direct.h> # include <windows.h> # include <sys/utime.h> #else # include <sys/time.h> # include <pwd.h> # include <grp.h> #endif #if INTERFACE /* Many APIs take an eFType argument which must be one of ExtFILE, RepoFILE, ** or SymFILE. ** |
| ︙ | ︙ | |||
227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
/*
** Return TRUE if the named file is an ordinary file. Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isfile(const char *zFilename, int eFType){
return getStat(zFilename, eFType) ? 0 : S_ISREG(fx.fileStat.st_mode);
}
/*
** Create a symbolic link named zLinkFile that points to zTargetFile.
**
** If allow-symlinks is off, create an ordinary file named zLinkFile
** with the name of zTargetFile as its content.
**/
| > > > > > > > > > > > > > > | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
/*
** Return TRUE if the named file is an ordinary file. Return false
** for directories, devices, fifos, symlinks, etc.
*/
int file_isfile(const char *zFilename, int eFType){
return getStat(zFilename, eFType) ? 0 : S_ISREG(fx.fileStat.st_mode);
}
/*
** Return TRUE if zFilename is a socket.
*/
int file_issocket(const char *zFilename){
#ifdef _WIN32
return 0;
#else
if( getStat(zFilename, ExtFILE) ){
return 0; /* stat() failed. Return false. */
}
return S_ISSOCK(fx.fileStat.st_mode);
#endif
}
/*
** Create a symbolic link named zLinkFile that points to zTargetFile.
**
** If allow-symlinks is off, create an ordinary file named zLinkFile
** with the name of zTargetFile as its content.
**/
|
| ︙ | ︙ | |||
711 712 713 714 715 716 717 718 719 720 721 722 723 724 |
iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
zFile = g.argv[2];
file_set_mtime(zFile, iMTime);
iMTime = file_mtime(zFile, RepoFILE);
zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
}
/*
** Delete a file.
**
** If zFilename is a symbolic link, then it is the link itself that is
** removed, not the object that zFilename points to.
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 |
iMTime = db_int64(0, "SELECT strftime('%%s',%Q)", g.argv[3]);
zFile = g.argv[2];
file_set_mtime(zFile, iMTime);
iMTime = file_mtime(zFile, RepoFILE);
zDate = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMTime);
fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
}
/*
** Change access permissions on a file.
*/
void file_set_mode(const char *zFN, int fd, const char *zMode, int bNoErr){
#if !defined(_WIN32)
mode_t m;
char *zEnd = 0;
m = strtol(zMode, &zEnd, 0);
if( (zEnd[0] || fchmod(fd, m)) && !bNoErr ){
fossil_fatal("cannot change permissions on %s to \"%s\"",
zFN, zMode);
}
#endif
}
/* Change the owner of a file to zOwner. zOwner can be of the form
** USER:GROUP.
*/
void file_set_owner(const char *zFN, int fd, const char *zOwner){
#if !defined(_WIN32)
const char *zGrp;
const char *zUsr = zOwner;
struct passwd *pw;
struct group *grp;
uid_t uid = -1;
gid_t gid = -1;
zGrp = strchr(zUsr, ':');
if( zGrp ){
int n = (int)(zGrp - zUsr);
zUsr = fossil_strndup(zUsr, n);
zGrp++;
}
pw = getpwnam(zUsr);
if( pw==0 ){
fossil_fatal("no such user: \"%s\"", zUsr);
}
uid = pw->pw_uid;
if( zGrp ){
grp = getgrnam(zGrp);
if( grp==0 ){
fossil_fatal("no such group: \"%s\"", zGrp);
}
gid = grp->gr_gid;
}
if( chown(zFN, uid, gid) ){
fossil_fatal("cannot change ownership of %s to %s",zFN, zOwner);
}
if( zOwner!=zUsr ){
fossil_free((char*)zUsr);
}
#endif
}
/*
** Delete a file.
**
** If zFilename is a symbolic link, then it is the link itself that is
** removed, not the object that zFilename points to.
**
|
| ︙ | ︙ | |||
1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 |
char *file_canonical_name_dup(const char *zOrigName){
Blob x;
if( zOrigName==0 ) return 0;
blob_init(&x, 0, 0);
file_canonical_name(zOrigName, &x, 0);
return blob_str(&x);
}
/*
** The input is the name of an executable, such as one might
** type on a command-line. This routine resolves that name into
** a full pathname. The result is obtained from fossil_malloc()
** and should be freed by the caller.
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 |
char *file_canonical_name_dup(const char *zOrigName){
Blob x;
if( zOrigName==0 ) return 0;
blob_init(&x, 0, 0);
file_canonical_name(zOrigName, &x, 0);
return blob_str(&x);
}
/*
** Convert zPath, which is a relative pathname rooted at zDir, into the
** case preferred by the underlying filesystem. Return the a copy
** of the converted path in memory obtained from fossil_malloc().
**
** For case-sensitive filesystems, such as on Linux, this routine is
** just fossil_strdup(). But for case-insenstiive but "case preserving"
** filesystems, such as on MacOS or Windows, we want the filename to be
** in the preserved casing. That's what this routine does.
*/
char *file_case_preferred_name(const char *zDir, const char *zPath){
DIR *d;
int i;
char *zResult = 0;
void *zNative = 0;
if( filenames_are_case_sensitive() ){
return fossil_strdup(zPath);
}
for(i=0; zPath[i] && zPath[i]!='/' && zPath[i]!='\\'; i++){}
zNative = fossil_utf8_to_path(zDir, 1);
d = opendir(zNative);
if( d ){
struct dirent *pEntry;
while( (pEntry = readdir(d))!=0 ){
char *zUtf8 = fossil_path_to_utf8(pEntry->d_name);
if( fossil_strnicmp(zUtf8, zPath, i)==0 && zUtf8[i]==0 ){
if( zPath[i]==0 ){
zResult = fossil_strdup(zUtf8);
}else{
char *zSubDir = mprintf("%s/%s", zDir, zUtf8);
char *zSubPath = file_case_preferred_name(zSubDir, &zPath[i+1]);
zResult = mprintf("%s/%s", zUtf8, zSubPath);
fossil_free(zSubPath);
fossil_free(zSubDir);
}
fossil_path_free(zUtf8);
break;
}
fossil_path_free(zUtf8);
}
closedir(d);
}
fossil_path_free(zNative);
if( zResult==0 ) zResult = fossil_strdup(zPath);
return zResult;
}
/*
** COMMAND: test-case-filename
**
** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH ....
**
** All the PATH arguments (there must be one at least one) are pathnames
** relative to DIRECTORY. This test command prints the OS-preferred name
** for each PATH in filesystems where case is not significant.
*/
void test_preferred_fn(void){
int i;
if( g.argc<4 ){
usage("DIRECTORY PATH ...");
}
for(i=3; i<g.argc; i++){
char *z = file_case_preferred_name(g.argv[2], g.argv[i]);
fossil_print("%s -> %s\n", g.argv[i], z);
fossil_free(z);
}
}
/*
** The input is the name of an executable, such as one might
** type on a command-line. This routine resolves that name into
** a full pathname. The result is obtained from fossil_malloc()
** and should be freed by the caller.
*/
|
| ︙ | ︙ | |||
1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 |
z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
fossil_free(z);
fossil_print(" file_mtime(ExtFILE) = %s\n", zBuf);
fossil_print(" file_mode(ExtFILE) = 0%o\n", file_mode(zPath,ExtFILE));
fossil_print(" file_isfile(ExtFILE) = %d\n", file_isfile(zPath,ExtFILE));
fossil_print(" file_isdir(ExtFILE) = %d\n", file_isdir(zPath,ExtFILE));
if( reset ) resetStat();
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,RepoFILE));
fossil_print(" file_size(RepoFILE) = %s\n", zBuf);
iMtime = file_mtime(zPath,RepoFILE);
z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
fossil_free(z);
| > | 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 |
z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
fossil_free(z);
fossil_print(" file_mtime(ExtFILE) = %s\n", zBuf);
fossil_print(" file_mode(ExtFILE) = 0%o\n", file_mode(zPath,ExtFILE));
fossil_print(" file_isfile(ExtFILE) = %d\n", file_isfile(zPath,ExtFILE));
fossil_print(" file_isdir(ExtFILE) = %d\n", file_isdir(zPath,ExtFILE));
fossil_print(" file_issocket() = %d\n", file_issocket(zPath));
if( reset ) resetStat();
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_size(zPath,RepoFILE));
fossil_print(" file_size(RepoFILE) = %s\n", zBuf);
iMtime = file_mtime(zPath,RepoFILE);
z = db_text(0, "SELECT datetime(%lld, 'unixepoch')", iMtime);
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld (%s)", iMtime, z);
fossil_free(z);
|
| ︙ | ︙ | |||
2243 2244 2245 2246 2247 2248 2249 |
/*
** Return non-NULL if zFilename contains pathname elements that
** are reserved on Windows. The returned string is the disallowed
** path element.
*/
const char *file_is_win_reserved(const char *zPath){
| | | 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 |
/*
** Return non-NULL if zFilename contains pathname elements that
** are reserved on Windows. The returned string is the disallowed
** path element.
*/
const char *file_is_win_reserved(const char *zPath){
static const char *const azRes[] = { "CON","PRN","AUX","NUL","COM","LPT" };
static char zReturn[5];
int i;
while( zPath[0] ){
for(i=0; i<count(azRes); i++){
if( sqlite3_strnicmp(zPath, azRes[i], 3)==0
&& ((i>=4 && fossil_isdigit(zPath[3])
&& (zPath[4]=='/' || zPath[4]=='.' || zPath[4]==0))
|
| ︙ | ︙ | |||
2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 |
return z;
}
/*
** Count the number of objects (files and subdirectories) in a given
** directory. Return the count. Return -1 if the object is not a
** directory.
*/
int file_directory_size(const char *zDir, const char *zGlob, int omitDotFiles){
void *zNative;
DIR *d;
int n = -1;
zNative = fossil_utf8_to_path(zDir,1);
d = opendir(zNative);
if( d ){
struct dirent *pEntry;
n = 0;
while( (pEntry=readdir(d))!=0 ){
if( pEntry->d_name[0]==0 ) continue;
| > > > | > > > > > > | 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 |
return z;
}
/*
** Count the number of objects (files and subdirectories) in a given
** directory. Return the count. Return -1 if the object is not a
** directory.
**
** This routine never counts the two "." and ".." special directory
** entries, even if the provided glob would match them.
*/
int file_directory_size(const char *zDir, const char *zGlob, int omitDotFiles){
void *zNative;
DIR *d;
int n = -1;
zNative = fossil_utf8_to_path(zDir,1);
d = opendir(zNative);
if( d ){
struct dirent *pEntry;
n = 0;
while( (pEntry=readdir(d))!=0 ){
if( pEntry->d_name[0]==0 ) continue;
if( pEntry->d_name[0]=='.' &&
(omitDotFiles
/* Skip the special "." and ".." entries. */
|| pEntry->d_name[1]==0
|| (pEntry->d_name[1]=='.' && pEntry->d_name[2]==0))){
continue;
}
if( zGlob ){
char *zUtf8 = fossil_path_to_utf8(pEntry->d_name);
int rc = sqlite3_strglob(zGlob, zUtf8);
fossil_path_free(zUtf8);
if( rc ) continue;
}
n++;
|
| ︙ | ︙ |
Changes to src/fileedit.c.
| ︙ | ︙ | |||
434 435 436 437 438 439 440 | ** pCI's ownership is not modified. ** ** This function validates pCI's state and fails if any validation ** fails. ** ** On error, returns false (0) and, if pErr is not NULL, writes a ** diagnostic message there. | | | 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | ** pCI's ownership is not modified. ** ** This function validates pCI's state and fails if any validation ** fails. ** ** On error, returns false (0) and, if pErr is not NULL, writes a ** diagnostic message there. ** ** Returns true on success. If pRid is not NULL, the RID of the ** resulting manifest is written to *pRid. ** ** The check-in process is largely influenced by pCI->flags, and that ** must be populated before calling this. See the fossil_cimini_flags ** enum for the docs for each flag. */ |
| ︙ | ︙ | |||
571 572 573 574 575 576 577 |
&& blob_size(&pCI->fileContent)>0
){
/* Convert to the requested EOL style. Note that this inherently
** runs a risk of breaking content, e.g. string literals which
** contain embedded newlines. Note that HTML5 specifies that
** form-submitted TEXTAREA content gets normalized to CRLF-style:
**
| | | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 |
&& blob_size(&pCI->fileContent)>0
){
/* Convert to the requested EOL style. Note that this inherently
** runs a risk of breaking content, e.g. string literals which
** contain embedded newlines. Note that HTML5 specifies that
** form-submitted TEXTAREA content gets normalized to CRLF-style:
**
** https://html.spec.whatwg.org/#the-textarea-element
*/
const int pseudoBinary = LOOK_LONG | LOOK_NUL;
const int lookFlags = LOOK_CRLF | LOOK_LONE_LF | pseudoBinary;
const int lookNew = looks_like_utf8( &pCI->fileContent, lookFlags );
if(!(pseudoBinary & lookNew)){
int rehash = 0;
/*fossil_print("lookNew=%08x\n",lookNew);*/
|
| ︙ | ︙ | |||
979 980 981 982 983 984 985 |
char ** zRevUuid,
int * pVid,
const char * zFilename,
int * frid){
char * zFileUuid = 0; /* file content UUID */
const int checkFile = zFilename!=0 || frid!=0;
int vid = 0;
| | | 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 |
char ** zRevUuid,
int * pVid,
const char * zFilename,
int * frid){
char * zFileUuid = 0; /* file content UUID */
const int checkFile = zFilename!=0 || frid!=0;
int vid = 0;
if(checkFile && !fileedit_ajax_check_filename(zFilename)){
return 0;
}
vid = symbolic_name_to_rid(zRev, "ci");
if(0==vid){
ajax_route_error(404,"Cannot resolve name as a check-in: %s",
zRev);
|
| ︙ | ︙ | |||
1174 1175 1176 1177 1178 1179 1180 |
**
** Intended to be used only by /filepage and /filepage_commit.
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr,
int * bIsMissingArg){
char * zFileUuid = 0; /* UUID of file content */
const char * zFlag; /* generic flag */
| | | 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 |
**
** Intended to be used only by /filepage and /filepage_commit.
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr,
int * bIsMissingArg){
char * zFileUuid = 0; /* UUID of file content */
const char * zFlag; /* generic flag */
int rc = 0, vid = 0, frid = 0; /* result code, check-in/file rids */
#define fail(EXPR) blob_appendf EXPR; goto end_fail
zFlag = PD("filename",P("fn"));
if(zFlag==0 || !*zFlag){
rc = 400;
if(bIsMissingArg){
*bIsMissingArg = 1;
|
| ︙ | ︙ | |||
1369 1370 1371 1372 1373 1374 1375 |
if(i++){
CX(",");
}
CX("%!j", zFilename);
}
}
db_finalize(&q);
| | | 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 |
if(i++){
CX(",");
}
CX("%!j", zFilename);
}
}
db_finalize(&q);
CX("]}");
}
/*
** AJAX route /fileedit?ajax=filelist
**
** Fetches a JSON-format list of leaves and/or filenames for use in
** creating a file selection list in /fileedit. It has different modes
|
| ︙ | ︙ | |||
1425 1426 1427 1428 1429 1430 1431 | } } /* ** AJAX route /fileedit?ajax=commit ** ** Required query parameters: | | | | 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 | } } /* ** AJAX route /fileedit?ajax=commit ** ** Required query parameters: ** ** filename=FILENAME ** checkin=Parent check-in UUID ** content=text ** comment=non-empty text ** ** Optional query parameters: ** ** comment_mimetype=text (NOT currently honored) ** ** dry_run=int (1 or 0) ** ** include_manifest=int (1 or 0), whether to include ** the generated manifest in the response. ** ** ** User must have Write permissions to use this page. ** ** Responds with JSON (with some state repeated ** from the input in order to avoid certain race conditions ** client-side): ** |
| ︙ | ︙ | |||
1575 1576 1577 1578 1579 1580 1581 | ** use of the name parameter. ** ** Which additional parameters are used by each distinct ajax route ** is an internal implementation detail and may change with any ** given build of this code. An unknown "name" value triggers an ** error, as documented for ajax_route_error(). */ | | | 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 |
** use of the name parameter.
**
** Which additional parameters are used by each distinct ajax route
** is an internal implementation detail and may change with any
** given build of this code. An unknown "name" value triggers an
** error, as documented for ajax_route_error().
*/
/* Allow no access to this page without check-in privilege */
login_check_credentials();
if( !g.perm.Write ){
if(zAjax!=0){
ajax_route_error(403, "Write permissions required.");
}else{
login_needed(g.anon.Write);
|
| ︙ | ︙ | |||
1668 1669 1670 1671 1672 1673 1674 |
** have a common, page-specific container we can filter our CSS
** selectors, but we do have the BODY, which we can decorate with
** whatever CSS we wish...
*/
style_script_begin(__FILE__,__LINE__);
CX("document.body.classList.add('fileedit');\n");
style_script_end();
| | | 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 |
** have a common, page-specific container we can filter our CSS
** selectors, but we do have the BODY, which we can decorate with
** whatever CSS we wish...
*/
style_script_begin(__FILE__,__LINE__);
CX("document.body.classList.add('fileedit');\n");
style_script_end();
/* Status bar */
CX("<div id='fossil-status-bar' "
"title='Status message area. Double-click to clear them.'>"
"Status messages will go here.</div>\n"
/* will be moved into the tab container via JS */);
CX("<div id='fileedit-edit-status'>"
|
| ︙ | ︙ | |||
1937 1938 1939 1940 1941 1942 1943 |
*/
style_select_list_str("comment-mimetype", "comment_mimetype",
"Comment style:",
"Specify how fossil will interpret the "
"comment string.",
NULL,
"Fossil", "text/x-fossil-wiki",
| | | 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 |
*/
style_select_list_str("comment-mimetype", "comment_mimetype",
"Comment style:",
"Specify how fossil will interpret the "
"comment string.",
NULL,
"Fossil", "text/x-fossil-wiki",
"Markdown", "text/x-markdown",
"Plain text", "text/plain",
NULL);
CX("</div>\n");
}
CX("<div class='fileedit-hint flex-container flex-row'>"
"(Warning: switching from multi- to single-line mode will "
"strip out all newlines!)</div>");
|
| ︙ | ︙ |
Changes to src/finfo.c.
| ︙ | ︙ | |||
565 566 567 568 569 570 571 |
if( ridTo ){
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridTo);
zLink = href("%R/info/%!S", zUuid);
blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
fossil_free(zUuid);
}
}else if( ridCi ){
| | | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
if( ridTo ){
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridTo);
zLink = href("%R/info/%!S", zUuid);
blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
fossil_free(zUuid);
}
}else if( ridCi ){
blob_appendf(&title, "History of file ");
hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
blob_appendf(&title, " at check-in %z%h</a>",
href("%R/info?name=%t",zCI), zCI);
}else{
blob_appendf(&title, "History for ");
hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
|
| ︙ | ︙ |
Changes to src/forum.c.
| ︙ | ︙ | |||
1518 1519 1520 1521 1522 1523 1524 |
@ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \
@ Dry run</label>
@ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \
@ Require moderator approval</label>
@ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \
@ Show query parameters</label>
@ <br><label><input type="checkbox" name="fpsilent" %s(PCK("fpsilent"))> \
| | | 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 |
@ <label><input type="checkbox" name="dryrun" %s(PCK("dryrun"))> \
@ Dry run</label>
@ <br><label><input type="checkbox" name="domod" %s(PCK("domod"))> \
@ Require moderator approval</label>
@ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \
@ Show query parameters</label>
@ <br><label><input type="checkbox" name="fpsilent" %s(PCK("fpsilent"))> \
@ Do not send notification emails</label>
@ </div>
}
}
/*
** WEBPAGE: forume1
**
|
| ︙ | ︙ |
Changes to src/fossil.bootstrap.js.
| ︙ | ︙ | |||
135 136 137 138 139 140 141 |
};
/**
repoUrl( repoRelativePath [,urlParams] )
Creates a URL by prepending this.rootPath to the given path
(which must be relative from the top of the site, without a
leading slash). If urlParams is a string, it must be
| | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
};
/**
repoUrl( repoRelativePath [,urlParams] )
Creates a URL by prepending this.rootPath to the given path
(which must be relative from the top of the site, without a
leading slash). If urlParams is a string, it must be
parameters encoded in the form "key=val&key2=val2..." WITHOUT
a leading '?'. If it's an object, all of its properties get
appended to the URL in that form.
*/
F.repoUrl = function(path,urlParams){
if(!urlParams) return this.rootPath+path;
const url=[this.rootPath,path];
url.push('?');
|
| ︙ | ︙ | |||
298 299 300 301 302 303 304 |
the callback immediately and hinder future invocations until at
least the given time has passed.
If passed only 1 argument, or passed a falsy 2nd argument,
the default wait time set in this function's $defaultDelay
property is used.
| | | | | | | | | | 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 |
the callback immediately and hinder future invocations until at
least the given time has passed.
If passed only 1 argument, or passed a falsy 2nd argument,
the default wait time set in this function's $defaultDelay
property is used.
Inspiration: underscore.js, by way of https://davidwalsh.name/javascript-debounce-function
*/
F.debounce = function f(func, waitMs, immediate) {
var timeoutId;
if(!waitMs) waitMs = f.$defaultDelay;
return function() {
const context = this, args = Array.prototype.slice.call(arguments);
const later = function() {
timeoutId = undefined;
if(!immediate) func.apply(context, args);
};
const callNow = immediate && !timeoutId;
clearTimeout(timeoutId);
timeoutId = setTimeout(later, waitMs);
if(callNow) func.apply(context, args);
};
};
F.debounce.$defaultDelay = 500 /*arbitrary*/;
})(window);
|
Changes to src/fossil.diff.js.
| ︙ | ︙ | |||
104 105 106 107 108 109 110 |
const td = tr.querySelector('td:nth-child('+(
/* TD element with the line numbers */
getLHS ? 1 : (isSplit ? 4 : 2)
)+')');
const m = f.rx[getStart ? 'start' : 'end'].exec(td.innerText);
return m ? +m[1] : undefined/*"shouldn't happen"*/;
};
| | | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
const td = tr.querySelector('td:nth-child('+(
/* TD element with the line numbers */
getLHS ? 1 : (isSplit ? 4 : 2)
)+')');
const m = f.rx[getStart ? 'start' : 'end'].exec(td.innerText);
return m ? +m[1] : undefined/*"shouldn't happen"*/;
};
/**
Installs chunk-loading controls into TR.diffskip element tr.
Each instance corresponds to a single TR.diffskip element.
The goal is to base these controls roughly on github's, a good
example of which, for use as a model, is:
|
| ︙ | ︙ | |||
398 399 400 401 402 403 404 |
if(doAppend) content.push(lineNoTxt);
else content.unshift(lineNoTxt);
if(joinTr){
content.push(trNext.querySelector(selector).innerHTML);
}
td.innerHTML = content.join('');
if(joinTr) D.remove(joinTr);
| < | 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
if(doAppend) content.push(lineNoTxt);
else content.unshift(lineNoTxt);
if(joinTr){
content.push(trNext.querySelector(selector).innerHTML);
}
td.innerHTML = content.join('');
if(joinTr) D.remove(joinTr);
this.destroy();
return this;
}else if(this.FetchType.PrevDown===fetchType){
/* Append context to previous TR. */
// RHS line numbers...
let startLnR = this.pos.prev.endRhs+1;
lineno.length = lines.length;
|
| ︙ | ︙ | |||
427 428 429 430 431 432 433 |
if(lines.length < (urlParam.to - urlParam.from)){
/* No more data. */
this.destroy();
}else{
this.maybeReplaceButtons();
this.updatePosDebug();
}
| < | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
if(lines.length < (urlParam.to - urlParam.from)){
/* No more data. */
this.destroy();
}else{
this.maybeReplaceButtons();
this.updatePosDebug();
}
return this;
}else if(this.FetchType.NextUp===fetchType){
/* Prepend content to next TR. */
// RHS line numbers...
if(doAppend){
throw new Error("Internal precondition violation: doAppend is true.");
}
|
| ︙ | ︙ | |||
456 457 458 459 460 461 462 |
|| lines.length < (urlParam.to - urlParam.from)){
/* No more data. */
this.destroy();
}else{
this.maybeReplaceButtons();
this.updatePosDebug();
}
| < | 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
|| lines.length < (urlParam.to - urlParam.from)){
/* No more data. */
this.destroy();
}else{
this.maybeReplaceButtons();
this.updatePosDebug();
}
return this;
}else{
throw new Error("Unexpected 'fetchType' value.");
}
},
/**
|
| ︙ | ︙ | |||
621 622 623 624 625 626 627 |
new ChunkLoadControls(D.addClass(tr, 'jchunk'));
});
});
return F;
};
Diff.setupDiffContextLoad();
});
| | < < < < < | < < > < < < < < | < | > > > > > | > > | < | | < < > > | < < < | > < < < < < < | < < | < | > | < > | | > | < | | > | < < < < < | < < < < < < > | < | < < > > < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < | < < | < < | | | | | < > > > | < | < < < < < < < > > | | < | < | 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 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 |
new ChunkLoadControls(D.addClass(tr, 'jchunk'));
});
});
return F;
};
Diff.setupDiffContextLoad();
});
/*
** For a side-by-side diff, ensure that horizontal scrolling of either
** side of the diff is synchronized with the other side.
*/
window.fossil.onPageLoad(function(){
const F = window.fossil, D = F.dom, Diff = F.diff;
/* Look for a parent element to hold the sbs-sync-scroll toggle
checkbox. This differs per page. If we don't find one, simply
elide that toggle and use whatever preference the user last
specified (defaulting to on). */
let cbSync /* scroll-sync checkbox */;
let eToggleParent /* element to put the sync-scroll checkbox in */;
const potentialParents = [ /* possible parents for the checkbox */
/* Put the most likely pages at the end, as array.pop() is more
efficient than array.shift() (see loop below). */
/* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
/* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
/* /vdiff */ 'body.vdiff form div.submenu',
/* /info, /vinfo */ 'body.vinfo div.sectionmenu.info-changes-menu'
];
while( potentialParents.length ){
if( (eToggleParent = document.querySelector(potentialParents.pop())) ){
break;
}
}
const keySbsScroll = 'sync-diff-scroll' /* F.storage key */;
if( eToggleParent ){
/* Add a checkbox to toggle sbs scroll sync. Remember that in
order to be UI-consistent in the /vdiff page we have to ensure
that the checkbox is to the LEFT of of its label. We store the
sync-scroll preference in F.storage (not a cookie) so that it
persists across page loads and different apps. */
cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true));
D.append(eToggleParent, D.append(
D.addClass(D.create('span'), 'input-with-label'),
D.append(D.create('label'),
cbSync, "Sync side-by-side scrolling")
));
cbSync.addEventListener('change', function(e){
F.storage.set(keySbsScroll, e.target.checked);
});
}
const useSync = cbSync ? ()=>cbSync.checked : ()=>F.storage.getBool(keySbsScroll,true);
/* Now set up the events to enable syncronized scrolling... */
const scrollLeft = function(event){
const table = this.parentElement/*TD*/.parentElement/*TR*/.
parentElement/*TBODY*/.parentElement/*TABLE*/;
if( useSync() ){
table.$txtPres.forEach((e)=>(e===this) ? 1 : (e.scrollLeft = this.scrollLeft));
}
return false;
};
const SCROLL_LEN = 64/* pixels to scroll for keyboard events */;
const keycodes = Object.assign(Object.create(null),{
37/*cursor left*/: -SCROLL_LEN, 39/*cursor right*/: SCROLL_LEN
});
/** keydown handler for a diff element */
const handleDiffKeydown = function(e){
const len = keycodes[e.keyCode];
if( !len ) return false;
if( useSync() ){
this.$txtPres[0].scrollLeft += len;
}else if( this.$preCurrent ){
this.$preCurrent.scrollLeft += len;
}
return false;
};
/**
Sets up synchronized scrolling of table.splitdiff element
`diff`. If passed no argument, it scans the dom for elements to
initialize. The second argument is for this function's own
internal use.
It's okay (but wasteful) to pass the same element to this
function multiple times: it will only be set up for sync
scrolling the first time it's passed to this function.
Note that this setup is ignorant of the cbSync toggle: the toggle
is checked when scrolling, not when initializing the sync-scroll
capability.
*/
const initTableDiff = function f(diff, unifiedDiffs){
if(!diff){
let i, diffs;
diffs = document.querySelectorAll('table.splitdiff');
for(i=0; i<diffs.length; ++i){
f.call(this, diffs[i], false);
}
diffs = document.querySelectorAll('table.udiff');
for(i=0; i<diffs.length; ++i){
f.call(this, diffs[i], true);
}
return this;
}
diff.$txtCols = diff.querySelectorAll('td.difftxt');
diff.$txtPres = diff.querySelectorAll('td.difftxt pre');
if(!unifiedDiffs){
diff.$txtPres.forEach(function(e){
if(!e.classList.contains('scroller')){
D.addClass(e, 'scroller');
e.addEventListener('scroll', scrollLeft, false);
}
});
diff.tabIndex = 0;
if(!diff.classList.contains('scroller')){
/* Keyboard-based scrolling requires special-case handling to
ensure that we scroll the proper side of the diff when sync
is off. */
D.addClass(diff, 'scroller');
diff.addEventListener('keydown', handleDiffKeydown, false);
const handleLRClick = function(ev){
diff.$preCurrent = this /* NOT ev.target, which is probably a child element */;
};
diff.$txtPres.forEach((e)=>e.addEventListener('click', handleLRClick, false));
}
}
return this;
}
window.fossil.page.tweakSbsDiffs = function(){
document.querySelectorAll('table.splitdiff').forEach((e)=>initTableDiff(e));
};
initTableDiff();
}, false);
|
Changes to src/fossil.dom.js.
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
*/
dom.splitClassList = function f(str){
if(!f.rx){
f.rx = /(\s+|\s*,\s*)/;
}
return str ? str.split(f.rx) : [str];
};
| | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
*/
dom.splitClassList = function f(str){
if(!f.rx){
f.rx = /(\s+|\s*,\s*)/;
}
return str ? str.split(f.rx) : [str];
};
dom.div = dom.createElemFactory('div');
dom.p = dom.createElemFactory('p');
dom.code = dom.createElemFactory('code');
dom.pre = dom.createElemFactory('pre');
dom.header = dom.createElemFactory('header');
dom.footer = dom.createElemFactory('footer');
dom.section = dom.createElemFactory('section');
|
| ︙ | ︙ | |||
547 548 549 550 551 552 553 |
finally{
--f.counter;
}
if(!f.counter){
old.parentNode.removeChild(old);
}
};
| | | 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
finally{
--f.counter;
}
if(!f.counter){
old.parentNode.removeChild(old);
}
};
dom.replaceNode.counter = 0;
/**
Two args == getter: (e,key), returns value
Three or more == setter: (e,key,val[...,keyN,valN]), returns
e. If val===null or val===undefined then the attribute is
removed. If (e) has a forEach method then this routine is applied
to each element of that collection via that method. Each pair of
|
| ︙ | ︙ |
Changes to src/fossil.fetch.js.
| ︙ | ︙ | |||
232 233 234 235 236 237 238 |
/**
urlTransform() must refer to a function which accepts a relative path
to the same site as fetch() is served from and an optional set of
URL parameters to pass with it (in the form a of a string
("a=b&c=d...") or an object of key/value pairs (which it converts
to such a string), and returns the resulting URL or URI as a string.
| | | 232 233 234 235 236 237 238 239 240 241 242 243 244 |
/**
urlTransform() must refer to a function which accepts a relative path
to the same site as fetch() is served from and an optional set of
URL parameters to pass with it (in the form a of a string
("a=b&c=d...") or an object of key/value pairs (which it converts
to such a string), and returns the resulting URL or URI as a string.
*/
fossil.fetch.urlTransform = (u,p)=>fossil.repoUrl(u,p);
fossil.fetch.beforesend = function(){};
fossil.fetch.aftersend = function(){};
fossil.fetch.timeout = 15000/* Default timeout, in ms. */;
})(window.fossil);
|
Changes to src/fossil.page.chat.js.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
let dbg = document.querySelector('#debugMsg');
if(dbg){
/* This can inadvertently influence our flexbox layouts, so move
it out of the way. */
D.append(document.body,dbg);
}
})();
const ForceResizeKludge = (function(){
/* Workaround for Safari mayhem regarding use of vh CSS units....
We tried to use vh units to set the content area size for the
chat layout, but Safari chokes on that, so we calculate that
height here: 85% when in "normal" mode and 95% in chat-only
mode. Larger than ~95% is too big for Firefox on Android,
causing the input area to move off-screen.
While we're here, we also use this to cap the max-height
of the input field so that pasting huge text does not scroll
the upper area of the input widget off-screen. */
| > > > > > > > > > > > > | < < < < < | | | > > > | 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 134 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 |
let dbg = document.querySelector('#debugMsg');
if(dbg){
/* This can inadvertently influence our flexbox layouts, so move
it out of the way. */
D.append(document.body,dbg);
}
})();
/* Returns a list of DOM elements which "frame" the chat UI. These
elements are considered to _not_ be part of the chat UI and that
info is used for sizing the chat UI. In chat-only mode, these are
the elements that get hidden. */
const GetFramingElements = function() {
return document.querySelectorAll([
"body > header",
"body > nav.mainmenu",
"body > footer",
"#debugMsg"
].join(','));
};
const ForceResizeKludge = (function(){
/* Workaround for Safari mayhem regarding use of vh CSS units....
We tried to use vh units to set the content area size for the
chat layout, but Safari chokes on that, so we calculate that
height here: 85% when in "normal" mode and 95% in chat-only
mode. Larger than ~95% is too big for Firefox on Android,
causing the input area to move off-screen.
While we're here, we also use this to cap the max-height
of the input field so that pasting huge text does not scroll
the upper area of the input widget off-screen. */
const elemsToCount = GetFramingElements();
const contentArea = E1('div.content');
const bcl = document.body.classList;
const resized = function f(){
if(f.$disabled) return;
const wh = window.innerHeight,
com = bcl.contains('chat-only-mode');
var ht;
var extra = 0;
if(com){
ht = wh;
}else{
elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false);
ht = wh - extra;
}
f.chat.e.inputX.style.maxHeight = (ht/2)+"px";
/* ^^^^ this is a middle ground between having no size cap
on the input field and having a fixed arbitrary cap. */;
contentArea.style.height =
contentArea.style.maxHeight = [
"calc(", (ht>=100 ? ht : 100), "px",
" - 0.65em"/*fudge value*/,")"
/* ^^^^ hypothetically not needed, but both Chrome/FF on
Linux will force scrollbars on the body if this value is
too small; current value is empirically selected. */
].join('');
if(false){
console.debug("resized.",wh, extra, ht,
window.getComputedStyle(contentArea).maxHeight,
contentArea);
console.debug("Set input max height to: ",
f.chat.e.inputX.style.maxHeight);
}
};
resized.$disabled = true/*gets deleted when setup is finished*/;
window.addEventListener('resize', F.debounce(resized, 250), false);
return resized;
})();
fossil.FRK = ForceResizeKludge/*for debugging*/;
const Chat = ForceResizeKludge.chat = (function(){
const cs = { // the "Chat" object (result of this function)
verboseErrors: false /* if true then certain, mostly extraneous,
error messages may be sent to the console. */,
playedBeep: false /* used for the beep-once setting */,
e:{/*map of certain DOM elements.*/
messageInjectPoint: E1('#message-inject-point'),
pageTitle: E1('head title'),
loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
inputWrapper: E1("#chat-input-area"),
inputElementWrapper: E1('#chat-input-line-wrapper'),
fileSelectWrapper: E1('#chat-input-file-area'),
viewMessages: E1('#chat-messages-wrapper'),
btnSubmit: E1('#chat-button-submit'),
btnAttach: E1('#chat-button-attach'),
inputX: E1('#chat-input-field-x'),
input1: E1('#chat-input-field-single'),
inputM: E1('#chat-input-field-multi'),
inputFile: E1('#chat-input-file'),
contentDiv: E1('div.content'),
viewConfig: E1('#chat-config'),
viewPreview: E1('#chat-preview'),
previewContent: E1('#chat-preview-content'),
viewSearch: E1('#chat-search'),
searchContent: E1('#chat-search-content'),
btnPreview: E1('#chat-button-preview'),
views: document.querySelectorAll('.chat-view'),
activeUserListWrapper: E1('#chat-user-list-wrapper'),
activeUserList: E1('#chat-user-list')
},
me: F.user.name,
mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
|
| ︙ | ︙ | |||
168 169 170 171 172 173 174 |
},
filterState:{
activeUser: undefined,
match: function(uname){
return this.activeUser===uname || !this.activeUser;
}
},
| > | | | > > > > | | | > > > > > | 178 179 180 181 182 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 |
},
filterState:{
activeUser: undefined,
match: function(uname){
return this.activeUser===uname || !this.activeUser;
}
},
/**
Gets (no args) or sets (1 arg) the current input text field
value, taking into account single- vs multi-line input. The
getter returns a trim()'d string and the setter returns this
object. As a special case, if arguments[0] is a boolean
value, it behaves like a getter and, if arguments[0]===true
it clears the input field before returning.
*/
inputValue: function(/*string newValue | bool clearInputField*/){
const e = this.inputElement();
if(arguments.length && 'boolean'!==typeof arguments[0]){
if(e.isContentEditable) e.innerText = arguments[0];
else e.value = arguments[0];
return this;
}
const rc = e.isContentEditable ? e.innerText : e.value;
if( true===arguments[0] ){
if(e.isContentEditable) e.innerText = '';
else e.value = '';
}
return rc && rc.trim();
},
/** Asks the current user input field to take focus. Returns this. */
inputFocus: function(){
this.inputElement().focus();
return this;
},
/** Returns the current message input element. */
|
| ︙ | ︙ | |||
322 323 324 325 326 327 328 |
"chat-only" mode. That mode hides the page's header and
footer, leaving only the chat application visible to the
user.
*/
chatOnlyMode: function f(yes){
if(undefined === f.elemsToToggle){
f.elemsToToggle = [];
| < < < < < < | | 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
"chat-only" mode. That mode hides the page's header and
footer, leaving only the chat application visible to the
user.
*/
chatOnlyMode: function f(yes){
if(undefined === f.elemsToToggle){
f.elemsToToggle = [];
GetFramingElements().forEach((e)=>f.elemsToToggle.push(e));
}
if(!arguments.length) yes = true;
if(yes === this.isChatOnlyMode()) return this;
if(yes){
D.addClass(f.elemsToToggle, 'hidden');
D.addClass(document.body, 'chat-only-mode');
document.body.scroll(0,document.body.height);
|
| ︙ | ︙ | |||
411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
hidden */
"chat-only-mode": false,
/* When set to a URI, it is assumed to be an audio file,
which gets played when new messages arrive. When true,
the first entry in the audio file selection list will be
used. */
"audible-alert": true,
/* When on, show the list of "active" users - those from
whom we have messages in the currently-loaded history
(noting that deletions are also messages). */
"active-user-list": false,
/* When on, the [active-user-list] setting includes the
timestamp of each user's most recent message. */
"active-user-list-timestamps": false,
| > > > | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 |
hidden */
"chat-only-mode": false,
/* When set to a URI, it is assumed to be an audio file,
which gets played when new messages arrive. When true,
the first entry in the audio file selection list will be
used. */
"audible-alert": true,
/*
*/
"beep-once": false,
/* When on, show the list of "active" users - those from
whom we have messages in the currently-loaded history
(noting that deletions are also messages). */
"active-user-list": false,
/* When on, the [active-user-list] setting includes the
timestamp of each user's most recent message. */
"active-user-list-timestamps": false,
|
| ︙ | ︙ | |||
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 |
}
},
/** Plays a new-message notification sound IF the audible-alert
setting is true, else this is a no-op. Returns this.
*/
playNewMessageSound: function f(){
if(f.uri){
try{
if(!f.audio) f.audio = new Audio(f.uri);
f.audio.currentTime = 0;
f.audio.play();
}catch(e){
console.error("Audio playblack failed.", f.uri, e);
}
}
return this;
},
/**
Sets the current new-message audio alert URI (must be a
repository-relative path which responds with an audio
file). Pass a falsy value to disable audio alerts. Returns
this.
*/
setNewMessageSound: function f(uri){
delete this.playNewMessageSound.audio;
this.playNewMessageSound.uri = uri;
this.settings.set('audible-alert', uri);
return this;
},
/**
Expects e to be one of the elements in this.e.views.
| > > > > > > > | 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 481 482 483 484 485 486 487 488 489 |
}
},
/** Plays a new-message notification sound IF the audible-alert
setting is true, else this is a no-op. Returns this.
*/
playNewMessageSound: function f(){
if(f.uri){
if(!cs.pageIsActive
/* ^^^ this could also arguably apply when chat is visible */
&& this.playedBeep && this.settings.getBool('beep-once',false)){
return;
}
try{
this.playedBeep = true;
if(!f.audio) f.audio = new Audio(f.uri);
f.audio.currentTime = 0;
f.audio.play();
}catch(e){
console.error("Audio playblack failed.", f.uri, e);
}
}
return this;
},
/**
Sets the current new-message audio alert URI (must be a
repository-relative path which responds with an audio
file). Pass a falsy value to disable audio alerts. Returns
this.
*/
setNewMessageSound: function f(uri){
this.playedBeep = false;
delete this.playNewMessageSound.audio;
this.playNewMessageSound.uri = uri;
this.settings.set('audible-alert', uri);
return this;
},
/**
Expects e to be one of the elements in this.e.views.
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 |
callee.addUserElem = function(u){
const uSpan = D.addClass(D.span(), 'chat-user');
const uDate = self.usersLastSeen[u];
if(self.filterState.activeUser===u){
uSpan.classList.add('selected');
}
uSpan.dataset.uname = u;
| | | 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 |
callee.addUserElem = function(u){
const uSpan = D.addClass(D.span(), 'chat-user');
const uDate = self.usersLastSeen[u];
if(self.filterState.activeUser===u){
uSpan.classList.add('selected');
}
uSpan.dataset.uname = u;
D.append(uSpan, u, "\n",
D.append(
D.addClass(D.span(),'timestamp'),
localTimeString(uDate)//.substr(5/*chop off year*/)
));
if(uDate.$uColor){
uSpan.style.backgroundColor = uDate.$uColor;
}
|
| ︙ | ︙ | |||
689 690 691 692 693 694 695 |
/**
Toggles the given message between its parsed and plain-text
representations. It requires a server round-trip to collect the
plain-text form but caches it for subsequent toggles.
Expects the ID of a currently-loaded message or a
message-widget DOM elment from which it can extract an id.
| | | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 |
/**
Toggles the given message between its parsed and plain-text
representations. It requires a server round-trip to collect the
plain-text form but caches it for subsequent toggles.
Expects the ID of a currently-loaded message or a
message-widget DOM elment from which it can extract an id.
This is an async operation the first time it's passed a given
message and synchronous on subsequent calls for that
message. It is a no-op if id does not resolve to a loaded
message.
*/
cs.toggleTextMode = function(id){
var e;
if(id instanceof HTMLElement){
|
| ︙ | ︙ | |||
761 762 763 764 765 766 767 |
aftersend:function(){
delete e.$isToggling;
Chat.ajaxEnd();
}
});
return true;
};
| | | | | 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 |
aftersend:function(){
delete e.$isToggling;
Chat.ajaxEnd();
}
});
return true;
};
/** Given a .message-row element, this function returns whethe the
current user may, at least hypothetically, delete the message
globally. A user may always delete a local copy of a
post. The server may trump this, e.g. if the login has been
cancelled after this page was loaded.
*/
cs.userMayDelete = function(eMsg){
return +eMsg.dataset.msgid>0
&& (this.me === eMsg.dataset.xfrom
|| F.user.isAdmin/*will be confirmed server-side*/);
};
/** Returns a new Error() object encapsulating state from the given
fetch() response promise. */
cs._newResponseError = function(response){
return new Error([
"HTTP status ", response.status,": ",response.url,": ",
response.statusText].join(''));
};
/** Helper for reporting HTTP-level response errors via fetch().
If response.ok then response.json() is returned, else an Error
is thrown. */
cs._fetchJsonOrError = function(response){
if(response.ok) return response.json();
else throw cs._newResponseError(response);
};
/**
Removes the given message ID from the local chat record and, if
the message was posted by this user OR this user in an
admin/setup, also submits it for removal on the remote.
id may optionally be a DOM element, in which case it must be a
.message-row element.
|
| ︙ | ︙ | |||
820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
});
}else{
this.deleteMessageElem(id);
}
};
document.addEventListener('visibilitychange', function(ev){
cs.pageIsActive = ('visible' === document.visibilityState);
if(cs.pageIsActive){
cs.e.pageTitle.innerText = cs.pageTitleOrig;
if(document.activeElement!==cs.inputElement()){
/* An attempt to resolve usability problem reported by Joe
M. where the Pale Moon browser is giving input focus to
the Preview button. The down-side of this is that it will
deselect any text which was previously selected on this
| > | 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 |
});
}else{
this.deleteMessageElem(id);
}
};
document.addEventListener('visibilitychange', function(ev){
cs.pageIsActive = ('visible' === document.visibilityState);
cs.playedBeep = false;
if(cs.pageIsActive){
cs.e.pageTitle.innerText = cs.pageTitleOrig;
if(document.activeElement!==cs.inputElement()){
/* An attempt to resolve usability problem reported by Joe
M. where the Pale Moon browser is giving input focus to
the Preview button. The down-side of this is that it will
deselect any text which was previously selected on this
|
| ︙ | ︙ | |||
887 888 889 890 891 892 893 |
deficiencies in Safari.
*/
Chat.MessageWidget = (function(){
/**
Constructor. If passed an argument, it is passed to
this.setMessage() after initialization.
*/
| | | | > > | | > > > | | > > > > > > | | | > > | > | | | < < | 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 949 950 951 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 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 |
deficiencies in Safari.
*/
Chat.MessageWidget = (function(){
/**
Constructor. If passed an argument, it is passed to
this.setMessage() after initialization.
*/
const ctor = function(){
this.e = {
body: D.addClass(D.div(), 'message-widget'),
tab: D.addClass(D.div(), 'message-widget-tab'),
content: D.addClass(D.div(), 'message-widget-content')
};
D.append(this.e.body, this.e.tab, this.e.content);
this.e.tab.setAttribute('role', 'button');
if(arguments.length){
this.setMessage(arguments[0]);
}
};
/* Left-zero-pad a number to at least 2 digits */
const dowMap = {
/* Map of Date.getDay() values to weekday names. */
0: "Sunday", 1: "Monday", 2: "Tuesday",
3: "Wednesday", 4: "Thursday", 5: "Friday",
6: "Saturday"
};
/* Given a Date, returns the timestamp string used in the "tab"
part of message widgets. If longFmt is true then a verbose
format is used, else a brief format is used. The returned string
is in client-local time. */
const theTime = function(d, longFmt=false){
const li = [];
if( longFmt ){
li.push(
d.getFullYear(),
'-', pad2(d.getMonth()+1),
'-', pad2(d.getDate()),
' ',
d.getHours(), ":",
(d.getMinutes()+100).toString().slice(1,3)
);
}else{
li.push(
d.getHours(),":",
(d.getMinutes()+100).toString().slice(1,3),
' ', dowMap[d.getDay()]
);
}
return li.join('');
};
/**
Returns true if this page believes it can embed a view of the
file wrapped by the given message object, else returns false.
*/
const canEmbedFile = function f(msg){
if(!f.$rx){
f.$rx = /\.((html?)|(txt)|(md)|(wiki)|(pikchr))$/i;
f.$specificTypes = [
/* Mime types we know we can embed, sans image/... */
'text/plain',
'text/html',
'text/x-markdown',
/* Firefox sends text/markdown when uploading .md files */
'text/markdown',
'text/x-pikchr',
'text/x-fossil-wiki'
/* Add more as we discover which ones Firefox won't
force the user to try to download. */
];
}
if(msg.fmime){
if(msg.fmime.startsWith("image/")
|| f.$specificTypes.indexOf(msg.fmime)>=0){
return true;
}
}
return (msg.fname && f.$rx.test(msg.fname));
};
/**
Returns true if the given message object "should"
be embedded in fossil-rendered form instead of
raw content form. This is only intended to be passed
message objects for which canEmbedFile() returns true.
*/
const shouldFossilRenderEmbed = function f(msg){
if(!f.$rx){
f.$rx = /\.((md)|(wiki)|(pikchr))$/i;
f.$specificTypes = [
'text/x-markdown',
'text/markdown' /* Firefox-uploaded md files */,
'text/x-pikchr',
'text/x-fossil-wiki'
];
}
if(msg.fmime){
if(f.$specificTypes.indexOf(msg.fmime)>=0) return true;
}
return msg.fname && f.$rx.test(msg.fname);
};
|
| ︙ | ︙ | |||
991 992 993 994 995 996 997 |
const isHidden = iframe.classList.contains('hidden');
if(isHidden) D.removeClass(iframe, 'hidden');
iframe.style.maxHeight = iframe.style.height
= iframe.contentWindow.document.documentElement.scrollHeight + 'px';
if(isHidden) D.addClass(iframe, 'hidden');
}
};
| | | | 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 |
const isHidden = iframe.classList.contains('hidden');
if(isHidden) D.removeClass(iframe, 'hidden');
iframe.style.maxHeight = iframe.style.height
= iframe.contentWindow.document.documentElement.scrollHeight + 'px';
if(isHidden) D.addClass(iframe, 'hidden');
}
};
ctor.prototype = {
scrollIntoView: function(){
this.e.content.scrollIntoView();
},
setMessage: function(m){
const ds = this.e.body.dataset;
ds.timestamp = m.mtime;
ds.lmtime = m.lmtime;
|
| ︙ | ︙ | |||
1017 1018 1019 1020 1021 1022 1023 |
D.clearElement(this.e.tab);
var contentTarget = this.e.content;
var eXFrom /* element holding xfrom name */;
if(m.xfrom){
eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
const wrapper = D.append(
D.span(), eXFrom,
| > > | > > > > | | 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 |
D.clearElement(this.e.tab);
var contentTarget = this.e.content;
var eXFrom /* element holding xfrom name */;
if(m.xfrom){
eXFrom = D.append(D.addClass(D.span(), 'xfrom'), m.xfrom);
const wrapper = D.append(
D.span(), eXFrom,
' ',
D.append(D.addClass(D.span(), 'msgid'),
'#' + (m.msgid||'???')),
(m.isSearchResult ? ' ' : ' @ '),
D.append(D.addClass(D.span(), 'timestamp'),
theTime(d,!!m.isSearchResult))
);
D.append(this.e.tab, wrapper);
}else{/*notification*/
D.addClass(this.e.body, 'notification');
if(m.isError){
D.addClass([contentTarget, this.e.tab], 'error');
}
D.append(
this.e.tab,
D.append(D.code(), 'notification @ ',theTime(d,false))
);
}
if( m.xfrom && m.fsize>0 ){
if( m.fmime
&& m.fmime.startsWith("image/")
&& Chat.settings.getBool('images-inline',true)
){
|
| ︙ | ︙ | |||
1053 1054 1055 1056 1057 1058 1059 |
"(" + m.fname + " " + m.fsize + " bytes)"
)
D.attr(a,'target','_blank');
D.append(w, a);
if(canEmbedFile(m)){
/* Add an option to embed HTML attachments in an iframe. The primary
use case is attached diffs. */
| | | | | 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 |
"(" + m.fname + " " + m.fsize + " bytes)"
)
D.attr(a,'target','_blank');
D.append(w, a);
if(canEmbedFile(m)){
/* Add an option to embed HTML attachments in an iframe. The primary
use case is attached diffs. */
const shouldFossilRender = shouldFossilRenderEmbed(m);
const downloadArgs = shouldFossilRender ? '?render' : '';
D.addClass(contentTarget, 'wide');
const embedTarget = this.e.content;
const self = this;
const btnEmbed = D.attr(D.checkbox("1", false), 'id',
'embed-'+ds.msgid);
const btnLabel = D.label(btnEmbed, shouldFossilRender
? "Embed (fossil-rendered)" : "Embed");
/* Maintenance reminder: do not disable the toggle
button while the content is loading because that will
cause it to get stuck in disabled mode if the browser
decides that loading the content should prompt the
user to download it, rather than embed it in the
iframe. */
|
| ︙ | ︙ | |||
1269 1270 1271 1272 1273 1274 1275 |
}
}/*f.popup*/;
}/*end static init*/
const theMsg = findMessageWidgetParent(ev.target);
if(theMsg) f.popup.show(theMsg);
}/*_handleLegendClicked()*/
};
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 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 1462 1463 1464 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 1492 1493 1494 1495 1496 1497 1498 |
}
}/*f.popup*/;
}/*end static init*/
const theMsg = findMessageWidgetParent(ev.target);
if(theMsg) f.popup.show(theMsg);
}/*_handleLegendClicked()*/
};
return ctor;
})()/*MessageWidget*/;
/**
A widget for loading more messages (context) around a /chat-query
result message.
*/
Chat.SearchCtxLoader = (function(){
const nMsgContext = 5;
const zUpArrow = '\u25B2';
const zDownArrow = '\u25BC';
const ctor = function(o){
/* iFirstInTable:
** msgid of first row in chatfts table.
**
** iLastInTable:
** msgid of last row in chatfts table.
**
** iPrevId:
** msgid of message immediately above this spacer. Or 0 if this
** spacer is above all results.
**
** iNextId:
** msgid of message immediately below this spacer. Or 0 if this
** spacer is below all results.
**
** bIgnoreClick:
** ignore any clicks if this is true. This is used to ensure there
** is only ever one request belonging to this widget outstanding
** at any time.
*/
this.o = {
iFirstInTable: o.first,
iLastInTable: o.last,
iPrevId: o.previd,
iNextId: o.nextid,
bIgnoreClick: false
};
this.e = {
body: D.addClass(D.div(), 'spacer-widget'),
up: D.addClass(
D.button(zDownArrow+' Load '+nMsgContext+' more '+zDownArrow),
'up'
),
down: D.addClass(
D.button(zUpArrow+' Load '+nMsgContext+' more '+zUpArrow),
'down'
),
all: D.addClass(D.button('Load More'), 'all')
};
D.append( this.e.body, this.e.up, this.e.down, this.e.all );
const ms = this;
this.e.up.addEventListener('click', ()=>ms.load_messages(false));
this.e.down.addEventListener('click', ()=>ms.load_messages(true));
this.e.all.addEventListener('click', ()=>ms.load_messages( (ms.o.iPrevId==0) ));
this.set_button_visibility();
};
ctor.prototype = {
set_button_visibility: function() {
if( !this.e ) return;
const o = this.o;
const iPrevId = (o.iPrevId!=0) ? o.iPrevId : o.iFirstInTable-1;
const iNextId = (o.iNextId!=0) ? o.iNextId : o.iLastInTable+1;
let nDiff = (iNextId - iPrevId) - 1;
for( const x of [this.e.up, this.e.down, this.e.all] ){
if( x ) D.addClass(x, 'hidden');
}
let nVisible = 0;
if( nDiff>0 ){
if( nDiff>nMsgContext && (o.iPrevId==0 || o.iNextId==0) ){
nDiff = nMsgContext;
}
if( nDiff<=nMsgContext && o.iPrevId!=0 && o.iNextId!=0 ){
D.removeClass(this.e.all, 'hidden');
++nVisible;
this.e.all.innerText = (
zUpArrow + " Load " + nDiff + " more " + zDownArrow
);
}else{
if( o.iPrevId!=0 ){
++nVisible;
D.removeClass(this.e.up, 'hidden');
}else if( this.e.up ){
if( this.e.up.parentNode ) D.remove(this.e.up);
delete this.e.up;
}
if( o.iNextId!=0 ){
++nVisible;
D.removeClass(this.e.down, 'hidden');
}else if( this.e.down ){
if( this.e.down.parentNode ) D.remove( this.e.down );
delete this.e.down;
}
}
}
if( !nVisible ){
/* The DOM elements can now be disposed of. */
for( const x of [this.e.up, this.e.down, this.e.all, this.e.body] ){
if( x?.parentNode ) D.remove(x);
}
delete this.e;
}
},
load_messages: function(bDown) {
if( this.bIgnoreClick ) return;
var iFirst = 0; /* msgid of first message to fetch */
var nFetch = 0; /* Number of messages to fetch */
var iEof = 0; /* last msgid in spacers range, plus 1 */
const e = this.e, o = this.o;
this.bIgnoreClick = true;
/* Figure out the required range of messages. */
if( bDown ){
iFirst = this.o.iNextId - nMsgContext;
if( iFirst<this.o.iFirstInTable ){
iFirst = this.o.iFirstInTable;
}
}else{
iFirst = this.o.iPrevId+1;
}
nFetch = nMsgContext;
iEof = (this.o.iNextId > 0) ? this.o.iNextId : this.o.iLastInTable+1;
if( iFirst+nFetch>iEof ){
nFetch = iEof - iFirst;
}
const ms = this;
F.fetch("chat-query",{
urlParams:{
q: '',
n: nFetch,
i: iFirst
},
responseType: "json",
onload:function(jx){
if( bDown ) jx.msgs.reverse();
jx.msgs.forEach((m) => {
m.isSearchResult = true;
var mw = new Chat.MessageWidget(m);
if( bDown ){
/* Inject the message below this object's body, or
append it to Chat.e.searchContent if this element
is the final one in its parent (Chat.e.searchContent). */
const eAnchor = e.body.nextElementSibling;
if( eAnchor ) Chat.e.searchContent.insertBefore(mw.e.body, eAnchor);
else D.append(Chat.e.searchContent, mw.e.body);
}else{
Chat.e.searchContent.insertBefore(mw.e.body, e.body);
}
});
if( bDown ){
o.iNextId -= jx.msgs.length;
}else{
o.iPrevId += jx.msgs.length;
}
ms.set_button_visibility();
ms.bIgnoreClick = false;
}
});
}
};
return ctor;
})() /*SearchCtxLoader*/;
const BlobXferState = (function(){
/* State for paste and drag/drop */
const bxs = {
dropDetails: document.querySelector('#chat-drop-details'),
blob: undefined,
clear: function(){
this.blob = undefined;
|
| ︙ | ︙ | |||
1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 |
Chat.reportErrorAsMessage(w);
};
/**
Submits the contents of the message input field (if not empty)
and/or the file attachment field to the server. If both are
empty, this is a no-op.
*/
Chat.submitMessage = function f(){
if(!f.spaces){
f.spaces = /\s+$/;
f.markdownContinuation = /\\\s+$/;
f.spaces2 = /\s{3,}$/;
}
this.setCurrentView(this.e.viewMessages);
const fd = new FormData();
const fallback = {msg: this.inputValue()};
var msg = fallback.msg;
if(msg && (msg.indexOf('\n')>0 || f.spaces.test(msg))){
/* Cosmetic: trim most whitespace from the ends of lines to try to
| > > > > > > > > | 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 |
Chat.reportErrorAsMessage(w);
};
/**
Submits the contents of the message input field (if not empty)
and/or the file attachment field to the server. If both are
empty, this is a no-op.
If the current view is the history search, this instead sends the
input text to that widget.
*/
Chat.submitMessage = function f(){
if(!f.spaces){
f.spaces = /\s+$/;
f.markdownContinuation = /\\\s+$/;
f.spaces2 = /\s{3,}$/;
}
switch( this.e.currentView ){
case this.e.viewSearch: this.submitSearch();
return;
default: break;
}
this.setCurrentView(this.e.viewMessages);
const fd = new FormData();
const fallback = {msg: this.inputValue()};
var msg = fallback.msg;
if(msg && (msg.indexOf('\n')>0 || f.spaces.test(msg))){
/* Cosmetic: trim most whitespace from the ends of lines to try to
|
| ︙ | ︙ | |||
1496 1497 1498 1499 1500 1501 1502 |
const text = Chat.inputValue().trim();
const ctrlMode = Chat.settings.getBool('edit-ctrl-send', false);
//console.debug("Enter key event:", ctrlMode, ev.ctrlKey, ev.shiftKey, ev);
if(ev.shiftKey){
const compactMode = Chat.settings.getBool('edit-compact-mode', false);
ev.preventDefault();
ev.stopPropagation();
| | | > | > | 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 |
const text = Chat.inputValue().trim();
const ctrlMode = Chat.settings.getBool('edit-ctrl-send', false);
//console.debug("Enter key event:", ctrlMode, ev.ctrlKey, ev.shiftKey, ev);
if(ev.shiftKey){
const compactMode = Chat.settings.getBool('edit-compact-mode', false);
ev.preventDefault();
ev.stopPropagation();
/* Shift-enter will run preview mode UNLESS the input field is empty
AND (preview or search mode) is active, in which cases it will
switch back to message view. */
if(!text &&
(Chat.e.currentView===Chat.e.viewPreview
| Chat.e.currentView===Chat.e.viewSearch)){
Chat.setCurrentView(Chat.e.viewMessages);
}else if(!text){
f.$toggleCompact(compactMode);
}else if(Chat.settings.getBool('edit-shift-enter-preview', true)){
Chat.e.btnPreview.click();
}
return false;
|
| ︙ | ︙ | |||
1561 1562 1563 1564 1565 1566 1567 |
windows (desktop/tablet landscape mode), so we default to a
layout based on the apparent "orientation" of the window:
tall vs wide. Can be toggled via settings. */
document.body.classList.add('my-messages-right');
}
const settingsButton = document.querySelector('#chat-button-settings');
const optionsMenu = E1('#chat-config-options');
| | | | | 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 |
windows (desktop/tablet landscape mode), so we default to a
layout based on the apparent "orientation" of the window:
tall vs wide. Can be toggled via settings. */
document.body.classList.add('my-messages-right');
}
const settingsButton = document.querySelector('#chat-button-settings');
const optionsMenu = E1('#chat-config-options');
const eToggleView = function(ev){
ev.preventDefault();
ev.stopPropagation();
Chat.setCurrentView(Chat.e.currentView===Chat.e.viewConfig
? Chat.e.viewMessages : Chat.e.viewConfig);
return false;
};
D.attr(settingsButton, 'role', 'button').addEventListener('click', eToggleView, false);
Chat.e.viewConfig.querySelector('button.action-close').addEventListener('click', eToggleView, false);
/** Internal acrobatics to allow certain settings toggles to access
related toggles. */
const namedOptions = {
activeUsers:{
label: "Show active users list",
hint: "List users who have messages in the currently-loaded chat history.",
|
| ︙ | ︙ | |||
1593 1594 1595 1596 1597 1598 1599 |
optAu.theLegend.addEventListener('click',function(){
D.toggleClass(Chat.e.activeUserListWrapper, 'collapsed');
if(!Chat.e.activeUserListWrapper.classList.contains('collapsed')){
Chat.animate(optAu.theList,'anim-flip-v');
}
}, false);
}/*namedOptions.activeUsers additional setup*/
| > | | | 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 |
optAu.theLegend.addEventListener('click',function(){
D.toggleClass(Chat.e.activeUserListWrapper, 'collapsed');
if(!Chat.e.activeUserListWrapper.classList.contains('collapsed')){
Chat.animate(optAu.theList,'anim-flip-v');
}
}, false);
}/*namedOptions.activeUsers additional setup*/
/* Settings menu entries... they are presented in the order listed
here, so the most frequently-needed ones "should" (arguably) be
closer to the start of this list. */
/**
Settings ops structure:
label: string for the UI
boolValue: string (name of Chat.settings setting) or a function
which returns true or false. If it is a string, it gets
|
| ︙ | ︙ | |||
1659 1660 1661 1662 1663 1664 1665 |
"then Ctrl-Enter toggles this setting."
].join(''),
boolValue: 'edit-ctrl-send'
},{
label: "Compact mode",
hint: [
"Toggle between a space-saving or more spacious writing area. ",
| | > | | 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 |
"then Ctrl-Enter toggles this setting."
].join(''),
boolValue: 'edit-ctrl-send'
},{
label: "Compact mode",
hint: [
"Toggle between a space-saving or more spacious writing area. ",
"When the input field has focus and is empty ",
"then Shift-Enter may (depending on the current view) toggle this setting."
].join(''),
boolValue: 'edit-compact-mode'
},{
label: "Use 'contenteditable' editing mode",
boolValue: 'edit-widget-x',
hint: [
"When enabled, chat input uses a so-called 'contenteditable' ",
"field. Though generally more comfortable and modern than ",
|
| ︙ | ︙ | |||
1684 1685 1686 1687 1688 1689 1690 |
boolValue: 'edit-shift-enter-preview'
}]
},{
label: "Appearance Options...",
children:[{
label: "Left-align my posts",
hint: "Default alignment of your own messages is selected "
| | | 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 |
boolValue: 'edit-shift-enter-preview'
}]
},{
label: "Appearance Options...",
children:[{
label: "Left-align my posts",
hint: "Default alignment of your own messages is selected "
+ "based on the window width/height ratio.",
boolValue: ()=>!document.body.classList.contains('my-messages-right'),
callback: function f(){
document.body.classList[
this.checkbox.checked ? 'remove' : 'add'
]('my-messages-right');
}
},{
|
| ︙ | ︙ | |||
1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 |
select: selectSound,
callback: function(ev){
const v = ev.target.value;
Chat.setNewMessageSound(v);
F.toast.message("Audio notifications "+(v ? "enabled" : "disabled")+".");
if(v) setTimeout(()=>Chat.playNewMessageSound(), 0);
}
},{
label: "Play notification for your own messages",
hint: "When enabled, the audio notification will be played for all messages, "+
"including your own. When disabled only messages from other users "+
"will trigger a notification.",
boolValue: 'alert-own-messages'
}]
| > > > > | 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 |
select: selectSound,
callback: function(ev){
const v = ev.target.value;
Chat.setNewMessageSound(v);
F.toast.message("Audio notifications "+(v ? "enabled" : "disabled")+".");
if(v) setTimeout(()=>Chat.playNewMessageSound(), 0);
}
},{
label: "Notify only once when away",
hint: "Notify only for the first message received after chat is hidden from view.",
boolValue: 'beep-once'
},{
label: "Play notification for your own messages",
hint: "When enabled, the audio notification will be played for all messages, "+
"including your own. When disabled only messages from other users "+
"will trigger a notification.",
boolValue: 'alert-own-messages'
}]
|
| ︙ | ︙ | |||
1825 1826 1827 1828 1829 1830 1831 |
if(op.persistentSetting){
Chat.settings.addListener(
op.persistentSetting,
function(setting){
if(op.checkbox) op.checkbox.checked = !!setting.value;
else if(op.select) op.select.value = setting.value;
if(op.callback) op.callback(setting);
| | | 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 |
if(op.persistentSetting){
Chat.settings.addListener(
op.persistentSetting,
function(setting){
if(op.checkbox) op.checkbox.checked = !!setting.value;
else if(op.select) op.select.value = setting.value;
if(op.callback) op.callback(setting);
}
);
if(op.checkbox){
op.checkbox.addEventListener(
'change', function(){
Chat.settings.set(op.persistentSetting, op.checkbox.checked)
}, false);
}
|
| ︙ | ︙ | |||
1901 1902 1903 1904 1905 1906 1907 |
}
Chat.e.inputElementWrapper.classList[
s.value ? 'add' : 'remove'
]('compact');
Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus();
});
Chat.settings.addListener('edit-ctrl-send',function(s){
| | | 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 |
}
Chat.e.inputElementWrapper.classList[
s.value ? 'add' : 'remove'
]('compact');
Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus();
});
Chat.settings.addListener('edit-ctrl-send',function(s){
const label = (s.value ? "Ctrl-" : "")+"Enter submits message";
Chat.e.inputFields.forEach((e)=>{
const v = e.dataset.placeholder0 + " " +label;
if(e.isContentEditable) e.dataset.placeholder = v;
else D.attr(e,'placeholder',v);
});
Chat.e.btnSubmit.title = label;
});
|
| ︙ | ︙ | |||
1923 1924 1925 1926 1927 1928 1929 |
if(Chat===v) v = Chat.settings.defaults[k];
if(valueKludges.hasOwnProperty(v)) v = valueKludges[v];
Chat.settings.set(k,v)
/* fires event listeners so that the Config area checkboxes
get in sync */;
});
})();
| | | > | > > | 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 |
if(Chat===v) v = Chat.settings.defaults[k];
if(valueKludges.hasOwnProperty(v)) v = valueKludges[v];
Chat.settings.set(k,v)
/* fires event listeners so that the Config area checkboxes
get in sync */;
});
})();
(function(){/*set up message preview*/
const btnPreview = Chat.e.btnPreview;
Chat.setPreviewText = function(t){
this.setCurrentView(this.e.viewPreview);
this.e.previewContent.innerHTML = t;
this.e.viewPreview.querySelectorAll('a').forEach(addAnchorTargetBlank);
this.inputFocus();
};
Chat.e.viewPreview.querySelector('button.action-close').
addEventListener('click', ()=>Chat.setCurrentView(Chat.e.viewMessages), false);
let previewPending = false;
const elemsToEnable = [btnPreview, Chat.e.btnSubmit, Chat.e.inputFields];
const submit = function(ev){
ev.preventDefault();
ev.stopPropagation();
if(previewPending) return false;
const txt = Chat.inputValue();
if(!txt){
Chat.setPreviewText('');
previewPending = false;
return false;
}
const fd = new FormData();
fd.append('content', txt);
fd.append('filename','chat.md'
/*filename needed for mimetype determination*/);
fd.append('render_mode',F.page.previewModes.wiki);
F.fetch('ajax/preview-text',{
payload: fd,
onload: function(html){
Chat.setPreviewText(html);
F.pikchr.addSrcView(Chat.e.viewPreview.querySelectorAll('svg.pikchr'));
},
onerror: function(e){
F.fetch.onerror(e);
Chat.setPreviewText("ERROR: "+(
e.message || 'Unknown error fetching preview!'
));
},
beforesend: function(){
|
| ︙ | ︙ | |||
1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 |
D.enable(elemsToEnable);
}
});
return false;
};
btnPreview.addEventListener('click', submit, false);
})()/*message preview setup*/;
/** Callback for poll() to inject new content into the page. jx ==
the response from /chat-poll. If atEnd is true, the message is
appended to the end of the chat list (for loading older
messages), else the beginning (the default). */
const newcontent = function f(jx,atEnd){
if(!f.processPost){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 |
D.enable(elemsToEnable);
}
});
return false;
};
btnPreview.addEventListener('click', submit, false);
})()/*message preview setup*/;
(function(){/*Set up #chat-search and related bits */
const btn = document.querySelector('#chat-button-search');
D.attr(btn, 'role', 'button').addEventListener('click', function(ev){
ev.preventDefault();
ev.stopPropagation();
const msg = Chat.inputValue();
if( Chat.e.currentView===Chat.e.viewSearch ){
if( msg ) Chat.submitSearch();
else Chat.setCurrentView(Chat.e.viewMessages);
}else{
Chat.setCurrentView(Chat.e.viewSearch);
if( msg ) Chat.submitSearch();
}
return false;
}, false);
Chat.e.viewSearch.querySelector('button.action-clear').addEventListener('click', function(ev){
ev.preventDefault();
ev.stopPropagation();
Chat.clearSearch(true);
Chat.setCurrentView(Chat.e.viewMessages);
return false;
}, false);
Chat.e.viewSearch.querySelector('button.action-close').addEventListener('click', function(ev){
ev.preventDefault();
ev.stopPropagation();
Chat.setCurrentView(Chat.e.viewMessages);
return false;
}, false);
})()/*search view setup*/;
/** Callback for poll() to inject new content into the page. jx ==
the response from /chat-poll. If atEnd is true, the message is
appended to the end of the chat list (for loading older
messages), else the beginning (the default). */
const newcontent = function f(jx,atEnd){
if(!f.processPost){
|
| ︙ | ︙ | |||
2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 |
btn = D.button("All previous messages");
D.append(wrapper, btn);
btn.addEventListener('click',()=>loadOldMessages(-1));
D.append(Chat.e.viewMessages, toolbar);
toolbar.disabled = true /*will be enabled when msg load finishes */;
})()/*end history loading widget setup*/;
const afterFetch = function f(){
if(true===f.isFirstCall){
f.isFirstCall = false;
Chat.ajaxEnd();
Chat.e.viewMessages.classList.remove('loading');
setTimeout(function(){
Chat.scrollMessagesTo(1);
}, 250);
}
if(Chat._gotServerError && Chat.intervalTimer){
clearInterval(Chat.intervalTimer);
Chat.reportErrorAsMessage(
"Shutting down chat poller due to server-side error. ",
"Reload this page to reactivate it.");
delete Chat.intervalTimer;
}
poll.running = false;
};
afterFetch.isFirstCall = true;
const poll = async function f(){
if(f.running) return;
f.running = true;
Chat._isBatchLoading = f.isFirstCall;
if(true===f.isFirstCall){
f.isFirstCall = false;
Chat.ajaxStart();
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 |
btn = D.button("All previous messages");
D.append(wrapper, btn);
btn.addEventListener('click',()=>loadOldMessages(-1));
D.append(Chat.e.viewMessages, toolbar);
toolbar.disabled = true /*will be enabled when msg load finishes */;
})()/*end history loading widget setup*/;
/**
Clears the search result view. If addInstructions is true it adds
text to that view instructing the user to enter their query into
the message-entry widget (noting that that widget has text
implying that it's only for submitting a message, which isn't
exactly true when the search view is active).
Returns the DOM element which wraps all of the chat search
result elements.
*/
Chat.clearSearch = function(addInstructions=false){
const e = D.clearElement( this.e.searchContent );
if(addInstructions){
D.append(e, "Enter search terms in the message field. "+
"Use #NNNNN to search for the message with ID NNNNN.");
}
return e;
};
Chat.clearSearch(true);
/**
Submits a history search using the main input field's current
text. It is assumed that Chat.e.viewSearch===Chat.e.currentView.
*/
Chat.submitSearch = function(){
const term = this.inputValue(true);
const eMsgTgt = this.clearSearch(true);
if( !term ) return;
D.append( eMsgTgt, "Searching for ",term," ...");
const fd = new FormData();
fd.set('q', term);
F.fetch(
"chat-query", {
payload: fd,
responseType: 'json',
onerror:function(err){
Chat.setCurrentView(Chat.e.viewMessages);
Chat.reportErrorAsMessage(err);
},
onload:function(jx){
let previd = 0;
D.clearElement(eMsgTgt);
jx.msgs.forEach((m)=>{
m.isSearchResult = true;
const mw = new Chat.MessageWidget(m);
const spacer = new Chat.SearchCtxLoader({
first: jx.first,
last: jx.last,
previd: previd,
nextid: m.msgid
});
if( spacer.e ) D.append( eMsgTgt, spacer.e.body );
D.append( eMsgTgt, mw.e.body );
previd = m.msgid;
});
if( jx.msgs.length ){
const spacer = new Chat.SearchCtxLoader({
first: jx.first,
last: jx.last,
previd: previd,
nextid: 0
});
if( spacer.e ) D.append( eMsgTgt, spacer.e.body );
}else{
D.append( D.clearElement(eMsgTgt),
'No search results found for: ',
term );
}
}
}
);
}/*Chat.submitSearch()*/;
const afterFetch = function f(){
if(true===f.isFirstCall){
f.isFirstCall = false;
Chat.ajaxEnd();
Chat.e.viewMessages.classList.remove('loading');
setTimeout(function(){
Chat.scrollMessagesTo(1);
}, 250);
}
if(Chat._gotServerError && Chat.intervalTimer){
clearInterval(Chat.intervalTimer);
Chat.reportErrorAsMessage(
"Shutting down chat poller due to server-side error. ",
"Reload this page to reactivate it.");
delete Chat.intervalTimer;
}
poll.running = false;
};
afterFetch.isFirstCall = true;
/**
FIXME: when polling fails because the remote server is
reachable but it's not accepting HTTP requests, we should back
off on polling for a while. e.g. if the remote web server process
is killed, the poll fails quickly and immediately retries,
hammering the remote server until the httpd is back up. That
happens often during development of this application.
XHR does not offer a direct way of distinguishing between
HTTP/connection errors, but we can hypothetically use the
xhrRequest.status value to do so, with status==0 being a
connection error. We do not currently have a clean way of passing
that info back to the fossil.fetch() client, so we'll need to
hammer on that API a bit to get this working.
*/
const poll = async function f(){
if(f.running) return;
f.running = true;
Chat._isBatchLoading = f.isFirstCall;
if(true===f.isFirstCall){
f.isFirstCall = false;
Chat.ajaxStart();
|
| ︙ | ︙ | |||
2169 2170 2171 2172 2173 2174 2175 |
};
poll.isFirstCall = true;
Chat._gotServerError = poll.running = false;
if( window.fossil.config.chat.fromcli ){
Chat.chatOnlyMode(true);
}
Chat.intervalTimer = setInterval(poll, 1000);
| < < < < < < | 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 |
};
poll.isFirstCall = true;
Chat._gotServerError = poll.running = false;
if( window.fossil.config.chat.fromcli ){
Chat.chatOnlyMode(true);
}
Chat.intervalTimer = setInterval(poll, 1000);
delete ForceResizeKludge.$disabled;
ForceResizeKludge();
Chat.animate.$disabled = false;
setTimeout( ()=>Chat.inputFocus(), 0 );
F.page.chat = Chat/* enables testing the APIs via the dev tools */;
});
|
Changes to src/fossil.page.pikchrshow.js.
| ︙ | ︙ | |||
314 315 316 317 318 319 320 |
\u00a0 to , so...*/.split(' ').join('\u00a0'));
if(needsPreview) P.preview();
else{
/*If it's from the server, it's already rendered, but this
gets all labels/headers in sync.*/
P.renderPreview();
}
| | | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
\u00a0 to , so...*/.split(' ').join('\u00a0'));
if(needsPreview) P.preview();
else{
/*If it's from the server, it's already rendered, but this
gets all labels/headers in sync.*/
P.renderPreview();
}
}
}/*F.onPageLoad()*/);
/**
Updates the preview view based on the current preview mode and
error state.
*/
P.renderPreview = function f(){
|
| ︙ | ︙ |
Changes to src/fossil.page.pikchrshowasm.js.
| ︙ | ︙ | |||
390 391 392 393 394 395 396 |
const val = ev.target.value;
if(!val) return;
setCurrentText(val);
}, false);
}/*Examples*/
/**
| | | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
const val = ev.target.value;
if(!val) return;
setCurrentText(val);
}, false);
}/*Examples*/
/**
TODO? Handle load/import of an external pikchr file.
*/
if(0) E('#load-pikchr').addEventListener('change',function(){
const f = this.files[0];
const r = new FileReader();
const status = {loaded: 0, total: 0};
this.setAttribute('disabled','disabled');
const that = this;
|
| ︙ | ︙ | |||
477 478 479 480 481 482 483 |
that height here. Larger than ~95% is too big for
Firefox on Android, causing the input area to move
off-screen. */
const appViews = EAll('.app-view');
const elemsToCount = [
/* Elements which we need to always count in the
visible body size. */
| | | | | 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
that height here. Larger than ~95% is too big for
Firefox on Android, causing the input area to move
off-screen. */
const appViews = EAll('.app-view');
const elemsToCount = [
/* Elements which we need to always count in the
visible body size. */
E('body > header'),
E('body > nav.mainmenu'),
E('body > footer')
];
const resized = function f(){
if(f.$disabled) return;
const wh = window.innerHeight;
var ht;
var extra = 0;
elemsToCount.forEach((e)=>e ? extra += F.dom.effectiveHeight(e) : false);
|
| ︙ | ︙ |
Changes to src/fossil.page.wikiedit.js.
| ︙ | ︙ | |||
1652 1653 1654 1655 1656 1657 1658 |
/**
If a stashed version of the given winfo object exists (same
filename/checkin values), return it, else return undefined.
*/
P.getStashedWinfo = function(winfo){
return $stash.getWinfo(winfo);
};
| < | 1652 1653 1654 1655 1656 1657 1658 1659 |
/**
If a stashed version of the given winfo object exists (same
filename/checkin values), return it, else return undefined.
*/
P.getStashedWinfo = function(winfo){
return $stash.getWinfo(winfo);
};
})(window.fossil);
|
Changes to src/glob.c.
| ︙ | ︙ | |||
136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
}
if( z[i]==0 ) break;
z[i] = 0;
z += i+1;
}
return p;
}
/*
** Return true (non-zero) if zString matches any of the patterns in
** the Glob. The value returned is actually a 1-based index of the pattern
** that matched. Return 0 if none of the patterns match zString.
**
** A NULL glob matches nothing.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
}
if( z[i]==0 ) break;
z[i] = 0;
z += i+1;
}
return p;
}
/*
** Return TRUE if zString matches any of the GLOB patterns in the
** string zPatternList.
**
** This is a like calling glob_create(), glob_match(), and glob_free()
** in sequence, without the overhead of creating the reusable Glob object.
** Use this for one-time matches against a comma-separated GLOB list.
*/
int glob_multi_match(const char *zPatternList, const char *zString){
int i; /* Loop counters */
int n = 0; /* Pattern counter */
const char *z; /* Current GLOB pattern */
char delimiter; /* '\'' or '\"' or 0 */
int rc; /* Result of comparison */
char zPat[100]; /* Copy of just the current pattern */
if( zPatternList==0 ) return 0;
z = zPatternList;
while( z[0] ){
while( fossil_isspace(z[0]) || z[0]==',' ){
z++; /* Skip leading commas, spaces, and newlines */
}
if( z[0]==0 ) break;
if( z[0]=='\'' || z[0]=='"' ){
delimiter = z[0];
z++;
}else{
delimiter = ',';
}
/* Find the next delimiter (or the end of the string). */
for(i=0; z[i] && z[i]!=delimiter &&
!(delimiter==',' && fossil_isspace(z[i])); i++){
/* keep looking for the end of the glob pattern */
}
n++;
if( i>sizeof(zPat)-1 ){
char *zMPat = fossil_strndup(z, i);
rc = sqlite3_strglob(zMPat, zString);
fossil_free(zMPat);
}else{
memcpy(zPat, z, i);
zPat[i] = 0;
rc = sqlite3_strglob(zPat, zString);
}
if( rc==0 ) return n;
if( z[i]==0 ) break;
z += i+1;
}
return 0;
}
/*
** Return true (non-zero) if zString matches any of the patterns in
** the Glob. The value returned is actually a 1-based index of the pattern
** that matched. Return 0 if none of the patterns match zString.
**
** A NULL glob matches nothing.
|
| ︙ | ︙ | |||
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 |
** PATTERN is a comma- and whitespace-separated list of optionally
** quoted glob patterns. Show which of the STRINGs that follow match
** the PATTERN.
**
** If PATTERN begins with "@" the rest of the pattern is understood
** to be a setting name (such as binary-glob, crln-glob, or encoding-glob)
** and the value of that setting is used as the actually glob pattern.
*/
void glob_test_cmd(void){
Glob *pGlob;
int i;
char *zPattern;
if( g.argc<4 ) usage("PATTERN STRING ...");
zPattern = g.argv[2];
if( zPattern[0]=='@' ){
db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
zPattern = db_get(zPattern+1, 0);
if( zPattern==0 ) fossil_fatal("no such setting: %s", g.argv[2]+1);
fossil_print("GLOB pattern: %s\n", zPattern);
}
fossil_print("SQL expression: %s\n", glob_expr("x", zPattern));
pGlob = glob_create(zPattern);
for(i=0; i<pGlob->nPattern; i++){
fossil_print("pattern[%d] = [%s]\n", i, pGlob->azPattern[i]);
}
for(i=3; i<g.argc; i++){
| > > > > > | > > | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
** PATTERN is a comma- and whitespace-separated list of optionally
** quoted glob patterns. Show which of the STRINGs that follow match
** the PATTERN.
**
** If PATTERN begins with "@" the rest of the pattern is understood
** to be a setting name (such as binary-glob, crln-glob, or encoding-glob)
** and the value of that setting is used as the actually glob pattern.
**
** The output consists of two numbers and a STRING. The first number
** is the result of glob_match() and the second is the result of
** glob_multi_match().
*/
void glob_test_cmd(void){
Glob *pGlob;
int i;
char *zPattern;
if( g.argc<4 ) usage("PATTERN STRING ...");
zPattern = g.argv[2];
if( zPattern[0]=='@' ){
db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
zPattern = db_get(zPattern+1, 0);
if( zPattern==0 ) fossil_fatal("no such setting: %s", g.argv[2]+1);
fossil_print("GLOB pattern: %s\n", zPattern);
}
fossil_print("SQL expression: %s\n", glob_expr("x", zPattern));
pGlob = glob_create(zPattern);
for(i=0; i<pGlob->nPattern; i++){
fossil_print("pattern[%d] = [%s]\n", i, pGlob->azPattern[i]);
}
for(i=3; i<g.argc; i++){
fossil_print("%d %d %s\n",
glob_match(pGlob, g.argv[i]),
glob_multi_match(zPattern, g.argv[i]),
g.argv[i]);
}
glob_free(pGlob);
}
|
Changes to src/graph.c.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 | #include <assert.h> /* Notes: ** ** The graph is laid out in 1 or more "rails". A "rail" is a vertical ** band in the graph in which one can place nodes or arrows connecting ** nodes. There can be between 1 and GR_MAX_RAIL rails. If the graph | | > > > > > > | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #include <assert.h> /* Notes: ** ** The graph is laid out in 1 or more "rails". A "rail" is a vertical ** band in the graph in which one can place nodes or arrows connecting ** nodes. There can be between 1 and GR_MAX_RAIL rails. If the graph ** is too complex to be displayed in GR_MAX_RAIL rails, it is omitted. ** ** A "riser" is the thick line that comes out of the top of a node and ** goes up to the next node on the branch, or to the top of the screen. ** A "descender" is a thick line that comes out of the bottom of a node ** and proceeds down to the bottom of the page. ** ** A "merge riser" is a thin line going up out of a node to indicate a ** merge or cherrypick. (Cherrypicks are drawn with thin dashed lines. ** Merges are drawn with thin solid lines.) A "merge riser" might go ** stright up out of the top of a leaf node, but for non-leaves, they ** go horizontally to their assigned rail first, then up. ** ** Invoke graph_init() to create a new GraphContext object. Then ** call graph_add_row() to add nodes, one by one, to the graph. ** Nodes must be added in display order, from top to bottom. ** Then invoke graph_render() to run the layout algorithm. The ** layout algorithm computes which rail each nodes sit on, and ** the rails used for merge arrows. */ #if INTERFACE /* ** The type of integer identifiers for rows of the graph. |
| ︙ | ︙ | |||
309 310 311 312 313 314 315 |
dist = i - iNearto;
if( dist<0 ) dist = -dist;
if( dist<iBestDist ){
iBestDist = dist;
iBest = i;
}
}
| | | 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
dist = i - iNearto;
if( dist<0 ) dist = -dist;
if( dist<iBestDist ){
iBestDist = dist;
iBest = i;
}
}
/* If no match, consider all possible rails */
if( iBestDist>1000 ){
for(i=0; i<=p->mxRail+1; i++){
int dist;
if( inUseMask & BIT(i) ) continue;
if( iNearto<=0 ){
iBest = i;
|
| ︙ | ︙ | |||
537 538 539 540 541 542 543 |
** the aParent[] array.
*/
if( (tmFlags & (TIMELINE_DISJOINT|TIMELINE_XMERGE))!=0 ){
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
for(i=1; i<pRow->nParent; i++){
GraphRow *pParent = hashFind(p, pRow->aParent[i]);
if( pParent==0 ){
| | | | 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 |
** the aParent[] array.
*/
if( (tmFlags & (TIMELINE_DISJOINT|TIMELINE_XMERGE))!=0 ){
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
for(i=1; i<pRow->nParent; i++){
GraphRow *pParent = hashFind(p, pRow->aParent[i]);
if( pParent==0 ){
memmove(pRow->aParent+i, pRow->aParent+i+1,
sizeof(pRow->aParent[0])*(pRow->nParent-i-1));
pRow->nParent--;
if( i<pRow->nNonCherrypick ){
pRow->nNonCherrypick--;
}else{
pRow->nCherrypick--;
}
i--;
}
}
}
}
/* Put the deepest (earliest) merge parent first in the list.
** An off-screen merge parent is considered deepest.
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext ){
if( pRow->nParent<=1 ) continue;
for(i=1; i<pRow->nParent; i++){
GraphRow *pParent = hashFind(p, pRow->aParent[i]);
|
| ︙ | ︙ | |||
928 929 930 931 932 933 934 |
}
/* Find the "root" of the branch. The root is a different branch
** from which the pRow branch emerges. There might not be a root
** if the pRow branch started off the bottom of the screen.
*/
for(pRoot=pBottom->pNext; pRoot; pRoot=pRoot->pNext){
| | | | 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 |
}
/* Find the "root" of the branch. The root is a different branch
** from which the pRow branch emerges. There might not be a root
** if the pRow branch started off the bottom of the screen.
*/
for(pRoot=pBottom->pNext; pRoot; pRoot=pRoot->pNext){
if( pRoot->aiRiser[iFrom]==pBottom->idx ) break;
}
if( pRoot && pRoot->iRail==iTo ){
/* The parent branch from which this branch emerges is on the
** same rail as pRow. Do not shift as that would stack a child
** branch directly above its parent. */
continue;
}
/* All clear. Make the translation
*/
for(pLoop=pRow; pLoop && pLoop->idx<=pBottom->idx; pLoop=pLoop->pNext){
if( pLoop->iRail==iFrom ){
pLoop->iRail = iTo;
pLoop->aiRiser[iTo] = pLoop->aiRiser[iFrom];
pLoop->aiRiser[iFrom] = -1;
}
}
|
| ︙ | ︙ | |||
971 972 973 974 975 976 977 |
aMap = p->aiRailMap;
for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
if( nTimewarp==0 ){
/* Priority bits:
**
** 0x04 The preferred branch
**
| | > > > > > > > > < < < < < | 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 |
aMap = p->aiRailMap;
for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
if( nTimewarp==0 ){
/* Priority bits:
**
** 0x04 The preferred branch
**
** 0x02 A merge rail - a rail that contains merge lines into
** the preferred branch. Only applies if a preferred branch
** is defined. This improves the display of r=BRANCH
** options to /timeline.
**
** 0x01 A rail that merges with the preferred branch
*/
u8 aPriority[GR_MAX_RAIL];
memset(aPriority, 0, p->mxRail+1);
if( zLeftBranch ){
char *zLeft = persistBranchName(p, zLeftBranch);
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->zBranch==zLeft ){
aPriority[pRow->iRail] |= 4;
for(i=0; i<=p->mxRail; i++){
if( pRow->mergeIn[i] ) aPriority[i] |= 1;
}
if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
}
}
for(i=0; i<=p->mxRail; i++){
if( p->mergeRail & BIT(i) ){
aPriority[i] |= 2;
}
}
}else{
j = 1;
aPriority[0] = 4;
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->iRail==0 ){
for(i=0; i<=p->mxRail; i++){
if( pRow->mergeIn[i] ) aPriority[i] |= 1;
}
if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
}
}
}
#if 0
fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail);
fprintf(stderr,"Priority:");
for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]);
fprintf(stderr,"\n");
#endif
|
| ︙ | ︙ |
Changes to src/hbmenu.js.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | ** ** This was original the "js.txt" file for the default skin. It was subsequently ** moved into src/hbmenu.js so that it could be more easily reused by other skins ** using the "builtin_request_js" TH1 command. ** ** Operation: ** | | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | ** ** This was original the "js.txt" file for the default skin. It was subsequently ** moved into src/hbmenu.js so that it could be more easily reused by other skins ** using the "builtin_request_js" TH1 command. ** ** Operation: ** ** This script expects the HTML to contain two elements: ** ** <a id="hbbtn"> <--- The hamburger menu button ** <nav id="hbdrop"> <--- Container for the hamburger menu ** ** Bindings are made on hbbtn so that when it is clicked, the following ** happens: ** ** 1. An XHR is made to /sitemap?popup to fetch the HTML for the ** popup menu. ** |
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | */ #include "config.h" #include "http.h" #include <assert.h> #ifdef _WIN32 #include <io.h> | < < < < < < > > > > > | 19 20 21 22 23 24 25 26 27 28 29 30 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 | */ #include "config.h" #include "http.h" #include <assert.h> #ifdef _WIN32 #include <io.h> #endif #if INTERFACE /* ** Bits of the mHttpFlags parameter to http_exchange() */ #define HTTP_USE_LOGIN 0x00001 /* Add a login card to the sync message */ #define HTTP_GENERIC 0x00002 /* Generic HTTP request */ #define HTTP_VERBOSE 0x00004 /* HTTP status messages */ #define HTTP_QUIET 0x00008 /* No surplus output */ #define HTTP_NOCOMPRESS 0x00010 /* Omit payload compression */ #endif /* Maximum number of HTTP Authorization attempts */ #define MAX_HTTP_AUTH 2 /* Keep track of HTTP Basic Authorization failures */ static int fSeenHttpAuth = 0; /* The N value for most recent http-request-N.txt and http-reply-N.txt ** trace files. */ static int traceCnt = 0; /* ** Construct the "login" card with the client credentials. ** ** login LOGIN NONCE SIGNATURE ** ** The LOGIN is the user id of the client. NONCE is the sha1 checksum |
| ︙ | ︙ | |||
96 97 98 99 100 101 102 | ** sha1_shared_secret()), not the original password. So convert the ** password to its SHA1 encoding if it isn't already a SHA1 hash. ** ** We assume that a hexadecimal string of exactly 40 characters is a ** SHA1 hash, not an original password. If a user has a password which ** just happens to be a 40-character hex string, then this routine won't ** be able to distinguish it from a hash, the translation will not be | | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
** sha1_shared_secret()), not the original password. So convert the
** password to its SHA1 encoding if it isn't already a SHA1 hash.
**
** We assume that a hexadecimal string of exactly 40 characters is a
** SHA1 hash, not an original password. If a user has a password which
** just happens to be a 40-character hex string, then this routine won't
** be able to distinguish it from a hash, the translation will not be
** performed, and the sync won't work.
*/
if( zPw && zPw[0] && (strlen(zPw)!=40 || !validate16(zPw,40)) ){
const char *zProjectCode = 0;
if( g.url.flags & URL_USE_PARENT ){
zProjectCode = db_get("parent-project-code", 0);
}else{
zProjectCode = db_get("project-code", 0);
|
| ︙ | ︙ | |||
199 200 201 202 203 204 205 |
*/
char *prompt_for_httpauth_creds(void){
Blob x;
char *zUser;
char *zPw;
char *zPrompt;
char *zHttpAuth = 0;
| | | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
*/
char *prompt_for_httpauth_creds(void){
Blob x;
char *zUser;
char *zPw;
char *zPrompt;
char *zHttpAuth = 0;
if( !fossil_isatty(fossil_fileno(stdin)) ) return 0;
zPrompt = mprintf("\n%s authorization required by\n%s\n",
g.url.isHttps==1 ? "Encrypted HTTPS" : "Unencrypted HTTP", g.url.canonical);
fossil_print("%s", zPrompt);
free(zPrompt);
if ( g.url.user && g.url.passwd && use_fossil_creds_for_httpauth_prompt() ){
zHttpAuth = mprintf("%s:%s", g.url.user, g.url.passwd);
}else{
|
| ︙ | ︙ | |||
256 257 258 259 260 261 262 |
blob_write_to_file(pSend, zUplink);
if( g.fHttpTrace ){
fossil_print("RUN %s\n", zCmd);
}
rc = fossil_system(zCmd);
if( rc ){
fossil_warning("Transport command failed: %s\n", zCmd);
| | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 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 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
blob_write_to_file(pSend, zUplink);
if( g.fHttpTrace ){
fossil_print("RUN %s\n", zCmd);
}
rc = fossil_system(zCmd);
if( rc ){
fossil_warning("Transport command failed: %s\n", zCmd);
}
fossil_free(zCmd);
file_delete(zUplink);
if( file_size(zDownlink, ExtFILE)<0 ){
blob_zero(pReply);
}else{
blob_read_from_file(pReply, zDownlink, ExtFILE);
file_delete(zDownlink);
}
return rc;
}
/* If iTruth<0 then guess as to whether or not a PATH= argument is required
** when using ssh to run fossil on a remote machine name zHostname. Return
** true if a PATH= should be provided and 0 if not.
**
** If iTruth is 1 or 0 then that means that the PATH= is or is not required,
** respectively. Record this fact for future reference.
**
** If iTruth is 99 or more, then toggle the value that will be returned
** for future iTruth==(-1) queries.
*/
int ssh_needs_path_argument(const char *zHostname, int iTruth){
int ans = 0; /* Default to "no" */
char *z = mprintf("use-path-for-ssh:%s", zHostname);
if( iTruth<0 ){
if( db_get_boolean(z/*works-like:"x"*/, 0) ) ans = 1;
}else{
if( iTruth>=99 ){
iTruth = !db_get_boolean(z/*works-like:"x"*/, 0);
}
if( iTruth ){
ans = 1;
db_set(z/*works-like:"x"*/, "1", 1);
}else{
db_unset(z/*works-like:"x"*/, 1);
}
}
fossil_free(z);
return ans;
}
/*
** COMMAND: test-ssh-needs-path
**
** Usage: fossil test-ssh-needs-path ?HOSTNAME? ?BOOLEAN?
**
** With one argument, show whether or not the PATH= argument is included
** by default for HOSTNAME. If the second argument is a boolean, then
** change the value.
**
** With no arguments, show all hosts for which ssh-needs-path is true.
*/
void test_ssh_needs_path(void){
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
db_open_config(0,0);
if( g.argc>=3 ){
const char *zHost = g.argv[2];
int a = -1;
int rc;
if( g.argc>=4 ) a = is_truth(g.argv[3]);
rc = ssh_needs_path_argument(zHost, a);
fossil_print("%-20s %s\n", zHost, rc ? "yes" : "no");
}else{
Stmt s;
db_swap_connections();
db_prepare(&s, "SELECT substr(name,18) FROM global_config"
" WHERE name GLOB 'use-path-for-ssh:*'");
while( db_step(&s)==SQLITE_ROW ){
const char *zHost = db_column_text(&s,0);
fossil_print("%-20s yes\n", zHost);
}
db_finalize(&s);
db_swap_connections();
}
}
/* Add an approprate PATH= argument to the SSH command under construction
** in pCmd.
**
** About This Feature
** ==================
**
** On some ssh servers (Macs in particular are guilty of this) the PATH
** variable in the shell that runs the command that is sent to the remote
** host contains a limited number of read-only system directories:
**
** /usr/bin:/bin:/usr/sbin:/sbin
**
** The fossil executable cannot be installed into any of those directories
** because they are locked down, and so the "fossil" command cannot run.
**
** To work around this, the fossil command is prefixed with the PATH=
** argument, inserted by this function, to augment the PATH with additional
** directories in which the fossil executable is often found.
**
** But other ssh servers are confused by this initial PATH= argument.
** Some ssh servers have a list of programs that they are allowed to run
** and will fail if the first argument is not on that list, and PATH=....
** is not on that list.
**
** So that various commands that use ssh can run seamlessly on a variety
** of systems (commands that use ssh include "fossil sync" with an ssh:
** URL and the "fossil patch pull" and "fossil patch push" commands where
** the destination directory starts with HOSTNAME: or USER@HOSTNAME:.)
** the following algorithm is used:
**
** * First try running the fossil without any PATH= argument. If that
** works (and it does on a majority of systems) then we are done.
**
** * If the first attempt fails, then try again after adding the
** PATH= prefix argument. (This function is what adds that
** argument.) If the retry works, then remember that fact using
** the use-path-for-ssh:HOSTNAME setting so that the first step
** is skipped on subsequent uses of the same command.
**
** See the forum thread at
** https://fossil-scm.org/forum/forumpost/4903cb4b691af7ce for more
** background.
**
** See also:
**
** * The ssh_needs_path_argument() function above.
** * The test-ssh-needs-path command that shows the settings
** that cache whether or not a PATH= is needed for a particular
** HOSTNAME.
*/
void ssh_add_path_argument(Blob *pCmd){
blob_append_escaped_arg(pCmd,
"PATH=$HOME/bin:/usr/local/bin:/opt/homebrew/bin:$PATH", 1);
}
/*
** Return the complete text of the last HTTP reply as saved in the
** http-reply-N.txt file. This only works if run using --httptrace.
** Without the --httptrace option, this routine returns a NULL pointer.
** It still might return a NULL pointer if for some reason it cannot
** find and open the last http-reply-N.txt file.
*/
char *http_last_trace_reply(void){
Blob x;
int n;
char *zFilename;
if( g.fHttpTrace==0 ) return 0;
zFilename = mprintf("http-reply-%d.txt", traceCnt);
n = blob_read_from_file(&x, zFilename, ExtFILE);
fossil_free(zFilename);
if( n<=0 ) return 0;
return blob_str(&x);
}
/*
** Sign the content in pSend, compress it, and send it to the server
** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply
** in pRecv. pRecv is assumed to be uninitialized when
** this routine is called - this routine will initialize it.
|
| ︙ | ︙ | |||
290 291 292 293 294 295 296 |
const char *zAltMimetype /* Alternative mimetype if not NULL */
){
Blob login; /* The login card */
Blob payload; /* The complete payload including login card */
Blob hdr; /* The HTTP request header */
int closeConnection; /* True to close the connection when done */
int iLength; /* Expected length of the reply payload */
| < > > > > > > > > > > | 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 |
const char *zAltMimetype /* Alternative mimetype if not NULL */
){
Blob login; /* The login card */
Blob payload; /* The complete payload including login card */
Blob hdr; /* The HTTP request header */
int closeConnection; /* True to close the connection when done */
int iLength; /* Expected length of the reply payload */
int rc = 0; /* Result code */
int iHttpVersion; /* Which version of HTTP protocol server uses */
char *zLine; /* A single line of the reply header */
int i; /* Loop counter */
int isError = 0; /* True if the reply is an error message */
int isCompressed = 1; /* True if the reply is compressed */
if( g.zHttpCmd!=0 ){
/* Handle the --transport-command option for "fossil sync" and similar */
return http_exchange_external(pSend,pReply,mHttpFlags,zAltMimetype);
}
/* Activate the PATH= auxiliary argument to the ssh command if that
** is called for.
*/
if( g.url.isSsh
&& (g.url.flags & URL_SSH_RETRY)==0
&& ssh_needs_path_argument(g.url.hostname, -1)
){
g.url.flags |= URL_SSH_PATH;
}
if( transport_open(&g.url) ){
fossil_warning("%s", transport_errmsg(&g.url));
return 1;
}
/* Construct the login card and prepare the complete payload */
|
| ︙ | ︙ | |||
333 334 335 336 337 338 339 |
/* When tracing, write the transmitted HTTP message both to standard
** output and into a file. The file can then be used to drive the
** server-side like this:
**
** ./fossil test-http <http-request-1.txt
*/
if( g.fHttpTrace ){
| < | 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
/* When tracing, write the transmitted HTTP message both to standard
** output and into a file. The file can then be used to drive the
** server-side like this:
**
** ./fossil test-http <http-request-1.txt
*/
if( g.fHttpTrace ){
char *zOutFile;
FILE *out;
traceCnt++;
zOutFile = mprintf("http-request-%d.txt", traceCnt);
out = fopen(zOutFile, "wb");
if( out ){
fwrite(blob_buffer(&hdr), 1, blob_size(&hdr), out);
|
| ︙ | ︙ | |||
370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
transport_flip(&g.url);
/*
** Read and interpret the server reply
*/
closeConnection = 1;
iLength = -1;
while( (zLine = transport_receive_line(&g.url))!=0 && zLine[0]!=0 ){
if( mHttpFlags & HTTP_VERBOSE ){
fossil_print("Read: [%s]\n", zLine);
}
if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){
if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
if( rc==401 ){
| > | 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 |
transport_flip(&g.url);
/*
** Read and interpret the server reply
*/
closeConnection = 1;
iLength = -1;
iHttpVersion = -1;
while( (zLine = transport_receive_line(&g.url))!=0 && zLine[0]!=0 ){
if( mHttpFlags & HTTP_VERBOSE ){
fossil_print("Read: [%s]\n", zLine);
}
if( fossil_strnicmp(zLine, "http/1.", 7)==0 ){
if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
if( rc==401 ){
|
| ︙ | ︙ | |||
408 409 410 411 412 413 414 415 416 417 418 419 |
if( rc!=200 && rc!=301 && rc!=302 && rc!=307 && rc!=308 ){
int ii;
for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
while( zLine[ii]==' ' ) ii++;
fossil_warning("server says: %s", &zLine[ii]);
goto write_err;
}
closeConnection = 0;
}else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){
for(i=15; fossil_isspace(zLine[i]); i++){}
iLength = atoi(&zLine[i]);
}else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
| > < < | < | | 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 |
if( rc!=200 && rc!=301 && rc!=302 && rc!=307 && rc!=308 ){
int ii;
for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){}
while( zLine[ii]==' ' ) ii++;
fossil_warning("server says: %s", &zLine[ii]);
goto write_err;
}
if( iHttpVersion<0 ) iHttpVersion = 1;
closeConnection = 0;
}else if( fossil_strnicmp(zLine, "content-length:", 15)==0 ){
for(i=15; fossil_isspace(zLine[i]); i++){}
iLength = atoi(&zLine[i]);
}else if( fossil_strnicmp(zLine, "connection:", 11)==0 ){
if( sqlite3_strlike("%close%", &zLine[11], 0)==0 ){
closeConnection = 1;
}else if( sqlite3_strlike("%keep-alive%", &zLine[11], 0)==0 ){
closeConnection = 0;
}
}else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) &&
fossil_strnicmp(zLine, "location:", 9)==0 ){
int i, j;
int wasHttps;
|
| ︙ | ︙ | |||
481 482 483 484 485 486 487 |
if( mHttpFlags & HTTP_NOCOMPRESS ) isCompressed = 0;
}else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){
isError = 1;
}
}
}
}
| > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > | > > > > > > | | > > > | | > | | > > > > > > > | > > > > > > > > > > > | 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 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 |
if( mHttpFlags & HTTP_NOCOMPRESS ) isCompressed = 0;
}else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){
isError = 1;
}
}
}
}
if( iHttpVersion<0 ){
/* We got nothing back from the server. If using the ssh: protocol,
** this might mean we need to add or remove the PATH=... argument
** to the SSH command being sent. If that is the case, retry the
** request after adding or removing the PATH= argument.
*/
if( g.url.isSsh /* This is an SSH: sync */
&& (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
&& (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
){
/* Retry after flipping the SSH_PATH setting */
transport_close(&g.url);
fossil_print(
"First attempt to run fossil on %s using SSH failed.\n"
"Retrying %s the PATH= argument.\n",
g.url.hostname,
(g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
);
g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
if( rc==0 ){
(void)ssh_needs_path_argument(g.url.hostname,
(g.url.flags & URL_SSH_PATH)!=0);
}
return rc;
}else{
/* The problem could not be corrected by retrying. Report the
** the error. */
if( g.url.isSsh && !g.fSshTrace ){
fossil_warning("server did not reply: "
" rerun with --sshtrace for diagnostics");
}else{
fossil_warning("server did not reply");
}
goto write_err;
}
}
if( rc!=200 ){
fossil_warning("\"location:\" missing from %d redirect reply", rc);
goto write_err;
}
/*
** Extract the reply payload that follows the header
*/
blob_zero(pReply);
if( iLength==0 ){
/* No content to read */
}else if( iLength>0 ){
/* Read content of a known length */
int iRecvLen; /* Received length of the reply payload */
blob_resize(pReply, iLength);
iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength);
if( mHttpFlags & HTTP_VERBOSE ){
fossil_print("Reply received: %d of %d bytes\n", iRecvLen, iLength);
}
if( iRecvLen != iLength ){
fossil_warning("response truncated: got %d bytes of %d",
iRecvLen, iLength);
goto write_err;
}
}else if( closeConnection ){
/* Read content until end-of-file */
int iRecvLen; /* Received length of the reply payload */
unsigned int nReq = 1000;
unsigned int nPrior = 0;
do{
nReq *= 2;
blob_resize(pReply, nPrior+nReq);
iRecvLen = transport_receive(&g.url, &pReply->aData[nPrior], (int)nReq);
nPrior += iRecvLen;
pReply->nUsed = nPrior;
}while( iRecvLen==nReq && nReq<0x20000000 );
if( mHttpFlags & HTTP_VERBOSE ){
fossil_print("Reply received: %u bytes (w/o content-length)\n", nPrior);
}
}else{
assert( iLength<0 && !closeConnection );
fossil_warning("\"content-length\" missing from %d keep-alive reply", rc);
}
if( isError ){
char *z;
int i, j;
z = blob_str(pReply);
for(i=j=0; z[i]; i++, j++){
if( z[i]=='<' ){
while( z[i] && z[i]!='>' ) i++;
|
| ︙ | ︙ |
Changes to src/http_ssl.c.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 | } sException; static int sslNoCertVerify = 0; /* Do not verify SSL certs */ /* This is a self-signed cert in the PEM format that can be used when ** no other certs are available. */ | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | } sException; static int sslNoCertVerify = 0; /* Do not verify SSL certs */ /* This is a self-signed cert in the PEM format that can be used when ** no other certs are available. */ static const char sslSelfCert[] = "-----BEGIN CERTIFICATE-----\n" "MIIDMTCCAhkCFGrDmuJkkzWERP/ITBvzwwI2lv0TMA0GCSqGSIb3DQEBCwUAMFQx\n" "CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMw\n" "EQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYDVQQDDAZGb3NzaWwwIBcNMjExMjI3MTEz\n" "MTU2WhgPMjEyMTEyMjcxMTMxNTZaMFQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJO\n" "QzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMwEQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYD\n" "VQQDDAZGb3NzaWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCCbTU2\n" |
| ︙ | ︙ | |||
81 82 83 84 85 86 87 | "G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n" "pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n" "s/TsrXk=\n" "-----END CERTIFICATE-----\n"; /* This is the private-key corresponding to the cert above */ | | | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | "G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n" "pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n" "s/TsrXk=\n" "-----END CERTIFICATE-----\n"; /* This is the private-key corresponding to the cert above */ static const char sslSelfPKey[] = "-----BEGIN PRIVATE KEY-----\n" "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCCbTU26GRQHQqL\n" "q7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqXxZlzmS/C\n" "glZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfefiIYPDk1\n" "GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlurTlv0rjsY\n" "Ofq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12JavhFcd4J\n" "U4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1kKxJxXQh7\n" |
| ︙ | ︙ | |||
204 205 206 207 208 209 210 |
"or the ssl-identity setting.");
return 0; /* no cert available */
}
/*
** Convert an OpenSSL ASN1_TIME to an ISO8601 timestamp.
**
| | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
"or the ssl-identity setting.");
return 0; /* no cert available */
}
/*
** Convert an OpenSSL ASN1_TIME to an ISO8601 timestamp.
**
** Per RFC 5280, ASN1 timestamps in X.509 certificates must
** be in UTC (Zulu timezone) with no fractional seconds.
**
** If showUtc==1, add " UTC" at the end of the returned string. This is
** not ISO8601-compliant, but makes the displayed value more user-friendly.
*/
static const char *ssl_asn1time_to_iso8601(ASN1_TIME *asn1_time,
int showUtc){
|
| ︙ | ︙ | |||
305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
}
}
if( zFile==0 ){
/* fossil_fatal("Cannot find a trust store"); */
}else if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){
fossil_fatal("Cannot load CA root certificates from %s", zFile);
}
/* Load client SSL identity, preferring the filename specified on the
** command line */
if( g.zSSLIdentity!=0 ){
identityFile = g.zSSLIdentity;
}else{
identityFile = db_get("ssl-identity", 0);
| > > > > > > > > > > > > > > > > > | 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 |
}
}
if( zFile==0 ){
/* fossil_fatal("Cannot find a trust store"); */
}else if( SSL_CTX_load_verify_locations(sslCtx, zCaFile, zCaDirectory)==0 ){
fossil_fatal("Cannot load CA root certificates from %s", zFile);
}
/* Enable OpenSSL to use the Windows system ROOT certificate store to search for
** certificates missing in the file and directory trust stores already loaded by
** `SSL_CTX_load_verify_locations()'.
** This feature was introduced with OpenSSL 3.2.0, and may be enabled by default
** for future versions of OpenSSL, and explicit initialization may be redundant.
** NOTE TO HACKERS TWEAKING THEIR OPENSSL CONFIGURATION:
** The following OpenSSL configuration options must not be used for this feature
** to be available: `no-autoalginit', `no-winstore'. The Fossil makefiles do not
** currently set these options when building OpenSSL for Windows. */
#if defined(_WIN32)
#if OPENSSL_VERSION_NUMBER >= 0x030200000
if( SSL_CTX_load_verify_store(sslCtx, "org.openssl.winstore:")==0 ){
fossil_print("NOTICE: Failed to load the Windows root certificates.\n");
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x030200000 */
#endif /* _WIN32 */
/* Load client SSL identity, preferring the filename specified on the
** command line */
if( g.zSSLIdentity!=0 ){
identityFile = g.zSSLIdentity;
}else{
identityFile = db_get("ssl-identity", 0);
|
| ︙ | ︙ | |||
410 411 412 413 414 415 416 |
** Invoke this routine to disable SSL cert verification. After
** this call is made, any SSL cert that the server provides will
** be accepted. Communication will still be encrypted, but the
** client has no way of knowing whether it is talking to the
** real server or a man-in-the-middle imposter.
*/
void ssl_disable_cert_verification(void){
| | | 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 |
** Invoke this routine to disable SSL cert verification. After
** this call is made, any SSL cert that the server provides will
** be accepted. Communication will still be encrypted, but the
** client has no way of knowing whether it is talking to the
** real server or a man-in-the-middle imposter.
*/
void ssl_disable_cert_verification(void){
sslNoCertVerify = 1;
}
/*
** Open an SSL connection as a client that is to connect to the server
** identified by pUrlData.
**
* The identify of the server is determined as follows:
|
| ︙ | ︙ | |||
563 564 565 566 567 568 569 |
X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE);
BIO_printf(mem, "\n notBefore: %s",
ssl_asn1time_to_iso8601(X509_get_notBefore(cert), 1));
BIO_printf(mem, "\n notAfter: %s",
ssl_asn1time_to_iso8601(X509_get_notAfter(cert), 1));
BIO_printf(mem, "\n sha256: %s", zHash);
desclen = BIO_get_mem_data(mem, &desc);
| | | | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 |
X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE);
BIO_printf(mem, "\n notBefore: %s",
ssl_asn1time_to_iso8601(X509_get_notBefore(cert), 1));
BIO_printf(mem, "\n notAfter: %s",
ssl_asn1time_to_iso8601(X509_get_notAfter(cert), 1));
BIO_printf(mem, "\n sha256: %s", zHash);
desclen = BIO_get_mem_data(mem, &desc);
prompt = mprintf("Unable to verify SSL cert from %s\n%.*s\n"
"accept this cert and continue (y/N/fingerprint)? ",
pUrlData->name, desclen, desc);
BIO_free(mem);
prompt_user(prompt, &ans);
free(prompt);
cReply = blob_str(&ans)[0];
if( cReply!='y' && cReply!='Y'
&& fossil_stricmp(blob_str(&ans),zHash)!=0
){
X509_free(cert);
|
| ︙ | ︙ | |||
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 |
" The default locations for the set of root certificates\n"
" used by the \"fossil sync\" and similar commands to verify\n"
" the identity of servers for \"https:\" URLs. These values\n"
" come into play when Fossil is used as a TLS client. These\n"
" values are built into your OpenSSL library.\n\n"
);
}
if( zUsed==0 ) zUsed = "";
fossil_print(" Trust store used: %s\n", zUsed);
if( verbose ){
fossil_print("\n"
" The location that is actually used for the root certificates\n"
" used to verify the identity of servers for \"https:\" URLs.\n"
| > > > > > > > > > > > > > > > > | 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 |
" The default locations for the set of root certificates\n"
" used by the \"fossil sync\" and similar commands to verify\n"
" the identity of servers for \"https:\" URLs. These values\n"
" come into play when Fossil is used as a TLS client. These\n"
" values are built into your OpenSSL library.\n\n"
);
}
#if defined(_WIN32)
#if OPENSSL_VERSION_NUMBER >= 0x030200000
fossil_print(" OpenSSL-winstore: Yes\n");
#else /* OPENSSL_VERSION_NUMBER >= 0x030200000 */
fossil_print(" OpenSSL-winstore: No\n");
#endif /* OPENSSL_VERSION_NUMBER >= 0x030200000 */
if( verbose ){
fossil_print("\n"
" OpenSSL 3.2.0, or newer, use the root certificates managed by\n"
" the Windows operating system. The installed root certificates\n"
" are listed by the command:\n\n"
" certutil -store \"ROOT\"\n\n"
);
}
#endif /* _WIN32 */
if( zUsed==0 ) zUsed = "";
fossil_print(" Trust store used: %s\n", zUsed);
if( verbose ){
fossil_print("\n"
" The location that is actually used for the root certificates\n"
" used to verify the identity of servers for \"https:\" URLs.\n"
|
| ︙ | ︙ | |||
1183 1184 1185 1186 1187 1188 1189 |
/*
** Return the OpenSSL version number being used. Space to hold
** this name is obtained from fossil_malloc() and should be
** freed by the caller.
*/
char *fossil_openssl_version(void){
| | | 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 |
/*
** Return the OpenSSL version number being used. Space to hold
** this name is obtained from fossil_malloc() and should be
** freed by the caller.
*/
char *fossil_openssl_version(void){
#if defined(FOSSIL_ENABLE_SSL)
return mprintf("%s (0x%09x)\n",
SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER);
#else
return mprintf("none");
#endif
}
|
Changes to src/http_transport.c.
| ︙ | ︙ | |||
129 130 131 132 133 134 135 |
if( pUrlData->user && pUrlData->user[0] ){
zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name);
blob_append_escaped_arg(&zCmd, zHost, 0);
fossil_free(zHost);
}else{
blob_append_escaped_arg(&zCmd, pUrlData->name, 0);
}
| > | > > > > > > | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
if( pUrlData->user && pUrlData->user[0] ){
zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name);
blob_append_escaped_arg(&zCmd, zHost, 0);
fossil_free(zHost);
}else{
blob_append_escaped_arg(&zCmd, pUrlData->name, 0);
}
if( (pUrlData->flags & URL_SSH_EXE)!=0
&& !is_safe_fossil_command(pUrlData->fossil)
){
fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
"the server.", pUrlData->fossil);
}
if( (pUrlData->flags & URL_SSH_EXE)==0
&& (pUrlData->flags & URL_SSH_PATH)!=0
){
ssh_add_path_argument(&zCmd);
}
blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
blob_append(&zCmd, " test-http", 10);
if( pUrlData->path && pUrlData->path[0] ){
blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
}else{
fossil_fatal("ssh:// URI does not specify a path to the repository");
|
| ︙ | ︙ | |||
310 311 312 313 314 315 316 |
/*
** Read N bytes of content directly from the wire and write into
** the buffer.
*/
static int transport_fetch(UrlData *pUrlData, char *zBuf, int N){
int got;
| | | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
/*
** Read N bytes of content directly from the wire and write into
** the buffer.
*/
static int transport_fetch(UrlData *pUrlData, char *zBuf, int N){
int got;
if( pUrlData->isSsh ){
int x;
int wanted = N;
got = 0;
while( wanted>0 ){
x = read(sshIn, &zBuf[got], wanted);
if( x<=0 ) break;
got += x;
|
| ︙ | ︙ |
Changes to src/import.c.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 | int nFileAlloc; /* Number of slots in aFile[] */ ImportFile *aFile; /* Information about files in a commit */ ImportFile *pInlineFile; /* File marked "inline" */ int fromLoaded; /* True zFrom content loaded into aFile[] */ int tagCommit; /* True if the commit adds a tag */ } gg; | < < < < < < < < < < < < < < < < < < < < < < | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
int nFileAlloc; /* Number of slots in aFile[] */
ImportFile *aFile; /* Information about files in a commit */
ImportFile *pInlineFile; /* File marked "inline" */
int fromLoaded; /* True zFrom content loaded into aFile[] */
int tagCommit; /* True if the commit adds a tag */
} gg;
/*
** A no-op "xFinish" method
*/
static void finish_noop(void){}
/*
** Deallocate the state information.
|
| ︙ | ︙ | |||
815 816 817 818 819 820 821 |
gg.fromLoaded = 1;
}else
if( strncmp(zLine, "N ", 2)==0 ){
/* No-op */
}else
if( strncmp(zLine, "property branch-nick ", 21)==0 ){
/* Breezy uses this property to store the branch name.
| | | 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 |
gg.fromLoaded = 1;
}else
if( strncmp(zLine, "N ", 2)==0 ){
/* No-op */
}else
if( strncmp(zLine, "property branch-nick ", 21)==0 ){
/* Breezy uses this property to store the branch name.
** It has two values. Integer branch number, then the
** user-readable branch name. */
z = &zLine[21];
next_token(&z);
fossil_free(gg.zBranch);
gg.zBranch = fossil_strdup(next_token(&z));
}else
if( strncmp(zLine, "property rebase-of ", 19)==0 ){
|
| ︙ | ︙ | |||
1246 1247 1248 1249 1250 1251 1252 |
lenOld = blob_size(pOut);
blob_resize(pOut, lenOut+lenOld);
zOut = blob_buffer(pOut)+lenOld;
while( zDiff<zInst+lenInst ){
u64 lenCpy = (*zDiff)&0x3f;
const char *zCpy;
switch( (*zDiff)&0xC0 ){
| > > > > > > > | > | 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 |
lenOld = blob_size(pOut);
blob_resize(pOut, lenOut+lenOld);
zOut = blob_buffer(pOut)+lenOld;
while( zDiff<zInst+lenInst ){
u64 lenCpy = (*zDiff)&0x3f;
const char *zCpy;
switch( (*zDiff)&0xC0 ){
case 0x00:
if( 0==blob_size(pSrc) ){
/* https://fossil-scm.org/forum/forumpost/15d4b242bda2a108 */
fossil_fatal("Don't know how to handle NULL input. "
"Tip: do not use the --incremental flag "
"in the svn dump command.");
}
zCpy = blob_buffer(pSrc)+offSrc;
break;
case 0x40: zCpy = blob_buffer(pOut); break;
case 0x80: zCpy = zData; break;
default: fossil_fatal("Invalid svndiff0 instruction");
}
zDiff++;
if( lenCpy==0 ){
lenCpy = svn_get_varint(&zDiff);
|
| ︙ | ︙ | |||
1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 |
if( rid!=0 ){
content_get(rid, &deltaSrc);
}else{
blob_zero(&deltaSrc);
}
svn_apply_svndiff(&rec.content, &deltaSrc, &target);
rid = svn_handle_symlinks(zPerm, &target);
}else if( rec.contentFlag ){
rid = svn_handle_symlinks(zPerm, &rec.content);
}else if( zSrcPath ){
if ( zPerm==0 ){
zPerm = db_text(0, "SELECT tperm FROM xfiles"
" WHERE tpath=%Q AND tbranch=%d"
"", zSrcPath, branchId);
| > > | 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 |
if( rid!=0 ){
content_get(rid, &deltaSrc);
}else{
blob_zero(&deltaSrc);
}
svn_apply_svndiff(&rec.content, &deltaSrc, &target);
rid = svn_handle_symlinks(zPerm, &target);
blob_reset(&deltaSrc);
blob_reset(&target);
}else if( rec.contentFlag ){
rid = svn_handle_symlinks(zPerm, &rec.content);
}else if( zSrcPath ){
if ( zPerm==0 ){
zPerm = db_text(0, "SELECT tperm FROM xfiles"
" WHERE tpath=%Q AND tbranch=%d"
"", zSrcPath, branchId);
|
| ︙ | ︙ | |||
1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 |
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=("
" SELECT tuuid FROM xfiles"
" WHERE tpath=%Q AND tbranch=%d"
")", zFile, branchId);
content_get(rid, &deltaSrc);
svn_apply_svndiff(&rec.content, &deltaSrc, &target);
rid = svn_handle_symlinks(zPerm, &target);
}else{
rid = svn_handle_symlinks(zPerm, &rec.content);
}
db_bind_text(&addFile, ":path", zFile);
db_bind_int(&addFile, ":branch", branchId);
db_bind_int(&addFile, ":rid", rid);
db_bind_text(&addFile, ":perm", zPerm);
| > > | 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 |
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=("
" SELECT tuuid FROM xfiles"
" WHERE tpath=%Q AND tbranch=%d"
")", zFile, branchId);
content_get(rid, &deltaSrc);
svn_apply_svndiff(&rec.content, &deltaSrc, &target);
rid = svn_handle_symlinks(zPerm, &target);
blob_reset(&deltaSrc);
blob_reset(&target);
}else{
rid = svn_handle_symlinks(zPerm, &rec.content);
}
db_bind_text(&addFile, ":path", zFile);
db_bind_int(&addFile, ":branch", branchId);
db_bind_int(&addFile, ":rid", rid);
db_bind_text(&addFile, ":perm", zPerm);
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
889 890 891 892 893 894 895 |
}
render_backlink_graph(zUuid,
"<div class=\"section accordion\">References</div>\n");
@ <div class="section accordion">Context</div><div class="accordion_panel">
render_checkin_context(rid, 0, 0, 0);
@ </div><div class="section accordion">Changes</div>
@ <div class="accordion_panel">
| | > | 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 |
}
render_backlink_graph(zUuid,
"<div class=\"section accordion\">References</div>\n");
@ <div class="section accordion">Context</div><div class="accordion_panel">
render_checkin_context(rid, 0, 0, 0);
@ </div><div class="section accordion">Changes</div>
@ <div class="accordion_panel">
@ <div class="sectionmenu info-changes-menu">
/* ^^^ .info-changes-menu is used by diff scroll sync */
pCfg = construct_diff_flags(diffType, &DCfg);
DCfg.pRe = pRe;
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=0 ){
@ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
@ Hide Diffs</a>
}
|
| ︙ | ︙ | |||
1222 1223 1224 1225 1226 1227 1228 |
}
}
pTo = vdiff_parse_manifest("to", &ridTo);
if( pTo==0 ) return;
pFrom = vdiff_parse_manifest("from", &ridFrom);
if( pFrom==0 ) return;
zGlob = P("glob");
| > > > > > > > | | | 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 |
}
}
pTo = vdiff_parse_manifest("to", &ridTo);
if( pTo==0 ) return;
pFrom = vdiff_parse_manifest("from", &ridFrom);
if( pFrom==0 ) return;
zGlob = P("glob");
/*
** Maintenace reminder: we explicitly do _not_ use P_NoBot()
** for "from" and "to" because those args can contain legitimate
** strings which may trigger the looks-like SQL checks, e.g.
** from=merge-in:OR-clause-improvement
** to=OR-clause-improvement
*/
zFrom = P("from");
zTo = P("to");
if( bInvert ){
Manifest *pTemp = pTo;
const char *zTemp = zTo;
pTo = pFrom;
pFrom = pTemp;
zTo = zFrom;
zFrom = zTemp;
|
| ︙ | ︙ | |||
2500 2501 2502 2503 2504 2505 2506 |
) ){
if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
page_tree();
return;
}
/* No directory found, look for an historic version of the file
** that was subsequently deleted. */
| | | 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 |
) ){
if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
page_tree();
return;
}
/* No directory found, look for an historic version of the file
** that was subsequently deleted. */
db_prepare(&q,
"SELECT fid, uuid FROM mlink, filename, event, blob"
" WHERE filename.name=%Q"
" AND mlink.fnid=filename.fnid AND mlink.fid>0"
" AND event.objid=mlink.mid"
" AND blob.rid=mlink.mid"
" ORDER BY event.mtime DESC",
zName
|
| ︙ | ︙ | |||
2622 2623 2624 2625 2626 2627 2628 |
style_header("%s", zHeader);
fossil_free(zCIUuid);
fossil_free(zHeader);
if( !isFile && g.perm.Admin ){
Stmt q;
db_prepare(&q,
"SELECT coalesce(user.login,rcvfrom.uid),"
| | > | 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 |
style_header("%s", zHeader);
fossil_free(zCIUuid);
fossil_free(zHeader);
if( !isFile && g.perm.Admin ){
Stmt q;
db_prepare(&q,
"SELECT coalesce(user.login,rcvfrom.uid),"
" datetime(rcvfrom.mtime,toLocal()),"
" coalesce(rcvfrom.ipaddr,'unknown')"
" FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
" WHERE blob.rid=%d"
" AND rcvfrom.rcvid=blob.rcvid;", rid);
while( db_step(&q)==SQLITE_ROW ){
const char *zUser = db_column_text(&q,0);
const char *zDate = db_column_text(&q,1);
const char *zIp = db_column_text(&q,2);
|
| ︙ | ︙ | |||
2798 2799 2800 2801 2802 2803 2804 |
}
}
if( strcmp(zModAction,"approve")==0 ){
moderation_approve('t', rid);
}
}
zTktTitle = db_table_has_column("repository", "ticket", "title" )
| | | 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 |
}
}
if( strcmp(zModAction,"approve")==0 ){
moderation_approve('t', rid);
}
}
zTktTitle = db_table_has_column("repository", "ticket", "title" )
? db_text("(No title)",
"SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
: 0;
style_set_current_feature("tinfo");
style_header("Ticket Change Details");
style_submenu_element("Raw", "%R/artifact/%s", zUuid);
style_submenu_element("History", "%R/tkthistory/%s#%S", zTktName,zUuid);
style_submenu_element("Page", "%R/tktview/%t", zTktName);
|
| ︙ | ︙ | |||
3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 |
" LEFT JOIN singletonTag ON singletonTag.rid=event.objid"
" WHERE event.objid=%d"
" UNION ALL"
" SELECT plink.pid, event.mtime, singletonTag.tagname, n+1"
" FROM ancestor, plink, event"
" LEFT JOIN singletonTag ON singletonTag.rid=plink.pid"
" WHERE plink.cid=ancestor.rid"
" AND event.objid=plink.pid"
" AND ancestor.tagname IS NULL"
" ORDER BY mtime DESC"
" )"
"SELECT tagname, n"
" FROM ancestor"
" WHERE tagname IS NOT NULL"
" ORDER BY n LIMIT 1;",
rid, rid
);
| > > | 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 |
" LEFT JOIN singletonTag ON singletonTag.rid=event.objid"
" WHERE event.objid=%d"
" UNION ALL"
" SELECT plink.pid, event.mtime, singletonTag.tagname, n+1"
" FROM ancestor, plink, event"
" LEFT JOIN singletonTag ON singletonTag.rid=plink.pid"
" WHERE plink.cid=ancestor.rid"
" AND plink.isprim=1"
" AND event.objid=plink.pid"
" AND ancestor.tagname IS NULL"
" ORDER BY mtime DESC"
" LIMIT 100000"
" )"
"SELECT tagname, n"
" FROM ancestor"
" WHERE tagname IS NOT NULL"
" ORDER BY n LIMIT 1;",
rid, rid
);
|
| ︙ | ︙ | |||
3830 3831 3832 3833 3834 3835 3836 | ** If no VERSION is provided, describe the currently checked-out version. ** ** If VERSION and the found ancestor refer to the same commit, the last two ** components are omitted, unless --long is provided. When no fitting tagged ** ancestor is found, show only the short hash of VERSION. ** ** Options: | | | 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 |
** If no VERSION is provided, describe the currently checked-out version.
**
** If VERSION and the found ancestor refer to the same commit, the last two
** components are omitted, unless --long is provided. When no fitting tagged
** ancestor is found, show only the short hash of VERSION.
**
** Options:
** --digits Display so many hex digits of the hash
** (default: the larger of 6 and the 'hash-digit' setting)
** -d|--dirty Show whether there are changes to be committed
** --long Always show all three components
** --match GLOB Consider only non-propagating tags matching GLOB
*/
void describe_cmd(void){
const char *zName;
|
| ︙ | ︙ |
Changes to src/interwiki.c.
| ︙ | ︙ | |||
43 44 45 46 47 48 49 |
**
** {
** "base": Base URL for the remote site.
** "hash": Append this to "base" for Hash targets.
** "wiki": Append this to "base" for Wiki targets.
** }
**
| | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
**
** {
** "base": Base URL for the remote site.
** "hash": Append this to "base" for Hash targets.
** "wiki": Append this to "base" for Wiki targets.
** }
**
** If the remote wiki is Fossil, then the correct value for "hash"
** is "/info/" and the correct value for "wiki" is "/wiki?name=".
** If (for example) Wikipedia is the remote, then "hash" should be
** omitted and the correct value for "wiki" is "/wiki/".
**
** PageName is link name of the target wiki. Several different forms
** of PageName are recognized.
**
** Path If PageName is empty or begins with a "/" character, then
** it is a pathname that is appended to "base".
**
|
| ︙ | ︙ | |||
80 81 82 83 84 85 86 |
static Stmt q;
for(i=0; fossil_isalnum(zTarget[i]); i++){}
if( zTarget[i]!=':' ) return 0;
nCode = i;
if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0;
zPage = zTarget + nCode + 1;
nPage = (int)strlen(zPage);
| | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
static Stmt q;
for(i=0; fossil_isalnum(zTarget[i]); i++){}
if( zTarget[i]!=':' ) return 0;
nCode = i;
if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0;
zPage = zTarget + nCode + 1;
nPage = (int)strlen(zPage);
db_static_prepare(&q,
"SELECT value->>'base', value->>'hash', value->>'wiki'"
" FROM config WHERE name=lower($name) AND json_valid(value)"
);
zName = mprintf("interwiki:%.*s", nCode, zTarget);
db_bind_text(&q, "$name", zName);
while( db_step(&q)==SQLITE_ROW ){
const char *zBase = db_column_text(&q,0);
|
| ︙ | ︙ | |||
220 221 222 223 224 225 226 |
verify_all_options();
if( g.argc<4 ) usage("delete ID ...");
db_begin_write();
db_unprotect(PROTECT_CONFIG);
for(i=3; i<g.argc; i++){
const char *zName = g.argv[i];
db_multi_exec(
| | | 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
verify_all_options();
if( g.argc<4 ) usage("delete ID ...");
db_begin_write();
db_unprotect(PROTECT_CONFIG);
for(i=3; i<g.argc; i++){
const char *zName = g.argv[i];
db_multi_exec(
"DELETE FROM config WHERE name='interwiki:%q'",
zName
);
}
setup_incr_cfgcnt();
db_protect_pop();
db_commit_transaction();
}else
|
| ︙ | ︙ |
Changes to src/json.c.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 | ** The JSON API's public interface is documented at: ** ** https://fossil-scm.org/fossil/doc/trunk/www/json-api/index.md ** ** Notes for hackers... ** ** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or | | | > | > | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
** The JSON API's public interface is documented at:
**
** https://fossil-scm.org/fossil/doc/trunk/www/json-api/index.md
**
** Notes for hackers...
**
** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or
** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions
** then dispatch to a JSON-mode-specific command/page handler with the type
** fossil_json_f().
** See the API docs for that typedef (below) for the semantics of the callbacks.
**
**
*/
#include "VERSION.h"
#include "config.h"
#include "json.h"
#include <assert.h>
#include <time.h>
#if INTERFACE
#include "json_detail.h" /* workaround for apparent enum limitation
in makeheaders */
#endif
const FossilJsonKeys_ FossilJsonKeys = {
"anonymousSeed" /*anonymousSeed*/,
"authToken" /*authToken*/,
"COMMAND_PATH" /*commandPath*/,
"mtime" /*mtime*/,
|
| ︙ | ︙ | |||
174 175 176 177 178 179 180 | return 0; } /* ** Convenience wrapper around cson_output() which appends the output ** to pDest. pOpt may be NULL, in which case g.json.outOpt will be used. */ | | > | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
return 0;
}
/*
** Convenience wrapper around cson_output() which appends the output
** to pDest. pOpt may be NULL, in which case g.json.outOpt will be used.
*/
int cson_output_Blob( cson_value const * pVal, Blob * pDest,
cson_output_opt const * pOpt ){
return cson_output( pVal, cson_data_dest_Blob,
pDest, pOpt ? pOpt : &g.json.outOpt );
}
/*
** Convenience wrapper around cson_parse() which reads its input
** from pSrc. pSrc is rewound before parsing.
|
| ︙ | ︙ | |||
216 217 218 219 220 221 222 |
**
** In practice we will only ever call this one time per app execution
** when constructing the JSON response envelope, so the static buffer
** "shouldn't" be a problem.
**
*/
char const * json_rc_cstr( int code ){
| | | | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
**
** In practice we will only ever call this one time per app execution
** when constructing the JSON response envelope, so the static buffer
** "shouldn't" be a problem.
**
*/
char const * json_rc_cstr( int code ){
enum { BufSize = 13 };
static char buf[BufSize] = {'F','O','S','S','I','L','-',0};
assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
sqlite3_snprintf((int)BufSize, buf+7,"%04d", code);
return buf;
}
/*
** Adds v to the API-internal cleanup mechanism. key is ignored
** (legacy) but might be re-introduced and "should" be a unique
** (app-wide) value. Failure to insert an item may be caused by any
|
| ︙ | ︙ | |||
705 706 707 708 709 710 711 |
login_cookie_name(), there is(?) a potential(?) login hijacking
window here. We may need to change the JSON auth token to be in
the form: login_cookie_name()=...
Then again, the hardened cookie value helps ensure that
only a proper key/value match is valid.
*/
| | > | 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 |
login_cookie_name(), there is(?) a potential(?) login hijacking
window here. We may need to change the JSON auth token to be in
the form: login_cookie_name()=...
Then again, the hardened cookie value helps ensure that
only a proper key/value match is valid.
*/
cgi_replace_parameter( login_cookie_name(),
cson_value_get_cstr(g.json.authToken) );
}else if( g.isHTTP ){
/* try fossil's conventional cookie. */
/* Reminder: chicken/egg scenario regarding db access in CLI
mode because login_cookie_name() needs the db. CLI
mode does not use any authentication, so we don't need
to support it here.
*/
|
| ︙ | ︙ | |||
902 903 904 905 906 907 908 |
assert( head != p );
zPart = (char*)fossil_malloc(len+1);
memcpy(zPart, head, len);
zPart[len] = 0;
if(doDeHttp){
dehttpize(zPart);
}
| > | | 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 |
assert( head != p );
zPart = (char*)fossil_malloc(len+1);
memcpy(zPart, head, len);
zPart[len] = 0;
if(doDeHttp){
dehttpize(zPart);
}
if( *zPart ){
/* should only fail if someone manages to url-encoded a NUL byte */
part = cson_value_new_string(zPart, strlen(zPart));
if( 0 != cson_array_append( target, part ) ){
cson_value_free(part);
rc = -rc;
break;
}
}else{
|
| ︙ | ︙ | |||
1084 1085 1086 1087 1088 1089 1090 |
break;
}
/* g.json.reqPayload exists only to simplify some of our access to
the request payload. We currently only use this in the context of
Object payloads, not Arrays, strings, etc.
*/
| | | 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 |
break;
}
/* g.json.reqPayload exists only to simplify some of our access to
the request payload. We currently only use this in the context of
Object payloads, not Arrays, strings, etc.
*/
g.json.reqPayload.v = cson_object_get( g.json.post.o,FossilJsonKeys.payload );
if( g.json.reqPayload.v ){
g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
/* g.json.reqPayload.o may legally be NULL, which means only that
g.json.reqPayload.v is-not-a Object.
*/;
}
|
| ︙ | ︙ | |||
1113 1114 1115 1116 1117 1118 1119 |
}
if(!g.json.jsonp){
g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL);
}
if(!g.isHTTP){
| | | 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 |
}
if(!g.json.jsonp){
g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL);
}
if(!g.isHTTP){
g.json.errorDetailParanoia = 0;/*disable error code dumb-down for CLI mode*/
}
{/* set up JSON output formatting options. */
int indent = -1;
indent = json_find_option_int("indent",NULL,"I",-1);
g.json.outOpt.indentation = (0>indent)
? (g.isHTTP ? 0 : 1)
|
| ︙ | ︙ | |||
1164 1165 1166 1167 1168 1169 1170 |
** Note that CLI options are not included in the command path. Use
** find_option() to get those.
**
*/
char const * json_command_arg(unsigned short ndx){
cson_array * ar = g.json.cmd.a;
assert((NULL!=ar) && "Internal error. Was json_bootstrap_late() called?");
| | | 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 |
** Note that CLI options are not included in the command path. Use
** find_option() to get those.
**
*/
char const * json_command_arg(unsigned short ndx){
cson_array * ar = g.json.cmd.a;
assert((NULL!=ar) && "Internal error. Was json_bootstrap_late() called?");
assert((g.argc>1) &&"Internal error - we never should have gotten this far.");
if( g.json.cmd.offset < 0 ){
/* first-time setup. */
short i = 0;
#define NEXT cson_string_cstr( \
cson_value_get_string( \
cson_array_get(ar,i) \
))
|
| ︙ | ︙ | |||
1190 1191 1192 1193 1194 1195 1196 |
}
}
#undef NEXT
if(g.json.cmd.offset < 0){
return NULL;
}else{
ndx = g.json.cmd.offset + ndx;
| | > | > | 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 |
}
}
#undef NEXT
if(g.json.cmd.offset < 0){
return NULL;
}else{
ndx = g.json.cmd.offset + ndx;
return cson_string_cstr(cson_value_get_string(
cson_array_get( ar, g.json.cmd.offset + ndx )));
}
}
/* Returns the C-string form of json_auth_token(), or NULL
** if json_auth_token() returns NULL.
*/
char const * json_auth_token_cstr(){
return cson_value_get_cstr( json_auth_token() );
}
/*
** Returns the JsonPageDef with the given name, or NULL if no match is
** found.
**
** head must be a pointer to an array of JsonPageDefs in which the
** last entry has a NULL name.
*/
JsonPageDef const * json_handler_for_name( char const * name,
JsonPageDef const * head ){
JsonPageDef const * pageDef = head;
assert( head != NULL );
if(name && *name) for( ; pageDef->name; ++pageDef ){
if( 0 == strcmp(name, pageDef->name) ){
return pageDef;
}
}
|
| ︙ | ︙ | |||
1290 1291 1292 1293 1294 1295 1296 |
*/
static cson_value * json_response_command_path(){
if(!g.json.cmd.a){
return NULL;
}else{
cson_value * rc = NULL;
Blob path = empty_blob;
| | > | > | 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 |
*/
static cson_value * json_response_command_path(){
if(!g.json.cmd.a){
return NULL;
}else{
cson_value * rc = NULL;
Blob path = empty_blob;
unsigned int aLen = g.json.dispatchDepth+1;
/*cson_array_length_get(g.json.cmd.a);*/
unsigned int i = 1;
for( ; i < aLen; ++i ){
char const * part = cson_string_cstr(cson_value_get_string(
cson_array_get(g.json.cmd.a, i)));
if(!part){
#if 1
fossil_warning("Iterating further than expected in %s.",
__FILE__);
#endif
break;
}
|
| ︙ | ︙ | |||
1327 1328 1329 1330 1331 1332 1333 |
*/
cson_value * json_g_to_json(){
cson_object * o = NULL;
cson_object * pay = NULL;
pay = o = cson_new_object();
#define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
| | > | 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 |
*/
cson_value * json_g_to_json(){
cson_object * o = NULL;
cson_object * pay = NULL;
pay = o = cson_new_object();
#define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
#define CSTR(OBJ,K) cson_object_set(o, #K, OBJ.K ? json_new_string(OBJ.K) \
: cson_value_null())
#define VAL(K,V) cson_object_set(o, #K, (V) ? (V) : cson_value_null())
VAL(capabilities, json_cap_value());
INT(g, argc);
INT(g, isConst);
CSTR(g, zConfigDbName);
INT(g, repositoryOpen);
INT(g, localOpen);
|
| ︙ | ︙ | |||
1811 1812 1813 1814 1815 1816 1817 |
cson_string * kDesc;
cson_array_reserve( list, 35 );
kRC = cson_new_string("resultCode",10);
kSymbol = cson_new_string("cSymbol",7);
kNumber = cson_new_string("number",6);
kDesc = cson_new_string("description",11);
#define C(K) obj = cson_new_object(); \
| | | | | > | 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 |
cson_string * kDesc;
cson_array_reserve( list, 35 );
kRC = cson_new_string("resultCode",10);
kSymbol = cson_new_string("cSymbol",7);
kNumber = cson_new_string("number",6);
kDesc = cson_new_string("description",11);
#define C(K) obj = cson_new_object(); \
cson_object_set_s(obj, kRC,json_new_string(json_rc_cstr(FSL_JSON_E_##K))); \
cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) ); \
cson_object_set_s(obj, kNumber, cson_value_new_integer(FSL_JSON_E_##K) ); \
cson_object_set_s(obj, kDesc, \
json_new_string(json_err_cstr(FSL_JSON_E_##K))); \
cson_array_append( list, cson_object_value(obj) ); obj = NULL;
C(GENERIC);
C(INVALID_REQUEST);
C(UNKNOWN_COMMAND);
C(UNKNOWN);
C(TIMEOUT);
|
| ︙ | ︙ | |||
2004 2005 2006 2007 2008 2009 2010 |
if( !g.perm.Read ){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'o' permissions.");
return NULL;
}
full = json_find_option_bool("full",NULL,"f",
json_find_option_bool("verbose",NULL,"v",0));
| | > | | 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 |
if( !g.perm.Read ){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'o' permissions.");
return NULL;
}
full = json_find_option_bool("full",NULL,"f",
json_find_option_bool("verbose",NULL,"v",0));
#define SETBUF(O,K) cson_object_set(O, K, \
cson_value_new_string(zBuf, strlen(zBuf)));
jv = cson_value_new_object();
jo = cson_value_get_object(jv);
zTmp = db_get("project-name",NULL);
cson_object_set(jo, "projectName", json_new_string(zTmp));
fossil_free(zTmp);
zTmp = db_get("project-description",NULL);
cson_object_set(jo, "projectDescription", json_new_string(zTmp));
fossil_free(zTmp);
zTmp = NULL;
fsize = file_size(g.zRepositoryName, ExtFILE);
cson_object_set(jo, "repositorySize",
cson_value_new_integer((cson_int_t)fsize));
if(full){
n = db_int(0, "SELECT count(*) FROM blob");
m = db_int(0, "SELECT count(*) FROM delta");
cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));
|
| ︙ | ︙ | |||
2066 2067 2068 2069 2070 2071 2072 |
}/*full*/
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
SETBUF(jo, "projectCode");
| > | | | | > | > | > | | > | > | 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 |
}/*full*/
n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
" + 0.99");
cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
SETBUF(jo, "projectCode");
cson_object_set(jo, "compiler",
cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));
jv2 = cson_value_new_object();
jo2 = cson_value_get_object(jv2);
cson_object_set(jo, "sqlite", jv2);
sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)", sqlite3_sourceid(),
&sqlite3_sourceid()[20], sqlite3_libversion());
SETBUF(jo2, "version");
cson_object_set(jo2, "pageCount", cson_value_new_integer(
(cson_int_t)db_int(0, "PRAGMA repository.page_count")));
cson_object_set(jo2, "pageSize", cson_value_new_integer(
(cson_int_t)db_int(0, "PRAGMA repository.page_size")));
cson_object_set(jo2, "freeList", cson_value_new_integer(
(cson_int_t)db_int(0, "PRAGMA repository.freelist_count")));
sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0,"PRAGMA repository.encoding"));
SETBUF(jo2, "encoding");
sqlite3_snprintf(BufLen, zBuf, "%s",
db_text(0, "PRAGMA repository.journal_mode"));
cson_object_set(jo2, "journalMode", *zBuf ?
cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
return jv;
#undef SETBUF
}
|
| ︙ | ︙ | |||
2235 2236 2237 2238 2239 2240 2241 |
cson_value * json_page_status(void);
/*
** Mapping of names to JSON pages/commands. Each name is a subpath of
** /json (in CGI mode) or a subcommand of the json command in CLI mode
*/
static const JsonPageDef JsonPageDefs[] = {
| | > | 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 |
cson_value * json_page_status(void);
/*
** Mapping of names to JSON pages/commands. Each name is a subpath of
** /json (in CGI mode) or a subcommand of the json command in CLI mode
*/
static const JsonPageDef JsonPageDefs[] = {
/* please keep alphabetically sorted (case-insensitive)
for maintenance reasons. */
{"anonymousPassword", json_page_anon_password, 0},
{"artifact", json_page_artifact, 0},
{"branch", json_page_branch,0},
{"cap", json_page_cap, 0},
{"config", json_page_config, 0 },
{"diff", json_page_diff, 0},
{"dir", json_page_dir, 0},
|
| ︙ | ︙ |
Changes to src/json_artifact.c.
| ︙ | ︙ | |||
209 210 211 212 213 214 215 |
}
/*
** Sub-impl of /json/artifact for check-ins.
*/
static cson_value * json_artifact_ci( cson_object * zParent, int rid ){
if(!g.perm.Read){
| | > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
}
/*
** Sub-impl of /json/artifact for check-ins.
*/
static cson_value * json_artifact_ci( cson_object * zParent, int rid ){
if(!g.perm.Read){
json_set_err( FSL_JSON_E_DENIED,
"Viewing check-ins requires 'o' privileges." );
return NULL;
}else{
cson_value * artV = json_artifact_for_ci(rid, 1);
cson_object * art = cson_value_get_object(artV);
if(art){
cson_object_merge( zParent, art, CSON_MERGE_REPLACE );
cson_free_object(art);
|
| ︙ | ︙ | |||
248 249 250 251 252 253 254 |
** if either the includeContent (HTTP) or -content|-c boolean flags
** (CLI) are set.
*/
static int json_artifact_get_content_format_flag(void){
enum { MagicValue = -9 };
int contentFormat = json_wiki_get_content_format_flag(MagicValue);
if(MagicValue == contentFormat){
| | > | | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
** if either the includeContent (HTTP) or -content|-c boolean flags
** (CLI) are set.
*/
static int json_artifact_get_content_format_flag(void){
enum { MagicValue = -9 };
int contentFormat = json_wiki_get_content_format_flag(MagicValue);
if(MagicValue == contentFormat){
contentFormat = json_find_option_bool("includeContent",
"content","c",0) /* deprecated */ ? -1 : 0;
}
return contentFormat;
}
extern int json_wiki_get_content_format_flag(int defaultValue) /* json_wiki.c*/;
cson_value * json_artifact_wiki(cson_object * zParent, int rid){
if( ! g.perm.RdWiki ){
json_set_err(FSL_JSON_E_DENIED,
"Requires 'j' privileges.");
return NULL;
}else{
|
| ︙ | ︙ | |||
378 379 380 381 382 383 384 |
);
/* TODO: add a "state" flag for the file in each check-in,
e.g. "modified", "new", "deleted".
*/
checkin_arr = cson_new_array();
cson_object_set(pay, "checkins", cson_array_value(checkin_arr));
while( (SQLITE_ROW==db_step(&q) ) ){
| | > | | | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
);
/* TODO: add a "state" flag for the file in each check-in,
e.g. "modified", "new", "deleted".
*/
checkin_arr = cson_new_array();
cson_object_set(pay, "checkins", cson_array_value(checkin_arr));
while( (SQLITE_ROW==db_step(&q) ) ){
cson_object * row = cson_value_get_object(
cson_sqlite3_row_to_object(q.pStmt));
/* FIXME: move this isNew/isDel stuff into an SQL CASE statement. */
char const isNew = cson_value_get_bool(cson_object_get(row,"isNew"));
char const isDel = cson_value_get_bool(cson_object_get(row,"isDel"));
cson_object_set(row, "isNew", NULL);
cson_object_set(row, "isDel", NULL);
cson_object_set(row, "state", json_new_string(
json_artifact_status_to_string(isNew, isDel)));
cson_array_append( checkin_arr, cson_object_value(row) );
}
db_finalize(&q);
return cson_object_value(pay);
}
/*
|
| ︙ | ︙ |
Changes to src/json_branch.c.
| ︙ | ︙ | |||
195 196 197 198 199 200 201 | char *zUuid; /* Artifact ID of origin */ Stmt q; /* Generic query */ char *zDate; /* Date that branch was created */ char *zComment; /* Check-in comment for the new branch */ Blob branch; /* manifest for the new branch */ Manifest *pParent; /* Parsed parent manifest */ Blob mcksum; /* Self-checksum on the manifest */ | < < < < < | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
char *zUuid; /* Artifact ID of origin */
Stmt q; /* Generic query */
char *zDate; /* Date that branch was created */
char *zComment; /* Check-in comment for the new branch */
Blob branch; /* manifest for the new branch */
Manifest *pParent; /* Parsed parent manifest */
Blob mcksum; /* Self-checksum on the manifest */
/* fossil branch new name */
if( zBranch==0 || zBranch[0]==0 ){
zOpt->rcErrMsg = "Branch name may not be null/empty.";
return FSL_JSON_E_INVALID_ARGS;
}
if( db_exists(
"SELECT 1 FROM tagxref"
|
| ︙ | ︙ | |||
263 264 265 266 267 268 269 |
blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
}
manifest_destroy(pParent);
/* Add the symbolic branch name and the "branch" tag to identify
** this as a new branch */
if( content_is_private(rootid) ) zOpt->isPrivate = 1;
| < | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
blob_appendf(&branch, "R %s\n", pParent->zRepoCksum);
}
manifest_destroy(pParent);
/* Add the symbolic branch name and the "branch" tag to identify
** this as a new branch */
if( content_is_private(rootid) ) zOpt->isPrivate = 1;
if( zColor!=0 ){
blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
}
blob_appendf(&branch, "T *branch * %F\n", zBranch);
blob_appendf(&branch, "T *sym-%F *\n", zBranch);
/* Cancel all other symbolic tags */
|
| ︙ | ︙ | |||
333 334 335 336 337 338 339 |
}
if(!opt.zName){
opt.zName = json_command_arg(g.json.dispatchDepth+1);
}
if(!opt.zName){
| | > | 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
}
if(!opt.zName){
opt.zName = json_command_arg(g.json.dispatchDepth+1);
}
if(!opt.zName){
json_set_err(FSL_JSON_E_MISSING_ARGS,
"'name' parameter was not specified." );
return NULL;
}
opt.zColor = json_find_option_cstr("bgColor","bgcolor",NULL);
opt.zBasis = json_find_option_cstr("basis",NULL,NULL);
if(!opt.zBasis && !g.isHTTP){
opt.zBasis = json_command_arg(g.json.dispatchDepth+2);
|
| ︙ | ︙ |
Changes to src/json_config.c.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
** mostly a copy of the config options in configure.c, but that data
** is private and cannot be re-used directly here.
*/
static const struct JsonConfigProperty {
char const * name;
int groupMask;
} JsonConfigProperties[] = {
| | > > > > > > > > > > > > > > > > > > > > > > > | 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 134 135 136 137 138 |
** mostly a copy of the config options in configure.c, but that data
** is private and cannot be re-used directly here.
*/
static const struct JsonConfigProperty {
char const * name;
int groupMask;
} JsonConfigProperties[] = {
{ "css", CONFIGSET_CSS },
{ "header", CONFIGSET_SKIN },
{ "mainmenu", CONFIGSET_SKIN },
{ "footer", CONFIGSET_SKIN },
{ "details", CONFIGSET_SKIN },
{ "js", CONFIGSET_SKIN },
{ "default-skin", CONFIGSET_SKIN },
{ "logo-mimetype", CONFIGSET_SKIN },
{ "logo-image", CONFIGSET_SKIN },
{ "background-mimetype", CONFIGSET_SKIN },
{ "background-image", CONFIGSET_SKIN },
{ "icon-mimetype", CONFIGSET_SKIN },
{ "icon-image", CONFIGSET_SKIN },
{ "timeline-block-markup", CONFIGSET_SKIN },
{ "timeline-date-format", CONFIGSET_SKIN },
{ "timeline-default-style", CONFIGSET_SKIN },
{ "timeline-dwelltime", CONFIGSET_SKIN },
{ "timeline-closetime", CONFIGSET_SKIN },
{ "timeline-hard-newlines", CONFIGSET_SKIN },
{ "timeline-max-comment", CONFIGSET_SKIN },
{ "timeline-plaintext", CONFIGSET_SKIN },
{ "timeline-truncate-at-blank", CONFIGSET_SKIN },
{ "timeline-tslink-info", CONFIGSET_SKIN },
{ "timeline-utc", CONFIGSET_SKIN },
{ "adunit", CONFIGSET_SKIN },
{ "adunit-omit-if-admin", CONFIGSET_SKIN },
{ "adunit-omit-if-user", CONFIGSET_SKIN },
{ "default-csp", CONFIGSET_SKIN },
{ "sitemap-extra", CONFIGSET_SKIN },
{ "safe-html", CONFIGSET_SKIN },
{ "project-name", CONFIGSET_PROJ },
{ "short-project-name", CONFIGSET_PROJ },
{ "project-description", CONFIGSET_PROJ },
{ "index-page", CONFIGSET_PROJ },
{ "manifest", CONFIGSET_PROJ },
{ "binary-glob", CONFIGSET_PROJ },
{ "clean-glob", CONFIGSET_PROJ },
{ "ignore-glob", CONFIGSET_PROJ },
{ "keep-glob", CONFIGSET_PROJ },
{ "crlf-glob", CONFIGSET_PROJ },
{ "crnl-glob", CONFIGSET_PROJ },
{ "encoding-glob", CONFIGSET_PROJ },
{ "empty-dirs", CONFIGSET_PROJ },
{ "dotfiles", CONFIGSET_PROJ },
{ "parent-project-code", CONFIGSET_PROJ },
{ "parent-project-name", CONFIGSET_PROJ },
{ "hash-policy", CONFIGSET_PROJ },
{ "comment-format", CONFIGSET_PROJ },
{ "mimetypes", CONFIGSET_PROJ },
{ "forbid-delta-manifests", CONFIGSET_PROJ },
{ "mv-rm-files", CONFIGSET_PROJ },
{ "user-color-map", CONFIGSET_USER },
{ "ticket-table", CONFIGSET_TKT },
{ "ticket-common", CONFIGSET_TKT },
{ "ticket-change", CONFIGSET_TKT },
{ "ticket-newpage", CONFIGSET_TKT },
{ "ticket-viewpage", CONFIGSET_TKT },
{ "ticket-editpage", CONFIGSET_TKT },
|
| ︙ | ︙ | |||
255 256 257 258 259 260 261 |
}
for(i=0; i<nSetting; ++i){
const Setting *pSet = &aSetting[i];
cson_object * jSet;
cson_value * pVal = 0, * pSrc = 0;
jSet = cson_new_object();
cson_object_set(pay, pSet->name, cson_object_value(jSet));
| | | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
}
for(i=0; i<nSetting; ++i){
const Setting *pSet = &aSetting[i];
cson_object * jSet;
cson_value * pVal = 0, * pSrc = 0;
jSet = cson_new_object();
cson_object_set(pay, pSet->name, cson_object_value(jSet));
cson_object_set(jSet, "versionable",cson_value_new_bool(pSet->versionable));
cson_object_set(jSet, "sensitive", cson_value_new_bool(pSet->sensitive));
cson_object_set(jSet, "defaultValue", (pSet->def && pSet->def[0])
? json_new_string(pSet->def)
: cson_value_null());
if( 0==pSet->sensitive || 0!=g.perm.Setup ){
if( pSet->versionable ){
/* Check to see if this is overridden by a versionable
|
| ︙ | ︙ | |||
290 291 292 293 294 295 296 |
Blob versionedPathname;
blob_zero(&versionedPathname);
blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
g.zLocalRoot, pSet->name);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
Blob content;
blob_zero(&content);
| | | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
Blob versionedPathname;
blob_zero(&versionedPathname);
blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
g.zLocalRoot, pSet->name);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
Blob content;
blob_zero(&content);
blob_read_from_file(&content, blob_str(&versionedPathname),ExtFILE);
pSrc = json_new_string("versioned");
pVal = json_new_string(blob_str(&content));
blob_reset(&content);
}
blob_reset(&versionedPathname);
}
}
|
| ︙ | ︙ |
Changes to src/json_finfo.c.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 |
int currentRow = 0;
char const * zCheckin = NULL;
signed char sort = -1;
if(!g.perm.Read){
json_set_err(FSL_JSON_E_DENIED,"Requires 'o' privileges.");
return NULL;
}
| > | | | | | 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 |
int currentRow = 0;
char const * zCheckin = NULL;
signed char sort = -1;
if(!g.perm.Read){
json_set_err(FSL_JSON_E_DENIED,"Requires 'o' privileges.");
return NULL;
}
json_warn( FSL_JSON_W_UNKNOWN,
"Achtung: the output of the finfo command is up for change.");
/* For the "name" argument we have to jump through some hoops to make sure
that we don't get the fossil-internally-assigned "name" option.
*/
zFilename = json_find_option_cstr2("name",NULL,NULL, g.json.dispatchDepth+1);
if(!zFilename || !*zFilename){
json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing 'name' parameter.");
return NULL;
}
if(0==db_int(0,"SELECT 1 FROM filename WHERE name=%Q",zFilename)){
json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "File entry not found.");
return NULL;
}
zBefore = json_find_option_cstr("before",NULL,"b");
zAfter = json_find_option_cstr("after",NULL,"a");
limit = json_find_option_int("limit",NULL,"n", -1);
zCheckin = json_find_option_cstr("checkin",NULL,"ci");
blob_append_sql(&sql,
/*0*/ "SELECT b.uuid,"
/*1*/ " ci.uuid,"
/*2*/ " (SELECT uuid FROM blob WHERE rid=mlink.fid),"/* Current file uuid */
/*3*/ " cast(strftime('%%s',event.mtime) AS INTEGER),"
/*4*/ " coalesce(event.euser, event.user),"
/*5*/ " coalesce(event.ecomment, event.comment),"
/*6*/ " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */
/*7*/ " event.bgcolor,"
/*8*/ " b.size,"
/*9*/ " (mlink.pid==0) AS isNew,"
|
| ︙ | ︙ | |||
86 87 88 89 90 91 92 |
);
if( zCheckin && *zCheckin ){
char * zU = NULL;
int rc = name_to_uuid2( zCheckin, "ci", &zU );
/*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
if(rc<=0){
| | > | > | > | > | | | 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
);
if( zCheckin && *zCheckin ){
char * zU = NULL;
int rc = name_to_uuid2( zCheckin, "ci", &zU );
/*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
if(rc<=0){
json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID :
FSL_JSON_E_RESOURCE_NOT_FOUND,
"Check-in hash %s.", (rc<0) ? "is ambiguous" : "not found");
blob_reset(&sql);
return NULL;
}
blob_append_sql(&sql, " AND ci.uuid='%q'", zU);
free(zU);
}else{
if( zAfter && *zAfter ){
blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zAfter);
sort = 1;
}else if( zBefore && *zBefore ){
blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zBefore);
}
}
blob_append_sql(&sql," ORDER BY event.mtime %s /*sort*/",
(sort>0 ? "ASC" : "DESC"));
/*printf("SQL=\n%s\n",blob_str(&sql));*/
db_prepare(&q, "%s", blob_sql_text(&sql));
blob_reset(&sql);
pay = cson_new_object();
cson_object_set(pay, "name", json_new_string(zFilename));
if( limit > 0 ){
cson_object_set(pay, "limit", json_new_int(limit));
}
checkins = cson_new_array();
cson_object_set(pay, "checkins", cson_array_value(checkins));
while( db_step(&q)==SQLITE_ROW ){
cson_object * row = cson_new_object();
int const isNew = db_column_int(&q,9);
int const isDel = db_column_int(&q,10);
cson_array_append( checkins, cson_object_value(row) );
cson_object_set(row, "checkin", json_new_string( db_column_text(&q,1) ));
cson_object_set(row, "uuid", json_new_string( db_column_text(&q,2) ));
/*cson_object_set(row, "parentArtifact",
json_new_string( db_column_text(&q,6) ));*/
cson_object_set(row, "timestamp", json_new_int( db_column_int64(&q,3) ));
cson_object_set(row, "user", json_new_string( db_column_text(&q,4) ));
cson_object_set(row, "comment", json_new_string( db_column_text(&q,5) ));
/*cson_object_set(row, "bgColor",
json_new_string( db_column_text(&q,7) ));*/
cson_object_set(row, "size", json_new_int( db_column_int64(&q,8) ));
cson_object_set(row, "state", json_new_string(
json_artifact_status_to_string(isNew, isDel)));
if( (0 < limit) && (++currentRow >= limit) ){
break;
}
}
db_finalize(&q);
return pay ? cson_object_value(pay) : NULL;
}
#endif /* FOSSIL_ENABLE_JSON */
|
Changes to src/json_login.c.
| ︙ | ︙ | |||
103 104 105 106 107 108 109 |
jseed = json_req_payload_get(FossilJsonKeys.anonymousSeed);
if( !jseed ){
jseed = json_getenv("cs") /* name used by HTML interface */;
}
}
if(jseed){
if( cson_value_is_number(jseed) ){
| > | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
jseed = json_req_payload_get(FossilJsonKeys.anonymousSeed);
if( !jseed ){
jseed = json_getenv("cs") /* name used by HTML interface */;
}
}
if(jseed){
if( cson_value_is_number(jseed) ){
sqlite3_snprintf((int)SeedBufLen, seedBuffer, "%"CSON_INT_T_PFMT,
cson_value_get_integer(jseed));
anonSeed = seedBuffer;
}else if( cson_value_is_string(jseed) ){
anonSeed = cson_string_cstr(cson_value_get_string(jseed));
}
}
if(!anonSeed){
g.json.resultCode = preciseErrors
|
| ︙ | ︙ | |||
143 144 145 146 147 148 149 |
: FSL_JSON_E_LOGIN_FAILED;
return NULL;
}else{
char * cookie = NULL;
cson_object * po;
char * cap = NULL;
if(anonSeed){
| | | > | > | 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 |
: FSL_JSON_E_LOGIN_FAILED;
return NULL;
}else{
char * cookie = NULL;
cson_object * po;
char * cap = NULL;
if(anonSeed){
login_set_anon_cookie(&cookie, 0);
}else{
login_set_user_cookie(name, uid, &cookie, 0);
}
payload = cson_value_new_object();
po = cson_value_get_object(payload);
cson_object_set(po, "authToken", json_new_string(cookie));
free(cookie);
cson_object_set(po, "name", json_new_string(name));
cap = db_text(NULL, "SELECT cap FROM user WHERE login=%Q", name);
cson_object_set(po, "capabilities",
cap ? json_new_string(cap) : cson_value_null() );
free(cap);
cson_object_set(po, "loginCookieName",
json_new_string( login_cookie_name() ) );
/* TODO: add loginExpiryTime to the payload. To do this properly
we "should" add an ([unsigned] int *) to
login_set_user_cookie() and login_set_anon_cookie(), to which
the expiry time is assigned. (Remember that JSON doesn't do
unsigned int.)
For non-anonymous users we could also simply query the
|
| ︙ | ︙ | |||
209 210 211 212 213 214 215 |
/*
** Implementation of the /json/anonymousPassword page.
*/
cson_value * json_page_anon_password(void){
cson_value * v = cson_value_new_object();
cson_object * o = cson_value_get_object(v);
unsigned const int seed = captcha_seed();
| | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
/*
** Implementation of the /json/anonymousPassword page.
*/
cson_value * json_page_anon_password(void){
cson_value * v = cson_value_new_object();
cson_object * o = cson_value_get_object(v);
unsigned const int seed = captcha_seed();
char const * zCaptcha = captcha_decode(seed, 0);
cson_object_set(o, "seed",
cson_value_new_integer( (cson_int_t)seed )
);
cson_object_set(o, "password",
cson_value_new_string( zCaptcha, strlen(zCaptcha) )
);
return v;
|
| ︙ | ︙ |
Changes to src/json_tag.c.
| ︙ | ︙ | |||
115 116 117 118 119 120 121 |
cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
{
Blob uu = empty_blob;
int rc;
blob_append(&uu, zName, -1);
rc = name_to_uuid(&uu, 9, "*");
if(0!=rc){
| | > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
{
Blob uu = empty_blob;
int rc;
blob_append(&uu, zName, -1);
rc = name_to_uuid(&uu, 9, "*");
if(0!=rc){
json_set_err(FSL_JSON_E_UNKNOWN,
"Could not convert name back to artifact hash!");
blob_reset(&uu);
goto error;
}
cson_object_set(pay, "appliedTo", json_new_string(blob_buffer(&uu)));
blob_reset(&uu);
}
|
| ︙ | ︙ |
Changes to src/json_timeline.c.
| ︙ | ︙ | |||
167 168 169 170 171 172 173 |
zUnhide = json_find_option_cstr("unhide",NULL,NULL);
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
zTag);
if(tagid<=0){
return -1;
}
if(pPayload){
| | > | > | 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 |
zUnhide = json_find_option_cstr("unhide",NULL,NULL);
tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
zTag);
if(tagid<=0){
return -1;
}
if(pPayload){
cson_object_set( pPayload, zBranch ? "branch" : "tag",
json_new_string(zTag) );
}
blob_appendf(pSql,
" AND ("
" EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
tagid);
if(!zUnhide){
blob_appendf(pSql,
" AND NOT EXISTS(SELECT 1 FROM plink "
" JOIN tagxref ON rid=blob.rid"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
TAG_HIDDEN);
}
if(zBranch){
/* from "r" flag code in page_timeline().*/
blob_appendf(pSql,
" OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"
|
| ︙ | ︙ | |||
350 351 352 353 354 355 356 |
cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3)));
if(!isNew && (flags & json_get_changed_files_ELIDE_PARENT)){
cson_object_set(row, "parent", json_new_string(db_column_text(&q,4)));
}
cson_object_set(row, "size", json_new_int(db_column_int(&q,5)));
cson_object_set(row, "state",
| | | > | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3)));
if(!isNew && (flags & json_get_changed_files_ELIDE_PARENT)){
cson_object_set(row, "parent", json_new_string(db_column_text(&q,4)));
}
cson_object_set(row, "size", json_new_int(db_column_int(&q,5)));
cson_object_set(row, "state",
json_new_string(json_artifact_status_to_string(isNew,isDel)));
zDownload = mprintf("/raw/%s?name=%s",
/* reminder: g.zBaseURL is of course not set
for CLI mode. */
db_column_text(&q,2),
db_column_text(&q,3));
cson_object_set(row, "downloadPath", json_new_string(zDownload));
free(zDownload);
}
db_finalize(&q);
return rowsV;
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 |
int const rid = db_column_int(&q,0);
cson_value * rowV = json_artifact_for_ci(rid, verboseFlag);
cson_object * row = cson_value_get_object(rowV);
if(!row){
if( !warnRowToJsonFailed ){
warnRowToJsonFailed = 1;
json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
| | | 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 |
int const rid = db_column_int(&q,0);
cson_value * rowV = json_artifact_for_ci(rid, verboseFlag);
cson_object * row = cson_value_get_object(rowV);
if(!row){
if( !warnRowToJsonFailed ){
warnRowToJsonFailed = 1;
json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
"Could not convert at least one timeline result row to JSON." );
}
continue;
}
cson_array_append(list, rowV);
}
#undef SET
goto ok;
|
| ︙ | ︙ | |||
546 547 548 549 550 551 552 |
if(check){
json_set_err(check, "Query initialization failed.");
goto error;
}
#if 0
/* only for testing! */
| | > | > > | 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 |
if(check){
json_set_err(check, "Query initialization failed.");
goto error;
}
#if 0
/* only for testing! */
cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),
strlen(blob_buffer(&sql))));
#endif
db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
blob_reset(&sql);
db_prepare(&q, "SELECT"
/* For events, the name is generally more useful than
the uuid, but the uuid is unambiguous and can be used
with commands like 'artifact'. */
" substr((SELECT tagname FROM tag AS tn "
" WHERE tn.tagid=json_timeline.tagId "
" AND tagname LIKE 'event-%%'),7) AS name,"
" uuid as uuid,"
" mtime AS timestamp,"
" comment AS comment, "
" user AS user,"
" eventType AS eventType"
" FROM json_timeline"
" ORDER BY rowid");
|
| ︙ | ︙ | |||
589 590 591 592 593 594 595 |
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
if( !g.perm.RdWiki && !g.perm.Read ){
| | > | > | 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 |
cson_value * payV = NULL;
cson_object * pay = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
if( !g.perm.RdWiki && !g.perm.Read ){
json_set_err( FSL_JSON_E_DENIED,
"Wiki timeline requires 'o' or 'j' access.");
return NULL;
}
payV = cson_value_new_object();
pay = cson_value_get_object(payV);
check = json_timeline_setup_sql( "w", &sql, pay );
if(check){
json_set_err(check, "Query initialization failed.");
goto error;
}
#if 0
/* only for testing! */
cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),
strlen(blob_buffer(&sql))));
#endif
db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
blob_reset(&sql);
db_prepare(&q, "SELECT"
" uuid AS uuid,"
" mtime AS timestamp,"
#if 0
|
| ︙ | ︙ | |||
652 653 654 655 656 657 658 |
cson_value * tmp = NULL;
cson_value * listV = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
if( !g.perm.RdTkt && !g.perm.Read ){
| | > | 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 |
cson_value * tmp = NULL;
cson_value * listV = NULL;
cson_array * list = NULL;
int check = 0;
Stmt q = empty_Stmt;
Blob sql = empty_blob;
if( !g.perm.RdTkt && !g.perm.Read ){
json_set_err(FSL_JSON_E_DENIED,
"Ticket timeline requires 'o' or 'r' access.");
return NULL;
}
payV = cson_value_new_object();
pay = cson_value_get_object(payV);
check = json_timeline_setup_sql( "t", &sql, pay );
if(check){
json_set_err(check, "Query initialization failed.");
|
| ︙ | ︙ | |||
724 725 726 727 728 729 730 |
}
rowV = cson_sqlite3_row_to_object(q.pStmt);
row = cson_value_get_object(rowV);
if(!row){
manifest_destroy(pMan);
json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
| | | 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 |
}
rowV = cson_sqlite3_row_to_object(q.pStmt);
row = cson_value_get_object(rowV);
if(!row){
manifest_destroy(pMan);
json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
"Could not convert at least one timeline result row to JSON." );
continue;
}
/* FIXME: certainly there's a more efficient way for use to get
the ticket UUIDs?
*/
cson_object_set(row,"ticketUuid",json_new_string(pMan->zTicketUuid));
manifest_destroy(pMan);
|
| ︙ | ︙ |
Changes to src/json_user.c.
| ︙ | ︙ | |||
168 169 170 171 172 173 174 |
** Requires either Admin, Setup, or Password access. Non-admin/setup
** users can only change their own information. Non-setup users may
** not modify the 's' permission. Admin users without setup
** permissions may not edit any other user who has the 's' permission.
**
*/
int json_user_update_from_json( cson_object * pUser ){
| | > | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
** Requires either Admin, Setup, or Password access. Non-admin/setup
** users can only change their own information. Non-setup users may
** not modify the 's' permission. Admin users without setup
** permissions may not edit any other user who has the 's' permission.
**
*/
int json_user_update_from_json( cson_object * pUser ){
#define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, \
X ) ))
char const * zName = CSTR("name");
char const * zNameNew = zName;
char * zNameFree = NULL;
char const * zInfo = CSTR("info");
char const * zCap = CSTR("capabilities");
char const * zPW = CSTR("password");
cson_value const * forceLogout = cson_object_get(pUser, "forceLogout");
|
| ︙ | ︙ |
Changes to src/json_wiki.c.
| ︙ | ︙ | |||
161 162 163 164 165 166 167 | } /* ** Searches for the latest version of a wiki page with the given ** name. If found it behaves like json_get_wiki_page_by_rid(theRid, ** contentFormat), else it returns NULL. */ | | > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
}
/*
** Searches for the latest version of a wiki page with the given
** name. If found it behaves like json_get_wiki_page_by_rid(theRid,
** contentFormat), else it returns NULL.
*/
cson_value * json_get_wiki_page_by_name(char const * zPageName,
int contentFormat){
int rid;
rid = db_int(0,
"SELECT x.rid FROM tag t, tagxref x, blob b"
" WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' "
" AND b.rid=x.rid"
" ORDER BY x.mtime DESC LIMIT 1",
zPageName
|
| ︙ | ︙ | |||
257 258 259 260 261 262 263 |
}
zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1);
zSymName = json_find_option_cstr("uuid",NULL,"u");
if((!zPageName||!*zPageName) && (!zSymName || !*zSymName)){
json_set_err(FSL_JSON_E_MISSING_ARGS,
| | | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
}
zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1);
zSymName = json_find_option_cstr("uuid",NULL,"u");
if((!zPageName||!*zPageName) && (!zSymName || !*zSymName)){
json_set_err(FSL_JSON_E_MISSING_ARGS,
"At least one of the 'name' or 'uuid' arguments must be provided.");
return NULL;
}
/* TODO: see if we have a page named zPageName. If not, try to resolve
zPageName as a UUID.
*/
|
| ︙ | ︙ | |||
295 296 297 298 299 300 301 |
zMime = cson_value_get_cstr(cson_object_get(g.json.reqPayload.o,
"mimetype"));
}else{
sContent = cson_value_get_string(g.json.reqPayload.v);
}
if(!sContent) {
json_set_err(FSL_JSON_E_MISSING_ARGS,
| | | | | | > | 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 |
zMime = cson_value_get_cstr(cson_object_get(g.json.reqPayload.o,
"mimetype"));
}else{
sContent = cson_value_get_string(g.json.reqPayload.v);
}
if(!sContent) {
json_set_err(FSL_JSON_E_MISSING_ARGS,
"The 'payload' property must be either a string containing the "
"Fossil wiki code to preview or an object with body + mimetype "
"properties.");
return NULL;
}
zContent = cson_string_cstr(sContent);
blob_append( &contentOrig, zContent, (int)cson_string_length_bytes(sContent));
zMime = wiki_filter_mimetypes(zMime);
if( 0==fossil_strcmp(zMime, "text/x-markdown") ){
markdown_to_html(&contentOrig, 0, &contentHtml);
}else if( 0==fossil_strcmp(zMime, "text/plain") ){
blob_append(&contentHtml, "<pre class='textPlain'>", -1);
blob_append(&contentHtml, blob_str(&contentOrig), blob_size(&contentOrig));
blob_append(&contentHtml, "</pre>", -1);
}else{
wiki_convert( &contentOrig, &contentHtml, 0 );
}
blob_reset( &contentOrig );
pay = cson_value_new_string( blob_str(&contentHtml),
(unsigned int)blob_size(&contentHtml));
blob_reset( &contentHtml );
return pay;
}
/*
** Internal impl of /wiki/save and /wiki/create. If createMode is 0
|
| ︙ | ︙ | |||
344 345 346 347 348 349 350 |
char allowCreateIfNotExists){
Blob content = empty_blob; /* wiki page content */
cson_value * nameV; /* wiki page name */
char const * zPageName; /* cstr form of page name */
cson_value * contentV; /* passed-in content */
cson_value * emptyContent = NULL; /* placeholder for empty content. */
cson_value * payV = NULL; /* payload/return value */
| | > | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
char allowCreateIfNotExists){
Blob content = empty_blob; /* wiki page content */
cson_value * nameV; /* wiki page name */
char const * zPageName; /* cstr form of page name */
cson_value * contentV; /* passed-in content */
cson_value * emptyContent = NULL; /* placeholder for empty content. */
cson_value * payV = NULL; /* payload/return value */
cson_string const * jstr = NULL; /* temp for cson_value-to-cson_string
conversions. */
char const * zMimeType = 0;
unsigned int contentLen = 0;
int rid;
if( (createMode && !g.perm.NewWiki)
|| (!createMode && !g.perm.WrWiki)){
json_set_err(FSL_JSON_E_DENIED,
"Requires '%c' permissions.",
|
| ︙ | ︙ |
Changes to src/loadctrl.c.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
/*
** Abort the current page request if the load average of the host
** computer is too high. Admin and Setup users are exempt from this
** restriction.
*/
void load_control(void){
| | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
/*
** Abort the current page request if the load average of the host
** computer is too high. Admin and Setup users are exempt from this
** restriction.
*/
void load_control(void){
double mxLoad = atof(db_get("max-loadavg", "0.0"));
#if 1
/* Disable this block only to test load restrictions */
if( mxLoad<=0.0 || mxLoad>=load_average() ) return;
login_check_credentials();
if(g.perm.Admin || g.perm.Setup){
return;
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
115 116 117 118 119 120 121 | return zCookieName; } /* ** Redirect to the page specified by the "g" query parameter. ** Or if there is no "g" query parameter, redirect to the homepage. */ | | > > > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
return zCookieName;
}
/*
** Redirect to the page specified by the "g" query parameter.
** Or if there is no "g" query parameter, redirect to the homepage.
*/
NORETURN void login_redirect_to_g(void){
const char *zGoto = P("g");
if( zGoto ){
cgi_redirectf("%R/%s",zGoto);
}else if( (zGoto = P("fossil-goto"))!=0 && zGoto[0]!=0 ){
cgi_set_cookie("fossil-goto","",0,1);
cgi_redirect(zGoto);
}else{
fossil_redirect_home();
}
}
/*
** Return an abbreviated project code. The abbreviation is the first
|
| ︙ | ︙ | |||
149 150 151 152 153 154 155 156 157 158 159 160 |
int login_is_valid_anonymous(
const char *zUsername, /* The username. Must be "anonymous" */
const char *zPassword, /* The supplied password */
const char *zCS /* The captcha seed value */
){
const char *zPw; /* The correct password shown in the captcha */
int uid; /* The user ID of anonymous */
if( zUsername==0 ) return 0;
else if( zPassword==0 ) return 0;
else if( zCS==0 ) return 0;
else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
| > > | > | > > | 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 |
int login_is_valid_anonymous(
const char *zUsername, /* The username. Must be "anonymous" */
const char *zPassword, /* The supplied password */
const char *zCS /* The captcha seed value */
){
const char *zPw; /* The correct password shown in the captcha */
int uid; /* The user ID of anonymous */
int n = 0; /* Counter of captcha-secrets */
if( zUsername==0 ) return 0;
else if( zPassword==0 ) return 0;
else if( zCS==0 ) return 0;
else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
while( 1/*exit-by-break*/ ){
zPw = captcha_decode((unsigned int)atoi(zCS), n);
if( zPw==0 ) return 0;
if( fossil_stricmp(zPw, zPassword)==0 ) break;
n++;
}
uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
" AND octet_length(pw)>0 AND octet_length(cap)>0");
return uid;
}
/*
** Make sure the accesslog table exists. Create it if it does not
|
| ︙ | ︙ | |||
252 253 254 255 256 257 258 |
const char *zLogin = db_column_text(&q,0);
if( (uid = login_search_uid(&zLogin, zPasswd) ) != 0 ){
*pzUsername = fossil_strdup(zLogin);
break;
}
}
db_finalize(&q);
| | | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
const char *zLogin = db_column_text(&q,0);
if( (uid = login_search_uid(&zLogin, zPasswd) ) != 0 ){
*pzUsername = fossil_strdup(zLogin);
break;
}
}
db_finalize(&q);
}
free(zSha1Pw);
return uid;
}
/*
** Generates a login cookie value for a non-anonymous user.
**
|
| ︙ | ︙ | |||
340 341 342 343 344 345 346 | ** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret. ** ** If zCookieDest is not NULL then the generated cookie is assigned to ** *zCookieDest and the caller must eventually free() it. ** ** If bSessionCookie is true, the cookie will be a session cookie. */ | | < | | > | 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 |
** Where HASH is the sha1sum of TIME/SECRET, in which SECRET is captcha-secret.
**
** If zCookieDest is not NULL then the generated cookie is assigned to
** *zCookieDest and the caller must eventually free() it.
**
** If bSessionCookie is true, the cookie will be a session cookie.
*/
void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
char *zNow; /* Current time (julian day number) */
char *zCookie; /* The login cookie */
const char *zCookieName; /* Name of the login cookie */
Blob b; /* Blob used during cookie construction */
int expires = bSessionCookie ? 0 : 6*3600;
zCookieName = login_cookie_name();
zNow = db_text("0", "SELECT julianday('now')");
assert( zCookieName && zNow );
blob_init(&b, zNow, -1);
blob_appendf(&b, "/%z", captcha_secret(0));
sha1sum_blob(&b, &b);
zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
blob_reset(&b);
cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
if( zCookieDest ){
*zCookieDest = zCookie;
}else{
free(zCookie);
}
fossil_free(zNow);
}
/*
** "Unsets" the login cookie (insofar as cookies can be unset) and
** clears the current user's (g.userUid) login information from the
** user table. Sets: user.cookie, user.ipaddr, user.cexpire.
**
|
| ︙ | ︙ | |||
392 393 394 395 396 397 398 |
" 'developer','reader')", g.userUid);
db_protect_pop();
cgi_replace_parameter(cookie, NULL);
cgi_replace_parameter("anon", NULL);
}
}
| < < < < < < < < < < < < < < < < < < | < | | | | < > > > | | 400 401 402 403 404 405 406 407 408 409 410 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 |
" 'developer','reader')", g.userUid);
db_protect_pop();
cgi_replace_parameter(cookie, NULL);
cgi_replace_parameter("anon", NULL);
}
}
/*
** Look at the HTTP_USER_AGENT parameter and try to determine if the user agent
** is a manually operated browser or a bot. When in doubt, assume a bot.
** Return true if we believe the agent is a real person.
*/
static int isHuman(const char *zAgent){
if( zAgent==0 ) return 0; /* If no UserAgent, then probably a bot */
if( strstr(zAgent, "bot")!=0 ) return 0;
if( strstr(zAgent, "spider")!=0 ) return 0;
if( strstr(zAgent, "crawl")!=0 ) return 0;
/* If a URI appears in the User-Agent, it is probably a bot */
if( strstr(zAgent, "http")!=0 ) return 0;
if( strncmp(zAgent, "Mozilla/", 8)==0 ){
if( atoi(&zAgent[8])<4 ) return 0; /* Many bots advertise as Mozilla/3 */
/* Google AI Robot, maybe? */
if( strstr(zAgent, "GoogleOther)")!=0 ) return 0;
/* 2016-05-30: A pernicious spider that likes to walk Fossil timelines has
** been detected on the SQLite website. The spider changes its user-agent
** string frequently, but it always seems to include the following text:
*/
if( strstr(zAgent, "Safari/537.36Mozilla/5.0")!=0 ) return 0;
if( sqlite3_strglob("*Firefox/[1-9]*", zAgent)==0 ) return 1;
if( sqlite3_strglob("*Chrome/[1-9]*", zAgent)==0 ) return 1;
if( sqlite3_strglob("*(compatible;?MSIE?[1789]*", zAgent)==0 ) return 1;
if( sqlite3_strglob("*Trident/[1-9]*;?rv:[1-9]*", zAgent)==0 ){
return 1; /* IE11+ */
}
|
| ︙ | ︙ | |||
598 599 600 601 602 603 604 |
constant_time_cmp_function, 0, 0);
zUsername = P("u");
zPasswd = P("p");
anonFlag = g.zLogin==0 && PB("anon");
/* Handle log-out requests */
if( P("out") && cgi_csrf_safe(2) ){
login_clear_login_data();
| | | 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 |
constant_time_cmp_function, 0, 0);
zUsername = P("u");
zPasswd = P("p");
anonFlag = g.zLogin==0 && PB("anon");
/* Handle log-out requests */
if( P("out") && cgi_csrf_safe(2) ){
login_clear_login_data();
login_redirect_to_g();
return;
}
/* Redirect for create-new-account requests */
if( P("self") ){
cgi_redirectf("%R/register");
return;
|
| ︙ | ︙ | |||
671 672 673 674 675 676 677 |
** Replicate changes above to tag-20230106-2
*/
if( rc ){
zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr);
fossil_free(zErr);
}else{
| | | 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 |
** Replicate changes above to tag-20230106-2
*/
if( rc ){
zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr);
fossil_free(zErr);
}else{
login_redirect_to_g();
return;
}
}
}else{
zErrMsg =
@ <p><span class="loginError">
@ The password cannot be changed for this type of login.
|
| ︙ | ︙ | |||
693 694 695 696 697 698 699 |
if(zUsername==0){
/* Initial login page hit. */
rememberMe = 0;
}else{
rememberMe = P("remember")!=0;
}
if( uid>0 ){
| | | | 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 |
if(zUsername==0){
/* Initial login page hit. */
rememberMe = 0;
}else{
rememberMe = P("remember")!=0;
}
if( uid>0 ){
login_set_anon_cookie(NULL, rememberMe?0:1);
record_login_attempt("anonymous", zIpAddr, 1);
login_redirect_to_g();
}
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
/* Attempting to log in as a user other than anonymous.
*/
uid = login_search_uid(&zUsername, zPasswd);
if( uid<=0 ){
sleep(1);
|
| ︙ | ︙ | |||
719 720 721 722 723 724 725 |
**
** HASH/PROJECT/LOGIN
**
** where HASH is a random hex number, PROJECT is either project
** code prefix, and LOGIN is the user name.
*/
login_set_user_cookie(zUsername, uid, NULL, rememberMe?0:1);
| | | 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 |
**
** HASH/PROJECT/LOGIN
**
** where HASH is a random hex number, PROJECT is either project
** code prefix, and LOGIN is the user name.
*/
login_set_user_cookie(zUsername, uid, NULL, rememberMe?0:1);
login_redirect_to_g();
}
}
style_set_current_feature("login");
style_header("Login/Logout");
style_adunit_config(ADUNIT_OFF);
@ %s(zErrMsg)
if( zGoto && !noAnon ){
|
| ︙ | ︙ | |||
773 774 775 776 777 778 779 |
}else{
zAnonPw = 0;
}
@ <table class="login_out">
if( P("HTTPS")==0 ){
@ <tr><td class="form_label">Warning:</td>
@ <td><span class='securityWarning'>
| | | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 |
}else{
zAnonPw = 0;
}
@ <table class="login_out">
if( P("HTTPS")==0 ){
@ <tr><td class="form_label">Warning:</td>
@ <td><span class='securityWarning'>
@ Login information, including the password,
@ will be sent in the clear over an unencrypted connection.
if( !g.sslNotAvailable ){
@ Consider logging in at
@ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
}
@ </span></td></tr>
}
|
| ︙ | ︙ | |||
819 820 821 822 823 824 825 |
@ <tr>
@ <td></td>
@ <td><input type="submit" name="pwreset" value="Reset My Password">
@ </tr>
}
@ </table>
if( zAnonPw && !noAnon ){
| | | | 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 |
@ <tr>
@ <td></td>
@ <td><input type="submit" name="pwreset" value="Reset My Password">
@ </tr>
}
@ </table>
if( zAnonPw && !noAnon ){
const char *zDecoded = captcha_decode(uSeed, 0);
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
char *zCaptcha = captcha_render(zDecoded);
@ <p><input type="hidden" name="cs" value="%u(uSeed)">
@ Visitors may enter <b>anonymous</b> as the user-ID with
@ the 8-character hexadecimal password shown below:</p>
@ <div class="captcha"><table class="captcha"><tr><td>\
@ <pre class="captcha">
@ %h(zCaptcha)
@ </pre></td></tr></table>
|
| ︙ | ︙ | |||
851 852 853 854 855 856 857 858 859 860 861 862 863 864 |
@ for user <b>%h(g.zLogin)</b></p>
}
if( db_table_exists("repository","forumpost") ){
@ <hr><p>
@ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum
@ post timeline</a> for user <b>%h(g.zLogin)</b></p>
}
if( g.perm.Password ){
char *zRPW = fossil_random_password(12);
@ <hr>
@ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
form_begin(0, "%R/login");
@ <table>
@ <tr><td class="form_label" id="oldpw">Old Password:</td>
| > > > > > > > | 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 |
@ for user <b>%h(g.zLogin)</b></p>
}
if( db_table_exists("repository","forumpost") ){
@ <hr><p>
@ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum
@ post timeline</a> for user <b>%h(g.zLogin)</b></p>
}
}
@ <hr><p>
@ Select your preferred <a href="%R/skins">site skin</a>.
@ </p>
@ <hr><p>
@ Manage your <a href="%R/cookies">cookies</a>.</p>
if( login_is_individual() ){
if( g.perm.Password ){
char *zRPW = fossil_random_password(12);
@ <hr>
@ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
form_begin(0, "%R/login");
@ <table>
@ <tr><td class="form_label" id="oldpw">Old Password:</td>
|
| ︙ | ︙ | |||
1027 1028 1029 1030 1031 1032 1033 |
uid = login_resetpw_suffix_is_valid(zName);
if( uid==0 ){
@ <p><span class="loginError">
@ This password-reset URL is invalid, probably because it has expired.
@ Password-reset URLs have a short lifespan.
@ </span></p>
style_finish_page();
| | | 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 |
uid = login_resetpw_suffix_is_valid(zName);
if( uid==0 ){
@ <p><span class="loginError">
@ This password-reset URL is invalid, probably because it has expired.
@ Password-reset URLs have a short lifespan.
@ </span></p>
style_finish_page();
sleep(1); /* Introduce a small delay on an invalid suffix as an
** extra defense against search attacks */
return;
}
fossil_redirect_to_https_if_needed(1);
login_set_uid(uid, 0);
if( g.perm.Setup || g.perm.Admin || !g.perm.Password || g.zLogin==0 ){
@ <p><span class="loginError">
|
| ︙ | ︙ | |||
1161 1162 1163 1164 1165 1166 1167 |
pStmt = 0;
rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
db_unprotect(PROTECT_USER);
db_multi_exec(
"UPDATE user SET cookie=%Q, cexpire=%.17g"
" WHERE login=%Q",
| | | 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 |
pStmt = 0;
rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
db_unprotect(PROTECT_USER);
db_multi_exec(
"UPDATE user SET cookie=%Q, cexpire=%.17g"
" WHERE login=%Q",
zHash,
sqlite3_column_double(pStmt, 0), zLogin
);
db_protect_pop();
nXfer++;
}
sqlite3_finalize(pStmt);
}
|
| ︙ | ︙ | |||
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 |
cgi_reply();
fossil_exit(0);
}
}
fossil_free(zDecode);
return uid;
}
/*
** This routine examines the login cookie to see if it exists and
** is valid. If the login cookie checks out, it then sets global
** variables appropriately.
**
** g.userUid Database USER.UID value. Might be -1 for "nobody"
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 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 |
cgi_reply();
fossil_exit(0);
}
}
fossil_free(zDecode);
return uid;
}
/*
** SETTING: robot-restrict width=40 block-text
** The VALUE of this setting is a list of GLOB patterns that match
** pages for which complex HTTP requests from robots should be disallowed.
** The recommended value for this setting is:
**
** timeline,vdiff,fdiff,annotate,blame
**
*/
/*
** Check to see if the current HTTP request is a complex request that
** is coming from a robot and if access should restricted for such robots.
** For the purposes of this module, a "complex request" is an HTTP
** request with one or more query parameters other than "name".
**
** If this routine determines that robots should be restricted, then
** this routine publishes a redirect to the honeypot and exits without
** returning to the caller.
**
** This routine believes that this is a complex request is coming from
** a robot if all of the following are true:
**
** * The user is "nobody".
** * Either the REFERER field of the HTTP header is missing or empty,
** or the USERAGENT field of the HTTP header suggests that
** the request as coming from a robot.
** * There are one or more query parameters other than "name".
**
** Robot restrictions are governed by settings.
**
** robot-restrict The value is a list of GLOB patterns for pages
** that should restrict robot access. No restrictions
** are applied if this setting is undefined or is
** an empty string.
*/
void login_restrict_robot_access(void){
const char *zReferer;
const char *zGlob;
int isMatch = 1;
if( g.zLogin!=0 ) return;
zGlob = db_get("robot-restrict",0);
if( zGlob==0 || zGlob[0]==0 ) return;
if( g.isHuman ){
zReferer = P("HTTP_REFERER");
if( zReferer && zReferer[0]!=0 ) return;
}
if( cgi_qp_count()<1 ) return;
isMatch = glob_multi_match(zGlob, g.zPath);
if( !isMatch ) return;
/* If we reach this point, it means we have a situation where we
** want to restrict the activity of a robot.
*/
g.isHuman = 0;
(void)exclude_spiders(0);
cgi_reply();
fossil_exit(0);
}
/*
** When this routine is called, we know that the request does not
** have a login on the present repository. This routine checks to
** see if their login cookie might be for another member of the
** login-group.
**
** If this repository is not a part of any login group, then this
** routine always returns false.
**
** If this repository is part of a login group, and the login cookie
** appears to be well-formed, then return true. That might be a
** false-positive, as we don't actually check to see if the login
** cookie is valid for some other repository. But false-positives
** are ok. This routine is used for robot defense only.
*/
int login_cookie_wellformed(void){
const char *zCookie;
int n;
zCookie = P(login_cookie_name());
if( zCookie==0 ){
return 0;
}
if( !db_exists("SELECT 1 FROM config WHERE name='login-group-code'") ){
return 0;
}
for(n=0; fossil_isXdigit(zCookie[n]); n++){}
return n>48 && zCookie[n]=='/' && zCookie[n+1]!=0;
}
/*
** This routine examines the login cookie to see if it exists and
** is valid. If the login cookie checks out, it then sets global
** variables appropriately.
**
** g.userUid Database USER.UID value. Might be -1 for "nobody"
|
| ︙ | ︙ | |||
1347 1348 1349 1350 1351 1352 1353 |
}else if( fossil_strcmp(zUser, "anonymous")==0 ){
/* Cookies of the form "HASH/TIME/anonymous". The TIME must not be
** too old and the sha1 hash of TIME/SECRET must match HASH.
** SECRET is the "captcha-secret" value in the repository.
*/
double rTime = atof(zArg);
Blob b;
| > > > > | > > | | | | | | | | | | | > > | > > > > > > > > | 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 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 |
}else if( fossil_strcmp(zUser, "anonymous")==0 ){
/* Cookies of the form "HASH/TIME/anonymous". The TIME must not be
** too old and the sha1 hash of TIME/SECRET must match HASH.
** SECRET is the "captcha-secret" value in the repository.
*/
double rTime = atof(zArg);
Blob b;
char *zSecret;
int n = 0;
do{
blob_zero(&b);
zSecret = captcha_secret(n++);
if( zSecret==0 ) break;
blob_appendf(&b, "%s/%s", zArg, zSecret);
sha1sum_blob(&b, &b);
if( fossil_strcmp(zHash, blob_str(&b))==0 ){
uid = db_int(0,
"SELECT uid FROM user WHERE login='anonymous'"
" AND octet_length(cap)>0"
" AND octet_length(pw)>0"
" AND %.17g+0.25>julianday('now')",
rTime
);
}
}while( uid==0 );
blob_reset(&b);
}else{
/* Cookies of the form "HASH/CODE/USER". Search first in the
** local user table, then the user table for project CODE if we
** are part of a login-group.
*/
uid = login_find_user(zUser, zHash);
if( uid==0 && login_transfer_credentials(zUser,zArg,zHash) ){
uid = login_find_user(zUser, zHash);
if( uid ){
record_login_attempt(zUser, zIpAddr, 1);
}else{
/* The login cookie is a valid login for project CODE, but no
** user named USER exists on this repository. Cannot login as
** USER, but at least give them "anonymous" login. */
uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
" AND octet_length(cap)>0"
" AND octet_length(pw)>0");
}
}
}
login_create_csrf_secret(zHash);
}
/* If no user found and the REMOTE_USER environment variable is set,
** then accept the value of REMOTE_USER as the user.
|
| ︙ | ︙ | |||
1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 |
uid = -1;
zCap = "";
}
login_create_csrf_secret("none");
}
login_set_uid(uid, zCap);
}
/*
** Set the current logged in user to be uid. zCap is precomputed
** (override) capabilities. If zCap==0, then look up the capabilities
** in the USER table.
*/
| > > > | 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 |
uid = -1;
zCap = "";
}
login_create_csrf_secret("none");
}
login_set_uid(uid, zCap);
/* Maybe restrict access to robots */
login_restrict_robot_access();
}
/*
** Set the current logged in user to be uid. zCap is precomputed
** (override) capabilities. If zCap==0, then look up the capabilities
** in the USER table.
*/
|
| ︙ | ︙ | |||
1507 1508 1509 1510 1511 1512 1513 |
/* If the public-pages glob pattern is defined and REQUEST_URI matches
** one of the globs in public-pages, then also add in all default-perms
** permissions.
*/
zPublicPages = db_get("public-pages",0);
if( zPublicPages!=0 ){
| < | < | 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 |
/* If the public-pages glob pattern is defined and REQUEST_URI matches
** one of the globs in public-pages, then also add in all default-perms
** permissions.
*/
zPublicPages = db_get("public-pages",0);
if( zPublicPages!=0 ){
const char *zUri = PD("REQUEST_URI","");
zUri += (int)strlen(g.zTop);
if( glob_multi_match(zPublicPages, zUri) ){
login_set_capabilities(db_get("default-perms", "u"), 0);
}
}
return g.zLogin!=0;
}
/*
** Memory of settings
*/
|
| ︙ | ︙ | |||
1578 1579 1580 1581 1582 1583 1584 |
case 'a': p->Admin = p->RdTkt = p->WrTkt = p->Zip =
p->RdWiki = p->WrWiki = p->NewWiki =
p->ApndWiki = p->Hyperlink = p->Clone =
p->NewTkt = p->Password = p->RdAddr =
p->TktFmt = p->Attach = p->ApndTkt =
p->ModWiki = p->ModTkt =
p->RdForum = p->WrForum = p->ModForum =
| | | 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 |
case 'a': p->Admin = p->RdTkt = p->WrTkt = p->Zip =
p->RdWiki = p->WrWiki = p->NewWiki =
p->ApndWiki = p->Hyperlink = p->Clone =
p->NewTkt = p->Password = p->RdAddr =
p->TktFmt = p->Attach = p->ApndTkt =
p->ModWiki = p->ModTkt =
p->RdForum = p->WrForum = p->ModForum =
p->WrTForum = p->AdminForum = p->Chat =
p->EmailAlert = p->Announce = p->Debug = 1;
/* Fall thru into Read/Write */
case 'i': p->Read = p->Write = 1; break;
case 'o': p->Read = 1; break;
case 'z': p->Zip = 1; break;
case 'h': p->Hyperlink = 1; break;
|
| ︙ | ︙ | |||
1825 1826 1827 1828 1829 1830 1831 |
*/
void login_insert_csrf_secret(void){
@ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
}
/*
** Check to see if the candidate username zUserID is already used.
| | | 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 |
*/
void login_insert_csrf_secret(void){
@ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
}
/*
** Check to see if the candidate username zUserID is already used.
** Return 1 if it is already in use. Return 0 if the name is
** available for a self-registeration.
*/
static int login_self_choosen_userid_already_exists(const char *zUserID){
int rc = db_exists(
"SELECT 1 FROM user WHERE login=%Q "
"UNION ALL "
"SELECT 1 FROM event WHERE user=%Q OR euser=%Q",
|
| ︙ | ︙ | |||
1847 1848 1849 1850 1851 1852 1853 |
** searches for a user or subscriber that has that email address. If the
** email address is used no-where in the system, return 0. If the email
** address is assigned to a particular user return the UID for that user.
** If the email address is used, but not by a particular user, return -1.
*/
static int email_address_in_use(const char *zEMail){
int uid;
| | | 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 |
** searches for a user or subscriber that has that email address. If the
** email address is used no-where in the system, return 0. If the email
** address is assigned to a particular user return the UID for that user.
** If the email address is used, but not by a particular user, return -1.
*/
static int email_address_in_use(const char *zEMail){
int uid;
uid = db_int(0,
"SELECT uid FROM user"
" WHERE info LIKE '%%<%q>%%'", zEMail);
if( uid>0 ){
if( db_exists("SELECT 1 FROM user WHERE uid=%d AND ("
" cap GLOB '*[as]*' OR"
" find_emailaddr(info)<>%Q COLLATE nocase)",
uid, zEMail) ){
|
| ︙ | ︙ | |||
1876 1877 1878 1879 1880 1881 1882 | } return uid; } /* ** COMMAND: test-email-used ** Usage: fossil test-email-used EMAIL ... | | | 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 |
}
return uid;
}
/*
** COMMAND: test-email-used
** Usage: fossil test-email-used EMAIL ...
**
** Given a list of email addresses, show the UID and LOGIN associated
** with each one.
*/
void test_email_used(void){
int i;
db_find_and_open_repository(0, 0);
verify_all_options();
|
| ︙ | ︙ | |||
1901 1902 1903 1904 1905 1906 1907 |
}else{
char *zLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid);
fossil_print("%s: UID %d (%s)\n", zEMail, uid, zLogin);
fossil_free(zLogin);
}
}
}
| | < < < < | | | | 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 |
}else{
char *zLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid);
fossil_print("%s: UID %d (%s)\n", zEMail, uid, zLogin);
fossil_free(zLogin);
}
}
}
/*
** Check an email address and confirm that it is valid for self-registration.
** The email address is known already to be well-formed. Return true
** if the email address is on the allowed list.
**
** The default behavior is that any valid email address is accepted.
** But if the "auth-sub-email" setting exists and is not empty, then
** it is a comma-separated list of GLOB patterns for email addresses
** that are authorized to self-register.
*/
int authorized_subscription_email(const char *zEAddr){
char *zGlob = db_get("auth-sub-email",0);
char *zAddr;
int rc;
if( zGlob==0 || zGlob[0]==0 ) return 1;
zGlob = fossil_strtolwr(fossil_strdup(zGlob));
zAddr = fossil_strtolwr(fossil_strdup(zEAddr));
rc = glob_multi_match(zGlob, zAddr);
fossil_free(zGlob);
fossil_free(zAddr);
return rc!=0;
}
/*
** WEBPAGE: register
**
** Page to allow users to self-register. The "self-register" setting
|
| ︙ | ︙ | |||
1993 1994 1995 1996 1997 1998 1999 |
zErr = "Incorrect CAPTCHA";
}else if( strlen(zUserID)<6 ){
iErrLine = 1;
zErr = "User ID too short. Must be at least 6 characters.";
}else if( sqlite3_strglob("*[^-a-zA-Z0-9_.]*",zUserID)==0 ){
iErrLine = 1;
zErr = "User ID may not contain spaces or special characters.";
| | | 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 |
zErr = "Incorrect CAPTCHA";
}else if( strlen(zUserID)<6 ){
iErrLine = 1;
zErr = "User ID too short. Must be at least 6 characters.";
}else if( sqlite3_strglob("*[^-a-zA-Z0-9_.]*",zUserID)==0 ){
iErrLine = 1;
zErr = "User ID may not contain spaces or special characters.";
}else if( sqlite3_strlike("anonymous%", zUserID, 0)==0
|| sqlite3_strlike("nobody%", zUserID, 0)==0
|| sqlite3_strlike("reader%", zUserID, 0)==0
|| sqlite3_strlike("developer%", zUserID, 0)==0
){
iErrLine = 1;
zErr = "This User ID is reserved. Choose something different.";
}else if( zDName[0]==0 ){
|
| ︙ | ︙ | |||
2088 2089 2090 2091 2092 2093 2094 |
/* smip */ g.zIpAddr
);
if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q"
" AND sverified", zEAddr) ){
/* This the case where the user was formerly a verified subscriber
** and here they have also registered as a user as well. It is
** not necessary to repeat the verfication step */
| | | 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 |
/* smip */ g.zIpAddr
);
if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q"
" AND sverified", zEAddr) ){
/* This the case where the user was formerly a verified subscriber
** and here they have also registered as a user as well. It is
** not necessary to repeat the verfication step */
login_redirect_to_g();
}
/* A verification email */
pSender = alert_sender_new(0,0);
blob_init(&hdr,0,0);
blob_init(&body,0,0);
blob_appendf(&hdr, "To: <%s>\n", zEAddr);
blob_appendf(&hdr, "Subject: Subscription verification\n");
|
| ︙ | ︙ | |||
2117 2118 2119 2120 2121 2122 2123 |
alert_sender_free(pSender);
if( zGoto ){
@ <p><a href='%h(zGoto)'>Continue</a>
}
style_finish_page();
return;
}
| | | | 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 |
alert_sender_free(pSender);
if( zGoto ){
@ <p><a href='%h(zGoto)'>Continue</a>
}
style_finish_page();
return;
}
login_redirect_to_g();
}
/* Prepare the captcha. */
if( captchaIsCorrect ){
uSeed = strtoul(P("captchaseed"),0,10);
}else{
uSeed = captcha_seed();
}
zDecoded = captcha_decode(uSeed, 0);
zCaptcha = captcha_render(zDecoded);
style_header("Register");
/* Print out the registration form. */
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
form_begin(0, "%R/register");
if( P("g") ){
|
| ︙ | ︙ | |||
2336 2337 2338 2339 2340 2341 2342 |
/* Prepare the captcha. */
if( captchaIsCorrect ){
uSeed = strtoul(P("captchaseed"),0,10);
}else{
uSeed = captcha_seed();
}
| | | 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 |
/* Prepare the captcha. */
if( captchaIsCorrect ){
uSeed = strtoul(P("captchaseed"),0,10);
}else{
uSeed = captcha_seed();
}
zDecoded = captcha_decode(uSeed, 0);
zCaptcha = captcha_render(zDecoded);
style_header("Request Password Reset");
/* Print out the registration form. */
g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
form_begin(0, "%R/reqpwreset");
@ <p><input type="hidden" name="captchaseed" value="%u(uSeed)">
|
| ︙ | ︙ |
Changes to src/lookslike.c.
| ︙ | ︙ | |||
268 269 270 271 272 273 274 |
const WCHAR_T *z = (WCHAR_T *)blob_buffer(pContent);
unsigned int n = blob_size(pContent);
int j, c, flags = LOOK_NONE; /* Assume UTF-16 text, prove otherwise */
if( n%sizeof(WCHAR_T) ){
flags |= LOOK_ODD; /* Odd number of bytes -> binary (UTF-8?) */
}
| | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
const WCHAR_T *z = (WCHAR_T *)blob_buffer(pContent);
unsigned int n = blob_size(pContent);
int j, c, flags = LOOK_NONE; /* Assume UTF-16 text, prove otherwise */
if( n%sizeof(WCHAR_T) ){
flags |= LOOK_ODD; /* Odd number of bytes -> binary (UTF-8?) */
}
if( n<sizeof(WCHAR_T) ) return flags;/* Zero or One byte -> binary (UTF-8?) */
c = *z;
if( bReverse ){
c = UTF16_SWAP(c);
}
if( c==0 ){
flags |= LOOK_NUL; /* NUL character in a file -> binary */
}else if( c=='\r' ){
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | ** program is invoked. */ #include "VERSION.h" #include "config.h" #if defined(_WIN32) # include <windows.h> # include <io.h> | < | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ** program is invoked. */ #include "VERSION.h" #include "config.h" #if defined(_WIN32) # include <windows.h> # include <io.h> # define GETPID (int)GetCurrentProcessId #endif /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */ #if USE_SEE #if defined(_WIN32) typedef DWORD PID_T; |
| ︙ | ︙ | |||
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
Blob httpHeader; /* Complete text of the HTTP request header */
UrlData url; /* Information about current URL */
const char *zLogin; /* Login name. NULL or "" if not logged in. */
const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
** SSL client identity */
#if USE_SEE
const char *zPidKey; /* Saved value of the --usepidkey option. Only
* applicable when using SEE on Windows or Linux. */
#endif
int useLocalauth; /* No login required if from 127.0.0.1 */
int noPswd; /* Logged in without password (on 127.0.0.1) */
int userUid; /* Integer user id */
int isHuman; /* True if access by a human, not a spider or bot */
int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
** accessed through get_comment_format(). */
/* Information used to populate the RCVFROM table */
int rcvid; /* The rcvid. 0 if not yet defined. */
char *zIpAddr; /* The remote IP address */
char *zNonce; /* The nonce used for login */
/* permissions available to current user */
| > > > > > | 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
Blob httpHeader; /* Complete text of the HTTP request header */
UrlData url; /* Information about current URL */
const char *zLogin; /* Login name. NULL or "" if not logged in. */
const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
** SSL client identity */
const char *zCgiFile; /* Name of the CGI file */
const char *zReqType; /* Type of request: "HTTP", "CGI", "SCGI" */
#if USE_SEE
const char *zPidKey; /* Saved value of the --usepidkey option. Only
* applicable when using SEE on Windows or Linux. */
#endif
int useLocalauth; /* No login required if from 127.0.0.1 */
int noPswd; /* Logged in without password (on 127.0.0.1) */
int userUid; /* Integer user id */
int isHuman; /* True if access by a human, not a spider or bot */
int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
** accessed through get_comment_format(). */
const char *zSockName; /* Name of the unix-domain socket file */
const char *zSockMode; /* File permissions for unix-domain socket */
const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
/* Information used to populate the RCVFROM table */
int rcvid; /* The rcvid. 0 if not yet defined. */
char *zIpAddr; /* The remote IP address */
char *zNonce; /* The nonce used for login */
/* permissions available to current user */
|
| ︙ | ︙ | |||
387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
** exiting the process. This issue does not impact 64-bit Windows.
*/
unloadTcl(g.interp, &g.tcl);
#endif
#ifdef FOSSIL_ENABLE_JSON
cson_value_free(g.json.gc.v);
memset(&g.json, 0, sizeof(g.json));
#endif
free(g.zErrMsg);
if(g.db){
db_close(0);
}
manifest_clear_cache();
content_clear_cache(1);
| > > > > > | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
** exiting the process. This issue does not impact 64-bit Windows.
*/
unloadTcl(g.interp, &g.tcl);
#endif
#ifdef FOSSIL_ENABLE_JSON
cson_value_free(g.json.gc.v);
memset(&g.json, 0, sizeof(g.json));
#endif
#if !defined(_WIN32)
if( g.zSockName && file_issocket(g.zSockName) ){
unlink(g.zSockName);
}
#endif
free(g.zErrMsg);
if(g.db){
db_close(0);
}
manifest_clear_cache();
content_clear_cache(1);
|
| ︙ | ︙ | |||
693 694 695 696 697 698 699 |
const char *zCmdName = "unknown";
const CmdOrPage *pCmd = 0;
int rc;
g.zPhase = "init";
#if !defined(_WIN32_WCE)
if( fossil_getenv("FOSSIL_BREAK") ){
| | | | | 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 |
const char *zCmdName = "unknown";
const CmdOrPage *pCmd = 0;
int rc;
g.zPhase = "init";
#if !defined(_WIN32_WCE)
if( fossil_getenv("FOSSIL_BREAK") ){
if( fossil_isatty(0) && fossil_isatty(2) ){
fprintf(stderr,
"attach debugger to process %d and press any key to continue.\n",
GETPID());
fgetc(stdin);
}else{
#if defined(_WIN32) || defined(WIN32)
DebugBreak();
#elif defined(SIGTRAP)
raise(SIGTRAP);
#endif
}
}
#endif
fossil_printf_selfcheck();
fossil_limit_memory(1);
/* When updating the minimum SQLite version, change the number here,
** and also MINIMUM_SQLITE_VERSION value set in ../auto.def. Take
** care that both places agree! */
if( sqlite3_libversion_number()<3046000
|| strncmp(sqlite3_sourceid(),"2024-08-16",10)<0
){
fossil_panic("Unsuitable SQLite version %s, must be at least 3.43.0",
sqlite3_libversion());
}
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
|
| ︙ | ︙ | |||
850 851 852 853 854 855 856 |
zNewArgv[0] = g.argv[0];
zNewArgv[1] = "ui";
zNewArgv[2] = g.argv[1];
zNewArgv[3] = 0;
g.argc = 3;
g.argv = zNewArgv;
#endif
| | | 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 |
zNewArgv[0] = g.argv[0];
zNewArgv[1] = "ui";
zNewArgv[2] = g.argv[1];
zNewArgv[3] = 0;
g.argc = 3;
g.argv = zNewArgv;
#endif
}
zCmdName = g.argv[1];
}
#ifndef _WIN32
/* There is a bug in stunnel4 in which it sometimes starts up client
** processes without first opening file descriptor 2 (standard error).
** If this happens, and a subsequent open() of a database returns file
** descriptor 2, and then an assert() fires and writes on fd 2, that
|
| ︙ | ︙ | |||
1412 1413 1414 1415 1416 1417 1418 |
/* Remove trailing ":443" from the HOST, if any */
if( i>4 && z[i-1]=='3' && z[i-2]=='4' && z[i-3]=='4' && z[i-4]==':' ){
i -= 4;
}
}else{
/* Remove trailing ":80" from the HOST */
if( i>3 && z[i-1]=='0' && z[i-2]=='8' && z[i-3]==':' ) i -= 3;
| | | 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 |
/* Remove trailing ":443" from the HOST, if any */
if( i>4 && z[i-1]=='3' && z[i-2]=='4' && z[i-3]=='4' && z[i-4]==':' ){
i -= 4;
}
}else{
/* Remove trailing ":80" from the HOST */
if( i>3 && z[i-1]=='0' && z[i-2]=='8' && z[i-3]==':' ) i -= 3;
}
if( i && z[i-1]=='.' ) i--;
z[i] = 0;
zCur = PD("SCRIPT_NAME","/");
i = strlen(zCur);
while( i>0 && zCur[i-1]=='/' ) i--;
if( fossil_stricmp(zMode,"on")==0 ){
g.zBaseURL = mprintf("https://%s%.*s", z, i, zCur);
|
| ︙ | ︙ | |||
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 |
static char *enter_chroot_jail(const char *zRepo, int noJail){
#if !defined(_WIN32)
if( getuid()==0 ){
int i;
struct stat sStat;
Blob dir;
char *zDir;
if( g.db!=0 ){
db_close(1);
}
file_canonical_name(zRepo, &dir, 0);
zDir = blob_str(&dir);
if( !noJail ){
if( file_isdir(zDir, ExtFILE)==1 ){
if( g.zRepositoryName ){
| > > > < | | > > > > > > > > > > > > | 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 |
static char *enter_chroot_jail(const char *zRepo, int noJail){
#if !defined(_WIN32)
if( getuid()==0 ){
int i;
struct stat sStat;
Blob dir;
char *zDir;
size_t nDir;
if( g.db!=0 ){
db_close(1);
}
file_canonical_name(zRepo, &dir, 0);
zDir = blob_str(&dir);
nDir = blob_size(&dir);
if( !noJail ){
if( file_isdir(zDir, ExtFILE)==1 ){
/* Translate the repository name to the new root */
if( g.zRepositoryName ){
Blob repo;
file_canonical_name(g.zRepositoryName, &repo, 0);
zRepo = blob_str(&repo);
if( strncmp(zRepo, zDir, nDir)!=0 ){
fossil_fatal("repo %s not under chroot dir %s", zRepo, zDir);
}
zRepo += nDir;
if( *zRepo == '\0' ) zRepo = "/";
}else {
zRepo = "/";
}
/* If a unix socket is defined, try to translate its name into
** the new root so that it can be delete by atexit(). If unable,
** just zero out the socket name. */
if( g.zSockName ){
if( strncmp(g.zSockName, zDir, nDir)==0
&& g.zSockName[nDir]=='/'
){
g.zSockName += nDir;
}else{
g.zSockName = 0;
}
}
if( file_chdir(zDir, 1) ){
fossil_panic("unable to chroot into %s", zDir);
}
}else{
for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
if( zDir[i]!='/' ) fossil_fatal("bad repository name: %s", zRepo);
|
| ︙ | ︙ | |||
1569 1570 1571 1572 1573 1574 1575 | size_t size; char **strings; size_t i; Blob out; size = backtrace(array, sizeof(array)/sizeof(array[0])); strings = backtrace_symbols(array, size); blob_init(&out, 0, 0); | | > > > > > > > > | > | > | 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 |
size_t size;
char **strings;
size_t i;
Blob out;
size = backtrace(array, sizeof(array)/sizeof(array[0]));
strings = backtrace_symbols(array, size);
blob_init(&out, 0, 0);
blob_appendf(&out, "Segfault during %s in fossil %s",
g.zPhase, MANIFEST_VERSION);
for(i=0; i<size; i++){
size_t len;
const char *z = strings[i];
if( i==0 ) blob_appendf(&out, "\nBacktrace:");
len = strlen(strings[i]);
if( z[0]=='[' && z[len-1]==']' ){
blob_appendf(&out, " %.*s", (int)(len-2), &z[1]);
}else{
blob_appendf(&out, " %s", z);
}
}
fossil_panic("%s", blob_str(&out));
#else
fossil_panic("Segfault during %s in fossil %s",
g.zPhase, MANIFEST_VERSION);
#endif
exit(1);
}
/*
** Called if a server gets a SIGPIPE. This often happens when a client
** webbrowser opens a connection but never sends the HTTP request
|
| ︙ | ︙ | |||
1612 1613 1614 1615 1616 1617 1618 |
if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
if( P("HTTPS")!=0 ) return 0;
return 1;
}
/*
** Redirect to the equivalent HTTPS request if the current connection is
| | | 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 |
if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
if( P("HTTPS")!=0 ) return 0;
return 1;
}
/*
** Redirect to the equivalent HTTPS request if the current connection is
** insecure and if the redirect-to-https flag greater than or equal to
** iLevel. iLevel is 1 for /login pages and 2 for every other page.
*/
int fossil_redirect_to_https_if_needed(int iLevel){
if( fossil_wants_https(iLevel) ){
const char *zQS = P("QUERY_STRING");
char *zURL;
if( zQS==0 || zQS[0]==0 ){
|
| ︙ | ︙ | |||
1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 |
const char *zBase = g.zRepositoryName;
int isReadonly = 0;
g.zPhase = "process_one_web_page";
#if !defined(_WIN32)
signal(SIGSEGV, sigsegv_handler);
#endif
/* Handle universal query parameters */
if( PB("utc") ){
g.fTimeFormat = 1;
}else if( PB("localtime") ){
g.fTimeFormat = 2;
}
| > > > > > > > | 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 |
const char *zBase = g.zRepositoryName;
int isReadonly = 0;
g.zPhase = "process_one_web_page";
#if !defined(_WIN32)
signal(SIGSEGV, sigsegv_handler);
#endif
/* Decode %HH escapes in PATHINFO */
if( strchr(zPathInfo,'%') ){
char *z = fossil_strdup(zPathInfo);
dehttpize(z);
zPathInfo = z;
}
/* Handle universal query parameters */
if( PB("utc") ){
g.fTimeFormat = 1;
}else if( PB("localtime") ){
g.fTimeFormat = 2;
}
|
| ︙ | ︙ | |||
1743 1744 1745 1746 1747 1748 1749 |
zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt);
if( g.fHttpTrace ){
@ <!-- Looking for repository named "%h(zRepo)" -->
fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo);
}
| | | | > > | > > | < > > | > | | > > > | > > > | 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 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 |
zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt);
if( g.fHttpTrace ){
@ <!-- Looking for repository named "%h(zRepo)" -->
fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo);
}
/* Restrictions on the URI for security:
**
** 1. Reject characters that are not ASCII alphanumerics,
** "-", "_", ".", "/", or unicode (above ASCII).
** In other words: No ASCII punctuation or control characters
** other than "-", "_", "." and "/".
** 2. Exception to rule 1: Allow /X:/ where X is any ASCII
** alphabetic character at the beginning of the name on windows.
** 3. "-" may not occur immediately after "/"
** 4. "." may not be adjacent to another "." or to "/"
**
** Any character does not satisfy these constraints a Not Found
** error is returned.
*/
szFile = 0;
for(j=nBase+1, k=0; zRepo[j] && k<i-1; j++, k++){
char c = zRepo[j];
if( c>='a' && c<='z' ) continue;
if( c>='A' && c<='Z' ) continue;
if( c>='0' && c<='9' ) continue;
if( (c&0x80)==0x80 ) continue;
#if defined(_WIN32) || defined(__CYGWIN__)
/* Allow names to begin with "/X:/" on windows */
if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){
continue;
}
#endif
if( c=='/' ) continue;
if( c=='_' ) continue;
if( c=='-' && zRepo[j-1]!='/' ) continue;
if( c=='.'
&& zRepo[j-1]!='.' && zRepo[j-1]!='/'
&& zRepo[j+1]!='.' && zRepo[j+1]!='/'
){
continue;
}
if( c=='.' && g.fAllowACME && j==(int)nBase+1
&& strncmp(&zRepo[j-1],"/.well-known/",12)==0
){
/* We allow .well-known as the top-level directory for ACME */
continue;
|
| ︙ | ︙ | |||
1967 1968 1969 1970 1971 1972 1973 |
zPathInfo += 7;
g.nExtraURL += 7;
cgi_replace_parameter("PATH_INFO", zPathInfo);
cgi_replace_parameter("SCRIPT_NAME", zNewScript);
etag_cancel();
}
| | | 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 |
zPathInfo += 7;
g.nExtraURL += 7;
cgi_replace_parameter("PATH_INFO", zPathInfo);
cgi_replace_parameter("SCRIPT_NAME", zNewScript);
etag_cancel();
}
/* If the content type is application/x-fossil or
** application/x-fossil-debug, then a sync/push/pull/clone is
** desired, so default the PATH_INFO to /xfer
*/
if( g.zContentType &&
strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
/* Special case: If the content mimetype shows that it is "fossil sync"
** payload, then pretend that the PATH_INFO is /xfer so that we always
|
| ︙ | ︙ | |||
2288 2289 2290 2291 2292 2293 2294 | ** from 127.0.0.1 or ::1. ** ** nossl Signal that no SSL connections are available. ** ** nocompress Do not compress HTTP replies. ** ** skin: LABEL Use the built-in skin called LABEL rather than | | > | | 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 | ** from 127.0.0.1 or ::1. ** ** nossl Signal that no SSL connections are available. ** ** nocompress Do not compress HTTP replies. ** ** skin: LABEL Use the built-in skin called LABEL rather than ** the default, or the default if LABEL is empty. ** If there are no skins called LABEL then this ** line is a no-op. ** ** files: GLOBLIST GLOBLIST is a comma-separated list of GLOB ** patterns that specify files that can be ** returned verbatim. This feature allows Fossil ** to act as a web server returning static ** content. ** |
| ︙ | ︙ | |||
2338 2339 2340 2341 2342 2343 2344 |
** significant for "errorlog:", which should be set before "repository:"
** so that any warnings from the database when opening the repository
** go to that log file.
**
** See also: [[http]], [[server]], [[winsrv]]
*/
void cmd_cgi(void){
| < > | | | | 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 |
** significant for "errorlog:", which should be set before "repository:"
** so that any warnings from the database when opening the repository
** go to that log file.
**
** See also: [[http]], [[server]], [[winsrv]]
*/
void cmd_cgi(void){
const char *zNotFound = 0;
char **azRedirect = 0; /* List of repositories to redirect to */
int nRedirect = 0; /* Number of entries in azRedirect */
Glob *pFileGlob = 0; /* Pattern for files */
int allowRepoList = 0; /* Allow lists of repository files */
Blob config, line, key, value, value2;
/* Initialize the CGI environment. */
g.httpOut = stdout;
g.httpIn = stdin;
fossil_binary_mode(g.httpOut);
fossil_binary_mode(g.httpIn);
g.cgiOutput = 1;
g.zReqType = "CGI";
fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
/* Find the name of the CGI control file */
if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
g.zCgiFile = g.argv[2];
}else if( g.argc>=2 ){
g.zCgiFile = g.argv[1];
}else{
cgi_panic("No CGI control file specified");
}
/* Read and parse the CGI control file. */
blob_read_from_file(&config, g.zCgiFile, ExtFILE);
while( blob_line(&config, &line) ){
if( !blob_token(&line, &key) ) continue;
if( blob_buffer(&key)[0]=='#' ) continue;
if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){
/* repository: FILENAME
**
** The name of the Fossil repository to be served via CGI. Most
|
| ︙ | ︙ | |||
2523 2524 2525 2526 2527 2528 2529 |
** the elements of the built-in skin. If LABEL does not match,
** this directive is a silent no-op. It may alternately be
** an absolute path to a directory which holds skin definition
** files (header.txt, footer.txt, etc.). If LABEL is empty,
** the skin stored in the CONFIG db table is used.
*/
blob_token(&line, &value);
| | | 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 |
** the elements of the built-in skin. If LABEL does not match,
** this directive is a silent no-op. It may alternately be
** an absolute path to a directory which holds skin definition
** files (header.txt, footer.txt, etc.). If LABEL is empty,
** the skin stored in the CONFIG db table is used.
*/
blob_token(&line, &value);
fossil_free(skin_use_alternative(blob_str(&value), 1, SKIN_FROM_CGI));
blob_reset(&value);
continue;
}
if( blob_eq(&key, "jsmode:") && blob_token(&line, &value) ){
/* jsmode: MODE
**
** Change how JavaScript resources are delivered with each HTML
|
| ︙ | ︙ | |||
2783 2784 2785 2786 2787 2788 2789 | ** --nocompress Do not compress HTTP replies ** --nodelay Omit backoffice processing if it would delay ** process exit ** --nojail Drop root privilege but do not enter the chroot jail ** --nossl Do not do http: to https: redirects, regardless of ** the redirect-to-https setting. ** --notfound URL Use URL as the "HTTP 404, object not found" page | | | 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 |
** --nocompress Do not compress HTTP replies
** --nodelay Omit backoffice processing if it would delay
** process exit
** --nojail Drop root privilege but do not enter the chroot jail
** --nossl Do not do http: to https: redirects, regardless of
** the redirect-to-https setting.
** --notfound URL Use URL as the "HTTP 404, object not found" page
** --out FILE Write the HTTP reply to FILE instead of to
** standard output
** --pkey FILE Read the private key used for TLS from FILE
** --repolist If REPOSITORY is directory, URL "/" lists all repos
** --scgi Interpret input as SCGI rather than HTTP
** --skin LABEL Use override skin LABEL. Use an empty string ("")
** to force use of the current local skin config.
** --th-trace Trace TH1 execution (for debugging purposes)
|
| ︙ | ︙ | |||
2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 |
noJail = find_option("nojail",0,0)!=0;
allowRepoList = find_option("repolist",0,0)!=0;
g.useLocalauth = find_option("localauth", 0, 0)!=0;
g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
g.fNoHttpCompress = find_option("nocompress",0,0)!=0;
g.zExtRoot = find_option("extroot",0,1);
g.zCkoutAlias = find_option("ckout-alias",0,1);
zInFile = find_option("in",0,1);
if( zInFile ){
backoffice_disable();
g.httpIn = fossil_fopen(zInFile, "rb");
if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile);
}else{
g.httpIn = stdin;
| > | 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 |
noJail = find_option("nojail",0,0)!=0;
allowRepoList = find_option("repolist",0,0)!=0;
g.useLocalauth = find_option("localauth", 0, 0)!=0;
g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
g.fNoHttpCompress = find_option("nocompress",0,0)!=0;
g.zExtRoot = find_option("extroot",0,1);
g.zCkoutAlias = find_option("ckout-alias",0,1);
g.zReqType = "HTTP";
zInFile = find_option("in",0,1);
if( zInFile ){
backoffice_disable();
g.httpIn = fossil_fopen(zInFile, "rb");
if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile);
}else{
g.httpIn = stdin;
|
| ︙ | ︙ | |||
2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 |
g.httpOut = stdout;
#if defined(_WIN32)
_setmode(_fileno(stdout), _O_BINARY);
#endif
}
zIpAddr = find_option("ipaddr",0,1);
useSCGI = find_option("scgi", 0, 0)!=0;
zAltBase = find_option("baseurl", 0, 1);
if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay();
if( zAltBase ) set_base_url(zAltBase);
if( find_option("https",0,0)!=0 ){
zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */
cgi_replace_parameter("HTTPS","on");
}
| > | 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 |
g.httpOut = stdout;
#if defined(_WIN32)
_setmode(_fileno(stdout), _O_BINARY);
#endif
}
zIpAddr = find_option("ipaddr",0,1);
useSCGI = find_option("scgi", 0, 0)!=0;
if( useSCGI ) g.zReqType = "SCGI";
zAltBase = find_option("baseurl", 0, 1);
if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay();
if( zAltBase ) set_base_url(zAltBase);
if( find_option("https",0,0)!=0 ){
zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */
cgi_replace_parameter("HTTPS","on");
}
|
| ︙ | ︙ | |||
2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 |
** and the SSH_CONNECTION environment variable is set. Use the --test
** option on interactive sessions to avoid that special processing when
** using this command interactively over SSH. A better solution would be
** to use a different command for "ssh" sync, but we cannot do that without
** breaking legacy.
**
** Options:
** --test Do not do special "sync" processing when operating
** over an SSH link
** --th-trace Trace TH1 execution (for debugging purposes)
** --usercap CAP User capability string (Default: "sxy")
**
*/
void cmd_test_http(void){
const char *zIpAddr; /* IP address of remote client */
const char *zUserCap;
int bTest = 0;
Th_InitTraceLog();
zUserCap = find_option("usercap",0,1);
| > > | | | | < | > > > | 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 |
** and the SSH_CONNECTION environment variable is set. Use the --test
** option on interactive sessions to avoid that special processing when
** using this command interactively over SSH. A better solution would be
** to use a different command for "ssh" sync, but we cannot do that without
** breaking legacy.
**
** Options:
** --nobody Pretend to be user "nobody"
** --test Do not do special "sync" processing when operating
** over an SSH link
** --th-trace Trace TH1 execution (for debugging purposes)
** --usercap CAP User capability string (Default: "sxy")
**
*/
void cmd_test_http(void){
const char *zIpAddr; /* IP address of remote client */
const char *zUserCap;
int bTest = 0;
Th_InitTraceLog();
zUserCap = find_option("usercap",0,1);
if( !find_option("nobody",0,0) ){
if( zUserCap==0 ){
g.useLocalauth = 1;
zUserCap = "sxy";
}
login_set_capabilities(zUserCap, 0);
}
bTest = find_option("test",0,0)!=0;
g.httpIn = stdin;
g.httpOut = stdout;
fossil_binary_mode(g.httpOut);
fossil_binary_mode(g.httpIn);
g.zExtRoot = find_option("extroot",0,1);
find_server_repository(2, 0);
g.zReqType = "HTTP";
g.cgiOutput = 1;
g.fNoHttpCompress = 1;
g.fullHttpReply = 1;
g.sslNotAvailable = 1; /* Avoid attempts to redirect */
zIpAddr = bTest ? 0 : cgi_ssh_remote_addr(0);
if( zIpAddr && zIpAddr[0] ){
g.fSshClient |= CGI_SSH_CLIENT;
|
| ︙ | ︙ | |||
3008 3009 3010 3011 3012 3013 3014 |
*/
#ifndef _WIN32
static int nAlarmSeconds = 0;
static void sigalrm_handler(int x){
sqlite3_uint64 tmUser = 0, tmKernel = 0;
fossil_cpu_times(&tmUser, &tmKernel);
if( fossil_strcmp(g.zPhase, "web-page reply")==0
| | | | 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 |
*/
#ifndef _WIN32
static int nAlarmSeconds = 0;
static void sigalrm_handler(int x){
sqlite3_uint64 tmUser = 0, tmKernel = 0;
fossil_cpu_times(&tmUser, &tmKernel);
if( fossil_strcmp(g.zPhase, "web-page reply")==0
&& tmUser+tmKernel<10000000
){
/* Do not log time-outs during web-page reply unless more than
** 10 seconds of CPU time has been consumed */
return;
}
fossil_panic("Timeout after %d seconds during %s"
" - user %,llu µs, sys %,llu µs",
nAlarmSeconds, g.zPhase, tmUser, tmKernel);
}
#endif
|
| ︙ | ︙ | |||
3063 3064 3065 3066 3067 3068 3069 | ** This only works for the "fossil ui" command, not the "fossil server" ** command. ** ** If REPOSITORY begins with a "HOST:" or "USER@HOST:" prefix, then ** the command is run on the remote host specified and the results are ** tunneled back to the local machine via SSH. This feature only works for ** the "fossil ui" command, not the "fossil server" command. The name of the | | | < | > > | 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 | ** This only works for the "fossil ui" command, not the "fossil server" ** command. ** ** If REPOSITORY begins with a "HOST:" or "USER@HOST:" prefix, then ** the command is run on the remote host specified and the results are ** tunneled back to the local machine via SSH. This feature only works for ** the "fossil ui" command, not the "fossil server" command. The name of the ** fossil executable on the remote host is specified by the --fossilcmd ** option, or if there is no --fossilcmd, it first tries "fossil" and if it ** is not found in the default $PATH set by SSH on the remote, it then adds ** "$HOME/bin:/usr/local/bin:/opt/homebrew/bin" to the PATH and tries again to ** run "fossil". ** ** REPOSITORY may also be a directory (aka folder) that contains one or ** more repositories with names ending in ".fossil". In this case, a ** prefix of the URL pathname is used to search the directory for an ** appropriate repository. To thwart mischief, the pathname in the URL must ** contain only alphanumerics, "_", "/", "-", and ".", and no "-" may ** occur after "/", and every "." must be surrounded on both sides by |
| ︙ | ︙ | |||
3139 3140 3141 3142 3143 3144 3145 | ** --nojail Drop root privileges but do not enter the chroot jail ** --nossl Do not force redirects to SSL even if the repository ** setting "redirect-to-https" requests it. This is set ** by default for the "ui" command. ** --notfound URL Redirect to URL if a page is not found. ** -p|--page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci" ** --pkey FILE Read the private key used for TLS from FILE | | | > > > > > > > > | 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 |
** --nojail Drop root privileges but do not enter the chroot jail
** --nossl Do not force redirects to SSL even if the repository
** setting "redirect-to-https" requests it. This is set
** by default for the "ui" command.
** --notfound URL Redirect to URL if a page is not found.
** -p|--page PAGE Start "ui" on PAGE. ex: --page "timeline?y=ci"
** --pkey FILE Read the private key used for TLS from FILE
** -P|--port [IP:]PORT Listen on the given IP (optional) and port
** --repolist If REPOSITORY is dir, URL "/" lists repos
** --scgi Accept SCGI rather than HTTP
** --skin LABEL Use override skin LABEL, or the site's default skin if
** LABEL is an empty string.
** --socket-mode MODE File permissions to set for the unix socket created
** by the --socket-name option.
** --socket-name NAME Use a unix-domain socket called NAME instead of a
** TCP/IP socket.
** --socket-owner USR Try to set the owner of the unix socket to USR.
** USR can be of the form USER:GROUP to set both
** user and group.
** --th-trace Trace TH1 execution (for debugging purposes)
** --usepidkey Use saved encryption key from parent process. This is
** only necessary when using SEE on Windows or Linux.
**
** See also: [[cgi]], [[http]], [[winsrv]]
*/
void cmd_webserver(void){
|
| ︙ | ︙ | |||
3165 3166 3167 3168 3169 3170 3171 | const char *zChRoot; /* Use for chroot instead of repository path */ int noJail; /* Do not enter the chroot jail */ const char *zTimeout = 0; /* Max runtime of any single HTTP request */ #endif int allowRepoList; /* List repositories on URL "/" */ const char *zAltBase; /* Argument to the --baseurl option */ const char *zFileGlob; /* Static content must match this */ | | | | 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 | const char *zChRoot; /* Use for chroot instead of repository path */ int noJail; /* Do not enter the chroot jail */ const char *zTimeout = 0; /* Max runtime of any single HTTP request */ #endif int allowRepoList; /* List repositories on URL "/" */ const char *zAltBase; /* Argument to the --baseurl option */ const char *zFileGlob; /* Static content must match this */ char *zIpAddr = 0; /* Bind to this IP address or UN socket */ int fCreate = 0; /* The --create flag */ int fNoBrowser = 0; /* Do not auto-launch web-browser */ const char *zInitPage = 0; /* Start on this page. --page option */ int findServerArg = 2; /* argv index for find_server_repository() */ char *zRemote = 0; /* Remote host on which to run "fossil ui" */ const char *zJsMode; /* The --jsmode parameter */ const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ #if USE_SEE db_setup_for_saved_encryption_key(); #endif #if defined(_WIN32) const char *zStopperFile; /* Name of file used to terminate server */ |
| ︙ | ︙ | |||
3218 3219 3220 3221 3222 3223 3224 |
zFossilCmd = find_option("fossilcmd", 0, 1);
}
zNotFound = find_option("notfound", 0, 1);
allowRepoList = find_option("repolist",0,0)!=0;
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
zAltBase = find_option("baseurl", 0, 1);
fCreate = find_option("create",0,0)!=0;
| > | > > > > > > > > > > > > > > > > > > | 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 |
zFossilCmd = find_option("fossilcmd", 0, 1);
}
zNotFound = find_option("notfound", 0, 1);
allowRepoList = find_option("repolist",0,0)!=0;
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
zAltBase = find_option("baseurl", 0, 1);
fCreate = find_option("create",0,0)!=0;
g.zReqType = "HTTP";
if( find_option("scgi", 0, 0)!=0 ){
g.zReqType = "SCGI";
flags |= HTTP_SERVER_SCGI;
}
if( zAltBase ){
set_base_url(zAltBase);
}
g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd;
fNoBrowser = find_option("nobrowser", "B", 0)!=0;
decode_ssl_options();
if( find_option("https",0,0)!=0 || g.httpUseSSL ){
cgi_replace_parameter("HTTPS","on");
}
if( find_option("localhost", 0, 0)!=0 ){
flags |= HTTP_SERVER_LOCALHOST;
}
g.zCkoutAlias = find_option("ckout-alias",0,1);
g.zMainMenuFile = find_option("mainmenu",0,1);
if( g.zMainMenuFile!=0 && file_size(g.zMainMenuFile,ExtFILE)<0 ){
fossil_fatal("Cannot read --mainmenu file %s", g.zMainMenuFile);
}
if( find_option("acme",0,0)!=0 ) g.fAllowACME = 1;
g.zSockMode = find_option("socket-mode",0,1);
g.zSockName = find_option("socket-name",0,1);
g.zSockOwner = find_option("socket-owner",0,1);
if( g.zSockName ){
#if defined(_WIN32)
fossil_fatal("unix sockets are not supported on Windows");
#endif
if( zPort ){
fossil_fatal("cannot specify a port number for a unix socket");
}
if( isUiCmd && !fNoBrowser ){
fossil_fatal("cannot start a web-browser on a unix socket");
}
flags |= HTTP_SERVER_UNIXSOCKET;
}
/* Undocumented option: --debug-nofork
**
** This sets the HTTP_SERVER_NOFORK flag, which causes only the
** very first incoming TCP/IP connection to be processed. Used for
** debugging, since debugging across a fork() can be tricky
*/
|
| ︙ | ︙ | |||
3341 3342 3343 3344 3345 3346 3347 3348 3349 |
}
if( zRemote ){
/* If a USER@HOST:REPO argument is supplied, then use SSH to run
** "fossil ui --nobrowser" on the remote system and to set up a
** tunnel from the local machine to the remote. */
FILE *sshIn;
Blob ssh;
char zLine[1000];
blob_init(&ssh, 0, 0);
| > > > > > | < | | | | | > > > | | | | | | | | | | | | | > > > > | | | | | | | | | > > > > > > | | | | | | | > | > | | 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 |
}
if( zRemote ){
/* If a USER@HOST:REPO argument is supplied, then use SSH to run
** "fossil ui --nobrowser" on the remote system and to set up a
** tunnel from the local machine to the remote. */
FILE *sshIn;
Blob ssh;
int bRunning = 0; /* True when fossil starts up on the remote */
int isRetry; /* True if on the second attempt */
char zLine[1000];
blob_init(&ssh, 0, 0);
for(isRetry=0; isRetry<2 && !bRunning; isRetry++){
blob_reset(&ssh);
transport_ssh_command(&ssh);
blob_appendf(&ssh,
" -t -L 127.0.0.1:%d:127.0.0.1:%d %!$",
iPort, iPort, zRemote
);
if( zFossilCmd==0 ){
if( ssh_needs_path_argument(zRemote,-1) ^ isRetry ){
ssh_add_path_argument(&ssh);
}
blob_append_escaped_arg(&ssh, "fossil", 1);
}else{
blob_appendf(&ssh, " %$", zFossilCmd);
}
blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias);
if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
if( fCreate ) blob_appendf(&ssh, " --create");
blob_appendf(&ssh, " %$", g.argv[2]);
if( isRetry ){
fossil_print("First attempt to run \"fossil\" on %s failed\n"
"Retry: ", zRemote);
}
fossil_print("%s\n", blob_str(&ssh));
sshIn = popen(blob_str(&ssh), "r");
if( sshIn==0 ){
fossil_fatal("unable to %s", blob_str(&ssh));
}
while( fgets(zLine, sizeof(zLine), sshIn) ){
fputs(zLine, stdout);
fflush(stdout);
if( !bRunning && sqlite3_strglob("*Listening for HTTP*",zLine)==0 ){
bRunning = 1;
if( isRetry ){
ssh_needs_path_argument(zRemote,99);
}
db_close_config();
if( zBrowserCmd ){
char *zCmd = mprintf(zBrowserCmd/*works-like:"%d"*/,iPort);
fossil_system(zCmd);
fossil_free(zCmd);
fossil_free(zBrowserCmd);
zBrowserCmd = 0;
}
}
}
pclose(sshIn);
}
fossil_free(zBrowserCmd);
return;
}
if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
db_close(1);
#if !defined(_WIN32)
if( 1 ){
/* Modern kernels suppress SIGTERM to PID 1 to prevent root from
** rebooting the system by nuking the init system. The only way
** Fossil becomes that PID 1 is when it's running solo in a Linux
** container or similar, so we do want to exit immediately, to
** allow the container to shut down quickly.
**
** This has to happen ahead of the other signal() calls below.
|
| ︙ | ︙ | |||
3410 3411 3412 3413 3414 3415 3416 |
/* Start up an HTTP server
*/
fossil_setenv("SERVER_SOFTWARE", "fossil version " RELEASE_VERSION
" " MANIFEST_VERSION " " MANIFEST_DATE);
#if !defined(_WIN32)
/* Unix implementation */
if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
| | | 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 |
/* Start up an HTTP server
*/
fossil_setenv("SERVER_SOFTWARE", "fossil version " RELEASE_VERSION
" " MANIFEST_VERSION " " MANIFEST_DATE);
#if !defined(_WIN32)
/* Unix implementation */
if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
fossil_fatal("unable to listen on CGI socket");
}
/* For the parent process, the cgi_http_server() command above never
** returns (except in the case of an error). Instead, for each incoming
** client connection, a child process is created, file descriptors 0
** and 1 are bound to that connection, and the child returns.
**
** So, when control reaches this point, we are running as a
|
| ︙ | ︙ | |||
3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 |
if( g.httpUseSSL && g.httpSSLConn ){
ssl_close_server(g.httpSSLConn);
g.httpSSLConn = 0;
}
#endif /* FOSSIL_ENABLE_SSL */
#else /* WIN32 */
/* Win32 implementation */
if( allowRepoList ){
flags |= HTTP_SERVER_REPOLIST;
}
if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
zAltBase, zNotFound, zFileGlob, zIpAddr, flags);
| > > > > | 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 |
if( g.httpUseSSL && g.httpSSLConn ){
ssl_close_server(g.httpSSLConn);
g.httpSSLConn = 0;
}
#endif /* FOSSIL_ENABLE_SSL */
#else /* WIN32 */
find_server_repository(2, 0);
if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
allowRepoList = 1;
}
/* Win32 implementation */
if( allowRepoList ){
flags |= HTTP_SERVER_REPOLIST;
}
if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
zAltBase, zNotFound, zFileGlob, zIpAddr, flags);
|
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
189 190 191 192 193 194 195 196 197 198 199 200 201 202 | $(SRCDIR)/../skins/default/details.txt \ $(SRCDIR)/../skins/default/footer.txt \ $(SRCDIR)/../skins/default/header.txt \ $(SRCDIR)/../skins/eagle/css.txt \ $(SRCDIR)/../skins/eagle/details.txt \ $(SRCDIR)/../skins/eagle/footer.txt \ $(SRCDIR)/../skins/eagle/header.txt \ $(SRCDIR)/../skins/khaki/css.txt \ $(SRCDIR)/../skins/khaki/details.txt \ $(SRCDIR)/../skins/khaki/footer.txt \ $(SRCDIR)/../skins/khaki/header.txt \ $(SRCDIR)/../skins/original/css.txt \ $(SRCDIR)/../skins/original/details.txt \ $(SRCDIR)/../skins/original/footer.txt \ | > > > > | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | $(SRCDIR)/../skins/default/details.txt \ $(SRCDIR)/../skins/default/footer.txt \ $(SRCDIR)/../skins/default/header.txt \ $(SRCDIR)/../skins/eagle/css.txt \ $(SRCDIR)/../skins/eagle/details.txt \ $(SRCDIR)/../skins/eagle/footer.txt \ $(SRCDIR)/../skins/eagle/header.txt \ $(SRCDIR)/../skins/etienne/css.txt \ $(SRCDIR)/../skins/etienne/details.txt \ $(SRCDIR)/../skins/etienne/footer.txt \ $(SRCDIR)/../skins/etienne/header.txt \ $(SRCDIR)/../skins/khaki/css.txt \ $(SRCDIR)/../skins/khaki/details.txt \ $(SRCDIR)/../skins/khaki/footer.txt \ $(SRCDIR)/../skins/khaki/header.txt \ $(SRCDIR)/../skins/original/css.txt \ $(SRCDIR)/../skins/original/details.txt \ $(SRCDIR)/../skins/original/footer.txt \ |
| ︙ | ︙ | |||
265 266 267 268 269 270 271 272 273 274 275 276 277 278 | $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.chat.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.pikchrshow.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ | > | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.chat.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.pikchrshow.css \ $(SRCDIR)/style.uvlist.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ |
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
1224 1225 1226 1227 1228 1229 1230 | ** control artifact. Make a copy, and run it through the official ** artifact parser. This is the slow path, but it is rarely taken. */ blob_init(©, 0, 0); blob_init(&errmsg, 0, 0); blob_append(©, zIn, nIn); pManifest = manifest_parse(©, 0, &errmsg); | | | 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 | ** control artifact. Make a copy, and run it through the official ** artifact parser. This is the slow path, but it is rarely taken. */ blob_init(©, 0, 0); blob_init(&errmsg, 0, 0); blob_append(©, zIn, nIn); pManifest = manifest_parse(©, 0, &errmsg); iRes = pManifest!=0; manifest_destroy(pManifest); blob_reset(&errmsg); return iRes; } /* ** COMMAND: test-parse-manifest |
| ︙ | ︙ | |||
1336 1337 1338 1339 1340 1341 1342 |
id, blob_str(&err));
nErr++;
}else if( !isWF && p!=0 ){
fossil_print("%d ERROR: manifest_is_well_formed() reported false "
"but manifest_parse() found nothing wrong.\n", id);
nErr++;
}
| | | 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 |
id, blob_str(&err));
nErr++;
}else if( !isWF && p!=0 ){
fossil_print("%d ERROR: manifest_is_well_formed() reported false "
"but manifest_parse() found nothing wrong.\n", id);
nErr++;
}
}else{
p = manifest_get(id, CFTYPE_ANY, &err);
if( p==0 ){
fossil_print("%d ERROR: %s\n", id, blob_str(&err));
nErr++;
}
}
blob_reset(&err);
|
| ︙ | ︙ | |||
2111 2112 2113 2114 2115 2116 2117 |
** Activate EVENT triggers if they do not already exist.
*/
void manifest_create_event_triggers(void){
if( manifest_event_triggers_are_enabled ){
return; /* Triggers already exists. No-op. */
}
alert_create_trigger();
| | | 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 |
** Activate EVENT triggers if they do not already exist.
*/
void manifest_create_event_triggers(void){
if( manifest_event_triggers_are_enabled ){
return; /* Triggers already exists. No-op. */
}
alert_create_trigger();
manifest_event_triggers_are_enabled = 1;
}
/*
** Disable manifest event triggers. Drop them if they exist, but mark
** them has having been created so that they won't be recreated. This
** is used during "rebuild" to prevent triggers from firing then.
*/
|
| ︙ | ︙ |
Changes to src/markdown.c.
| ︙ | ︙ | |||
62 63 64 65 66 67 68 |
void (*paragraph)(struct Blob *ob, struct Blob *text, void *opaque);
void (*table)(struct Blob *ob, struct Blob *head_row, struct Blob *rows,
void *opaque);
void (*table_cell)(struct Blob *ob, struct Blob *text, int flags,
void *opaque);
void (*table_row)(struct Blob *ob, struct Blob *cells, int flags,
void *opaque);
| | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
void (*paragraph)(struct Blob *ob, struct Blob *text, void *opaque);
void (*table)(struct Blob *ob, struct Blob *head_row, struct Blob *rows,
void *opaque);
void (*table_cell)(struct Blob *ob, struct Blob *text, int flags,
void *opaque);
void (*table_row)(struct Blob *ob, struct Blob *cells, int flags,
void *opaque);
void (*footnote_item)(struct Blob *ob, const struct Blob *text,
int index, int nUsed, void *opaque);
/* span level callbacks - NULL or return 0 prints the span verbatim */
int (*autolink)(struct Blob *ob, struct Blob *link,
enum mkd_autolink type, void *opaque);
int (*codespan)(struct Blob *ob, struct Blob *text, int nSep, void *opaque);
int (*double_emphasis)(struct Blob *ob, struct Blob *text,
|
| ︙ | ︙ | |||
380 381 382 383 384 385 386 |
/* release the given working buffer back to the cache */
static void release_work_buffer(struct render *rndr, struct Blob *buf){
if( !buf ) return;
rndr->iDepth--;
blob_reset(buf);
| > | | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
/* release the given working buffer back to the cache */
static void release_work_buffer(struct render *rndr, struct Blob *buf){
if( !buf ) return;
rndr->iDepth--;
blob_reset(buf);
if( rndr->nBlobCache <
(int)(sizeof(rndr->aBlobCache)/sizeof(rndr->aBlobCache[0])) ){
rndr->aBlobCache[rndr->nBlobCache++] = buf;
}else{
fossil_free(buf);
}
}
|
| ︙ | ︙ | |||
1615 1616 1617 1618 1619 1620 1621 |
/* parse_blockquote -- handles parsing of a blockquote fragment */
static size_t parse_blockquote(
struct Blob *ob,
struct render *rndr,
char *data,
size_t size
){
| | > > > > > > > > > > > > > > > > > > > > > > > > > | | 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 |
/* parse_blockquote -- handles parsing of a blockquote fragment */
static size_t parse_blockquote(
struct Blob *ob,
struct render *rndr,
char *data,
size_t size
){
size_t beg, end = 0, pre, work_size = 0, nb, endFence = 0;
char *work_data = 0;
struct Blob *out = new_work_buffer(rndr);
/* Check to see if this is a quote of a fenced code block, because
** if it is, then blank lines do not terminated the quoted text. Ex:
**
** > ~~~~
** First line
**
** Line after blank
** ~~~~
**
** If this is a quoted fenced block, then set endFence to be the
** offset of the end of the fenced block.
*/
pre = prefix_quote(data,size);
pre += is_empty(data+pre,size-pre);
nb = prefix_fencedcode(data+pre,size-pre);
if( nb ){
size_t i = 0;
char delim = data[pre];
for(end=pre+nb; end<size && i<nb; end++){
if( data[end]==delim ) i++; else i = 0;
}
if( i>=nb ) endFence = end;
}
beg = 0;
while( beg<size ){
for(end=beg+1; end<size && data[end-1]!='\n'; end++);
pre = prefix_quote(data+beg, end-beg);
if( pre ){
beg += pre; /* skipping prefix */
}else if( is_empty(data+beg, end-beg)
&& (end>=size
|| (end>endFence
&& prefix_quote(data+end, size-end)==0
&& !is_empty(data+end, size-end)))
){
/* empty line followed by non-quote line */
break;
}
if( beg<end ){ /* copy into the in-place working buffer */
if( !work_data ){
|
| ︙ | ︙ | |||
2343 2344 2345 2346 2347 2348 2349 |
beg += parse_blockcode(ob, rndr, txt_data, end);
}else if( prefix_uli(txt_data, end) ){
beg += parse_list(ob, rndr, txt_data, end, 0);
}else if( prefix_oli(txt_data, end) ){
beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);
}else if( has_table && is_tableline(txt_data, end) ){
beg += parse_table(ob, rndr, txt_data, end);
| | | 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 |
beg += parse_blockcode(ob, rndr, txt_data, end);
}else if( prefix_uli(txt_data, end) ){
beg += parse_list(ob, rndr, txt_data, end, 0);
}else if( prefix_oli(txt_data, end) ){
beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);
}else if( has_table && is_tableline(txt_data, end) ){
beg += parse_table(ob, rndr, txt_data, end);
}else if( prefix_fencedcode(txt_data, end)
&& (i = char_codespan(ob, rndr, txt_data, 0, end))!=0
){
beg += i;
}else{
beg += parse_paragraph(ob, rndr, txt_data, end);
}
}
|
| ︙ | ︙ |
Changes to src/markdown.md.
| ︙ | ︙ | |||
128 129 130 131 132 133 134 |
|:Left-aligned |:Centered :| Right-aligned:|
| | ← Blank → | |
| Row 4 Col 1 | Row 4 Col 2 | Row 4 Col 3 |
> The first row is a header if followed by a horizontal rule or a blank line.
> Placing **:** at the left, both, or right sides of a cell gives left-aligned,
| | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
|:Left-aligned |:Centered :| Right-aligned:|
| | ← Blank → | |
| Row 4 Col 1 | Row 4 Col 2 | Row 4 Col 3 |
> The first row is a header if followed by a horizontal rule or a blank line.
> Placing **:** at the left, both, or right sides of a cell gives left-aligned,
> centered, or right-aligned text, respectively. By default, both header and
> body cells are left-aligned.
> The leftmost or rightmost **\|** is required only if the first or last column,
> respectively, contains at least one blank cell.
## Diagrams ##
>
|
| ︙ | ︙ |
Changes to src/markdown_html.c.
| ︙ | ︙ | |||
304 305 306 307 308 309 310 |
if( flags & MKD_CELL_HEAD ){
blob_append_literal(ob, " <th");
}else{
blob_append_literal(ob, " <td");
}
switch( flags & MKD_CELL_ALIGN_MASK ){
case MKD_CELL_ALIGN_LEFT: {
| | | | | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
if( flags & MKD_CELL_HEAD ){
blob_append_literal(ob, " <th");
}else{
blob_append_literal(ob, " <td");
}
switch( flags & MKD_CELL_ALIGN_MASK ){
case MKD_CELL_ALIGN_LEFT: {
blob_append_literal(ob, " style=\"text-align:left\"");
break;
}
case MKD_CELL_ALIGN_RIGHT: {
blob_append_literal(ob, " style=\"text-align:right\"");
break;
}
case MKD_CELL_ALIGN_CENTER: {
blob_append_literal(ob, " style=\"text-align:center\"");
break;
}
}
blob_append_literal(ob, ">");
blob_appendb(ob, text);
if( flags & MKD_CELL_HEAD ){
blob_append_literal(ob, "</th>\n");
|
| ︙ | ︙ | |||
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 |
html_escape(ob, blob_buffer(link)+7, blob_size(link)-7);
}else{
html_escape(ob, blob_buffer(link), blob_size(link));
}
blob_append_literal(ob, "</a>");
return 1;
}
/*
** The nSrc bytes at zSrc[] are Pikchr input text (allegedly). Process that
** text and insert the result in place of the original.
*/
void pikchr_to_html(
Blob *ob, /* Write the generated SVG here */
const char *zSrc, int nSrc, /* The Pikchr source text */
const char *zArg, int nArg /* Addition arguments */
){
int pikFlags = PIKCHR_PROCESS_NONCE
| PIKCHR_PROCESS_DIV
| PIKCHR_PROCESS_SRC
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 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 647 648 649 650 651 652 653 654 |
html_escape(ob, blob_buffer(link)+7, blob_size(link)-7);
}else{
html_escape(ob, blob_buffer(link), blob_size(link));
}
blob_append_literal(ob, "</a>");
return 1;
}
/*
** Flags for use with/via pikchr_to_html_add_flags().
*/
static int pikchrToHtmlFlags = 0;
/*
** Sets additional pikchr_process() flags to use for all future calls
** to pikch_to_html(). This is intended to be used by commands such as
** test-wiki-render and test-markdown-render to set the
** PIKCHR_PROCESS_DARK_MODE flag for all embedded pikchr elements.
**
** Not all PIKCHR_PROCESS flags are legal, as pikchr_to_html()
** hard-codes a subset of flags and passing arbitrary flags here may
** interfere with that.
**
** The only tested/intended use of this function is to pass it either
** 0 or PIKCHR_PROCESS_DARK_MODE.
**
** Design note: this is not implemented as an additional argument to
** pikchr_to_html() because the commands for which dark-mode rendering
** are now supported (test-wiki-render and test-markdown-render) are
** far removed from their corresponding pikchr_to_html() calls and
** there is no direct path from those commands to those calls. A
** cleaner, but much more invasive, approach would be to add a flag to
** markdown_to_html(), extend the WIKI_... flags with
** WIKI_DARK_PIKCHR, and extend both wiki.c:Renderer and
** markdown_html.c:MarkdownToHtml to contain and pass on that flag.
*/
void pikchr_to_html_add_flags( int f ){
pikchrToHtmlFlags = f;
}
/*
** The nSrc bytes at zSrc[] are Pikchr input text (allegedly). Process that
** text and insert the result in place of the original.
*/
void pikchr_to_html(
Blob *ob, /* Write the generated SVG here */
const char *zSrc, int nSrc, /* The Pikchr source text */
const char *zArg, int nArg /* Addition arguments */
){
int pikFlags = PIKCHR_PROCESS_NONCE
| PIKCHR_PROCESS_DIV
| PIKCHR_PROCESS_SRC
| PIKCHR_PROCESS_ERR_PRE
| pikchrToHtmlFlags;
Blob bSrc = empty_blob;
const char *zPikVar;
double rPikVar;
while( nArg>0 ){
int i;
for(i=0; i<nArg && !fossil_isspace(zArg[i]); i++){}
|
| ︙ | ︙ | |||
778 779 780 781 782 783 784 |
){
char *zLink = blob_buffer(link);
char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0;
char zClose[20];
if( zLink==0 || zLink[0]==0 ){
zClose[0] = 0;
| | | | 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 |
){
char *zLink = blob_buffer(link);
char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0;
char zClose[20];
if( zLink==0 || zLink[0]==0 ){
zClose[0] = 0;
}else{
static const int flags =
WIKI_NOBADLINKS |
WIKI_MARKDOWNLINKS
;
wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle);
}
if( blob_size(content)==0 ){
if( link ) blob_appendb(ob, link);
|
| ︙ | ︙ |
Changes to src/merge.c.
| ︙ | ︙ | |||
134 135 136 137 138 139 140 | /* ** Add an entry to the FV table for all files renamed between ** version N and the version specified by vid. */ static void add_renames( const char *zFnCol, /* The FV column for the filename in vid */ int vid, /* The desired version's- RID */ | | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
/*
** Add an entry to the FV table for all files renamed between
** version N and the version specified by vid.
*/
static void add_renames(
const char *zFnCol, /* The FV column for the filename in vid */
int vid, /* The desired version's- RID */
int nid, /* The check-in rid for the name pivot */
int revOK, /* OK to move backwards (child->parent) if true */
const char *zDebug /* Generate trace output if not NULL */
){
int nChng; /* Number of file name changes */
int *aChng; /* An array of file name changes */
int i; /* Loop counter */
find_filename_changes(nid, vid, revOK, &nChng, &aChng, zDebug);
|
| ︙ | ︙ | |||
266 267 268 269 270 271 272 |
*/
void test_show_vfile_cmd(void){
if( g.argc!=2 ){
fossil_fatal("unknown arguments to the %s command\n", g.argv[1]);
}
verify_all_options();
db_must_be_within_tree();
| | | > | > | | > > > > | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
*/
void test_show_vfile_cmd(void){
if( g.argc!=2 ){
fossil_fatal("unknown arguments to the %s command\n", g.argv[1]);
}
verify_all_options();
db_must_be_within_tree();
debug_show_vfile();
}
/*
** COMMAND: merge
** COMMAND: cherry-pick alias
** COMMAND: cherrypick
**
** Usage: %fossil merge ?OPTIONS? ?VERSION ...?
** Or: %fossil cherrypick ?OPTIONS? ?VERSION ...?
**
** The argument VERSION is a version that should be merged into the
** current check-out. All changes from VERSION back to the nearest
** common ancestor are merged. Except, if either of the --cherrypick
** or --backout options are used only the changes associated with the
** single check-in VERSION are merged. The --backout option causes
** the changes associated with VERSION to be removed from the current
** check-out rather than added. When invoked with the name
** "cherrypick" instead of "merge", this command works exactly like
** "merge --cherrypick".
**
** Files which are renamed in the merged-in branch will be renamed in
** the current check-out.
**
** If the VERSION argument is omitted, then Fossil attempts to find
** a recent fork on the current branch to merge.
**
** If there are multiple VERSION arguments, then each VERSION is merged
** (or cherrypicked) in the order that they appear on the command-line.
**
** Options:
** --backout Do a reverse cherrypick merge against VERSION.
** In other words, back out the changes that were
** added by VERSION.
** --baseline BASELINE Use BASELINE as the "pivot" of the merge instead
** of the nearest common ancestor. This allows
|
| ︙ | ︙ | |||
313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
** -f|--force Force the merge even if it would be a no-op
** --force-missing Force the merge even if there is missing content
** --integrate Merged branch will be closed when committing
** -K|--keep-merge-files On merge conflict, retain the temporary files
** used for merging, named *-baseline, *-original,
** and *-merge.
** -n|--dry-run If given, display instead of run actions
** -v|--verbose Show additional details of the merge
*/
void merge_cmd(void){
int vid; /* Current version "V" */
int mid; /* Version we are merging from "M" */
int pid = 0; /* The pivot version - most recent common ancestor P */
int nid = 0; /* The name pivot version "N" */
| > | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
** -f|--force Force the merge even if it would be a no-op
** --force-missing Force the merge even if there is missing content
** --integrate Merged branch will be closed when committing
** -K|--keep-merge-files On merge conflict, retain the temporary files
** used for merging, named *-baseline, *-original,
** and *-merge.
** -n|--dry-run If given, display instead of run actions
** --nosync Do not auto-sync prior to merging
** -v|--verbose Show additional details of the merge
*/
void merge_cmd(void){
int vid; /* Current version "V" */
int mid; /* Version we are merging from "M" */
int pid = 0; /* The pivot version - most recent common ancestor P */
int nid = 0; /* The name pivot version "N" */
|
| ︙ | ︙ | |||
335 336 337 338 339 340 341 | const char *zPivot; /* The value of --baseline */ int debugFlag; /* True if --debug is present */ int showVfileFlag; /* True if the --show-vfile flag is present */ int keepMergeFlag; /* True if --keep-merge-files is present */ int nConflict = 0; /* Number of conflicts seen */ int nOverwrite = 0; /* Number of unmanaged files overwritten */ char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ | > > > | | > | | 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 396 397 398 |
const char *zPivot; /* The value of --baseline */
int debugFlag; /* True if --debug is present */
int showVfileFlag; /* True if the --show-vfile flag is present */
int keepMergeFlag; /* True if --keep-merge-files is present */
int nConflict = 0; /* Number of conflicts seen */
int nOverwrite = 0; /* Number of unmanaged files overwritten */
char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */
const char *zVersion; /* The VERSION argument */
int bMultiMerge = 0; /* True if there are two or more VERSION arguments */
int nMerge = 0; /* Number of prior merges processed */
Stmt q; /* SQL statment used for merge processing */
/* Notation:
**
** V The current check-out
** M The version being merged in
** P The "pivot" - the most recent common ancestor of V and M.
** N The "name pivot" - for detecting renames
*/
undo_capture_command_line();
verboseFlag = find_option("verbose","v",0)!=0;
forceMissingFlag = find_option("force-missing",0,0)!=0;
if( !verboseFlag ){
verboseFlag = find_option("detail",0,0)!=0; /* deprecated */
}
pickFlag = find_option("cherrypick",0,0)!=0;
if('c'==*g.zCmdName /*called as cherrypick, possibly a short form*/){
pickFlag = 1;
}
integrateFlag = find_option("integrate",0,0)!=0;
backoutFlag = find_option("backout",0,0)!=0;
zBinGlob = find_option("binary",0,1);
dryRunFlag = find_option("dry-run","n",0)!=0;
if( !dryRunFlag ){
dryRunFlag = find_option("nochange",0,0)!=0; /* deprecated */
}
if( find_option("nosync",0,0) ) g.fNoSync = 1;
forceFlag = find_option("force","f",0)!=0;
zPivot = find_option("baseline",0,1);
keepMergeFlag = find_option("keep-merge-files", "K",0)!=0;
/* Undocumented --debug and --show-vfile options:
**
** When included on the command-line, --debug causes lots of state
** information to be displayed. This option is undocumented as it
** might change or be eliminated in future releases.
**
** The --show-vfile flag does a dump of the VFILE table for reference.
**
** Hints:
** * Combine --debug and --verbose for still more output.
** * The --dry-run option is also useful in combination with --debug.
*/
debugFlag = find_option("debug",0,0)!=0;
if( debugFlag && verboseFlag ) debugFlag = 2;
|
| ︙ | ︙ | |||
399 400 401 402 403 404 405 |
}
if( !dryRunFlag ){
if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "merge") ){
fossil_fatal("merge abandoned due to sync failure");
}
}
| > > > > > > > > > > > > > > > | | > | > > | | > > > > > | 410 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 440 441 442 443 444 445 446 447 448 449 450 451 452 453 |
}
if( !dryRunFlag ){
if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "merge") ){
fossil_fatal("merge abandoned due to sync failure");
}
}
/*
** A "multi-merge" means two or more other check-ins are being merged
** into the current check-in. In other words, there are two or more
** VERSION arguments on the command-line. Multi-merge works by doing
** the merges one by one, as long as there are no conflicts. At the
** bottom of this routine, a jump is made back up to this point if there
** are more merges yet to be done and no errors have yet been seen.
**
** Related variables:
** bMultiMerge True if there are one or more merges yet to do
** zVersion The name of the current checking being merged in
** nMerge Number of prior merges
*/
merge_next_child:
/* Find mid, the artifactID of the version to be merged into
** the current check-out.
*/
if( g.argc>=3 ){
int i;
/* Mid is specified as an argument on the command-line */
zVersion = g.argv[2];
mid = name_to_typed_rid(zVersion, "ci");
if( mid==0 || !is_a_version(mid) ){
fossil_fatal("not a version: %s", zVersion);
}
bMultiMerge = g.argc>3;
if( bMultiMerge ){
for(i=3; i<g.argc; i++) g.argv[i-1] = g.argv[i];
g.argc--;
}
}else if( g.argc==2 ){
/* No version specified on the command-line so pick the most recent
** leaf that is (1) not the version currently checked out and (2)
** has not already been merged into the current check-out and (3)
** the leaf is not closed and (4) the leaf is in the same branch
** as the current check-out.
|
| ︙ | ︙ | |||
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
" datetime(event.mtime,toLocal()),"
" coalesce(ecomment, comment),"
" coalesce(euser, user)"
" FROM event, blob"
" WHERE event.objid=%d AND blob.rid=%d",
mid, mid
);
if( db_step(&q)==SQLITE_ROW ){
char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
db_column_text(&q, 0), db_column_text(&q, 1),
db_column_text(&q, 3), db_column_text(&q, 2));
comment_print(zCom, db_column_text(&q,2), 0, -1, get_comment_format());
fossil_free(zCom);
}
db_finalize(&q);
}else{
usage("?OPTIONS? ?VERSION?");
return;
}
| > > | 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
" datetime(event.mtime,toLocal()),"
" coalesce(ecomment, comment),"
" coalesce(euser, user)"
" FROM event, blob"
" WHERE event.objid=%d AND blob.rid=%d",
mid, mid
);
zVersion = 0;
if( db_step(&q)==SQLITE_ROW ){
char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
db_column_text(&q, 0), db_column_text(&q, 1),
db_column_text(&q, 3), db_column_text(&q, 2));
comment_print(zCom, db_column_text(&q,2), 0, -1, get_comment_format());
fossil_free(zCom);
zVersion = mprintf("%S",db_column_text(&q,0));
}
db_finalize(&q);
}else{
usage("?OPTIONS? ?VERSION?");
return;
}
|
| ︙ | ︙ | |||
465 466 467 468 469 470 471 |
if( pickFlag || backoutFlag ){
if( integrateFlag ){
fossil_fatal("incompatible options: --integrate and --cherrypick "
"with --backout");
}
pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
if( pid<=0 ){
| | | | 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 |
if( pickFlag || backoutFlag ){
if( integrateFlag ){
fossil_fatal("incompatible options: --integrate and --cherrypick "
"with --backout");
}
pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid);
if( pid<=0 ){
fossil_fatal("cannot find an ancestor for %s", zVersion);
}
}else{
if( !zPivot ){
pivot_set_primary(mid);
pivot_set_secondary(vid);
db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0");
while( db_step(&q)==SQLITE_ROW ){
pivot_set_secondary(db_column_int(&q,0));
}
db_finalize(&q);
pid = pivot_find(0);
if( pid<=0 ){
fossil_fatal("cannot find a common ancestor between the current "
"check-out and %s", zVersion);
}
}
pivot_set_primary(mid);
pivot_set_secondary(vid);
nid = pivot_find(1);
if( nid!=pid ){
pivot_set_primary(nid);
|
| ︙ | ︙ | |||
506 507 508 509 510 511 512 |
}
if( !forceFlag && mid==pid ){
fossil_print("Merge skipped because it is a no-op. "
" Use --force to override.\n");
return;
}
if( integrateFlag && !is_a_leaf(mid)){
| | | | | 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 |
}
if( !forceFlag && mid==pid ){
fossil_print("Merge skipped because it is a no-op. "
" Use --force to override.\n");
return;
}
if( integrateFlag && !is_a_leaf(mid)){
fossil_warning("ignoring --integrate: %s is not a leaf", zVersion);
integrateFlag = 0;
}
if( integrateFlag && content_is_private(mid) ){
fossil_warning(
"ignoring --integrate: %s is on a private branch"
"\n Use \"fossil amend --close\" (after commit) to close the leaf.",
zVersion);
integrateFlag = 0;
}
if( verboseFlag ){
print_checkin_description(mid, 12,
integrateFlag ? "integrate:" : "merge-from:");
print_checkin_description(pid, 12, "baseline:");
}
vfile_check_signature(vid, CKSIG_ENOTFILE);
if( nMerge==0 ) db_begin_transaction();
if( !dryRunFlag ) undo_begin();
if( load_vfile_from_rid(mid) && !forceMissingFlag ){
fossil_fatal("missing content, unable to merge");
}
if( load_vfile_from_rid(pid) && !forceMissingFlag ){
fossil_fatal("missing content, unable to merge");
}
|
| ︙ | ︙ | |||
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 |
}
db_finalize(&q);
/* Report on conflicts
*/
if( nConflict ){
fossil_warning("WARNING: %d merge conflicts", nConflict);
}
if( nOverwrite ){
fossil_warning("WARNING: %d unmanaged files were overwritten",
nOverwrite);
}
| > > > > > > > > > > > > > > | | > > > > > | 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 |
}
db_finalize(&q);
/* Report on conflicts
*/
if( nConflict ){
fossil_warning("WARNING: %d merge conflicts", nConflict);
if( bMultiMerge ){
int i;
Blob msg;
blob_init(&msg, 0, 0);
blob_appendf(&msg,
"The following %ss were not attempted due to prior conflicts:",
pickFlag ? "cherrypick" : backoutFlag ? "backout" : "merge"
);
for(i=2; i<g.argc; i++){
blob_appendf(&msg, " %s", g.argv[i]);
}
fossil_warning("%s", blob_str(&msg));
blob_zero(&msg);
}
}
if( nOverwrite ){
fossil_warning("WARNING: %d unmanaged files were overwritten",
nOverwrite);
}
if( dryRunFlag && !bMultiMerge ){
fossil_warning("REMINDER: this was a dry run -"
" no files were actually changed.");
}
/*
** Clean up the mid and pid VFILE entries. Then commit the changes.
*/
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
if( pickFlag ){
vmerge_insert(-1, mid);
/* For a cherrypick merge, make the default check-in comment the same
** as the check-in comment on the check-in that is being merged in. */
db_multi_exec(
"REPLACE INTO vvar(name,value)"
" SELECT 'ci-comment', coalesce(ecomment,comment) FROM event"
" WHERE type='ci' AND objid=%d",
mid
);
}else if( backoutFlag ){
vmerge_insert(-2, pid);
}else if( integrateFlag ){
vmerge_insert(-4, mid);
}else{
vmerge_insert(0, mid);
}
if( bMultiMerge && nConflict==0 ){
nMerge++;
goto merge_next_child;
}
if( !dryRunFlag ) undo_finish();
db_end_transaction(dryRunFlag);
}
|
Changes to src/merge3.c.
| ︙ | ︙ | |||
209 210 211 212 213 214 215 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ int nConflict = 0; /* Number of merge conflicts seen so far */ int useCrLf = 0; int ln1, ln2, lnPivot; /* Line numbers for all files */ DiffConfig DCfg; blob_zero(pOut); /* Merge results stored in pOut */ | | | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
int limit1, limit2; /* Sizes of aC1[] and aC2[] */
int nConflict = 0; /* Number of merge conflicts seen so far */
int useCrLf = 0;
int ln1, ln2, lnPivot; /* Line numbers for all files */
DiffConfig DCfg;
blob_zero(pOut); /* Merge results stored in pOut */
/* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM),
** keep it in the output. This should be secure enough not to cause
** unintended changes to the merged file and consistent with what
** users are using in their source files.
*/
if( starts_with_utf8_bom(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){
blob_append(pOut, (char*)get_utf8_bom(0), -1);
|
| ︙ | ︙ |
Changes to src/name.c.
| ︙ | ︙ | |||
208 209 210 211 212 213 214 | ** Find the RID of the most recent object with symbolic tag zTag ** and having a type that matches zType. ** ** Return 0 if there are no matches. ** ** This is a tricky query to do efficiently. ** If the tag is very common (ex: "trunk") then | | | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
** Find the RID of the most recent object with symbolic tag zTag
** and having a type that matches zType.
**
** Return 0 if there are no matches.
**
** This is a tricky query to do efficiently.
** If the tag is very common (ex: "trunk") then
** we want to use the query identified below as Q1 - which searches
** the most recent EVENT table entries for the most recent with the tag.
** But if the tag is relatively scarce (anything other than "trunk", basically)
** then we want to do the indexed search show below as Q2.
*/
static int most_recent_event_with_tag(const char *zTag, const char *zType){
return db_int(0,
"SELECT objid FROM ("
|
| ︙ | ︙ | |||
511 512 513 514 515 516 517 |
return start_of_branch(rid, 0);
}
/* start:BR -> The first check-in on branch named BR */
if( strncmp(zTag, "start:", 6)==0 ){
rid = symbolic_name_to_rid(zTag+6, zType);
return start_of_branch(rid, 1);
| | | | | 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 |
return start_of_branch(rid, 0);
}
/* start:BR -> The first check-in on branch named BR */
if( strncmp(zTag, "start:", 6)==0 ){
rid = symbolic_name_to_rid(zTag+6, zType);
return start_of_branch(rid, 1);
}
/* merge-in:BR -> Most recent merge-in for the branch named BR */
if( strncmp(zTag, "merge-in:", 9)==0 ){
rid = symbolic_name_to_rid(zTag+9, zType);
return start_of_branch(rid, 2);
}
/* symbolic-name ":" date-time */
nTag = strlen(zTag);
for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
if( zTag[i]==':'
&& (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0)
){
char *zDate = mprintf("%s", &zTag[i+1]);
char *zTagBase = mprintf("%.*s", i, zTag);
char *zXDate;
int nDate = strlen(zDate);
if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
|
| ︙ | ︙ | |||
817 818 819 820 821 822 823 824 825 826 827 828 829 830 |
fossil_fatal("cannot resolve name: %s", zName);
}
return rid;
}
int name_to_rid(const char *zName){
return name_to_typed_rid(zName, "*");
}
/*
** WEBPAGE: ambiguous
** URL: /ambiguous?name=NAME&src=WEBPAGE
**
** The NAME given by the name parameter is ambiguous. Display a page
** that shows all possible choices and let the user select between them.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 |
fossil_fatal("cannot resolve name: %s", zName);
}
return rid;
}
int name_to_rid(const char *zName){
return name_to_typed_rid(zName, "*");
}
/*
** Try to resolve zQP1 into a check-in name. If zQP1 does not exist,
** return 0. If zQP1 exists but cannot be resolved, then also try to
** resolve zQP2 if it exists. If zQP1 cannot be resolved but zQP2 does
** not exist, then raise an error. If both zQP1 and zQP2 exists but
** neither can be resolved, also raise an error.
**
** If pzPick is not a NULL pointer, then *pzPick to be the value of whichever
** query parameter ended up being used.
*/
int name_choice(const char *zQP1, const char *zQP2, const char **pzPick){
const char *zName, *zName2;
int rid;
zName = P(zQP1);
if( zName==0 || zName[0]==0 ) return 0;
rid = symbolic_name_to_rid(zName, "ci");
if( rid>0 ){
if( pzPick ) *pzPick = zName;
return rid;
}
if( rid<0 ){
fossil_fatal("ambiguous name: %s", zName);
}
zName2 = P(zQP2);
if( zName2==0 || zName2[0]==0 ){
fossil_fatal("cannot resolve name: %s", zName);
}
if( pzPick ) *pzPick = zName2;
return name_to_typed_rid(zName2, "ci");
}
/*
** WEBPAGE: ambiguous
** URL: /ambiguous?name=NAME&src=WEBPAGE
**
** The NAME given by the name parameter is ambiguous. Display a page
** that shows all possible choices and let the user select between them.
|
| ︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 |
" coalesce(euser,user), coalesce(ecomment,comment)"
" FROM mlink, filename, blob, event"
" WHERE mlink.fid=%d"
" AND filename.fnid=mlink.fnid"
" AND event.objid=mlink.mid"
" AND blob.rid=mlink.mid"
" ORDER BY event.mtime %s /*sort*/",
| | | 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 |
" coalesce(euser,user), coalesce(ecomment,comment)"
" FROM mlink, filename, blob, event"
" WHERE mlink.fid=%d"
" AND filename.fnid=mlink.fnid"
" AND event.objid=mlink.mid"
" AND blob.rid=mlink.mid"
" ORDER BY event.mtime %s /*sort*/",
rid,
(flags & WHATIS_BRIEF) ? "LIMIT 1" : "DESC");
while( db_step(&q)==SQLITE_ROW ){
if( flags & WHATIS_BRIEF ){
fossil_print("mtime: %s\n", db_column_text(&q,2));
}
fossil_print("file: %s\n", db_column_text(&q,0));
fossil_print(" part of [%S] by %s on %s\n",
|
| ︙ | ︙ |
Changes to src/patch.c.
| ︙ | ︙ | |||
43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /* ** Flags passed from the main patch_cmd() routine into subfunctions used ** to implement the various subcommands. */ #define PATCH_DRYRUN 0x0001 #define PATCH_VERBOSE 0x0002 #define PATCH_FORCE 0x0004 /* ** Implementation of the "readfile(X)" SQL function. The entire content ** of the check-out file named X is read and returned as a BLOB. */ static void readfileFunc( sqlite3_context *context, | > | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | /* ** Flags passed from the main patch_cmd() routine into subfunctions used ** to implement the various subcommands. */ #define PATCH_DRYRUN 0x0001 #define PATCH_VERBOSE 0x0002 #define PATCH_FORCE 0x0004 #define PATCH_RETRY 0x0008 /* Second attempt */ /* ** Implementation of the "readfile(X)" SQL function. The entire content ** of the check-out file named X is read and returned as a BLOB. */ static void readfileFunc( sqlite3_context *context, |
| ︙ | ︙ | |||
69 70 71 72 73 74 75 | } /* ** mkdelta(X,Y) ** ** X is an numeric artifact id. Y is a filename. ** | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
}
/*
** mkdelta(X,Y)
**
** X is an numeric artifact id. Y is a filename.
**
** Compute a compressed delta that carries X into Y. Or return
** and zero-length blob if X is equal to Y.
*/
static void mkdeltaFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
|
| ︙ | ︙ | |||
130 131 132 133 134 135 136 |
SQLITE_TRANSIENT);
blob_reset(&x);
}
/*
** Generate a binary patch file and store it into the file
| | > > | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
SQLITE_TRANSIENT);
blob_reset(&x);
}
/*
** Generate a binary patch file and store it into the file
** named zOut. Or if zOut is NULL, write it into out.
**
** Return the number of errors.
*/
void patch_create(unsigned mFlags, const char *zOut, FILE *out){
int vid;
char *z;
if( zOut && file_isdir(zOut, ExtFILE)!=0 ){
if( mFlags & PATCH_FORCE ){
|
| ︙ | ︙ | |||
160 161 162 163 164 165 166 |
"PRAGMA patch.page_size=512;\n"
"CREATE TABLE patch.chng(\n"
" pathname TEXT,\n" /* Filename */
" origname TEXT,\n" /* Name before rename. NULL if not renamed */
" hash TEXT,\n" /* Baseline hash. NULL for new files. */
" isexe BOOL,\n" /* True if executable */
" islink BOOL,\n" /* True if is a symbolic link */
| | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
"PRAGMA patch.page_size=512;\n"
"CREATE TABLE patch.chng(\n"
" pathname TEXT,\n" /* Filename */
" origname TEXT,\n" /* Name before rename. NULL if not renamed */
" hash TEXT,\n" /* Baseline hash. NULL for new files. */
" isexe BOOL,\n" /* True if executable */
" islink BOOL,\n" /* True if is a symbolic link */
" delta BLOB\n" /* compressed delta. NULL if deleted.
** length 0 if unchanged */
");"
"CREATE TABLE patch.cfg(\n"
" key TEXT,\n"
" value ANY\n"
");"
);
|
| ︙ | ︙ | |||
194 195 196 197 198 199 200 |
";", vid, g.zLocalRoot, g.zRepositoryName, g.zLogin);
z = fossil_hostname();
if( z ){
db_multi_exec(
"INSERT INTO patch.cfg(key,value)VALUES('hostname',%Q)", z);
fossil_free(z);
}
| | | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
";", vid, g.zLocalRoot, g.zRepositoryName, g.zLogin);
z = fossil_hostname();
if( z ){
db_multi_exec(
"INSERT INTO patch.cfg(key,value)VALUES('hostname',%Q)", z);
fossil_free(z);
}
/* New files */
db_multi_exec(
"INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)"
" SELECT pathname, NULL, isexe, islink,"
" compress(read_co_file(%Q||pathname))"
" FROM vfile WHERE rid==0;",
g.zLocalRoot
|
| ︙ | ︙ | |||
246 247 248 249 250 251 252 |
if( pData==0 ){
fossil_fatal("out of memory");
}
#ifdef _WIN32
fflush(out);
_setmode(_fileno(out), _O_BINARY);
#endif
| | < > > | > > > > > | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
if( pData==0 ){
fossil_fatal("out of memory");
}
#ifdef _WIN32
fflush(out);
_setmode(_fileno(out), _O_BINARY);
#endif
fwrite(pData, 1, sz, out);
fflush(out);
sqlite3_free(pData);
}
db_multi_exec("DETACH patch;");
}
/*
** Attempt to load and validate a patchfile identified by the first
** argument.
*/
void patch_attach(const char *zIn, FILE *in, int bIgnoreEmptyPatch){
Stmt q;
if( g.db==0 ){
sqlite3_open(":memory:", &g.db);
}
if( zIn==0 ){
Blob buf;
int rc;
int sz;
unsigned char *pData;
blob_init(&buf, 0, 0);
#ifdef _WIN32
_setmode(_fileno(in), _O_BINARY);
#endif
sz = blob_read_from_channel(&buf, in, -1);
pData = (unsigned char*)blob_buffer(&buf);
if( sz<512 ){
blob_reset(&buf);
if( bIgnoreEmptyPatch ) return;
fossil_fatal("input is too small to be a patch file");
}
db_multi_exec("ATTACH ':memory:' AS patch");
if( g.fSqlTrace ){
fossil_trace("-- deserialize(\"patch\", pData, %lld);\n", sz);
}
rc = sqlite3_deserialize(g.db, "patch", pData, sz, sz, 0);
if( rc ){
fossil_fatal("cannot open patch database: %s", sqlite3_errmsg(g.db));
|
| ︙ | ︙ | |||
299 300 301 302 303 304 305 |
}
/*
** Show a summary of the content of a patch on standard output
*/
void patch_view(unsigned mFlags){
Stmt q;
| | | | 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 |
}
/*
** Show a summary of the content of a patch on standard output
*/
void patch_view(unsigned mFlags){
Stmt q;
db_prepare(&q,
"WITH nmap(nkey,nm) AS (VALUES"
"('baseline','BASELINE'),"
"('project-name','PROJECT-NAME'))"
"SELECT nm, value FROM nmap, patch.cfg WHERE nkey=key;"
);
while( db_step(&q)==SQLITE_ROW ){
fossil_print("%-12s %s\n", db_column_text(&q,0), db_column_text(&q,1));
}
db_finalize(&q);
if( mFlags & PATCH_VERBOSE ){
db_prepare(&q,
"WITH nmap(nkey,nm,isDate) AS (VALUES"
"('project-code','PROJECT-CODE',0),"
"('date','TIMESTAMP',1),"
"('user','USER',0),"
"('hostname','HOSTNAME',0),"
"('ckout','CHECKOUT',0),"
"('repo','REPOSITORY',0))"
|
| ︙ | ︙ | |||
373 374 375 376 377 378 379 |
void patch_apply(unsigned mFlags){
Stmt q;
Blob cmd;
blob_init(&cmd, 0, 0);
if( unsaved_changes(0) ){
if( (mFlags & PATCH_FORCE)==0 ){
| > | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 |
void patch_apply(unsigned mFlags){
Stmt q;
Blob cmd;
blob_init(&cmd, 0, 0);
if( unsaved_changes(0) ){
if( (mFlags & PATCH_FORCE)==0 ){
fossil_fatal("Cannot apply patch: there are unsaved changes "
"in the current check-out");
}else{
blob_appendf(&cmd, "%$ revert", g.nameOfExe);
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s\n", blob_str(&cmd));
}else{
int rc = fossil_system(blob_str(&cmd));
if( rc ){
|
| ︙ | ︙ | |||
431 432 433 434 435 436 437 |
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
if( strcmp(zType,"merge")==0 ){
blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));
}else{
blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));
}
if( mFlags & PATCH_VERBOSE ){
| | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
if( strcmp(zType,"merge")==0 ){
blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));
}else{
blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));
}
if( mFlags & PATCH_VERBOSE ){
fossil_print("%-10s %s\n", db_column_text(&q,2),
db_column_text(&q,0));
}
}
db_finalize(&q);
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s", blob_str(&cmd));
}else{
|
| ︙ | ︙ | |||
520 521 522 523 524 525 526 527 528 529 530 531 532 533 |
blob_init(&data, 0, 0);
db_ephemeral_blob(&q, 4, &data);
if( blob_size(&data) ){
blob_uncompress(&data, &data);
}
if( blob_size(&data)==0 ){
/* No changes to the file */
}else if( zHash ){
Blob basis;
int rid = fast_uuid_to_rid(zHash);
int outSize, sz;
char *aOut;
if( rid==0 ){
fossil_fatal("cannot locate basis artifact %s for %s",
| > | 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 |
blob_init(&data, 0, 0);
db_ephemeral_blob(&q, 4, &data);
if( blob_size(&data) ){
blob_uncompress(&data, &data);
}
if( blob_size(&data)==0 ){
/* No changes to the file */
continue;
}else if( zHash ){
Blob basis;
int rid = fast_uuid_to_rid(zHash);
int outSize, sz;
char *aOut;
if( rid==0 ){
fossil_fatal("cannot locate basis artifact %s for %s",
|
| ︙ | ︙ | |||
559 560 561 562 563 564 565 |
}else{
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
blob_appendf(&cmd, " add %$\n", zPathname);
if( mFlags & PATCH_VERBOSE ){
fossil_print("%-10s %s\n", "NEW", zPathname);
}
}
| | | 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 |
}else{
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
blob_appendf(&cmd, " add %$\n", zPathname);
if( mFlags & PATCH_VERBOSE ){
fossil_print("%-10s %s\n", "NEW", zPathname);
}
}
if( (mFlags & PATCH_DRYRUN)==0 ){
if( isLink ){
symlink_create(blob_str(&data), zPathname);
}else{
blob_write_to_file(&data, zPathname);
}
file_setexe(zPathname, isExe);
blob_reset(&data);
|
| ︙ | ︙ | |||
662 663 664 665 666 667 668 |
static FILE *patch_remote_command(
unsigned mFlags, /* flags */
const char *zThisCmd, /* "push" or "pull" */
const char *zRemoteCmd, /* "apply" or "create" */
const char *zFossilCmd, /* Name of "fossil" on remote system */
const char *zRW /* "w" or "r" */
){
| | | | | > > > < > | > > > | > > > > > > > > > > > > > > > > > > > > | 673 674 675 676 677 678 679 680 681 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 756 757 758 759 760 |
static FILE *patch_remote_command(
unsigned mFlags, /* flags */
const char *zThisCmd, /* "push" or "pull" */
const char *zRemoteCmd, /* "apply" or "create" */
const char *zFossilCmd, /* Name of "fossil" on remote system */
const char *zRW /* "w" or "r" */
){
char *zRemote = 0;
char *zDir = 0;
Blob cmd;
FILE *f = 0;
Blob flgs;
char *zForce = 0;
int isRetry = (mFlags & PATCH_RETRY)!=0;
blob_init(&flgs, 0, 0);
blob_init(&cmd, 0, 0);
if( mFlags & PATCH_FORCE ) blob_appendf(&flgs, " -f");
if( mFlags & PATCH_VERBOSE ) blob_appendf(&flgs, " -v");
if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n");
zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : "";
if( g.argc!=4 ){
usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
}
zRemote = fossil_strdup(g.argv[3]);
zDir = (char*)file_skip_userhost(zRemote);
if( zDir==0 ){
if( isRetry ) goto remote_command_error;
zDir = zRemote;
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
blob_appendf(&cmd, " patch %s%s %$ -", zRemoteCmd, zForce, zDir);
}else{
Blob remote;
*(char*)(zDir-1) = 0;
transport_ssh_command(&cmd);
blob_appendf(&cmd, " -T");
blob_append_escaped_arg(&cmd, zRemote, 0);
blob_init(&remote, 0, 0);
if( zFossilCmd==0 ){
if( ssh_needs_path_argument(zRemote,-1) ^ isRetry ){
ssh_add_path_argument(&cmd);
}
zFossilCmd = "fossil";
}else if( mFlags & PATCH_RETRY ){
goto remote_command_error;
}
blob_appendf(&remote, "%$ patch %s%s --dir64 %z -",
zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1));
blob_append_escaped_arg(&cmd, blob_str(&remote), 0);
blob_reset(&remote);
}
if( isRetry ){
fossil_print("First attempt to run \"fossil\" on %s failed\n"
"Retry: ", zRemote);
}
fossil_print("%s\n", blob_str(&cmd));
fflush(stdout);
f = popen(blob_str(&cmd), zRW);
if( f==0 ){
fossil_fatal("cannot run command: %s", blob_str(&cmd));
}
remote_command_error:
fossil_free(zRemote);
blob_reset(&cmd);
blob_reset(&flgs);
return f;
}
/*
** Toggle the use-path-for-ssh setting for the remote host defined
** by g.argv[3].
*/
static void patch_toggle_ssh_needs_path(void){
char *zRemote = fossil_strdup(g.argv[3]);
char *zDir = (char*)file_skip_userhost(zRemote);
if( zDir ){
*(char*)(zDir - 1) = 0;
ssh_needs_path_argument(zRemote, 99);
}
fossil_free(zRemote);
}
/*
** Show a diff for the patch currently loaded into database "patch".
*/
static void patch_diff(
unsigned mFlags, /* Patch flags. only -f is allowed */
DiffConfig *pCfg /* Diff options */
|
| ︙ | ︙ | |||
775 776 777 778 779 780 781 |
" FROM patch.chng"
" ORDER BY pathname"
);
while( db_step(&q)==SQLITE_ROW ){
int rid;
const char *zName;
Blob a, b;
| | | 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 |
" FROM patch.chng"
" ORDER BY pathname"
);
while( db_step(&q)==SQLITE_ROW ){
int rid;
const char *zName;
Blob a, b;
if( db_column_type(&q,0)!=SQLITE_INTEGER
&& db_column_type(&q,4)==SQLITE_TEXT
){
char *zUuid = fossil_strdup(db_column_text(&q,4));
char *zName = fossil_strdup(db_column_text(&q,1));
if( mFlags & PATCH_FORCE ){
fossil_print("ERROR cannot find base artifact %S for file \"%s\"\n",
|
| ︙ | ︙ | |||
897 898 899 900 901 902 903 | ** ** Command-line options: ** ** -f|--force Apply the patch even though there are unsaved ** changes in the current check-out. Unsaved ** changes will be reverted and then the patch is ** applied. | | | 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 | ** ** Command-line options: ** ** -f|--force Apply the patch even though there are unsaved ** changes in the current check-out. Unsaved ** changes will be reverted and then the patch is ** applied. ** --fossilcmd EXE Name of the "fossil" executable on the remote ** -n|--dry-run Do nothing, but print what would have happened ** -v|--verbose Extra output explaining what happens ** ** ** > fossil patch pull REMOTE-CHECKOUT ** ** Like "fossil patch push" except that the transfer is from remote |
| ︙ | ︙ | |||
932 933 934 935 936 937 938 |
char *zIn;
unsigned flags = 0;
if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN;
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
zIn = patch_find_patch_filename("apply");
db_must_be_within_tree();
| | | 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 |
char *zIn;
unsigned flags = 0;
if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN;
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
zIn = patch_find_patch_filename("apply");
db_must_be_within_tree();
patch_attach(zIn, stdin, 0);
patch_apply(flags);
fossil_free(zIn);
}else
if( strncmp(zCmd, "create", n)==0 ){
char *zOut;
unsigned flags = 0;
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
|
| ︙ | ︙ | |||
961 962 963 964 965 966 967 |
return;
}
db_find_and_open_repository(0, 0);
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
diff_options(&DCfg, zCmd[0]=='g', 0);
verify_all_options();
zIn = patch_find_patch_filename("apply");
| | | | | > > > > > > > > > > | > > > > > > > > > | | | 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 |
return;
}
db_find_and_open_repository(0, 0);
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
diff_options(&DCfg, zCmd[0]=='g', 0);
verify_all_options();
zIn = patch_find_patch_filename("apply");
patch_attach(zIn, stdin, 0);
patch_diff(flags, &DCfg);
fossil_free(zIn);
}else
if( strncmp(zCmd, "pull", n)==0 ){
FILE *pIn = 0;
unsigned flags = 0;
const char *zFossilCmd = find_option("fossilcmd",0,1);
if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN;
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
db_must_be_within_tree();
verify_all_options();
pIn = patch_remote_command(flags & (~PATCH_FORCE),
"pull", "create", zFossilCmd, "r");
if( pIn ){
patch_attach(0, pIn, 1);
if( pclose(pIn) ){
flags |= PATCH_RETRY;
pIn = patch_remote_command(flags & (~PATCH_FORCE),
"pull", "create", zFossilCmd, "r");
if( pIn ){
patch_attach(0, pIn, 0);
if( pclose(pIn)==0 ){
patch_toggle_ssh_needs_path();
}
}
}
patch_apply(flags);
}
}else
if( strncmp(zCmd, "push", n)==0 ){
FILE *pOut = 0;
unsigned flags = 0;
const char *zFossilCmd = find_option("fossilcmd",0,1);
if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN;
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
db_must_be_within_tree();
verify_all_options();
pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w");
if( pOut ){
patch_create(0, 0, pOut);
if( pclose(pOut)!=0 ){
flags |= PATCH_RETRY;
pOut = patch_remote_command(flags, "push", "apply", zFossilCmd, "w");
if( pOut ){
patch_create(0, 0, pOut);
if( pclose(pOut)==0 ){
patch_toggle_ssh_needs_path();
}
}
}
}
}else
if( strncmp(zCmd, "view", n)==0 ){
const char *zIn;
unsigned int flags = 0;
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
verify_all_options();
if( g.argc!=4 ){
usage("view FILENAME");
}
zIn = g.argv[3];
if( fossil_strcmp(zIn, "-")==0 ) zIn = 0;
patch_attach(zIn, stdin, 0);
patch_view(flags);
}else
{
goto patch_usage;
}
}
|
Changes to src/path.c.
| ︙ | ︙ | |||
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
for(p=path.pStart; p; p=p->u.pTo){
int fnid, pfnid;
if( !p->fromIsParent && (p->u.pTo==0 || p->u.pTo->fromIsParent) ){
/* Skip nodes where the parent is not on the path */
continue;
}
db_bind_int(&q1, ":mid", p->rid);
while( db_step(&q1)==SQLITE_ROW ){
fnid = db_column_int(&q1, 1);
pfnid = db_column_int(&q1, 0);
if( pfnid==0 ){
pfnid = fnid;
fnid = 0;
}
if( !p->fromIsParent ){
int t = fnid;
fnid = pfnid;
pfnid = t;
}
if( zDebug ){
| > > > > > > > > | | < | 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 |
for(p=path.pStart; p; p=p->u.pTo){
int fnid, pfnid;
if( !p->fromIsParent && (p->u.pTo==0 || p->u.pTo->fromIsParent) ){
/* Skip nodes where the parent is not on the path */
continue;
}
db_bind_int(&q1, ":mid", p->rid);
if( zDebug ){
fossil_print("%s check-in %.16z %z rid %d\n",
zDebug,
db_text(0, "SELECT uuid FROM blob WHERE rid=%d", p->rid),
db_text(0, "SELECT date(mtime) FROM event WHERE objid=%d", p->rid),
p->rid
);
}
while( db_step(&q1)==SQLITE_ROW ){
fnid = db_column_int(&q1, 1);
pfnid = db_column_int(&q1, 0);
if( pfnid==0 ){
pfnid = fnid;
fnid = 0;
}
if( !p->fromIsParent ){
int t = fnid;
fnid = pfnid;
pfnid = t;
}
if( zDebug ){
fossil_print("%s %d[%z] -> %d[%z]\n",
zDebug,
pfnid,
db_text(0, "SELECT name FROM filename WHERE fnid=%d", pfnid),
fnid,
db_text(0, "SELECT name FROM filename WHERE fnid=%d", fnid));
}
for(pChng=pAll; pChng; pChng=pChng->pNext){
if( pChng->curName==pfnid ){
|
| ︙ | ︙ |
Changes to src/pikchrshow.c.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include "config.h" #include <assert.h> #include <ctype.h> #include "pikchrshow.h" #if INTERFACE /* These are described in pikchr_process()'s docs. */ #define PIKCHR_PROCESS_PASSTHROUGH 0x0003 /* Pass through these flags */ #define PIKCHR_PROCESS_TH1 0x0004 #define PIKCHR_PROCESS_TH1_NOSVG 0x0008 #define PIKCHR_PROCESS_NONCE 0x0010 #define PIKCHR_PROCESS_ERR_PRE 0x0020 #define PIKCHR_PROCESS_SRC 0x0040 #define PIKCHR_PROCESS_DIV 0x0080 | > > > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include "config.h" #include <assert.h> #include <ctype.h> #include "pikchrshow.h" #if INTERFACE /* These are described in pikchr_process()'s docs. */ /* The first two must match the values from pikchr.c */ #define PIKCHR_PROCESS_PLAINTEXT_ERRORS 0x0001 #define PIKCHR_PROCESS_DARK_MODE 0x0002 /* end of flags supported directly by pikchr() */ #define PIKCHR_PROCESS_PASSTHROUGH 0x0003 /* Pass through these flags */ #define PIKCHR_PROCESS_TH1 0x0004 #define PIKCHR_PROCESS_TH1_NOSVG 0x0008 #define PIKCHR_PROCESS_NONCE 0x0010 #define PIKCHR_PROCESS_ERR_PRE 0x0020 #define PIKCHR_PROCESS_SRC 0x0040 #define PIKCHR_PROCESS_DIV 0x0080 |
| ︙ | ︙ | |||
133 134 135 136 137 138 139 |
) & pikFlags){
pikFlags |= PIKCHR_PROCESS_DIV;
}
if(!(PIKCHR_PROCESS_TH1 & pikFlags)
/* If any TH1_xxx flags are set, set TH1 */
&& (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
pikFlags |= PIKCHR_PROCESS_TH1;
| | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
) & pikFlags){
pikFlags |= PIKCHR_PROCESS_DIV;
}
if(!(PIKCHR_PROCESS_TH1 & pikFlags)
/* If any TH1_xxx flags are set, set TH1 */
&& (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
pikFlags |= PIKCHR_PROCESS_TH1;
}
if(zNonce){
blob_appendf(pOut, "%s\n", zNonce);
}
if(PIKCHR_PROCESS_TH1 & pikFlags){
Blob out = empty_blob;
isErr = Th_RenderToBlob(zIn, &out, thFlags)
? 1 : 0;
|
| ︙ | ︙ | |||
544 545 546 547 548 549 550 | ** ** -div-source Set the 'source' CSS class on the div, which tells ** CSS to hide the SVG and reveal the source by default. ** ** -src Store the input pikchr's source code in the output as ** a separate element adjacent to the SVG one. Implied ** by -div-source. | | > > | 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 | ** ** -div-source Set the 'source' CSS class on the div, which tells ** CSS to hide the SVG and reveal the source by default. ** ** -src Store the input pikchr's source code in the output as ** a separate element adjacent to the SVG one. Implied ** by -div-source. ** ** ** -th Process the input using TH1 before passing it to pikchr ** ** -th-novar Disable $var and $<var> TH1 processing. Use this if the ** pikchr script uses '$' for its own purposes and that ** causes issues. This only affects parsing of '$' outside ** of TH1 script blocks. Code in such blocks is unaffected. ** ** -th-nosvg When using -th, output the post-TH1'd script ** instead of the pikchr-rendered output ** ** -th-trace Trace TH1 execution (for debugging purposes) ** ** -dark Change pikchr colors to assume a dark-mode theme. ** ** ** The -div-indent/center/left/right flags may not be combined. ** ** TH1-related Notes and Caveats: ** ** If the -th flag is used, this command must open a fossil database |
| ︙ | ︙ | |||
611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
}
if(find_option("div-toggle",0,0)!=0){
pikFlags |= PIKCHR_PROCESS_DIV_TOGGLE;
}
if(find_option("div-source",0,0)!=0){
pikFlags |= PIKCHR_PROCESS_DIV_SOURCE | PIKCHR_PROCESS_SRC;
}
verify_all_options();
if(g.argc>4){
usage("?INFILE? ?OUTFILE?");
}
if(g.argc>2){
zInfile = g.argv[2];
| > > > | 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 |
}
if(find_option("div-toggle",0,0)!=0){
pikFlags |= PIKCHR_PROCESS_DIV_TOGGLE;
}
if(find_option("div-source",0,0)!=0){
pikFlags |= PIKCHR_PROCESS_DIV_SOURCE | PIKCHR_PROCESS_SRC;
}
if(find_option("dark",0,0)!=0){
pikFlags |= PIKCHR_PROCESS_DARK_MODE;
}
verify_all_options();
if(g.argc>4){
usage("?INFILE? ?OUTFILE?");
}
if(g.argc>2){
zInfile = g.argv[2];
|
| ︙ | ︙ |
Changes to src/pqueue.c.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 |
** Integers must be positive.
*/
struct PQueue {
int cnt; /* Number of entries in the queue */
int sz; /* Number of slots in a[] */
struct QueueElement {
int id; /* ID of the element */
| < | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
** Integers must be positive.
*/
struct PQueue {
int cnt; /* Number of entries in the queue */
int sz; /* Number of slots in a[] */
struct QueueElement {
int id; /* ID of the element */
double value; /* Value of element. Kept in ascending order */
} *a;
};
#endif
/*
** Initialize a PQueue structure
|
| ︙ | ︙ | |||
72 73 74 75 76 77 78 | p->a = fossil_realloc(p->a, sizeof(p->a[0])*N); p->sz = N; } /* ** Insert element e into the queue. */ | | < | < < | 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 |
p->a = fossil_realloc(p->a, sizeof(p->a[0])*N);
p->sz = N;
}
/*
** Insert element e into the queue.
*/
void pqueuex_insert(PQueue *p, int e, double v){
int i, j;
if( p->cnt+1>p->sz ){
pqueuex_resize(p, p->cnt+5);
}
for(i=0; i<p->cnt; i++){
if( p->a[i].value>v ){
for(j=p->cnt; j>i; j--){
p->a[j] = p->a[j-1];
}
break;
}
}
p->a[i].id = e;
p->a[i].value = v;
p->cnt++;
}
/*
** Extract the first element from the queue (the element with
** the smallest value) and return its ID. Return 0 if the queue
** is empty.
*/
int pqueuex_extract(PQueue *p){
int e, i;
if( p->cnt==0 ){
return 0;
}
e = p->a[0].id;
for(i=0; i<p->cnt-1; i++){
p->a[i] = p->a[i+1];
}
p->cnt--;
return e;
}
|
Changes to src/printf.c.
| ︙ | ︙ | |||
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 |
if( zFormat[0]=='X' ){
bDetail = 1;
zFormat++;
}
vfprintf(out, zFormat, ap);
fprintf(out, "\n");
va_end(ap);
if( bDetail ){
cgi_print_all(1,3,out);
}else{
for(i=0; i<count(azEnv); i++){
char *p;
if( (p = fossil_getenv(azEnv[i]))!=0 && p[0]!=0 ){
fprintf(out, "%s=%s\n", azEnv[i], p);
| > | 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 |
if( zFormat[0]=='X' ){
bDetail = 1;
zFormat++;
}
vfprintf(out, zFormat, ap);
fprintf(out, "\n");
va_end(ap);
if( g.zPhase!=0 ) fprintf(out, "while in %s\n", g.zPhase);
if( bDetail ){
cgi_print_all(1,3,out);
}else{
for(i=0; i<count(azEnv); i++){
char *p;
if( (p = fossil_getenv(azEnv[i]))!=0 && p[0]!=0 ){
fprintf(out, "%s=%s\n", azEnv[i], p);
|
| ︙ | ︙ | |||
1266 1267 1268 1269 1270 1271 1272 |
** argument. This is a no-op on unix but is necessary on windows.
*/
void fossil_binary_mode(FILE *p){
#if defined(_WIN32)
_setmode(_fileno(p), _O_BINARY);
#endif
#ifdef __EMX__ /* OS/2 */
| | | 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 |
** argument. This is a no-op on unix but is necessary on windows.
*/
void fossil_binary_mode(FILE *p){
#if defined(_WIN32)
_setmode(_fileno(p), _O_BINARY);
#endif
#ifdef __EMX__ /* OS/2 */
setmode(fossil_fileno(p), O_BINARY);
#endif
}
|
Changes to src/rebuild.c.
| ︙ | ︙ | |||
138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
"UPDATE concealed SET mtime=now();"
);
}
/* Do the fossil-2.0 updates to the schema. (2017-02-28)
*/
rebuild_schema_update_2_0();
}
/*
** Update the repository schema for Fossil version 2.0. (2017-02-28)
** (1) Change the CHECK constraint on BLOB.UUID so that the length
** is greater than or equal to 40, not exactly equal to 40.
*/
| > > > > > | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
"UPDATE concealed SET mtime=now();"
);
}
/* Do the fossil-2.0 updates to the schema. (2017-02-28)
*/
rebuild_schema_update_2_0();
/* Add the user.jx and reportfmt.jx columns if they are missing. (2022-11-18)
*/
user_update_user_table();
report_update_reportfmt_table();
}
/*
** Update the repository schema for Fossil version 2.0. (2017-02-28)
** (1) Change the CHECK constraint on BLOB.UUID so that the length
** is greater than or equal to 40, not exactly equal to 40.
*/
|
| ︙ | ︙ | |||
659 660 661 662 663 664 665 | ** --compress Strive to make the database as small as possible ** --compress-only Skip the rebuilding step. Do --compress only ** --force Force the rebuild to complete even if errors are seen ** --ifneeded Only do the rebuild if it would change the schema version ** --index Always add in the full-text search index ** --noverify Skip the verification of changes to the BLOB table ** --noindex Always omit the full-text search index | | | 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
** --compress Strive to make the database as small as possible
** --compress-only Skip the rebuilding step. Do --compress only
** --force Force the rebuild to complete even if errors are seen
** --ifneeded Only do the rebuild if it would change the schema version
** --index Always add in the full-text search index
** --noverify Skip the verification of changes to the BLOB table
** --noindex Always omit the full-text search index
** --pagesize N Set the database pagesize to N (512..65536, power of 2)
** --quiet Only show output if there are errors
** --stats Show artifact statistics after rebuilding
** --vacuum Run VACUUM on the database after rebuilding
** --wal Set Write-Ahead-Log journalling mode on the database
*/
void rebuild_database(void){
int forceFlag;
|
| ︙ | ︙ | |||
1393 1394 1395 1396 1397 1398 1399 |
*/
verify_cancel();
db_end_transaction(0);
fossil_print("project-id: %s\n", db_get("project-code", 0));
fossil_print("server-id: %s\n", db_get("server-code", 0));
zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
| | > | 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 |
*/
verify_cancel();
db_end_transaction(0);
fossil_print("project-id: %s\n", db_get("project-code", 0));
fossil_print("server-id: %s\n", db_get("server-code", 0));
zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin,
zPassword);
hash_user_password(g.zLogin);
}
/*
** COMMAND: deconstruct*
**
** Usage %fossil deconstruct ?OPTIONS? DESTINATION
|
| ︙ | ︙ |
Changes to src/regexp.c.
| ︙ | ︙ | |||
709 710 711 712 713 714 715 |
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
| > | | 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 |
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
sqlite3_result_int(context, 0);
/* sqlite3_result_error(context, zErr, -1); */
return;
}
if( pRe==0 ){
sqlite3_result_error_nomem(context);
return;
}
setAux = 1;
|
| ︙ | ︙ |
Changes to src/report.c.
| ︙ | ︙ | |||
1124 1125 1126 1127 1128 1129 1130 | char *zClrKey; char *zDesc; char *zMimetype; int tabs; Stmt q; char *zErr1 = 0; char *zErr2 = 0; | | | 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 |
char *zClrKey;
char *zDesc;
char *zMimetype;
int tabs;
Stmt q;
char *zErr1 = 0;
char *zErr2 = 0;
login_check_credentials();
if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
report_update_reportfmt_table();
rn = report_number();
tabs = P("tablist")!=0;
db_prepare(&q,
"SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'"
|
| ︙ | ︙ | |||
1366 1367 1368 1369 1370 1371 1372 | Stmt q; char *zSql; char *zErr1 = 0; char *zErr2 = 0; int count = 0; int rn; | | > | 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 |
Stmt q;
char *zSql;
char *zErr1 = 0;
char *zErr2 = 0;
int count = 0;
int rn;
if( !zRep || !strcmp(zRep,zFullTicketRptRn)
|| !strcmp(zRep,zFullTicketRptTitle) ){
zSql = "SELECT * FROM ticket";
}else{
rn = atoi(zRep);
if( rn ){
db_prepare(&q,
"SELECT sqlcode FROM reportfmt WHERE rn=%d", rn);
}else{
|
| ︙ | ︙ |
Changes to src/rss.c.
| ︙ | ︙ | |||
141 142 143 144 145 146 147 |
blob_append_sql( &bSQL, " ORDER BY event.mtime DESC" );
cgi_set_content_type("application/rss+xml");
zProjectName = db_get("project-name", 0);
if( zProjectName==0 ){
| | | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
blob_append_sql( &bSQL, " ORDER BY event.mtime DESC" );
cgi_set_content_type("application/rss+xml");
zProjectName = db_get("project-name", 0);
if( zProjectName==0 ){
zFreeProjectName = zProjectName =
mprintf("Fossil source repository for: %s", g.zBaseURL);
}
zProjectDescr = db_get("project-description", 0);
if( zProjectDescr==0 ){
zProjectDescr = zProjectName;
}
zPubDate = cgi_rfc822_datestamp(time(NULL));
|
| ︙ | ︙ | |||
256 257 258 259 260 261 262 |
** The default is "URL-PLACEHOLDER" (without quotes).
*/
void cmd_timeline_rss(void){
Stmt q;
int nLine=0;
char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
Blob bSQL;
| | | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
** The default is "URL-PLACEHOLDER" (without quotes).
*/
void cmd_timeline_rss(void){
Stmt q;
int nLine=0;
char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
Blob bSQL;
const char *zType = find_option("type","y",1); /* Type of events;All if NULL*/
const char *zTicketUuid = find_option("tkt",NULL,1);
const char *zTag = find_option("tag",NULL,1);
const char *zFilename = find_option("name",NULL,1);
const char *zWiki = find_option("wiki",NULL,1);
const char *zLimit = find_option("limit", "n",1);
const char *zBaseURL = find_option("url", NULL, 1);
int nLimit = atoi( (zLimit && *zLimit) ? zLimit : "20" );
|
| ︙ | ︙ | |||
330 331 332 333 334 335 336 |
}else if( nTagId!=0 ){
blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
}
if( zFilename ){
blob_append_sql(&bSQL,
| | > | | | > | 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 |
}else if( nTagId!=0 ){
blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
}
if( zFilename ){
blob_append_sql(&bSQL,
" AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) "
" IN (SELECT fnid FROM filename WHERE name=%Q %s)",
zFilename, filename_collation()
);
}
blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );
zProjectName = db_get("project-name", 0);
if( zProjectName==0 ){
zFreeProjectName = zProjectName =
mprintf("Fossil source repository for: %s", zBaseURL);
}
zProjectDescr = db_get("project-description", 0);
if( zProjectDescr==0 ){
zProjectDescr = zProjectName;
}
zPubDate = cgi_rfc822_datestamp(time(NULL));
fossil_print("<?xml version=\"1.0\"?>");
fossil_print("<rss xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
" version=\"2.0\">");
fossil_print("<channel>\n");
fossil_print("<title>%h</title>\n", zProjectName);
fossil_print("<link>%s</link>\n", zBaseURL);
fossil_print("<description>%h</description>\n", zProjectDescr);
fossil_print("<pubDate>%s</pubDate>\n", zPubDate);
fossil_print("<generator>Fossil version %s %s</generator>\n",
MANIFEST_VERSION, MANIFEST_DATE);
|
| ︙ | ︙ |
Changes to src/search.c.
| ︙ | ︙ | |||
694 695 696 697 698 699 700 |
db_finalize(&q);
blob_reset(&pattern);
}else{
/* Legacy timeline search (the default) */
(void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC);
blob_reset(&pattern);
search_sql_setup(g.db);
| | | 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 |
db_finalize(&q);
blob_reset(&pattern);
}else{
/* Legacy timeline search (the default) */
(void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC);
blob_reset(&pattern);
search_sql_setup(g.db);
db_multi_exec(
"CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
"CREATE INDEX srch_idx1 ON srch(x);"
"INSERT INTO srch(rid,uuid,date,comment,x)"
" SELECT blob.rid, uuid, datetime(event.mtime,toLocal()),"
" coalesce(ecomment,comment),"
" search_score()"
|
| ︙ | ︙ | |||
973 974 975 976 977 978 979 980 981 982 983 984 985 986 |
blob_appendf(&x," r=%g", r);
sqlite3_result_text(context, blob_str(&x), -1, fossil_free);
}
#else
sqlite3_result_double(context, r);
#endif
}
/*
** When this routine is called, there already exists a table
**
** x(label,url,score,id,snip).
**
** label: The "name" of the document containing the match
| > > > > > > > > > > > > > > > > > > > > > > > | 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 |
blob_appendf(&x," r=%g", r);
sqlite3_result_text(context, blob_str(&x), -1, fossil_free);
}
#else
sqlite3_result_double(context, r);
#endif
}
/*
** Expects a search pattern string. Makes a copy of the string,
** replaces all non-alphanum ASCII characters with a space, and
** lower-cases all upper-case ASCII characters. The intent is to avoid
** causing errors in FTS5 searches with inputs which contain AND, OR,
** and symbols like #. The caller is responsible for passing the
** result to fossil_free().
*/
char *search_simplify_pattern(const char * zPattern){
char *zPat = mprintf("%s",zPattern);
int i;
for(i=0; zPat[i]; i++){
if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' ';
if( fossil_isupper(zPat[i]) ) zPat[i] = fossil_tolower(zPat[i]);
}
for(i--; i>=0 && zPat[i]==' '; i--){}
if( i<0 ){
fossil_free(zPat);
zPat = mprintf("\"\"");
}
return zPat;
}
/*
** When this routine is called, there already exists a table
**
** x(label,url,score,id,snip).
**
** label: The "name" of the document containing the match
|
| ︙ | ︙ | |||
995 996 997 998 999 1000 1001 |
** The companion full-scan search routine is search_fullscan().
*/
LOCAL void search_indexed(
const char *zPattern, /* The query pattern */
unsigned int srchFlags /* What to search over */
){
Blob sql;
| | < | < < | 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 |
** The companion full-scan search routine is search_fullscan().
*/
LOCAL void search_indexed(
const char *zPattern, /* The query pattern */
unsigned int srchFlags /* What to search over */
){
Blob sql;
char *zPat;
static const char *zSnippetCall;
if( srchFlags==0 ) return;
sqlite3_create_function(g.db, "rank", 1, SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
search_rank_sqlfunc, 0, 0);
zPat = search_simplify_pattern(zPattern);
blob_init(&sql, 0, 0);
if( search_index_type(0)==4 ){
/* If this repo is still using the legacy FTS4 search index, then
** the snippet() function is slightly different */
zSnippetCall = "snippet(ftsidx,'<mark>','</mark>',' ... ',-1,35)";
}else{
/* This is the common case - Using newer FTS5 search index */
|
| ︙ | ︙ | |||
1155 1156 1157 1158 1159 1160 1161 |
}
nRow++;
@ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a>
if( fDebug ){
@ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4))
}
@ <br><span class='snippet'>%z(cleanSnippet(zSnippet)) \
| | | 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 |
}
nRow++;
@ <li><p><a href='%R%s(zUrl)'>%h(zLabel)</a>
if( fDebug ){
@ (%e(db_column_double(&q,3)), %s(db_column_text(&q,4))
}
@ <br><span class='snippet'>%z(cleanSnippet(zSnippet)) \
if( zLabel && zDate && zDate[0] && strstr(zLabel,zDate)==0 ){
@ <small>(%h(zDate))</small>
}
@ </span></li>
if( nLimit && nRow>=nLimit ) break;
}
db_finalize(&q);
if( nRow ){
|
| ︙ | ︙ | |||
1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 | @ CREATE VIRTUAL TABLE IF NOT EXISTS repository.ftsidx @ USING fts5(content="ftscontent", title, body%s); ; static const char zFtsDrop[] = @ DROP TABLE IF EXISTS repository.ftsidx; @ DROP VIEW IF EXISTS repository.ftscontent; @ DROP TABLE IF EXISTS repository.ftsdocs; ; #if INTERFACE /* ** Values for the search-tokenizer config option. */ #define FTS5TOK_NONE 0 /* disabled */ | > | 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 | @ CREATE VIRTUAL TABLE IF NOT EXISTS repository.ftsidx @ USING fts5(content="ftscontent", title, body%s); ; static const char zFtsDrop[] = @ DROP TABLE IF EXISTS repository.ftsidx; @ DROP VIEW IF EXISTS repository.ftscontent; @ DROP TABLE IF EXISTS repository.ftsdocs; @ DROP TABLE IF EXISTS repository.chatfts1; ; #if INTERFACE /* ** Values for the search-tokenizer config option. */ #define FTS5TOK_NONE 0 /* disabled */ |
| ︙ | ︙ | |||
1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 |
iFtsTokenizer = FTS5TOK_TRIGRAM;
}else{
iFtsTokenizer = is_truth(z) ? FTS5TOK_PORTER : FTS5TOK_NONE;
}
fossil_free(z);
return iFtsTokenizer;
}
/*
** Returns a string value suitable for use as the search-tokenizer
** setting's value, depending on the value of z. If z is 0 then the
** current search-tokenizer value is used as the basis for formulating
** the result (which may differ from the current value but will have
** the same meaning). Any unknown/unsupported value is interpreted as
| > > > > > > > > > > > > > > > | 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 |
iFtsTokenizer = FTS5TOK_TRIGRAM;
}else{
iFtsTokenizer = is_truth(z) ? FTS5TOK_PORTER : FTS5TOK_NONE;
}
fossil_free(z);
return iFtsTokenizer;
}
/*
** Returns a string in the form ",tokenize=X", where X is the string
** counterpart of the given FTS5TOK_xyz value. Returns "" if tokType
** does not correspond to a known FTS5 tokenizer.
*/
const char * search_tokenize_arg_for_type(int tokType){
switch( tokType ){
case FTS5TOK_PORTER: return ",tokenize=porter";
case FTS5TOK_UNICODE61: return ",tokenize=unicode61";
case FTS5TOK_TRIGRAM: return ",tokenize=trigram";
case FTS5TOK_NONE:
default: return "";
}
}
/*
** Returns a string value suitable for use as the search-tokenizer
** setting's value, depending on the value of z. If z is 0 then the
** current search-tokenizer value is used as the basis for formulating
** the result (which may differ from the current value but will have
** the same meaning). Any unknown/unsupported value is interpreted as
|
| ︙ | ︙ | |||
1718 1719 1720 1721 1722 1723 1724 |
}
/*
** Create or drop the tables associated with a full-text index.
*/
static int searchIdxExists = -1;
void search_create_index(void){
| < | | < < < | < | 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 |
}
/*
** Create or drop the tables associated with a full-text index.
*/
static int searchIdxExists = -1;
void search_create_index(void){
const char *zExtra =
search_tokenize_arg_for_type(search_tokenizer_type(0));
assert( zExtra );
search_sql_setup(g.db);
db_multi_exec(zFtsSchema/*works-like:"%s"*/, zExtra/*safe-for-%s*/);
searchIdxExists = 1;
}
void search_drop_index(void){
db_multi_exec(zFtsDrop/*works-like:""*/);
searchIdxExists = 0;
|
| ︙ | ︙ | |||
2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 |
*/
void search_rebuild_index(void){
fossil_print("rebuilding the search index...");
fflush(stdout);
search_create_index();
search_fill_index();
search_update_index(search_restrict(SRCH_ALL));
fossil_print(" done\n");
}
/*
** COMMAND: fts-config*
**
** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
| > > > | 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 |
*/
void search_rebuild_index(void){
fossil_print("rebuilding the search index...");
fflush(stdout);
search_create_index();
search_fill_index();
search_update_index(search_restrict(SRCH_ALL));
if( db_table_exists("repository","chat") ){
chat_rebuild_index(1);
}
fossil_print(" done\n");
}
/*
** COMMAND: fts-config*
**
** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
|
| ︙ | ︙ | |||
2378 2379 2380 2381 2382 2383 2384 | return rc; } /* ** Argument f should be a flag accepted by matchinfo() (a valid character | | | 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 |
return rc;
}
/*
** Argument f should be a flag accepted by matchinfo() (a valid character
** in the string passed as the second argument). If it is not, -1 is
** returned. Otherwise, if f is a valid matchinfo flag, the value returned
** is the number of 32-bit integers added to the output array if the
** table has nCol columns and the query nPhrase phrases.
*/
static int fts5MatchinfoFlagsize(int nCol, int nPhrase, char f){
int ret = -1;
switch( f ){
|
| ︙ | ︙ |
Changes to src/security_audit.c.
| ︙ | ︙ | |||
334 335 336 337 338 339 340 |
}
/* Anonymous users probably should not be allowed act as moderators
** for wiki or tickets.
*/
if( hasAnyCap(zAnonCap, "lq5") ){
@ <li><p><b>WARNING:</b>
| | | | 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 |
}
/* Anonymous users probably should not be allowed act as moderators
** for wiki or tickets.
*/
if( hasAnyCap(zAnonCap, "lq5") ){
@ <li><p><b>WARNING:</b>
@ Anonymous users can act as moderators for wiki, tickets, or
@ forum posts. This defeats the whole purpose of moderation.
@ Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum"
@ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5")
@ from users "anonymous" and "nobody"
@ on the <a href="setup_ulist">User Configuration</a> page.
}
/* Check to see if any TH1 scripts are configured to run on a sync
*/
if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'"
" AND length(value)>0") ){
@ <li><p><b>WARNING:</b>
@ TH1 scripts might be configured to run on any sync, push, pull, or
@ clone operation. See the the <a href="%R/xfersetup">/xfersetup</a>
@ page for more information. These TH1 scripts are a potential
@ security concern and so should be carefully audited by a human.
}
/* The strict-manifest-syntax setting should be on. */
if( db_get_boolean("strict-manifest-syntax",1)==0 ){
@ <li><p><b>WARNING:</b>
@ The "strict-manifest-syntax" flag is off. This is a security
@ risk. Turn this setting on (its default) to protect the users
@ of this repository.
|
| ︙ | ︙ | |||
574 575 576 577 578 579 580 |
@ <li><p>
@ Unable to get the system load average. This can prevent Fossil
@ from throttling expensive operations during peak demand.
@ If running in a chroot jail on Linux, verify that the /proc
@ filesystem is mounted within the jail, so that the load average
@ can be obtained from the /proc/loadavg file.
}else {
| | | | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
@ <li><p>
@ Unable to get the system load average. This can prevent Fossil
@ from throttling expensive operations during peak demand.
@ If running in a chroot jail on Linux, verify that the /proc
@ filesystem is mounted within the jail, so that the load average
@ can be obtained from the /proc/loadavg file.
}else {
double r = atof(db_get("max-loadavg", "0.0"));
if( r<=0.0 ){
@ <li><p>
@ Load average limiting is turned off. This can cause the server
@ to bog down if many requests for expensive services (such as
@ large diffs or tarballs) arrive at about the same time.
@ To fix this, set the
@ <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on the
@ <a href='%R/setup_access'>Access Control</a> page to the approximate
@ the number of available cores on your server, or maybe just a little
@ less.
}else if( r>=8.0 ){
@ <li><p>
@ The <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on
|
| ︙ | ︙ | |||
602 603 604 605 606 607 608 |
@ <li><p>
@ The server error log is disabled.
@ To set up an error log,
if( fossil_strcmp(g.zCmdName, "cgi")==0 ){
@ make an entry like "errorlog: <i>FILENAME</i>" in the
@ CGI script at %h(P("SCRIPT_FILENAME")).
}else{
| | | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
@ <li><p>
@ The server error log is disabled.
@ To set up an error log,
if( fossil_strcmp(g.zCmdName, "cgi")==0 ){
@ make an entry like "errorlog: <i>FILENAME</i>" in the
@ CGI script at %h(P("SCRIPT_FILENAME")).
}else{
@ add the "--errorlog <i>FILENAME</i>" option to the
@ "%h(g.argv[0]) %h(g.zCmdName)" command that launched this server.
}
}else{
FILE *pTest = fossil_fopen(g.zErrlog,"a");
if( pTest==0 ){
@ <li><p>
@ <b>Error:</b>
|
| ︙ | ︙ | |||
633 634 635 636 637 638 639 |
@ <li><p> CGI Extensions are enabled with a document root
@ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
@ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
}
if( fileedit_glob()!=0 ){
@ <li><p><a href='%R/fileedit'>Online File Editing</a> is enabled
| | | | 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 |
@ <li><p> CGI Extensions are enabled with a document root
@ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
@ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
}
if( fileedit_glob()!=0 ){
@ <li><p><a href='%R/fileedit'>Online File Editing</a> is enabled
@ for this repository. Clear the
@ <a href='%R/setup_settings'>"fileedit-glob" setting</a> to
@ disable online editing.</p>
}
@ <li><p> User capability summary:
capability_summary();
azCSP = parse_content_security_policy();
if( azCSP==0 ){
@ <li><p> WARNING: No Content Security Policy (CSP) is specified in the
@ header. Though not required, a strong CSP is recommended. Fossil will
@ automatically insert an appropriate CSP if you let it generate the
@ HTML <tt><head></tt> element by omitting <tt><body></tt>
@ from the header configuration in your customized skin.
@
}else{
int ii;
@ <li><p> Content Security Policy:
@ <ol type="a">
for(ii=0; azCSP[ii]; ii++){
@ <li>%h(azCSP[ii])
}
|
| ︙ | ︙ | |||
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 |
@ <blockquote><pre>
@ INSERT INTO private SELECT rid FROM blob WHERE content IS NULL;
@ </pre></blockquote>
@ </p>
table_of_public_phantoms();
@ </li>
}
blob_init(&cmd, 0, 0);
for(i=0; g.argvOrig[i]!=0; i++){
blob_append_escaped_arg(&cmd, g.argvOrig[i], 0);
}
@ <li><p>
@ The command that generated this page:
@ <blockquote>
@ <tt>%h(blob_str(&cmd))</tt>
@ </blockquote></li>
blob_zero(&cmd);
@ </ol>
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
@ <blockquote><pre>
@ INSERT INTO private SELECT rid FROM blob WHERE content IS NULL;
@ </pre></blockquote>
@ </p>
table_of_public_phantoms();
@ </li>
}
@ <li><p>Robot Defenses:
@ <ol type="a">
switch( db_get_int("auto-hyperlink",1) ){
default:
@ <li> No auto-enable of hyperlinks.
break;
case 1:
@ <li> Hyperlinks auto-enabled based on UserAgent and Javascript.
break;
case 2:
@ <li> Hyperlinks auto-enabled based on UserAgent only.
break;
}
z = db_get("max-loadavg",0);
if( z && atof(z)>0.0 ){
@ <li> Maximum load average for expensive requests: %h(z);
}else{
@ <li> No limits on the load average
}
z = db_get("robot-restrict",0);
if( z==0 ){
@ <li> No complex-request constraints on robots
}else{
@ <li> Complex requests limited for pages matching: %h(z)
}
@ </ol>
blob_init(&cmd, 0, 0);
for(i=0; g.argvOrig[i]!=0; i++){
blob_append_escaped_arg(&cmd, g.argvOrig[i], 0);
}
@ <li><p>
if( g.zCgiFile ){
Blob fullname;
blob_init(&fullname, 0, 0);
file_canonical_name(g.zCgiFile, &fullname, 0);
@ The CGI control file for this page is "%h(blob_str(&fullname))".
}
@ The command that generated this page:
@ <blockquote>
@ <tt>%h(blob_str(&cmd))</tt>
@ </blockquote></li>
blob_zero(&cmd);
@ </ol>
|
| ︙ | ︙ | |||
749 750 751 752 753 754 755 | @ <input type="submit" name="cancel" value="Cancel"> @ </form> style_finish_page(); } /* | > > > > > > > > > > > > > > > > > > > > > | | > | | | < < < < < < | < < < < < < < | 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 |
@ <input type="submit" name="cancel" value="Cancel">
@ </form>
style_finish_page();
}
/*
** Output a message explaining that no error log is available.
*/
static void no_error_log_available(void){
@ <p>No error log is configured.
if( g.zCgiFile==0 ){
@ To create an error log, add the "--errorlog FILENAME"
@ command-line option to the command that launches the Fossil server.
}else{
Blob fullname;
blob_init(&fullname, 0, 0);
file_canonical_name(g.zCgiFile, &fullname, 0);
@ To create an error log, edit the CGI control file
@ named "%h(blob_str(&fullname))" to add a line like this:
@ <blockquote><pre>
@ errorlog: <i>FILENAME</i>
@ </pre></blockquote>
blob_reset(&fullname);
}
}
/*
** The maximum number of bytes of the error log to show by default.
*/
#define MXSHOWLOG 500000
/*
** WEBPAGE: errorlog
**
** Show the content of the error log. Only the administrator can view
** this page.
*/
void errorlog_page(void){
i64 szFile;
FILE *in;
char *zLog;
char z[10000];
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Server Error Log");
style_submenu_element("Test", "%R/test-warning");
style_submenu_element("Refresh", "%R/errorlog");
style_submenu_element("Log-Menu", "%R/setup-logmenu");
style_submenu_element("Panics", "%R/paniclog");
style_submenu_element("Non-Hacks", "%R/hacklog?not");
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
no_error_log_available();
style_finish_page();
return;
}
if( P("truncate1") && cgi_csrf_safe(2) ){
fclose(fopen(g.zErrlog,"w"));
}
if( P("download") ){
|
| ︙ | ︙ | |||
814 815 816 817 818 819 820 |
@ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log:
@ <input type="submit" name="truncate1" value="Confirm">
@ <input type="submit" name="cancel" value="Cancel">
@ </form>
style_finish_page();
return;
}
| > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 856 857 858 859 860 861 862 863 864 865 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 934 935 936 937 938 939 940 941 942 943 944 945 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 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 |
@ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log:
@ <input type="submit" name="truncate1" value="Confirm">
@ <input type="submit" name="cancel" value="Cancel">
@ </form>
style_finish_page();
return;
}
zLog = file_canonical_name_dup(g.zErrlog);
@ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size.
fossil_free(zLog);
style_submenu_element("Download", "%R/errorlog?download");
style_submenu_element("Truncate", "%R/errorlog?truncate");
in = fossil_fopen(g.zErrlog, "rb");
if( in==0 ){
@ <p class='generalError'>Unable to open that file for reading!</p>
style_finish_page();
return;
}
if( szFile>MXSHOWLOG && P("all")==0 ){
@ <form action="%R/errorlog" method="POST">
@ <p>Only the last %,d(MXSHOWLOG) bytes are shown.
@ <input type="submit" name="all" value="Show All">
@ </form>
fseek(in, -MXSHOWLOG, SEEK_END);
}
@ <hr>
@ <pre>
while( fgets(z, sizeof(z), in) ){
@ %h(z)\
}
fclose(in);
@ </pre>
style_finish_page();
}
/*
** WEBPAGE: paniclog
**
** Scan the error log for panics. Show all panic messages, ignoring all
** other error log entries.
*/
void paniclog_page(void){
i64 szFile;
char *zLog;
FILE *in;
int bOutput = 0;
int prevWasTime = 0;
char z[10000];
char zTime[10000];
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Server Panic Log");
style_submenu_element("Log-Menu", "%R/setup-logmenu");
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
no_error_log_available();
style_finish_page();
return;
}
in = fossil_fopen(g.zErrlog, "rb");
if( in==0 ){
@ <p class='generalError'>Unable to open that file for reading!</p>
style_finish_page();
return;
}
szFile = file_size(g.zErrlog, ExtFILE);
zLog = file_canonical_name_dup(g.zErrlog);
@ Panic messages contained within the %lld(szFile)-byte
@ <a href="%R/errorlog?all">error log</a> found at
@ "%h(zLog)".
fossil_free(zLog);
@ <hr>
@ <pre>
while( fgets(z, sizeof(z), in) ){
if( prevWasTime
&& (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0)
){
@ %h(zTime)\
bOutput = 1;
}
if( strncmp(z, "--------", 8)==0 ){
size_t n = strlen(z);
memcpy(zTime, z, n+1);
prevWasTime = 1;
bOutput = 0;
}else{
prevWasTime = 0;
}
if( bOutput ){
@ %h(z)\
}
}
fclose(in);
@ </pre>
style_finish_page();
}
/*
** WEBPAGE: hacklog
**
** Scan the error log for "possible hack attempt" entries Show hack
** attempt messages only, omitting all others. Or if the "not" query
** parameter is present, show only messages that are not hack attempts.
*/
void hacklog_page(void){
i64 szFile;
char *zLog;
FILE *in;
int bOutput = 0;
int prevWasTime = 0;
int isNot = P("not")!=0;
char z[10000];
char zTime[10000];
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Server Hack Log");
style_submenu_element("Log-Menu", "%R/setup-logmenu");
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
no_error_log_available();
style_finish_page();
return;
}
in = fossil_fopen(g.zErrlog, "rb");
if( in==0 ){
@ <p class='generalError'>Unable to open that file for reading!</p>
style_finish_page();
return;
}
szFile = file_size(g.zErrlog, ExtFILE);
zLog = file_canonical_name_dup(g.zErrlog);
@ %s(isNot?"Non-hack":"Hack") messages contained within the %lld(szFile)-byte
@ <a href="%R/errorlog?all">error log</a> found at
@ "%h(zLog)".
fossil_free(zLog);
@ <hr>
@ <pre>
while( fgets(z, sizeof(z), in) ){
if( prevWasTime
&& ((strncmp(z,"possible hack attempt - 418 ", 27)==0) ^ isNot)
){
@ %h(zTime)\
bOutput = 1;
}
if( strncmp(z, "--------", 8)==0 ){
size_t n = strlen(z);
memcpy(zTime, z, n+1);
prevWasTime = 1;
bOutput = 0;
}else{
prevWasTime = 0;
}
if( bOutput ){
@ %h(z)\
}
}
fclose(in);
@ </pre>
style_finish_page();
}
|
Changes to src/setup.c.
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
void setup_menu_entry(
const char *zTitle,
const char *zLink,
const char *zDesc
){
@ <tr><td valign="top" align="right">
if( zLink && zLink[0] ){
| | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
void setup_menu_entry(
const char *zTitle,
const char *zLink,
const char *zDesc
){
@ <tr><td valign="top" align="right">
if( zLink && zLink[0] ){
@ <a href="%s(zLink)"><nobr>%h(zTitle)</nobr></a>
}else{
@ %h(zTitle)
}
@ </td><td width="5"></td><td valign="top">%h(zDesc)</td></tr>
}
|
| ︙ | ︙ | |||
141 142 143 144 145 146 147 |
"Configure URL aliases");
if( setup_user ){
setup_menu_entry("Notification", "setup_notification",
"Automatic notifications of changes via outbound email");
setup_menu_entry("Transfers", "xfersetup",
"Configure the transfer system for this repository");
}
| | < < | < < | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
"Configure URL aliases");
if( setup_user ){
setup_menu_entry("Notification", "setup_notification",
"Automatic notifications of changes via outbound email");
setup_menu_entry("Transfers", "xfersetup",
"Configure the transfer system for this repository");
}
setup_menu_entry("Skins", "setup_skin_admin",
"Select and/or modify the web interface \"skins\"");
setup_menu_entry("Moderation", "setup_modreq",
"Enable/Disable requiring moderator approval of Wiki and/or Ticket"
" changes and attachments.");
setup_menu_entry("Ad-Unit", "setup_adunit",
"Edit HTML text for an ad unit inserted after the menu bar");
setup_menu_entry("URLs & Checkouts", "urllist",
"Show URLs used to access this repo and known check-outs");
if( setup_user ){
setup_menu_entry("Web-Cache", "cachestat",
"View the status of the expensive-page cache");
}
setup_menu_entry("Logo", "setup_logo",
"Change the logo and background images for the server");
setup_menu_entry("Shunned", "shun",
"Show artifacts that are shunned by this repository");
setup_menu_entry("Log Files", "setup-logmenu",
"A menu of available log files");
setup_menu_entry("Unversioned Files", "uvlist?byage=1",
"Show all unversioned files held");
setup_menu_entry("Stats", "stat",
"Repository Status Reports");
setup_menu_entry("Sitemap", "sitemap",
"Links to miscellaneous pages");
if( setup_user ){
setup_menu_entry("SQL", "admin_sql",
"Enter raw SQL commands");
setup_menu_entry("TH1", "admin_th1",
"Enter raw TH1 commands");
}
@ </table>
style_finish_page();
}
/*
** WEBPAGE: setup-logmenu
**
** Show a menu of available log renderings accessible to an administrator,
** together with a succinct explanation of each.
**
** This page is only accessible by administrators.
*/
void setup_logmenu_page(void){
Blob desc;
blob_init(&desc, 0, 0);
/* Administrator access only */
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Log Menu");
@ <table border="0" cellspacing="3">
setup_menu_entry("Admin Log", "admin_log",
"The admin log records configuration changes to the repository.\n"
"The admin log is stored in the \"admin_log\" table of the repository.\n"
);
setup_menu_entry("Artifact Log", "rcvfromlist",
"The artifact log records when new content is added to the repository.\n"
"The time and date and origin of the new content is entered into the\n"
"Log. The artifact log is always on and is stored in the \"rcvfrom\"\n"
"table of the repository.\n"
);
blob_appendf(&desc,
"The error log is a separate text file to which warning and error\n"
"messages are appended. A single error log can and often is shared\n"
"across multiple repositories.\n"
);
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
blob_appendf(&desc,"The error log is disabled for this repository.");
}else{
blob_appendf(&desc,"In this repository, the error log is in the file"
"named \"%s\".", g.zErrlog);
}
setup_menu_entry("Error Log", "errorlog", blob_str(&desc));
blob_reset(&desc);
setup_menu_entry("Panic Log", "paniclog",
"The panic log is a filtering of the Error Log that shows only the\n"
"most important messages - assertion faults, segmentation faults, and\n"
"similar malfunctions."
);
setup_menu_entry("User Log", "user_log",
"The user log is a record of login attempts. The user log is stored\n"
"in the \"accesslog\" table of the respository.\n"
);
setup_menu_entry("Hack Log", "hacklog",
"All 418 hack attempts"
);
setup_menu_entry("Non-Hack Log", "hacklog?not",
"All log messages that are not hack attempts"
);
@ </table>
style_finish_page();
}
/*
** Generate a checkbox for an attribute.
*/
void onoff_attribute(
const char *zLabel, /* The text label on the checkbox */
const char *zVar, /* The corresponding row in the CONFIG table */
const char *zQParm, /* The query parameter */
|
| ︙ | ︙ | |||
424 425 426 427 428 429 430 431 432 433 434 435 436 437 | @ computer is too large. Set the threshold for disallowing expensive @ computations here. Set this to 0.0 to disable the load average limit. @ This limit is only enforced on Unix servers. On Linux systems, @ access to the /proc virtual filesystem is required, which means this limit @ might not work inside a chroot() jail. @ (Property: "max-loadavg")</p> @ <hr> @ <p><input type="submit" name="submit" value="Apply Changes"></p> @ </div></form> db_end_transaction(0); style_finish_page(); } | > > > > > > > > > > > > > > > | 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 |
@ computer is too large. Set the threshold for disallowing expensive
@ computations here. Set this to 0.0 to disable the load average limit.
@ This limit is only enforced on Unix servers. On Linux systems,
@ access to the /proc virtual filesystem is required, which means this limit
@ might not work inside a chroot() jail.
@ (Property: "max-loadavg")</p>
@ <hr>
@ <p><b>Do not allow robots to make complex requests
@ against the following pages.</b>
@ <p> A "complex request" is an HTTP request that has one or more query
@ parameters. Some robots will spend hours juggling around query parameters
@ or even forging fake query parameters in an effort to discover new
@ behavior or to find an SQL injection opportunity or similar. This can
@ waste hours of CPU time and gigabytes of bandwidth on the server. A
@ suggested value for this setting is:
@ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>".
@ (Property: robot-restrict)
@ <p>
textarea_attribute("", 2, 80,
"robot-restrict", "rbrestrict", "", 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ | |||
586 587 588 589 590 591 592 |
@ for users who are not logged in. (Property: "require-captcha")</p>
@ <hr>
entry_attribute("Public pages", 30, "public-pages",
"pubpage", "", 0);
@ <p>A comma-separated list of glob patterns for pages that are accessible
@ without needing a login and using the privileges given by the
| | | 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 |
@ for users who are not logged in. (Property: "require-captcha")</p>
@ <hr>
entry_attribute("Public pages", 30, "public-pages",
"pubpage", "", 0);
@ <p>A comma-separated list of glob patterns for pages that are accessible
@ without needing a login and using the privileges given by the
@ "Default privileges" setting below.
@
@ <p>Example use case: Set this field to "/doc/trunk/www/*" and set
@ the "Default privileges" to include the "o" privilege
@ to give anonymous users read-only permission to the
@ latest version of the embedded documentation in the www/ folder without
@ allowing them to see the rest of the source code.
@ (Property: "public-pages")
|
| ︙ | ︙ | |||
679 680 681 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 |
**
** Change how the current repository participates in a login
** group.
*/
void setup_login_group(void){
const char *zGroup;
char *zErrMsg = 0;
Blob fullName;
char *zSelfRepo;
const char *zRepo = PD("repo", "");
const char *zLogin = PD("login", "");
const char *zPw = PD("pw", "");
const char *zNewName = PD("newname", "New Login Group");
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
file_canonical_name(g.zRepositoryName, &fullName, 0);
zSelfRepo = fossil_strdup(blob_str(&fullName));
blob_reset(&fullName);
if( P("join")!=0 ){
login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg);
}else if( P("leave") ){
login_group_leave(&zErrMsg);
}
style_set_current_feature("setup");
style_header("Login Group Configuration");
if( zErrMsg ){
@ <p class="generalError">%s(zErrMsg)</p>
}
zGroup = login_group_name();
| > > > | 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 |
**
** Change how the current repository participates in a login
** group.
*/
void setup_login_group(void){
const char *zGroup;
char *zErrMsg = 0;
Stmt q;
Blob fullName;
char *zSelfRepo;
const char *zRepo = PD("repo", "");
const char *zLogin = PD("login", "");
const char *zPw = PD("pw", "");
const char *zNewName = PD("newname", "New Login Group");
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
file_canonical_name(g.zRepositoryName, &fullName, 0);
zSelfRepo = fossil_strdup(blob_str(&fullName));
blob_reset(&fullName);
if( P("join")!=0 ){
login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg);
}else if( P("leave") ){
login_group_leave(&zErrMsg);
}else if( P("rotate") ){
captcha_secret_rotate();
}
style_set_current_feature("setup");
style_header("Login Group Configuration");
if( zErrMsg ){
@ <p class="generalError">%s(zErrMsg)</p>
}
zGroup = login_group_name();
|
| ︙ | ︙ | |||
740 741 742 743 744 745 746 |
@ value="%h(zNewName)" name="newname">
@ (only used if creating a new login-group).</td></tr>
@
@ <tr><td colspan="3" align="center">
@ <input type="submit" value="Join" name="join"></td></tr>
@ </table></blockquote></div></form>
}else{
| < | 822 823 824 825 826 827 828 829 830 831 832 833 834 835 |
@ value="%h(zNewName)" name="newname">
@ (only used if creating a new login-group).</td></tr>
@
@ <tr><td colspan="3" align="center">
@ <input type="submit" value="Join" name="join"></td></tr>
@ </table></blockquote></div></form>
}else{
int n = 0;
@ <p>This repository (in the file "%h(zSelfRepo)")
@ is currently part of the "<b>%h(zGroup)</b>" login group.
@ Other repositories in that group are:</p>
@ <table border="0" cellspacing="4">
@ <tr><td colspan="2"><th align="left">Project Name<td>
@ <th align="left">Repository File</tr>
|
| ︙ | ︙ | |||
768 769 770 771 772 773 774 |
@ <td>%h(zTitle)<td width="10"><td>%h(zRepo)</tr>
}
db_finalize(&q);
@ </table>
@
@ <p><form action="%R/setup_login_group" method="post"><div>
login_insert_csrf_secret();
| | > > > > > > > > | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 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 |
@ <td>%h(zTitle)<td width="10"><td>%h(zRepo)</tr>
}
db_finalize(&q);
@ </table>
@
@ <p><form action="%R/setup_login_group" method="post"><div>
login_insert_csrf_secret();
@ <p>To leave this login group press:
@ <input type="submit" value="Leave Login Group" name="leave">
@ <p>Setting a common captcha-secret on all repositories in the login-group
@ allows anonymous logins for one repository in the login group to be used
@ by all other repositories of the group within the same domain. Warning:
@ If a captcha dialog was painted before setting the common captcha-secret
@ and the "Speak password for 'anonymous'" button is pressed afterwards,
@ the spoken text will be incorrect.
@ <input type="submit" name="rotate" value="Set common captcha-secret">
@ </form></p>
}
@ <hr><h2>Implementation Details</h2>
@ <p>The following are fields from the CONFIG table related to login-groups.
@ </p>
@ <table border='1' cellspacing="0" cellpadding="4"\
@ class='sortable' data-column-types='ttt' data-init-sort='1'>
@ <thead><tr>
@ <th>Config.Name<th>Config.Value<th>Config.mtime</tr>
@ </thead><tbody>
db_prepare(&q, "SELECT name, value, datetime(mtime,'unixepoch') FROM config"
" WHERE name GLOB 'peer-*'"
" OR name GLOB 'project-*'"
" OR name GLOB 'login-group-*'"
" ORDER BY name");
while( db_step(&q)==SQLITE_ROW ){
@ <tr><td>%h(db_column_text(&q,0))</td>
@ <td>%h(db_column_text(&q,1))</td>
@ <td>%h(db_column_text(&q,2))</td></tr>
}
db_finalize(&q);
@ </tbody></table>
@ <h2>Interpretation</h2>
@ <ul>
@ <li><p><b>login-group-code</b> →
@ A random code assigned to each login-group. The login-group-code is
@ a unique identifier for the login-group.
@
@ <li><p><b>login-group-name</b> →
@ The human-readable name of the login-group.
@
@ <li><p><b>project-code</b> →
@ A random code assigned to each project. The project-code is
@ a unique identifier for the project. Multiple repositories can share
@ the same project-code. When two or more repositories have the same
@ project code, that mean those repositories are clones of each other.
@ Repositories are only able to sync if they share the same project-code.
@
@ <li><p><b>project-description</b> →
@ A description of project in this repository. This is a verbose form
@ of project-name. This description can be edited in the second entry
@ box on the <a href="./setup_config">Setup/Configuration page</a>.
@
@ <li><p><b>project-name</b> →
@ The human-readable name for the project. The project-name can be
@ modified in the first entry on the
@ <a href="./setup_config">Setup/Configuration page</a>.
@
@ <li><p><b>peer-repo-<i>CODE</i></b> →
@ <i>CODE</i> is 16-character prefix of the project-code for another
@ repository that is part of the same login-group. The value is the
@ filename for the peer repository.
@
@ <li><p><b>peer-name-<i>CODE</i></b> →
@ <i>CODE</i> is 16-character prefix of the project-code for another
@ repository that is part of the same login-group. The value is
@ project-name value for the other repository.
@ </ul>
style_table_sorter();
style_finish_page();
}
/*
** WEBPAGE: setup_timeline
**
** Edit administrative settings controlling the display of
|
| ︙ | ︙ | |||
1199 1200 1201 1202 1203 1204 1205 |
@ choices (such as the hamburger button) to the menu that are not shown
@ on this list. (Property: mainmenu)
@ <p>
if(P("resetMenu")!=0){
db_unset("mainmenu", 0);
cgi_delete_parameter("mmenu");
}
| | | 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 |
@ choices (such as the hamburger button) to the menu that are not shown
@ on this list. (Property: mainmenu)
@ <p>
if(P("resetMenu")!=0){
db_unset("mainmenu", 0);
cgi_delete_parameter("mmenu");
}
textarea_attribute("Main Menu", 12, 80,
"mainmenu", "mmenu", style_default_mainmenu(), 0);
@ </p>
@ <p><input type='checkbox' id='cbResetMenu' name='resetMenu' value='1'>
@ <label for='cbResetMenu'>Reset menu to default value</label>
@ </p>
@ <hr>
@ <p>Extra links to appear on the <a href="%R/sitemap">/sitemap</a> page,
|
| ︙ | ︙ | |||
1227 1228 1229 1230 1231 1232 1233 | @ If capexpr evaluates to true, then the entry is shown. If not, @ the entry is omitted. "*" is always true. @ </ol> @ @ <p>The default value is blank, meaning no added entries. @ (Property: sitemap-extra) @ <p> | | | 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 |
@ If capexpr evaluates to true, then the entry is shown. If not,
@ the entry is omitted. "*" is always true.
@ </ol>
@
@ <p>The default value is blank, meaning no added entries.
@ (Property: sitemap-extra)
@ <p>
textarea_attribute("Custom Sitemap Entries", 8, 80,
"sitemap-extra", "smextra", "", 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ | |||
1960 1961 1962 1963 1964 1965 1966 |
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_set_current_feature("setup");
style_header("Admin Log");
| | < < | 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 |
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_set_current_feature("setup");
style_header("Admin Log");
style_submenu_element("Log-Menu", "setup-logmenu");
create_admin_log_table();
limit = atoi(PD("n","200"));
ofst = atoi(PD("x","0"));
fLogEnabled = db_get_boolean("admin-log", 0);
@ <div>Admin logging is %s(fLogEnabled?"on":"off").
@ (Change this on the <a href="setup_settings">settings</a> page.)</div>
|
| ︙ | ︙ |
Changes to src/setupuser.c.
| ︙ | ︙ | |||
808 809 810 811 812 813 814 | @ subscript suffix @ indicates the privileges of <span class="usertype">anonymous</span> that @ are inherited by all logged-in users. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>" | | | 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 | @ subscript suffix @ indicates the privileges of <span class="usertype">anonymous</span> that @ are inherited by all logged-in users. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>" @ subscript suffix indicates the privileges of @ <span class="usertype">developer</span> that @ are inherited by all users with the @ <span class="capability">Developer</span> privilege. @ </p></li> @ @ <li><p> @ The "<span class="ueditInheritReader"><sub>R</sub></span>" subscript suffix |
| ︙ | ︙ | |||
937 938 939 940 941 942 943 | @ template for users who are allowed more access than @ <span class="usertype">anonymous</span>, @ but less than a <span class="usertype">developer</span>. @ </p></li> @ </ul> style_finish_page(); } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 937 938 939 940 941 942 943 944 945 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 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 |
@ template for users who are allowed more access than
@ <span class="usertype">anonymous</span>,
@ but less than a <span class="usertype">developer</span>.
@ </p></li>
@ </ul>
style_finish_page();
}
/*
** WEBPAGE: setup_uinfo
**
** Detailed information about a user account, available to administrators
** only.
**
** u=UID
** l=LOGIN
*/
void setup_uinfo_page(void){
Stmt q;
Blob sql;
const char *zLogin;
int uid;
/* Must have ADMIN privileges to access this page
*/
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
style_set_current_feature("setup");
zLogin = P("l");
uid = atoi(PD("u","0"));
if( zLogin==0 && uid==0 ){
uid = db_int(1,"SELECT uid FROM user");
}
blob_init(&sql, 0, 0);
blob_append_sql(&sql,
"SELECT "
/* 0 */ "uid,"
/* 1 */ "login,"
/* 2 */ "cap,"
/* 3 */ "cookie,"
/* 4 */ "datetime(cexpire),"
/* 5 */ "info,"
/* 6 */ "datetime(user.mtime,'unixepoch'),"
);
if( db_table_exists("repository","subscriber") ){
blob_append_sql(&sql,
/* 7 */ "subscriberId,"
/* 8 */ "semail,"
/* 9 */ "sverified,"
/* 10 */ "date(lastContact+2440587.5)"
" FROM user LEFT JOIN subscriber ON suname=login"
);
}else{
blob_append_sql(&sql,
/* 7 */ "NULL,"
/* 8 */ "NULL,"
/* 9 */ "NULL,"
/* 10 */ "NULL"
" FROM user"
);
}
if( zLogin!=0 ){
blob_append_sql(&sql, " WHERE login=%Q", zLogin);
}else{
blob_append_sql(&sql, " WHERE uid=%d", uid);
}
db_prepare(&q, "%s", blob_sql_text(&sql));
blob_zero(&sql);
if( db_step(&q)!=SQLITE_ROW ){
style_header("No Such User");
if( zLogin ){
@ <p>Cannot find any information on user %h(zLogin).
}else{
@ <p>Cannot find any information on userid %d(uid).
}
style_finish_page();
db_finalize(&q);
return;
}
style_header("User %h", db_column_text(&q,1));
@ <table class="label-value">
@ <tr><th>uid:</th><td>%d(db_column_int(&q,0))
@ (<a href="%R/setup_uedit?id=%d(db_column_int(&q,0))">edit</a>)</td></tr>
@ <tr><th>login:</th><td>%h(db_column_text(&q,1))</td></tr>
@ <tr><th>capabilities:</th><td>%h(db_column_text(&q,2))</th></tr>
@ <tr><th valign="top">info:</th>
@ <td valign="top"><span style='white-space:pre-line;'>\
@ %h(db_column_text(&q,5))</span></td></tr>
@ <tr><th>user.mtime:</th><td>%h(db_column_text(&q,6))</td></tr>
if( db_column_type(&q,7)!=SQLITE_NULL ){
@ <tr><th>subscriberId:</th><td>%d(db_column_int(&q,7))
@ (<a href="%R/alerts?sid=%d(db_column_int(&q,7))">edit</a>)</td></tr>
@ <tr><th>semail:</th><td>%h(db_column_text(&q,8))</td></tr>
@ <tr><th>verified:</th><td>%s(db_column_int(&q,9)?"yes":"no")</td></th>
@ <tr><th>lastContact:</th><td>%h(db_column_text(&q,10))</td></tr>
}
@ </table>
db_finalize(&q);
style_finish_page();
}
|
Changes to src/sha1.c.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 | ** ** Downloaded on 2017-03-01 then repackaged to work with Fossil ** and makeheaders. */ #if FOSSIL_HARDENED_SHA1 #if INTERFACE | | > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
**
** Downloaded on 2017-03-01 then repackaged to work with Fossil
** and makeheaders.
*/
#if FOSSIL_HARDENED_SHA1
#if INTERFACE
typedef void(*collision_block_callback)(uint64_t, const uint32_t*,
const uint32_t*, const uint32_t*, const uint32_t*);
struct SHA1_CTX {
uint64_t total;
uint32_t ihv[5];
unsigned char buffer[64];
int bigendian;
int found_collision;
int safe_hash;
|
| ︙ | ︙ |
Changes to src/sha1hard.c.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
void sha1_message_expansion(uint32_t W[80]);
void sha1_compression(uint32_t ihv[5], const uint32_t m[16]);
void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]);
void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5]);
extern sha1_recompression_type sha1_recompression_step[80];
typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
typedef struct {
| | | | | | | | | | | | | | | | | 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 |
void sha1_message_expansion(uint32_t W[80]);
void sha1_compression(uint32_t ihv[5], const uint32_t m[16]);
void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]);
void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5]);
extern sha1_recompression_type sha1_recompression_step[80];
typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
typedef struct {
uint64_t total;
uint32_t ihv[5];
unsigned char buffer[64];
int bigendian;
int found_collision;
int safe_hash;
int detect_coll;
int ubc_check;
int reduced_round_coll;
collision_block_callback callback;
uint32_t ihv1[5];
uint32_t ihv2[5];
uint32_t m1[80];
uint32_t m2[80];
uint32_t states[80][5];
} SHA1_CTX;
/******************** File: lib/ubc_check.c **************************/
/***
* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
* Distributed under the MIT Software License.
* See accompanying file LICENSE.txt or copy at
|
| ︙ | ︙ |
Changes to src/shun.c.
| ︙ | ︙ | |||
119 120 121 122 123 124 125 |
}
@ are no longer being shunned.</p>
}else{
@ <p class="noMoreShun">Artifact(s)<br>
for( p = zUuid ; *p ; p += strlen(p)+1 ){
@ %s(p)<br>
}
| | | | | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
}
@ are no longer being shunned.</p>
}else{
@ <p class="noMoreShun">Artifact(s)<br>
for( p = zUuid ; *p ; p += strlen(p)+1 ){
@ %s(p)<br>
}
@ will no longer be shunned but they may not exist in the repository.
@ It may be necessary to rebuild the repository
@ before the artifact content can be pulled in
@ from other repositories.</p>
}
}
if( zUuid && P("add") && cgi_csrf_safe(2) ){
const char *p = zUuid;
int rid, tagid;
while( *p ){
db_multi_exec(
|
| ︙ | ︙ | |||
372 373 374 375 376 377 378 |
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Artifact Receipts");
| | < < | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Artifact Receipts");
style_submenu_element("Log-Menu", "setup-logmenu");
if( showAll ){
ofst = 0;
}else{
style_submenu_element("All", "rcvfromlist?all=1");
}
if( ofst>0 ){
style_submenu_element("Newer", "rcvfromlist?ofst=%d",
|
| ︙ | ︙ |
Changes to src/sitemap.c.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 |
g.jsHref = 0;
}
srchFlags = search_restrict(SRCH_ALL);
if( !isPopup ){
style_header("Site Map");
style_adunit_config(ADUNIT_RIGHT_OK);
}
| | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
g.jsHref = 0;
}
srchFlags = search_restrict(SRCH_ALL);
if( !isPopup ){
style_header("Site Map");
style_adunit_config(ADUNIT_RIGHT_OK);
}
@ <ul id="sitemap" class="columns" style="column-width:20em">
if( (e&1)==0 ){
@ <li>%z(href("%R/home"))Home Page</a>
}
#if 0 /* Removed 2021-01-26 */
for(i=0; i<sizeof(aExtra)/sizeof(aExtra[0]); i++){
|
| ︙ | ︙ | |||
150 151 152 153 154 155 156 |
}
@ <li>%z(href("%R/docsrch"))Documentation Search</a></li>
}
#endif
if( inSublist ){
@ </ul>
| | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
}
@ <li>%z(href("%R/docsrch"))Documentation Search</a></li>
}
#endif
if( inSublist ){
@ </ul>
inSublist = 0;
}
@ </li>
if( g.perm.Read ){
const char *zEditGlob = db_get("fileedit-glob","");
@ <li>%z(href("%R/tree"))File Browser</a>
@ <ul>
@ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view,
|
| ︙ | ︙ |
Changes to src/skins.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** ** Implementation of the Setup page for "skins". */ #include "config.h" #include <assert.h> #include "skins.h" /* ** An array of available built-in skins. ** ** To add new built-in skins: ** ** 1. Pick a name for the new skin. (Here we use "xyzzy"). ** | > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ** ** Implementation of the Setup page for "skins". */ #include "config.h" #include <assert.h> #include "skins.h" /* ** SETTING: default-skin width=16 ** ** If the text value if this setting is the name of a built-in skin ** then the named skin becomes the default skin for the repository. */ /* ** An array of available built-in skins. ** ** To add new built-in skins: ** ** 1. Pick a name for the new skin. (Here we use "xyzzy"). ** |
| ︙ | ︙ | |||
43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
} aBuiltinSkin[] = {
{ "Default", "default", 0 },
{ "Ardoise", "ardoise", 0 },
{ "Black & White", "black_and_white", 0 },
{ "Blitz", "blitz", 0 },
{ "Dark Mode", "darkmode", 0 },
{ "Eagle", "eagle", 0 },
{ "Khaki", "khaki", 0 },
{ "Original", "original", 0 },
{ "Plain Gray", "plain_gray", 0 },
{ "Xekri", "xekri", 0 },
};
/*
| > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
} aBuiltinSkin[] = {
{ "Default", "default", 0 },
{ "Ardoise", "ardoise", 0 },
{ "Black & White", "black_and_white", 0 },
{ "Blitz", "blitz", 0 },
{ "Dark Mode", "darkmode", 0 },
{ "Eagle", "eagle", 0 },
{ "Étienne", "etienne", 0 },
{ "Khaki", "khaki", 0 },
{ "Original", "original", 0 },
{ "Plain Gray", "plain_gray", 0 },
{ "Xekri", "xekri", 0 },
};
/*
|
| ︙ | ︙ | |||
73 74 75 76 77 78 79 | static char *zAltSkinDir = 0; static int iDraftSkin = 0; /* ** Used by skin_use_alternative() to store the current skin rank skin ** so that the /skins page can, if warranted, warn the user that skin ** changes won't have any effect. */ | | > > > > > > > > > > > > > > > > > | 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 | static char *zAltSkinDir = 0; static int iDraftSkin = 0; /* ** Used by skin_use_alternative() to store the current skin rank skin ** so that the /skins page can, if warranted, warn the user that skin ** changes won't have any effect. */ static int nSkinRank = 6; /* ** How the specific skin being used was chosen */ #if INTERFACE #define SKIN_FROM_DRAFT 0 /* The "draftN" prefix on the PATH_INFO */ #define SKIN_FROM_CMDLINE 1 /* --skin option to server command-line */ #define SKIN_FROM_CGI 2 /* skin: parameter in CGI script */ #define SKIN_FROM_QPARAM 3 /* skin= query parameter */ #define SKIN_FROM_COOKIE 4 /* skin= from fossil_display_settings cookie*/ #define SKIN_FROM_SETTING 5 /* Built-in named by "default-skin" setting */ #define SKIN_FROM_CUSTOM 6 /* Skin values in CONFIG table */ #define SKIN_FROM_DEFAULT 7 /* The built-in named "default" */ #define SKIN_FROM_UNKNOWN 8 /* Do not yet know which skin to use */ #endif /* INTERFACE */ static int iSkinSource = SKIN_FROM_UNKNOWN; /* ** Skin details are a set of key/value pairs that define display ** attributes of the skin that cannot be easily specified using CSS ** or that need to be known on the server-side. ** ** The following array holds the value for all known skin details. |
| ︙ | ︙ | |||
122 123 124 125 126 127 128 | ** preferred ranking, making it otherwise more invasive to tell the ** internals "the --skin flag ranks higher than a URL parameter" (the ** former gets initialized before both URL parameters and the /draft ** path determination). ** ** The rankings were initially defined in ** https://fossil-scm.org/forum/forumpost/caf8c9a8bb | | | > | > | | | > > > > | > | > | > > > > > > > > > > > > | > | 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 192 193 194 195 196 197 198 199 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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
** preferred ranking, making it otherwise more invasive to tell the
** internals "the --skin flag ranks higher than a URL parameter" (the
** former gets initialized before both URL parameters and the /draft
** path determination).
**
** The rankings were initially defined in
** https://fossil-scm.org/forum/forumpost/caf8c9a8bb
** but where subsequently revised:
**
** 0) A skin name matching the glob pattern "draft[1-9]" at the start of
** the PATH_INFO.
**
** 1) The --skin flag for commands like "fossil ui", "fossil server", or
** "fossil http", or the "skin:" CGI config setting.
**
** 2) The "skin" display setting cookie or URL argument, in that
** order. If the "skin" URL argument is provided and refers to a legal
** skin then that will update the display cookie. If the skin name is
** illegal it is silently ignored.
**
** 3) The built-in skin identfied by the "default-skin" setting, if such
** a setting exists and matches one of the built-in skin names.
**
** 4) Skin properties (settings "css", "details", "footer", "header",
** and "js") from the CONFIG db table
**
** 5) The built-in skin named "default"
**
** The iSource integer privides additional detail about where the skin
**
** As a special case, a NULL or empty name resets zAltSkinDir and
** pAltSkin to 0 to indicate that the current config-side skin should
** be used (rank 3, above), then returns 0.
*/
char *skin_use_alternative(const char *zName, int rank, int iSource){
int i;
Blob err = BLOB_INITIALIZER;
if(rank > nSkinRank) return 0;
nSkinRank = rank;
if( zName && 1==rank && strchr(zName, '/')!=0 ){
zAltSkinDir = fossil_strdup(zName);
iSkinSource = iSource;
return 0;
}
if( zName && sqlite3_strglob("draft[1-9]", zName)==0 ){
skin_use_draft(zName[5] - '0');
iSkinSource = iSource;
return 0;
}
if(!zName || !*zName){
pAltSkin = 0;
zAltSkinDir = 0;
return 0;
}
if( fossil_strcmp(zName, "custom")==0 ){
pAltSkin = 0;
zAltSkinDir = 0;
iSkinSource = iSource;
return 0;
}
for(i=0; i<count(aBuiltinSkin); i++){
if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){
pAltSkin = &aBuiltinSkin[i];
iSkinSource = iSource;
return 0;
}
}
blob_appendf(&err, "available skins: %s", aBuiltinSkin[0].zLabel);
for(i=1; i<count(aBuiltinSkin); i++){
blob_append(&err, " ", 1);
blob_append(&err, aBuiltinSkin[i].zLabel, -1);
}
return blob_str(&err);
}
/*
** Look for the --skin command-line option and process it. Or
** call fossil_fatal() if an unknown skin is specified.
**
** This routine is called during command-line parsing for commands
** like "fossil ui" and "fossil http".
*/
void skin_override(void){
const char *zSkin = find_option("skin",0,1);
if( zSkin ){
char *zErr = skin_use_alternative(zSkin, 1, SKIN_FROM_CMDLINE);
if( zErr ) fossil_fatal("%s", zErr);
}
}
/*
** Use one of the draft skins.
*/
void skin_use_draft(int i){
iDraftSkin = i;
iSkinSource = SKIN_FROM_DRAFT;
}
/*
** The following routines return the various components of the skin
** that should be used for the current run.
**
** zWhat is one of: "css", "header", "footer", "details", "js"
|
| ︙ | ︙ | |||
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
if( file_isfile(z, ExtFILE) ){
Blob x;
blob_read_from_file(&x, z, ExtFILE);
fossil_free(z);
return blob_str(&x);
}
fossil_free(z);
}
if( pAltSkin ){
z = mprintf("skins/%s/%s.txt", pAltSkin->zLabel, zWhat);
zOut = builtin_text(z);
fossil_free(z);
}else{
zOut = db_get(zWhat, 0);
if( zOut==0 ){
z = mprintf("skins/default/%s.txt", zWhat);
zOut = builtin_text(z);
fossil_free(z);
}
}
return zOut;
}
/*
** Return the command-line option used to set the skin, or return NULL
| > > > > > > > > > > > > > > > > | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
if( file_isfile(z, ExtFILE) ){
Blob x;
blob_read_from_file(&x, z, ExtFILE);
fossil_free(z);
return blob_str(&x);
}
fossil_free(z);
}
if( iSkinSource==SKIN_FROM_UNKNOWN ){
const char *zDflt = db_get("default-skin", 0);
iSkinSource = SKIN_FROM_DEFAULT;
if( zDflt!=0 ){
int i;
for(i=0; i<count(aBuiltinSkin); i++){
if( fossil_strcmp(aBuiltinSkin[i].zLabel, zDflt)==0 ){
pAltSkin = &aBuiltinSkin[i];
iSkinSource = SKIN_FROM_SETTING;
break;
}
}
}
}
if( pAltSkin ){
z = mprintf("skins/%s/%s.txt", pAltSkin->zLabel, zWhat);
zOut = builtin_text(z);
fossil_free(z);
}else{
zOut = db_get(zWhat, 0);
if( zOut==0 ){
z = mprintf("skins/default/%s.txt", zWhat);
zOut = builtin_text(z);
fossil_free(z);
}else if( iSkinSource==SKIN_FROM_DEFAULT ){
iSkinSource = SKIN_FROM_CUSTOM;
}
}
return zOut;
}
/*
** Return the command-line option used to set the skin, or return NULL
|
| ︙ | ︙ | |||
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 |
"VALUES('skin:%q',%Q,now())",
zNewName, zCurrent
);
db_protect_pop();
return 0;
}
/*
** WEBPAGE: setup_skin_admin
**
** Administrative actions on skins. For administrators only.
*/
void setup_skin_admin(void){
const char *z;
char *zName;
char *zErr = 0;
const char *zCurrent = 0; /* Current skin */
int i; /* Loop counter */
Stmt q;
| > > > > > > > > > > < > > > > | | > > > > | > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | > > > > > | > > > > > > | < < < < | > > > | | | > > > | | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | < < < < < | | < < < < < < < < < < < | < > > > | | | | | | > | > | 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 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 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 |
"VALUES('skin:%q',%Q,now())",
zNewName, zCurrent
);
db_protect_pop();
return 0;
}
/*
** Return true if a custom skin exists
*/
static int skin_exists_custom(void){
return db_exists("SELECT 1 FROM config WHERE name IN"
" ('css','details','footer','header','js')");
}
static void skin_publish(int); /* Forward reference */
/*
** WEBPAGE: setup_skin_admin
**
** Administrative actions on skins. For administrators only.
*/
void setup_skin_admin(void){
const char *z;
char *zName;
char *zErr = 0;
const char *zCurrent = 0; /* Current skin */
int i; /* Loop counter */
Stmt q;
int once;
const char *zOverride = 0;
const char *zDfltSkin = 0;
int seenDefault = 0;
int hasCustom;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
db_begin_transaction();
zCurrent = getSkin(0);
for(i=0; i<count(aBuiltinSkin); i++){
aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel);
}
style_set_current_feature("skins");
if( cgi_csrf_safe(2) ){
/* Process requests to delete a user-defined skin */
if( P("del1") && P("sn")!=0 ){
style_header("Confirm Custom Skin Delete");
@ <form action="%R/setup_skin_admin" method="post"><div>
@ <p>Deletion of a custom skin is a permanent action that cannot
@ be undone. Please confirm that this is what you want to do:</p>
@ <input type="hidden" name="sn" value="%h(P("sn"))">
@ <input type="submit" name="del2" value="Confirm - Delete The Skin">
@ <input type="submit" name="cancel" value="Cancel - Do Not Delete">
login_insert_csrf_secret();
@ </div></form>
style_finish_page();
db_end_transaction(1);
return;
}
if( P("del2")!=0 ){
db_unprotect(PROTECT_CONFIG);
if( fossil_strcmp(P("sn"),"custom")==0 ){
db_multi_exec("DELETE FROM config WHERE name IN"
"('css','details','footer','header','js')");
}else if( (zName = skinVarName(P("sn"), 1))!=0 ){
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
}
db_protect_pop();
}
if( P("draftdel")!=0 ){
const char *zDraft = P("name");
if( sqlite3_strglob("draft[1-9]",zDraft)==0 ){
db_unprotect(PROTECT_CONFIG);
db_multi_exec("DELETE FROM config WHERE name GLOB '%q-*'", zDraft);
db_protect_pop();
}
}
if( P("editdraft")!=0 ){
db_end_transaction(0);
cgi_redirectf("%R/setup_skin");
return;
}
if( skinRename() || skinSave(zCurrent) ){
db_end_transaction(0);
return;
}
if( P("setdflt") && (z = P("bisl"))!=0 ){
if( z[0] ){
db_set("default-skin", z, 0);
}else{
db_unset("default-skin", 0);
}
db_end_transaction(0);
cgi_redirectf("%R/setup_skin_admin");
return;
}
/* The user pressed one of the "Install" buttons. */
if( P("load") && (z = P("sn"))!=0 && z[0] ){
int seen = 0;
/* Check to see if the current skin is already saved. If it is, there
** is no need to create a backup */
hasCustom = skin_exists_custom();
if( hasCustom ){
zCurrent = getSkin(0);
for(i=0; i<count(aBuiltinSkin); i++){
if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){
seen = 1;
break;
}
}
if( !seen ){
seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'"
" AND value=%Q", zCurrent);
if( !seen ){
db_unprotect(PROTECT_CONFIG);
db_multi_exec(
"INSERT INTO config(name,value,mtime) VALUES("
" strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S'),"
" %Q,now())", zCurrent
);
db_protect_pop();
}
}
}
seen = 0;
if( z[0]>='1' && z[0]<='9' && z[1]==0 ){
skin_publish(z[0]-'0');
seen = 1;
}
for(i=0; seen==0 && i<count(aBuiltinSkin); i++){
if( fossil_strcmp(aBuiltinSkin[i].zDesc, z)==0 ){
seen = 1;
zCurrent = aBuiltinSkin[i].zSQL;
db_unprotect(PROTECT_CONFIG);
db_multi_exec("%s", zCurrent/*safe-for-%s*/);
db_protect_pop();
break;
}
}
if( !seen ){
zName = skinVarName(z,0);
zCurrent = db_get(zName, 0);
db_unprotect(PROTECT_CONFIG);
db_multi_exec("%s", zCurrent/*safe-for-%s*/);
db_protect_pop();
}
}
}
zDfltSkin = db_get("default-skin",0);
hasCustom = skin_exists_custom();
if( !hasCustom && zDfltSkin==0 ){
zDfltSkin = "default";
}
style_header("Skins");
if( zErr ){
@ <p style="color:red">%h(zErr)</p>
}
@ <table border="0">
@ <tr><td colspan=4><h2>Built-in Skins:</h2></td></tr>
for(i=0; i<count(aBuiltinSkin); i++){
z = aBuiltinSkin[i].zDesc;
@ <tr><td>%d(i+1).<td>%h(z)<td> <td>
@ <form action="%R/setup_skin_admin" method="POST">
login_insert_csrf_secret();
if( zDfltSkin==0 || fossil_strcmp(aBuiltinSkin[i].zLabel, zDfltSkin)!=0 ){
/* vvvv--- mnemonic: Built-In Skin Label */
@ <input type="hidden" name="bisl" value="%h(aBuiltinSkin[i].zLabel)">
@ <input type="submit" name="setdflt" value="Set">
}else{
@ (Selected)
seenDefault = 1;
}
if( pAltSkin==&aBuiltinSkin[i] && iSkinSource!=SKIN_FROM_SETTING ){
@ (Override)
zOverride = z;
}
@ </form></td></tr>
}
if( zOverride ){
@ <tr><td> <td colspan="3">
@ <p>Note: Built-in skin "%h(zOverride)" is currently being used because of
switch( iSkinSource ){
case SKIN_FROM_CMDLINE:
@ the --skin command-line option.
break;
case SKIN_FROM_CGI:
@ the "skin:" option on CGI script.
break;
case SKIN_FROM_QPARAM:
@ the "skin=NAME" query parameter.
break;
case SKIN_FROM_COOKIE:
@ the "skin" value of the
@ <a href='./fdscookie'>fossil_display_setting</a> cookie.
break;
case SKIN_FROM_SETTING:
@ the "default-skin" setting.
break;
default:
@ reasons unknown. (Fix me!)
break;
}
@ </tr>
}
i++;
@ <tr><td colspan=4><h2>Custom skin:</h2></td></tr>
@ <tr><td>%d(i).
if( hasCustom ){
@ <td>Custom<td> <td>
}else{
@ <td><i>(None)</i><td> <td>
}
@ <form method="post">
login_insert_csrf_secret();
if( hasCustom ){
@ <input type="submit" name="save" value="Backup">
@ <input type="submit" name="editdraft" value="Edit">
if( !seenDefault ){
@ (Selected)
}else{
@ <input type="hidden" name="bisl" value="">
@ <input type="submit" name="setdflt" value="Set">
@ <input type="submit" name="del1" value="Delete">
@ <input type="hidden" name="sn" value="custom">
}
}else{
@ <input type="submit" name="editdraft" value="Create">
}
@ </form>
@ </td></tr>
db_prepare(&q,
"SELECT substr(name, 6) FROM config"
" WHERE name GLOB 'skin:*'"
" ORDER BY name"
);
once = 1;
while( db_step(&q)==SQLITE_ROW ){
const char *zN = db_column_text(&q, 0);
i++;
if( once ){
once = 0;
@ <tr><td colspan=4><h2>Backups of past custom skins:</h2></td></tr>
}
@ <tr><td>%d(i).<td>%h(zN)<td> <td>
@ <form action="%R/setup_skin_admin" method="post">
login_insert_csrf_secret();
@ <input type="submit" name="load" value="Install">
@ <input type="submit" name="del1" value="Delete">
@ <input type="submit" name="rename" value="Rename">
@ <input type="hidden" name="sn" value="%h(zN)">
@ </form></tr>
}
db_finalize(&q);
db_prepare(&q,
"SELECT DISTINCT substr(name, 1, 6) FROM config"
" WHERE name GLOB 'draft[1-9]-*'"
" ORDER BY name"
);
once = 1;
while( db_step(&q)==SQLITE_ROW ){
const char *zN = db_column_text(&q, 0);
i++;
if( once ){
once = 0;
@ <tr><td colspan=4><h2>Draft skins:</h2></td></tr>
}
@ <tr><td>%d(i).<td>%h(zN)<td> <td>
@ <form action="%R/setup_skin_admin" method="post">
login_insert_csrf_secret();
@ <input type="submit" name="load" value="Install">
@ <input type="submit" name="draftdel" value="Delete">
@ <input type="hidden" name="name" value="%h(zN)">
@ <input type="hidden" name="sn" value="%h(zN+5)">
@ </form></tr>
}
db_finalize(&q);
@ </table>
style_finish_page();
db_end_transaction(0);
}
/*
** Generate HTML for a <select> that lists all the available skin names,
** except for zExcept if zExcept!=NULL.
*/
static void skin_emit_skin_selector(
const char *zVarName, /* Variable name for the <select> */
const char *zDefault, /* The default value, if not NULL */
const char *zExcept /* Omit this skin if not NULL */
){
int i;
Stmt s;
@ <select size='1' name='%s(zVarName)'>
if( fossil_strcmp(zExcept, "current")!=0 && skin_exists_custom() ){
@ <option value='current'>Current Custom Skin</option>
}
for(i=0; i<count(aBuiltinSkin); i++){
const char *zName = aBuiltinSkin[i].zLabel;
if( fossil_strcmp(zName, zExcept)==0 ) continue;
if( fossil_strcmp(zDefault, zName)==0 ){
@ <option value='%s(zName)' selected>\
@ %h(aBuiltinSkin[i].zDesc)</option>
}else{
@ <option value='%s(zName)'>\
@ %h(aBuiltinSkin[i].zDesc)</option>
}
}
db_prepare(&s, "SELECT DISTINCT substr(name,1,6) FROM config"
" WHERE name GLOB 'draft[1-9]-*' ORDER BY 1");
while( db_step(&s)==SQLITE_ROW ){
const char *zName = db_column_text(&s, 0);
if( fossil_strcmp(zName, zExcept)==0 ) continue;
if( fossil_strcmp(zDefault, zName)==0 ){
@ <option value='%s(zName)' selected>%s(zName)</option>
}else{
@ <option value='%s(zName)'>%s(zName)</option>
}
}
db_finalize(&s);
@ </select>
}
/*
** Return the text of one of the skin files.
*/
static const char *skin_file_content(const char *zLabel, const char *zFile){
|
| ︙ | ︙ | |||
883 884 885 886 887 888 889 |
DiffConfig DCfg;
construct_diff_flags(1, &DCfg);
DCfg.diffFlags |= DIFF_STRIP_EOLCR;
if( P("sbsdiff")!=0 ) DCfg.diffFlags |= DIFF_SIDEBYSIDE;
blob_init(&to, zContent, -1);
blob_init(&from, skin_file_content(zBasis, zFile), -1);
blob_zero(&out);
| | | 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 |
DiffConfig DCfg;
construct_diff_flags(1, &DCfg);
DCfg.diffFlags |= DIFF_STRIP_EOLCR;
if( P("sbsdiff")!=0 ) DCfg.diffFlags |= DIFF_SIDEBYSIDE;
blob_init(&to, zContent, -1);
blob_init(&from, skin_file_content(zBasis, zFile), -1);
blob_zero(&out);
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
text_diff(&from, &to, &out, &DCfg);
@ %s(blob_str(&out))
}else{
DCfg.diffFlags |= DIFF_LINENO;
text_diff(&from, &to, &out, &DCfg);
@ <pre class="udiff">
|
| ︙ | ︙ | |||
954 955 956 957 958 959 960 961 962 963 964 965 |
}
/* Publish draft iSkin */
for(i=0; i<count(azSkinFile); i++){
char *zNew = db_get_mprintf("", "draft%d-%s", iSkin, azSkinFile[i]);
db_set(azSkinFile[i]/*works-like:"x"*/, zNew, 0);
}
}
/*
** WEBPAGE: setup_skin
**
| > | > | 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 |
}
/* Publish draft iSkin */
for(i=0; i<count(azSkinFile); i++){
char *zNew = db_get_mprintf("", "draft%d-%s", iSkin, azSkinFile[i]);
db_set(azSkinFile[i]/*works-like:"x"*/, zNew, 0);
}
db_unset("default-skin", 0);
}
/*
** WEBPAGE: setup_skin
**
** Generate a page showing the steps needed to create or edit
** a custom skin.
*/
void setup_skin(void){
int i; /* Loop counter */
int iSkin; /* Which draft skin is being edited */
int isSetup; /* True for an administrator */
int isEditor; /* Others authorized to make edits */
char *zAllowedEditors; /* Who may edit the draft skin */
|
| ︙ | ︙ | |||
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 |
/* Publish the draft skin */
if( P("pub7")!=0 && PB("pub7ck1") && PB("pub7ck2") ){
skin_publish(iSkin);
}
style_set_current_feature("skins");
style_header("Customize Skin");
@ <p>Customize the look of this Fossil repository by making changes
@ to the CSS, Header, Footer, and Detail Settings in one of nine "draft"
@ configurations. Then, after verifying that all is working correctly,
@ publish the draft to become the new main Skin. Users can select a skin
@ of their choice from the built-in ones or the locally-edited one via
@ <a href='%R/skins'>the /skins page</a>.</p>
| > > > | 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 |
/* Publish the draft skin */
if( P("pub7")!=0 && PB("pub7ck1") && PB("pub7ck2") ){
skin_publish(iSkin);
}
style_set_current_feature("skins");
style_header("Customize Skin");
if( g.perm.Admin ){
style_submenu_element("Skin-Admin", "%R/setup_skin_admin");
}
@ <p>Customize the look of this Fossil repository by making changes
@ to the CSS, Header, Footer, and Detail Settings in one of nine "draft"
@ configurations. Then, after verifying that all is working correctly,
@ publish the draft to become the new main Skin. Users can select a skin
@ of their choice from the built-in ones or the locally-edited one via
@ <a href='%R/skins'>the /skins page</a>.</p>
|
| ︙ | ︙ | |||
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 |
@ <a name='step3'></a>
@ <h1>Step 3: Initialize The Draft</h1>
@
if( !isEditor ){
@ <p>You are not allowed to initialize draft%d(iSkin). Contact
@ the administrator for this repository for more information.
}else{
@ <p>Initialize the draft%d(iSkin) skin to one of the built-in skins
@ or a preexisting skin, to use as a baseline.</p>
@
@ <form method='POST' action='%R/setup_skin#step4' id='f03'>
@ <p class='skinInput'>
@ <input type='hidden' name='sk' value='%d(iSkin)'>
@ Initialize skin <b>draft%d(iSkin)</b> using
| > | > | 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 |
@ <a name='step3'></a>
@ <h1>Step 3: Initialize The Draft</h1>
@
if( !isEditor ){
@ <p>You are not allowed to initialize draft%d(iSkin). Contact
@ the administrator for this repository for more information.
}else{
char *zDraft = mprintf("draft%d", iSkin);
@ <p>Initialize the draft%d(iSkin) skin to one of the built-in skins
@ or a preexisting skin, to use as a baseline.</p>
@
@ <form method='POST' action='%R/setup_skin#step4' id='f03'>
@ <p class='skinInput'>
@ <input type='hidden' name='sk' value='%d(iSkin)'>
@ Initialize skin <b>draft%d(iSkin)</b> using
skin_emit_skin_selector("initskin", 0, zDraft);
fossil_free(zDraft);
@ <input type='submit' name='init3' value='Go'>
@ </p>
@ </form>
}
@
@ <a name='step4'></a>
@ <h1>Step 4: Make Edits</h1>
|
| ︙ | ︙ | |||
1187 1188 1189 1190 1191 1192 1193 |
builtin_request_js("skin.js");
style_finish_page();
}
/*
** WEBPAGE: skins
**
| | > | < | | | > < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 |
builtin_request_js("skin.js");
style_finish_page();
}
/*
** WEBPAGE: skins
**
** Show a list of all of the built-in skins, plus the respository skin,
** and provide the user with an opportunity to change to any of them.
*/
void skins_page(void){
int i;
char *zBase = fossil_strdup(g.zTop);
size_t nBase = strlen(zBase);
login_check_credentials();
if( iDraftSkin && sqlite3_strglob("*/draft?", zBase)==0 ){
nBase -= 7;
zBase[nBase] = 0;
}else if( pAltSkin ){
char *zPattern = mprintf("*/skn_%s", pAltSkin->zLabel);
if( sqlite3_strglob(zPattern, zBase)==0 ){
nBase -= strlen(zPattern)-1;
zBase[nBase] = 0;
}
fossil_free(zPattern);
}
style_header("Skins");
if( iDraftSkin || nSkinRank<=1 ){
@ <p class="warning">Warning:
if( iDraftSkin>0 ){
@ you are using a draft skin,
}else{
@ this fossil instance was started with a hard-coded skin
@ value
}
@ which supercedes any option selected below. A skin selected
@ below will be recorded in your
@ "%z(href("%R/fdscookie"))fossil_display_settings</a>" cookie
@ but will not be used so long as the site has a
@ higher-priority skin in place.
@ </p>
}
@ <p>The following skins are available for this repository:</p>
@ <ul>
for(i=0; i<count(aBuiltinSkin); i++){
if( pAltSkin==&aBuiltinSkin[i] ){
@ <li> %h(aBuiltinSkin[i].zDesc) ← <i>Currently in use</i>
}else{
char *zUrl = href("%R/skins?skin=%T", aBuiltinSkin[i].zLabel);
@ <li> %z(zUrl)%h(aBuiltinSkin[i].zDesc)</a>
}
}
if( skin_exists_custom() ){
if( pAltSkin==0 && zAltSkinDir==0 && iDraftSkin==0 ){
@ <li> Custom skin for this repository ← <i>Currently in use</i>
}else{
@ <li> %z(href("%R/skins?skin=custom"))\
@ Custom skin for this repository</a>
}
}
@ </ul>
if( iSkinSource<SKIN_FROM_CUSTOM ){
@ <p>The current skin is selected by
switch( iSkinSource ){
case SKIN_FROM_DRAFT:
@ the "debugN" prefix on the PATH_INFO portion of the URL.
break;
case SKIN_FROM_CMDLINE:
@ the "--skin" command-line option on the Fossil server.
break;
case SKIN_FROM_CGI:
@ the "skin:" property in the CGI script that runs the Fossil server.
break;
case SKIN_FROM_QPARAM:
@ the "skin=NAME" query parameter on the URL.
break;
case SKIN_FROM_COOKIE:
@ the "skin" property in the
@ "%z(href("%R/fdscookie"))fossil_display_settings</a>" cookie.
break;
case SKIN_FROM_SETTING:
@ the "default-skin" setting on the repository.
break;
}
}
if( iSkinSource==SKIN_FROM_COOKIE || iSkinSource==SKIN_FROM_QPARAM ){
@ <ul>
@ <li> %z(href("%R/skins?skin="))<i>Let Fossil choose \
@ which skin to use</i></a>
@ </ul>
}
style_finish_page();
if( P("skin")!=0 ){
sqlite3_uint64 x;
sqlite3_randomness(sizeof(x), &x);
cgi_redirectf("%R/skins/%llx", x);
}
fossil_free(zBase);
}
|
Changes to src/smtp.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** Implementation of SMTP (Simple Mail Transport Protocol) according ** to RFC 5321. */ #include "config.h" #include "smtp.h" #include <assert.h> | | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
**
** Implementation of SMTP (Simple Mail Transport Protocol) according
** to RFC 5321.
*/
#include "config.h"
#include "smtp.h"
#include <assert.h>
#if (HAVE_DN_EXPAND || HAVE___NS_NAME_UNCOMPRESS || HAVE_NS_NAME_UNCOMPRESS) \
&& (HAVE_NS_PARSERR || HAVE___NS_PARSERR) && !defined(FOSSIL_OMIT_DNS)
# include <sys/types.h>
# include <netinet/in.h>
# if defined(HAVE_BIND_RESOLV_H)
# include <bind/resolv.h>
# include <bind/arpa/nameser_compat.h>
# else
# include <arpa/nameser.h>
|
| ︙ | ︙ |
Changes to src/sqlcmd.c.
| ︙ | ︙ | |||
382 383 384 385 386 387 388 |
** files_of_checkin(X) A table-valued function that returns info on
** all files contained in check-in X. Example:
**
** SELECT * FROM files_of_checkin('trunk');
**
** helptext A virtual table with one row for each command,
** webpage, and setting together with the built-in
| | | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
** files_of_checkin(X) A table-valued function that returns info on
** all files contained in check-in X. Example:
**
** SELECT * FROM files_of_checkin('trunk');
**
** helptext A virtual table with one row for each command,
** webpage, and setting together with the built-in
** help text.
**
** now() Return the number of seconds since 1970.
**
** obscure(T) Obfuscate the text password T so that its
** original value is not readily visible. Fossil
** uses this same algorithm when storing passwords
** of remote URLs.
|
| ︙ | ︙ |
Changes to src/stash.c.
| ︙ | ︙ | |||
579 580 581 582 583 584 585 |
fossil_fatal("nothing to stash");
}
stashid = stash_create();
undo_disable();
if( g.argc>=2 ){
int nFile = db_int(0, "SELECT count(*) FROM stashfile WHERE stashid=%d",
stashid);
| | > > > > < | 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 |
fossil_fatal("nothing to stash");
}
stashid = stash_create();
undo_disable();
if( g.argc>=2 ){
int nFile = db_int(0, "SELECT count(*) FROM stashfile WHERE stashid=%d",
stashid);
char **newArgv;
int i = 2;
Stmt q;
if( nFile==0 ){
fossil_fatal("No modified files match the provided pattern.");
}
newArgv = fossil_malloc( sizeof(char*)*(nFile+2) );
db_prepare(&q,"SELECT origname FROM stashfile WHERE stashid=%d", stashid);
while( db_step(&q)==SQLITE_ROW ){
newArgv[i++] = mprintf("%s%s", g.zLocalRoot, db_column_text(&q, 0));
}
db_finalize(&q);
newArgv[0] = g.argv[0];
newArgv[1] = 0;
g.argv = newArgv;
g.argc = nFile+2;
}
/* Make sure the stash has committed before running the revert, so that
** we have a copy of the changes before deleting them. */
db_commit_transaction();
g.argv[1] = "revert";
revert_cmd();
fossil_print("stash %d saved\n", stashid);
|
| ︙ | ︙ |
Changes to src/stat.c.
| ︙ | ︙ | |||
555 556 557 558 559 560 561 |
}else{
@ <tr><td width='100%%'>%h(db_column_text(&q,0))</td>
@ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
}
cnt++;
}
db_finalize(&q);
| | | 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 |
}else{
@ <tr><td width='100%%'>%h(db_column_text(&q,0))</td>
@ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
}
cnt++;
}
db_finalize(&q);
if( nOmitted ){
@ <tr><td><a href="urllist?all"><i>Show %d(nOmitted) more...</i></a>
}
if( cnt ){
@ </table>
total += cnt;
}
|
| ︙ | ︙ | |||
713 714 715 716 717 718 719 |
void repo_schema_page(void){
Stmt q;
Blob sql;
const char *zArg = P("n");
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
| | | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 |
void repo_schema_page(void){
Stmt q;
Blob sql;
const char *zArg = P("n");
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
if( zArg!=0
&& db_table_exists("repository",zArg)
&& cgi_csrf_safe(1)
){
if( P("analyze")!=0 ){
db_multi_exec("ANALYZE \"%w\"", zArg);
}else if( P("analyze200")!=0 ){
db_multi_exec("PRAGMA analysis_limit=200; ANALYZE \"%w\"", zArg);
|
| ︙ | ︙ | |||
932 933 934 935 936 937 938 |
/*
** Gather statistics on artifact types, counts, and sizes.
**
** Only populate the artstat.atype field if the bWithTypes parameter is true.
*/
void gather_artifact_stats(int bWithTypes){
| | | | | | | | | | | | 932 933 934 935 936 937 938 939 940 941 942 943 944 945 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 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 |
/*
** Gather statistics on artifact types, counts, and sizes.
**
** Only populate the artstat.atype field if the bWithTypes parameter is true.
*/
void gather_artifact_stats(int bWithTypes){
static const char zSql[] =
@ CREATE TEMP TABLE artstat(
@ id INTEGER PRIMARY KEY, -- Corresponds to BLOB.RID
@ atype TEXT, -- 'data', 'manifest', 'tag', 'wiki', etc.
@ isDelta BOOLEAN, -- true if stored as a delta
@ szExp, -- expanded, uncompressed size
@ szCmpr -- size as stored on disk
@ );
@ INSERT INTO artstat(id,atype,isDelta,szExp,szCmpr)
@ SELECT blob.rid, NULL,
@ delta.rid IS NOT NULL,
@ size, octet_length(content)
@ FROM blob LEFT JOIN delta ON blob.rid=delta.rid
@ WHERE content IS NOT NULL;
;
static const char zSql2[] =
@ UPDATE artstat SET atype='file'
@ WHERE +id IN (SELECT fid FROM mlink);
@ UPDATE artstat SET atype='manifest'
@ WHERE id IN (SELECT objid FROM event WHERE type='ci') AND atype IS NULL;
@ UPDATE artstat SET atype='forum'
@ WHERE id IN (SELECT objid FROM event WHERE type='f') AND atype IS NULL;
@ UPDATE artstat SET atype='cluster'
@ WHERE atype IS NULL
@ AND id IN (SELECT rid FROM tagxref
@ WHERE tagid=(SELECT tagid FROM tag
@ WHERE tagname='cluster'));
@ UPDATE artstat SET atype='ticket'
@ WHERE atype IS NULL
@ AND id IN (SELECT rid FROM tagxref
@ WHERE tagid IN (SELECT tagid FROM tag
@ WHERE tagname GLOB 'tkt-*'));
@ UPDATE artstat SET atype='wiki'
@ WHERE atype IS NULL
@ AND id IN (SELECT rid FROM tagxref
@ WHERE tagid IN (SELECT tagid FROM tag
@ WHERE tagname GLOB 'wiki-*'));
@ UPDATE artstat SET atype='technote'
@ WHERE atype IS NULL
@ AND id IN (SELECT rid FROM tagxref
@ WHERE tagid IN (SELECT tagid FROM tag
@ WHERE tagname GLOB 'event-*'));
@ UPDATE artstat SET atype='attachment'
@ WHERE atype IS NULL
@ AND id IN (SELECT attachid FROM attachment UNION
@ SELECT blob.rid FROM attachment JOIN blob ON uuid=src);
@ UPDATE artstat SET atype='tag'
@ WHERE atype IS NULL
@ AND id IN (SELECT srcid FROM tagxref);
@ UPDATE artstat SET atype='tag'
@ WHERE atype IS NULL
@ AND id IN (SELECT objid FROM event WHERE type='g');
@ UPDATE artstat SET atype='unused' WHERE atype IS NULL;
;
db_multi_exec("%s", zSql/*safe-for-%s*/);
if( bWithTypes ){
db_multi_exec("%s", zSql2/*safe-for-%s*/);
}
|
| ︙ | ︙ |
Changes to src/statrep.c.
| ︙ | ︙ | |||
128 129 130 131 132 133 134 |
const char *zNot = rc=='n' ? "NOT" : "";
statsReportTimelineYFlag = "ci";
db_multi_exec(
"CREATE TEMP VIEW v_reports AS "
"SELECT * FROM event WHERE type='ci' AND %s"
" AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)",
zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/
| | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
const char *zNot = rc=='n' ? "NOT" : "";
statsReportTimelineYFlag = "ci";
db_multi_exec(
"CREATE TEMP VIEW v_reports AS "
"SELECT * FROM event WHERE type='ci' AND %s"
" AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)",
zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/
);
}
return statsReportType = rc;
}
/*
** Returns a string suitable (for a given value of suitable) for
** use in a label with the header of the /reports pages, dependent
|
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
82 83 84 85 86 87 88 | static unsigned adUnitFlags = 0; /* ** Submenu disable flag */ static int submenuEnable = 1; | < < < < < < | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | static unsigned adUnitFlags = 0; /* ** Submenu disable flag */ static int submenuEnable = 1; /* ** Flags for various javascript files needed prior to </body> */ static int needHrefJs = 0; /* href.js */ /* ** Extra JS added to the end of the file. |
| ︙ | ︙ | |||
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 |
** This URL will include query parameters such as "id=" and "once&skin="
** to cause the correct stylesheet to be loaded after a skin change
** or after a change to the stylesheet.
*/
static void stylesheet_url_var(void){
char *zBuiltin; /* Auxiliary page-specific CSS page */
Blob url; /* The URL */
/* Initialize the URL to its baseline */
url = empty_blob;
blob_appendf(&url, "%R/style.css");
/* If page-specific CSS exists for the current page, then append
** the pathname for the page-specific CSS. The default CSS is
**
** /style.css
**
** But for the "/wikiedit" page (to name but one example), we
** append a path as follows:
**
** /style.css/wikiedit
**
** The /style.css page (implemented below) will detect this extra "wikiedit"
** path information and include the page-specific CSS along with the
** default CSS when it delivers the page.
*/
| > | | | | | 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 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 468 469 470 471 472 473 474 475 476 477 478 479 |
** This URL will include query parameters such as "id=" and "once&skin="
** to cause the correct stylesheet to be loaded after a skin change
** or after a change to the stylesheet.
*/
static void stylesheet_url_var(void){
char *zBuiltin; /* Auxiliary page-specific CSS page */
Blob url; /* The URL */
const char * zPage = local_zCurrentPage ? local_zCurrentPage : g.zPath;
/* Initialize the URL to its baseline */
url = empty_blob;
blob_appendf(&url, "%R/style.css");
/* If page-specific CSS exists for the current page, then append
** the pathname for the page-specific CSS. The default CSS is
**
** /style.css
**
** But for the "/wikiedit" page (to name but one example), we
** append a path as follows:
**
** /style.css/wikiedit
**
** The /style.css page (implemented below) will detect this extra "wikiedit"
** path information and include the page-specific CSS along with the
** default CSS when it delivers the page.
*/
zBuiltin = mprintf("style.%s.css", zPage);
if( builtin_file(zBuiltin,0)!=0 ){
blob_appendf(&url, "/%s", zPage);
}
fossil_free(zBuiltin);
/* Add query parameters that will change whenever the skin changes
** or after any updates to the CSS files
*/
blob_appendf(&url, "?id=%x", skin_id("css"));
if( P("once")!=0 && P("skin")!=0 ){
blob_appendf(&url, "&skin=%s&once", skin_in_use());
}
/* Generate the CSS URL variable */
Th_Store("stylesheet_url", blob_str(&url));
blob_reset(&url);
}
/*
** Create a TH1 variable containing the URL for the specified image.
** The resulting variable name will be of the form $[zImageName]_image_url.
** The value will be a URL that includes an id= query parameter that
** changes if the underlying resource changes or if a different skin
** is selected.
*/
static void image_url_var(const char *zImageName){
char *zVarName; /* Name of the new TH1 variable */
char *zResource; /* Name of CONFIG entry holding content */
char *zUrl; /* The URL */
zResource = mprintf("%s-image", zImageName);
zUrl = mprintf("%R/%s?id=%x", zImageName, skin_id(zResource));
free(zResource);
zVarName = mprintf("%s_image_url", zImageName);
Th_Store(zVarName, zUrl);
free(zVarName);
free(zUrl);
}
/*
** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
|
| ︙ | ︙ | |||
595 596 597 598 599 600 601 |
** The text '$nonce' is replaced by style_nonce() if and whereever it
** occurs in the input string.
**
** The string returned is obtained from fossil_malloc() and
** should be released by the caller.
*/
char *style_csp(int toHeader){
| | < | < < < < < < < < < < < | | 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 |
** The text '$nonce' is replaced by style_nonce() if and whereever it
** occurs in the input string.
**
** The string returned is obtained from fossil_malloc() and
** should be released by the caller.
*/
char *style_csp(int toHeader){
static const char zBackupCSP[] =
"default-src 'self' data:; "
"script-src 'self' 'nonce-$nonce'; "
"style-src 'self' 'unsafe-inline'; "
"img-src * data:";
const char *zFormat;
Blob csp;
char *zNonce;
char *zCsp;
int i;
zFormat = db_get("default-csp",0);
if( zFormat==0 || zFormat[0]==0 ){
zFormat = zBackupCSP;
}
blob_init(&csp, 0, 0);
while( zFormat[0] && (zNonce = strstr(zFormat,"$nonce"))!=0 ){
blob_append(&csp, zFormat, (int)(zNonce - zFormat));
blob_append(&csp, style_nonce(), -1);
zFormat = zNonce + 6;
}
blob_append(&csp, zFormat, -1);
zCsp = blob_str(&csp);
/* No whitespace other than actual space characters allowed in the CSP
** string. See https://fossil-scm.org/forum/forumpost/d29e3af43c */
for(i=0; zCsp[i]; i++){ if( fossil_isspace(zCsp[i]) ) zCsp[i] = ' '; }
if( toHeader ){
cgi_printf_header("Content-Security-Policy: %s\r\n", zCsp);
}
return zCsp;
}
/*
** Default HTML page header text through <body>. If the repository-specific
** header template lacks a <body> tag, then all of the following is
** prepended.
*/
static const char zDfltHeader[] =
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <base href="$baseurl/$current_page">
@ <meta http-equiv="Content-Security-Policy" content="$default_csp">
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>$<project_name>: $<title></title>
|
| ︙ | ︙ | |||
668 669 670 671 672 673 674 |
const char *get_default_header(){
return zDfltHeader;
}
/*
** The default TCL list that defines the main menu.
*/
| | | 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 |
const char *get_default_header(){
return zDfltHeader;
}
/*
** The default TCL list that defines the main menu.
*/
static const char zDfltMainMenu[] =
@ Home /home * {}
@ Timeline /timeline {o r j} {}
@ Files /dir?ci=tip oh desktoponly
@ Branches /brlist o wideonly
@ Tags /taglist o wideonly
@ Forum /forum {@2 3 4 5 6} wideonly
@ Chat /chat C wideonly
|
| ︙ | ︙ | |||
793 794 795 796 797 798 799 |
if( !login_is_nobody() ){
Th_Store("login", g.zLogin);
}
Th_MaybeStore("current_feature", feature_from_page_path(local_zCurrentPage) );
if( g.ftntsIssues[0] || g.ftntsIssues[1] ||
g.ftntsIssues[2] || g.ftntsIssues[3] ){
char buf[80];
| | | | 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 |
if( !login_is_nobody() ){
Th_Store("login", g.zLogin);
}
Th_MaybeStore("current_feature", feature_from_page_path(local_zCurrentPage) );
if( g.ftntsIssues[0] || g.ftntsIssues[1] ||
g.ftntsIssues[2] || g.ftntsIssues[3] ){
char buf[80];
sqlite3_snprintf(sizeof(buf), buf, "%i %i %i %i", g.ftntsIssues[0],
g.ftntsIssues[1], g.ftntsIssues[2], g.ftntsIssues[3]);
Th_Store("footnotes_issues_counters", buf);
}
}
/*
** Draw the header.
*/
|
| ︙ | ︙ | |||
1004 1005 1006 1007 1008 1009 1010 |
if( p->zLink==0 ){
@ <span class="label sml-%s(zClass)">%h(p->zLabel)</span>
}else{
@ <a class="label sml-%s(zClass)" href="%h(p->zLink)">%h(p->zLabel)</a>
}
}
}
| | | 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 |
if( p->zLink==0 ){
@ <span class="label sml-%s(zClass)">%h(p->zLabel)</span>
}else{
@ <a class="label sml-%s(zClass)" href="%h(p->zLink)">%h(p->zLabel)</a>
}
}
}
fossil_strcpy(zClass,"smc-"); /* common prefix for submenu controls */
for(i=0; i<nSubmenuCtrl; i++){
const char *zQPN = aSubmenuCtrl[i].zName;
const char *zDisabled = "";
const char *zXtraClass = "";
if( aSubmenuCtrl[i].eVisible & STYLE_DISABLED ){
zDisabled = " disabled";
}else if( zQPN ){
|
| ︙ | ︙ | |||
1283 1284 1285 1286 1287 1288 1289 | ** * $basename ** * $secureurl ** * $home ** * $logo ** * $background ** ** The output from TH1 becomes the style sheet. Fossil always reports | | | 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 |
** * $basename
** * $secureurl
** * $home
** * $logo
** * $background
**
** The output from TH1 becomes the style sheet. Fossil always reports
** that the style sheet is cacheable.
*/
void page_style_css(void){
Blob css = empty_blob;
int i;
const char * zDefaults;
const char *zSkin;
|
| ︙ | ︙ | |||
1323 1324 1325 1326 1327 1328 1329 | /* Tell CGI that the content returned by this page is considered cacheable */ g.isConst = 1; } /* ** All possible capabilities */ | | | 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 |
/* Tell CGI that the content returned by this page is considered cacheable */
g.isConst = 1;
}
/*
** All possible capabilities
*/
static const char allCap[] =
"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL";
/*
** Compute the current login capabilities
*/
static char *find_capabilities(char *zCap){
int i, j;
|
| ︙ | ︙ | |||
1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 |
}
/*
** WEBPAGE: honeypot
** This page is a honeypot for spiders and bots.
*/
void honeypot_page(void){
style_header("I think you are a robot");
@ <p>You seem like a robot.</p>
@
| > > > > > | > | | > > > > | > > > > | > > > > > > > > > | < > | > > | | 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 |
}
/*
** WEBPAGE: honeypot
** This page is a honeypot for spiders and bots.
*/
void honeypot_page(void){
unsigned int uSeed = captcha_seed();
const char *zDecoded = captcha_decode(uSeed, 0);
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
char *zCaptcha = captcha_render(zDecoded);
style_header("I think you are a robot");
@ <p>You seem like a robot.</p>
@
@ <p>Is that incorrect? Are you really human?
@ If so, please prove it by transcribing the captcha text
@ into the entry box below and pressing "Submit".
@ <form action="%R/login" method="post">
@ <input type="hidden" id="u" name="u" value="anonymous">
@ <p>
@ Captcha: <input type="text" id="p" name="p" value="">
@ <input type="submit" name="in" value="Submit">
@
@ <p>Alternatively, you can <a href="%R/login">log in</a> using an
@ existing userid.
@
@ <p><input type="hidden" name="cs" value="%u(uSeed)">
@ <div class="captcha"><table class="captcha"><tr><td>\
@ <pre class="captcha">
@ %h(zCaptcha)
@ </pre></td></tr></table>
if( bAutoCaptcha ) {
@ <input type="button" value="Fill out captcha" id='autofillButton' \
@ data-af='%s(zDecoded)'>
builtin_request_js("login.js");
}
@ </div>
free(zCaptcha);
@
@ <p>We regret this inconvenience. However, robots have become so
@ prolific and so aggressive that they will soak up too much CPU time
@ and network bandwidth on our servers if allowed to run unchecked.
@ Your cooperation in demonstrating that you are human is
@ appreciated.
style_finish_page();
}
/*
** Webpages that encounter an error due to missing or incorrect
** query parameters can jump to this routine to render an error
** message screen.
**
** For administators, or if the test_env_enable setting is true, then
** details of the request environment are displayed. Otherwise, just
** the error message is shown.
**
** If zFormat is an empty string, then this is the /test_env page.
*/
void webpage_error(const char *zFormat, ...){
int showAll = 0;
char *zErr = 0;
int isAuth = 0;
char zCap[100];
login_check_credentials();
if( g.perm.Admin || g.perm.Setup || db_get_boolean("test_env_enable",0) ){
isAuth = 1;
|
| ︙ | ︙ | |||
1477 1478 1479 1480 1481 1482 1483 |
break;
}
default: {
@ CSRF safety = unsafe<br>
break;
}
}
| | > > > > | 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 |
break;
}
default: {
@ CSRF safety = unsafe<br>
break;
}
}
@ fossil_exe_id() = %h(fossil_exe_id())<br>
if( g.perm.Admin ){
int k;
for(k=0; g.argvOrig[k]; k++){
Blob t;
blob_init(&t, 0, 0);
blob_append_escaped_arg(&t, g.argvOrig[k], 0);
@ argv[%d(k)] = %h(blob_str(&t))<br>
blob_zero(&t);
}
}
@ <hr>
P("HTTP_USER_AGENT");
P("SERVER_SOFTWARE");
cgi_print_all(showAll, 0, 0);
@ <p><form method="POST" action="%R/test_env">
@ <input type="hidden" name="showall" value="%d(showAll)">
@ <input type="submit" name="post-test-button" value="POST Test">
@ </form>
if( showAll && blob_size(&g.httpHeader)>0 ){
@ <hr>
@ <pre>
@ %h(blob_str(&g.httpHeader))
@ </pre>
}
}
|
| ︙ | ︙ | |||
1649 1650 1651 1652 1653 1654 1655 |
** Example:
**
** style_select_list_int("my-grapes", "my_grapes", "Grapes",
** "Select the number of grapes",
** atoi(PD("my_field","0")),
** "", 1, "2", 2, "Three", 3,
** NULL);
| | | 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 |
** Example:
**
** style_select_list_int("my-grapes", "my_grapes", "Grapes",
** "Select the number of grapes",
** atoi(PD("my_field","0")),
** "", 1, "2", 2, "Three", 3,
** NULL);
**
*/
void style_select_list_int(const char * zWrapperId,
const char *zFieldName, const char * zLabel,
const char * zToolTip, int selectedVal,
... ){
char * zLabelID = style_next_input_id();
va_list vargs;
|
| ︙ | ︙ | |||
1773 1774 1775 1776 1777 1778 1779 |
if( z[0]=='/' || z[0]=='\\' ){
zOrigin = z+1;
}
}
CX("<script nonce='%s'>/* %s:%d */\n", style_nonce(), zOrigin, iLine);
}
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 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 |
if( z[0]=='/' || z[0]=='\\' ){
zOrigin = z+1;
}
}
CX("<script nonce='%s'>/* %s:%d */\n", style_nonce(), zOrigin, iLine);
}
/* Generate the closing </script> tag
*/
void style_script_end(void){
CX("</script>\n");
}
/*
** Emits a NOSCRIPT tag with an error message stating that JS is
** required for the current page. This "should" be called near the top
** of pages which *require* JS. The inner DIV has the CSS class
** 'error' and can be styled via a (noscript > .error) CSS selector.
*/
void style_emit_noscript_for_js_page(void){
CX("<noscript><div class='error'>"
"This page requires JavaScript (ES2015, a.k.a. ES6, or newer)."
"</div></noscript>");
}
/*
** SETTING: robots-txt width=70 block-text keep-empty
**
** This setting is the override value for the /robots.txt file that
** Fossil returns when run as a stand-alone server for a domain. As
** Fossil is seldom run as a stand-alone server (and is more commonly
** deployed as a CGI or SCGI or behind a reverse proxy) this setting
** rarely needed. A reasonable default robots.txt is sent if this
** setting is empty.
*/
/*
** WEBPAGE: robots.txt
**
** Return text/plain which is the content of the "robots-txt" setting, if
** such a setting exists and is non-empty. Or construct an RFC-9309 complaint
** robots.txt file and return that if there is not "robots.txt" setting.
**
** This is useful for robot exclusion in cases where Fossil is run as a
** stand-alone server in its own domain. For the more common case where
** Fossil is run as a CGI, or SCGI, or a server that responding to a reverse
** proxy, the returns robots.txt file will not be at the top level of the
** domain, and so it will be pointless.
*/
void robotstxt_page(void){
const char *z;
static const char *zDflt =
"User-agent: *\n"
"Allow: /doc\n"
"Allow: /home\n"
"Allow: /forum\n"
"Allow: /technote\n"
"Allow: /tktview\n"
"Allow: /wiki\n"
"Allow: /uv/\n"
"Allow: /$\n"
"Disallow: /*\n"
;
z = db_get("robots-txt",zDflt);
cgi_set_content_type("text/plain");
cgi_append_content(z, -1);
}
|
Changes to src/style.chat.css.
| ︙ | ︙ | |||
66 67 68 69 70 71 72 |
body.chat .message-widget-content> a {
/* Cosmetic: keep skin-induced on-hover underlining from shifting
content placed below this. */
border-bottom: 1px transparent;
}
body.chat.monospace-messages .message-widget-content,
body.chat.monospace-messages .chat-input-field{
| | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
body.chat .message-widget-content> a {
/* Cosmetic: keep skin-induced on-hover underlining from shifting
content placed below this. */
border-bottom: 1px transparent;
}
body.chat.monospace-messages .message-widget-content,
body.chat.monospace-messages .chat-input-field{
font-family: monospace;
}
body.chat .message-widget-content > * {
margin: 0;
padding: 0;
}
body.chat .message-widget-content > pre {
white-space: pre-wrap;
|
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
padding: 0 0.5em 0.15em 0.5em;
cursor: pointer;
white-space: nowrap;
}
body.chat .fossil-tooltip.help-buttonlet-content {
font-size: 80%;
}
| > | | > > | > > > > > > > > > > > > > > > > > > | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
padding: 0 0.5em 0.15em 0.5em;
cursor: pointer;
white-space: nowrap;
}
body.chat .fossil-tooltip.help-buttonlet-content {
font-size: 80%;
}
body.chat .message-widget .message-widget-tab {
/* Element which renders the main metadata for a given message. */
}
body.chat .message-widget .message-widget-tab .xfrom {
/* xfrom part of the message tab */
font-style: italic;
font-weight: bold;
}
body.chat .message-widget .message-widget-tab .mtime {
/* mtime part of the message tab */
}
body.chat .message-widget .message-widget-tab .msgid {
/* msgid part of the message tab */
}
body.chat .message-widget .match {
font-weight: bold;
background-color: yellow;
}
body.chat.fossil-dark-style .message-widget .match {
background-color: #ff4800;
}
/* The popup element for displaying message timestamps
and deletion controls. */
body.chat .chat-message-popup {
font-family: monospace;
font-size: 0.9em;
text-align: left;
display: flex;
|
| ︙ | ︙ | |||
180 181 182 183 184 185 186 |
/* "Chat-only mode" hides the site header/footer, showing only
the chat app. */
body.chat.chat-only-mode{
padding: 0;
margin: 0 auto;
}
body.chat #chat-button-settings {}
| < < < < < < < < < < | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
/* "Chat-only mode" hides the site header/footer, showing only
the chat app. */
body.chat.chat-only-mode{
padding: 0;
margin: 0 auto;
}
body.chat #chat-button-settings {}
/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {
overflow: auto;
padding: 0 0.25em;
}
body.chat #chat-messages-wrapper.loading > * {
|
| ︙ | ︙ | |||
344 345 346 347 348 349 350 |
font-size: 130%;
}
body.chat #chat-buttons-wrapper > .cbutton:hover {
background-color: rgba(200,200,200,0.3);
}
body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper > .cbutton {
margin: 2px 0.125em 0 0.125em;
| | | | | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
font-size: 130%;
}
body.chat #chat-buttons-wrapper > .cbutton:hover {
background-color: rgba(200,200,200,0.3);
}
body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper > .cbutton {
margin: 2px 0.125em 0 0.125em;
min-width: 4.5ex;
max-width: 4.5ex;
min-height: 2.3ex;
max-height: 2.3ex;
font-size: 120%;
}
body.chat #chat-input-line-wrapper.compact #chat-buttons-wrapper #chat-button-submit {
min-width: 10ex;
}
.chat-input-field {
font-family: inherit
}
body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-multi,
body.chat #chat-input-line-wrapper:not(.compact) #chat-input-field-x {
min-height: 4rem;
|
| ︙ | ︙ | |||
438 439 440 441 442 443 444 445 446 447 448 449 450 451 |
body.chat .chat-view {
flex: 20 1 auto
/*ensure that these grow more than the non-.chat-view elements.
Note that setting flex shrink to 0 breaks/disables scrolling!*/;
margin-bottom: 0.2em;
}
body.chat #chat-config,
body.chat #chat-preview {
/* /chat configuration widget */
display: flex;
flex-direction: column;
overflow: auto;
padding: 0;
margin: 0;
| > | 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 |
body.chat .chat-view {
flex: 20 1 auto
/*ensure that these grow more than the non-.chat-view elements.
Note that setting flex shrink to 0 breaks/disables scrolling!*/;
margin-bottom: 0.2em;
}
body.chat #chat-config,
body.chat #chat-search,
body.chat #chat-preview {
/* /chat configuration widget */
display: flex;
flex-direction: column;
overflow: auto;
padding: 0;
margin: 0;
|
| ︙ | ︙ | |||
516 517 518 519 520 521 522 |
font-weight: normal;
white-space: pre-wrap;
display: inline-block;
opacity: 0.85;
}
body.chat #chat-config #chat-config-options .menu-entry select {
}
| | > > | | < | > > > > > > | 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 |
font-weight: normal;
white-space: pre-wrap;
display: inline-block;
opacity: 0.85;
}
body.chat #chat-config #chat-config-options .menu-entry select {
}
body.chat #chat-preview #chat-preview-content,
body.chat #chat-search #chat-search-content {
overflow: auto;
flex: 1 1 auto;
padding: 0.5em;
border: 1px dotted;
}
body.chat #chat-preview #chat-preview-content > * {
margin: 0;
padding: 0;
}
body.chat .chat-view .button-bar {
flex: 0 1 auto;
display: flex;
flex-direction: column;
}
body.chat .chat-view .button-bar button {
padding: 0.5em;
flex: 1 1 auto;
margin: 0.25em 0;
}
body.chat #chat-search .button-bar {
flex: 0 1 auto;
display: flex;
flex-direction: row;
}
body.chat #chat-user-list-wrapper {
/* Safari can't do fieldsets right, so we emulate one. */
border-radius: 0.5em;
margin: 1em 0 0.2em 0;
padding: 0 0.5em;
border-style: inset;
|
| ︙ | ︙ | |||
603 604 605 606 607 608 609 |
display: none;
}
body.chat #chat-user-list .chat-user.selected {
font-weight: bold;
text-decoration: underline;
}
| | | > | > > | > > | 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 |
display: none;
}
body.chat #chat-user-list .chat-user.selected {
font-weight: bold;
text-decoration: underline;
}
body.chat .searchForm {
margin-top: 1em;
}
body.chat .spacer-widget button {
margin-left: 1ex;
margin-right: 1ex;
display: block;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
body.chat .anim-rotate-360 {
animation: rotate-360 750ms linear;
}
@keyframes rotate-360 {
from { transform: rotate(0deg); }
|
| ︙ | ︙ | |||
647 648 649 650 651 652 653 |
body.chat .anim-fade-out-fast {
animation: fade-out 250ms linear;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
| > | 671 672 673 674 675 676 677 678 |
body.chat .anim-fade-out-fast {
animation: fade-out 250ms linear;
}
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
|
Added src/style.uvlist.css.
> > > > > > > | 1 2 3 4 5 6 7 |
body.uvlist input {
margin: 0.5em;
}
body.uvlist form {
display: block;
}
|
Changes to src/sync.c.
| ︙ | ︙ | |||
50 51 52 53 54 55 56 |
*/
static int client_sync_all_urls(
unsigned syncFlags, /* Mask of SYNC_* flags */
unsigned configRcvMask, /* Receive these configuration items */
unsigned configSendMask, /* Send these configuration items */
const char *zAltPCode /* Alternative project code (usually NULL) */
){
| | | | | > > > > | > > > | | | > > > > > > > > > | | > > | > > > | > | | > > > > > > | > > > | | | | | | | | | > > > > > > > > > > > | 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 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
*/
static int client_sync_all_urls(
unsigned syncFlags, /* Mask of SYNC_* flags */
unsigned configRcvMask, /* Receive these configuration items */
unsigned configSendMask, /* Send these configuration items */
const char *zAltPCode /* Alternative project code (usually NULL) */
){
int nErr = 0; /* Number of errors seen */
int nOther; /* Number of extra remote URLs */
char **azOther; /* Text of extra remote URLs */
int i; /* Loop counter */
int iEnd; /* Loop termination point */
int nextIEnd; /* Loop termination point for next pass */
int iPass; /* Which pass through the remotes. 0 or 1 */
int nPass; /* Number of passes to make. 1 or 2 */
Stmt q; /* An SQL statement */
UrlData baseUrl; /* Saved parse of the default remote */
sync_explain(syncFlags);
if( (syncFlags & SYNC_ALLURL)==0 ){
/* Common-case: Only sync with the remote identified by g.url */
nErr = client_sync(syncFlags, configRcvMask, configSendMask, zAltPCode, 0);
if( nErr==0 ) url_remember();
return nErr;
}
/* If we reach this point, it means we want to sync with all remotes */
memset(&baseUrl, 0, sizeof(baseUrl));
url_move_parse(&baseUrl, &g.url);
nOther = 0;
azOther = 0;
db_prepare(&q,
"SELECT substr(name,10) FROM config"
" WHERE name glob 'sync-url:*'"
" AND value<>(SELECT value FROM config WHERE name='last-sync-url')"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zUrl = db_column_text(&q, 0);
azOther = fossil_realloc(azOther, sizeof(*azOther)*(nOther+1));
azOther[nOther++] = fossil_strdup(zUrl);
}
db_finalize(&q);
iEnd = nOther+1;
nextIEnd = 0;
nPass = 1 + ((syncFlags & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL));
for(iPass=0; iPass<nPass; iPass++){
for(i=0; i<iEnd; i++){
int rc;
int nRcvd;
if( i==0 ){
url_move_parse(&g.url, &baseUrl); /* Load canonical URL */
}else{
/* Load an auxiliary remote URL */
url_parse(azOther[i-1],
URL_PROMPT_PW|URL_ASK_REMEMBER_PW|URL_USE_CONFIG);
}
if( i>0 || iPass>0 ) sync_explain(syncFlags);
rc = client_sync(syncFlags, configRcvMask, configSendMask,
zAltPCode, &nRcvd);
if( nRcvd>0 ){
/* If new artifacts were received, we want to repeat all prior
** remotes on the second pass */
nextIEnd = i;
}
nErr += rc;
if( rc==0 && iPass==0 ){
if( i==0 ){
url_remember();
}else if( (g.url.flags & URL_REMEMBER_PW)!=0 ){
char *zKey = mprintf("sync-pw:%s", azOther[i-1]);
char *zPw = obscure(g.url.passwd);
if( zPw && zPw[0] ){
db_set(zKey/*works-like:""*/, zPw, 0);
}
fossil_free(zPw);
fossil_free(zKey);
}
}
if( i==0 ){
url_move_parse(&baseUrl, &g.url); /* Don't forget canonical URL */
}else{
url_unparse(&g.url); /* Delete auxiliary URL parses */
}
}
iEnd = nextIEnd;
}
for(i=0; i<nOther; i++){
fossil_free(azOther[i]);
azOther[i] = 0;
}
fossil_free(azOther);
url_move_parse(&g.url, &baseUrl); /* Restore the canonical URL parse */
return nErr;
}
/*
** If the repository is configured for autosyncing, then do an
** autosync. Bits of the "flags" parameter determine details of behavior:
|
| ︙ | ︙ | |||
126 127 128 129 130 131 132 |
int configSync = 0; /* configuration changes transferred */
if( g.fNoSync ){
return 0;
}
zAutosync = db_get_for_subsystem("autosync", zSubsys);
if( zAutosync==0 ) zAutosync = "on"; /* defend against misconfig */
if( is_false(zAutosync) ) return 0;
| | | | 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 193 194 195 196 197 198 199 200 201 202 203 |
int configSync = 0; /* configuration changes transferred */
if( g.fNoSync ){
return 0;
}
zAutosync = db_get_for_subsystem("autosync", zSubsys);
if( zAutosync==0 ) zAutosync = "on"; /* defend against misconfig */
if( is_false(zAutosync) ) return 0;
if( db_get_boolean("dont-push",0)
|| sqlite3_strglob("*pull*", zAutosync)==0
){
flags &= ~SYNC_CKIN_LOCK;
if( flags & SYNC_PUSH ) return 0;
}
if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
url_parse(0, URL_REMEMBER|URL_USE_CONFIG);
if( g.url.protocol==0 ) return 0;
if( g.url.user!=0 && g.url.passwd==0 ){
g.url.passwd = unobscure(db_get("last-sync-pw", 0));
g.url.flags |= URL_PROMPT_PW;
url_prompt_for_password();
}
g.zHttpAuth = get_httpauth();
if( sqlite3_strglob("*all*", zAutosync)==0 ){
rc = client_sync_all_urls(flags|SYNC_ALLURL, configSync, 0, 0);
}else{
url_remember();
sync_explain(flags);
url_enable_proxy("via proxy: ");
rc = client_sync(flags, configSync, 0, 0, 0);
}
return rc;
}
/*
** This routine will try a number of times to perform autosync with a
** 0.5 second sleep between attempts. The number of attempts is determined
|
| ︙ | ︙ | |||
301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
if( g.url.protocol==0 ){
if( urlOptional ) fossil_exit(0);
usage("URL");
}
user_select();
url_enable_proxy("via proxy: ");
*pConfigFlags |= configSync;
}
/*
** COMMAND: pull
**
** Usage: %fossil pull ?URL? ?options?
| > > > > > > | 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 |
if( g.url.protocol==0 ){
if( urlOptional ) fossil_exit(0);
usage("URL");
}
user_select();
url_enable_proxy("via proxy: ");
*pConfigFlags |= configSync;
if( (*pSyncFlags & SYNC_ALLURL)==0 && zUrl==0 ){
const char *zAutosync = db_get_for_subsystem("autosync", "sync");
if( sqlite3_strglob("*all*", zAutosync)==0 ){
*pSyncFlags |= SYNC_ALLURL;
}
}
}
/*
** COMMAND: pull
**
** Usage: %fossil pull ?URL? ?options?
|
| ︙ | ︙ | |||
413 414 415 416 417 418 419 | client_sync_all_urls(syncFlags, 0, 0, 0); } /* ** COMMAND: sync ** | | > > | | | | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | client_sync_all_urls(syncFlags, 0, 0, 0); } /* ** COMMAND: sync ** ** Usage: %fossil sync ?REMOTE? ?options? ** ** Synchronize all sharable changes between the local repository and a ** remote repository, with the remote provided as a URL or a ** configured remote name (see the [[remote]] command). Sharable ** changes include public check-ins and edits to wiki pages, tickets, ** forum posts, and technical notes. ** ** If REMOTE is not specified, then the URL from the most recent clone, push, ** pull, remote, or sync command is used. See "fossil help clone" for ** details on the URL formats. ** ** Options: ** --all Sync with all remotes, not just the default ** -B|--httpauth USER:PASS Credentials for the simple HTTP auth protocol, ** if required by the remote website |
| ︙ | ︙ | |||
472 473 474 475 476 477 478 |
** commands.
*/
void sync_unversioned(unsigned syncFlags){
unsigned configFlags = 0;
(void)find_option("uv-noop",0,0);
process_sync_args(&configFlags, &syncFlags, 1, 0);
verify_all_options();
| | | 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
** commands.
*/
void sync_unversioned(unsigned syncFlags){
unsigned configFlags = 0;
(void)find_option("uv-noop",0,0);
process_sync_args(&configFlags, &syncFlags, 1, 0);
verify_all_options();
client_sync(syncFlags, 0, 0, 0, 0);
}
/*
** COMMAND: remote
** COMMAND: remote-url*
**
** Usage: %fossil remote ?SUBCOMMAND ...?
|
| ︙ | ︙ | |||
525 526 527 528 529 530 531 | ** ** > fossil remote list|ls ** ** Show all remote repository URLs. ** ** > fossil remote off ** | | | 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 | ** ** > fossil remote list|ls ** ** Show all remote repository URLs. ** ** > fossil remote off ** ** Forget the default URL. This disables autosync. ** ** This is a convenient way to enter "airplane mode". To enter ** airplane mode, first save the current default URL, then turn the ** default off. Perhaps like this: ** ** fossil remote add main default ** fossil remote off |
| ︙ | ︙ | |||
587 588 589 590 591 592 593 | ** ** The last-sync-url is called "default" for the display list. ** ** The last-sync-url might be duplicated into one of the sync-url:NAME ** entries. Thus, when doing a "fossil sync --all" or an autosync with ** autosync=all, each sync-url:NAME entry is checked to see if it is the ** same as last-sync-url and if it is then that entry is skipped. | | | 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 |
**
** The last-sync-url is called "default" for the display list.
**
** The last-sync-url might be duplicated into one of the sync-url:NAME
** entries. Thus, when doing a "fossil sync --all" or an autosync with
** autosync=all, each sync-url:NAME entry is checked to see if it is the
** same as last-sync-url and if it is then that entry is skipped.
*/
if( g.argc==2 ){
/* "fossil remote" with no arguments: Show the last sync URL. */
zUrl = db_get("last-sync-url", 0);
if( zUrl==0 ){
fossil_print("off\n");
}else{
|
| ︙ | ︙ |
Changes to src/tag.c.
| ︙ | ︙ | |||
42 43 44 45 46 47 48 | PQueue queue; /* Queue of check-ins to be tagged */ Stmt s; /* Query the children of :pid to which to propagate */ Stmt ins; /* INSERT INTO tagxref */ Stmt eventupdate; /* UPDATE event */ assert( tagType==0 || tagType==2 ); pqueuex_init(&queue); | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
PQueue queue; /* Queue of check-ins to be tagged */
Stmt s; /* Query the children of :pid to which to propagate */
Stmt ins; /* INSERT INTO tagxref */
Stmt eventupdate; /* UPDATE event */
assert( tagType==0 || tagType==2 );
pqueuex_init(&queue);
pqueuex_insert(&queue, pid, 0.0);
/* Query for children of :pid to which to propagate the tag.
** Three returns: (1) rid of the child. (2) timestamp of child.
** (3) True to propagate or false to block.
*/
db_prepare(&s,
"SELECT cid, plink.mtime,"
|
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
);
}
if( tagid==TAG_BGCOLOR ){
db_prepare(&eventupdate,
"UPDATE event SET bgcolor=%Q WHERE objid=:rid", zValue
);
}
| | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
);
}
if( tagid==TAG_BGCOLOR ){
db_prepare(&eventupdate,
"UPDATE event SET bgcolor=%Q WHERE objid=:rid", zValue
);
}
while( (pid = pqueuex_extract(&queue))!=0 ){
db_bind_int(&s, ":pid", pid);
while( db_step(&s)==SQLITE_ROW ){
int doit = db_column_int(&s, 2);
if( doit ){
int cid = db_column_int(&s, 0);
double mtime = db_column_double(&s, 1);
pqueuex_insert(&queue, cid, mtime);
db_bind_int(&ins, ":rid", cid);
db_step(&ins);
db_reset(&ins);
if( tagid==TAG_BGCOLOR ){
db_bind_int(&eventupdate, ":rid", cid);
db_step(&eventupdate);
db_reset(&eventupdate);
|
| ︙ | ︙ | |||
400 401 402 403 404 405 406 | ** ARTIFACT-ID. For check-ins, the tag will be usable instead ** of a CHECK-IN in commands such as update and merge. If the ** --propagate flag is present and ARTIFACT-ID refers to a ** wiki page, forum post, technote, or check-in, the tag ** propagates to all descendants of that artifact. ** ** Options: | | > | | > < < < < < > > > > < < < < > > > > > > > > | 400 401 402 403 404 405 406 407 408 409 410 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 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 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 | ** ARTIFACT-ID. For check-ins, the tag will be usable instead ** of a CHECK-IN in commands such as update and merge. If the ** --propagate flag is present and ARTIFACT-ID refers to a ** wiki page, forum post, technote, or check-in, the tag ** propagates to all descendants of that artifact. ** ** Options: ** --date-override DATETIME Set date and time added ** -n|--dry-run Display the tag text, but do not ** actually insert it into the database ** --propagate Propagating tag ** --raw Raw tag name. Ignored for ** non-CHECK-IN artifacts. ** --user-override USER Name USER when adding the tag ** ** The --date-override and --user-override options support ** importing history from other SCM systems. DATETIME has ** the form 'YYYY-MMM-DD HH:MM:SS'. ** ** Note that fossil uses some tag prefixes internally and this ** command will reject tags with these prefixes to avoid ** causing problems or confusion: "wiki-", "tkt-", "event-". ** ** > fossil tag cancel ?--raw? TAGNAME ARTIFACT-ID ** ** Remove the tag TAGNAME from the artifact referenced by ** ARTIFACT-ID, and also remove the propagation of the tag to ** any descendants. Use the the -n|--dry-run option to see ** what would have happened. Certain tag name prefixes are ** forbidden, as documented for the 'add' subcommand. ** ** Options: ** --date-override DATETIME Set date and time deleted ** -n|--dry-run Display the control artifact, but do ** not insert it into the database ** --raw Raw tag name. Ignored for ** non-CHECK-IN artifacts. ** --user-override USER Name USER when deleting the tag ** ** > fossil tag find ?OPTIONS? TAGNAME ** ** List all objects that use TAGNAME. ** ** Options: ** -n|--limit N Limit to N results ** --raw Interprets tag as a raw name instead of a ** branch name and matches any type of artifact. ** Changes the output to include only the ** hashes of matching objects. ** -t|--type TYPE One of: ci (check-in), w (wiki), ** e (event/technote), f (forum post), ** t (ticket). Default is all types. Ignored ** if --raw is used. ** ** > fossil tag list|ls ?OPTIONS? ?ARTIFACT-ID? ** ** List all tags or, if ARTIFACT-ID is supplied, all tags and ** their values for that artifact. The tagtype option accepts ** one of: propagated, singleton, cancel. For historical ** scripting compatibility, the internal tag types "wiki-", ** "tkt-", and "event-" (technote) are elided by default ** unless the --raw or --prefix options are used. ** ** Options: ** -v|--inverse Inverse the meaning of --tagtype TYPE ** --prefix List only tags with the given prefix ** Fossil-internal prefixes include "sym-" ** (branch name), "wiki-", "event-" ** (technote), and "tkt-" (ticket). The ** prefix is stripped from the resulting ** list unless --raw is provided. Ignored if ** ARTIFACT-ID is provided. ** --raw List raw names of tags ** --sep SEP Separator when concatenating values ** --tagtype TYPE List only tags of type TYPE, which must ** be one of: cancel, singleton, propagated ** --values List tag values ** If --sep is supplied, list all values of a tag on ** the same line, separated by SEP; otherwise list ** each value on its own line. ** ** The option --raw allows the manipulation of all types of tags ** used for various internal purposes in fossil. It also shows ** "cancel" tags for the "find" and "list" subcommands. You should ** not use this option to make changes unless you are sure what ** you are doing. ** |
| ︙ | ︙ | |||
633 634 635 636 637 638 639 640 641 642 643 644 |
if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){
Stmt q;
const int fRaw = find_option("raw","",0)!=0;
const char *zTagType = find_option("tagtype","t",1);
const int fInverse = find_option("inverse","v",0)!=0;
const char *zTagPrefix = find_option("prefix","",1);
int nTagType = fRaw ? -1 : 0;
if( zTagType!=0 ){
int l = strlen(zTagType);
if( strncmp(zTagType,"cancel",l)==0 ){
nTagType = 0;
| > > > | | > | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | 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 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 |
if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){
Stmt q;
const int fRaw = find_option("raw","",0)!=0;
const char *zTagType = find_option("tagtype","t",1);
const int fInverse = find_option("inverse","v",0)!=0;
const char *zTagPrefix = find_option("prefix","",1);
int nTagType = fRaw ? -1 : 0;
int fValues = find_option("values","",0)!=0;
const char *zSep = find_option("sep","",1);
if( zTagType!=0 ){
int l = strlen(zTagType);
if( strncmp(zTagType,"cancel",l)==0 ){
nTagType = 0;
}else if( strncmp(zTagType,"singleton",l)==0 ){
nTagType = 1;
}else if( strncmp(zTagType,"propagated",l)==0 ){
nTagType = 2;
}else{
fossil_fatal("unrecognized tag type");
}
}
if( g.argc==3 ){
const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0;
if( !fValues ){
db_prepare(&q,
"SELECT tagname FROM tag"
" WHERE EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=tag.tagid"
" AND tagtype%s%d)"
" AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' "
" END ORDER BY tagname COLLATE uintnocase",
zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/,
nTagType, zTagPrefix, zTagPrefix
);
}else{
if( zSep ){
db_prepare(&q,
/* work around group_concat() with DISTINCT and custom separator */
"SELECT tagname,"
" rtrim(replace(group_concat(DISTINCT value||'@!' "
" ORDER BY value ASC), '@!,', %Q),'@!')"
" FROM tagxref, tag"
" WHERE tagxref.tagid=tag.tagid AND tagtype%s%d"
" AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' END"
" GROUP BY tagname"
" ORDER BY tagname COLLATE uintnocase",
( zSep && strlen(zSep)>0 ) ? zSep : ",",
zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/,
nTagType, zTagPrefix, zTagPrefix
);
}else{
db_prepare(&q,
"SELECT DISTINCT tagname, value"
" FROM tagxref, tag"
" WHERE tagxref.tagid=tag.tagid AND tagtype%s%d"
" AND CASE WHEN %Q IS NULL THEN 1 ELSE tagname GLOB %Q||'*' END"
" ORDER BY tagname, value COLLATE uintnocase",
zTagType!=0 ? (fInverse!=0?"<>":"=") : ">"/*safe-for-%s*/,
nTagType, zTagPrefix, zTagPrefix
);
}
}
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const char *zValue = db_column_text(&q, 1);
int nWidth = fValues ? 20 : 0;
const char *zzValue = (fValues && zValue) ? mprintf(" %s", zValue) : "";
if( fRaw ){
fossil_print("%-*s%s\n", nWidth, zName, zzValue);
}else if( nTagPrefix>0 ){
assert(db_column_bytes(&q,0)>=nTagPrefix);
fossil_print("%-*s%s\n", nWidth, &zName[nTagPrefix], zzValue);
}else if( strncmp(zName, "sym-", 4)==0 ){
fossil_print("%-*s%s\n", nWidth, &zName[4], zzValue);
}
}
db_finalize(&q);
}else if( g.argc==4 ){
char const *zObjId = g.argv[3];
const int rid = name_to_rid(zObjId);
const int objType = whatis_rid_type(rid);
|
| ︙ | ︙ | |||
809 810 811 812 813 814 815 816 |
}
cgi_check_for_malice();
login_anonymous_available();
style_header("Tags");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_element("Timeline", "tagtimeline");
@ <h2>Non-propagating tags:</h2>
db_prepare(&q,
| > > > > > > > | > > > | | | < | > | < > > > > | < | > > | > | 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 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 |
}
cgi_check_for_malice();
login_anonymous_available();
style_header("Tags");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_element("Timeline", "tagtimeline");
@ <h2>Non-propagating tags:</h2>
@ <table class='sortable' data-column-types='ktn' data-init-sort='2'>
@ <thead><tr>
@ <th>Tag Name</th>
@ <th>Most Recent</th>
@ <th>Count</th>
@ </tr></thead><tbody>
db_prepare(&q,
"SELECT substr(tagname,5),\n"
"row_number()OVER(ORDER BY tagname COLLATE uintnocase),\n"
"substr(datetime(max(event.mtime)),1,16),\n"
"count(*)\n"
"FROM tagxref JOIN tag USING(tagid)\n"
" JOIN event ON event.objid=tagxref.rid\n"
"WHERE tagname like 'sym-%%'\n"
"AND tagxref.tagtype=1\n"
"GROUP BY 1\n"
"ORDER BY 3 DESC;\n"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
int rn = db_column_int(&q, 1);
const char *zDate = db_column_text(&q, 2);
int cnt = db_column_int(&q, 3);
@ <tr><td data-sortkey="%06x(rn)">\
if( g.perm.Hyperlink ){
@ %z(chref("taglink","%R/timeline?t=%T",zName))%h(zName)</a></td>\
}else{
@ <span class="tagDsp">%h(zName)</span></td>\
}
@ <td> %h(zDate) </td>\
@ <td align="center">%d(cnt)</td></tr>
}
@ </table>
db_finalize(&q);
style_table_sorter();
style_finish_page();
}
/*
** WEBPAGE: /tagtimeline
**
** Render a timeline with all check-ins that contain non-propagating
|
| ︙ | ︙ |
Changes to src/tar.c.
| ︙ | ︙ | |||
242 243 244 245 246 247 248 |
n /= 10;
}
/* adding the length extended the length field? */
if(blen > next10){
blen++;
}
/* build the string */
| | > | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
n /= 10;
}
/* adding the length extended the length field? */
if(blen > next10){
blen++;
}
/* build the string */
blob_appendf(&tball.pax, "%d %s=%*.*s\n",
blen, zField, nValue, nValue, zValue);
/* this _must_ be right */
if((int)blob_size(&tball.pax) != blen){
fossil_panic("internal error: PAX tar header has bad length");
}
}
|
| ︙ | ︙ |
Changes to src/th_main.c.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 | */ #define TH_INIT_NONE ((u32)0x00000000) /* No flags. */ #define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */ #define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */ #define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */ #define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */ #define TH_INIT_NO_REPO ((u32)0x00000010) /* Skip opening repository. */ | | > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
*/
#define TH_INIT_NONE ((u32)0x00000000) /* No flags. */
#define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */
#define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */
#define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */
#define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */
#define TH_INIT_NO_REPO ((u32)0x00000010) /* Skip opening repository. */
#define TH_INIT_NO_ENCODE ((u32)0x00000020) /* Do not html-encode sendText()*/
/* output. */
#define TH_INIT_MASK ((u32)0x0000003F) /* All possible init flags. */
/*
** Useful and/or "well-known" combinations of flag values.
*/
#define TH_INIT_DEFAULT (TH_INIT_NONE) /* Default flags. */
#define TH_INIT_HOOK (TH_INIT_NEED_CONFIG | TH_INIT_FORCE_SETUP)
|
| ︙ | ︙ |
Changes to src/th_tcl.c.
| ︙ | ︙ | |||
1162 1163 1164 1165 1166 1167 1168 |
Tcl_DeleteInterp(tclInterp); /* TODO: Redundant? */
tclInterp = 0;
return TH_ERROR;
}
tclContext->interp = tclInterp;
if( Tcl_Init(tclInterp)!=TCL_OK ){
Th_ErrorMessage(interp,
| | > | > | 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 |
Tcl_DeleteInterp(tclInterp); /* TODO: Redundant? */
tclInterp = 0;
return TH_ERROR;
}
tclContext->interp = tclInterp;
if( Tcl_Init(tclInterp)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl initialization error:",
Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
if( setTclArguments(tclInterp, argc, argv)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl error setting arguments:",
Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
/*
** Determine (and cache) if an objProc can be called directly for a Tcl
** command invoked via the tclInvoke TH1 command.
|
| ︙ | ︙ | |||
1192 1193 1194 1195 1196 1197 1198 |
Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
/* If necessary, evaluate the custom Tcl setup script. */
setup = tclContext->setup;
if( setup && Tcl_EvalEx(tclInterp, setup, -1, 0)!=TCL_OK ){
Th_ErrorMessage(interp,
| | > | 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 |
Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
/* If necessary, evaluate the custom Tcl setup script. */
setup = tclContext->setup;
if( setup && Tcl_EvalEx(tclInterp, setup, -1, 0)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl setup script error:",
Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
return TH_OK;
}
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 |
#define TIMELINE_FMT_ONELINE \
"%h %c"
#define TIMELINE_FMT_MEDIUM \
"Commit: %h%nDate: %d%nAuthor: %a%nComment: %c"
#define TIMELINE_FMT_FULL \
"Commit: %H%nDate: %d%nAuthor: %a%nComment: %c%n"\
| | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#define TIMELINE_FMT_ONELINE \
"%h %c"
#define TIMELINE_FMT_MEDIUM \
"Commit: %h%nDate: %d%nAuthor: %a%nComment: %c"
#define TIMELINE_FMT_FULL \
"Commit: %H%nDate: %d%nAuthor: %a%nComment: %c%n"\
"Branch: %b%nTags: %t%nPhase: %p"
/*
** Add an appropriate tag to the output if "rid" is unpublished (private)
*/
#define UNPUB_TAG "<em>(unpublished)</em>"
void tag_private_status(int rid){
if( content_is_private(rid) ){
cgi_printf(" %s", UNPUB_TAG);
|
| ︙ | ︙ | |||
151 152 153 154 155 156 157 | db_bind_int(&q, "$rid", rid); res = db_step(&q)==SQLITE_ROW; db_reset(&q); return res; } /* | | | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
db_bind_int(&q, "$rid", rid);
res = db_step(&q)==SQLITE_ROW;
db_reset(&q);
return res;
}
/*
** Return the text of the unformatted
** forum post given by the RID in the argument.
*/
static void forum_post_content_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
|
| ︙ | ︙ | |||
364 365 366 367 368 369 370 |
int isClosed = 0;
if( is_ticket(zTktid, &isClosed) && isClosed ){
zExtraClass = " tktTlClosed";
}else{
zExtraClass = " tktTlOpen";
}
fossil_free(zTktid);
| | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
int isClosed = 0;
if( is_ticket(zTktid, &isClosed) && isClosed ){
zExtraClass = " tktTlClosed";
}else{
zExtraClass = " tktTlOpen";
}
fossil_free(zTktid);
}
}
if( zType[0]=='e' && tagid ){
if( bTimestampLinksToInfo ){
char *zId;
zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
tagid);
zDateLink = href("%R/technote/%s",zId);
|
| ︙ | ︙ | |||
674 675 676 677 678 679 680 |
cgi_printf(" tags: %h", zTagList);
}
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
| | | 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 |
cgi_printf(" tags: %h", zTagList);
}
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
cgi_printf(" id: %z%d←%d</a>",
href("%R/deltachain/%d",rid), rid, srcId);
}else{
cgi_printf(" id: %z%d</a>",
href("%R/deltachain/%d",rid), rid);
}
}
tag_private_status(rid);
|
| ︙ | ︙ | |||
1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 | } zEDate[j] = 0; /* It looks like this may be a date. Return it with punctuation added. */ return zEDate; } /* ** WEBPAGE: timeline ** ** Query parameters: ** ** a=TIMEORTAG Show events after TIMEORTAG | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 |
}
zEDate[j] = 0;
/* It looks like this may be a date. Return it with punctuation added. */
return zEDate;
}
/*
** Find the first check-in encountered with a particular tag
** when moving either forwards are backwards in time from a
** particular starting point (iFrom). Return the rid of that
** first check-in. If there are no check-ins in the decendent
** or ancestor set of check-in iFrom that match the tag, then
** return 0.
*/
static int timeline_endpoint(
int iFrom, /* Starting point */
const char *zEnd, /* Tag we are searching for */
int bForward /* 1: forwards in time (descendents) 0: backwards */
){
int tagId;
int endId = 0;
Stmt q;
int ans = 0;
tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd);
if( tagId==0 ){
endId = symbolic_name_to_rid(zEnd, "ci");
if( endId==0 ) return 0;
}
if( bForward ){
if( tagId ){
db_prepare(&q,
"WITH RECURSIVE dx(id,mtime) AS ("
" SELECT %d, event.mtime FROM event WHERE objid=%d"
" UNION"
" SELECT plink.cid, plink.mtime"
" FROM dx, plink"
" WHERE plink.pid=dx.id"
" AND plink.mtime<=(SELECT max(event.mtime) FROM tagxref, event"
" WHERE tagxref.tagid=%d AND tagxref.tagtype>0"
" AND event.objid=tagxref.rid)"
" ORDER BY plink.mtime)"
"SELECT id FROM dx, tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1",
iFrom, iFrom, tagId, tagId
);
}else{
db_prepare(&q,
"WITH RECURSIVE dx(id,mtime) AS ("
" SELECT %d, event.mtime FROM event WHERE objid=%d"
" UNION"
" SELECT plink.cid, plink.mtime"
" FROM dx, plink"
" WHERE plink.pid=dx.id"
" AND plink.mtime<=(SELECT mtime FROM event WHERE objid=%d)"
" ORDER BY plink.mtime)"
"SELECT id FROM dx WHERE id=%d",
iFrom, iFrom, endId, endId
);
}
}else{
if( tagId ){
db_prepare(&q,
"WITH RECURSIVE dx(id,mtime) AS ("
" SELECT %d, event.mtime FROM event WHERE objid=%d"
" UNION"
" SELECT plink.pid, event.mtime"
" FROM dx, plink, event"
" WHERE plink.cid=dx.id AND event.objid=plink.pid"
" AND event.mtime>=(SELECT min(event.mtime) FROM tagxref, event"
" WHERE tagxref.tagid=%d AND tagxref.tagtype>0"
" AND event.objid=tagxref.rid)"
" ORDER BY event.mtime DESC)"
"SELECT id FROM dx, tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1",
iFrom, iFrom, tagId, tagId
);
}else{
db_prepare(&q,
"WITH RECURSIVE dx(id,mtime) AS ("
" SELECT %d, event.mtime FROM event WHERE objid=%d"
" UNION"
" SELECT plink.pid, event.mtime"
" FROM dx, plink, event"
" WHERE plink.cid=dx.id AND event.objid=plink.pid"
" AND event.mtime>=(SELECT mtime FROM event WHERE objid=%d)"
" ORDER BY event.mtime DESC)"
"SELECT id FROM dx WHERE id=%d",
iFrom, iFrom, endId, endId
);
}
}
if( db_step(&q)==SQLITE_ROW ){
ans = db_column_int(&q, 0);
}
db_finalize(&q);
return ans;
}
/*
** COMMAND: test-endpoint
**
** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
**
** Show the first check-in with TAG that is a descendent or ancestor
** of BASE. The first descendent checkin is shown by default. Use
** the --backto to see the first ancestor checkin.
**
** Options:
**
** --backto Show ancestor. Others defaults to descendents.
*/
void timeline_test_endpoint(void){
int bForward = find_option("backto",0,0)==0;
int from_rid;
int ans;
db_find_and_open_repository(0, 0);
verify_all_options();
if( g.argc!=4 ){
usage("BASE-CHECKIN TAG ?--backto?");
}
from_rid = symbolic_name_to_rid(g.argv[2],"ci");
ans = timeline_endpoint(from_rid, g.argv[3], bForward);
if( ans ){
fossil_print("Result: %d (%S)\n", ans, rid_to_uuid(ans));
}else{
fossil_print("No path found\n");
}
}
/*
** WEBPAGE: timeline
**
** Query parameters:
**
** a=TIMEORTAG Show events after TIMEORTAG
|
| ︙ | ︙ | |||
1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 | ** match a check-in that is already in the timeline. ** sel2=TIMEORTAG Like sel1= but use the secondary highlight. ** n=COUNT Maximum number of events. "all" for no limit ** n1=COUNT Same as "n" but doesn't set the display-preference cookie ** Use "n1=COUNT" for a one-time display change ** p=CHECKIN Parents and ancestors of CHECKIN ** bt=PRIOR ... going back to PRIOR ** d=CHECKIN Children and descendants of CHECKIN ** ft=DESCENDANT ... going forward to DESCENDANT ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" | > > > | | | > | > | 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 | ** match a check-in that is already in the timeline. ** sel2=TIMEORTAG Like sel1= but use the secondary highlight. ** n=COUNT Maximum number of events. "all" for no limit ** n1=COUNT Same as "n" but doesn't set the display-preference cookie ** Use "n1=COUNT" for a one-time display change ** p=CHECKIN Parents and ancestors of CHECKIN ** bt=PRIOR ... going back to PRIOR ** p2=CKIN2 ... use CKIN2 if CHECKIN is not found ** d=CHECKIN Children and descendants of CHECKIN ** d2=CKIN2 ... Use CKIN2 if CHECKIN is not found ** ft=DESCENDANT ... going forward to DESCENDANT ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN ** p=CX ... from CX back to time of CHECKIN ** from=CX ... shortest path from CX back to CHECKIN ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN ** d=CX ... from CX up to the time of CHECKIN ** from=CX ... shortest path from CX up to CHECKIN ** t=TAG Show only check-ins with the given TAG ** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel ** tl=TAGLIST Shorthand for t=TAGLIST&ms=brlist ** rl=TAGLIST Shorthand for r=TAGLIST&ms=brlist ** rel Show related check-ins as well as those matching t=TAG ** mionly Limit rel to show ancestors but not descendants ** nowiki Do not show wiki associated with branch or tag |
| ︙ | ︙ | |||
1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 | ** nsm Omit the submenu ** nc Omit all graph colors other than highlights ** v Show details of files changed ** vfx Show complete text of forum messages ** f=CHECKIN Show family (immediate parents and children) of CHECKIN ** from=CHECKIN Path from... ** to=CHECKIN ... to this ** shortest ... show only the shortest path ** rel ... also show related checkins ** uf=FILE_HASH Show only check-ins that contain the given file version ** All qualifying check-ins are shown unless there is ** also an n= or n1= query parameter. ** chng=GLOBLIST Show only check-ins that involve changes to a file whose ** name matches one of the comma-separate GLOBLIST ** brbg Background color determined by branch name ** ubg Background color determined by user | > > > | 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 | ** nsm Omit the submenu ** nc Omit all graph colors other than highlights ** v Show details of files changed ** vfx Show complete text of forum messages ** f=CHECKIN Show family (immediate parents and children) of CHECKIN ** from=CHECKIN Path from... ** to=CHECKIN ... to this ** to2=CHECKIN ... backup name if to= doesn't resolve ** shortest ... show only the shortest path ** rel ... also show related checkins ** bt=PRIOR ... path from CHECKIN back to PRIOR ** ft=LATER ... path from CHECKIN forward to LATER ** uf=FILE_HASH Show only check-ins that contain the given file version ** All qualifying check-ins are shown unless there is ** also an n= or n1= query parameter. ** chng=GLOBLIST Show only check-ins that involve changes to a file whose ** name matches one of the comma-separate GLOBLIST ** brbg Background color determined by branch name ** ubg Background color determined by user |
| ︙ | ︙ | |||
1729 1730 1731 1732 1733 1734 1735 |
const char *zBisect = P("bid"); /* Bisect description */
int cpOnly = PB("cherrypicks"); /* Show all cherrypick checkins */
int tmFlags = 0; /* Timeline flags */
const char *zThisTag = 0; /* Suppress links to this tag */
const char *zThisUser = 0; /* Suppress links to this user */
HQuery url; /* URL for various branch links */
int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
| > | > > | 1861 1862 1863 1864 1865 1866 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 |
const char *zBisect = P("bid"); /* Bisect description */
int cpOnly = PB("cherrypicks"); /* Show all cherrypick checkins */
int tmFlags = 0; /* Timeline flags */
const char *zThisTag = 0; /* Suppress links to this tag */
const char *zThisUser = 0; /* Suppress links to this user */
HQuery url; /* URL for various branch links */
int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
const char *zTo2 = 0;
int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */
int noMerge = P("shortest")==0; /* Follow merge links if shorter */
int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
int pd_rid;
const char *zDPName; /* Value of p=, d=, or dp= params */
double rBefore, rAfter, rCirca; /* Boundary times */
const char *z;
char *zOlderButton = 0; /* URL for Older button at the bottom */
char *zOlderButtonLabel = 0; /* Label for the Older Button */
char *zNewerButton = 0; /* URL for Newer button at the top */
char *zNewerButtonLabel = 0; /* Label for the Newer button */
int selectedRid = 0; /* Show a highlight on this RID */
int secondaryRid = 0; /* Show secondary highlight */
int disableY = 0; /* Disable type selector on submenu */
int advancedMenu = 0; /* Use the advanced menu design */
char *zPlural; /* Ending for plural forms */
int showCherrypicks = 1; /* True to show cherrypick merges */
int haveParameterN; /* True if n= query parameter present */
int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */
url_initialize(&url, "timeline");
cgi_query_parameters_to_url(&url);
(void)P_NoBot("ss")
/* "ss" is processed via the udc but at least one spider likes to
** try to SQL inject via this argument, so let's catch that. */;
|
| ︙ | ︙ | |||
1791 1792 1793 1794 1795 1796 1797 |
}
}
}else{
nEntry = 50;
}
/* Query parameters d=, p=, and f= and variants */
| < | < | > > > > > | | 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 |
}
}
}else{
nEntry = 50;
}
/* Query parameters d=, p=, and f= and variants */
p_rid = name_choice("p","p2", &zDPName);
d_rid = name_choice("d","d2", &zDPName);
z = P("f");
f_rid = z ? name_to_typed_rid(z,"ci") : 0;
z = P("df");
if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){
nEntry = 0;
useDividers = 0;
cgi_replace_query_parameter("d",fossil_strdup(z));
zDPName = z;
}
/* Undocumented query parameter to set JS mode */
builtin_set_js_delivery_mode(P("jsmode"),1);
secondaryRid = name_to_typed_rid(P("sel2"),"ci");
selectedRid = name_to_typed_rid(P("sel1"),"ci");
if( from_rid!=0 && to_rid!=0 ){
if( selectedRid==0 ) selectedRid = from_rid;
if( secondaryRid==0 ) secondaryRid = to_rid;
}
tmFlags |= timeline_ss_submenu();
cookie_link_parameter("advm","advm","0");
advancedMenu = atoi(PD("advm","0"));
/* Omit all cherry-pick merge lines if the "ncp" query parameter is
** present or if this repository lacks a "cherrypick" table. */
if( PB("ncp") || !db_table_exists("repository","cherrypick") ){
showCherrypicks = 0;
}
/* To view the timeline, must have permission to read project data.
*/
pd_rid = name_choice("dp","dp2",&zDPName);
if( pd_rid ){
p_rid = d_rid = pd_rid;
}
login_check_credentials();
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
|| (bisectLocal && !g.perm.Setup)
){
|
| ︙ | ︙ | |||
2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 |
}
if( (tmFlags & TIMELINE_UNHIDE)==0 ){
blob_append_sql(&sql,
" AND NOT EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n",
TAG_HIDDEN
);
}
if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.perm.Read ){
/* If from= and to= are present, display all nodes on a path connecting
** the two */
PathNode *p = 0;
const char *zFrom = 0;
const char *zTo = 0;
Blob ins;
int nNodeOnPath = 0;
if( from_rid && to_rid ){
| > > > > > > > > > > > > > > > > > > > > > | > > > > > | | 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 |
}
if( (tmFlags & TIMELINE_UNHIDE)==0 ){
blob_append_sql(&sql,
" AND NOT EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n",
TAG_HIDDEN
);
}
if( from_rid && !to_rid && (P("ft")!=0 || P("bt")!=0) ){
const char *zTo = P("ft");
if( zTo ){
from_to_mode = 1;
to_rid = timeline_endpoint(from_rid, zTo, 1);
}else{
from_to_mode = 2;
zTo = P("bt");
to_rid = timeline_endpoint(from_rid, zTo, 0);
}
if( to_rid ){
cgi_replace_parameter("to", zTo);
if( selectedRid==0 ) selectedRid = from_rid;
if( secondaryRid==0 ) secondaryRid = to_rid;
}else{
to_rid = from_rid;
blob_appendf(&desc, "There is no path from %h %s to %h.<br>Instead: ",
P("from"), from_to_mode==1 ? "forward" : "back", zTo);
}
}
if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.perm.Read ){
/* If from= and to= are present, display all nodes on a path connecting
** the two */
PathNode *p = 0;
const char *zFrom = 0;
const char *zTo = 0;
Blob ins;
int nNodeOnPath = 0;
if( from_rid && to_rid ){
if( from_to_mode==0 ){
p = path_shortest(from_rid, to_rid, noMerge, 0, 0);
}else if( from_to_mode==1 ){
p = path_shortest(from_rid, to_rid, 0, 1, 0);
}else{
p = path_shortest(to_rid, from_rid, 0, 1, 0);
}
zFrom = P("from");
zTo = zTo2 ? zTo2 : P("to");
}else{
if( path_common_ancestor(me_rid, you_rid) ){
p = path_first();
}
zFrom = P("me");
zTo = P("you");
}
|
| ︙ | ︙ | |||
2138 2139 2140 2141 2142 2143 2144 |
}
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
db_multi_exec("%s", blob_sql_text(&sql));
if( advancedMenu ){
style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
}
nNodeOnPath = db_int(0, "SELECT count(*) FROM temp.pathnode");
| > > > > > | > > > > > > > > | > > > | > | | | | | > | 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 |
}
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
db_multi_exec("%s", blob_sql_text(&sql));
if( advancedMenu ){
style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
}
nNodeOnPath = db_int(0, "SELECT count(*) FROM temp.pathnode");
if( nNodeOnPath==1 && from_to_mode>0 ){
blob_appendf(&desc,"Check-in ");
}else if( from_to_mode>0 ){
blob_appendf(&desc, "%d check-ins on the shorted path from ",nNodeOnPath);
}else{
blob_appendf(&desc, "%d check-ins going from ", nNodeOnPath);
}
if( from_rid==selectedRid ){
blob_appendf(&desc, "<span class='timelineSelected'>");
}
blob_appendf(&desc, "%z%h</a>", href("%R/info/%h", zFrom), zFrom);
if( from_rid==selectedRid ) blob_appendf(&desc, "</span>");
if( nNodeOnPath==1 && from_to_mode>0 ){
blob_appendf(&desc, " only");
}else{
blob_append(&desc, " to ", -1);
if( to_rid==secondaryRid ){
blob_appendf(&desc,"<span class='timelineSelected timelineSecondary'>");
}
blob_appendf(&desc, "%z%h</a>", href("%R/info/%h",zTo), zTo);
if( to_rid==secondaryRid ) blob_appendf(&desc, "</span>");
if( related ){
int nRelated = db_int(0, "SELECT count(*) FROM timeline") - nNodeOnPath;
if( nRelated>0 ){
blob_appendf(&desc, " and %d related check-in%s", nRelated,
nRelated>1 ? "s" : "");
}
}
}
addFileGlobDescription(zChng, &desc);
}else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
/* If p= or d= is present, ignore all other parameters other than n= */
char *zUuid;
const char *zCiName;
|
| ︙ | ︙ | |||
2170 2171 2172 2173 2174 2175 2176 |
if( !haveParameterN ) nEntry = 10;
}
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
p_rid ? p_rid : d_rid);
| | | 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 |
if( !haveParameterN ) nEntry = 10;
}
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
p_rid ? p_rid : d_rid);
zCiName = zDPName;
if( zCiName==0 ) zCiName = zUuid;
blob_append_sql(&sql, " AND event.objid IN ok");
nd = 0;
if( d_rid ){
Stmt s;
double rStopTime = 9e99;
zFwdTo = P("ft");
|
| ︙ | ︙ | |||
2242 2243 2244 2245 2246 2247 2248 |
}
blob_appendf(&desc, " of %z%h</a>",
href("%R/info?name=%h", zCiName), zCiName);
if( ridBackTo ){
if( np==0 ){
blob_reset(&desc);
| | | | 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 |
}
blob_appendf(&desc, " of %z%h</a>",
href("%R/info?name=%h", zCiName), zCiName);
if( ridBackTo ){
if( np==0 ){
blob_reset(&desc);
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
href("%R/info?name=%h",zCiName), zCiName,
href("%R/info?name=%h",zBackTo), zBackTo);
}else{
blob_appendf(&desc, " back to %z%h</a>",
href("%R/info?name=%h",zBackTo), zBackTo);
if( ridFwdTo && zFwdTo ){
blob_appendf(&desc, " and up to %z%h</a>",
href("%R/info?name=%h",zFwdTo), zFwdTo);
}
}
}else if( ridFwdTo ){
if( nd==0 ){
blob_reset(&desc);
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> is not an descendant)",
href("%R/info?name=%h",zCiName), zCiName,
href("%R/info?name=%h",zFwdTo), zFwdTo);
}else{
blob_appendf(&desc, " up to %z%h</a>",
href("%R/info?name=%h",zFwdTo), zFwdTo);
}
|
| ︙ | ︙ | |||
2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 |
}
if( PB("showsql") ){
@ <pre>%h(blob_sql_text(&sql2))</pre>
}
db_multi_exec("%s", blob_sql_text(&sql2));
if( nEntry>0 ){
nEntry -= db_int(0,"select count(*) from timeline");
}
blob_reset(&sql2);
blob_append_sql(&sql,
" AND event.mtime<=%f ORDER BY event.mtime DESC",
rCirca
);
if( zMark==0 ) zMark = zCirca;
| > | 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 |
}
if( PB("showsql") ){
@ <pre>%h(blob_sql_text(&sql2))</pre>
}
db_multi_exec("%s", blob_sql_text(&sql2));
if( nEntry>0 ){
nEntry -= db_int(0,"select count(*) from timeline");
if( nEntry<=0 ) nEntry = 1;
}
blob_reset(&sql2);
blob_append_sql(&sql,
" AND event.mtime<=%f ORDER BY event.mtime DESC",
rCirca
);
if( zMark==0 ) zMark = zCirca;
|
| ︙ | ︙ | |||
2750 2751 2752 2753 2754 2755 2756 |
tmFlags |= TIMELINE_DISJOINT;
}
if( cpOnly && showCherrypicks ){
blob_appendf(&desc, " that participate in a cherrypick merge");
tmFlags |= TIMELINE_CHPICK|TIMELINE_DISJOINT;
}
if( zUser ){
| > > > > | > | 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 |
tmFlags |= TIMELINE_DISJOINT;
}
if( cpOnly && showCherrypicks ){
blob_appendf(&desc, " that participate in a cherrypick merge");
tmFlags |= TIMELINE_CHPICK|TIMELINE_DISJOINT;
}
if( zUser ){
if( g.perm.Admin ){
blob_appendf(&desc, " by user <a href='%R/setup_uinfo?l=%h'>%h</a>",
zUser, zUser);
}else{
blob_appendf(&desc, " by user %h", zUser);
}
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
}
if( zTagSql ){
if( matchStyle==MS_EXACT || matchStyle==MS_BRLIST ){
if( related ){
blob_appendf(&desc, " related to %h", zMatchDesc);
}else{
|
| ︙ | ︙ | |||
3037 3038 3039 3040 3041 3042 3043 | ** 6. mtime ** 7. branch ** 8. event-type: 'ci', 'w', 't', 'f', and so forth. ** 9. comment ** 10. user ** 11. tags */ | | > > | | > | | 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 |
** 6. mtime
** 7. branch
** 8. event-type: 'ci', 'w', 't', 'f', and so forth.
** 9. comment
** 10. user
** 11. tags
*/
void print_timeline(Stmt *q, int nLimit, int width, const char *zFormat,
int verboseFlag){
int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
int nLine = 0;
int nEntry = 0;
char zPrevDate[20];
const char *zCurrentUuid = 0;
int fchngQueryInit = 0; /* True if fchngQuery is initialized */
Stmt fchngQuery; /* Query for file changes on check-ins */
int rc;
/* True: separate entries with a newline after file listing */
int bVerboseNL = (zFormat &&
(fossil_strcmp(zFormat, TIMELINE_FMT_ONELINE)!=0));
/* True: separate entries with a newline even with no file listing */
int bNoVerboseNL = (zFormat &&
(fossil_strcmp(zFormat, TIMELINE_FMT_MEDIUM)==0 ||
fossil_strcmp(zFormat, TIMELINE_FMT_FULL)==0));
zPrevDate[0] = 0;
if( g.localOpen ){
int rid = db_lget_int("checkout", 0);
zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
}
|
| ︙ | ︙ | |||
3142 3143 3144 3145 3146 3147 3148 |
if( zFormat ){
char *zEntry;
int nEntryLine = 0;
if( nChild==0 ){
sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* ");
}
| | | > | 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 |
if( zFormat ){
char *zEntry;
int nEntryLine = 0;
if( nChild==0 ){
sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* ");
}
zEntry = timeline_entry_subst(zFormat, &nEntryLine, zId, zDate,
zUserShort, zComShort, zBranch, zTags,
zPrefix);
nLine += nEntryLine;
fossil_print("%s\n", zEntry);
fossil_free(zEntry);
}
else{
/* record another X lines */
nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
|
| ︙ | ︙ | |||
3187 3188 3189 3190 3191 3192 3193 |
nLine++; /* record another line */
}
db_reset(&fchngQuery);
if( bVerboseNL ) fossil_print("\n");
}else{
if( bNoVerboseNL ) fossil_print("\n");
}
| | | 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 |
nLine++; /* record another line */
}
db_reset(&fchngQuery);
if( bVerboseNL ) fossil_print("\n");
}else{
if( bNoVerboseNL ) fossil_print("\n");
}
nEntry++; /* record another complete entry */
}
if( rc==SQLITE_DONE ){
/* Did the underlying query actually have all entries? */
if( nAbsLimit==0 ){
fossil_print("+++ end of timeline (%d) +++\n", nEntry);
}else{
|
| ︙ | ︙ | |||
3231 3232 3233 3234 3235 3236 3237 |
@ event.type
@ , coalesce(ecomment,comment) AS comment0
@ , coalesce(euser,user,'?') AS user0
@ , (SELECT case when length(x)>0 then x else '' end
@ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
@ FROM tag, tagxref
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
| | | 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 |
@ event.type
@ , coalesce(ecomment,comment) AS comment0
@ , coalesce(euser,user,'?') AS user0
@ , (SELECT case when length(x)>0 then x else '' end
@ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
@ FROM tag, tagxref
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
@ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) AS tags
@ FROM tag CROSS JOIN event CROSS JOIN blob
@ LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
@ AND tagxref.tagtype>0
@ AND tagxref.rid=blob.rid
@ WHERE blob.rid=event.objid
@ AND tag.tagname='branch'
;
|
| ︙ | ︙ | |||
3292 3293 3294 3295 3296 3297 3298 | ** means UTC. ** ** ** Options: ** -b|--branch BRANCH Show only items on the branch named BRANCH ** -c|--current-branch Show only items on the current branch ** -F|--format Entry format. Values "oneline", "medium", and "full" | | | | 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 | ** means UTC. ** ** ** Options: ** -b|--branch BRANCH Show only items on the branch named BRANCH ** -c|--current-branch Show only items on the current branch ** -F|--format Entry format. Values "oneline", "medium", and "full" ** get mapped to the full options below. Otherwise a ** string which can contain these placeholders: ** %n newline ** %% a raw % ** %H commit hash ** %h abbreviated commit hash ** %a author name ** %d date ** %c comment (NL, TAB replaced by space, LF erased) ** %b branch ** %t tags ** %p phase: zero or more of *CURRENT*, *MERGE*, ** *FORK*, *UNPUBLISHED*, *LEAF*, *BRANCH* ** --oneline Show only short hash and comment for each entry ** --medium Medium-verbose entry formatting ** --full Extra verbose entry formatting |
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
555 556 557 558 559 560 561 |
case SQLITE_CREATE_VIEW:
case SQLITE_CREATE_TABLE: {
if( sqlite3_stricmp(z2,"main")!=0
&& sqlite3_stricmp(z2,"repository")!=0
){
goto ticket_schema_error;
}
| | | 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 |
case SQLITE_CREATE_VIEW:
case SQLITE_CREATE_TABLE: {
if( sqlite3_stricmp(z2,"main")!=0
&& sqlite3_stricmp(z2,"repository")!=0
){
goto ticket_schema_error;
}
if( sqlite3_strnicmp(z0,"ticket",6)!=0
&& sqlite3_strnicmp(z0,"fx_",3)!=0
){
goto ticket_schema_error;
}
break;
}
case SQLITE_DROP_INDEX:
|
| ︙ | ︙ | |||
1211 1212 1213 1214 1215 1216 1217 | } /* ** WEBPAGE: tkttimeline ** URL: /tkttimeline/TICKETUUID ** ** Show the change history for a single ticket in timeline format. | | | 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 |
}
/*
** WEBPAGE: tkttimeline
** URL: /tkttimeline/TICKETUUID
**
** Show the change history for a single ticket in timeline format.
**
** Query parameters:
**
** y=ci Show only check-ins associated with the ticket
*/
void tkttimeline_page(void){
char *zTitle;
const char *zUuid;
|
| ︙ | ︙ |
Changes to src/unicode.c.
| ︙ | ︙ | |||
238 239 240 241 242 243 244 |
iLo = iTest+1;
}else{
iHi = iTest-1;
}
}
assert( key>=aDia[iRes] );
if( bComplex==0 && (aChar[iRes] & 0x80) ) return c;
| | > | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
iLo = iTest+1;
}else{
iHi = iTest-1;
}
}
assert( key>=aDia[iRes] );
if( bComplex==0 && (aChar[iRes] & 0x80) ) return c;
return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c :
((int)aChar[iRes] & 0x7F);
}
/*
** Return true if the argument interpreted as a unicode codepoint
** is a diacritical modifier character.
*/
|
| ︙ | ︙ |
Changes to src/unversioned.c.
| ︙ | ︙ | |||
289 290 291 292 293 294 295 | ** -n|--dry-run Show what would have happened ** ** touch FILE ... Update the TIMESTAMP on all of the listed files ** ** Options: ** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add", ** "edit", "remove", and "touch" subcommands. | | | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
** -n|--dry-run Show what would have happened
**
** touch FILE ... Update the TIMESTAMP on all of the listed files
**
** Options:
** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add",
** "edit", "remove", and "touch" subcommands.
** -R|--repository REPO Use REPO as the repository
*/
void unversioned_cmd(void){
const char *zCmd;
int nCmd;
const char *zMtime = find_option("mtime", 0, 1);
sqlite3_int64 mtime;
db_find_and_open_repository(0, 0);
|
| ︙ | ︙ | |||
518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
}
db_unset("uv-hash", 0);
db_end_transaction(0);
}else{
usage("add|cat|edit|export|list|revert|remove|sync|touch");
}
}
/*
** WEBPAGE: uvlist
**
** Display a list of all unversioned files in the repository.
** Query parameters:
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
}
db_unset("uv-hash", 0);
db_end_transaction(0);
}else{
usage("add|cat|edit|export|list|revert|remove|sync|touch");
}
}
/*
** Emit an HTML form for uploading a new unversioned file if
** the current user has WrUnver permissions, else this is
** a no-op.
**
** If this function detects that the form it emits has been submitted,
** it will add the uploaded file to the unversioned file list before
** returning.
**
** Intended only for use by /uvlist, and its form's action is that
** page.
*/
static void uvlist_upload(void){
const char * aContent;
if( !g.perm.WrUnver ) return;
aContent = P("f");
if( aContent!=0 ){
const char * const zName = P("f:filename");
int const nContent = atoi(PD("f:bytes","0"));
const char * zError = 0;
Blob content;
if( zName[0]==0 ){
zError = "be an empty string";
}else if( contains_whitespace(zName) ){
zError = "contain spaces";
}
if( zError ){
fossil_fatal("Unversioned filenames may not %s: %h",
zError, zName);
}
unversioned_schema();
db_begin_transaction();
content_rcvid_init("#!fossil /uvlist upload");
blob_init(&content, aContent, nContent);
unversioned_write(zName, &content, time(0));
blob_reset(&content);
db_end_transaction(0);
CX("<div>Added: %h</div>", zName);
}
form_begin("enctype='multipart/form-data'", "%R/uvlist");
@ <label for='uvupload'>Upload unversioned file:</label>
@ <input type='file' id='uvupload' name='f'/>
@ <input type='submit' id='uvsubmit' value='Upload' disabled='disabled'/>
@ </form>
@ <script nonce='%h(style_nonce())'>;/* unversioned.c:%d(__LINE__) */
@ var upl = document.getElementById('uvupload');
@ var sbm = document.getElementById('uvsubmit');
@ upl.onchange = function(){
@ if (!upl.value) sbm.setAttribute('disabled', 'disabled');
@ else sbm.removeAttribute('disabled');
@ }
@ </script>
}
/*
** WEBPAGE: uvlist
**
** Display a list of all unversioned files in the repository.
** Query parameters:
**
|
| ︙ | ︙ | |||
543 544 545 546 547 548 549 550 551 552 553 554 555 556 |
char zSzName[100];
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
cgi_check_for_malice();
etag_check(ETAG_DATA,0);
style_header("Unversioned Files");
if( !db_table_exists("repository","unversioned") ){
@ No unversioned files on this server
style_finish_page();
return;
}
if( PB("byage") ) zOrderBy = "mtime DESC";
if( PB("showdel") ) showDel = 1;
| > | 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
char zSzName[100];
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
cgi_check_for_malice();
etag_check(ETAG_DATA,0);
style_header("Unversioned Files");
uvlist_upload();
if( !db_table_exists("repository","unversioned") ){
@ No unversioned files on this server
style_finish_page();
return;
}
if( PB("byage") ) zOrderBy = "mtime DESC";
if( PB("showdel") ) showDel = 1;
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
565 566 567 568 569 570 571 |
db_finalize(&q);
db_finalize(&mtimeXfer);
fossil_print("%.79c\n",'-');
if( nUpdate==0 ){
show_common_info(tid, "checkout:", 1, 0);
fossil_print("%-13s None. Already up-to-date\n", "changes:");
}else{
| | | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
db_finalize(&q);
db_finalize(&mtimeXfer);
fossil_print("%.79c\n",'-');
if( nUpdate==0 ){
show_common_info(tid, "checkout:", 1, 0);
fossil_print("%-13s None. Already up-to-date\n", "changes:");
}else{
fossil_print("%-13s %.40s %s\n", "updated-from:", rid_to_uuid(vid),
db_text("", "SELECT datetime(mtime) || ' UTC' FROM event "
" WHERE objid=%d", vid));
show_common_info(tid, "updated-to:", 1, 0);
fossil_print("%-13s %d file%s modified.\n", "changes:",
nUpdate, nUpdate>1 ? "s" : "");
}
|
| ︙ | ︙ | |||
614 615 616 617 618 619 620 621 622 623 624 625 626 627 |
}
/*
** Clean up the mid and pid VFILE entries. Then commit the changes.
*/
if( dryRunFlag ){
db_end_transaction(1); /* With --dry-run, rollback changes */
}else{
char *zPwd;
ensure_empty_dirs_created(1);
sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
file_rmdir_sql_function, 0, 0);
zPwd = file_getcwd(0,0);
db_multi_exec(
| > > > | 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 |
}
/*
** Clean up the mid and pid VFILE entries. Then commit the changes.
*/
if( dryRunFlag ){
db_end_transaction(1); /* With --dry-run, rollback changes */
fossil_warning("\nREMINDER: this was a dry run -"
" no files were actually changed "
"(checkout is still %.10s).", rid_to_uuid(vid));
}else{
char *zPwd;
ensure_empty_dirs_created(1);
sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
file_rmdir_sql_function, 0, 0);
zPwd = file_getcwd(0,0);
db_multi_exec(
|
| ︙ | ︙ |
Changes to src/url.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | */ #include "config.h" #include "url.h" #include <stdio.h> #ifdef _WIN32 #include <io.h> | < < < < < < | | | | | | | | > > > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
*/
#include "config.h"
#include "url.h"
#include <stdio.h>
#ifdef _WIN32
#include <io.h>
#endif
#if INTERFACE
/*
** Flags for url_parse()
*/
#define URL_PROMPT_PW 0x0001 /* Prompt for password if needed */
#define URL_REMEMBER 0x0002 /* Remember the url for later reuse */
#define URL_ASK_REMEMBER_PW 0x0004 /* Ask whether to remember prompted pw */
#define URL_REMEMBER_PW 0x0008 /* Should remember pw */
#define URL_PROMPTED 0x0010 /* Prompted for PW already */
#define URL_OMIT_USER 0x0020 /* Omit the user name from URL */
#define URL_USE_CONFIG 0x0040 /* Use remembered URLs from CONFIG table */
#define URL_USE_PARENT 0x0080 /* Use the URL of the parent project */
#define URL_SSH_PATH 0x0100 /* Include PATH= on SSH syncs */
#define URL_SSH_RETRY 0x0200 /* This a retry of an SSH */
#define URL_SSH_EXE 0x0400 /* ssh: URL contains fossil= query param*/
/*
** The URL related data used with this subsystem.
*/
struct UrlData {
int isFile; /* True if a "file:" url */
int isHttps; /* True if a "https:" url */
|
| ︙ | ︙ | |||
87 88 89 90 91 92 93 | ** path Path name for HTTP or HTTPS. ** user Userid. ** passwd Password. ** hostname HOST:PORT or just HOST if port is the default. ** canonical The URL in canonical form, omitting the password ** ** If URL_USECONFIG is set and zUrl is NULL or "default", then parse the | | | | 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 |
** path Path name for HTTP or HTTPS.
** user Userid.
** passwd Password.
** hostname HOST:PORT or just HOST if port is the default.
** canonical The URL in canonical form, omitting the password
**
** If URL_USECONFIG is set and zUrl is NULL or "default", then parse the
** URL stored in last-sync-url and last-sync-pw of the CONFIG table. Or if
** URL_USE_PARENT is also set, then use parent-project-url and
** parent-project-pw from the CONFIG table instead of last-sync-url
** and last-sync-pw.
**
** If URL_USE_CONFIG is set and zUrl is a symbolic name, then look up
** the URL in sync-url:%Q and sync-pw:%Q elements of the CONFIG table where
** %Q is the symbolic name.
**
** This routine differs from url_parse() in that this routine stores the
** results in pUrlData and does not change the values of global variables.
** The url_parse() routine puts its result in g.url.
*/
void url_parse_local(
const char *zUrl,
unsigned int urlFlags,
UrlData *pUrlData
){
int i, j, c;
char *zFile = 0;
memset(pUrlData, 0, sizeof(*pUrlData));
if( urlFlags & URL_USE_CONFIG ){
if( zUrl==0 || strcmp(zUrl,"default")==0 ){
const char *zPwConfig = "last-sync-pw";
if( urlFlags & URL_USE_PARENT ){
zUrl = db_get("parent-project-url", 0);
if( zUrl==0 ){
zUrl = db_get("last-sync-url",0);
|
| ︙ | ︙ | |||
157 158 159 160 161 162 163 |
|| strncmp(zUrl, "ssh://", 6)==0
){
int iStart;
char *zLogin;
char *zExe;
char cQuerySep = '?';
| < < | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
|| strncmp(zUrl, "ssh://", 6)==0
){
int iStart;
char *zLogin;
char *zExe;
char cQuerySep = '?';
if( zUrl[4]=='s' ){
pUrlData->isHttps = 1;
pUrlData->protocol = "https";
pUrlData->dfltPort = 443;
iStart = 8;
}else if( zUrl[0]=='s' ){
pUrlData->isSsh = 1;
|
| ︙ | ︙ | |||
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
while( pUrlData->path[i] && pUrlData->path[i]!='&' ){ i++; }
}
if( pUrlData->path[i] ){
pUrlData->path[i] = 0;
i++;
}
if( fossil_strcmp(zName,"fossil")==0 ){
pUrlData->fossil = fossil_strdup(zValue);
dehttpize(pUrlData->fossil);
fossil_free(zExe);
zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil);
cQuerySep = '&';
}
}
dehttpize(pUrlData->path);
if( pUrlData->dfltPort==pUrlData->port ){
pUrlData->canonical = mprintf(
"%s://%s%T%T%z",
| > > | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
while( pUrlData->path[i] && pUrlData->path[i]!='&' ){ i++; }
}
if( pUrlData->path[i] ){
pUrlData->path[i] = 0;
i++;
}
if( fossil_strcmp(zName,"fossil")==0 ){
fossil_free(pUrlData->fossil);
pUrlData->fossil = fossil_strdup(zValue);
dehttpize(pUrlData->fossil);
fossil_free(zExe);
zExe = mprintf("%cfossil=%T", cQuerySep, pUrlData->fossil);
cQuerySep = '&';
urlFlags |= URL_SSH_EXE;
}
}
dehttpize(pUrlData->path);
if( pUrlData->dfltPort==pUrlData->port ){
pUrlData->canonical = mprintf(
"%s://%s%T%T%z",
|
| ︙ | ︙ | |||
315 316 317 318 319 320 321 |
free(zFile);
zFile = 0;
pUrlData->protocol = "file";
pUrlData->path = mprintf("");
pUrlData->name = mprintf("%b", &cfile);
pUrlData->canonical = mprintf("file://%T", pUrlData->name);
blob_reset(&cfile);
| | > | | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
free(zFile);
zFile = 0;
pUrlData->protocol = "file";
pUrlData->path = mprintf("");
pUrlData->name = mprintf("%b", &cfile);
pUrlData->canonical = mprintf("file://%T", pUrlData->name);
blob_reset(&cfile);
}else if( pUrlData->user!=0 && pUrlData->passwd==0
&& (urlFlags & URL_PROMPT_PW)!=0 ){
url_prompt_for_password_local(pUrlData);
}else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){
if( fossil_isatty(fossil_fileno(stdin))
&& ( urlFlags & URL_REMEMBER_PW )==0 ){
if( save_password_prompt(pUrlData->passwd) ){
pUrlData->flags = urlFlags |= URL_REMEMBER_PW;
}else{
pUrlData->flags = urlFlags &= ~URL_REMEMBER_PW;
}
}
}
|
| ︙ | ︙ | |||
409 410 411 412 413 414 415 416 417 418 419 420 421 422 | fossil_free(p->path); fossil_free(p->user); fossil_free(p->passwd); fossil_free(p->fossil); fossil_free(p->pwConfig); memset(p, 0, sizeof(*p)); } /* ** Parse the given URL, which describes a sync server. Populate variables ** in the global "g.url" structure as shown below. If zUrl is NULL, then ** parse the URL given in the last-sync-url setting, taking the password ** form last-sync-pw. ** | > > > > > > > > > | 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
fossil_free(p->path);
fossil_free(p->user);
fossil_free(p->passwd);
fossil_free(p->fossil);
fossil_free(p->pwConfig);
memset(p, 0, sizeof(*p));
}
/*
** Move a URL parse from one UrlData object to another.
*/
void url_move_parse(UrlData *pTo, UrlData *pFrom){
url_unparse(pTo);
memcpy(pTo, pFrom, sizeof(*pTo));
memset(pFrom, 0, sizeof(*pFrom));
}
/*
** Parse the given URL, which describes a sync server. Populate variables
** in the global "g.url" structure as shown below. If zUrl is NULL, then
** parse the URL given in the last-sync-url setting, taking the password
** form last-sync-pw.
**
|
| ︙ | ︙ | |||
451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
** password is taken from the CONFIG table, the g.url.pwConfig field is
** set to the CONFIG.NAME value from which that password is taken. Otherwise,
** g.url.pwConfig is NULL.
*/
void url_parse(const char *zUrl, unsigned int urlFlags){
url_parse_local(zUrl, urlFlags, &g.url);
}
/*
** COMMAND: test-urlparser
**
** Usage: %fossil test-urlparser URL ?options?
**
** --prompt-pw Prompt for password if missing
| > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
** password is taken from the CONFIG table, the g.url.pwConfig field is
** set to the CONFIG.NAME value from which that password is taken. Otherwise,
** g.url.pwConfig is NULL.
*/
void url_parse(const char *zUrl, unsigned int urlFlags){
url_parse_local(zUrl, urlFlags, &g.url);
}
/*
** Print the content of g.url
*/
void urlparse_print(int showPw){
fossil_print("g.url.isFile = %d\n", g.url.isFile);
fossil_print("g.url.isHttps = %d\n", g.url.isHttps);
fossil_print("g.url.isSsh = %d\n", g.url.isSsh);
fossil_print("g.url.protocol = %s\n", g.url.protocol);
fossil_print("g.url.name = %s\n", g.url.name);
fossil_print("g.url.port = %d\n", g.url.port);
fossil_print("g.url.dfltPort = %d\n", g.url.dfltPort);
fossil_print("g.url.hostname = %s\n", g.url.hostname);
fossil_print("g.url.path = %s\n", g.url.path);
fossil_print("g.url.user = %s\n", g.url.user);
if( showPw || g.url.pwConfig==0 ){
fossil_print("g.url.passwd = %s\n", g.url.passwd);
}else{
fossil_print("g.url.passwd = ************\n");
}
fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
fossil_print("g.url.canonical = %s\n", g.url.canonical);
fossil_print("g.url.fossil = %s\n", g.url.fossil);
fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
}
/*
** COMMAND: test-urlparser
**
** Usage: %fossil test-urlparser URL ?options?
**
** --prompt-pw Prompt for password if missing
|
| ︙ | ︙ | |||
480 481 482 483 484 485 486 |
if( find_option("show-pw",0,0) ) showPw = 1;
if( (fg & URL_USE_CONFIG)==0 ) showPw = 1;
if( g.argc!=3 && g.argc!=4 ){
usage("URL");
}
url_parse(g.argv[2], fg);
for(i=0; i<2; i++){
| < < < < < < < < < < | < < < < < < < < < | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
if( find_option("show-pw",0,0) ) showPw = 1;
if( (fg & URL_USE_CONFIG)==0 ) showPw = 1;
if( g.argc!=3 && g.argc!=4 ){
usage("URL");
}
url_parse(g.argv[2], fg);
for(i=0; i<2; i++){
urlparse_print(showPw);
if( g.url.isFile || g.url.isSsh ) break;
if( i==0 ){
fossil_print("********\n");
url_enable_proxy("Using proxy: ");
}
url_unparse(0);
}
|
| ︙ | ︙ | |||
714 715 716 717 718 719 720 |
/*
** Prompt the user for the password that corresponds to the "user" member of
** the provided UrlData structure. Store the result into the "passwd" member
** of the provided UrlData structure.
*/
void url_prompt_for_password_local(UrlData *pUrlData){
if( pUrlData->isSsh || pUrlData->isFile ) return;
| | | 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 |
/*
** Prompt the user for the password that corresponds to the "user" member of
** the provided UrlData structure. Store the result into the "passwd" member
** of the provided UrlData structure.
*/
void url_prompt_for_password_local(UrlData *pUrlData){
if( pUrlData->isSsh || pUrlData->isFile ) return;
if( fossil_isatty(fossil_fileno(stdin))
&& (pUrlData->flags & URL_PROMPT_PW)!=0
&& (pUrlData->flags & URL_PROMPTED)==0
){
pUrlData->flags |= URL_PROMPTED;
pUrlData->passwd = prompt_for_user_password(pUrlData->canonical);
if( pUrlData->passwd[0]
&& (pUrlData->flags & (URL_REMEMBER|URL_ASK_REMEMBER_PW))!=0
|
| ︙ | ︙ | |||
775 776 777 778 779 780 781 |
/* Preemptively prompt for a password if a username is given in the
** URL but no password.
*/
void url_get_password_if_needed(void){
if( (g.url.user && g.url.user[0])
&& (g.url.passwd==0 || g.url.passwd[0]==0)
| | | | 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 |
/* Preemptively prompt for a password if a username is given in the
** URL but no password.
*/
void url_get_password_if_needed(void){
if( (g.url.user && g.url.user[0])
&& (g.url.passwd==0 || g.url.passwd[0]==0)
&& fossil_isatty(fossil_fileno(stdin))
){
url_prompt_for_password();
}
}
/*
** Given a URL for a remote repository clone point, try to come up with a
** reasonable basename of a local clone of that repository.
**
** * If the URL has a path, use the tail of the path, with any suffix
** elided.
**
** * If the URL is just a domain name, without a path, then use the
** first element of the domain name, except skip over "www." if
** present and if there is a ".com" or ".org" or similar suffix.
**
** The string returned is obtained from fossil_malloc(). NULL might be
** returned if there is an error.
*/
char *url_to_repo_basename(const char *zUrl){
const char *zTail = 0;
|
| ︙ | ︙ |
Changes to src/user.c.
| ︙ | ︙ | |||
397 398 399 400 401 402 403 |
}
if( g.localOpen ){
db_lset("default-user", g.argv[3]);
}else{
db_set("default-user", g.argv[3], 0);
}
}
| | > | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
}
if( g.localOpen ){
db_lset("default-user", g.argv[3]);
}else{
db_set("default-user", g.argv[3], 0);
}
}
}else if(( n>=2 && strncmp(g.argv[2],"list",n)==0 ) ||
( n>=2 && strncmp(g.argv[2],"ls",n)==0 )){
Stmt q;
db_prepare(&q, "SELECT login, info FROM user ORDER BY login");
while( db_step(&q)==SQLITE_ROW ){
fossil_print("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1));
}
db_finalize(&q);
}else if( n>=2 && strncmp(g.argv[2],"password",2)==0 ){
|
| ︙ | ︙ | |||
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 |
iVerify = atoi(g.argv[3]);
prompt_for_password(g.argv[2], &answer, iVerify);
fossil_print("[%s]\n", blob_str(&answer));
}
/*
** WEBPAGE: access_log
**
** Show login attempts, including timestamp and IP address.
** Requires Admin privileges.
**
** Query parameters:
**
** y=N 1: success only. 2: failure only. 3: both (default: 3)
** n=N Number of entries to show (default: 200)
** o=N Skip this many entries (default: 0)
*/
| > | | | | | | | < < | | | | | | | | | 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 |
iVerify = atoi(g.argv[3]);
prompt_for_password(g.argv[2], &answer, iVerify);
fossil_print("[%s]\n", blob_str(&answer));
}
/*
** WEBPAGE: access_log
** WEBPAGE: user_log
**
** Show login attempts, including timestamp and IP address.
** Requires Admin privileges.
**
** Query parameters:
**
** y=N 1: success only. 2: failure only. 3: both (default: 3)
** n=N Number of entries to show (default: 200)
** o=N Skip this many entries (default: 0)
*/
void user_log_page(void){
int y = atoi(PD("y","3"));
int n = atoi(PD("n","200"));
int skip = atoi(PD("o","0"));
const char *zUser = P("u");
Blob sql;
Stmt q;
int cnt = 0;
int rc;
int fLogEnabled;
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
create_accesslog_table();
if( P("delall") && P("delallbtn") ){
db_multi_exec("DELETE FROM accesslog");
cgi_redirectf("%R/user_log?y=%d&n=%d&o=%o", y, n, skip);
return;
}
if( P("delanon") && P("delanonbtn") ){
db_multi_exec("DELETE FROM accesslog WHERE uname='anonymous'");
cgi_redirectf("%R/user_log?y=%d&n=%d&o=%o", y, n, skip);
return;
}
if( P("delfail") && P("delfailbtn") ){
db_multi_exec("DELETE FROM accesslog WHERE NOT success");
cgi_redirectf("%R/user_log?y=%d&n=%d&o=%o", y, n, skip);
return;
}
if( P("delold") && P("deloldbtn") ){
db_multi_exec("DELETE FROM accesslog WHERE rowid in"
"(SELECT rowid FROM accesslog ORDER BY rowid DESC"
" LIMIT -1 OFFSET 200)");
cgi_redirectf("%R/user_log?y=%d&n=%d", y, n);
return;
}
style_header("User Log");
style_submenu_element("Log-Menu", "setup-logmenu");
blob_zero(&sql);
blob_append_sql(&sql,
"SELECT uname, ipaddr, datetime(mtime,toLocal()), success"
" FROM accesslog"
);
if( zUser ){
blob_append_sql(&sql, " WHERE uname=%Q", zUser);
n = 1000000000;
skip = 0;
}else if( y==1 ){
blob_append(&sql, " WHERE success", -1);
}else if( y==2 ){
blob_append(&sql, " WHERE NOT success", -1);
}
blob_append_sql(&sql," ORDER BY rowid DESC LIMIT %d OFFSET %d", n+1, skip);
if( skip ){
style_submenu_element("Newer", "%R/user_log?o=%d&n=%d&y=%d",
skip>=n ? skip-n : 0, n, y);
}
rc = db_prepare_ignore_error(&q, "%s", blob_sql_text(&sql));
fLogEnabled = db_get_boolean("access-log", 0);
@ <div align="center">User logging is %s(fLogEnabled?"on":"off").
@ (Change this on the <a href="setup_settings">settings</a> page.)</div>
@ <table border="1" cellpadding="5" class="sortable" align="center" \
@ data-column-types='Ttt' data-init-sort='1'>
@ <thead><tr><th width="33%%">Date</th><th width="34%%">User</th>
@ <th width="33%%">IP Address</th></tr></thead><tbody>
while( rc==SQLITE_OK && db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const char *zIP = db_column_text(&q, 1);
const char *zDate = db_column_text(&q, 2);
int bSuccess = db_column_int(&q, 3);
cnt++;
if( cnt>n ){
style_submenu_element("Older", "%R/user_log?o=%d&n=%d&y=%d",
skip+n, n, y);
break;
}
if( bSuccess ){
@ <tr>
}else{
@ <tr bgcolor="#ffacc0">
}
@ <td>%s(zDate)</td><td>%h(zName)</td><td>%h(zIP)</td></tr>
}
if( skip>0 || cnt>n ){
style_submenu_element("All", "%R/user_log?n=10000000");
}
@ </tbody></table>
db_finalize(&q);
@ <hr>
@ <form method="post" action="%R/user_log">
@ <label><input type="checkbox" name="delold">
@ Delete all but the most recent 200 entries</input></label>
@ <input type="submit" name="deloldbtn" value="Delete"></input>
@ </form>
@ <form method="post" action="%R/user_log">
@ <label><input type="checkbox" name="delanon">
@ Delete all entries for user "anonymous"</input></label>
@ <input type="submit" name="delanonbtn" value="Delete"></input>
@ </form>
@ <form method="post" action="%R/user_log">
@ <label><input type="checkbox" name="delfail">
@ Delete all failed login attempts</input></label>
@ <input type="submit" name="delfailbtn" value="Delete"></input>
@ </form>
@ <form method="post" action="%R/user_log">
@ <label><input type="checkbox" name="delall">
@ Delete all entries</input></label>
@ <input type="submit" name="delallbtn" value="Delete"></input>
@ </form>
style_table_sorter();
style_finish_page();
}
|
Changes to src/util.c.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#include <math.h>
/*
** For the fossil_timer_xxx() family of functions...
*/
#ifdef _WIN32
# include <windows.h>
#else
# include <sys/time.h>
# include <sys/resource.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <fcntl.h>
# include <errno.h>
#endif
/*
** Exit. Take care to close the database first.
*/
NORETURN void fossil_exit(int rc){
db_close(1);
#ifndef _WIN32
| > > > > > > > > > > > > > > > > > > > > > > > | 26 27 28 29 30 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 |
#include <math.h>
/*
** For the fossil_timer_xxx() family of functions...
*/
#ifdef _WIN32
# include <windows.h>
# include <io.h>
#else
# include <sys/time.h>
# include <sys/resource.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
# include <fcntl.h>
# include <errno.h>
#endif
/*
** Returns the same as the platform's isatty() or _isatty() function.
*/
int fossil_isatty(int fd){
#ifdef _WIN32
return _isatty(fd);
#else
return isatty(fd);
#endif
}
/*
** Returns the same as the platform's fileno() or _fileno() function.
*/
int fossil_fileno(FILE *p){
#ifdef _WIN32
return _fileno(p);
#else
return fileno(p);
#endif
}
/*
** Exit. Take care to close the database first.
*/
NORETURN void fossil_exit(int rc){
db_close(1);
#ifndef _WIN32
|
| ︙ | ︙ | |||
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
if( munmap(p, n) ){
fossil_panic("munmap failed: %d\n", errno);
}
#else
fossil_free(p);
#endif
}
/*
** Translate every upper-case character in the input string into
** its equivalent lower-case.
*/
char *fossil_strtolwr(char *zIn){
char *zStart = zIn;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 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 |
if( munmap(p, n) ){
fossil_panic("munmap failed: %d\n", errno);
}
#else
fossil_free(p);
#endif
}
/*
** Duplicate a string.
*/
char *fossil_strndup(const char *zOrig, ssize_t len){
char *z = 0;
if( zOrig ){
if( len<0 ) len = strlen(zOrig);
z = fossil_malloc( len+1 );
memcpy(z, zOrig, len);
z[len] = 0;
}
return z;
}
char *fossil_strdup(const char *zOrig){
return fossil_strndup(zOrig, -1);
}
char *fossil_strdup_nn(const char *zOrig){
if( zOrig==0 ) return fossil_strndup("", 0);
return fossil_strndup(zOrig, -1);
}
/*
** strcpy() workalike to squelch an unwarranted warning from OpenBSD.
*/
void fossil_strcpy(char *dest, const char *src){
while( (*(dest++) = *(src++))!=0 ){}
}
/*
** Translate every upper-case character in the input string into
** its equivalent lower-case.
*/
char *fossil_strtolwr(char *zIn){
char *zStart = zIn;
|
| ︙ | ︙ |
Changes to src/vfile.c.
| ︙ | ︙ | |||
408 409 410 411 412 413 414 |
"original",
"output",
};
int i, j, n;
if( sqlite3_strglob("ci-comment-????????????.txt", zName)==0 ) return 1;
for(; zName[0]!=0; zName++){
| > | | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 |
"original",
"output",
};
int i, j, n;
if( sqlite3_strglob("ci-comment-????????????.txt", zName)==0 ) return 1;
for(; zName[0]!=0; zName++){
if( zName[0]=='/'
&& sqlite3_strglob("/ci-comment-????????????.txt", zName)==0 ){
return 1;
}
if( zName[0]!='-' ) continue;
for(i=0; i<count(azTemp); i++){
n = (int)strlen(azTemp[i]);
if( memcmp(azTemp[i], zName+1, n) ) continue;
if( zName[n+1]==0 ) return 1;
|
| ︙ | ︙ | |||
752 753 754 755 756 757 758 |
md5sum_step_text(" 0\n", -1);
continue;
}
fseek(in, 0L, SEEK_END);
sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
fseek(in, 0L, SEEK_SET);
md5sum_step_text(zBuf, -1);
| | | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 |
md5sum_step_text(" 0\n", -1);
continue;
}
fseek(in, 0L, SEEK_END);
sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
fseek(in, 0L, SEEK_SET);
md5sum_step_text(zBuf, -1);
/*printf("%s %s %s",md5sum_current_state(),zName,zBuf);fflush(stdout);*/
for(;;){
int n;
n = fread(zBuf, 1, sizeof(zBuf), in);
if( n<=0 ) break;
md5sum_step_text(zBuf, n);
}
fclose(in);
|
| ︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 |
/* Add RID values for merged-in files */
db_multi_exec(
"INSERT OR IGNORE INTO idMap(oldrid, newrid)"
" SELECT vfile.mrid, blob.rid FROM vfile, blob"
" WHERE blob.uuid=vfile.mhash;"
);
| | | | 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 |
/* Add RID values for merged-in files */
db_multi_exec(
"INSERT OR IGNORE INTO idMap(oldrid, newrid)"
" SELECT vfile.mrid, blob.rid FROM vfile, blob"
" WHERE blob.uuid=vfile.mhash;"
);
if( dryRun ){
Stmt q;
db_prepare(&q, "SELECT oldrid, newrid, blob.uuid"
" FROM idMap, blob WHERE blob.rid=idMap.newrid");
while( db_step(&q)==SQLITE_ROW ){
fossil_print("%8d -> %8d %.25s\n",
db_column_int(&q,0),
db_column_int(&q,1),
db_column_text(&q,2));
}
db_finalize(&q);
}
|
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
83 84 85 86 87 88 89 |
}
int wiki_tagid2(const char *zPrefix, const char *zPageName){
return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q/%q'",
zPrefix, zPageName);
}
/*
| | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
}
int wiki_tagid2(const char *zPrefix, const char *zPageName){
return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q/%q'",
zPrefix, zPageName);
}
/*
** Return the RID of the next or previous version of a wiki page.
** Return 0 if rid is the last/first version.
*/
int wiki_next(int tagid, double mtime){
return db_int(0,
"SELECT srcid FROM tagxref"
" WHERE tagid=%d AND mtime>%.16g"
" ORDER BY mtime ASC LIMIT 1",
|
| ︙ | ︙ | |||
202 203 204 205 206 207 208 209 210 211 212 |
}else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
Blob tail = BLOB_INITIALIZER;
markdown_to_html(pWiki, 0, &tail);
safe_html(&tail);
@ %s(blob_str(&tail))
blob_reset(&tail);
}else if( fossil_strcmp(zMimetype, "text/x-pikchr")==0 ){
const char *zPikchr = blob_str(pWiki);
int w, h;
char *zOut = pikchr(zPikchr, "pikchr", 0, &w, &h);
if( w>0 ){
| > > > | > > | > | 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 |
}else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
Blob tail = BLOB_INITIALIZER;
markdown_to_html(pWiki, 0, &tail);
safe_html(&tail);
@ %s(blob_str(&tail))
blob_reset(&tail);
}else if( fossil_strcmp(zMimetype, "text/x-pikchr")==0 ){
int isPopup = P("popup")!=0;
const char *zPikchr = blob_str(pWiki);
int w, h;
char *zOut = pikchr(zPikchr, "pikchr", 0, &w, &h);
if( w>0 ){
if( isPopup ) cgi_set_content_type("image/svg+xml");
else{
@ <div class="pikchr-svg" style="max-width:%d(w)px">
}
@ %s(zOut)
if( !isPopup){
@ </div>
}
}else{
@ <pre class='error'>
@ %h(zOut)
@ </pre>
}
free(zOut);
}else{
|
| ︙ | ︙ | |||
411 412 413 414 415 416 417 |
/*
** Figure out what type of wiki page we are dealing with.
*/
int wiki_page_type(const char *zPageName){
if( db_get_boolean("wiki-about",1)==0 ){
return WIKITYPE_NORMAL;
}else
| | | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 |
/*
** Figure out what type of wiki page we are dealing with.
*/
int wiki_page_type(const char *zPageName){
if( db_get_boolean("wiki-about",1)==0 ){
return WIKITYPE_NORMAL;
}else
if( sqlite3_strglob("checkin/*", zPageName)==0
&& db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
){
return WIKITYPE_CHECKIN;
}else
if( sqlite3_strglob("branch/*", zPageName)==0 ){
return WIKITYPE_BRANCH;
}else
|
| ︙ | ︙ | |||
445 446 447 448 449 450 451 | /* ** Add an appropriate style_header() for either the /wiki or /wikiedit page ** for zPageName. zExtra is an empty string for /wiki but has the text ** "Edit: " for /wikiedit. ** ** If the page is /wiki and the page is one of the special times (check-in, | | | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 | /* ** Add an appropriate style_header() for either the /wiki or /wikiedit page ** for zPageName. zExtra is an empty string for /wiki but has the text ** "Edit: " for /wikiedit. ** ** If the page is /wiki and the page is one of the special times (check-in, ** branch, or tag) and the "p" query parameter is omitted, then do a ** redirect to the display of the check-in, branch, or tag rather than ** continuing to the plain wiki display. */ static int wiki_page_header( int eType, /* Page type. Might be WIKITYPE_UNKNOWN */ const char *zPageName, /* Name of the page */ const char *zExtra /* Extra prefix text on the page header */ |
| ︙ | ︙ | |||
467 468 469 470 471 472 473 |
}
case WIKITYPE_CHECKIN: {
zPageName += 8;
if( zExtra[0]==0 && !P("p") ){
cgi_redirectf("%R/info/%s",zPageName);
}else{
style_header("Notes About Check-in %S", zPageName);
| | > | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
}
case WIKITYPE_CHECKIN: {
zPageName += 8;
if( zExtra[0]==0 && !P("p") ){
cgi_redirectf("%R/info/%s",zPageName);
}else{
style_header("Notes About Check-in %S", zPageName);
style_submenu_element("Check-in Timeline","%R/timeline?f=%s",
zPageName);
style_submenu_element("Check-in Info","%R/info/%s", zPageName);
}
break;
}
case WIKITYPE_BRANCH: {
zPageName += 7;
if( zExtra[0]==0 && !P("p") ){
|
| ︙ | ︙ | |||
549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
int isPopup = P("popup")!=0;
char *zBody = mprintf("%s","<i>Empty Page</i>");
int noSubmenu = P("nsm")!=0 || g.isHome;
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
zPageName = P("name");
cgi_check_for_malice();
if( zPageName==0 ){
if( search_restrict(SRCH_WIKI)!=0 ){
wiki_srchpage();
}else{
wiki_helppage();
}
| > | 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 |
int isPopup = P("popup")!=0;
char *zBody = mprintf("%s","<i>Empty Page</i>");
int noSubmenu = P("nsm")!=0 || g.isHome;
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
zPageName = P("name");
(void)P("s")/*for cgi_check_for_malice(). "s" == search stringy*/;
cgi_check_for_malice();
if( zPageName==0 ){
if( search_restrict(SRCH_WIKI)!=0 ){
wiki_srchpage();
}else{
wiki_helppage();
}
|
| ︙ | ︙ | |||
741 742 743 744 745 746 747 |
** Note that the sandbox is a special case: it is a pseudo-page with
** no rid and the /wikiajax API does not allow anyone to actually save
** a sandbox page, but it is reported as writable here (with rid 0).
*/
static int wiki_ajax_can_write(const char *zPageName, int * pRid){
int rid = 0;
const char * zErr = 0;
| | | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
** Note that the sandbox is a special case: it is a pseudo-page with
** no rid and the /wikiajax API does not allow anyone to actually save
** a sandbox page, but it is reported as writable here (with rid 0).
*/
static int wiki_ajax_can_write(const char *zPageName, int * pRid){
int rid = 0;
const char * zErr = 0;
if(pRid) *pRid = 0;
if(!zPageName || !*zPageName
|| !wiki_name_is_wellformed((unsigned const char *)zPageName)){
zErr = "Invalid page name.";
}else if(is_sandbox(zPageName)){
return 1;
}else{
|
| ︙ | ︙ | |||
764 765 766 767 768 769 770 |
}else if(!rid && !g.perm.NewWiki){
zErr = "Requires new-wiki permissions.";
}else{
zErr = "Cannot happen! Please report this as a bug.";
}
}
ajax_route_error(403, "%s", zErr);
| | | 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 |
}else if(!rid && !g.perm.NewWiki){
zErr = "Requires new-wiki permissions.";
}else{
zErr = "Cannot happen! Please report this as a bug.";
}
}
ajax_route_error(403, "%s", zErr);
return 0;
}
/*
** Emits an array of attachment info records for the given wiki page
** artifact.
**
|
| ︙ | ︙ | |||
1010 1011 1012 1013 1014 1015 1016 |
**
** Responds with JSON. On error, an object in the form documented by
** ajax_route_error(). On success, an object in the form documented
** for wiki_ajax_emit_page_object().
*/
static void wiki_ajax_route_fetch(void){
const char * zPageName = P("page");
| | | 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 |
**
** Responds with JSON. On error, an object in the form documented by
** ajax_route_error(). On success, an object in the form documented
** for wiki_ajax_emit_page_object().
*/
static void wiki_ajax_route_fetch(void){
const char * zPageName = P("page");
if( zPageName==0 || zPageName[0]==0 ){
ajax_route_error(400,"Missing page name.");
return;
}
cgi_set_content_type("application/json");
wiki_ajax_emit_page_object(zPageName, 1);
}
|
| ︙ | ︙ | |||
1201 1202 1203 1204 1205 1206 1207 | } /* ** WEBPAGE: wikiajax hidden ** ** An internal dispatcher for wiki AJAX operations. Not for direct ** client use. All routes defined by this interface are app-internal, | | | 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 |
}
/*
** WEBPAGE: wikiajax hidden
**
** An internal dispatcher for wiki AJAX operations. Not for direct
** client use. All routes defined by this interface are app-internal,
** subject to change
*/
void wiki_ajax_page(void){
const char * zName = P("name");
AjaxRoute routeName = {0,0,0,0};
const AjaxRoute * pRoute = 0;
const AjaxRoute routes[] = {
/* Keep these sorted by zName (for bsearch()) */
|
| ︙ | ︙ | |||
1341 1342 1343 1344 1345 1346 1347 |
"data-tab-parent='wikiedit-tabs' "
"data-tab-label='Wiki Page List' "
"class='hidden'"
">");
CX("<div>Loading wiki pages list...</div>");
CX("</div>"/*#wikiedit-tab-pages*/);
}
| | | 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 |
"data-tab-parent='wikiedit-tabs' "
"data-tab-label='Wiki Page List' "
"class='hidden'"
">");
CX("<div>Loading wiki pages list...</div>");
CX("</div>"/*#wikiedit-tab-pages*/);
}
/******* Content tab *******/
{
CX("<div id='wikiedit-tab-content' "
"data-tab-parent='wikiedit-tabs' "
"data-tab-label='Editor' "
"class='hidden'"
">");
|
| ︙ | ︙ | |||
1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 |
** pid=HASH Hash prefix for the parent.
**
** The "id" query parameter is required. "pid" is optional. If "pid"
** is omitted, then the diff is against the first parent of the child.
*/
void wdiff_page(void){
const char *zId;
const char *zPid;
Manifest *pW1, *pW2 = 0;
int rid1, rid2, nextRid;
Blob w1, w2, d;
DiffConfig DCfg;
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
zId = P("id");
if( zId==0 ){
rid1 = atoi(PD("rid","0"));
}else{
rid1 = name_to_typed_rid(zId, "w");
}
| > | > > > > > > > > > | 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 |
** pid=HASH Hash prefix for the parent.
**
** The "id" query parameter is required. "pid" is optional. If "pid"
** is omitted, then the diff is against the first parent of the child.
*/
void wdiff_page(void){
const char *zId;
const char *zIdFull;
const char *zPid;
Manifest *pW1, *pW2 = 0;
int rid1, rid2, nextRid;
Blob w1, w2, d;
DiffConfig DCfg;
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
zId = P("id");
if( zId==0 ){
rid1 = atoi(PD("rid","0"));
}else{
rid1 = name_to_typed_rid(zId, "w");
}
zIdFull = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid1);
if( zIdFull==0 ){
if( zId ){
webpage_notfound_error("No such wiki page: \"%s\"", zId);
}else{
webpage_notfound_error("No such wiki page: %d", rid1);
}
return;
}
zId = zIdFull;
pW1 = manifest_get(rid1, CFTYPE_WIKI, 0);
if( pW1==0 ) fossil_redirect_home();
blob_init(&w1, pW1->zWiki, -1);
zPid = P("pid");
if( ( zPid==0 || zPid[0] == 0 ) && pW1->nParent ){
zPid = pW1->azParent[0];
}
|
| ︙ | ︙ | |||
1903 1904 1905 1906 1907 1908 1909 | ** wsort Sort names by this label ** wrid rid of the most recent version of the page ** wmtime time most recent version was created ** wcnt Number of versions of this wiki page ** ** The wrid value is zero for deleted wiki pages. */ | | | 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 | ** wsort Sort names by this label ** wrid rid of the most recent version of the page ** wmtime time most recent version was created ** wcnt Number of versions of this wiki page ** ** The wrid value is zero for deleted wiki pages. */ static const char listAllWikiPages[] = @ SELECT @ substr(tag.tagname, 6) AS wname, @ lower(substr(tag.tagname, 6)) AS sortname, @ tagxref.value+0 AS wrid, @ max(tagxref.mtime) AS wmtime, @ count(*) AS wcnt @ FROM |
| ︙ | ︙ | |||
2131 2132 2133 2134 2135 2136 2137 |
if( !rid ) {
/*
** At present, technote tags are prefixed with 'sym-', which shouldn't
** be the case, so we check for both with and without the prefix until
** such time as tags have the errant prefix dropped.
*/
rid = db_int(0, "SELECT e.objid"
| | | | | | | | | 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 |
if( !rid ) {
/*
** At present, technote tags are prefixed with 'sym-', which shouldn't
** be the case, so we check for both with and without the prefix until
** such time as tags have the errant prefix dropped.
*/
rid = db_int(0, "SELECT e.objid"
" FROM event e, tag t, tagxref tx"
" WHERE e.type='e'"
" AND e.tagid IS NOT NULL"
" AND e.objid IN"
" (SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag"
" WHERE tagname GLOB '%q'))"
" OR e.objid IN"
" (SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag"
" WHERE tagname GLOB 'sym-%q'))"
" ORDER BY e.mtime DESC LIMIT 1",
zETime, zETime);
}
return rid;
}
/*
** COMMAND: wiki*
**
|
| ︙ | ︙ | |||
2480 2481 2482 2483 2484 2485 2486 |
}
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const int wrid = db_column_int(&q, 2);
if(!showAll && !wrid){
continue;
}
| | | 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 |
}
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
const int wrid = db_column_int(&q, 2);
if(!showAll && !wrid){
continue;
}
if( !showCkBr &&
(sqlite3_strglob("checkin/*", zName)==0 ||
sqlite3_strglob("branch/*", zName)==0) ){
continue;
}
if( showIds ){
const char *zUuid = db_column_text(&q, 1);
fossil_print("%s ",zUuid);
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
int state; /* Flag that govern rendering */
unsigned renderFlags; /* Flags from the client */
int wikiList; /* Current wiki list type */
int inVerbatim; /* True in <verbatim> mode */
int preVerbState; /* Value of state prior to verbatim */
int wantAutoParagraph; /* True if a <p> is desired */
int inAutoParagraph; /* True if within an automatic paragraph */
const char *zVerbatimId; /* The id= attribute of <verbatim> */
int nStack; /* Number of elements on the stack */
int nAlloc; /* Space allocated for aStack */
struct sStack {
short iCode; /* Markup code */
short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */
const char *zId; /* ID attribute or NULL */
| > | 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 |
int state; /* Flag that govern rendering */
unsigned renderFlags; /* Flags from the client */
int wikiList; /* Current wiki list type */
int inVerbatim; /* True in <verbatim> mode */
int preVerbState; /* Value of state prior to verbatim */
int wantAutoParagraph; /* True if a <p> is desired */
int inAutoParagraph; /* True if within an automatic paragraph */
int pikchrHtmlFlags; /* Flags for pikchr_to_html() */
const char *zVerbatimId; /* The id= attribute of <verbatim> */
int nStack; /* Number of elements on the stack */
int nAlloc; /* Space allocated for aStack */
struct sStack {
short iCode; /* Markup code */
short allowWiki; /* ALLOW_WIKI if wiki allowed before tag */
const char *zId; /* ID attribute or NULL */
|
| ︙ | ︙ | |||
1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 |
}else
if( markup.iType==MUTYPE_TD ){
if( backupToType(p, MUTYPE_TABLE|MUTYPE_TR) ){
if( stackTopType(p)==MUTYPE_TABLE ){
pushStack(p, MARKUP_TR);
blob_append_string(p->pOut, "<tr>");
}
pushStack(p, markup.iCode);
renderMarkup(p->pOut, &markup);
}
}else
if( markup.iType==MUTYPE_HYPERLINK ){
if( !isButtonHyperlink(p, &markup, z, &n) ){
popStackToTag(p, markup.iCode);
| > | 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 |
}else
if( markup.iType==MUTYPE_TD ){
if( backupToType(p, MUTYPE_TABLE|MUTYPE_TR) ){
if( stackTopType(p)==MUTYPE_TABLE ){
pushStack(p, MARKUP_TR);
blob_append_string(p->pOut, "<tr>");
}
p->wantAutoParagraph = 0;
pushStack(p, markup.iCode);
renderMarkup(p->pOut, &markup);
}
}else
if( markup.iType==MUTYPE_HYPERLINK ){
if( !isButtonHyperlink(p, &markup, z, &n) ){
popStackToTag(p, markup.iCode);
|
| ︙ | ︙ | |||
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 1917 1918 1919 1920 1921 |
** Options:
** --buttons Set the WIKI_BUTTONS flag
** --htmlonly Set the WIKI_HTMLONLY flag
** --linksonly Set the WIKI_LINKSONLY flag
** --nobadlinks Set the WIKI_NOBADLINKS flag
** --inline Set the WIKI_INLINE flag
** --noblock Set the WIKI_NOBLOCK flag
*/
void test_wiki_render(void){
Blob in, out;
int flags = 0;
if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
verify_all_options();
if( g.argc!=3 ) usage("FILE");
blob_zero(&out);
blob_read_from_file(&in, g.argv[2], ExtFILE);
wiki_convert(&in, &out, flags);
blob_write_to_file(&out, "-");
}
/*
** COMMAND: test-markdown-render
**
** Usage: %fossil test-markdown-render FILE ...
**
** Render markdown in FILE as HTML on stdout.
** Options:
**
** --safe Restrict the output to use only "safe" HTML
** --lint-footnotes Print stats for footnotes-related issues
*/
void test_markdown_render(void){
Blob in, out;
int i;
int bSafe = 0, bFnLint = 0;
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
bSafe = find_option("safe",0,0)!=0;
bFnLint = find_option("lint-footnotes",0,0)!=0;
verify_all_options();
for(i=2; i<g.argc; i++){
blob_zero(&out);
blob_read_from_file(&in, g.argv[i], ExtFILE);
if( g.argc>3 ){
fossil_print("<!------ %h ------->\n", g.argv[i]);
}
| > > > > > > > > | 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 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 |
** Options:
** --buttons Set the WIKI_BUTTONS flag
** --htmlonly Set the WIKI_HTMLONLY flag
** --linksonly Set the WIKI_LINKSONLY flag
** --nobadlinks Set the WIKI_NOBADLINKS flag
** --inline Set the WIKI_INLINE flag
** --noblock Set the WIKI_NOBLOCK flag
** --dark-pikchr Render pikchrs in dark mode
*/
void test_wiki_render(void){
Blob in, out;
int flags = 0;
if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
if( find_option("dark-pikchr",0,0)!=0 ){
pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
}
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
verify_all_options();
if( g.argc!=3 ) usage("FILE");
blob_zero(&out);
blob_read_from_file(&in, g.argv[2], ExtFILE);
wiki_convert(&in, &out, flags);
blob_write_to_file(&out, "-");
}
/*
** COMMAND: test-markdown-render
**
** Usage: %fossil test-markdown-render FILE ...
**
** Render markdown in FILE as HTML on stdout.
** Options:
**
** --safe Restrict the output to use only "safe" HTML
** --lint-footnotes Print stats for footnotes-related issues
** --dark-pikchr Render pikchrs in dark mode
*/
void test_markdown_render(void){
Blob in, out;
int i;
int bSafe = 0, bFnLint = 0;
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
bSafe = find_option("safe",0,0)!=0;
bFnLint = find_option("lint-footnotes",0,0)!=0;
if( find_option("dark-pikchr",0,0)!=0 ){
pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
}
verify_all_options();
for(i=2; i<g.argc; i++){
blob_zero(&out);
blob_read_from_file(&in, g.argv[i], ExtFILE);
if( g.argc>3 ){
fossil_print("<!------ %h ------->\n", g.argv[i]);
}
|
| ︙ | ︙ | |||
2217 2218 2219 2220 2221 2222 2223 |
iMatchCnt = 1;
}else if( n==1 && zStart[0]=='=' && iMatchCnt==1 ){
iMatchCnt = 2;
}else if( iMatchCnt==2 ){
if( (zStart[0]=='"' || zStart[0]=='\'') && zStart[n-1]==zStart[0] ){
zStart++;
n -= 2;
| | | 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 |
iMatchCnt = 1;
}else if( n==1 && zStart[0]=='=' && iMatchCnt==1 ){
iMatchCnt = 2;
}else if( iMatchCnt==2 ){
if( (zStart[0]=='"' || zStart[0]=='\'') && zStart[n-1]==zStart[0] ){
zStart++;
n -= 2;
}
*pLen = n;
return zStart;
}else{
iMatchCnt = 0;
}
}
return 0;
|
| ︙ | ︙ |
Changes to src/winhttp.c.
| ︙ | ︙ | |||
666 667 668 669 670 671 672 |
fossil_panic("unable to get path to the temporary directory.");
}
/* Use a subdirectory for temp files (can then be excluded from virus scan) */
zTempSubDirPath = mprintf("%s%s\\",fossil_path_to_utf8(zTmpPath),zTempSubDir);
if ( !file_mkdir(zTempSubDirPath, ExtFILE, 0) ||
file_isdir(zTempSubDirPath, ExtFILE)==1 ){
wcscpy(zTmpPath, fossil_utf8_to_path(zTempSubDirPath, 1));
| | | 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 |
fossil_panic("unable to get path to the temporary directory.");
}
/* Use a subdirectory for temp files (can then be excluded from virus scan) */
zTempSubDirPath = mprintf("%s%s\\",fossil_path_to_utf8(zTmpPath),zTempSubDir);
if ( !file_mkdir(zTempSubDirPath, ExtFILE, 0) ||
file_isdir(zTempSubDirPath, ExtFILE)==1 ){
wcscpy(zTmpPath, fossil_utf8_to_path(zTempSubDirPath, 1));
}
if( g.fHttpTrace ){
zTempPrefix = mprintf("httptrace");
}else{
zTempPrefix = mprintf("%sfossil_server_P%d",
fossil_unicode_to_utf8(zTmpPath), iPort);
}
fossil_print("Temporary files: %s*\n", zTempPrefix);
|
| ︙ | ︙ | |||
1370 1371 1372 1373 1374 1375 1376 |
if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
SERVICE_ALL_ACCESS);
if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
QueryServiceStatus(hSvc, &sstat);
if( sstat.dwCurrentState!=SERVICE_RUNNING ){
fossil_print("Starting service '%s'", zSvcName);
| | | | | | | | 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 |
if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
SERVICE_ALL_ACCESS);
if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
QueryServiceStatus(hSvc, &sstat);
if( sstat.dwCurrentState!=SERVICE_RUNNING ){
fossil_print("Starting service '%s'", zSvcName);
if( sstat.dwCurrentState!=SERVICE_START_PENDING ){
if( !StartServiceW(hSvc, 0, NULL) ){
winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
}
QueryServiceStatus(hSvc, &sstat);
}
while( sstat.dwCurrentState==SERVICE_START_PENDING ||
sstat.dwCurrentState==SERVICE_STOPPED ){
Sleep(100);
fossil_print(".");
QueryServiceStatus(hSvc, &sstat);
}
if( sstat.dwCurrentState==SERVICE_RUNNING ){
|
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
353 354 355 356 357 358 359 |
}
}else{
nullContent = 1;
}
/* The isWriter flag must be true in order to land the new file */
if( !isWriter ){
| | | 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
}
}else{
nullContent = 1;
}
/* The isWriter flag must be true in order to land the new file */
if( !isWriter ){
blob_appendf(&pXfer->err,"Write permissions for unversioned files missing");
goto end_accept_unversioned_file;
}
/* Make sure we have a valid g.rcvid marker */
content_rcvid_init(0);
/* Check to see if current content really should be overwritten. Ideally,
|
| ︙ | ︙ | |||
1187 1188 1189 1190 1191 1192 1193 | /* ** The CGI/HTTP preprocessor always redirects requests with a content-type ** of application/x-fossil or application/x-fossil-debug to this page, ** regardless of what path was specified in the HTTP header. This allows ** clone clients to specify a URL that omits default pathnames, such ** as "http://fossil-scm.org/" instead of "http://fossil-scm.org/index.cgi". ** | | | 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 |
/*
** The CGI/HTTP preprocessor always redirects requests with a content-type
** of application/x-fossil or application/x-fossil-debug to this page,
** regardless of what path was specified in the HTTP header. This allows
** clone clients to specify a URL that omits default pathnames, such
** as "http://fossil-scm.org/" instead of "http://fossil-scm.org/index.cgi".
**
** WEBPAGE: xfer raw-content loadavg-exempt
**
** This is the transfer handler on the server side. The transfer
** message has been uncompressed and placed in the g.cgiIn blob.
** Process this message and form an appropriate reply.
*/
void page_xfer(void){
int isPull = 0;
|
| ︙ | ︙ | |||
1832 1833 1834 1835 1836 1837 1838 |
const char *zArg = db_column_text(&q, 1);
i64 iMtime = db_column_int64(&q, 2);
memset(&x, 0, sizeof(x));
url_parse_local(zUrl, URL_OMIT_USER, &x);
if( x.name!=0 && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){
@ pragma link %F(x.canonical) %F(zArg) %lld(iMtime)
}
| | | 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 |
const char *zArg = db_column_text(&q, 1);
i64 iMtime = db_column_int64(&q, 2);
memset(&x, 0, sizeof(x));
url_parse_local(zUrl, URL_OMIT_USER, &x);
if( x.name!=0 && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){
@ pragma link %F(x.canonical) %F(zArg) %lld(iMtime)
}
url_unparse(&x);
}
db_finalize(&q);
}
/* Send the server timestamp last, in case prior processing happened
** to use up a significant fraction of our time window.
*/
|
| ︙ | ︙ | |||
1857 1858 1859 1860 1861 1862 1863 | ** ** Usage: %fossil test-xfer ?OPTIONS? XFERFILE ** ** Pass the sync-protocol input file XFERFILE into the server-side sync ** protocol handler. Generate a reply on standard output. ** ** This command was original created to help debug the server side of | | | 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 | ** ** Usage: %fossil test-xfer ?OPTIONS? XFERFILE ** ** Pass the sync-protocol input file XFERFILE into the server-side sync ** protocol handler. Generate a reply on standard output. ** ** This command was original created to help debug the server side of ** sync messages. The XFERFILE is the uncompressed content of an ** "xfer" HTTP request from client to server. This command interprets ** that message and generates the content of an HTTP reply (without any ** encoding and without the HTTP reply headers) and writes that reply ** on standard output. ** ** One possible usages scenario is to capture some XFERFILE examples ** using a command like: |
| ︙ | ︙ | |||
1947 1948 1949 1950 1951 1952 1953 | ** are pulled if pullFlag is true. A full sync occurs if both are ** true. */ int client_sync( unsigned syncFlags, /* Mask of SYNC_* flags */ unsigned configRcvMask, /* Receive these configuration items */ unsigned configSendMask, /* Send these configuration items */ | | > | 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 |
** are pulled if pullFlag is true. A full sync occurs if both are
** true.
*/
int client_sync(
unsigned syncFlags, /* Mask of SYNC_* flags */
unsigned configRcvMask, /* Receive these configuration items */
unsigned configSendMask, /* Send these configuration items */
const char *zAltPCode, /* Alternative project code (usually NULL) */
int *pnRcvd /* Set to # received artifacts, if not NULL */
){
int go = 1; /* Loop until zero */
int nCardSent = 0; /* Number of cards sent */
int nCardRcvd = 0; /* Number of cards received */
int nCycle = 0; /* Number of round trips to the server */
int size; /* Size of a config value or uvfile */
int origConfigRcvMask; /* Original value of configRcvMask */
|
| ︙ | ︙ | |||
1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 |
int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
sqlite3_int64 mtime; /* Modification time on a UV file */
int autopushFailed = 0; /* Autopush following commit failed if true */
const char *zCkinLock; /* Name of check-in to lock. NULL for none */
const char *zClientId; /* A unique identifier for this check-out */
unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */
if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
&& configRcvMask==0
&& configSendMask==0
){
return 0; /* Nothing to do */
}
| > > | 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 |
int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
sqlite3_int64 mtime; /* Modification time on a UV file */
int autopushFailed = 0; /* Autopush following commit failed if true */
const char *zCkinLock; /* Name of check-in to lock. NULL for none */
const char *zClientId; /* A unique identifier for this check-out */
unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */
const int bOutIsTty = fossil_isatty(fossil_fileno(stdout));
if( pnRcvd ) *pnRcvd = 0;
if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
&& configRcvMask==0
&& configSendMask==0
){
return 0; /* Nothing to do */
}
|
| ︙ | ︙ | |||
2300 2301 2302 2303 2304 2305 2306 |
if( syncFlags & SYNC_VERBOSE ){
fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:",
blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
xfer.nFileSent, xfer.nDeltaSent);
}else{
nRoundtrip++;
nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
| > | | > | 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 |
if( syncFlags & SYNC_VERBOSE ){
fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:",
blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
xfer.nFileSent, xfer.nDeltaSent);
}else{
nRoundtrip++;
nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
if( bOutIsTty!=0 ){
fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
nRoundtrip, nArtifactSent, nArtifactRcvd);
}
}
nCardSent = 0;
nCardRcvd = 0;
xfer.nFileSent = 0;
xfer.nDeltaSent = 0;
xfer.nGimmeSent = 0;
xfer.nIGotSent = 0;
|
| ︙ | ︙ | |||
2526 2527 2528 2529 2530 2531 2532 |
if( iStatus>=4 && uvPullOnly==1 ){
fossil_warning(
"Warning: uv-pull-only \n"
" Unable to push unversioned content because you lack\n"
" sufficient permission on the server\n"
);
uvPullOnly = 2;
| | | 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 |
if( iStatus>=4 && uvPullOnly==1 ){
fossil_warning(
"Warning: uv-pull-only \n"
" Unable to push unversioned content because you lack\n"
" sufficient permission on the server\n"
);
uvPullOnly = 2;
}
if( iStatus<=3 || uvPullOnly ){
db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
}else if( iStatus==4 ){
db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q",zName);
}else if( iStatus==5 ){
db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)",
zName);
|
| ︙ | ︙ | |||
2801 2802 2803 2804 2805 2806 2807 |
}
origConfigRcvMask = 0;
if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){
fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Received:",
blob_size(&recv), nCardRcvd,
xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
}else{
| > | | > | 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 |
}
origConfigRcvMask = 0;
if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){
fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Received:",
blob_size(&recv), nCardRcvd,
xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
}else{
if( bOutIsTty!=0 ){
fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
nRoundtrip, nArtifactSent, nArtifactRcvd);
}
}
nUncRcvd += blob_size(&recv);
blob_reset(&recv);
nCycle++;
/* Set go to 1 if we need to continue the sync/push/pull/clone for
** another round. Set go to 0 if it is time to quit. */
|
| ︙ | ︙ | |||
2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 |
}else{
manifest_crosslink_end(MC_PERMIT_HOOKS);
content_enable_dephantomize(1);
}
db_end_transaction(0);
};
transport_stats(&nSent, &nRcvd, 1);
if( (rSkew*24.0*3600.0) > 10.0 ){
fossil_warning("*** time skew *** server is fast by %s",
db_timespan_name(rSkew));
g.clockSkewSeen = 1;
}else if( rSkew*24.0*3600.0 < -10.0 ){
fossil_warning("*** time skew *** server is slow by %s",
db_timespan_name(-rSkew));
g.clockSkewSeen = 1;
}
| > > > > > | > > | 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 |
}else{
manifest_crosslink_end(MC_PERMIT_HOOKS);
content_enable_dephantomize(1);
}
db_end_transaction(0);
};
transport_stats(&nSent, &nRcvd, 1);
if( pnRcvd ) *pnRcvd = nArtifactRcvd;
if( (rSkew*24.0*3600.0) > 10.0 ){
fossil_warning("*** time skew *** server is fast by %s",
db_timespan_name(rSkew));
g.clockSkewSeen = 1;
}else if( rSkew*24.0*3600.0 < -10.0 ){
fossil_warning("*** time skew *** server is slow by %s",
db_timespan_name(-rSkew));
g.clockSkewSeen = 1;
}
if( bOutIsTty==0 ){
fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
nRoundtrip, nArtifactSent, nArtifactRcvd);
fossil_force_newline();
}
fossil_force_newline();
if( g.zHttpCmd==0 ){
if( syncFlags & SYNC_VERBOSE ){
fossil_print(
"%s done, wire bytes sent: %lld received: %lld remote: %s%s\n",
zOpType, nSent, nRcvd,
(g.url.name && g.url.name[0]!='\0') ? g.url.name : "",
(g.zIpAddr && g.zIpAddr[0]!='\0'
&& fossil_strcmp(g.zIpAddr, g.url.name))
? mprintf(" (%s)", g.zIpAddr) : "");
}else{
fossil_print(
"%s done, wire bytes sent: %lld received: %lld remote: %s\n",
zOpType, nSent, nRcvd, g.zIpAddr);
}
}
if( syncFlags & SYNC_VERBOSE ){
fossil_print(
"Uncompressed payload sent: %lld received: %lld\n", nUncSent, nUncRcvd);
}
blob_reset(&send);
blob_reset(&recv);
transport_close(&g.url);
transport_global_shutdown(&g.url);
if( nErr && go==2 ){
db_multi_exec("DROP TABLE onremote; DROP TABLE unk;");
manifest_crosslink_end(MC_PERMIT_HOOKS);
content_enable_dephantomize(1);
db_end_transaction(0);
|
| ︙ | ︙ |
Changes to src/xfersetup.c.
| ︙ | ︙ | |||
78 79 80 81 82 83 84 |
@ <input type="submit" name="sync" value="%h(zButton)">
@ </div></form>
@
if( P("sync") ){
user_select();
url_enable_proxy(0);
@ <pre class="xfersetup">
| | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
@ <input type="submit" name="sync" value="%h(zButton)">
@ </div></form>
@
if( P("sync") ){
user_select();
url_enable_proxy(0);
@ <pre class="xfersetup">
client_sync(syncFlags, 0, 0, 0, 0);
@ </pre>
}
}
style_finish_page();
}
|
| ︙ | ︙ |
Changes to src/zip.c.
| ︙ | ︙ | |||
136 137 138 139 140 141 142 |
return 512;
}
static int archiveDeviceCharacteristics(sqlite3_file *pFile){
return 0;
}
static int archiveOpen(
| | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
return 512;
}
static int archiveDeviceCharacteristics(sqlite3_file *pFile){
return 0;
}
static int archiveOpen(
sqlite3_vfs *pVfs, const char *zName,
sqlite3_file *pFile, int flags, int *pOutFlags
){
static struct sqlite3_io_methods methods = {
1, /* iVersion */
archiveClose,
archiveRead,
archiveWrite,
|
| ︙ | ︙ | |||
245 246 247 248 249 250 251 | ** Append a single file to a growing ZIP archive. ** ** pFile is the file to be appended. zName is the name ** that the file should be saved as. */ static void zip_add_file_to_zip( Archive *p, | | | | 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
** Append a single file to a growing ZIP archive.
**
** pFile is the file to be appended. zName is the name
** that the file should be saved as.
*/
static void zip_add_file_to_zip(
Archive *p,
const char *zName,
const Blob *pFile,
int mPerm
){
z_stream stream;
int nameLen;
int toOut = 0;
int iStart;
unsigned long iCRC = 0;
|
| ︙ | ︙ | |||
372 373 374 375 376 377 378 | put16(&zExTime[2], 5); blob_append(&toc, zExTime, 9); nEntry++; } static void zip_add_file_to_sqlar( Archive *p, | | | | | | | | 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 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 |
put16(&zExTime[2], 5);
blob_append(&toc, zExTime, 9);
nEntry++;
}
static void zip_add_file_to_sqlar(
Archive *p,
const char *zName,
const Blob *pFile,
int mPerm
){
int nName = (int)strlen(zName);
if( p->db==0 ){
assert( p->vfs.zName==0 );
p->vfs.zName = (const char*)mprintf("archivevfs%p", (void*)p);
p->vfs.iVersion = 1;
p->vfs.szOsFile = sizeof(ArchiveFile);
p->vfs.mxPathname = 512;
p->vfs.pAppData = (void*)p->pBlob;
p->vfs.xOpen = archiveOpen;
p->vfs.xDelete = archiveDelete;
p->vfs.xAccess = archiveAccess;
p->vfs.xFullPathname = archiveFullPathname;
p->vfs.xRandomness = archiveRandomness;
p->vfs.xSleep = archiveSleep;
p->vfs.xCurrentTime = archiveCurrentTime;
p->vfs.xGetLastError = archiveGetLastError;
sqlite3_vfs_register(&p->vfs, 0);
sqlite3_open_v2("file:xyz.db", &p->db,
SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, p->vfs.zName
);
assert( p->db );
blob_zero(&p->tmp);
sqlite3_exec(p->db,
"PRAGMA page_size=512;"
"PRAGMA journal_mode = off;"
"PRAGMA cache_spill = off;"
"BEGIN;"
"CREATE TABLE sqlar("
"name TEXT PRIMARY KEY, -- name of the file\n"
"mode INT, -- access permissions\n"
"mtime INT, -- last modification time\n"
"sz INT, -- original file size\n"
"data BLOB -- compressed content\n"
");", 0, 0, 0
);
sqlite3_prepare(p->db,
"INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
&p->pInsert, 0
);
assert( p->pInsert );
sqlite3_bind_int64(p->pInsert, 3, unixTime);
blob_zero(p->pBlob);
}
|
| ︙ | ︙ | |||
435 436 437 438 439 440 441 |
sqlite3_bind_int(p->pInsert, 4, 0);
sqlite3_bind_null(p->pInsert, 5);
}else{
sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
if( mPerm==PERM_LNK ){
sqlite3_bind_int(p->pInsert, 2, 0120755);
sqlite3_bind_int(p->pInsert, 4, -1);
| | | | | | | 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 468 469 470 471 472 473 474 475 476 477 478 479 480 |
sqlite3_bind_int(p->pInsert, 4, 0);
sqlite3_bind_null(p->pInsert, 5);
}else{
sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
if( mPerm==PERM_LNK ){
sqlite3_bind_int(p->pInsert, 2, 0120755);
sqlite3_bind_int(p->pInsert, 4, -1);
sqlite3_bind_text(p->pInsert, 5,
blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
);
}else{
unsigned int nIn = blob_size(pFile);
unsigned long int nOut = nIn;
sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644);
sqlite3_bind_int(p->pInsert, 4, nIn);
zip_blob_minsize(&p->tmp, nIn);
compress( (unsigned char*)
blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn
);
if( nOut>=(unsigned long)nIn ){
sqlite3_bind_blob(p->pInsert, 5,
blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
);
}else{
sqlite3_bind_blob(p->pInsert, 5,
blob_buffer(&p->tmp), nOut, SQLITE_STATIC
);
}
}
}
sqlite3_step(p->pInsert);
sqlite3_reset(p->pInsert);
}
static void zip_add_file(
Archive *p,
const char *zName,
const Blob *pFile,
int mPerm
){
if( p->eType==ARCHIVE_ZIP ){
zip_add_file_to_zip(p, zName, pFile, mPerm);
}else{
zip_add_file_to_sqlar(p, zName, pFile, mPerm);
}
|
| ︙ | ︙ | |||
784 785 786 787 788 789 790 |
" || substr(blob.uuid, 1, 10)"
" FROM event, blob"
" WHERE event.objid=%d"
" AND blob.rid=%d",
db_get("project-name", "unnamed"), rid, rid
);
}
| | | 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 |
" || substr(blob.uuid, 1, 10)"
" FROM event, blob"
" WHERE event.objid=%d"
" AND blob.rid=%d",
db_get("project-name", "unnamed"), rid, rid
);
}
zip_of_checkin(eType, rid, zOut ? &zip : 0,
zName, pInclude, pExclude, listFlag);
glob_free(pInclude);
glob_free(pExclude);
if( zOut ){
blob_write_to_file(&zip, zOut);
blob_reset(&zip);
}
|
| ︙ | ︙ | |||
945 946 947 948 949 950 951 |
zInclude = P("in");
if( zInclude ) pInclude = glob_create(zInclude);
zExclude = P("ex");
if( zExclude ) pExclude = glob_create(zExclude);
if( zInclude==0 && zExclude==0 ){
etag_check_for_invariant_name(z);
}
| | | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 |
zInclude = P("in");
if( zInclude ) pInclude = glob_create(zInclude);
zExclude = P("ex");
if( zExclude ) pExclude = glob_create(zExclude);
if( zInclude==0 && zExclude==0 ){
etag_check_for_invariant_name(z);
}
if( eType==ARCHIVE_ZIP
&& nName>4
&& fossil_strcmp(&zName[nName-4], ".zip")==0
){
/* Special case: Remove the ".zip" suffix. */
nName -= 4;
zName[nName] = 0;
}else if( eType==ARCHIVE_SQLAR
&& nName>6
&& fossil_strcmp(&zName[nName-6], ".sqlar")==0
){
/* Special case: Remove the ".sqlar" suffix. */
nName -= 6;
zName[nName] = 0;
}else{
|
| ︙ | ︙ |
Changes to test/amend.test.
| ︙ | ︙ | |||
294 295 296 297 298 299 300 |
{000000 lower Upper alpha 0alpha} {000000 0alpha Upper alpha lower}
}
set tc 0
foreach {tagt result} $tagtests {
incr tc
set tags {}
set cancels {}
| | < | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
{000000 lower Upper alpha 0alpha} {000000 0alpha Upper alpha lower}
}
set tc 0
foreach {tagt result} $tagtests {
incr tc
set tags {}
set cancels {}
set t1exp [join $result ", "]
set t2exp "*"
set t3exp "*"
set t5exp "*"
foreach tag $tagt {
lappend tags -tag $tag
lappend cancels -cancel $tag
}
foreach res $result {
append t3exp "Add*tag*\"$res\".*"
append t5exp "Cancel*tag*\"$res\".*"
}
foreach res [lsort -nocase $result] {
append t2exp "sym-$res*"
}
eval fossil amend $HASH $tags
|
| ︙ | ︙ |
Changes to test/merge5.test.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
if {! $::QUIET} {
puts "Skipping Merge5 tests"
}
protOut {
fossil sqlite3 --no-repository reacts badly to SQL dumped from
repositories created from fossil older than version 2.0.
}
| | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
if {! $::QUIET} {
puts "Skipping Merge5 tests"
}
protOut {
fossil sqlite3 --no-repository reacts badly to SQL dumped from
repositories created from fossil older than version 2.0.
}
#test merge5-sqlite3-issue false knownBug
test_cleanup_then_return
# Verify the results of a check-out
#
proc checkout-test {testid expected_content} {
set flist {}
foreach {status filename} [exec $::fossilexe ls -l] {
|
| ︙ | ︙ |
Changes to test/release-checklist.wiki.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 | <li><p> Shift-click on each of the links in [./fileage-test-1.wiki] and verify correct operation of the file-age computation. <li><p> Verify correct name-change tracking behavior (no net changes) for: | < | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | <li><p> Shift-click on each of the links in [./fileage-test-1.wiki] and verify correct operation of the file-age computation. <li><p> Verify correct name-change tracking behavior (no net changes) for: <pre><b>fossil test-name-changes --debug b120bc8b262ac 374920b20944b </b></pre> <li><p> Compile for all of the following platforms: <ol type="a"> <li> Linux x86 <li> Linux x86_64 <li> Mac x86 |
| ︙ | ︙ |
Changes to test/stash.test.
| ︙ | ︙ | |||
166 167 168 169 170 171 172 | @@ -0,0 +1,1 @@ +f0} ######## # fossil stash show|cat ?STASHID? ?DIFF-OPTIONS? # fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS? | | | | | | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
@@ -0,0 +1,1 @@
+f0}
########
# fossil stash show|cat ?STASHID? ?DIFF-OPTIONS?
# fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS?
#fossil stash show
#test stash-1-show {[normalize_result] eq $diff_stash_1}
#fossil stash diff
#test stash-1-diff {[normalize_result] eq $diff_stash_1} knownBug
########
# fossil stash pop
stash-test 2 pop {
DELETE f1
UPDATE f2
|
| ︙ | ︙ | |||
204 205 206 207 208 209 210 | # 64 bit intel, 8-byte pointer, 4-byte integer # Stashed renamed file said: # fossil: ./src/delta.c:231: checksum: Assertion '...' failed. # Should be triggered by this stash-WY-1 test. fossil checkout --force c1 fossil clean fossil mv --soft f1 f1new | | | | | | | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# 64 bit intel, 8-byte pointer, 4-byte integer
# Stashed renamed file said:
# fossil: ./src/delta.c:231: checksum: Assertion '...' failed.
# Should be triggered by this stash-WY-1 test.
fossil checkout --force c1
fossil clean
fossil mv --soft f1 f1new
#stash-test WY-1 {-expectError save -m "Reported 2016-02-09"} {
# REVERT f1
# DELETE f1new
#} -changes {
#} -addremove {
#} -exists {f1 f2 f3} -notexists {f1new} -knownbugs {-code -result}
# TODO: add tests that verify the saved stash is sensible. Possibly
# by applying it and checking results. But until the SQLITE_CONSTRAINT
# error is fixed, there is nothing stashed to test.
# Test stashing the combination of a renamed file and an added file that
|
| ︙ | ︙ | |||
294 295 296 297 298 299 300 |
RENAME f2 f2n
MOVED_FILE} [file normalize f2] {
}] -changes {
RENAMED f2 -> f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}
| > | | | | | | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
RENAME f2 f2n
MOVED_FILE} [file normalize f2] {
}] -changes {
RENAMED f2 -> f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}
fossil stash save -m f2n
#stash-test 3-2 {save -m f2n} {
# REVERT f2
# DELETE f2n
#} -exists {f1 f2} -notexists {f2n} -knownbugs {-result}
fossil stash show
#test stash-3-2-show-1 {![regexp {\sf1} $RESULT]} knownBug
test stash-3-2-show-2 {[regexp {\sf2n} $RESULT]}
stash-test 3-2-pop {pop} {
UPDATE f1
UPDATE f2n
} -changes {
RENAMED f2 -> f2n
} -addremove {
|
| ︙ | ︙ |
Changes to test/tester.tcl.
| ︙ | ︙ | |||
306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
clean-glob \
clearsign \
comment-format \
crlf-glob \
crnl-glob \
default-csp \
default-perms \
diff-binary \
diff-command \
dont-commit \
dont-push \
dotfiles \
editor \
email-admin \
| > | 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
clean-glob \
clearsign \
comment-format \
crlf-glob \
crnl-glob \
default-csp \
default-perms \
default-skin \
diff-binary \
diff-command \
dont-commit \
dont-push \
dotfiles \
editor \
email-admin \
|
| ︙ | ︙ |
Changes to test/utf.test.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
proc utf-check {testname args} {
global tempPath
set i 1
foreach {fileName result} $args {
set fileName [file join $tempPath $fileName]
fossil test-looks-like-utf $fileName
set result [string map [list %TEMP% $tempPath \r\n \n] $result]
| | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
proc utf-check {testname args} {
global tempPath
set i 1
foreach {fileName result} $args {
set fileName [file join $tempPath $fileName]
fossil test-looks-like-utf $fileName
set result [string map [list %TEMP% $tempPath \r\n \n] $result]
# if {$::RESULT ne $result} {puts stdout $::RESULT; exit}
test utf-check-$testname.$i {$::RESULT eq $result}
incr i
}
}
unset -nocomplain enc
array set enc [list \
|
| ︙ | ︙ | |||
17607 17608 17609 17610 17611 17612 17613 | Has flag LOOK_LONE_LF: no Has flag LOOK_CRLF: no Has flag LOOK_LONG: no Has flag LOOK_INVALID: no Has flag LOOK_ODD: no Has flag LOOK_SHORT: no} | | | | | | | | | | | | | | | | | 17607 17608 17609 17610 17611 17612 17613 17614 17615 17616 17617 17618 17619 17620 17621 17622 17623 17624 17625 17626 17627 17628 17629 17630 17631 17632 17633 17634 17635 |
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: no
Has flag LOOK_INVALID: no
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}
#utf-check 1179 utf-check-1179-2-129-1.jnk \
#{File "%TEMP%/utf-check-1179-2-129-1.jnk" has 7 bytes.
#Starts with UTF-8 BOM: no
#Starts with UTF-16 BOM: yes
#Looks like UTF-8: yes
#Has flag LOOK_NUL: no
#Has flag LOOK_CR: no
#Has flag LOOK_LONE_CR: no
#Has flag LOOK_LF: no
#Has flag LOOK_LONE_LF: no
#Has flag LOOK_CRLF: no
#Has flag LOOK_LONG: no
#Has flag LOOK_INVALID: yes
#Has flag LOOK_ODD: no
#Has flag LOOK_SHORT: no}
utf-check 1180 utf-check-1180-2-130-0.jnk \
{File "%TEMP%/utf-check-1180-2-130-0.jnk" has 4 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: yes
Looks like UTF-16: yes
Has flag LOOK_NUL: no
|
| ︙ | ︙ | |||
24119 24120 24121 24122 24123 24124 24125 | Has flag LOOK_LONE_LF: no Has flag LOOK_CRLF: no Has flag LOOK_LONG: no Has flag LOOK_INVALID: yes Has flag LOOK_ODD: no Has flag LOOK_SHORT: no} | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 24119 24120 24121 24122 24123 24124 24125 24126 24127 24128 24129 24130 24131 24132 24133 24134 24135 24136 24137 24138 24139 24140 24141 24142 24143 24144 24145 24146 24147 24148 24149 24150 24151 24152 24153 24154 24155 24156 24157 24158 24159 24160 24161 24162 24163 |
Has flag LOOK_LONE_LF: no
Has flag LOOK_CRLF: no
Has flag LOOK_LONG: no
Has flag LOOK_INVALID: yes
Has flag LOOK_ODD: no
Has flag LOOK_SHORT: no}
#utf-check 1586 utf-check-1586-3-128-0.jnk \
#{File "%TEMP%/utf-check-1586-3-128-0.jnk" has 6 bytes.
#Starts with UTF-8 BOM: no
#Starts with UTF-16 BOM: reversed
#Looks like UTF-16: no
#Has flag LOOK_NUL: yes
#Has flag LOOK_CR: no
#Has flag LOOK_LONE_CR: no
#Has flag LOOK_LF: no
#Has flag LOOK_LONE_LF: no
#Has flag LOOK_CRLF: no
#Has flag LOOK_LONG: no
#Has flag LOOK_INVALID: no
#Has flag LOOK_ODD: no
#Has flag LOOK_SHORT: no}
#utf-check 1587 utf-check-1587-3-128-1.jnk \
#{File "%TEMP%/utf-check-1587-3-128-1.jnk" has 7 bytes.
#Starts with UTF-8 BOM: no
#Starts with UTF-16 BOM: reversed
#Looks like UTF-8: no
#Has flag LOOK_NUL: yes
#Has flag LOOK_CR: no
#Has flag LOOK_LONE_CR: no
#Has flag LOOK_LF: no
#Has flag LOOK_LONE_LF: no
#Has flag LOOK_CRLF: no
#Has flag LOOK_LONG: no
#Has flag LOOK_INVALID: yes
#Has flag LOOK_ODD: no
#Has flag LOOK_SHORT: no}
utf-check 1588 utf-check-1588-3-129-0.jnk \
{File "%TEMP%/utf-check-1588-3-129-0.jnk" has 6 bytes.
Starts with UTF-8 BOM: no
Starts with UTF-16 BOM: no
Looks like UTF-8: no
Has flag LOOK_NUL: yes
|
| ︙ | ︙ |
Changes to tools/codecheck1.c.
| ︙ | ︙ | |||
602 603 604 605 606 607 608 |
if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){
const char *zExpr = azArg[fmtArg+i];
if( never_safe(zExpr) ){
printf("%s:%d: Argument %d to %.*s() is not safe for"
" a query parameter\n",
zFilename, lnFCall, i+fmtArg, szFName, zFCall);
nErr++;
| | | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){
const char *zExpr = azArg[fmtArg+i];
if( never_safe(zExpr) ){
printf("%s:%d: Argument %d to %.*s() is not safe for"
" a query parameter\n",
zFilename, lnFCall, i+fmtArg, szFName, zFCall);
nErr++;
}else if( (fmtFlags & FMT_SQL)!=0 && !is_sql_safe(zExpr) ){
printf("%s:%d: Argument %d to %.*s() not safe for SQL\n",
zFilename, lnFCall, i+fmtArg, szFName, zFCall);
nErr++;
}
}
}
|
| ︙ | ︙ |
Changes to tools/makeheaders.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 | #include <stdlib.h> #include <ctype.h> #include <memory.h> #include <sys/stat.h> #include <assert.h> #include <string.h> | | > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#include <stdlib.h>
#include <ctype.h>
#include <memory.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>
#if defined( __MINGW32__) || defined(__DMC__) || \
defined(_MSC_VER) || defined(__POCC__)
# ifndef WIN32
# define WIN32
# endif
#else
# include <unistd.h>
#endif
|
| ︙ | ︙ | |||
2224 2225 2226 2227 2228 2229 2230 |
if (pToken->zText[pToken->nText-1] == '\r') { nArg--; }
if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
}else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Export);
}else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Local);
| > | | 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 |
if (pToken->zText[pToken->nText-1] == '\r') { nArg--; }
if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
}else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Export);
}else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Local);
}else if( nArg==15 &&
strncmp(zArg,"MAKEHEADERS_STOPLOCAL_INTERFACE",15)==0 ){
PushIfMacro(0,0,0,pToken->nLine,PS_Local);
}else{
PushIfMacro(0,zArg,nArg,pToken->nLine,0);
}
}else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){
/*
** Push an #ifdef.
|
| ︙ | ︙ | |||
3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 |
}
}
/* Done!
*/
return pFile;
}
/* MS-Windows and MS-DOS both have the following serious OS bug: the
** length of a command line is severely restricted. But this program
** occasionally requires long command lines. Hence the following
** work around.
**
** If the parameters "-f FILENAME" appear anywhere on the command line,
| > > > > > | 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 |
}
}
/* Done!
*/
return pFile;
}
/* Local strcpy() clone to squelch an unwarranted warning from OpenBSD. */
static void local_strcpy(char *dest, const char *src){
while( (*(dest++) = *(src++))!=0 ){}
}
/* MS-Windows and MS-DOS both have the following serious OS bug: the
** length of a command line is severely restricted. But this program
** occasionally requires long command lines. Hence the following
** work around.
**
** If the parameters "-f FILENAME" appear anywhere on the command line,
|
| ︙ | ︙ | |||
3239 3240 3241 3242 3243 3244 3245 |
zNew = realloc( zNew, sizeof(char*) * nAlloc );
}
}
if( zNew ){
int j = nNew + index;
zNew[j] = malloc( n + 1 );
if( zNew[j] ){
| | | 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 |
zNew = realloc( zNew, sizeof(char*) * nAlloc );
}
}
if( zNew ){
int j = nNew + index;
zNew[j] = malloc( n + 1 );
if( zNew[j] ){
local_strcpy( zNew[j], zBuf );
}
}
}
}
fclose(in);
newArgc = argc + nNew - 1;
for(i=0; i<=index; i++){
|
| ︙ | ︙ |
Changes to tools/mkindex.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 | ** legacy commands. Test commands are unsupported commands used for testing ** and analysis only. ** ** Commands are 1st-tier by default. If the command name begins with ** "test-" or if the command name has a "test" argument, then it becomes ** a test command. If the command name has a "2nd-tier" argument or ends ** with a "*" character, it is second tier. If the command name has an "alias" | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | ** legacy commands. Test commands are unsupported commands used for testing ** and analysis only. ** ** Commands are 1st-tier by default. If the command name begins with ** "test-" or if the command name has a "test" argument, then it becomes ** a test command. If the command name has a "2nd-tier" argument or ends ** with a "*" character, it is second tier. If the command name has an "alias" ** argument or ends with a "#" character, it is an alias: another name ** (a one-to-one replacement) for a command. Examples: ** ** COMMAND: abcde* ** COMMAND: fghij 2nd-tier ** COMMAND: mnopq# ** COMMAND: rstuv alias ** COMMAND: test-xyzzy |
| ︙ | ︙ | |||
325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
if( strncmp(zLine, "** DEFAULT: ", 12)!=0 ) return;
z = zLine + 12;
while( fossil_isspace(z[0]) ) z++;
len = (int)strlen(z);
while( len>0 && fossil_isspace(z[len-1]) ){ len--; }
aEntry[nUsed-1].zDflt = string_dup(z,len);
}
/*
** Scan a line for a function that implements a web page or command.
*/
void scan_for_func(char *zLine){
int i,j,k;
char *z;
| > > > > > | 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
if( strncmp(zLine, "** DEFAULT: ", 12)!=0 ) return;
z = zLine + 12;
while( fossil_isspace(z[0]) ) z++;
len = (int)strlen(z);
while( len>0 && fossil_isspace(z[len-1]) ){ len--; }
aEntry[nUsed-1].zDflt = string_dup(z,len);
}
/* Local strcpy() clone to squelch an unwarranted warning from OpenBSD. */
static void local_strcpy(char *dest, const char *src){
while( (*(dest++) = *(src++))!=0 ){}
}
/*
** Scan a line for a function that implements a web page or command.
*/
void scan_for_func(char *zLine){
int i,j,k;
char *z;
|
| ︙ | ︙ | |||
347 348 349 350 351 352 353 |
&& strncmp(zLine,"** SETTING:",11)!=0
&& strncmp(zLine,"** DEFAULT:",11)!=0
){
if( zLine[2]=='\n' ){
zHelp[nHelp++] = '\n';
}else{
if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0;
| | | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
&& strncmp(zLine,"** SETTING:",11)!=0
&& strncmp(zLine,"** DEFAULT:",11)!=0
){
if( zLine[2]=='\n' ){
zHelp[nHelp++] = '\n';
}else{
if( strncmp(&zLine[3], "Usage: ", 6)==0 ) nHelp = 0;
local_strcpy(&zHelp[nHelp], &zLine[3]);
nHelp += strlen(&zHelp[nHelp]);
}
return;
}
for(i=0; fossil_isspace(zLine[i]); i++){}
if( zLine[i]==0 ) return;
isSetting = (aEntry[nFixed].eType & CMDFLAG_SETTING)!=0;
|
| ︙ | ︙ |
Changes to tools/mkversion.c.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
s[j] = t;
t += s[i];
zOut[n] = "0123456789abcdef"[(t>>4)&0xf];
zOut[n+1] = "0123456789abcdef"[t&0xf];
}
zOut[n] = 0;
}
int main(int argc, char *argv[]){
FILE *m,*u,*v;
char *z;
#if defined(__DMC__) /* e.g. 0x857 */
int i = 0;
#endif
| > > > > > | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
s[j] = t;
t += s[i];
zOut[n] = "0123456789abcdef"[(t>>4)&0xf];
zOut[n+1] = "0123456789abcdef"[t&0xf];
}
zOut[n] = 0;
}
/* Local strcpy() clone to squelch an unwarranted warning from OpenBSD. */
static void local_strcpy(char *dest, const char *src){
while( (*(dest++) = *(src++))!=0 ){}
}
int main(int argc, char *argv[]){
FILE *m,*u,*v;
char *z;
#if defined(__DMC__) /* e.g. 0x857 */
int i = 0;
#endif
|
| ︙ | ︙ | |||
171 172 173 174 175 176 177 |
if( z[0]==0 ) break;
}
z++;
}
for(z=vx; z[0]=='0'; z++){}
printf("#define RELEASE_VERSION_NUMBER %d%02d%02d\n", vn[0], vn[1], vn[2]);
memset(vx,0,sizeof(vx));
| | | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
if( z[0]==0 ) break;
}
z++;
}
for(z=vx; z[0]=='0'; z++){}
printf("#define RELEASE_VERSION_NUMBER %d%02d%02d\n", vn[0], vn[1], vn[2]);
memset(vx,0,sizeof(vx));
local_strcpy(vx,b);
for(z=vx; z[0]; z++){
if( z[0]=='-' ){
z[0] = 0;
break;
}
if( z[0]!='.' ) continue;
if ( d<3 ){
|
| ︙ | ︙ |
Changes to tools/skintxt2config.c.
|
| | | 1 2 3 4 5 6 7 8 | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* ** Copyright (c) 2021 Stephan Beal (https://wanderinghorse.net/home/stephan/) ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) |
| ︙ | ︙ | |||
100 101 102 103 104 105 106 |
end:
fclose(f);
if(rc){
free(zMem);
}else{
*zContent = zMem;
*nContent = fpos;
| | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
end:
fclose(f);
if(rc){
free(zMem);
}else{
*zContent = zMem;
*nContent = fpos;
}
return rc;
}
/*
** Expects zFilename to be one of the conventional skin filename
** parts. This routine converts it to config format and emits it to
** App.ostr.
|
| ︙ | ︙ |
Changes to tools/sqlcompattest.c.
| ︙ | ︙ | |||
51 52 53 54 55 56 57 | #error "Must set -DMINIMUM_SQLITE_VERSION=nn.nn.nn in auto.def" #endif #define QUOTE(VAL) #VAL #define STR(MACRO_VAL) QUOTE(MACRO_VAL) char zMinimumVersionNumber[8]="nn.nn.nn"; | | > | > | 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 |
#error "Must set -DMINIMUM_SQLITE_VERSION=nn.nn.nn in auto.def"
#endif
#define QUOTE(VAL) #VAL
#define STR(MACRO_VAL) QUOTE(MACRO_VAL)
char zMinimumVersionNumber[8]="nn.nn.nn";
strncpy((char *)&zMinimumVersionNumber,STR(MINIMUM_SQLITE_VERSION),
sizeof(zMinimumVersionNumber));
long major, minor, release, version;
sscanf(zMinimumVersionNumber, "%li.%li.%li", &major, &minor, &release);
version=(major*1000000)+(minor*1000)+release;
int i;
static const char *zRequiredOpts[] = {
"ENABLE_FTS4", /* Required for repository search */
"ENABLE_DBSTAT_VTAB", /* Required by /repo-tabsize page */
};
/* Check minimum SQLite version number */
if( sqlite3_libversion_number()<version ){
printf("found system SQLite version %s but need %s or later, "
"consider removing --disable-internal-sqlite\n",
sqlite3_libversion(),STR(MINIMUM_SQLITE_VERSION));
return 1;
}
for(i=0; i<sizeof(zRequiredOpts)/sizeof(zRequiredOpts[0]); i++){
if( !sqlite3_compileoption_used(zRequiredOpts[i]) ){
printf("system SQLite library omits required build option -DSQLITE_%s\n",
|
| ︙ | ︙ |
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
575 576 577 578 579 580 581 582 583 584 585 586 587 588 | $(SRCDIR)/../skins/default/details.txt \ $(SRCDIR)/../skins/default/footer.txt \ $(SRCDIR)/../skins/default/header.txt \ $(SRCDIR)/../skins/eagle/css.txt \ $(SRCDIR)/../skins/eagle/details.txt \ $(SRCDIR)/../skins/eagle/footer.txt \ $(SRCDIR)/../skins/eagle/header.txt \ $(SRCDIR)/../skins/khaki/css.txt \ $(SRCDIR)/../skins/khaki/details.txt \ $(SRCDIR)/../skins/khaki/footer.txt \ $(SRCDIR)/../skins/khaki/header.txt \ $(SRCDIR)/../skins/original/css.txt \ $(SRCDIR)/../skins/original/details.txt \ $(SRCDIR)/../skins/original/footer.txt \ | > > > > | 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | $(SRCDIR)/../skins/default/details.txt \ $(SRCDIR)/../skins/default/footer.txt \ $(SRCDIR)/../skins/default/header.txt \ $(SRCDIR)/../skins/eagle/css.txt \ $(SRCDIR)/../skins/eagle/details.txt \ $(SRCDIR)/../skins/eagle/footer.txt \ $(SRCDIR)/../skins/eagle/header.txt \ $(SRCDIR)/../skins/etienne/css.txt \ $(SRCDIR)/../skins/etienne/details.txt \ $(SRCDIR)/../skins/etienne/footer.txt \ $(SRCDIR)/../skins/etienne/header.txt \ $(SRCDIR)/../skins/khaki/css.txt \ $(SRCDIR)/../skins/khaki/details.txt \ $(SRCDIR)/../skins/khaki/footer.txt \ $(SRCDIR)/../skins/khaki/header.txt \ $(SRCDIR)/../skins/original/css.txt \ $(SRCDIR)/../skins/original/details.txt \ $(SRCDIR)/../skins/original/footer.txt \ |
| ︙ | ︙ | |||
651 652 653 654 655 656 657 658 659 660 661 662 663 664 | $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.chat.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.pikchrshow.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ | > | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 | $(SRCDIR)/sounds/d.wav \ $(SRCDIR)/sounds/e.wav \ $(SRCDIR)/sounds/f.wav \ $(SRCDIR)/style.admin_log.css \ $(SRCDIR)/style.chat.css \ $(SRCDIR)/style.fileedit.css \ $(SRCDIR)/style.pikchrshow.css \ $(SRCDIR)/style.uvlist.css \ $(SRCDIR)/style.wikiedit.css \ $(SRCDIR)/tree.js \ $(SRCDIR)/useredit.js \ $(SRCDIR)/wiki.wiki TRANS_SRC = \ $(OBJDIR)/add_.c \ |
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
533 534 535 536 537 538 539 540 541 542 543 544 545 546 |
"$(SRCDIR)\..\skins\default\details.txt" \
"$(SRCDIR)\..\skins\default\footer.txt" \
"$(SRCDIR)\..\skins\default\header.txt" \
"$(SRCDIR)\..\skins\eagle\css.txt" \
"$(SRCDIR)\..\skins\eagle\details.txt" \
"$(SRCDIR)\..\skins\eagle\footer.txt" \
"$(SRCDIR)\..\skins\eagle\header.txt" \
"$(SRCDIR)\..\skins\khaki\css.txt" \
"$(SRCDIR)\..\skins\khaki\details.txt" \
"$(SRCDIR)\..\skins\khaki\footer.txt" \
"$(SRCDIR)\..\skins\khaki\header.txt" \
"$(SRCDIR)\..\skins\original\css.txt" \
"$(SRCDIR)\..\skins\original\details.txt" \
"$(SRCDIR)\..\skins\original\footer.txt" \
| > > > > | 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 |
"$(SRCDIR)\..\skins\default\details.txt" \
"$(SRCDIR)\..\skins\default\footer.txt" \
"$(SRCDIR)\..\skins\default\header.txt" \
"$(SRCDIR)\..\skins\eagle\css.txt" \
"$(SRCDIR)\..\skins\eagle\details.txt" \
"$(SRCDIR)\..\skins\eagle\footer.txt" \
"$(SRCDIR)\..\skins\eagle\header.txt" \
"$(SRCDIR)\..\skins\etienne\css.txt" \
"$(SRCDIR)\..\skins\etienne\details.txt" \
"$(SRCDIR)\..\skins\etienne\footer.txt" \
"$(SRCDIR)\..\skins\etienne\header.txt" \
"$(SRCDIR)\..\skins\khaki\css.txt" \
"$(SRCDIR)\..\skins\khaki\details.txt" \
"$(SRCDIR)\..\skins\khaki\footer.txt" \
"$(SRCDIR)\..\skins\khaki\header.txt" \
"$(SRCDIR)\..\skins\original\css.txt" \
"$(SRCDIR)\..\skins\original\details.txt" \
"$(SRCDIR)\..\skins\original\footer.txt" \
|
| ︙ | ︙ | |||
609 610 611 612 613 614 615 616 617 618 619 620 621 622 |
"$(SRCDIR)\sounds\d.wav" \
"$(SRCDIR)\sounds\e.wav" \
"$(SRCDIR)\sounds\f.wav" \
"$(SRCDIR)\style.admin_log.css" \
"$(SRCDIR)\style.chat.css" \
"$(SRCDIR)\style.fileedit.css" \
"$(SRCDIR)\style.pikchrshow.css" \
"$(SRCDIR)\style.wikiedit.css" \
"$(SRCDIR)\tree.js" \
"$(SRCDIR)\useredit.js" \
"$(SRCDIR)\wiki.wiki"
OBJ = "$(OX)\add$O" \
"$(OX)\ajax$O" \
| > | 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 |
"$(SRCDIR)\sounds\d.wav" \
"$(SRCDIR)\sounds\e.wav" \
"$(SRCDIR)\sounds\f.wav" \
"$(SRCDIR)\style.admin_log.css" \
"$(SRCDIR)\style.chat.css" \
"$(SRCDIR)\style.fileedit.css" \
"$(SRCDIR)\style.pikchrshow.css" \
"$(SRCDIR)\style.uvlist.css" \
"$(SRCDIR)\style.wikiedit.css" \
"$(SRCDIR)\tree.js" \
"$(SRCDIR)\useredit.js" \
"$(SRCDIR)\wiki.wiki"
OBJ = "$(OX)\add$O" \
"$(OX)\ajax$O" \
|
| ︙ | ︙ | |||
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 | echo "$(SRCDIR)\../skins/default/details.txt" >> $@ echo "$(SRCDIR)\../skins/default/footer.txt" >> $@ echo "$(SRCDIR)\../skins/default/header.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/css.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/details.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/footer.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/header.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/css.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/details.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/footer.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/header.txt" >> $@ echo "$(SRCDIR)\../skins/original/css.txt" >> $@ echo "$(SRCDIR)\../skins/original/details.txt" >> $@ echo "$(SRCDIR)\../skins/original/footer.txt" >> $@ | > > > > | 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 | echo "$(SRCDIR)\../skins/default/details.txt" >> $@ echo "$(SRCDIR)\../skins/default/footer.txt" >> $@ echo "$(SRCDIR)\../skins/default/header.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/css.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/details.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/footer.txt" >> $@ echo "$(SRCDIR)\../skins/eagle/header.txt" >> $@ echo "$(SRCDIR)\../skins/etienne/css.txt" >> $@ echo "$(SRCDIR)\../skins/etienne/details.txt" >> $@ echo "$(SRCDIR)\../skins/etienne/footer.txt" >> $@ echo "$(SRCDIR)\../skins/etienne/header.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/css.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/details.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/footer.txt" >> $@ echo "$(SRCDIR)\../skins/khaki/header.txt" >> $@ echo "$(SRCDIR)\../skins/original/css.txt" >> $@ echo "$(SRCDIR)\../skins/original/details.txt" >> $@ echo "$(SRCDIR)\../skins/original/footer.txt" >> $@ |
| ︙ | ︙ | |||
1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 | echo "$(SRCDIR)\sounds/d.wav" >> $@ echo "$(SRCDIR)\sounds/e.wav" >> $@ echo "$(SRCDIR)\sounds/f.wav" >> $@ echo "$(SRCDIR)\style.admin_log.css" >> $@ echo "$(SRCDIR)\style.chat.css" >> $@ echo "$(SRCDIR)\style.fileedit.css" >> $@ echo "$(SRCDIR)\style.pikchrshow.css" >> $@ echo "$(SRCDIR)\style.wikiedit.css" >> $@ echo "$(SRCDIR)\tree.js" >> $@ echo "$(SRCDIR)\useredit.js" >> $@ echo "$(SRCDIR)\wiki.wiki" >> $@ "$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\add_.c" | > | 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 | echo "$(SRCDIR)\sounds/d.wav" >> $@ echo "$(SRCDIR)\sounds/e.wav" >> $@ echo "$(SRCDIR)\sounds/f.wav" >> $@ echo "$(SRCDIR)\style.admin_log.css" >> $@ echo "$(SRCDIR)\style.chat.css" >> $@ echo "$(SRCDIR)\style.fileedit.css" >> $@ echo "$(SRCDIR)\style.pikchrshow.css" >> $@ echo "$(SRCDIR)\style.uvlist.css" >> $@ echo "$(SRCDIR)\style.wikiedit.css" >> $@ echo "$(SRCDIR)\tree.js" >> $@ echo "$(SRCDIR)\useredit.js" >> $@ echo "$(SRCDIR)\wiki.wiki" >> $@ "$(OX)\add$O" : "$(OX)\add_.c" "$(OX)\add.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\add_.c" |
| ︙ | ︙ |
Changes to win/buildmsvc.bat.
1 2 3 4 5 | @ECHO OFF :: :: buildmsvc.bat -- :: | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @ECHO OFF :: :: buildmsvc.bat -- :: :: This batch file attempts to build Fossil using the latest version of :: Microsoft Visual Studio installed on this machine. :: :: For VS 2017 and later, it uses the x64 build tools by default; :: pass "x86" as the first argument to use the x86 tools. :: SETLOCAL REM SET __ECHO=ECHO REM SET __ECHO2=ECHO IF NOT DEFINED _AECHO (SET _AECHO=REM) |
| ︙ | ︙ | |||
48 49 50 51 52 53 54 | ) REM REM Visual Studio 2017 / 2019 / 2022 REM CALL :fn_TryUseVsWhereExe IF NOT DEFINED VSWHEREINSTALLDIR GOTO skip_detectVisualStudio2017 | | > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | ) REM REM Visual Studio 2017 / 2019 / 2022 REM CALL :fn_TryUseVsWhereExe IF NOT DEFINED VSWHEREINSTALLDIR GOTO skip_detectVisualStudio2017 SET VSVARS32=%VSWHEREINSTALLDIR%\VC\Auxiliary\Build\vcvars64.bat IF "%~1" == "x86" ( SET VSVARS32=%VSWHEREINSTALLDIR%\VC\Auxiliary\Build\vcvars32.bat ) IF EXIST "%VSVARS32%" ( %_AECHO% Using Visual Studio 2017 / 2019 / 2022... GOTO skip_detectVisualStudio ) :skip_detectVisualStudio2017 REM |
| ︙ | ︙ |
Changes to www/aboutcgi.wiki.
1 | <title>How CGI Works In Fossil</title> | > | > | | > | > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 |
<title>How CGI Works In Fossil</title>
<h2>Introduction</h2>
CGI or "Common Gateway Interface" is a venerable yet reliable technique for
generating dynamic web content. This article gives a quick background on how
CGI works and describes how Fossil can act as a CGI service.
This is a "how it works" guide. This document provides background
information on the CGI protocol so that you can better understand what
is going on behind the scenes. If you just want to set up Fossil
as a CGI server, see the [./server/ | Fossil Server Setup] page. Or
if you want to development CGI-based extensions to Fossil, see
the [./serverext.wiki|CGI Server Extensions] page.
<h2>A Quick Review Of CGI</h2>
An HTTP request is a block of text that is sent by a client application
(usually a web browser) and arrives at the web server over a network
connection. The HTTP request contains a URL that describes the information
being requested. The URL in the HTTP request is typically the same URL
that appears in the URL bar at the top of the web browser that is making
the request. The URL might contain a "?" character followed
query parameters. The HTTP will usually also contain other information
such as the name of the application that made the request, whether or
not the requesting application can accept a compressed reply, POST
parameters from forms, and so forth.
The job of the web server is to interpret the HTTP request and formulate
an appropriate reply.
The web server is free to interpret the HTTP request in any way it wants.
But most web servers follow a similar pattern, described below.
(Note: details may vary from one web server to another.)
Suppose the filename component of the URL in the HTTP request looks like this:
<pre>/one/two/timeline/four</pre>
Most web servers will search their content area for files that match
some prefix of the URL. The search starts with <b>/one</b>, then goes to
<b>/one/two</b>, then <b>/one/two/timeline</b>, and finally
<b>/one/two/timeline/four</b> is checked. The search stops at the first
match.
Suppose the first match is <b>/one/two</b>. If <b>/one/two</b> is an
ordinary file in the content area, then that file is returned as static
content. The "<b>/timeline/four</b>" suffix is silently ignored.
If <b>/one/two</b> is a CGI script (or program), then the web server
executes the <b>/one/two</b> script. The output generated by
the script is collected and repackaged as the HTTP reply.
Before executing the CGI script, the web server will set up various
environment variables with information useful to the CGI script:
<table>
<tr><th>Variable<th>Meaning
<tr><td>GATEWAY_INTERFACE<td>Always set to "CGI/1.0"
<tr><td>REQUEST_URI
<td>The input URL from the HTTP request.
<tr><td>SCRIPT_NAME
<td>The prefix of the input URL that matches the CGI script name.
In this example: "/one/two".
<tr><td>PATH_INFO
|
| ︙ | ︙ | |||
83 84 85 86 87 88 89 | The CGI script exits as soon as it generates a single reply. The web server will (usually) persist and handle multiple HTTP requests, but a CGI script handles just one HTTP request and then exits. The above is a rough outline of how CGI works. There are many details omitted from this brief discussion. See other on-line CGI tutorials for further information. | | | > | | > | 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 | The CGI script exits as soon as it generates a single reply. The web server will (usually) persist and handle multiple HTTP requests, but a CGI script handles just one HTTP request and then exits. The above is a rough outline of how CGI works. There are many details omitted from this brief discussion. See other on-line CGI tutorials for further information. <h2>How Fossil Acts As A CGI Program</h2> An appropriate CGI script for running Fossil will look something like the following: <pre> #!/usr/bin/fossil repository: /home/www/repos/project.fossil </pre> The first line of the script is a "[https://en.wikipedia.org/wiki/Shebang_%28Unix%29|shebang]" that tells the operating system what program to use as the interpreter for this script. On unix, when you execute a script that starts with a shebang, the operating system runs the program identified by the shebang with a single argument that is the full pathname of the script itself. |
| ︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 142 143 144 145 | With Fossil, terms of PATH_INFO beyond the webpage name are converted into the "name" query parameter. Hence, the following two URLs mean exactly the same thing to Fossil: <ol type='A'> <li> [https://fossil-scm.org/home/info/c14ecc43] <li> [https://fossil-scm.org/home/info?name=c14ecc43] </ol> In both cases, the CGI script is called "/fossil". For case (A), the PATH_INFO variable will be "info/c14ecc43" and so the "[/help?cmd=/info|/info]" webpage will be generated and the suffix of PATH_INFO will be converted into the "name" query parameter, which identifies the artifact about which information is requested. In case (B), the PATH_INFO is just "info", but the same "name" query parameter is set explicitly by the URL itself. | > | | > | | > | > | > | > | > > > | > > > > > | > | > > > > > | > | < > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > | | < < | | 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 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
With Fossil, terms of PATH_INFO beyond the webpage name are converted into
the "name" query parameter. Hence, the following two URLs mean
exactly the same thing to Fossil:
<ol type='A'>
<li> [https://fossil-scm.org/home/info/c14ecc43]
<li> [https://fossil-scm.org/home/info?name=c14ecc43]
</ol>
In both cases, the CGI script is called "/fossil". For case (A),
the PATH_INFO variable will be "info/c14ecc43" and so the
"[/help?cmd=/info|/info]" webpage will be generated and the suffix of
PATH_INFO will be converted into the "name" query parameter, which
identifies the artifact about which information is requested.
In case (B), the PATH_INFO is just "info", but the same "name"
query parameter is set explicitly by the URL itself.
<h2>Serving Multiple Fossil Repositories From One CGI Script</h2>
The previous example showed how to serve a single Fossil repository
using a single CGI script.
On a website that wants to serve multiple repositories, one could
simply create multiple CGI scripts, one script for each repository.
But it is also possible to serve multiple Fossil repositories from
a single CGI script.
If the CGI script for Fossil contains a "directory:" line instead of
a "repository:" line, then the argument to "directory:" is the name
of a directory that contains multiple repository files, each ending
with ".fossil". For example:
<pre>
#!/usr/bin/fossil
directory: /home/www/repos
</pre>
Suppose the /home/www/repos directory contains files named
<b>one.fossil</b>, <b>two.fossil</b>, and <b>subdir/three.fossil</b>.
Further suppose that the name of the CGI script (relative to the root
of the webserver document area) is "cgis/example2". Then to
see the timeline for the "three.fossil" repository, the URL would be:
<pre>
http://example.com/cgis/example2/subdir/three/timeline
</pre>
Here is what happens:
<ol>
<li> The input URI on the HTTP request is
<b>/cgis/example2/subdir/three/timeline</b>
<li> The web server searches prefixes of the input URI until it finds
the "cgis/example2" script. The web server then sets
PATH_INFO to the "subdir/three/timeline" suffix and invokes the
"cgis/example2" script.
<li> Fossil runs and sees the "directory:" line pointing to
"/home/www/repos". Fossil then starts pulling terms off the
front of the PATH_INFO looking for a repository. It first looks
at "/home/www/resps/subdir.fossil" but there is no such repository.
So then it looks at "/home/www/repos/subdir/three.fossil" and finds
a repository. The PATH_INFO is shortened by removing
"subdir/three/" leaving it at just "timeline".
<li> Fossil looks at the rest of PATH_INFO to see that the webpage
requested is "timeline".
</ol>
<a id="cgivar"></a>
The web server sets many environment variables in step 2 in addition
to just PATH_INFO. The following diagram shows a few of these variables
and their relationship to the request URL:
<verbatim type="pikchr">
charwid = 0.075
thickness = 0
SCHEME: box "https://" mono fit
DOMAIN: box "example.com" mono fit
SCRIPT: box "/cgis/example2" mono fit
PATH: box "/subdir/three/timeline" mono fit
QUERY: box "?c=55d7e1" mono fit
thickness = 0.01
DB: box at 0.3 below DOMAIN "HTTP_HOST" mono fit invis
SB: box at 0.3 below SCRIPT "SCRIPT_NAME" mono fit invis
PB: box at 0.3 below PATH "PATH_INFO" mono fit invis
QB: box at 0.3 below QUERY "QUERY_STRING" mono fit invis
RB: box at 0.5 above PATH "REQUEST_URI" mono fit invis
color = lightgray
box at SCHEME width SCHEME.width height SCHEME.height
line fill 0x7799CC behind QUERY \
from SCRIPT.nw \
to RB.sw \
to RB.se \
to QUERY.ne \
close
line fill 0x99CCFF behind DOMAIN \
from DOMAIN.nw \
to DOMAIN.sw \
to DB.n \
to DOMAIN.se \
to DOMAIN.ne \
close
line fill 0xCCEEFF behind SCRIPT \
from SCRIPT.nw \
to SCRIPT.sw \
to SB.n \
to SCRIPT.se \
to SCRIPT.ne \
close
line fill 0x99CCFF behind PATH \
from PATH.nw \
to PATH.sw \
to PB.n \
to PATH.se \
to PATH.ne \
close
line fill 0xCCEEFF behind QUERY \
from QUERY.nw \
to QUERY.sw \
to QB.n \
to QUERY.se \
to QUERY.ne \
close
</verbatim>
<h2>Additional CGI Script Options</h2>
The CGI script can have additional options used to fine-tune
Fossil's behavior. See the [./cgi.wiki|CGI script documentation]
for details.
<h2>Additional Observations</h2>
<ol type="I">
<li><p>
Fossil does not distinguish between the various HTTP methods (GET, PUT,
DELETE, etc). Fossil figures out what it needs to do purely from the
webpage term of the URI.</p></li>
<li><p>
Fossil does not distinguish between query parameters that are part of the
URI, application/x-www-form-urlencoded or multipart/form-data encoded
|
| ︙ | ︙ | |||
237 238 239 240 241 242 243 | converted into CGI, then Fossil creates a separate child Fossil process to handle each CGI request.</p></li> <li><p> Fossil is itself often launched using CGI. But Fossil can also then turn around and launch [./serverext.wiki|sub-CGI scripts to implement extensions].</p></li> </ol> | < | 295 296 297 298 299 300 301 | converted into CGI, then Fossil creates a separate child Fossil process to handle each CGI request.</p></li> <li><p> Fossil is itself often launched using CGI. But Fossil can also then turn around and launch [./serverext.wiki|sub-CGI scripts to implement extensions].</p></li> </ol> |
Changes to www/aboutdownload.wiki.
1 | <title>How The Fossil Download Page Works</title> | < | 1 2 3 4 5 6 7 8 | <title>How The Fossil Download Page Works</title> <h2>1.0 Overview</h2> The [/uv/download.html|Download] page for the Fossil self-hosting repository is implemented using [./unvers.wiki|unversioned files]. The "download.html" screen itself, and the various build products are all stored as unversioned content. The download.html page |
| ︙ | ︙ | |||
41 42 43 44 45 46 47 | Notice how the hyperlinks above use the "mimetype=text/plain" query parameter in order to display the file as plain text instead of the usual HTML or Javascript. The default mimetype for "download.html" is text/html. But because the entire page is enclosed within | | | | | 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 | Notice how the hyperlinks above use the "mimetype=text/plain" query parameter in order to display the file as plain text instead of the usual HTML or Javascript. The default mimetype for "download.html" is text/html. But because the entire page is enclosed within <verbatim><div class='fossil-doc' data-title='Download Page'>...</div></verbatim> Fossil knows to add its standard header and footer information to the document, making it look just like any other page. See "[./embeddeddoc.wiki|embedded documentation]" for further details on how this <div class='fossil-doc'> markup works. With each new release, the "releases" variable in the javascript on the [/uv/download.js?mimetype=text/plain|download.js] page is edited (using "[/help?cmd=uv|fossil uv edit download.js]") to add details of the release. When the JavaScript in the "download.js" file runs, it requests a listing of all unversioned content using the /juvlist URL. ([/juvlist|sample /juvlist output]). The content of the download page is constructed by matching unversioned files against regular expressions in the "releases" variable. Build products need to be constructed on different machines. The precompiled binary for Linux is compiled on Linux, the precompiled binary for Windows is compiled on Windows11, and so forth. After a new release is tagged, the release manager goes around to each of the target platforms, checks out the release and compiles it, then runs [/help?cmd=uv|fossil uv add] for the build product followed by [/help?cmd=uv|fossil uv sync] to push the new build product to the [./selfhost.wiki|various servers]. This process is repeated for each build product. |
| ︙ | ︙ |
Changes to www/adding_code.wiki.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 | source tree. Suppose one wants to add a new source code file named "xyzzy.c". The first step is to add this file to the various makefiles. Do so by editing the file tools/makemake.tcl and adding "xyzzy" (without the final ".c") to the list of source modules at the top of that script. Save the result and then run the makemake.tcl script using a TCL interpreter. The command to run the makemake.tcl script is: | > | > | | | 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 87 88 89 90 91 92 | source tree. Suppose one wants to add a new source code file named "xyzzy.c". The first step is to add this file to the various makefiles. Do so by editing the file tools/makemake.tcl and adding "xyzzy" (without the final ".c") to the list of source modules at the top of that script. Save the result and then run the makemake.tcl script using a TCL interpreter. The command to run the makemake.tcl script is: <verbatim> tclsh makemake.tcl </verbatim> The working directory must be src/ when the command above is run. Note that TCL is not normally required to build Fossil, but it is required for this step. If you do not have a TCL interpreter on your system already, they are easy to install. A popular choice is the [http://www.activestate.com/activetcl|Active Tcl] installation from ActiveState. After the makefiles have been updated, create the xyzzy.c source file from the following template: <verbatim> /* ** Copyright boilerplate goes here. ***************************************************** ** High-level description of what this module goes ** here. */ #include "config.h" #include "xyzzy.h" #if INTERFACE /* Exported object (structure) definitions or #defines ** go here */ #endif /* INTERFACE */ /* New code goes here */ </verbatim> Note in particular the <b>#include "xyzzy.h"</b> line near the top. The "xyzzy.h" file is automatically generated by makeheaders. Every normal Fossil source file must have a #include at the top that imports its private header file. (Some source files, such as "sqlite3.c" are exceptions to this rule. Don't worry about those exceptions. The files you write will require this #include line.) |
| ︙ | ︙ | |||
105 106 107 108 109 110 111 | Fossil repository and then [/help/commit|commit] your changes! <h2 id="newcmd">4.0 Creating A New Command</h2> By "commands" we mean the keywords that follow "fossil" when invoking Fossil from the command-line. So, for example, in | > | > | | | | > | | | > | 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 134 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 |
Fossil repository and then [/help/commit|commit] your changes!
<h2 id="newcmd">4.0 Creating A New Command</h2>
By "commands" we mean the keywords that follow "fossil" when invoking
Fossil from the command-line. So, for example, in
<verbatim>
fossil diff xyzzy.c
</verbatim>
The "command" is "diff". Commands may optionally be followed by
arguments and/or options. To create new commands in Fossil, add code
(either to an existing source file, or to a new source file created as
described above) according to the following template:
<verbatim>
/*
** COMMAND: xyzzy
**
** Help text goes here. Backslashes must be escaped.
*/
void xyzzy_cmd(void){
/* Implement the command here */
fossil_print("Hello, World!\n");
}
</verbatim>
The example above creates a new command named "xyzzy" that prints the
message "Hello, World!" on the console. This command is a normal command
that will show up in the list of command from [/help/help|fossil help].
If you add an asterisk to the end of the command name, like this:
<verbatim>
** COMMAND: xyzzy*
</verbatim>
Then the command will only show up if you add the "--all" option to
[/help/help|fossil help]. Or, if the command name starts with
"test" then the command will be considered experimental and will only
show up when the --test option is used with [/help/help|fossil help].
The example above is a fully functioning Fossil command. You can add
the text shown to an existing Fossil source file, recompiling then test
it out by typing:
<verbatim>
./fossil xyzzy
./fossil help xyzzy
./fossil xyzzy --help
</verbatim>
The name of the C function that implements the command can be anything
you like (as long as it does not collide with some other symbol in the
Fossil code) but it is traditional to name the function
"<i>commandname</i><b>_cmd</b>", as is done in the example.
You could also use "printf()" instead of "fossil_print()" to generate
|
| ︙ | ︙ | |||
171 172 173 174 175 176 177 | <h2 id="newpage">5.0 Creating A New Web Page</h2> As with commands, new webpages can be added simply by inserting a function that generates the webpage together with a special header comment. A template follows: | | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
<h2 id="newpage">5.0 Creating A New Web Page</h2>
As with commands, new webpages can be added simply by inserting a function
that generates the webpage together with a special header comment. A
template follows:
<verbatim>
/*
** WEBPAGE: helloworld
*/
void helloworld_page(void){
style_header("Hello World!");
@ <p>Hello, World!</p>
style_footer();
}
</verbatim>
Add the code above to a new or existing Fossil source code file, then
recompile fossil and run [/help/ui|fossil ui] then enter
"http://localhost:8080/helloworld" in your web browser and the routine
above will generate a web page that says "Hello World."
It really is that simple.
|
| ︙ | ︙ |
Changes to www/alerts.md.
| ︙ | ︙ | |||
89 90 91 92 93 94 95 |
the "From" address above, or it could be a different value like
`admin@example.com`.
Save your changes.
At the command line, say
| | | | | | | 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 |
the "From" address above, or it could be a different value like
`admin@example.com`.
Save your changes.
At the command line, say
$ fossil set email-send-command
If that gives a blank value instead of `sendmail -ti`, say
$ fossil set email-send-command "sendmail -ti"
to force the setting. That works around a [known
bug](https://fossil-scm.org/forum/forumpost/840b676410) which may be
squished by the time you read this.
If you're running Postfix or Exim, you might think that command is
wrong, since you aren't running Sendmail. These mail servers provide a
`sendmail` command for compatibility with software like Fossil that has
no good reason to care exactly which SMTP server implementation is
running at a given site. There may be other SMTP servers that also
provide a compatible `sendmail` command, in which case they may work
with Fossil using the same steps as above.
<a id="status"></a>
If you reload the Admin → Notification page, the Status section at the
top should show:
Outgoing Email: Piped to command "sendmail -ti"
Pending Alerts: 0 normal, 0 digest
Subscribers: 0 active, 0 total
Before you move on to the next section, you might like to read up on
[some subtleties](#pipe) with the "pipe to a command" method that we did
not cover above.
<a id="usage"></a>
|
| ︙ | ︙ | |||
153 154 155 156 157 158 159 | by the way: a user can be signed up for email alerts without having a full-fledged Fossil user account. Only when both user names are the same are the two records tied together under the hood. For more on this, see [Users vs Subscribers below](#uvs). If you are seeing the following complaint from Fossil: | < | < < | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | by the way: a user can be signed up for email alerts without having a full-fledged Fossil user account. Only when both user names are the same are the two records tied together under the hood. For more on this, see [Users vs Subscribers below](#uvs). If you are seeing the following complaint from Fossil: > Use a different login with greater privilege than FOO to access /subscribe ...then the repository's administrator forgot to give the [**EmailAlert** capability][cap7] to that user or to a user category that the user is a member of. After a subscriber signs up for alerts for the first time, a single verification email is sent to that subscriber's given email address. |
| ︙ | ︙ | |||
212 213 214 215 216 217 218 | Announcement](/announce)" link at the top of the "Email Notification Setup" page. Put your email address in the "To:" line and a test message below, then press "Send Message" to verify that outgoing email is working. Another method is from the command line: | | | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
Announcement](/announce)" link at the top of the "Email Notification
Setup" page. Put your email address in the "To:" line and a test
message below, then press "Send Message" to verify that outgoing email
is working.
Another method is from the command line:
$ fossil alerts test-message you@example.com --body README.md --subject Test
That should send you an email with "Test" in the subject line and the
contents of your project's `README.md` file in the body.
That command assumes that your project contains a "readme" file, but of
course it does, because you have followed the [Programming Style Guide
Checklist][cl], right? Right.
|
| ︙ | ︙ | |||
262 263 264 265 266 267 268 | ### Troubleshooting If email alerts aren't working, there are several useful commands you can give to figure out why. (Be sure to [`cd` into a repo checkout directory](#cd) first!) | | | | | | | | | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
### Troubleshooting
If email alerts aren't working, there are several useful commands you
can give to figure out why.
(Be sure to [`cd` into a repo checkout directory](#cd) first!)
$ fossil alerts status
This should give much the same information as you saw [above](#status).
One difference is that, since you've created a forum post, the
`pending-alerts` value should only be zero if you did in fact get the
requested email alert. If it's zero, check your mailer's spam folder. If
it's nonzero, continue with these troubleshooting steps.
$ fossil backoffice
That forces Fossil to run its ["back office" process](./backoffice.md).
Its only purpose at the time of this writing is to push out alert
emails, but it might do other things later. Sometimes it can get stuck
and needs to be kicked. For that reason, you might want to set up a
crontab entry to make sure it runs occasionally.
$ fossil alerts send
This should also kick off the backoffice processing, if there are any
pending alerts to send out.
$ fossil alert pending
Show any pending alerts. The number of lines output here should equal
the [status output above](#status).
$ fossil test-add-alerts f5900
$ fossil alert send
Manually create an email alert and push it out immediately.
The `f` in the first command's final parameter means you're scheduling a
"forum" alert. The integer is the ID of a forum post, which you can find
by visiting `/timeline?showid` on your Fossil instance.
The second command above is necessary because the `test-add-alerts`
command doesn't kick off a backoffice run.
$ fossil ale send
This only does the same thing as the final command above, rather than
send you an ale, as you might be hoping. Sorry.
<a id="advanced"></a>
## Advanced Email Setups
|
| ︙ | ︙ | |||
422 423 424 425 426 427 428 | corruption][rdbc] if used with a file sharing technology that doesn't use proper file locking. You can start this Tcl script as a daemon automatically on most Unix and Unix-like systems by adding the following line to the `/etc/rc.local` file of the server that hosts the repository sending email alerts: | | | 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
corruption][rdbc] if used with a file sharing technology that doesn't
use proper file locking.
You can start this Tcl script as a daemon automatically on most Unix and
Unix-like systems by adding the following line to the `/etc/rc.local`
file of the server that hosts the repository sending email alerts:
/usr/bin/tclsh /home/www/fossil/email-sender.tcl &
[cj]: https://en.wikipedia.org/wiki/Chroot
[rdbc]: https://www.sqlite.org/howtocorrupt.html#_filesystems_with_broken_or_missing_lock_implementations
<a id="dir"></a>
### Method 3: Store in a Directory
|
| ︙ | ︙ | |||
579 580 581 582 583 584 585 | ### Data Design There are two new tables in the repository database, starting with Fossil 2.7. These tables are not created in new repositories by default. The tables only come into existence as needed when email alerts are configured and used. | < < < < < < | 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 |
### Data Design
There are two new tables in the repository database, starting with
Fossil 2.7. These tables are not created in new repositories by
default. The tables only come into existence as needed when email
alerts are configured and used.
* <b>SUBSCRIBER</b> →
The subscriber table records the email address for people who
want to receive email notifications. Each subscriber has a
`subscriberCode` which is a random 32-byte blob that uniquely
identifies the subscriber. There are also fields to indicate
what kinds of notifications the subscriber wishes to receive,
whether or not the email address of the subscriber has been
verified, etc.
* <b>PENDING\_ALERT</b> →
The PENDING\_ALERT table contains records that define events
about which alert emails might need to be sent.
A pending\_alert always refers to an entry in the
EVENT table. The EVENT table is part of the standard schema
and records timeline entries. In other words, there is one
row in the EVENT table for each possible timeline entry. The
PENDING\_ALERT table refers to EVENT table entries for which
we might need to send alert emails.
As pointed out above, ["subscribers" are distinct from "users"](#uvs).
The SUBSCRIBER.SUNAME field is the optional linkage between users and
subscribers.
<a id="stdout"></a>
### The "stdout" Method
|
| ︙ | ︙ | |||
681 682 683 684 685 686 687 |
attacker with the `subscriberCode`. Nor can knowledge of the
`subscriberCode` lead to an email flood or other annoyance attack, as
far as I can see.
If the `subscriberCodes` for a Fossil repository are ever compromised,
new ones can be generated as follows:
| | | 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 |
attacker with the `subscriberCode`. Nor can knowledge of the
`subscriberCode` lead to an email flood or other annoyance attack, as
far as I can see.
If the `subscriberCodes` for a Fossil repository are ever compromised,
new ones can be generated as follows:
UPDATE subscriber SET subscriberCode=randomblob(32);
Since this then affects all new email alerts going out from Fossil, your
end users may never even realize that they're getting new codes, as long
as they don't click on the URLs in the footer of old alert messages.
With that in mind, a Fossil server administrator could choose to
randomize the `subscriberCodes` periodically, such as just before the
|
| ︙ | ︙ |
Changes to www/backoffice.md.
| ︙ | ︙ | |||
77 78 79 80 81 82 83 | However, the daily digest of email notifications is handled by the backoffice. If a Fossil server can sometimes go more than a day without being accessed, then the automatic backoffice will never run, and the daily digest might not go out until somebody does visit a webpage. If this is a problem, an administrator can set up a cron job to periodically run: | | | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
However, the daily digest of email notifications is handled by the
backoffice. If a Fossil server can sometimes go more than a day without
being accessed, then the automatic backoffice will never run, and the
daily digest might not go out until somebody does visit a webpage.
If this is a problem, an administrator can set up a cron job to
periodically run:
fossil backoffice _REPOSITORY_
That command will cause backoffice processing to occur immediately.
Note that this is almost never necessary for an internet-facing
Fossil repository, since most repositories will get multiple accesses
per day from random robots, which will be sufficient to kick off the
daily digest emails. And even for a private server, if there is very
little traffic, then the daily digests are probably a no-op anyhow
|
| ︙ | ︙ | |||
100 101 102 103 104 105 106 | [Fossil Forum](https://fossil-scm.org/forum) so that we can perhaps fix the problem.) For now, the backoffice must be run manually on OpenBSD systems. To set up fully-manual backoffice, first disable the automatic backoffice using the "[backoffice-disable](/help?cmd=backoffice-disable)" setting. | | | | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
[Fossil Forum](https://fossil-scm.org/forum) so that we can perhaps
fix the problem.) For now, the backoffice must be run manually
on OpenBSD systems.
To set up fully-manual backoffice, first disable the automatic backoffice
using the "[backoffice-disable](/help?cmd=backoffice-disable)" setting.
fossil setting backoffice-disable on
Then arrange to invoke the backoffice separately using a command
like this:
fossil backoffice --poll 30 _REPOSITORY-LIST_
Multiple repositories can be named. This one command will handle
launching the backoffice for all of them. There are additional useful
command-line options. See the "[fossil backoffice](/help?cmd=backoffice)"
documentation for details.
The backoffice processes run manually using the "fossil backoffice"
|
| ︙ | ︙ | |||
145 146 147 148 149 150 151 | "no process". Sometimes the process id will be non-zero even if there is no corresponding process. Fossil knows how to figure out whether or not a process still exists. You can print out a decoded copy of the current backoffice lease using this command: | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
"no process". Sometimes the process id will be non-zero even if there
is no corresponding process. Fossil knows how to figure out whether or
not a process still exists.
You can print out a decoded copy of the current backoffice lease using
this command:
fossil test-backoffice-lease -R _REPOSITORY_
If a system has been idle for a long time, then there will be no
backoffice processes. (Either the process id entries in the lease
will be zero, or there will exist no process associated with the
process id.) When a new web request comes in, the system
sees that no backoffice process is active and so it kicks off a separate
process to run backoffice.
|
| ︙ | ︙ | |||
195 196 197 198 199 200 201 | The backoffice should "just work". It should not require administrator attention. However, if you suspect that something is not working right, there are some debugging aids. We have already mentioned the command that shows the backoffice lease for a repository: | | | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
The backoffice should "just work". It should not require administrator
attention. However, if you suspect that something is not working right,
there are some debugging aids.
We have already mentioned the command that shows the backoffice lease
for a repository:
fossil test-backoffice-lease -R _REPOSITORY_
Running that command every few seconds should show what is going on with
backoffice processing in a particular repository.
There are also settings that control backoffice behavior. The
"backoffice-nodelay" setting prevents the "next" process from taking a
lease and sleeping. If "backoffice-nodelay" is set, that causes all
|
| ︙ | ︙ |
Changes to www/backup.md.
| ︙ | ︙ | |||
134 135 136 137 138 139 140 | # <a id="sync-solution"></a> Solution 1: Explicit Pulls The following script solves most of the above problems for the use case where you want a *nearly-complete* clone of the remote repository using nothing but the normal Fossil sync protocol. It only does so if you are logged into the remote as a user with Setup capability, however. | < < < < | | | < < < < < < < < | 134 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 180 181 182 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 |
# <a id="sync-solution"></a> Solution 1: Explicit Pulls
The following script solves most of the above problems for the use case
where you want a *nearly-complete* clone of the remote repository using nothing
but the normal Fossil sync protocol. It only does so if you are logged into
the remote as a user with Setup capability, however.
``` shell
#!/bin/sh
fossil sync --unversioned
fossil configuration pull all
fossil rebuild
```
The last step is needed to ensure that shunned artifacts on the remote
are removed from the local clone. The second step includes
`fossil conf pull shun`, but until those artifacts are actually rebuilt
out of existence, your backup will be “more than complete” in the sense
that it will continue to have information that the remote says should
not exist any more. That would be not so much a “backup” as an
“archive,” which might not be what you want.
# <a id="sql-solution"></a> Solution 2: SQL-Level Backup
The first method doesn’t get you a copy of the remote’s
[private branches][pbr], on purpose. It may also miss other info on the
remote, such as SQL-level customizations that the sync protocol can’t
see. (Some [ticket system customization][tkt] schemes rely on this ability, for example.) You can
solve such problems if you have access to the remote server, which
allows you to get a SQL-level backup by delegating handling of locking
and transaction isolation to
[the `backup` command][bu], allowing the user to safely back up an in-use
repository.
If you have SSH access to the remote server, something like this will work:
``` shell
#!/bin/bash
bf=repo-$(date +%Y-%m-%d).fossil
ssh example.com "cd museum ; fossil backup -R repo.fossil backups/$bf" &&
scp example.com:museum/backups/$bf ~/museum/backups
```
Beware that this method does not solve [the intransitive sync
problem](#ait), in and of itself: if you do a SQL-level backup of a
stale repo DB, you have a *stale backup!* You should therefore run this
on every node that may need to serve as a backup so that at least *one*
of the backups is also up-to-date.
# <a id="enc"></a> Encrypted Off-Site Backups
A useful refinement that you can apply to both methods above is
encrypted off-site backups. You may wish to store backups of your
repositories off-site on a service such as Dropbox, Google Drive, iCloud,
or Microsoft OneDrive, where you don’t fully trust the service not to
leak your information. This addition to the prior scripts will encrypt
the resulting backup in such a way that the cloud copy is a useless blob
of noise to anyone without the key:
```shell
iter=152830
pass="h8TixP6Mt6edJ3d6COaexiiFlvAM54auF2AjT7ZYYn"
gd="$HOME/Google Drive/Fossil Backups/$bf.xz.enc"
fossil sql -R ~/museum/backups/"$bf" .dump | xz -9 |
openssl enc -e -aes-256-cbc -pbkdf2 -iter $iter -pass pass:"$pass" -out "$gd"
```
If you’re adding this to the first script above, remove the
“`-R repo-name`” bit so you get a dump of the repository backing the
current working directory.
Change the `pass` value to some other long random string, and change the
`iter` value to something in the hundreds of thousands range. A good source for
the first is [here][grcp], and for the second, [here][rint].
|
| ︙ | ︙ | |||
260 261 262 263 264 265 266 | lacked this capability until Ventura (13.0). If you’re on Monterey (12) or older, we recommend use of the [Homebrew][hb] OpenSSL package rather than give up on the security afforded by use of configurable-iteration PBKDF2. To avoid a conflict with the platform’s `openssl` binary, Homebrew’s installation is [unlinked][hbul] by default, so you have to give an explicit path to it, one of: | | | | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
lacked this capability until Ventura (13.0). If you’re on Monterey (12)
or older, we recommend use of the [Homebrew][hb] OpenSSL package rather
than give up on the security afforded by use of configurable-iteration
PBKDF2. To avoid a conflict with the platform’s `openssl` binary,
Homebrew’s installation is [unlinked][hbul] by default, so you have to
give an explicit path to it, one of:
/usr/local/opt/openssl/bin/openssl ... # Intel x86 Macs
/opt/homebrew/opt/openssl/bin/openssl ... # ARM Macs (“Apple silicon”)
[lssl]: https://www.libressl.org/
## <a id="rest"></a> Restoring From An Encrypted Backup
The “restore” script for the above fragment is basically an inverse of
|
| ︙ | ︙ |
Changes to www/branching.wiki.
| ︙ | ︙ | |||
244 245 246 247 248 249 250 | branches identified only by the commit ID currently at its tip, being a long string of hex digits. Therefore, Fossil conflates two concepts: branching as intentional forking and the naming of forks as branches. They are in fact separate concepts, but since Fossil is intended to be used primarily by humans, we combine them in Fossil's human user interfaces. | | | | | | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | branches identified only by the commit ID currently at its tip, being a long string of hex digits. Therefore, Fossil conflates two concepts: branching as intentional forking and the naming of forks as branches. They are in fact separate concepts, but since Fossil is intended to be used primarily by humans, we combine them in Fossil's human user interfaces. <p class="blockquote"> <b>Key Distinction:</b> A branch is a <i>named, intentional</i> fork. </p> Unnamed forks <i>may</i> be intentional, but most of the time, they're accidental and left unnamed. Fossil offers two primary ways to create named, intentional forks, a.k.a. branches. First: <pre> $ fossil commit --branch my-new-branch-name </pre> This is the method we recommend for most cases: it creates a branch as part of a check-in using the version in the current checkout directory as its basis. (This is normally the tip of the current branch, though it doesn't have to be. You can create a branch from an ancestor check-in on a branch as well.) After making this branch-creating check-in, your local working directory is switched to that branch, so that further check-ins occur on that branch as well, as children of the tip check-in on that branch. The second, more complicated option is: <pre> $ fossil branch new my-new-branch-name trunk $ fossil update my-new-branch-name $ fossil commit </pre> Not only is this three commands instead of one, the first of which is longer than the entire simpler command above, you must give the second command before creating any check-ins, because until you do, your local working directory remains on the same branch it was on at the time you issued the command, so that the commit would otherwise put the new material on |
| ︙ | ︙ | |||
375 376 377 378 379 380 381 | <h2 id="fix">Fixing Forks</h2> If your local checkout is on a forked branch, you can usually fix a fork automatically with: <pre> | | | 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | <h2 id="fix">Fixing Forks</h2> If your local checkout is on a forked branch, you can usually fix a fork automatically with: <pre> $ fossil merge </pre> Normally you need to pass arguments to <b>fossil merge</b> to tell it what you want to merge into the current basis view of the repository, but without arguments, the command seeks out and fixes forks. |
| ︙ | ︙ | |||
489 490 491 492 493 494 495 | <h2 id="bad-fork">How Can Forks Divide Development Effort?</h2> [#dist-clone|Above], we stated that forks carry a risk that development effort on a branch can be divided among the forks. It might not be immediately obvious why this is so. To see it, consider this swim lane diagram: | | | 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 |
<h2 id="bad-fork">How Can Forks Divide Development Effort?</h2>
[#dist-clone|Above], we stated that forks carry a risk that development
effort on a branch can be divided among the forks. It might not be
immediately obvious why this is so. To see it, consider this swim lane
diagram:
<verbatim type="pikchr center toggle">
$laneh = 0.75
ALL: [
# Draw the lanes
down
box width 3.5in height $laneh fill 0xacc9e3
box same fill 0xc5d8ef
|
| ︙ | ︙ | |||
693 694 695 696 697 698 699 | bad, which is why [./concepts.wiki#workflow|Fossil tries so hard to avoid them], why it warns you about it when they do occur, and why it makes it relatively [#fix|quick and painless to fix them] when they do occur. <h2>Review Of Terminology</h2> | | | | 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 | bad, which is why [./concepts.wiki#workflow|Fossil tries so hard to avoid them], why it warns you about it when they do occur, and why it makes it relatively [#fix|quick and painless to fix them] when they do occur. <h2>Review Of Terminology</h2> <dl> <dt><b>Branch</b></dt> <dd><p>A branch is a set of check-ins with the same value for their "branch" property.</p></dd> <dt><b>Leaf</b></dt> <dd><p>A leaf is a check-in with no children in the same branch.</p></dd> <dt><b>Closed Leaf</b></dt> <dd><p>A closed leaf is any leaf with the <b>closed</b> tag. These leaves are intended to never be extended with descendants and hence are omitted from lists of leaves in the command-line and web interface.</p></dd> <dt><b>Open Leaf</b></dt> <dd><p>A open leaf is a leaf that is not closed.</p></dd> <dt><b>Fork</b></dt> <dd><p>A fork is when a check-in has two or more direct (non-merge) children in the same branch.</p></dd> <dt><b>Branch Point</b></dt> <dd><p>A branch point occurs when a check-in has two or more direct (non-merge) children in different branches. A branch point is similar to a fork, except that the children are in different branches.</p></dd> </dl> Check-in 4 of Figure 3 is not a leaf because it has a child (check-in 5) in the same branch. Check-in 9 of Figure 5 also has a child (check-in 10) but that child is in a different branch, so check-in 9 is a leaf. Because of the <b>closed</b> tag on check-in 9, it is a closed leaf. Check-in 2 of Figure 3 is considered a "fork" |
| ︙ | ︙ |
Changes to www/build.wiki.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 | <ol> <li>Point your web browser to [https://fossil-scm.org/]</li> <li>Click on the [/timeline|Timeline] link at the top of the page.</li> | | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | <ol> <li>Point your web browser to [https://fossil-scm.org/]</li> <li>Click on the [/timeline|Timeline] link at the top of the page.</li> <li>Select a version of Fossil you want to download. The latest version on the trunk branch is usually a good choice. Click on its link.</li> <li>Finally, click on one of the "Zip Archive" or "Tarball" links, according to your preference. These links will build a ZIP archive or a gzip-compressed tarball of the complete source code and download it to your computer.</li> </ol> <h2>Aside: Is it really safe to use an unreleased development version of the Fossil source code?</h2> Yes! Any check-in on the |
| ︙ | ︙ | |||
174 175 176 177 178 179 180 | Alternatively, running <b>./configure</b> under MSYS should give a suitable top-level Makefile. However, options passed to configure that are not applicable on Windows may cause the configuration or compilation to fail (e.g. fusefs, internal-sqlite, etc). <li><i>MSVC</i> → Use the MSVC makefile.</li> | > | > > < | | | | | | | | | | 174 175 176 177 178 179 180 181 182 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 216 217 218 |
Alternatively, running <b>./configure</b> under MSYS should give a
suitable top-level Makefile. However, options passed to configure that are
not applicable on Windows may cause the configuration or compilation to fail
(e.g. fusefs, internal-sqlite, etc).
<li><i>MSVC</i> → Use the MSVC makefile.</li>
<em>NB:</em> Run the following <code>nmake</code> commands from a "x64 Native
Tools Command Prompt"; <code>buildmsvc.bat</code> is able to automatically load
the build tools (x64 by default, pass "x86" as the first argument to use the
x86 tools), so it can be called from a normal command prompt.
First, change to the "win/" subdirectory ("<b>cd win</b>"), then run
"<b>nmake /f Makefile.msc</b>".<br><br>Alternatively, the batch
file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to
detect and use the latest installed version of MSVC.<br><br>To enable
the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
first <a href="https://www.openssl.org/source/">download the official
source code for OpenSSL</a> and extract it to an appropriately named
"<b>openssl</b>" subdirectory within the local
[/tree?ci=trunk&name=compat | compat] directory then make sure that some recent
<a href="http://www.perl.org/">Perl</a> binaries are installed locally,
and finally run one of the following commands:
<pre>
nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
</pre>
<pre>
buildmsvc.bat FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
</pre>
To enable the optional native [./th1.md#tclEval | Tcl integration feature],
run one of the following commands or add the "FOSSIL_ENABLE_TCL=1"
argument to one of the other NMAKE command lines:
<pre>
nmake /f Makefile.msc FOSSIL_ENABLE_TCL=1
</pre>
<pre>
buildmsvc.bat FOSSIL_ENABLE_TCL=1
</pre>
<li><i>Cygwin</i> → The same as other Unix-like systems. It is
recommended to configure using: "<b>configure --disable-internal-sqlite</b>",
making sure you have the "libsqlite3-devel" , "zlib-devel" and
"openssl-devel" packages installed first.</li>
</ol>
</ol>
|
| ︙ | ︙ | |||
249 250 251 252 253 254 255 | be installed on the local machine. You can get Tcl/Tk from [http://www.activestate.com/activetcl|ActiveState]. </li> <li> To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile generated by configure to add the following lines: | | | | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | be installed on the local machine. You can get Tcl/Tk from [http://www.activestate.com/activetcl|ActiveState]. </li> <li> To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile generated by configure to add the following lines: <pre> TCC += -DSQLITE_WITHOUT_ZONEMALLOC TCC += -D_BSD_SOURCE TCC += -DWITHOUT_ICONV TCC += -Dsocketlen_t=int TCC += -DSQLITE_MAX_MMAP_SIZE=0 </pre> </li> </ul> <h2 id="docker" name="oci">5.0 Building a Docker Container</h2> The information on building Fossil inside an |
| ︙ | ︙ | |||
407 408 409 410 411 412 413 | along with <tt>--fuzztype</tt>, be sure to check your system's process list to ensure that your <tt>--fuzztype</tt> flag is there. <a id='wasm'></a> <h2>8.0 Building WebAssembly Components</h2> | | | 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | along with <tt>--fuzztype</tt>, be sure to check your system's process list to ensure that your <tt>--fuzztype</tt> flag is there. <a id='wasm'></a> <h2>8.0 Building WebAssembly Components</h2> Fossil uses one component built as [https://developer.mozilla.org/en-US/docs/WebAssembly | WebAssembly] a.k.a. WASM. Because compiling WASM code requires non-trivial client-side tooling, the repository includes compiled copies of these pieces. Most Fossil hackers should never need to concern themselves with the WASM parts, but this section describes how to for those who want or need to do so. |
| ︙ | ︙ | |||
436 437 438 439 440 441 442 | [https://emscripten.org/docs/getting_started/downloads.html] For instructions on keeping the SDK up to date, see: [https://emscripten.org/docs/tools_reference/emsdk.html] | | | | | | | | | | > | 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 | [https://emscripten.org/docs/getting_started/downloads.html] For instructions on keeping the SDK up to date, see: [https://emscripten.org/docs/tools_reference/emsdk.html] <div class="sidebar">Getting Emscripten up and running is trivial and painless, at least on Linux systems, but the installer downloads many hundreds of megabytes of tools and dependencies, all of which will be installed under the single SDK directory (as opposed to being installed at the system level). It does, however, require that python3 be installed at the system level and it can optionally make use of a system-level cmake for certain tasks unrelated to how fossil uses the SDK.</div> After installing the SDK, configure the fossil tree with emsdk support: <pre><code>$ ./configure --with-emsdk=/path/to/emsdk \ --and-other-options... </code></pre> If the <tt>--with-emsdk</tt> flag is not provided, the configure script will check for the environment variable <tt>EMSDK</tt>, which is one of the standard variables the SDK environment uses. If that variable is found, its value will implicitly be used in place of the missing <tt>--with-emsdk</tt> flag. Thus, if the <tt>emsdk_env.sh</tt> |
| ︙ | ︙ | |||
478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
build cycle. They are instead explicitly built as described below.
From the top of the source tree, all WASM-related components can be
built with:
<pre><code>$ make wasm</code></pre>
As of this writing, those parts include:
* <tt>extsrc/pikchr.wasm</tt> is a WASM-compiled form of
<tt>extsrc/pikchr.c</tt>.
* <tt>extsrc/pikchr.js</tt> is JS/WASM glue code generated by Emscripten
to give JS code access to the API exported by the WASM file.
| > > > > > > > > > > < < < < < < < < < < | < | < | | | | | | 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 |
build cycle. They are instead explicitly built as described below.
From the top of the source tree, all WASM-related components can be
built with:
<pre><code>$ make wasm</code></pre>
<div class="sidebar">The file
<tt>[/file/extsrc/pikcher-worker.js|extsrc/pikcher-worker.js]</tt>
is hand-coded and intended to be loaded as a "Worker" in
JavaScript. That file loads the main module and provides an
interface via which a main JavaScript thread can communicate with
pikchr running in a Worker thread. The file
<tt>[/file/src/fossil.page.pikchrshowasm.js|src/fossil.page.pikchrshowasm.js]</tt>
implements the [/pikchrshow] app and demonstrates how
<tt>pikchr-worker.js</tt> is used.</div>
As of this writing, those parts include:
* <tt>extsrc/pikchr.wasm</tt> is a WASM-compiled form of
<tt>extsrc/pikchr.c</tt>.
* <tt>extsrc/pikchr.js</tt> is JS/WASM glue code generated by Emscripten
to give JS code access to the API exported by the WASM file.
When a new version of <tt>extsrc/pikchr.c</tt> is installed, the
files <tt>pikchr.{js,wasm}</tt> will need to be recompiled to account
for that. Running <tt>make wasm</tt> will, if the build is set up for
the emsdk, recompile those:
<pre><code>$ make wasm
./tools/emcc.sh -o extsrc/pikchr.js ...
$ ls -la extsrc/pikchr.{js,wasm}
-rw-rw-r-- 1 stephan stephan 17263 Jun 8 03:59 extsrc/pikchr.js
-rw-rw-r-- 1 stephan stephan 97578 Jun 8 03:59 extsrc/pikchr.wasm
</code></pre>
<div class="sidebar">If that fails with a message along the lines of
“<code>setting `EXPORTED_RUNTIME_METHODS` expects `<class 'list'>` but
got `<class 'str'>`</code>” then the emcc being invoked is too old: emcc
changed the format of list-type arguments at some point. The required
minimum version is unknown, but any SDK version from May 2022 or later
"should" (as of this writing) suffice. Any older version may or may not
work.</div>
After that succeeds, we need to run the normal build so that those
generated files can be compiled in to the fossil binary, accessible
via the [/help?cmd=/builtin|/builtin page]:
<pre><code>$ make</code></pre>
|
| ︙ | ︙ |
Changes to www/caps/login-groups.md.
| ︙ | ︙ | |||
103 104 105 106 107 108 109 | Login groups have names. A repo can be in only one of these named login groups at a time. Trust in login groups is transitive within a single server. Consider this sequence: | < | | | | < | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
Login groups have names. A repo can be in only one of these named login
groups at a time.
Trust in login groups is transitive within a single server. Consider
this sequence:
$ cd /path/to/A/checkout
$ fossil login-group join --name G ~/museum/B.fossil
$ cd /path/to/C/checkout
$ fossil login-group join ~/museum/B.fossil
That creates login group G joining repo A to B, then joins C to B.
Although we didn’t explicitly tie C to A, a successful login on C gets
you into both A and B, within the restrictions set out above.
Changes are transitive in the same way, provided you check that “apply
to all” box on the user edit screen.
|
| ︙ | ︙ |
Changes to www/caps/ref.html.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 |
<tr id="d">
<th>d</th>
<th>n/a</th>
<td>
Legacy capability letter from Fossil's forebear <a
href="http://cvstrac.org/">CVSTrac</a>, which has no useful
| | < < | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
<tr id="d">
<th>d</th>
<th>n/a</th>
<td>
Legacy capability letter from Fossil's forebear <a
href="http://cvstrac.org/">CVSTrac</a>, which has no useful
meaning in Fossil due to the nature of its durable Merkle tree design.
We recommend that you remove it in case we
ever reuse this letter for another purpose. See <a
href="https://fossil-scm.org/forum/forumpost/43c78f4bef">this
post</a> for details.
</td>
</tr>
<tr id="e">
|
| ︙ | ︙ |
Changes to www/cgi.wiki.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 | those options. <h1>CGI Script Options</h1> The CGI script used to launch a Fossil server will usually look something like this: | | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | those options. <h1>CGI Script Options</h1> The CGI script used to launch a Fossil server will usually look something like this: <verbatim> #!/usr/bin/fossil repository: /home/www/fossils/myproject.fossil </verbatim> Of course, pathnames will likely be different. The first line (the "[wikipedia:/wiki/Shebang_(Unix)|shebang]") always gives the name of the Fossil executable. Subsequent lines are of the form "<b>property: argument ...</b>". The remainder of this document describes the available properties and their arguments. |
| ︙ | ︙ |
Changes to www/changes.wiki.
1 2 | <title>Change Log</title> | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | | > > > | > > > > > | < > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 87 88 |
<title>Change Log</title>
<h2 id='v2_25'>Changes for version 2.25 (pending)</h2>
* The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
that have non-ASCII filenames
* Add the [/help?cmd=tree|fossil tree] command.
* On case-insensitive filesystems, store files using the filesystem's
preferred case rather than the case typed in by the user.
* Change the name "fossil cherry-pick" command to "fossil cherrypick",
which is more familiar to Git users. Retain the legacy name for
compatibility.
* Add new query parameters to the [/help?cmd=/timeline|/timeline page]:
d2=, p2=, and dp2=.
* Add options to the [/help?cmd=tag|fossil tag] command that will list tag values.
* Add ability to upload unversioned files via the [/help?cmd=/uvlist|/uvlist page].
* Add history search to the [/help?cmd=/chat|/chat page].
<h2 id='v2_24'>Changes for version 2.24 (2024-04-23)</h2>
* Apache change work-around → As part of a security fix, the Apache webserver
mod_cgi module has stopped relaying the Content-Length field of the HTTP
reply header from the CGI programs back to the client in cases where the
connection is to be closed and the client is able to read until end-of-file.
The HTTP and CGI specs allow for this, though it does seem rude.
Older versions of Fossil were depending on the Content-Length header field
being set. To work around the change to Apache, Fossil has
been enhanced to cope with a missing Content-Length in the reply header. See
[forum:/forumpost/12ac403fd29cfc89|forum thread 12ac403fd29cfc89].
* [./customskin.md|Skin] enhancements:
<ul>
<li> Reworked the default skin to make everything more readable: larger
fonts, more whitespace, deeper indents to show hierarchy and to
offset command examples, etc. Adjusted colors slightly to bring
things into better accord with the WCAG accessibility guidelines.
This constitutes a <strong>breaking change</strong> for those with
custom skins; see [./customskin.md#version-2.24 | this section of
the docs] for migration advice.
<li> Add a new link added to the [/login] page that allows the user to
[/skins|select their preferred skin]. This preference is stored in
the [/fdscookie|fossil display_settings cookie].
<li> The /setup_skin_admin page is simplified to let administrators easily
select one of the built-in skins as a default, or to specify a
custom skin.
</ul>
* If an "ssh:" sync fails in a way that suggests that the fossil executable
could not be found on the remote host, then retry after adding a PATH=
prefix to the command. This helps "ssh:" to "just work" when the server
is a Mac.
* Enhancements to the [/help?cmd=/timeline|/timeline page]:
<ul>
<li> Add the x= query paramater
<li> Add the shortcut tl= and rl= query parameters
<li> Add support for from=,ft= and from=,bt= query parameter combinations
<li> Automatically highlight the endpoints for from=,to= queries.
<li> Add the to2=Z query parameter to augment from=X,to=Y so that the
path from X to Z is shown if Y cannot be found.
</ul>
* Moved the /museum/repo.fossil file referenced from the Dockerfile from
the ENTRYPOINT to the CMD part to allow use of --repolist mode.
* The [/uvlist] page now shows the hash algorithm used so that
viewers don't have to guess. The hash is shown in a fixed-width
font for a more visually pleasing display.
* If the [/help?cmd=autosync|autosync setting] contains keyword "all",
the automatic sync occurs against all defined remote repositories, not
just the default.
* Markdown formatter: improved handling of indented fenced code blocks
that contain blank lines.
* When doing a "[/help?cmd=add|fossil add]" on a system with case-insensitive
but case-preserving filenames (Mac and Windows) try to use the filename
case as it is known to the filesystem, not the case entered by the
user on the command-line. See
[forum:/forumpost/30d9c0d131610f53|forum thread 30d9c0d131610f53].
* Fix problems with one-click unsubscribe on email notifications.
* Import the latest [/doc/trunk/www/pikchr.md|Pikchr] containing support
for "diamond" objects.
* Add ability to render committed Pikchr files to SVG via
<samp>/doc/…/foo.pikchr?popup</samp> URLs.
* Update Fossil's internal robot detection logic so that it correctly
identifies the new GoogleOther crawler as a robot.
<h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>
* Add ability to "close" forum threads, such that unprivileged users
may no longer respond to them. Only administrators can close
threads or respond to them by default, and the
[/help?cmd=forum-close-policy|forum-close-policy setting] can be
|
| ︙ | ︙ |
Changes to www/chat.md.
1 2 3 4 | # Fossil Chat ## Introduction | < | | 1 2 3 4 5 6 7 8 9 10 11 12 |
# Fossil Chat
## Introduction
Fossil’s developer chatroom feature provides an
ephemeral discussion venue for insiders. Design goals include:
* **Simple but functional** →
Fossil chat is designed to provide a convenient real-time
communication mechanism for geographically dispersed developers.
Fossil chat is *not* intended as a replacement or competitor for
IRC, Slack, Discord, Telegram, Google Hangouts, etc.
|
| ︙ | ︙ | |||
60 61 62 63 64 65 66 | For users with appropriate permissions, simply browse to the [/chat](/help?cmd=/chat) to start up a chat session. The default skin includes a "Chat" entry on the menu bar on wide screens for people with chat privilege. There is also a "Chat" option on the [Sitemap page](/sitemap), which means that chat will appear as an option under the hamburger menu for many [skins](./customskin.md). | | | > > > > > > > > > | 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 | For users with appropriate permissions, simply browse to the [/chat](/help?cmd=/chat) to start up a chat session. The default skin includes a "Chat" entry on the menu bar on wide screens for people with chat privilege. There is also a "Chat" option on the [Sitemap page](/sitemap), which means that chat will appear as an option under the hamburger menu for many [skins](./customskin.md). Chat messages are subject to [Fossil's full range of Markdown processing](/md_rules). Because chat messages are stored as-is when they arrive from a client, this change applies retroactively to messages stored by previous fossil versions. Files may be sent via chat using the file selection element at the bottom of the page. If the desktop environment system supports it, files may be dragged and dropped onto that element. Files are not automatically sent - selection of a file can be cancelled using the Cancel button which appears only when a file is selected. When the Send button is pressed, any pending text is submitted along with the selected file. Image files sent this way will, by default, appear inline in messages, but each user may toggle that via the settings popup menu, such that images instead appear as downloadable links. Non-image files always appear in messages as download links. ### Deletion of Messages <div class="sidebar">Message deletion is itself a type of message, which is why deletions count towards updates in the recent activity list. (It is counted for the person who performed the deletion, not the author of the deleted comment.) That can potentially lead to odd corner cases where a user shows up in the list but has no messages which are currently visible because they were deleted, or an admin user who has not posted anything but deleted a message. That is a known minor cosmetic-only bug with a resolution of "will not fix."</div> Any user may *locally* delete a given message by clicking on the "tab" at the top of the message and clicking the button which appears. Such deletions are local-only, and the messages will reappear if the page is reloaded. The user who posted a given message, or any Admin users, may additionally choose to globally delete a message from the chat record, which deletes it not only from their own browser but also |
| ︙ | ︙ | |||
99 100 101 102 103 104 105 | will be included in that list. To switch sounds, tap the "settings" button. ### <a id='connection'></a> Who's Online? Because the chat app has to be able to work over transient CGI-based connections, as opposed to a stable socket connection to the server, | | | < < < < < < < < < | 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 | will be included in that list. To switch sounds, tap the "settings" button. ### <a id='connection'></a> Who's Online? Because the chat app has to be able to work over transient CGI-based connections, as opposed to a stable socket connection to the server, real-time tracking of "who's online" is not feasible. Chat offers an optional feature, toggleable in the settings, which can list users who have posted messages in the client's current list of loaded messages. This is not the same thing as tracking who's online, but it gives an overview of which users have been active most recently, noting that "lurkers" (people who post no messages) will not show up in that list, nor does the chat infrastructure have a way to track and present those. That list can be used to filter messages on a specific user by tapping on that user's name, tapping a second time to remove the filter. ### <a id="cli"></a> The `fossil chat` Command Type [fossil chat](/help?cmd=chat) from within any open check-out to bring up a chatroom for the project that is in that checkout. The new chat window will attempt to connect to the default sync target for that check-out (the server whose URL is shown by the [fossil remote](/help?cmd=remote) command). |
| ︙ | ︙ | |||
144 145 146 147 148 149 150 | The recommended way to allow robots to send chat messages is to create a new user on the server for each robot. Give each such robot account the "C" privilege only. That means that the robot user account will be able to send chat messages, but not do anything else. Then, in the program or script that runs the robot, when it wants to send a chat message, have it run a command like this: | | | 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | The recommended way to allow robots to send chat messages is to create a new user on the server for each robot. Give each such robot account the "C" privilege only. That means that the robot user account will be able to send chat messages, but not do anything else. Then, in the program or script that runs the robot, when it wants to send a chat message, have it run a command like this: ~~~~ fossil chat send --remote https://robot:PASSWORD@project.org/fossil \ --message 'MESSAGE TEXT' --file file-to-attach.txt ~~~~ Substitute the appropriate project URL, robot account name and password, message text and file attachment, of course. |
| ︙ | ︙ | |||
210 211 212 213 214 215 216 |
Fetches the file content associated with a post (one file per
post, maximum). In the UI, this is accessed via links to uploaded
files and via inlined image tags.
Chat messages are stored on the server-side in the CHAT table of
the repository.
| | | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
Fetches the file content associated with a post (one file per
post, maximum). In the UI, this is accessed via links to uploaded
files and via inlined image tags.
Chat messages are stored on the server-side in the CHAT table of
the repository.
~~~
CREATE TABLE repository.chat(
msgid INTEGER PRIMARY KEY AUTOINCREMENT,
mtime JULIANDAY, -- Time for this entry - Julianday Zulu
lmtime TEXT, -- Client YYYY-MM-DDZHH:MM:SS when message originally sent
xfrom TEXT, -- Login of the sender
xmsg TEXT, -- Raw, unformatted text of the message
fname TEXT, -- Filename of the uploaded file, or NULL
|
| ︙ | ︙ |
Changes to www/checkin_names.wiki.
1 2 | <title>Check-in Names</title> | | < | | | | | | | | | | | > | | | | | | > | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 | <title>Check-in Names</title> <div class="sidebar no-label"> <b>Quick Reference</b> <ul> <li> Hash prefix <li> Branch name <li> Tag name <li> Timestamp: <i>YYYY-MM-DD HH:MM:SS</i> <li> <i>tag-name</i> <big><b>:</b></big> <i>timestamp</i> <li> <b>root <big>:</big></b> <i>branchname</i> <li> <b>start <big>:</big></b> <i>branchname</i> <li> <b>merge-in <big>:</big></b> <i>branchname</i> <li> Special names: <ul> <li> <b>tip</b> <li> <b>current</b> <li> <b>next</b> <li> <b>previous</b> or <b>prev</b> <li> <b>ckout</b> (<a href='./embeddeddoc.wiki'>embedded docs</a> only) </ul> </ul> </div> Many Fossil [/help|commands] and [./webui.wiki | web interface] URLs accept check-in names as an argument. For example, the "[/help/info|info]" command accepts an optional check-in name to identify the specific check-in about which information is desired: <pre style="white-space: pre-wrap"> fossil info <i>checkin-name</i> </pre> You are perhaps reading this page from the following URL: <verbatim> https://fossil-scm.org/home/doc/trunk/www/checkin_names.wiki </verbatim> This is an example of an [./embeddeddoc.wiki | embedded documentation] page URL. The "trunk" element of the pathname is a [./glossary.md#check-in | check-in] name that determines which version of the documentation to display. Fossil provides a variety of ways to specify a check-in. This document describes the various methods. <h2 id="canonical">Canonical Check-in Name</h2> The canonical name of a check-in is the hash of its [./fileformat.wiki#manifest | manifest] expressed as a [./hashes.md | long lowercase hexadecimal number]. For example: <pre> fossil info e5a734a19a9826973e1d073b49dc2a16aa2308f9 </pre> The full 40 or 64 character hash is unwieldy to remember and type, though, so Fossil also accepts a unique prefix of the hash, using any combination of upper and lower case letters, as long as the prefix is at least 4 characters long. Hence the following commands all accomplish the same thing as the above: <pre> fossil info e5a734a19a9 fossil info E5a734A fossil info e5a7 </pre> Fossil uses this feature itself, identifying check-ins by 8 to 16-character prefixes of the canonical name in places where it doesn't want to chew up the screen real estate required to display the whole hash. <h2 id="tags">Tags And Branch Names</h2> Using a tag or branch name where a check-in name is expected causes Fossil to choose the most recent check-in with that tag or branch name. So for example, the most recent check-in that is tagged with "release" as of this writing is [b98ce23d4fc]. The command: <pre> fossil info release </pre> …results in the following output: <pre> hash: b98ce23d4fc3b734cdc058ee8a67e6dad675ca13 2020-08-20 13:27:04 UTC parent: 40feec329163103293d98dfcc2d119d1a16b227a 2020-08-20 13:01:51 UTC tags: release, branch-2.12, version-2.12.1 comment: Version 2.12.1 (user: drh) </pre> There are multiple check-ins that are tagged with "release" but (as of this writing) the [b98ce23d4fc] check-in is the most recent so it is the one that is selected. Note that unlike some other version control systems, a "branch" in Fossil is not anything special: it is simply a sequence of check-ins that share a common tag, so the same mechanism that resolves tag names also resolves branch names. <a id="tagpfx"></a> Note also that there can — in theory, if rarely in practice — be an ambiguity between tag names and canonical names. Suppose, for example, you had a check-in with the canonical name deed28aa99… and you also happened to have tagged a different check-in with "deed2". If you use the "deed2" name, does it choose the canonical name or the tag name? In such cases, you can prefix the tag name with "tag:". For example: <pre> fossil info tag:deed2 </pre> The "tag:deed2" name will refer to the most recent check-in tagged with "deed2" rather than the check-in whose canonical name begins with "deed2". <h2 id="whole-branches">Whole Branches</h2> |
| ︙ | ︙ | |||
178 179 180 181 182 183 184 | repo could have release tags like “2020-04-01”, the date the release was cut, but you could force Fossil to interpret that string as a date rather than as a tag by passing “date:2020-04-01”. For an example of how timestamps are useful, consider the homepage for the Fossil website itself: | | | | | | | | | | | | | | 179 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | repo could have release tags like “2020-04-01”, the date the release was cut, but you could force Fossil to interpret that string as a date rather than as a tag by passing “date:2020-04-01”. For an example of how timestamps are useful, consider the homepage for the Fossil website itself: <pre> https://fossil-scm.org/home/doc/<b>trunk</b>/www/index.wiki </pre> The bold component of that URL is a check-in name. To see the stored content of the Fossil website repository as of January 1, 2009, one has merely to change the URL to the following: <pre> https://fossil-scm.org/home/doc/<b>2009-01-01</b>/www/index.wiki </pre> (Note that this won't roll you back to the <i>skin</i> and other cosmetic configurations as of that date. It also won't change screens like the timeline, which has an independent date selector.) <h2 id="tag-ts">Tag And Timestamp</h2> A check-in name can also take the form of a tag or branch name followed by a colon and then a timestamp. The combination means to take the most recent check-in with the given tag or branch which is not more recent than the timestamp. So, for example: <pre> fossil update trunk:2010-07-01T14:30 </pre> Would cause Fossil to update the working check-out to be the most recent check-in on the trunk that is not more recent than 14:30 (UTC) on July 1, 2010. <h2 id="root">Root Of A Branch</h2> A branch name that begins with the "<tt>root:</tt>" prefix refers to the last check-in on the parent branch prior to the beginning of the branch. Such a label is useful, for example, in computing all diffs for a single branch. The following example will show all changes in the hypothetical branch "xyzzy": <pre> fossil diff --from root:xyzzy --to xyzzy </pre> <a id="merge-in"></a> That doesn't do what you might expect after you merge the parent branch's changes into the child branch: the above command will include changes made on the parent branch as well. You can solve this by using the prefix "<tt>merge-in:</tt>" instead of "<tt>root:</tt>" to tell Fossil to find the most recent merge-in point for that branch. The resulting diff will then show only the changes in the branch itself, omitting any changes that have already been merged in from the parent branch. <a id="start"></a> The prefix "<tt>start:</tt>" gives the first check-in of the named branch. The prefixes "<tt>root:</tt>", "<tt>start:</tt>", and "<tt>merge-in:</tt>" can be chained: one can say for example <pre> fossil info merge-in:xyzzy:2022-03-01 </pre> to get informations about the most recent merge-in point on the branch "xyzzy" that happened on or before March 1, 2022. <h2 id="special">Special Tags</h2> The tag "tip" means the most recent check-in. The "tip" tag is practically equivalent to the timestamp "9999-12-31". This special name works anywhere you can pass a "NAME", such as with <tt>/info</tt> URLs: <pre> http://localhost:8080/info/tip </pre> There are several other special names, but they only work from within a check-out directory because they are relative to the current checked-out version: * "current": the current checked-out version * "next": the youngest child of the current checked-out version |
| ︙ | ︙ | |||
280 281 282 283 284 285 286 | <h2 id="examples">Additional Examples</h2> To view the changes in the most recent check-in prior to the version currently checked out: | | | | | | 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | <h2 id="examples">Additional Examples</h2> To view the changes in the most recent check-in prior to the version currently checked out: <pre> fossil diff --from previous --to current </pre> Suppose you are of the habit of tagging each release with a "release" tag. Then to see everything that has changed on the trunk since the last release: <pre> fossil diff --from release --to trunk </pre> <h2 id="order">Resolution Order</h2> Fossil currently resolves name strings to artifact hashes in the following order: |
| ︙ | ︙ |
Changes to www/childprojects.wiki.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 | at the request of the child. <h2>Creating a Child Project</h2> To create a new child project, first clone the parent. Then make manual SQL changes to the child repository as follows: | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
at the request of the child.
<h2>Creating a Child Project</h2>
To create a new child project, first clone the parent. Then make manual
SQL changes to the child repository as follows:
<verbatim>
UPDATE config SET name='parent-project-code' WHERE name='project-code';
UPDATE config SET name='parent-project-name' WHERE name='project-name';
INSERT INTO config(name,value)
VALUES('project-code',lower(hex(randomblob(20))));
INSERT INTO config(name,value)
VALUES('project-name','CHILD-PROJECT-NAME');
</verbatim>
Modify the CHILD-PROJECT-NAME in the last statement to be the name of
the child project, of course.
The repository is now a separate project, independent from its parent.
Clone the new project to the developers as needed.
|
| ︙ | ︙ |
Changes to www/ckout-workflows.md.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 | ## <a id="mcw"></a> Multiple-Checkout Workflow With Fossil, it is routine to have multiple check-outs from the same repository: | | | | | | | | | | | | | | | | | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
## <a id="mcw"></a> Multiple-Checkout Workflow
With Fossil, it is routine to have multiple check-outs from the same
repository:
fossil clone https://example.com/repo /path/to/repo.fossil
mkdir -p ~/src/my-project/trunk
cd ~/src/my-project/trunk
fossil open /path/to/repo.fossil # implicitly opens “trunk”
mkdir ../release
cd ../release
fossil open /path/to/repo.fossil release
mkdir ../my-other-branch
cd ../my-other-branch
fossil open /path/to/repo.fossil my-other-branch
mkdir ../scratch
cd ../scratch
fossil open /path/to/repo.fossil abcd1234
mkdir ../test
cd ../test
fossil open /path/to/repo.fossil 2019-04-01
Now you have five separate check-out directories: one each for:
* trunk
* the latest tagged public release
* an alternate branch you’re working on
* a “scratch” directory for experiments you don’t want to do in the
other check-out directories; and
|
| ︙ | ︙ | |||
71 72 73 74 75 76 77 | Nevertheless, it is possible to work in a more typical Git sort of style, switching between versions in a single check-out directory. #### <a id="idiomatic"></a> The Idiomatic Fossil Way The most idiomatic way is as follows: | | | | | | | | | < | | | | | | | | | 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 134 135 136 137 138 139 140 141 142 |
Nevertheless, it is possible to work in a more typical Git sort of
style, switching between versions in a single check-out directory.
#### <a id="idiomatic"></a> The Idiomatic Fossil Way
The most idiomatic way is as follows:
fossil clone https://example.com/repo /path/to/repo.fossil
mkdir work-dir
cd work-dir
fossil open /path/to/repo.fossil
...work on trunk...
fossil update my-other-branch
...work on your other branch in the same directory...
Basically, you replace the `cd` commands in the multiple checkouts
workflow above with `fossil up` commands.
#### <a id="open"></a> Opening a Repository by URI
You can instead open the repo’s URI directly:
mkdir work-dir
cd work-dir
fossil open https://example.com/repo
Now you have “trunk” open in `work-dir`, with the repo file stored as
`repo.fossil` in that same directory.
Users of Git may be surprised that it doesn’t create a directory for you
and that you `cd` into it *before* the clone-and-open step, not after.
This is because we’re overloading the “open” command, which already had
the behavior of opening into the current working directory. Changing it
to behave like `git clone` would therefore make the behavior surprising
to Fossil users. (See [our discussions][caod] if you want the full
details.)
#### <a id="clone"></a> Git-Like Clone-and-Open
Fossil also supports a more Git-like alternative:
fossil clone https://fossil-scm.org/fossil
cd fossil
This results in a `fossil.fossil` repo DB file and a `fossil/` working
directory.
Note that our `clone URI` behavior does not commingle the repo and
check-out, solving our major problem with the Git design.
If you want the repo to be named something else, adjust the URL:
fossil clone https://fossil-scm.org/fossil/fsl
That gets you `fsl.fossil` checked out into `fsl/`.
For sites where the repo isn’t served from a subdirectory like this, you
might need another form of the URL. For example, you might have your
repo served from `dev.example.com` and want it cloned as `my-project`:
fossil clone https://dev.example.com/repo/my-project
The `/repo` addition is the key: whatever comes after is used as the
repository name. [See the docs][clone] for more details.
[caod]: https://fossil-scm.org/forum/forumpost/3f143cec74
[clone]: /help?cmd=clone
<div style="height:50em" id="this-space-intentionally-left-blank"></div>
|
Added www/colordiff.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# Colorized Diffs
The oldest and most widely compatible method to get colorized diffs in
Fossil is to use its web UI:
fossil ui --page '/vdiff?from=2024-04-01&to=trunk'
That syntax is admittedly awkward, and it doesn’t work where “from” is
the current checkout. Fortunately, there are many other methods to get
colorized `diff` output from Fossil.
<a id="ui"></a>
## `fossil diff -b`
This produces a graphical diff in HTML format and sends it to the
user’s default web browser for viewing.
<a id="ui"></a>
## `fossil diff -tk`
You may be surprised to learn that the prior feature doesn’t use any of
the skinning or chrome from Fossil UI. This is because it is meant as a
functional replacement for an older method of getting colorized diffs,
“`fossil diff -tk`”. The feature was added after Apple stopped shipping
Tcl/Tk in macOS, and the third-party replacements often failed to work
correctly. It’s useful on other platforms as well.
<a id="git"></a>
## Delegate to Git
It may be considered sacrilege by some, but the most direct method for
those who want Git-like diff behavior may be to delegate diff behavior
to Git:
fossil set --global diff-command 'git diff --no-index'
The flag permits it to diff files that aren’t inside a Git repository.
<a id="diffutils"></a>
## GNU Diffutils
If your system is from 2016 or later, it may include [GNU Diffutils][gd]
3.4 or newer, which lets you say:
fossil set --global diff-command 'diff -dwu --color=always'
You might think you could give `--color=auto`, but that fails with
commands like “`fossil diff | less`” since the pipe turns the output
non-interactive from the perspective of the underlying `diff` instance.
This use of unconditional colorization means you will then have to
remember to add the `-i` option to `fossil diff` commands when producing
`patch(1)` files or piping diff output to another command that doesn’t
understand ANSI escape sequences, such as [`diffstat`][ds].
[ds]: https://invisible-island.net/diffstat/
[gd]: https://www.gnu.org/software/diffutils/
<a id="bat"></a>
## Bat, the Cat with Wings
We can work around the `--color=auto` problem by switching from GNU less
as our pager to [`bat`][bat], as it can detect GNU diff output and
colorize it for you:
fossil set --global diff-command 'diff -dwu --color=auto'
fossil diff | bat
In this author’s experience, that works a lot more reliably than GNU
less’s ANSI color escape code handling, even when you set `LESS=-R` in
your environment.
The reason we don’t leave the `diff-command` unset in this case is that
Fossil produces additional lines at the start which confuse the diff
format detection in `bat`. Forcing output through an external diff
command solves that. It also means that if you forget to pipe the output
through `bat`, you still get colorized output from GNU diff.
[bat]: https://github.com/sharkdp/bat
<a id="colordiff"></a>
## Colordiff
A method that works on systems predating GNU diffutils 3.4 or the
widespread availability of `bat` is to install [`colordiff`][cdurl], as
it is included in [many package systems][cdpkg], including ones for
outdated OSes. That then lets you say:
fossil set --global diff-command 'colordiff -dwu'
The main reason we list this alternative last is that it has the same
limitation of unconditional color as [above](#diffutils).
[cdurl]: https://www.colordiff.org/
[cdpkg]: https://repology.org/project/colordiff/versions
<div style="height:50em" id="this-space-intentionally-left-blank"></div>
|
Changes to www/concepts.wiki.
1 | <title>Fossil Concepts</title> | < | 1 2 3 4 5 6 7 8 | <title>Fossil Concepts</title> <h2>1.0 Introduction</h2> [./index.wiki | Fossil] is a [http://en.wikipedia.org/wiki/Software_configuration_management | software configuration management] system. Fossil is software that is designed to control and track the development of a software project and to record the history |
| ︙ | ︙ | |||
113 114 115 116 117 118 119 | identifier for a blob of data, such as a file. Given any file, it is simple to find the artifact ID for that file. But given an artifact ID, it is computationally intractable to generate a file that will have that same artifact ID. Artifact IDs look something like this: | | | | | | | | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | identifier for a blob of data, such as a file. Given any file, it is simple to find the artifact ID for that file. But given an artifact ID, it is computationally intractable to generate a file that will have that same artifact ID. Artifact IDs look something like this: <pre> 6089f0b563a9db0a6d90682fe47fd7161ff867c8 59712614a1b3ccfd84078a37fa5b606e28434326 19dbf73078be9779edd6a0156195e610f81c94f9 b4104959a67175f02d6b415480be22a239f1f077 997c9d6ae03ad114b2b57f04e9eeef17dcb82788 </pre> When referring to an artifact using Fossil, you can use a unique prefix of the artifact ID that is four characters or longer. This saves a lot of typing. When displaying artifact IDs, Fossil will usually only show the first 10 digits since that is normally enough to uniquely identify a file. |
| ︙ | ︙ | |||
237 238 239 240 241 242 243 | an upgrade. Running "all rebuild" never hurts, so when upgrading it is a good policy to run it even if it is not strictly necessary. To use Fossil, simply type the name of the executable in your shell, followed by one of the various built-in commands and arguments appropriate for that command. For example: | < | < | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | an upgrade. Running "all rebuild" never hurts, so when upgrading it is a good policy to run it even if it is not strictly necessary. To use Fossil, simply type the name of the executable in your shell, followed by one of the various built-in commands and arguments appropriate for that command. For example: <pre>fossil help</pre> In the next section, when we say things like "use the <b>help</b> command" we mean to use the command name "help" as the first token after the name of the Fossil executable, as shown above. <h2 id="workflow">4.0 Workflow</h2> |
| ︙ | ︙ | |||
280 281 282 283 284 285 286 | An interesting feature of Fossil is that it supports both autosync and manual-merge work flows. The default setting for Fossil is to be in autosync mode. You can change the autosync setting or check the current autosync setting using commands like: | | | | | | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | An interesting feature of Fossil is that it supports both autosync and manual-merge work flows. The default setting for Fossil is to be in autosync mode. You can change the autosync setting or check the current autosync setting using commands like: <pre> fossil setting autosync on fossil setting autosync off fossil settings </pre> By default, Fossil runs with autosync mode turned on. The authors finds that projects run more smoothly in autosync mode since autosync helps to prevent pointless forking and merging and helps keeps all collaborators working on exactly the same code rather than on their own personal forks of the code. In the author's view, manual-merge mode should be reserved for disconnected operation. |
| ︙ | ︙ |
Changes to www/containers.md.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 | ## 1. Quick Start Fossil ships a `Dockerfile` at the top of its source tree, [here][DF], which you can build like so: | < | < < | < | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
## 1. Quick Start
Fossil ships a `Dockerfile` at the top of its source tree,
[here][DF], which you can build like so:
$ docker build -t fossil .
If the image built successfully, you can create a container from it and
test that it runs:
$ docker run --name fossil -p 9999:8080/tcp fossil
This shows us remapping the internal TCP listening port as 9999 on the
host. This feature of OCI runtimes means there’s little point to using
the “`fossil server --port`” feature inside the container. We can let
Fossil default to 8080 internally, then remap it to wherever we want it
on the host instead.
|
| ︙ | ︙ | |||
42 43 44 45 46 47 48 | fresh container based on that image. You can pass extra arguments to the first command via the Makefile’s `DBFLAGS` variable and to the second with the `DCFLAGS` variable. (DB is short for “`docker build`”, and DC is short for “`docker create`”, a sub-step of the “run” target.) To get the custom port setting as in second command above, say: | < | < | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
fresh container based on that image. You can pass extra arguments to the
first command via the Makefile’s `DBFLAGS` variable and to the second
with the `DCFLAGS` variable. (DB is short for “`docker build`”, and DC
is short for “`docker create`”, a sub-step of the “run” target.)
To get the custom port setting as in
second command above, say:
$ make container-run DCFLAGS='-p 9999:8080/tcp'
Contrast the raw “`docker`” commands above, which create an
_unversioned_ image called `fossil:latest` and from that a container
simply called `fossil`. The unversioned names are more convenient for
interactive use, while the versioned ones are good for CI/CD type
applications since they avoid a conflict with past versions; it lets you
keep old containers around for quick roll-backs while replacing them
|
| ︙ | ︙ | |||
79 80 81 82 83 84 85 | ### <a id="repo-inside"></a> 2.1 Storing the Repo Inside the Container The simplest method is to stop the container if it was running, then say: | < | | | < | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
### <a id="repo-inside"></a> 2.1 Storing the Repo Inside the Container
The simplest method is to stop the container if it was running, then
say:
$ docker cp /path/to/my-project.fossil fossil:/museum/repo.fossil
$ docker start fossil
$ docker exec fossil chown -R 499 /museum
That copies the local Fossil repo into the container where the server
expects to find it, so that the “start” command causes it to serve from
that copied-in file instead. Since it lives atop the immutable base
layers, it persists as part of the container proper, surviving restarts.
Notice that the copy command changes the name of the repository
|
| ︙ | ︙ | |||
118 119 120 121 122 123 124 | The simple storage method above has a problem: containers are designed to be killed off at the slightest cause, rebuilt, and redeployed. If you do that with the repo inside the container, it gets destroyed, too. The solution is to replace the “run” command above with the following: | < | | | | | < | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
The simple storage method above has a problem: containers are
designed to be killed off at the slightest cause, rebuilt, and
redeployed. If you do that with the repo inside the container, it gets
destroyed, too. The solution is to replace the “run” command above with
the following:
$ docker run \
--publish 9999:8080 \
--name fossil-bind-mount \
--volume ~/museum:/museum \
fossil
Because this bind mount maps a host-side directory (`~/museum`) into the
container, you don’t need to `docker cp` the repo into the container at
all. It still expects to find the repository as `repo.fossil` under that
directory, but now both the host and the container can see that repo DB.
Instead of a bind mount, you could instead set up a separate
|
| ︙ | ︙ | |||
149 150 151 152 153 154 155 | #### 2.2.1 <a id="wal-mode"></a>WAL Mode Interactions You might be aware that OCI containers allow mapping a single file into the repository rather than a whole directory. Since Fossil repositories are specially-formatted SQLite databases, you might be wondering why we don’t say things like: | < | < | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
#### 2.2.1 <a id="wal-mode"></a>WAL Mode Interactions
You might be aware that OCI containers allow mapping a single file into
the repository rather than a whole directory. Since Fossil repositories
are specially-formatted SQLite databases, you might be wondering why we
don’t say things like:
--volume ~/museum/my-project.fossil:/museum/repo.fossil
That lets us have a convenient file name for the project outside the
container while letting the configuration inside the container refer to
the generic “`/museum/repo.fossil`” name. Why should we have to name
the repo generically on the outside merely to placate the container?
The reason is, you might be serving that repo with [WAL mode][wal]
|
| ︙ | ︙ | |||
290 291 292 293 294 295 296 |
granularity beyond the classic Unix ones inside the container, so we
drop root’s ability to change them.
All together, we recommend adding the following options to your
“`docker run`” commands, as well as to any “`docker create`” command
that will be followed by “`docker start`”:
| < | | | | | | | | | < | 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
granularity beyond the classic Unix ones inside the container, so we
drop root’s ability to change them.
All together, we recommend adding the following options to your
“`docker run`” commands, as well as to any “`docker create`” command
that will be followed by “`docker start`”:
--cap-drop AUDIT_WRITE \
--cap-drop CHOWN \
--cap-drop FSETID \
--cap-drop KILL \
--cap-drop MKNOD \
--cap-drop NET_BIND_SERVICE \
--cap-drop NET_RAW \
--cap-drop SETFCAP \
--cap-drop SETPCAP
In the next section, we’ll show a case where you create a container
without ever running it, making these options pointless.
[backoffice]: ./backoffice.md
[defcap]: https://docs.docker.com/engine/security/#linux-kernel-capabilities
[capchg]: https://stackoverflow.com/a/45752205/142454
|
| ︙ | ︙ | |||
324 325 326 327 328 329 330 | A secondary benefit falls out of this process for free: it’s arguably the easiest way to build a purely static Fossil binary for Linux. Most modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s back-to-basics nature makes static builds work the way they used to, back in the day. If that’s all you’re after, you can do so as easily as this: | < | | | | < < | < < | < | 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 |
A secondary benefit falls out of this process for free: it’s arguably
the easiest way to build a purely static Fossil binary for Linux. Most
modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s
back-to-basics nature makes static builds work the way they used to,
back in the day. If that’s all you’re after, you can do so as easily as
this:
$ docker build -t fossil .
$ docker create --name fossil-static-tmp fossil
$ docker cp fossil-static-tmp:/bin/fossil .
$ docker container rm fossil-static-tmp
The result is six or seven megs, depending on the CPU architecture you
build for. It’s built stripped.
[lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead
## 5. <a id="custom" name="args"></a>Customization Points
### <a id="pkg-vers"></a> 5.1 Fossil Version
The default version of Fossil fetched in the build is the version in the
checkout directory at the time you run it. You could override it to get
a release build like so:
$ docker build -t fossil --build-arg FSLVER=version-2.20 .
Or equivalently, using Fossil’s `Makefile` convenience target:
$ make container-image DBFLAGS='--build-arg FSLVER=version-2.20'
While you could instead use the generic
“`release`” tag here, it’s better to use a specific version number
since container builders cache downloaded files, hoping to
reuse them across builds. If you ask for “`release`” before a new
version is tagged and then immediately after, you might expect to get
two different tarballs, but because the underlying source tarball URL
|
| ︙ | ︙ | |||
382 383 384 385 386 387 388 | leaving those below it for system users like this Fossil daemon owner. Since it’s typical for these to start at 0 and go upward, we started at 500 and went *down* one instead to reduce the chance of a conflict to as close to zero as we can manage. To change it to something else, say: | < | < < < < | | < | | | | 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 |
leaving those below it for system users like this Fossil daemon owner.
Since it’s typical for these to start at 0 and go upward, we started at
500 and went *down* one instead to reduce the chance of a conflict to as
close to zero as we can manage.
To change it to something else, say:
$ make container-image DBFLAGS='--build-arg UID=501'
This is particularly useful if you’re putting your repository on a
separate volume since the IDs “leak” out into the host environment via
file permissions. You may therefore wish them to mean something on both
sides of the container barrier rather than have “499” appear on the host
in “`ls -l`” output.
### 5.3 <a id="cengine"></a>Container Engine
Although the Fossil container build system defaults to Docker, we allow
for use of any OCI container system that implements the same interfaces.
We go into more details about this [below](#light), but
for now, it suffices to point out that you can switch to Podman while
using our `Makefile` convenience targets unchanged by saying:
$ make CENGINE=podman container-run
### 5.4 <a id="config"></a>Fossil Configuration Options
You can use this same mechanism to enable non-default Fossil
configuration options in your build. For instance, to turn on
the JSON API and the TH1 docs extension:
$ make container-image \
DBFLAGS='--build-arg FSLCFG="--json --with-th1-docs"'
If you also wanted [the Tcl evaluation extension](./th1.md#tclEval),
that brings us to [the next point](#run).
### 5.5 <a id="run"></a>Elaborating the Run Layer
If you want a basic shell environment for temporary debugging of the
running container, that’s easily added. Simply change this line in the
`Dockerfile`…
FROM scratch AS run
…to this:
FROM busybox AS run
Rebuild and redeploy to give your Fossil container a [BusyBox]-based
shell environment that you can get into via:
$ docker exec -it -u fossil $(make container-version) sh
That command assumes you built it via “`make container`” and are
therefore using its versioning scheme.
You will likely want to remove the `PATH` override in the “RUN” stage
when doing this since it’s written for the case where everything is in
`/bin`, and that will no longer be the case with a more full-featured
|
| ︙ | ︙ | |||
461 462 463 464 465 466 467 | Let’s say the extension is written in Python. Because this is one of the most popular programming languages in the world, we have many options for achieving this. For instance, there is a whole class of “[distroless]” images that will do this efficiently by changing “`STAGE 2`” in the `Dockefile` to this: | < < < < | 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 468 469 470 471 472 473 |
Let’s say the extension is written in Python. Because this is one of the
most popular programming languages in the world, we have many options
for achieving this. For instance, there is a whole class of
“[distroless]” images that will do this efficiently by changing
“`STAGE 2`” in the `Dockefile` to this:
## ---------------------------------------------------------------------
## STAGE 2: Pare that back to the bare essentials, plus Python.
## ---------------------------------------------------------------------
FROM cgr.dev/chainguard/python:latest
USER root
ARG UID=499
ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
COPY --from=builder /tmp/fossil /bin/
COPY --from=builder /bin/busybox.static /bin/busybox
RUN [ "/bin/busybox", "--install", "/bin" ]
RUN set -x \
&& echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
&& echo "fossil:x:${UID}:fossil" >> /etc/group \
&& install -d -m 700 -o fossil -g fossil log museum
You will also have to add `busybox-static` to the APK package list in
STAGE 1 for the `RUN` script at the end of that stage to work, since the
[Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to
install root-level binaries is why we change `USER` temporarily here.
Build it and test that it works like so:
$ make container-run &&
docker exec -i $(make container-version) python --version
3.11.2
The compensation for the hassle of using Chainguard over something more
general purpose like changing the `run` layer to Alpine and then adding
a “`apk add python`” command to the `Dockerfile`
is huge: we no longer leave a package manager sitting around inside the
container, waiting for some malefactor to figure out how to abuse it.
|
| ︙ | ︙ | |||
553 554 555 556 557 558 559 | default under the theory that you don’t want those services to run until you’ve logged into the GUI as that user. If you find yourself running into this, [enable linger mode](https://www.freedesktop.org/software/systemd/man/loginctl.html).) so I was able to create a unit file called `~/.local/share/systemd/user/alert-sender@.service` with these contents: | < < < < | 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 |
default under the theory that you don’t want those services to run until
you’ve logged into the GUI as that user. If you find yourself running
into this, [enable linger
mode](https://www.freedesktop.org/software/systemd/man/loginctl.html).)
so I was able to create a unit file called
`~/.local/share/systemd/user/alert-sender@.service` with these contents:
[Unit]
Description=Fossil email alert sender for %I
[Service]
WorkingDirectory=/home/fossil/museum
ExecStart=/home/fossil/bin/alert-sender %I/mail.db
Restart=always
RestartSec=3
[Install]
WantedBy=default.target
I was then able to enable email alert forwarding for select repositories
after configuring them per [the docs](./alerts.md) by saying:
$ systemctl --user daemon-reload
$ systemctl --user enable alert-sender@myproject
$ systemctl --user start alert-sender@myproject
Because this is a parameterized script and we’ve set our repository
paths predictably, you can do this for as many repositories as you need
to by passing their names after the “`@`” sign in the commands above.
## 6. <a id="light"></a>Lightweight Alternatives to Docker
|
| ︙ | ︙ | |||
604 605 606 607 608 609 610 | leaving the benefits of containerization to those with bigger budgets. For the sake of simple examples in this section, we’ll assume you’re integrating Fossil into a larger web site, such as with our [Debian + nginx + TLS][DNT] plan. This is why all of the examples below create the container with this option: | < | < | 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 |
leaving the benefits of containerization to those with bigger budgets.
For the sake of simple examples in this section, we’ll assume you’re
integrating Fossil into a larger web site, such as with our [Debian +
nginx + TLS][DNT] plan. This is why all of the examples below create
the container with this option:
--publish 127.0.0.1:9999:8080
The assumption is that there’s a reverse proxy running somewhere that
redirects public web hits to localhost port 9999, which in turn goes to
port 8080 inside the container. This use of port
publishing effectively replaces the use of the
“`fossil server --localhost`” option.
|
| ︙ | ︙ | |||
676 677 678 679 680 681 682 | On Ubuntu 22.04, the installation size is about 38 MiB, roughly a tenth the size of Docker Engine. For our purposes here, the only thing that changes relative to the examples at the top of this document are the initial command: | < | | < < | | | | | | | | | | | < < | < < | < < | | | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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 |
On Ubuntu 22.04, the installation size is about 38 MiB, roughly a
tenth the size of Docker Engine.
For our purposes here, the only thing that changes relative to the
examples at the top of this document are the initial command:
$ podman build -t fossil .
$ podman run --name fossil -p 9999:8080/tcp fossil
Your Linux package repo may have a `podman-docker` package which
provides a “`docker`” script that calls “`podman`” for you, eliminating
even the command name difference. With that installed, the `make`
commands above will work with Podman as-is.
The only difference that matters here is that Podman doesn’t have the
same [default Linux kernel capability set](#caps) as Docker, which
affects the `--cap-drop` flags recommended above to:
$ podman create \
--name fossil \
--cap-drop CHOWN \
--cap-drop FSETID \
--cap-drop KILL \
--cap-drop NET_BIND_SERVICE \
--cap-drop SETFCAP \
--cap-drop SETPCAP \
--publish 127.0.0.1:9999:8080 \
localhost/fossil
$ podman start fossil
[pmmac]: https://podman.io/getting-started/installation.html#macos
[pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md
[Podman]: https://podman.io/
[rl]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md
[whatis]: https://podman.io/whatis.html
### 6.3 <a id="nspawn"></a>`systemd-container`
If even the Podman stack is too big for you, the next-best option I’m
aware of is the `systemd-container` infrastructure on modern Linuxes,
available since version 239 or so. Its runtime tooling requires only
about 1.4 MiB of disk space:
$ sudo apt install systemd-container btrfs-tools
That command assumes the primary test environment for
this guide, Ubuntu 22.04 LTS with `systemd` 249. For best
results, `/var/lib/machines` should be a btrfs volume, because
[`$REASONS`][mcfad]. For CentOS Stream 9 and other Red Hattish
systems, you will have to make several adjustments, which we’ve
collected [below](#nspawn-centos) to keep these examples clear.
We’ll assume your Fossil repository stores something called
“`myproject`” within `~/museum/myproject/repo.fossil`, named according
to the reasons given [above](#repo-inside). We’ll make consistent use of
this naming scheme in the examples below so that you will be able to
replace the “`myproject`” element of the various file and path names.
If you use [the stock `Dockerfile`][DF] to generate your
base image, `nspawn` won’t recognize it as containing an OS unless you
change the “`FROM scratch AS os`” line at the top of the second stage
to something like this:
FROM gcr.io/distroless/static-debian11 AS os
Using that as a base image provides all the files `nspawn` checks for to
determine whether the container is sufficiently close to a Linux VM for
the following step to proceed:
$ make container
$ docker container export $(make container-version) |
machinectl import-tar - myproject
Next, create `/etc/systemd/nspawn/myproject.nspawn`:
----
[Exec]
WorkingDirectory=/
Parameters=bin/fossil server \
--baseurl https://example.com/myproject \
--create \
--jsmode bundled \
--localhost \
--port 9000 \
--scgi \
--user admin \
museum/repo.fossil
DropCapability= \
CAP_AUDIT_WRITE \
CAP_CHOWN \
CAP_FSETID \
CAP_KILL \
CAP_MKNOD \
CAP_NET_BIND_SERVICE \
CAP_NET_RAW \
CAP_SETFCAP \
CAP_SETPCAP
ProcessTwo=yes
LinkJournal=no
Timezone=no
[Files]
Bind=/home/fossil/museum/myproject:/museum
[Network]
VirtualEthernet=no
----
If you recognize most of that from the `Dockerfile` discussion above,
congratulations, you’ve been paying attention. The rest should also
be clear from context.
|
| ︙ | ︙ | |||
817 818 819 820 821 822 823 |
on the host for the reasons given [above](#bind-mount).
That being done, we also need a generic `systemd` unit file called
`/etc/systemd/system/fossil@.service`, containing:
----
| < | | | | | | | | < < | | < < | | | | | | | < < | < < | < < | | | < | 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 |
on the host for the reasons given [above](#bind-mount).
That being done, we also need a generic `systemd` unit file called
`/etc/systemd/system/fossil@.service`, containing:
----
[Unit]
Description=Fossil %i Repo Service
Wants=modprobe@tun.service modprobe@loop.service
After=network.target systemd-resolved.service modprobe@tun.service modprobe@loop.service
[Service]
ExecStart=systemd-nspawn --settings=override --read-only --machine=%i bin/fossil
[Install]
WantedBy=multi-user.target
----
You shouldn’t have to change any of this because we’ve given the
`--setting=override` flag, meaning any setting in the nspawn file
overrides the setting passed to `systemd-nspawn`. This arrangement
not only keeps the unit file simple, it allows multiple services to
share the base configuration, varying on a per-repo level through
adjustments to their individual `*.nspawn` files.
You may then start the service in the normal way:
$ sudo systemctl enable fossil@myproject
$ sudo systemctl start fossil@myproject
You should then find it running on localhost port 9000 per the nspawn
configuration file above, suitable for proxying Fossil out to the
public using nginx via SCGI. If you aren’t using a front-end proxy
and want Fossil exposed to the world via HTTPS, you might say this instead in
the `*.nspawn` file:
Parameters=bin/fossil server \
--cert /path/to/cert.pem \
--create \
--jsmode bundled \
--port 443 \
--user admin \
museum/repo.fossil
You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability
to allow Fossil to bind to this low-numbered port.
We use the `systemd` template file feature to allow multiple Fossil
servers running on a single machine, each on a different TCP port,
as when proxying them out as subdirectories of a larger site.
To add another project, you must first clone the base “machine” layer:
$ sudo machinectl clone myproject otherthing
That will not only create a clone of `/var/lib/machines/myproject`
as `../otherthing`, it will create a matching `otherthing.nspawn` file for you
as a copy of the first one. Adjust its contents to suit, then enable
and start it as above.
[mcfad]: https://www.freedesktop.org/software/systemd/man/machinectl.html#Files%20and%20Directories
### 6.3.1 <a id="nspawn-rhel"></a>Getting It Working on a RHEL Clone
The biggest difference between doing this on OSes like CentOS versus
Ubuntu is that RHEL (thus also its clones) doesn’t ship btrfs in
its kernel, thus ships with no package repositories containing `mkfs.btrfs`, which
[`machinectl`][mctl] depends on for achieving its various purposes.
Fortunately, there are workarounds.
First, the `apt install` command above becomes:
$ sudo dnf install systemd-container
Second, you have to hack around the lack of `machinectl import-tar`:
$ rootfs=/var/lib/machines/fossil
$ sudo mkdir -p $rootfs
$ docker container export fossil | sudo tar -xf -C $rootfs -
The parent directory path in the `rootfs` variable is important,
because although we aren’t able to use `machinectl` on such systems, the
`systemd-nspawn` developers assume you’re using them together; when you give
`--machine`, it assumes the `machinectl` directory scheme. You could
instead use `--directory`, allowing you to store the rootfs wherever
you like, but why make things difficult? It’s a perfectly sensible
|
| ︙ | ︙ |
Changes to www/contribute.wiki.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 | definition of that term is up to the project leader. <h2>2.0 Submitting Patches</h2> Suggested changes or bug fixes can be submitted by creating a patch against the current source tree: | | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | definition of that term is up to the project leader. <h2>2.0 Submitting Patches</h2> Suggested changes or bug fixes can be submitted by creating a patch against the current source tree: <pre>fossil diff -i > my-change.patch</pre> Alternatively, you can create a binary patch: <pre>fossil patch create my-change.db</pre> Post patches to [https://fossil-scm.org/forum | the forum] or email them to <a href="mailto:drh@sqlite.org">drh@sqlite.org</a>. Be sure to describe in detail what the patch does and which version of Fossil it is written against. It's best to make patches against tip-of-trunk rather than against past releases. |
| ︙ | ︙ |
Changes to www/custom_ticket.wiki.
1 | <title>Customizing The Ticket System</title> | | < > < < > < < | > | | | | | | > | | > | | | > | | > | | | | > | < < | | | | | | | | > | < < | > > | > | | | > > | > > | > | < | | | < | > | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
<title>Customizing The Ticket System</title>
<h2>Introduction</h2>
This guide will explain how to add the "assigned_to" and "submitted_by" fields
to the ticket system in Fossil, as well as making the system more useful. You
must have "admin" access to the repository to implement these instructions.
<h2>First modify the TICKET table</h2>
Click on the "Admin" menu, then "Tickets", then "Table". After the other fields
and before the final ")", insert:
<pre>
,
assigned_to TEXT,
opened_by TEXT
</pre>
And "Apply Changes". You have just added two more fields to the ticket
database! NOTE: I won't tell you to "Apply Changes" after each step from here
on out. Now, how do you use these fields?
<h2>Next add assignees</h2>
Back to the "Tickets" admin page, and click "Common". Add something like this:
<pre>
set assigned_choices {
unassigned
tom
dick
harriet
}
</pre>
Obviously, choose names corresponding to the logins on your system. The
'unassigned' entry is important, as it prevents you from having a NULL in that
field (which causes problems later when editing).
<h2>Now modify the 'new ticket' page</h2>
Back to the "Tickets" admin page, and click "New Ticket Page". This is a little
more tricky. Edit the top part:
<verbatim>
if {[info exists submit]} {
set status Open
set opened_by $login
set assigned_to "unassigned"
submit_ticket
}
</verbatim>
Note the "set opened_by" bit -- that will automatically set the "opened_by"
field to the login name of the bug reporter. Now, skip to the part with "EMail"
and modify it like so:
<verbatim>
<th1>enable_output expr { "$login" eq "anonymous"}</th1>
<tr>
<td align="right">
EMail:
<input type="text" name="private_contact" value="$<private_contact>" size="30">
</td>
<td>
<u>Not publicly visible</u>. Used by developers to contact you with questions.
</td>
</tr>
<th1>enable_output 1</th1>
</verbatim>
This bit of code will get rid of the "email" field entry for logged-in users.
Since we know the user's information, we don't have to ask for it. NOTE: it
might be good to automatically scoop up the user's email and put it here.
You might also want to enable people to actually assign the ticket to a specific
person during creation. For this to work, you need to add the code
for "assigned_to" as shown below under the heading "Modify the 'edit ticket' page".
This will give you an additional combobox where you can choose a person during
ticket creation.
<h2>Modify the 'view ticket' page</h2>
Look for the text "Contact:" (about halfway through). Then insert these lines
after the closing tr tag and before the "enable_output" line:
<verbatim>
<td align="right">Assigned to:</td><td bgcolor="#d0d0d0">
$<assigned_to>
</td>
<td align="right">Opened by:</td><td bgcolor="#d0d0d0">
$<opened_by>
</td>
</verbatim>
This will add a row which displays these two fields, in the event the user has
<a href="./caps/ref.html#w">ticket "edit" capability</a>.
<h2>Modify the 'edit ticket' page</h2>
Before the "Severity:" line, add this:
<verbatim>
<tr>
<td align="right">Assigned to:</td>
<td>
<th1>combobox assigned_to $assigned_choices 1</th1>
</td>
</tr>
</verbatim>
That will give you a drop-down list of assignees. The first argument to the TH1
command 'combobox' is the database field which the combobox is associated to.
The next argument is the list of choices you want to show in the combobox (and
that you specified in the second step above. The last argument should be 1 for a
true combobox (see the <a href="th1.md#combobox">TH1 documentation</a> for
details).
Now, similar to the previous
section, look for "Contact:" and add this:
<verbatim>
<tr>
<td align="right">Reported by:</td>
<td>
<input type="text" name="opened_by" size="40" value="$<opened_by>">
</td>
</tr>
</verbatim>
<h2>What next?</h2>
Now you can add custom reports which select based on the person to whom the
ticket is assigned. For example, an "Assigned to me" report could be:
<verbatim>
SELECT
CASE WHEN status IN ('Open','Verified') THEN '#f2dcdc'
WHEN status='Review' THEN '#e8e8e8'
WHEN status='Fixed' THEN '#cfe8bd'
WHEN status='Tested' THEN '#bde5d6'
WHEN status='Deferred' THEN '#cacae5'
ELSE '#c8c8c8' END AS 'bgcolor',
substr(tkt_uuid,1,10) AS '#',
datetime(tkt_mtime) AS 'mtime',
type,
status,
subsystem,
title
FROM ticket
WHERE assigned_to=user()
</verbatim>
|
Changes to www/customgraph.md.
1 2 3 4 5 6 7 8 9 10 11 | # Customizing the Timeline Graph Beginning with version 1.33, Fossil gives users and skin authors significantly more control over the look and feel of the timeline graph. ## <a id="basic-style"></a>Basic Style Options Fossil includes several options for changing the graph's style without having to delve into CSS. These can be found in the details.txt file of your skin or under Admin/Skins/Details in the web UI. | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# Customizing the Timeline Graph
Beginning with version 1.33, Fossil gives users and skin authors significantly
more control over the look and feel of the timeline graph.
## <a id="basic-style"></a>Basic Style Options
Fossil includes several options for changing the graph's style without having
to delve into CSS. These can be found in the details.txt file of your skin or
under Admin/Skins/Details in the web UI.
* **`timeline-arrowheads`**
Set this to `0` to hide arrowheads on primary child lines.
* **`timeline-circle-nodes`**
Set this to `1` to make check-in nodes circular instead of square.
* **`timeline-color-graph-lines`**
Set this to `1` to colorize primary child lines.
* **`white-foreground`**
Set this to `1` if your skin uses white (or any light color) text.
This tells Fossil to generate darker background colors for branches.
## <a id="adv-style"></a>Advanced Styling
|
| ︙ | ︙ | |||
40 41 42 43 44 45 46 | latter, less obvious type. ## <a id="pos-elems"></a>Positioning Elements These elements aren't intended to be seen. They're only used to help position the graph and its visible elements. | | | | | | | | | | | | 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 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
latter, less obvious type.
## <a id="pos-elems"></a>Positioning Elements
These elements aren't intended to be seen. They're only used to help position
the graph and its visible elements.
* <a id="tl-canvas"></a>**`.tl-canvas`**
Set the left and right margins on this class to give the desired amount
of space between the graph and its adjacent columns in the timeline.
**Additional Classes**
* `.sel`: See [`.tl-node`](#tl-node) for more information.
* <a id="tl-rail"></a>**`.tl-rail`**
Think of rails as invisible vertical lines on which check-in nodes are
placed. The more simultaneous branches in a graph, the more rails required
to draw it. Setting the `width` property on this class determines the
maximum spacing between rails. This spacing is automatically reduced as
the number of rails increases. If you change the `width` of `.tl-node`
elements, you'll probably need to change this value, too.
* <a id="tl-mergeoffset"></a>**`.tl-mergeoffset`**
A merge line often runs vertically right beside a primary child line. This
class's `width` property specifies the maximum spacing between the two.
Setting this value to `0` will eliminate the vertical merge lines.
Instead, the merge arrow will extend directly off the primary child line.
As with rail spacing, this is also adjusted automatically as needed.
* <a id="tl-nodemark"></a>**`.tl-nodemark`**
In the timeline table, the second cell in each check-in row contains an
invisible div with this class. These divs are used to determine the
vertical position of the nodes. By setting the `margin-top` property,
you can adjust this position.
## <a id="vis-elems"></a>Visible Elements
These are the elements you can actually see on the timeline graph: the nodes,
arrows, and lines. Each of these elements may also have additional classes
attached to them, depending on their context.
* <a id="tl-node"></a>**`.tl-node`**
A node exists for each check-in in the timeline.
**Additional Classes**
* `.leaf`: Specifies that the check-in is a leaf (i.e. that it has no
children in the same branch).
* `.merge`: Specifies that the check-in contains a merge.
* `.sel`: When the user clicks a node to designate it as the beginning
of a diff, this class is added to both the node itself and the
[`.tl-canvas`](#tl-canvas) element. The class is removed from both
elements when the node is clicked again.
* <a id="tl-arrow"></a>**`.tl-arrow`**
Arrows point from parent nodes to their children. Technically, this
class is just for the arrowhead. The rest of the arrow is composed
of [`.tl-line`](#tl-line) elements.
There are six additional classes that are used to distinguish the different
types of arrows. However, only these combinations are valid:
* `.u`: Up arrow that points to a child from its primary parent.
* `.u.sm`: Smaller up arrow, used when there is limited space between
parent and child nodes.
* `.merge.l` or `.merge.r`: Merge arrow pointing either to the left or
right.
* `.warp`: A timewarped arrow (always points to the right), used when a
misconfigured clock makes a check-in appear to have occurred before its
parent ([example](https://www.sqlite.org/src/timeline?c=2010-09-29&nd)).
* <a id="tl-line"></a>**`.tl-line`**
Along with arrows, lines connect parent and child nodes. Line thickness is
determined by the `width` property, regardless of whether the line is
horizontal or vertical. You can also use borders to create special line
styles. Here's a CSS snippet for making dotted merge lines:
.tl-line.merge {
width: 0;
background: transparent;
border: 0 dotted #000;
}
.tl-line.merge.h {
border-top-width: 1px;
}
.tl-line.merge.v {
border-left-width: 1px;
}
**Additional Classes**
* `.merge`: A merge line.
* `.h` or `.v`: Horizontal or vertical.
* `.warp`: A timewarped line.
|
| ︙ | ︙ |
Changes to www/customskin.md.
| ︙ | ︙ | |||
55 56 57 58 59 60 61 | When cloning a repository, the skin of the new repository is initialized to the skin of the repository from which it was cloned. # Structure Of A Fossil Web Page Every HTML page generated by Fossil has the same basic structure: | < < | | < | | < | < < < < | < < < | | | | | | | | | > > > > > | | | | > > | | | | | > > < < | > > > > > > > > > > | | | > > > | > < < < < > > | | | | | | | | | | | | | | | | 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 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
When cloning a repository, the skin of the new repository is initialized to
the skin of the repository from which it was cloned.
# Structure Of A Fossil Web Page
Every HTML page generated by Fossil has the same basic structure:
| Fossil-Generated HTML Header |
| Skin Header |
| Fossil-Generated Content |
| Skin Footer |
| Fossil-Generated HTML Footer |
By default, Fossil starts every generated HTML page with this:
<html>
<head>
<base href="...">
<meta http-equiv="Content-Security-Policy" content="....">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>....</title>
<link rel="stylesheet" href="..." type="text/css">
</head>
<body class="FEATURE">
Fossil used to require a static version of this in every skin’s Header
area, but over time, we have found good cause to generate multiple
elements at runtime.
One such is the `FEATURE` element, being either the top-level HTTP
request routing element (e.g. `doc`) or an aggregate feature class that
groups multiple routes under a single name. A prime example is `forum`,
which groups the `/forummain`, `/forumpost`, and `/forume2` routes,
allowing per-feature CSS. For instance, to style `<blockquote>` tags
specially for forum posts written in Markdown, leaving all other block
quotes alone, you could say:
body.forum div.markdown blockquote {
margin-left: 10px;
}
You can [override this generated HTML header](#override) by including a
“`<body>`” tag somewhere in the Header area of the skin, but it is
almost always best to limit a custom skin’s Header section to something
like this:
<div class="sidebar" id="version-2.24">Prior to Fossil 2.24, we used
generic `<div>` elements to mark up these sections of the header, but we
switched to these semantic tag names to give browser accessibility
features more freedom to do intelligent things with the page content.
Those who made custom skins based on the old way of doing things will
need to track this change when upgrading, else the corresponding CSS
will mistarget the page header elements. Also, if you’re using Fossil’s
chat feature, failing to track this change will cause it to miscalculate
the message area size, resulting in double scrollbars. Simply diffing
your custom header in the skin editor against the stock version should
be sufficient to show what you need to change.</div>
<header>
...
</header>
<nav class="mainmenu" title="Main Menu">
...
</nav>
<nav id="hbdrop" class="hbdrop" title="sitemap"></nav>
See the stock skins’ headers for ideas of what to put in place of the
ellipses.
The Fossil-generated Content section immediately follows this Header.
It will look like this:
<div class="content">
... Fossil-generated content here ...
</div>
After the Content is the custom Skin Footer section which should
follow this template:
<footer>
... skin-specific stuff here ...
</footer>
As with the `<header>` change called out above, this, too, is a breaking
change in Fossil 2.24.
Finally, Fossil always adds its own footer (unless overridden)
to close out the generated HTML:
</body>
</html>
## <a id="mainmenu"></a>Changing the Main Menu Contents
The actual text content of the skin’s main menu is not
part of the skin proper if you’re using one of the stock skins.
If you look at the Header section of the skin, you’ll find a
`<div class="mainmenu">` element whose contents are set by a short
[TH1](./th1.md) script from the contents of the **Main Menu** section of
the Setup → Configuration screen.
This feature allows the main menu contents to stay the same across
different skins, so you no longer have to reapply menu customizations
|
| ︙ | ︙ | |||
158 159 160 161 162 163 164 | Notice that the `<html>`, `<head>`, and opening `<body>` elements at the beginning of the document, and the closing `</body>` and `</html>` elements at the end are automatically generated by Fossil. This is recommended. However, for maximum design flexibility, Fossil allows those elements to be | | | | | | | | | | | | | | | | | | | | < < | | | | 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 193 194 195 196 197 198 199 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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | Notice that the `<html>`, `<head>`, and opening `<body>` elements at the beginning of the document, and the closing `</body>` and `</html>` elements at the end are automatically generated by Fossil. This is recommended. However, for maximum design flexibility, Fossil allows those elements to be supplied as part of the configurable Skin Header and Skin Footer. If the Skin Header contains the text "`<body`", then Fossil assumes that the Skin Header and Skin Footer will handle all of the `<html>`, `<head>`, and `<body>` text itself, and the Fossil-generated header and footer will be blank. When overriding the HTML Header in this way, you will probably want to use some of the [TH1 variables documented below](#vars) such as `$stylesheet_url` to avoid hand-writing code that Fossil can generate for you. # Designing, Debugging, and Installing A Custom Skin It is possible to develop a new skin from scratch. But a better and easier approach is to use one of the existing built-in skins as a baseline and make incremental modifications, testing after each step, to obtain the desired result. The skin is controlled by five files: <dl> <dt><b>css.txt</b></dt> <dd>The css.txt file is the text of the CSS for Fossil. Fossil might add additional CSS elements after the css.txt file, if it sees that the css.txt omits some CSS components that Fossil needs. But for the most part, the content of the css.txt is the CSS for the page.</dd> <dt><b>details.txt</b><dt> <dd>The details.txt file is short list of settings that control the look and feel, mostly of the timeline. The default details.txt file looks like this: <pre> pikchr-background: "" pikchr-fontscale: "" pikchr-foreground: "" pikchr-scale: "" timeline-arrowheads: 1 timeline-circle-nodes: 1 timeline-color-graph-lines: 1 white-foreground: 0 </pre> The three "timeline-" settings in details.txt control the appearance of certain aspects of the timeline graph. The number on the right is a boolean - "1" to activate the feature and "0" to disable it. The "white-foreground:" setting should be set to "1" if the page color has light-color text on a darker background, and "0" if the page has dark text on a light-colored background. If the "pikchr-foreground" setting is defined and is not an empty string then it specifies a foreground color to use for [pikchr diagrams](./pikchr.md). The default pikchr foreground color is black, or white if the "white-foreground" boolean is set. The "pikchr-background" settings does the same for the pikchr diagram background color. If the "pikchr-fontscale" and "pikchr-scale" values are not empty strings, then they should be floating point values (close to 1.0) that specify relative scaling of the fonts in pikchr diagrams and other elements of the diagrams, respectively. </dd> <dt><b>footer.txt</b> and <b>header.txt</b></dt> <dd>The footer.txt and header.txt files contain the Skin Footer and Skin Header respectively. Of these, the Skin Header is the most important, as it contains the markup used to generate the banner and menu bar for each page. Both the footer.txt and header.txt file are [processed using TH1](#headfoot) prior to being output as part of the overall web page.</dd> <dt><b>js.txt</b></dt> <dd>The js.txt file is optional. It is intended to be javascript. The complete text of this javascript might be inserted into the Skin Footer, after being processed using TH1, using code like the following in the "footer.txt" file: <pre> <script nonce="$nonce"> <th1>styleScript</th1> </script> </pre> The js.txt file was originally used to insert javascript that controls the hamburger menu in the default skin. More recently, the javascript for the hamburger menu was moved into a separate built-in file. Skins that use the hamburger menu typically cause the javascript to be loaded by including the following TH1 code in the "header.txt" file: <pre> <th1>builtin_request_js hbmenu.js</th1> </pre> The difference between `styleScript` and `builtin_request_js` is that the `styleScript` command interprets the file using TH1 and injects the content directly into the output stream, whereas the `builtin_request_js` command inserts the Javascript verbatim and does so at some unspecified future time down inside the Fossil-generated footer. You can use either approach in custom skins that you create. Note that the "js.txt" file is *not* automatically inserted into the generate HTML for a page. You, the skin designer, must cause the javascript to be inserted by issuing appropriate TH1 commands in the "header.txt" or "footer.txt" files.</dd> </dl> Developing a new skin is simply a matter of creating appropriate versions of these five control files. ### Skin Development Using The Web Interface Users with admin privileges can use the Admin/Skin configuration page |
| ︙ | ︙ | |||
317 318 319 320 321 322 323 | did not change. After you have finished work your skin, the caches should synchronize with your new design and you can reactivate your web browser's cache and take it out of developer mode. ## <a id="headfoot"></a>Header and Footer Processing The `header.txt` and `footer.txt` control files of a skin are the HTML text | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 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 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 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 |
did not change. After you have finished work your skin, the
caches should synchronize with your new design and you can reactivate
your web browser's cache and take it out of developer mode.
## <a id="headfoot"></a>Header and Footer Processing
The `header.txt` and `footer.txt` control files of a skin are the HTML text
of the Skin Header and Skin Footer, except that before being inserted
into the output stream, the text is run through a
[TH1 interpreter](./th1.md) that might adjust the text as follows:
* All text within <th1>...</th1> is omitted from the
output and is instead run as a TH1 script. That TH1
script has the opportunity to insert new text in place of itself,
or to inhibit or enable the output of subsequent text.
* Text of the form "$NAME" or "$<NAME>" is replaced with
the value of the TH1 variable NAME.
For example, first few lines of a typical Skin Header will look
like this:
<div class="header">
<div class="title"><h1>$<project_name></h1>$<title>/div>
After variables are substituted by TH1, that will look more like this:
<div class="header">
<div class="title"><h1>Project Name</h1>Page Title</div>
As you can see, two TH1 variable substitutions were done.
The same TH1 interpreter is used for both the header and the footer
and for all scripts contained within them both. Hence, any global
TH1 variables that are set by the header are available to the footer.
## <a id="menu"></a>Customizing the ≡ Hamburger Menu
The menu bar of the default skin has an entry to open a drop-down menu with
additional navigation links, represented by the ≡ button (hence the name
"hamburger menu"). The Javascript logic to open and close the hamburger menu
when the button is clicked is usually handled by a script named
"hbmenu.js" that is one of the [built-in resource files](/test-builtin-files)
that are part of Fossil.
The ≡ button for the hamburger menu is added to the menu bar by the following
TH1 commands in the `header.txt` file, right before the menu bar links:
html "<a id='hbbtn' href='$home/sitemap'>☰</a>"
builtin_request_js hbmenu.js
The hamburger button can be repositioned between the other menu links (but the
drop-down menu is always left-aligned with the menu bar), or it can be removed
by deleting the above statements. The "html" statement inserts the appropriate
`<a>` for the hamburger menu button (some skins require something slightly
different - for example the ardoise skins wants "`<li><a>`"). The
"builtin_request_js hbmenu.js" asks Fossil to include the "hbmenu.js"
resource files in the Fossil-generated footer.
The hbmenu.js script requires
the following `<div>` element somewhere in your header, in which to build
the hamburger menu.
<div id='hbdrop'></div>
Out of the box, the contents of the panel is populated with the [Site
Map](/sitemap), but only if the panel does not already contain any HTML
elements (that is, not just comments, plain text or non-presentational white
space). So the hamburger menu can be customized by replacing the empty `<div
id='hbdrop'></div>` element with a menu structure knitted according to the
following template:
<div id="hbdrop" data-anim-ms="400">
<ul class="columns" style="column-width: 20em; column-count: auto">
<!-- NEW GROUP WITH HEADING LINK -->
<li>
<a href="$home$index_page">Link: Home</a>
<ul>
<li><a href="$home/timeline">Link: Timeline</a></li>
<li><a href="$home/dir?ci=tip">Link: File List</a></li>
</ul>
</li>
<!-- NEW GROUP WITH HEADING TEXT -->
<li>
Heading Text
<ul>
<li><a href="$home/doc/trunk/www/customskin.md">Link: Theming</a></li>
<li><a href="$home/doc/trunk/www/th1.md">Link: TH1 Scripts</a></li>
</ul>
</li>
<!-- NEXT GROUP GOES HERE -->
</ul>
</div>
The custom `data-anim-ms` attribute can be added to the panel element to direct
the Javascript logic to override the default menu animation duration of 400 ms.
A faster animation duration of 80-200 ms may be preferred for smaller menus. The
animation is disabled by setting the attribute to `"0"`.
## <a id="vars"></a>TH1 Variables
Before expanding the TH1 within the header and footer, Fossil first
initializes a number of TH1 variables to values that depend on
repository settings and the specific page being generated.
* **`project_name`** - The project_name variable is filled with the
name of the project as configured under the Admin/Configuration
menu.
* **`project_description`** - The project_description variable is
filled with the description of the project as configured under
the Admin/Configuration menu.
* **`title`** - The title variable holds the title of the page being
generated.
The title variable is special in that it is deleted after
the header script runs and before the footer script. This is
necessary to avoid a conflict with a variable by the same name used
in my ticket-screen scripts.
* **`baseurl`** - The root of the URL namespace for this server.
* **`secureurl`** - The same as $baseurl except that if the scheme is
"http:" it is changed to "https:"
* **`home`** - The $baseurl without the scheme and hostname. For example,
if the $baseurl is "http://projectX.com/cgi-bin/fossil" then the
$home will be just "/cgi-bin/fossil".
* **`index_page`** - The landing page URI as
specified by the Admin/Configuration setup page.
* **`current_page`** - The name of the page currently being processed,
without the leading "/" and without query parameters.
Examples: "timeline", "doc/trunk/README.txt", "wiki".
* **`csrf_token`** - A token used to prevent cross-site request forgery.
* **`default_csp`** - [Fossil’s default CSP](./defcsp.md) unless
[overridden by custom TH1 code](./defcsp.md#th1). Useful within
the skin for inserting the CSP into a `<meta>` tag within [a
custom `<head>` element](#headfoot).
* **`nonce`** - The value of the cryptographic nonce for the request
being processed.
* **`release_version`** - The release version of Fossil. Ex: "1.31"
* **`manifest_version`** - A prefix on the check-in hash of the
specific version of fossil that is running. Ex: "\[47bb6432a1\]"
* **`manifest_date`** - The date of the source-code check-in for the
version of fossil that is running.
* **`compiler_name`** - The name and version of the compiler used to
build the fossil executable.
* **`login`** - This variable only exists if the user has logged in.
The value is the username of the user.
* **`stylesheet_url`** - A URL for the internal style-sheet maintained
by Fossil.
* **`logo_image_url`** - A URL for the logo image for this project, as
configured on the Admin/Logo page.
* **`background_image_url`** - A URL for a background image for this
project, as configured on the Admin/Logo page.
All of the above are variables in the sense that either the header or the
footer is free to change or erase them. But they should probably be treated
as constants. New predefined values are likely to be added in future
releases of Fossil.
|
| ︙ | ︙ |
Changes to www/defcsp.md.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 | bugs that might lead to a vulnerability. ## The Default Restrictions The default CSP used by Fossil is as follows: <pre> | | | | | < | < | | | > | | | | | 21 22 23 24 25 26 27 28 29 30 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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
bugs that might lead to a vulnerability.
## The Default Restrictions
The default CSP used by Fossil is as follows:
<pre>
default-src 'self' data:;
script-src 'self' 'nonce-$nonce';
style-src 'self' 'unsafe-inline';
img-src * data:;
</pre>
The default is recommended for most installations. However,
the site administrators can overwrite this default CSP using the
[default-csp setting](/help?cmd=default-csp). For example,
CSP restrictions can be completely disabled by setting the default-csp to:
default-src *;
The following sections detail the maining of the default CSP setting.
### <a id="base"></a> default-src 'self' data:
This policy means mixed-origin content isn’t allowed, so you can’t refer
to resources on other web domains. Browsers will ignore a link like the
one in the following Markdown under our default CSP:

If you look in the browser’s developer console, you should see a CSP
error when attempting to render such a page.
The default policy does allow inline `data:` URIs, which means you could
[data-encode][de] your image content and put it inline within the
document:

That method is best used for fairly small resources. Large `data:` URIs
are hard to read and edit. There are secondary problems as well: if you
put a large image into a Fossil forum post this way, anyone subscribed
to email alerts will get a copy of the raw URI text, which can amount to
pages and pages of [ugly Base64-encoded text][b64].
For inline images within [embedded documentation][ed], it suffices to
store the referred-to files in the repo and then refer to them using
repo-relative URLs:

This avoids bloating the doc text with `data:` URI blobs:
There are many other cases, [covered below](#serving).
[b64]: https://en.wikipedia.org/wiki/Base64
[svr]: ./server/
### <a id="img"></a> img-src * data:
It was not always thus, but after careful consideration, we’ve chosen to
leave the source of inline images unrestricted by default in Fossil.
This allows you to pull them in from remote systems, to pull them from
within the Fossil repository itself, or to use `data:` URIs.
If you are certain all images come from only within the repository, you
can close off certain risks — tracking pixels, broken image format
decoders, system dialog box spoofing, etc. — by changing this to
“`img-src 'self'`” possibly followed by “`data:`” if you will also use
`data:` URIs.
### <a id="style"></a> style-src 'self' 'unsafe-inline'
This policy allows CSS information to come from separate files hosted
under the Fossil repo server’s Internet domain. It also allows inline CSS
`<style>` tags within the document text.
The `'unsafe-inline'` declaration allows CSS within individual HTML
elements:
<p style="margin-left: 4em">Indented text.</p>
As the "`unsafe-`" prefix on the name implies, the `'unsafe-inline'`
feature is suboptimal for security. However, there are
a few places in the Fossil-generated HTML that benefit from this
flexibility and the work-arounds are verbose and difficult to maintain.
Furthermore, the harm that can be done with style injections is far
less than the harm possible with injected javascript. And so the
|
| ︙ | ︙ | |||
172 173 174 175 176 177 178 | offers free Fossil repository hosting to anyone on the Internet, all served under the same `http://chiselapp.com/user/$NAME/$REPO` URL scheme. Any one of those hundreds of repositories could trick you into visiting their repository home page, set to [an HTML-formatted embedded doc page][hfed] via Admin → Configuration → Index Page, with this content: | | | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
offers free Fossil repository hosting to anyone on the Internet, all
served under the same `http://chiselapp.com/user/$NAME/$REPO` URL
scheme. Any one of those hundreds of repositories could trick you into
visiting their repository home page, set to [an HTML-formatted embedded
doc page][hfed] via Admin → Configuration → Index Page, with this
content:
<script src="/doc/trunk/bad.js"></script>
That script can then do anything allowed in JavaScript to *any other*
Chisel repository your browser can access. The possibilities for mischief
are *vast*. For just one example, if you have login cookies on four
different Chisel repositories, your attacker could harvest the login
cookies for all of them through this path if we allowed Fossil to serve
JavaScript files under the same CSP policy as we do for CSS files.
|
| ︙ | ︙ | |||
196 197 198 199 200 201 202 | path around this restriction. If you are serving a Fossil repository that has any user you do not implicitly trust to a level that you would willingly run any JavaScript code they’ve provided, blind, you **must not** give the `--with-th1-docs` option when configuring Fossil, because that allows substitution of the [pre-defined `$nonce` TH1 variable](./th1.md#nonce) into [HTML-formatted embedded docs][hfed]: | | | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
path around this restriction. If you are serving a Fossil repository
that has any user you do not implicitly trust to a level that you would
willingly run any JavaScript code they’ve provided, blind, you **must
not** give the `--with-th1-docs` option when configuring Fossil, because
that allows substitution of the [pre-defined `$nonce` TH1
variable](./th1.md#nonce) into [HTML-formatted embedded docs][hfed]:
<script src="/doc/trunk/bad.js" nonce="$nonce"></script>
Even with this feature enabled, you cannot put `<script>` tags into
Fossil Wiki or Markdown-formatted content, because our HTML generators
for those formats purposely strip or disable such tags in the output.
Therefore, if you trust those users with check-in rights to provide
JavaScript but not those allowed to file tickets, append to wiki
articles, etc., you might justify enabling TH1 docs on your repository,
|
| ︙ | ︙ | |||
329 330 331 332 333 334 335 | Changing this setting is the easiest way to set a nonstandard CSP on your site. Because a blank setting tells Fossil to use its hard-coded default CSP, you have to say something like the following to get a repository without content security policy restrictions: | | | 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
Changing this setting is the easiest way to set a nonstandard CSP on
your site.
Because a blank setting tells Fossil to use its hard-coded default CSP,
you have to say something like the following to get a repository without
content security policy restrictions:
$ fossil set -R /path/to/served/repo.fossil default-csp 'default-src *'
We recommend that instead of using the command line to change this
setting that you do it via the repository’s web interface, in
Admin → Settings. Write your CSP rules in the edit box marked
"`default-csp`". Do not add hard newlines in that box: the setting needs
to be on a single long line. Beware that changes take effect
immediately, so be careful with your edits: you could end up locking
|
| ︙ | ︙ | |||
364 365 366 367 368 369 370 | `default-csp` setting and uses *that* to inject the value into generated HTML pages in its stock configuration. This means that another way you can override this value is to use the [`th1-setup` hook script](./th1-hooks.md), which runs before TH1 processing happens during skin processing: | | | 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
`default-csp` setting and uses *that* to inject the value into generated
HTML pages in its stock configuration.
This means that another way you can override this value is to use
the [`th1-setup` hook script](./th1-hooks.md), which runs before TH1
processing happens during skin processing:
$ fossil set th1-setup "set default_csp {default-src 'self'}"
After [the above](#admin-ui), this is the cleanest method.
[thvar]: ./customskin.md#vars
|
| ︙ | ︙ |
Changes to www/delta-manifests.md.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Delta Manifests This article describes "delta manifests," a special-case form of checkin manifest which is intended to take up far less space than a normal checkin manifest, in particular for repositories with many files. We'll see, however, that the space savings, if indeed there are any, come with some caveats. This article assumes that the reader is at least moderately familiar with Fossil's [artifact file format](./fileformat.wiki), in particular the structure of checkin manifests, and it won't make much sense to readers unfamiliar with that topic. | > > > > < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # Delta Manifests <div class="sidebar">Do not confuse these with the core [Fossil delta format](./delta_format.wiki). This document describes an optional feature not enabled by default.</div> This article describes "delta manifests," a special-case form of checkin manifest which is intended to take up far less space than a normal checkin manifest, in particular for repositories with many files. We'll see, however, that the space savings, if indeed there are any, come with some caveats. This article assumes that the reader is at least moderately familiar with Fossil's [artifact file format](./fileformat.wiki), in particular the structure of checkin manifests, and it won't make much sense to readers unfamiliar with that topic. # Background and Motivation of Delta Manifests A checkin manifest includes a list of every file in that checkin. A moderately-sized project can easily have a thousand files, and every checkin manifest will include those thousand files. As of this writing Fossil's own checkins contain 989 files and the manifests are 80kb each. Thus a checkin which changes only 2 bytes of source code |
| ︙ | ︙ |
Changes to www/delta_encoder_algorithm.wiki.
1 | <title>Fossil Delta Encoding Algorithm</title> | | | 1 2 3 4 5 6 7 8 9 | <title>Fossil Delta Encoding Algorithm</title> <h2>Abstract</h2> <p>A key component for the efficient storage of multiple revisions of a file in fossil repositories is the use of delta-compression, i.e. to store only the changes between revisions instead of the whole file.</p> |
| ︙ | ︙ | |||
105 106 107 108 109 110 111 | to <a href="delta_format.wiki#copyrange">copy a range</a>, or </li> <li>move the window forward one byte. </li> </ul> </p> | < | > | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
to <a href="delta_format.wiki#copyrange">copy a range</a>, or
</li>
<li>move the window forward one byte.
</li>
</ul>
</p>
<verbatim type="pikchr float-right">
TARGET: [
scale = 0.8
down
"Target" bold
box fill palegreen width 150% height 200% "Processed"
GI: box same as first box fill yellow height 25% "Gap → Insert"
CC: box same fill orange height 200% "Common → Copy"
W: box same as GI fill lightgray width 125% height 200% "Window" bold
box same as CC height 125% ""
|
| ︙ | ︙ | |||
131 132 133 134 135 136 137 | B1: box fill white B2: box fill orange height 200% B3: box fill white height 200% ] with .nw at 0.75 right of TARGET.ne arrow from TARGET.W.e to ORIGIN.B2.w "Signature" aligned above </verbatim> | < | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | B1: box fill white B2: box fill orange height 200% B3: box fill white height 200% ] with .nw at 0.75 right of TARGET.ne arrow from TARGET.W.e to ORIGIN.B2.w "Signature" aligned above </verbatim> <p>To make this decision the encoder first computes the hash value for the NHASH bytes in the window and then looks at all the locations in the "origin" which have the same signature. This part uses the hash table created by the pre-processing step to efficiently find these locations.</p> |
| ︙ | ︙ | |||
215 216 217 218 219 220 221 | and a new byte is shifted in.<p> <h3 id="rhdef">4.1 Definition</h3> <p>Assuming an array Z of NHASH bytes (indexing starting at 0) the hash V is computed via</p> | < | | | < < | | | < | 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | and a new byte is shifted in.<p> <h3 id="rhdef">4.1 Definition</h3> <p>Assuming an array Z of NHASH bytes (indexing starting at 0) the hash V is computed via</p> <div align="center"><img src="encode1.gif"></div> <div align="center"><img src="encode2.gif"></div> <div align="center"><img src="encode3.gif"></div> where A and B are unsigned 16-bit integers (hence the <u>mod</u>), and V is a 32-bit unsigned integer with B as MSB, A as LSB. <h3 id="rhincr">4.2 Incremental recalculation</h3> <p>Assuming an array Z of NHASH bytes (indexing starting at 0) with hash V (and components A and B), the dropped byte <img src="encode4.gif" align="center">, and the new byte <img src="encode5.gif" align="center"> , the new hash can be computed incrementally via: </p> <div align="center"><img src="encode6.gif"></div> <div align="center"><img src="encode7.gif"></div> <div align="center"><img src="encode8.gif"></div> <p>For A, the regular sum, it can be seen easily that this the correct way recomputing that component.</p> <p>For B, the weighted sum, note first that <img src="encode4.gif" align="center"> has the weight NHASH in the sum, so that is what has to be removed. Then adding in <img src="encode9.gif" align="center"> adds one weight factor to all the other values of Z, and at last adds in <img src="encode5.gif" align="center"> with weight 1, also generating the correct new sum</p> |
Changes to www/delta_format.wiki.
| ︙ | ︙ | |||
188 189 190 191 192 193 194 | The format currently handles only 32 bit integer numbers. They are written base-64 encoded, MSB first, and without leading "0"-characters, except if they are significant (i.e. 0 => "0"). The base-64 encoding uses one character for each 6 bits of the integer to be encoded. The encoding characters are: | | | | | 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 216 217 | The format currently handles only 32 bit integer numbers. They are written base-64 encoded, MSB first, and without leading "0"-characters, except if they are significant (i.e. 0 => "0"). The base-64 encoding uses one character for each 6 bits of the integer to be encoded. The encoding characters are: <pre> 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ </pre> The least significant 6 bits of the integer are encoded by the first character, followed by the next 6 bits, and so on until all non-zero bits of the integer are encoded. The minimum number of encoding characters is used. Note that for integers less than 10, the base-64 coding is a ASCII decimal rendering of the number itself. <h1 id="examples">4.0 Examples</h1> <h2 id="examplesint">4.1 Integer encoding</h2> <table> <tr> <th>Value</th> <th>Encoding</th> </tr> <tr> <td>0</td> <td>0</td> |
| ︙ | ︙ | |||
226 227 228 229 230 231 232 | </tr> </table> <h2 id="examplesdelta">4.2 Delta encoding</h2> An example of a delta using the specified encoding is: | | | | | | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
</tr>
</table>
<h2 id="examplesdelta">4.2 Delta encoding</h2>
An example of a delta using the specified encoding is:
<pre>
1Xb
4E@0,2:thFN@4C,6:scenda1B@Jd,6:scenda5x@Kt,6:pieces79@Qt,F: Example: eskil~E@Y0,2zMM3E;</pre>
</pre>
This can be taken apart into the following parts:
<table>
<tr><th>What </th> <th>Encoding </th><th>Meaning </th><th>Details</th></tr>
<tr><td>Header</td> <td>1Xb </td><td>Size </td><td> 6246 </td></tr>
<tr><td>S-List</td> <td>4E@0, </td><td>Copy </td><td> 270 @ 0 </td></tr>
<tr><td> </td> <td>2:th </td><td>Literal </td><td> 2 'th' </td></tr>
<tr><td> </td> <td>FN@4C, </td><td>Copy </td><td> 983 @ 268 </td></tr>
<tr><td> </td> <td>6:scenda </td><td>Literal </td><td> 6 'scenda' </td></tr>
<tr><td> </td> <td>1B@Jd, </td><td>Copy </td><td> 75 @ 1256 </td></tr>
<tr><td> </td> <td>6:scenda </td><td>Literal </td><td> 6 'scenda' </td></tr>
<tr><td> </td> <td>5x@Kt, </td><td>Copy </td><td> 380 @ 1336 </td></tr>
<tr><td> </td> <td>6:pieces </td><td>Literal </td><td> 6 'pieces' </td></tr>
<tr><td> </td> <td>79@Qt, </td><td>Copy </td><td> 457 @ 1720 </td></tr>
<tr><td> </td> <td>F: Example: eskil</td><td>Literal </td><td> 15 ' Example: eskil'</td></tr>
<tr><td> </td> <td>~E@Y0, </td><td>Copy </td><td> 4046 @ 2176 </td></tr>
<tr><td>Trailer</td><td>2zMM3E </td><td>Checksum</td><td> -1101438770 </td></tr>
</table>
The unified diff behind the above delta is
<verbatim>
bluepeak:(761) ~/Projects/Tcl/Fossil/Devel/devel > diff -u ../DELTA/old ../DELTA/new
--- ../DELTA/old 2007-08-23 21:14:40.000000000 -0700
+++ ../DELTA/new 2007-08-23 21:14:33.000000000 -0700
@@ -5,7 +5,7 @@
* If the server does not have write permission on the database
file, or on the directory containing the database file (and
|
| ︙ | ︙ | |||
293 294 295 296 297 298 299 |
single file. Allow diffs against any two arbitrary versions,
not just diffs against the current check-out. Allow
configuration options to replace tkdiff with some other
- visual differ of the users choice.
+ visual differ of the users choice. Example: eskil.
* Ticketing interface (expand this bullet)
| | < | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
single file. Allow diffs against any two arbitrary versions,
not just diffs against the current check-out. Allow
configuration options to replace tkdiff with some other
- visual differ of the users choice.
+ visual differ of the users choice. Example: eskil.
* Ticketing interface (expand this bullet)
</verbatim>
<h1 id="notes">Notes</h1>
<ul>
<li>Pure text files generate a pure text delta.
|
| ︙ | ︙ |
Changes to www/embeddeddoc.wiki.
1 | <title>Project Documentation</title> | < | 1 2 3 4 5 6 7 8 | <title>Project Documentation</title> Fossil provides a built-in <a href="wikitheory.wiki">wiki</a> that can be used to store the documentation for a project. This is sufficient for many projects. If your project is well-served by wiki documentation, then you need read no further. |
| ︙ | ︙ | |||
28 29 30 31 32 33 34 | <h1>1.0 Fossil Support For Embedded Documentation</h1> The fossil web interface supports embedded documentation using the "/doc" page. To access embedded documentation, one points a web browser to a fossil URL of the following form: | | | | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <h1>1.0 Fossil Support For Embedded Documentation</h1> The fossil web interface supports embedded documentation using the "/doc" page. To access embedded documentation, one points a web browser to a fossil URL of the following form: <pre> <i><baseurl></i><big><b>/doc/</b></big><i><version></i><big><b>/</b></big><i><filename></i> </pre> The <i><baseurl></i> is the main URL used to access the fossil web server. For example, the <i><baseurl></i> for the fossil project itself is [https://fossil-scm.org/home]. If you launch the web server using the "[/help?cmd=ui|fossil ui]" command line, then the <i><baseurl></i> is usually <b>http://localhost:8080/</b>. |
| ︙ | ︙ | |||
137 138 139 140 141 142 143 | Hyperlinks in Markdown and HTML embedded documents can reference the root of the Fossil repository using the special text "$ROOT" at the beginning of a URL. For example, a Markdown hyperlink to the Markdown formatting rules might be written in the embedded document like this: | | | | | | | | | | | | 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 180 181 182 183 | Hyperlinks in Markdown and HTML embedded documents can reference the root of the Fossil repository using the special text "$ROOT" at the beginning of a URL. For example, a Markdown hyperlink to the Markdown formatting rules might be written in the embedded document like this: <verbatim> [Markdown formatting rules]($ROOT/wiki_rules) </verbatim> Depending on how the how the Fossil server is configured, that hyperlink might be renderer like one of the following: <verbatim> <a href="/wiki_rules">Wiki formatting rule</a> <a href="/cgi-bin/fossil/wiki_rules">Wiki formatting rules</a> </verbatim> So, in other words, the "$ROOT" text is converted into whatever the "<baseurl>" is for the document. This substitution works for HTML and Markdown documents. It does not work for Wiki embedded documents, since with Wiki you can just begin a URL with "/" and it automatically knows to prepend the $ROOT. <h2>2.2 "$CURRENT" In "/doc/" Hyperlinks</h2> Similarly, URLs of the form "/doc/$CURRENT/..." have the check-in hash of the check-in currently being viewed substituted in place of the "$CURRENT" text. This feature, in combination with the "$ROOT" substitution above, allows an absolute path to be used for hyperlinks. For example, if an embedded document documented wanted to reference some other document in a separate file named "www/otherdoc.md", it could use a URL like this: <verbatim> [Other Document]($ROOT/doc/$CURRENT/www/otherdoc.md) </verbatim> As with "$ROOT", this substitution only works for Markdown and HTML documents. For Wiki documents, you would need to use a relative URL. <h2 id="th1">2.3 TH1 Documents</h2> Fossil will substitute the value of [./th1.md | TH1 expressions] within |
| ︙ | ︙ | |||
199 200 201 202 203 204 205 | This file that you are currently reading is an example of embedded documentation. The name of this file in the fossil source tree is "<b>www/embeddeddoc.wiki</b>". You are perhaps looking at this file using the URL: | | | | | | < | < | < | 198 199 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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | This file that you are currently reading is an example of embedded documentation. The name of this file in the fossil source tree is "<b>www/embeddeddoc.wiki</b>". You are perhaps looking at this file using the URL: <pre>[https://fossil-scm.org/home/doc/trunk/www/embeddeddoc.wiki]</pre> The first part of this path, the "[https://fossil-scm.org/home]", is the base URL. You might have originally typed: [https://fossil-scm.org/]. The web server at the fossil-scm.org site automatically redirects such links by appending "home". The "home" file on fossil-scm.org is really a [./server/any/cgi.md|CGI script] which runs the fossil web service in CGI mode. The "home" CGI script looks like this: <pre> #!/usr/bin/fossil repository: /fossil/fossil.fossil </pre> This is one of the many ways to set up a <a href="./server/">Fossil server</a>. The "<b>/trunk/</b>" part of the URL tells fossil to use the documentation files from the most recent trunk check-in. If you wanted to see an historical version of this document, you could substitute the name of a check-in for "<b>/trunk/</b>". For example, to see the version of this document associated with check-in [9be1b00392], simply replace the "<b>/trunk/</b>" with "<b>/9be1b00392/</b>". You can also substitute the symbolic name for a particular version or branch. For example, you might replace "<b>/trunk/</b>" with "<b>/experimental/</b>" to get the latest version of this document in the "experimental" branch. The symbolic name can also be a date and time string in any of the following formats:</p> <ul> <li> <i>YYYY-MM-DD</i> <li> <i>YYYY-MM-DD<b>T</b>HH:MM</i> <li> <i>YYYY-MM-DD<b>T</b>HH:MM:SS</i> </ul> When the symbolic name is a date and time, fossil shows the version of the document that was most recently checked in as of the date and time specified. So, for example, to see what the fossil website looked like at the beginning of 2010, enter: <pre><a href="https://fossil-scm.org/home/doc/2010-01-01/www/index.wiki">https://fossil-scm.org/home/doc/<b>2010-01-01</b>/www/index.wiki </a></pre> The file that encodes this document is stored in the fossil source tree under the name "<b>www/embeddeddoc.wiki</b>" and so that name forms the last part of the URL for this document. As I sit writing this documentation file, I am testing my work by running the "<b>fossil ui</b>" command line and viewing <b>http://localhost:8080/doc/ckout/www/embeddeddoc.wiki</b> in Firefox. I am doing this even though I have not yet checked in the "<b>www/embeddeddoc.wiki</b>" file for the first time. Using the special "<b>ckout</b>" version identifier on the "<b>/doc</b>" page it is easy to make multiple changes to multiple files and see how they all look together before committing anything to the repository. |
Changes to www/encryptedrepos.wiki.
1 | <title>How To Use Encrypted Repositories</title> | > | > | | > > | | > | | | | > | | > > | | > > | | > > | | > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 | <title>How To Use Encrypted Repositories</title> <h2>Introduction</h2> Fossil can be compiled so that it works with encrypted repositories using the [https://www.sqlite.org/see/doc/trunk/www/readme.wiki|SQLite Encryption Extension]. This technical note explains the process. <h2>Building An Encryption-Enabled Fossil</h2> The SQLite Encryption Extension (SEE) is proprietary software and requires [https://sqlite.org/purchase/see|purchasing a license]. Assuming you have an SEE license, the first step of compiling Fossil to use SEE is to create an SEE-enabled version of the SQLite database source code. This alternative SQLite database source file should be called "sqlite3-see.c" and should be placed in the extsrc/ subfolder of the Fossil sources, right beside the public-domain "sqlite3.c" source file. Also make a copy of the SEE-enabled "shell.c" file, renamed as "shell-see.c", and place it in the extsrc/ subfolder beside the original "shell.c". Add the --with-see command-line option to the configuration script to enable the use of SEE on unix-like systems. <pre> ./configure --with-see; make </pre> To build for Windows using MSVC, add the "USE_SEE=1" argument to the "nmake" command line. <pre> nmake -f makefile.msc USE_SEE=1 </pre> <h2>Using Encrypted Repositories</h2> Any Fossil repositories whose filename ends with ".efossil" is taken to be an encrypted repository. Fossil will prompt for the encryption password and attempt to open the repository database using that password. Every invocation of fossil on an encrypted repository requires retyping the encryption password. To avoid excess password typing, consider using the "fossil shell" command which prompts for the password just once, then reuses it for each subsequent Fossil command entered at the prompt. On Windows, the "fossil server", "fossil ui", and "fossil shell" commands do not (currently) work on an encrypted repository. <h2>Additional Security</h2> Use the FOSSIL_SECURITY_LEVEL environment for additional protection. <pre> export FOSSIL_SECURITY_LEVEL=1 </pre> A setting of 1 or greater prevents fossil from trying to remember the previous sync password. <pre> export FOSSIL_SECURITY_LEVEL=2 </pre> A setting of 2 or greater causes all password prompts to be preceded by a random translation matrix similar to the following: <pre> abcde fghij klmno pqrst uvwyz qresw gjymu dpcoa fhkzv inlbt </pre> When entering the password, the user must substitute the letter on the second line that corresponds to the letter on the first line. Uppercase substitutes for uppercase inputs, and lowercase substitutes for lowercase inputs. Letters that are not in the translation matrix (digits, punctuation, and "x") are not modified. For example, given the translation matrix above, if the password is "pilot-9crazy-xube", then the user must type "fmpav-9ekqtb-xirw". This simple substitution cypher helps prevent password capture by keyloggers. |
Changes to www/event.wiki.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 | There is a hyperlink under the /wikihelp menu that can be used to create new technotes. And there is a submenu hyperlink on technote displays for editing existing technotes. Technotes can also be created using the <b>wiki create</b> command: | < | | | | < < | | | | | < < | | | < | 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 | There is a hyperlink under the /wikihelp menu that can be used to create new technotes. And there is a submenu hyperlink on technote displays for editing existing technotes. Technotes can also be created using the <b>wiki create</b> command: <verbatim> fossil wiki create TestTechnote -t now --technote-bgcolor lightgreen technote.md Created new tech note 2021-03-15 13:05:56 </verbatim> This command inserts a light green technote in the timeline at 2021-03-15 13:05:56, with the contents of file <b>technote.md</b> and comment "TestTechnote". Specifying a different time using <b>-t DATETIME</b> will insert the technote at the specified timestamp location in the timeline. Different technotes can have the same timestamp. The first argument to create, <b>TECHNOTE-COMMENT</b>, is the title text for the technote that appears in the timeline. To view all technotes, use the <b>wiki ls</b> command: <verbatim> fossil wiki ls --technote --show-technote-ids z739263a134bf0da1d28e939f4c4367f51ef4c51 2020-12-19 13:20:19 e15a918a8bed71c2ac091d74dc397b8d3340d5e1 2018-09-22 17:40:10 </verbatim> A technote ID is the UUID of the technote. To view an individual technote, use the <b>wiki export</b> command: <verbatim> fossil wiki export --technote version-2.16 Release Notes 2021-07-02 This note describes changes in the Fossil snapshot for ... </verbatim> The <b>-t|--technote</b> option to the <b>export</b> subcommand takes one of three identifiers: <b>DATETIME</b>; <b>TECHNOTE-ID</b>; and <b>TAG</b>. See the [/help?cmd=wiki | wiki help] for specifics. Users must have check-in privileges (permission "i") in order to create or edit technotes. In addition, users must have create-wiki |
| ︙ | ︙ |
Changes to www/faq.tcl.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
faq {
What GUIs are available for fossil?
} {
The fossil executable comes with a [./webui.wiki | web-based GUI] built in.
Just run:
| | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
faq {
What GUIs are available for fossil?
} {
The fossil executable comes with a [./webui.wiki | web-based GUI] built in.
Just run:
<pre>
<b>fossil [/help/ui|ui]</b> <i>REPOSITORY-FILENAME</i>
</pre>
And your default web browser should pop up and automatically point to
the fossil interface. (Hint: You can omit the <i>REPOSITORY-FILENAME</i>
if you are within an open check-out.)
}
faq {
|
| ︙ | ︙ | |||
40 41 42 43 44 45 46 | When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add the option "--branch <i>BRANCH-NAME</i>" to make the new check-in be the first check-in for a new branch. If you want to create a new branch whose initial content is the same as an existing check-in, use this command: | | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add the option "--branch <i>BRANCH-NAME</i>" to make the new check-in be the first check-in for a new branch. If you want to create a new branch whose initial content is the same as an existing check-in, use this command: <pre> <b>fossil [/help/branch|branch] new</b> <i>BRANCH-NAME BASIS</i> </pre> The <i>BRANCH-NAME</i> argument is the name of the new branch and the <i>BASIS</i> argument is the name of the check-in that the branch splits off from. If you already have a fork in your check-in tree and you want to convert that fork to a branch, you can do this from the web interface. |
| ︙ | ︙ | |||
70 71 72 73 74 75 76 | When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add a tag to that check-in using the "--tag <i>TAGNAME</i>" command-line option. You can repeat the --tag option to give a check-in multiple tags. Tags need not be unique. So, for example, it is common to give every released version a "release" tag. | | | | | | | 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 |
When you are checking in a new change using the <b>[/help/commit|commit]</b>
command, you can add a tag to that check-in using the
"--tag <i>TAGNAME</i>" command-line option. You can repeat the --tag
option to give a check-in multiple tags. Tags need not be unique. So,
for example, it is common to give every released version a "release" tag.
If you want to add a tag to an existing check-in, you can use the
<b>[/help/tag|tag]</b> command. For example:
<pre>
<b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i>
</pre>
The CHECK-IN in the previous line can be any
[./checkin_names.wiki | valid check-in name format].
You can also add (and remove) tags from a check-in using the
[./webui.wiki | web interface]. First locate the check-in that you
want to tag on the timeline, then click on the link to go the detailed
information page for that check-in. Then find the "<b>edit</b>"
link (near the "Commands:" label) and click on that. There are
controls on the edit page that allow new tags to be added and existing
tags to be removed.
}
faq {
How do I create a private branch that won't get pushed back to the
main repository.
} {
Use the <b>--private</b> command-line option on the
<b>commit</b> command. The result will be a check-in which exists in
your local repository only and is never pushed to other repositories.
All descendants of a private check-in are also private.
Unless you specify something different using the <b>--branch</b> and/or
<b>--bgcolor</b> options, the new private check-in will be put on a branch
named "private" with an orange background color.
|
| ︙ | ︙ | |||
125 126 127 128 129 130 131 |
See the article on [./shunning.wiki | "shunning"] for details.
}
faq {
How do I make a clone of the fossil self-hosting repository?
} {
Any of the following commands should work:
| > | | > > | | > > | | | < | | 125 126 127 128 129 130 131 132 133 134 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 180 181 |
See the article on [./shunning.wiki | "shunning"] for details.
}
faq {
How do I make a clone of the fossil self-hosting repository?
} {
Any of the following commands should work:
<pre>
fossil [/help/clone|clone] https://fossil-scm.org/ fossil.fossil
fossil [/help/clone|clone] https://www2.fossil-scm.org/ fossil.fossil
fossil [/help/clone|clone] https://www3.fossil-scm.org/site.cgi fossil.fossil
</pre>
Once you have the repository cloned, you can open a local check-out
as follows:
<pre>
mkdir src; cd src; fossil [/help/open|open] ../fossil.fossil
</pre>
Thereafter you should be able to keep your local check-out up to date
with the latest code in the public repository by typing:
<pre>
fossil [/help/update|update]
</pre>
}
faq {
How do I import or export content from and to other version control systems?
} {
Please see [./inout.wiki | Import And Export]
}
#############################################################################
# Code to actually generate the FAQ
#
puts "<title>Fossil FAQ</title>\n"
puts "Note: See also <a href=\"qandc.wiki\">Questions and Criticisms</a>.\n"
puts {<ol>}
for {set i 1} {$i<$cnt} {incr i} {
puts "<li><a href=\"#q$i\">[lindex $faq($i) 0]</a></li>"
}
puts {</ol>}
puts {<hr>}
for {set i 1} {$i<$cnt} {incr i} {
puts "<p id=\"q$i\"><b>($i) [lindex $faq($i) 0]</b></p>\n"
set body [lindex $faq($i) 1]
regsub -all "\n *" [string trim $body] "\n" body
puts "$body</li>\n"
}
puts {</ol>}
|
Changes to www/faq.wiki.
1 | <title>Fossil FAQ</title> | < | | | | | | | | | | | | | | | | | | | | | > | | > > | | > > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | <title>Fossil FAQ</title> Note: See also <a href="qandc.wiki">Questions and Criticisms</a>. <ol> <li><a href="#q1">What GUIs are available for fossil?</a></li> <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li> <li><a href="#q3">How do I create a new branch?</a></li> <li><a href="#q4">How do I tag a check-in?</a></li> <li><a href="#q5">How do I create a private branch that won't get pushed back to the main repository.</a></li> <li><a href="#q6">How can I delete inappropriate content from my fossil repository?</a></li> <li><a href="#q7">How do I make a clone of the fossil self-hosting repository?</a></li> <li><a href="#q8">How do I import or export content from and to other version control systems?</a></li> </ol> <hr> <p id="q1"><b>(1) What GUIs are available for fossil?</b></p> The fossil executable comes with a [./webui.wiki | web-based GUI] built in. Just run: <pre> <b>fossil [/help/ui|ui]</b> <i>REPOSITORY-FILENAME</i> </pre> And your default web browser should pop up and automatically point to the fossil interface. (Hint: You can omit the <i>REPOSITORY-FILENAME</i> if you are within an open check-out.)</li> <p id="q2"><b>(2) What is the difference between a "branch" and a "fork"?</b></p> This is a big question - too big to answer in a FAQ. Please read the <a href="branching.wiki">Branching, Forking, Merging, and Tagging</a> document.</li> <p id="q3"><b>(3) How do I create a new branch?</b></p> There are lots of ways: When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add the option "--branch <i>BRANCH-NAME</i>" to make the new check-in be the first check-in for a new branch. If you want to create a new branch whose initial content is the same as an existing check-in, use this command: <pre> <b>fossil [/help/branch|branch] new</b> <i>BRANCH-NAME BASIS</i> </pre> The <i>BRANCH-NAME</i> argument is the name of the new branch and the <i>BASIS</i> argument is the name of the check-in that the branch splits off from. If you already have a fork in your check-in tree and you want to convert that fork to a branch, you can do this from the web interface. First locate the check-in that you want to be the initial check-in of your branch on the timeline and click on its link so that you are on the <b>ci</b> page. Then find the "<b>edit</b>" link (near the "Commands:" label) and click on that. On the "Edit Check-in" page, check the box beside "Branching:" and fill in the name of your new branch to the right and press the "Apply Changes" button.</li> <p id="q4"><b>(4) How do I tag a check-in?</b></p> There are several ways: When you are checking in a new change using the <b>[/help/commit|commit]</b> command, you can add a tag to that check-in using the "--tag <i>TAGNAME</i>" command-line option. You can repeat the --tag option to give a check-in multiple tags. Tags need not be unique. So, for example, it is common to give every released version a "release" tag. If you want to add a tag to an existing check-in, you can use the <b>[/help/tag|tag]</b> command. For example: <pre> <b>fossil [/help/branch|tag] add</b> <i>TAGNAME</i> <i>CHECK-IN</i> </pre> The CHECK-IN in the previous line can be any [./checkin_names.wiki | valid check-in name format]. You can also add (and remove) tags from a check-in using the [./webui.wiki | web interface]. First locate the check-in that you want to tag on the timeline, then click on the link to go the detailed information page for that check-in. Then find the "<b>edit</b>" link (near the "Commands:" label) and click on that. There are controls on the edit page that allow new tags to be added and existing tags to be removed.</li> <p id="q5"><b>(5) How do I create a private branch that won't get pushed back to the main repository.</b></p> Use the <b>--private</b> command-line option on the <b>commit</b> command. The result will be a check-in which exists in your local repository only and is never pushed to other repositories. All descendants of a private check-in are also private. Unless you specify something different using the <b>--branch</b> and/or <b>--bgcolor</b> options, the new private check-in will be put on a branch named "private" with an orange background color. You can merge from the trunk into your private branch in order to keep your private branch in sync with the latest changes on the trunk. Once you have everything in your private branch the way you want it, you can then merge your private branch back into the trunk and push. Only the final merge operation will appear in other repositories. It will seem as if all the changes that occurred on your private branch occurred in a single check-in. Of course, you can also keep your branch private forever simply by not merging the changes in the private branch back into the trunk. [./private.wiki | Additional information]</li> <p id="q6"><b>(6) How can I delete inappropriate content from my fossil repository?</b></p> See the article on [./shunning.wiki | "shunning"] for details.</li> <p id="q7"><b>(7) How do I make a clone of the fossil self-hosting repository?</b></p> Any of the following commands should work: <pre> fossil [/help/clone|clone] https://fossil-scm.org/ fossil.fossil fossil [/help/clone|clone] https://www2.fossil-scm.org/ fossil.fossil fossil [/help/clone|clone] https://www3.fossil-scm.org/site.cgi fossil.fossil </pre> Once you have the repository cloned, you can open a local check-out as follows: <pre> mkdir src; cd src; fossil [/help/open|open] ../fossil.fossil </pre> Thereafter you should be able to keep your local check-out up to date with the latest code in the public repository by typing: <pre> fossil [/help/update|update] </pre></li> <p id="q8"><b>(8) How do I import or export content from and to other version control systems?</b></p> Please see [./inout.wiki | Import And Export]</li> </ol> |
Changes to www/fileedit-page.md.
| ︙ | ︙ | |||
260 261 262 263 264 265 266 |
lost on a page reload. How that is done is completely dependent on the
3rd-party editor widget, but it generically looks something like:
```
myCustomWidget.on('eventName', ()=>fossil.page.notifyOfChange());
```
| < < < | 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
lost on a page reload. How that is done is completely dependent on the
3rd-party editor widget, but it generically looks something like:
```
myCustomWidget.on('eventName', ()=>fossil.page.notifyOfChange());
```
Lastly, if the 3rd-party editor does *not* hide or remove the native
editor widget, and does not inject itself into the DOM on the caller's
behalf, we can replace the native widget with the 3rd-party one with:
```javascript
fossil.page.replaceEditorWidget(yourNewWidgetElement);
```
|
| ︙ | ︙ |
Changes to www/fileformat.wiki.
1 | <title>Fossil File Formats</title> | < < < | 1 2 3 4 5 6 7 8 | <title>Fossil File Formats</title> The global state of a fossil repository is kept simple so that it can endure in useful form for decades or centuries. A fossil repository is intended to be readable, searchable, and extensible by people not yet born. The global state of a fossil repository is an unordered |
| ︙ | ︙ | |||
108 109 110 111 112 113 114 | well as information such as parent check-ins, the username of the programmer who created the check-in, the date and time when the check-in was created, and any check-in comments associated with the check-in. Allowed cards in the manifest are as follows: | | | | 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 | well as information such as parent check-ins, the username of the programmer who created the check-in, the date and time when the check-in was created, and any check-in comments associated with the check-in. Allowed cards in the manifest are as follows: <div class="indent"> <b>B</b> <i>baseline-manifest</i><br> <b>C</b> <i>checkin-comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br> <b>F</b> <i>filename</i> ?<i>hash</i>? ?<i>permissions</i>? ?<i>old-name</i>?<br> <b>N</b> <i>mimetype</i><br> <b>P</b> <i>artifact-hash</i>+<br> <b>Q</b> (<b>+</b>|<b>-</b>)<i>artifact-hash</i> ?<i>artifact-hash</i>?<br> <b>R</b> <i>repository-checksum</i><br> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <b>*</b> ?<i>value</i>?<br> <b>U</b> <i>user-login</i><br> <b>Z</b> <i>manifest-checksum</i> </div> A manifest may optionally have a single <b>B</b> card. The <b>B</b> card specifies another manifest that serves as the "baseline" for this manifest. A manifest that has a <b>B</b> card is called a delta-manifest and a manifest that omits the <b>B</b> card is a baseline-manifest. The other manifest identified by the argument of the <b>B</b> card must be a baseline-manifest. A baseline-manifest records the complete contents of a check-in. |
| ︙ | ︙ | |||
146 147 148 149 150 151 152 | in the comment. A manifest must have exactly one <b>D</b> card. The sole argument to the <b>D</b> card is a date-time stamp in the ISO8601 format. The date and time should be in coordinated universal time (UTC). The format one of: | | | < < | 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | in the comment. A manifest must have exactly one <b>D</b> card. The sole argument to the <b>D</b> card is a date-time stamp in the ISO8601 format. The date and time should be in coordinated universal time (UTC). The format one of: <pre class="indent"><i>YYYY-MM-DD<b>T</b>HH:MM:SS YYYY-MM-DD<b>T</b>HH:MM:SS.SSS</i></pre> A manifest has zero or more <b>F</b> cards. Each <b>F</b> card identifies a file that is part of the check-in. There are one, two, three, or four arguments. The first argument is the pathname of the file in the check-in relative to the root of the project file hierarchy. No ".." or "." directories are allowed within the filename. Space characters are escaped as in <b>C</b> card comment text. Backslash characters and |
| ︙ | ︙ | |||
261 262 263 264 265 266 267 | Clusters are used during repository synchronization to help reduce network traffic. As such, clusters are an optimization and may be removed from a repository without loss or damage to the underlying project code. Allowed cards in the cluster are as follows: | | | | | | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | Clusters are used during repository synchronization to help reduce network traffic. As such, clusters are an optimization and may be removed from a repository without loss or damage to the underlying project code. Allowed cards in the cluster are as follows: <div class="indent"> <b>M</b> <i>artifact-id</i><br /> <b>Z</b> <i>checksum</i> </div> A cluster contains one or more <b>M</b> cards followed by a single <b>Z</b> card. Each <b>M</b> card has a single argument which is the artifact ID of another artifact in the repository. The <b>Z</b> card works exactly like the <b>Z</b> card of a manifest. The argument to the <b>Z</b> card is the lower-case hexadecimal representation of the MD5 checksum of all prior cards in the cluster. The <b>Z</b> card is required. An example cluster from Fossil can be seen [/artifact/d03dbdd73a2a8 | here]. <h3 id="ctrl">2.3 Control Artifacts</h3> Control artifacts are used to assign properties to other artifacts within the repository. Allowed cards in a control artifact are as follows: <div class="indent"> <b>D</b> <i>time-and-date-stamp</i><br /> <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i><br /> </div> A control artifact must have one <b>D</b> card, one <b>U</b> card, one <b>Z</b> card and one or more <b>T</b> cards. No other cards or other text is allowed in a control artifact. Control artifacts might be PGP clearsigned. The <b>D</b> card and the <b>Z</b> card of a control artifact are the same |
| ︙ | ︙ | |||
336 337 338 339 340 341 342 | <h3 id="wikichng">2.4 Wiki Pages</h3> A wiki artifact defines a single version of a single wiki page. Wiki artifacts accept the following card types: | | | | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | <h3 id="wikichng">2.4 Wiki Pages</h3> A wiki artifact defines a single version of a single wiki page. Wiki artifacts accept the following card types: <div class="indent"> <b>C</b> <i>change-comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br /> <b>L</b> <i>wiki-title</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</i>+<br /> <b>U</b> <i>user-name</i><br /> <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> <b>Z</b> <i>checksum</i> </div> The <b>D</b> card is the date and time when the wiki page was edited. The <b>P</b> card specifies the parent wiki pages, if any. The <b>L</b> card gives the name of the wiki page. The optional <b>N</b> card specifies the mimetype of the wiki text. If the <b>N</b> card is omitted, the mimetype is assumed to be text/x-fossil-wiki. The <b>U</b> card specifies the login |
| ︙ | ︙ | |||
377 378 379 380 381 382 383 | [/artifact?name=7b2f5fd0e0&txt=1 | here]. <h3 id="tktchng">2.5 Ticket Changes</h3> A ticket-change artifact represents a change to a trouble ticket. The following cards are allowed on a ticket change artifact: | | | | 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 | [/artifact?name=7b2f5fd0e0&txt=1 | here]. <h3 id="tktchng">2.5 Ticket Changes</h3> A ticket-change artifact represents a change to a trouble ticket. The following cards are allowed on a ticket change artifact: <div class="indent"> <b>D</b> <i>time-and-date-stamp</i><br /> <b>J</b> ?<b>+</b>?<i>name</i> ?<i>value</i>?<br /> <b>K</b> <i>ticket-id</i><br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i> </div> The <b>D</b> card is the usual date and time stamp and represents the point in time when the change was entered. The <b>U</b> card is the login of the programmer who entered this change. The <b>Z</b> card is the required checksum over the entire artifact. Every ticket has a distinct ticket-id: |
| ︙ | ︙ | |||
425 426 427 428 429 430 431 | An attachment artifact associates some other artifact that is the attachment (the source artifact) with a ticket or wiki page or technical note to which the attachment is connected (the target artifact). The following cards are allowed on an attachment artifact: | | | | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 | An attachment artifact associates some other artifact that is the attachment (the source artifact) with a ticket or wiki page or technical note to which the attachment is connected (the target artifact). The following cards are allowed on an attachment artifact: <div class="indent"> <b>A</b> <i>filename target</i> ?<i>source</i>?<br /> <b>C</b> <i>comment</i><br /> <b>D</b> <i>time-and-date-stamp</i><br /> <b>N</b> <i>mimetype</i><br /> <b>U</b> <i>user-name</i><br /> <b>Z</b> <i>checksum</i> </div> The <b>A</b> card specifies a filename for the attachment in its first argument. The second argument to the <b>A</b> card is the name of the wiki page or ticket or technical note to which the attachment is connected. The third argument is either missing or else it is the lower-case artifact ID of the attachment itself. A missing third argument means that the attachment should be deleted. |
| ︙ | ︙ | |||
467 468 469 470 471 472 473 | A technical note or "technote" artifact (formerly known as an "event" artifact) associates a timeline comment and a page of text (similar to a wiki page) with a point in time. Technotes can be used to record project milestones, release notes, blog entries, process checkpoints, or news articles. The following cards are allowed on an technote artifact: | | | | 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 | A technical note or "technote" artifact (formerly known as an "event" artifact) associates a timeline comment and a page of text (similar to a wiki page) with a point in time. Technotes can be used to record project milestones, release notes, blog entries, process checkpoints, or news articles. The following cards are allowed on an technote artifact: <div class="indent"> <b>C</b> <i>comment</i><br> <b>D</b> <i>time-and-date-stamp</i><br /> <b>E</b> <i>technote-time</i> <i>technote-id</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</i>+<br /> <b>T</b> <b>+</b><i>tag-name</i> <b>*</b> ?<i>value</i>?<br /> <b>U</b> <i>user-name</i><br /> <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> <b>Z</b> <i>checksum</i> </div> The <b>C</b> card contains text that is displayed on the timeline for the technote. The <b>C</b> card is optional, but there can only be one. A single <b>D</b> card is required to give the date and time when the technote artifact was created. This is different from the time at which the technote appears on the timeline. |
| ︙ | ︙ | |||
532 533 534 535 536 537 538 | <h3 id="forum">2.8 Forum Posts</h3> Forum posts are intended as a mechanism for users and developers to discuss a project. Forum posts are like messages on a mailing list. The following cards are allowed on an forum post artifact: | | | | 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 | <h3 id="forum">2.8 Forum Posts</h3> Forum posts are intended as a mechanism for users and developers to discuss a project. Forum posts are like messages on a mailing list. The following cards are allowed on an forum post artifact: <div class="indent"> <b>D</b> <i>time-and-date-stamp</i><br /> <b>G</b> <i>thread-root</i><br /> <b>H</b> <i>thread-title</i><br /> <b>I</b> <i>in-reply-to</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</i><br /> <b>U</b> <i>user-name</i><br /> <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> <b>Z</b> <i>checksum</i> </div> Every forum post must have either one <b>I</b> card and one <b>G</b> card or one <b>H</b> card. Forum posts are organized into topic threads. The initial post for a thread (the root post) has an <b>H</b> card giving the title or subject for that thread. The argument to the <b>H</b> card is a string in the same format as a comment string in a <b>C</b> card. |
| ︙ | ︙ | |||
608 609 610 611 612 613 614 | The following table summarizes the various kinds of cards that appear on Fossil artifacts. A blank entry means that combination of card and artifact is not legal. A number or range of numbers indicates the number of times a card may (or must) appear in the corresponding artifact type. e.g. a value of 1 indicates a required unique card and 1+ indicates that one or more such cards are required. | | | < < < | 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 | The following table summarizes the various kinds of cards that appear on Fossil artifacts. A blank entry means that combination of card and artifact is not legal. A number or range of numbers indicates the number of times a card may (or must) appear in the corresponding artifact type. e.g. a value of 1 indicates a required unique card and 1+ indicates that one or more such cards are required. <table> <tr> <th>⇩ Card Format / Used By ⇨</th> <th>Manifest</th> <th>Cluster</th> <th>Control</th> <th>Wiki</th> <th>Ticket</th> <th>Attachment</th> <th>Technote</th> |
| ︙ | ︙ | |||
907 908 909 910 911 912 913 | wrong order. Both bugs have now been fixed. However, to prevent historical Technical Note artifacts that were inserted by users in good faith from being rejected by newer Fossil builds, the card ordering requirement is relaxed slightly. The actual implementation is this: | | | | 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 | wrong order. Both bugs have now been fixed. However, to prevent historical Technical Note artifacts that were inserted by users in good faith from being rejected by newer Fossil builds, the card ordering requirement is relaxed slightly. The actual implementation is this: <p class=blockquote> "All cards must be in strict lexicographic order, except that the N and P cards of a Technical Note artifact are allowed to be interchanged." </p> Future versions of Fossil might strengthen this slightly to only allow the out of order N and P cards for Technical Notes entered before a certain date. <h3>4.2 R-Card Hash Calculation</h3> |
| ︙ | ︙ |
Changes to www/forum.wiki.
| ︙ | ︙ | |||
132 133 134 135 136 137 138 | The remainder of this section summarizes the differences you're expected to see when taking option #2. The first thing is that you'll need to add something like the following to the Header part of the skin to create the navbar link: <verbatim> | | | | | | | | | | | | | | 132 133 134 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 180 181 182 183 184 185 186 187 188 189 190 191 |
The remainder of this section summarizes the differences you're expected
to see when taking option #2.
The first thing is that you'll need to add something like the following
to the Header part of the skin to create the navbar link:
<verbatim>
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
menulink /forum Forum
}
</verbatim>
These rules say that any logged-in user with any [./caps/ref.html#2 |
forum-related capability] or an anonymous user <b>RdForum</b> or
<b>WrForum</b> capability will see the "Forum" navbar
link, which just takes you to <tt>/forum</tt>.
The exact code you need here varies depending on which skin you're
using. Follow the style you see for the other navbar links.
The new forum feature also brings many new CSS styles to the table. If
you're using the stock skin or something sufficiently close, the changes
may work with your existing skin as-is. Otherwise, you might need to
adjust some things, such as the background color used for the selected
forum post:
<verbatim>
div.forumSel {
background-color: rgba(0, 0, 0, 0.05);
}
</verbatim>
That overrides the default — a hard-coded light cyan — with a 95%
transparent black overlay instead, which simply darkens your skin's
normal background color underneath the selected post. That should work
with almost any background color except for very dark background colors.
For dark skins, an inverse of the above trick will work better:
<verbatim>
div.forumSel {
background-color: rgba(255, 255, 255, 0.05);
}
</verbatim>
That overlays the background with 5% white to lighten it slightly.
Another new forum-related CSS style you might want to reflect into your
existing skin is:
<verbatim>
div.forumPosts a:visited {
color: #6A7F94;
}
</verbatim>
This changes the clicked-hyperlink color for the forum post links on the
main <tt>/forum</tt> page only, which allows your browser's history
mechanism to show which threads a user has read and which not. The link
color will change back to the normal link color — indicating "unread" —
when a reply is added to an existing thread because that changes where
|
| ︙ | ︙ |
Changes to www/fossil-v-git.wiki.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 | <h2>2.0 Differences Between Fossil And Git</h2> Differences between Fossil and Git are summarized by the following table, with further description in the text that follows. | | | > | | > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<h2>2.0 Differences Between Fossil And Git</h2>
Differences between Fossil and Git are summarized by the following table,
with further description in the text that follows.
<table style="width: fit-content">
<tr><th>GIT</th><th>FOSSIL</th><th>more</th></tr>
<tr>
<td>File versioning only</td>
<td>
VCS, tickets, wiki, docs, notes, forum, chat, UI,
[https://en.wikipedia.org/wiki/Role-based_access_control|RBAC]
</td>
<td><a href="#features">2.1 ↓</a></td>
</tr>
<tr>
<td>A federation of many small programs</td>
<td>One self-contained, stand-alone executable</td>
<td><a href="#selfcontained">2.2 ↓</a></td>
</tr>
|
| ︙ | ︙ | |||
95 96 97 98 99 100 101 |
<td><a href="#testing">2.8 ↓</a></td>
</tr>
<tr>
<td>SHA-1 or SHA-2</td>
<td>SHA-1 and/or SHA-3, in the same repository</td>
<td><a href="#hash">2.9 ↓</a></td>
</tr>
| | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
<td><a href="#testing">2.8 ↓</a></td>
</tr>
<tr>
<td>SHA-1 or SHA-2</td>
<td>SHA-1 and/or SHA-3, in the same repository</td>
<td><a href="#hash">2.9 ↓</a></td>
</tr>
</table>
<h3 id="features">2.1 Featureful</h3>
Git provides file versioning services only, whereas Fossil adds
an integrated [./wikitheory.wiki | wiki],
[./bugtheory.wiki | ticketing & bug tracking],
[./embeddeddoc.wiki | embedded documentation],
|
| ︙ | ︙ | |||
795 796 797 798 799 800 801 | which every commit is tested first. It encourages thinking before acting. We believe this is an inherently good thing. Incidentally, this is a good example of Git's messy command design. These three commands: <pre> | | | | | | | | 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | which every commit is tested first. It encourages thinking before acting. We believe this is an inherently good thing. Incidentally, this is a good example of Git's messy command design. These three commands: <pre> $ git merge HASH $ git cherry-pick HASH $ git revert HASH </pre> ...are all the same command in Fossil: <pre> $ fossil merge HASH $ fossil merge --cherrypick HASH $ fossil merge --backout HASH </pre> If you think about it, they're all the same function: apply work done on one branch to another. All that changes between these commands is how much work gets applied — just one check-in or a whole branch — and the merge direction. This is the sort of thing we mean when we point out that Fossil's command interface is simpler than Git's: there are fewer |
| ︙ | ︙ | |||
841 842 843 844 845 846 847 | Fossil delivered a new release allowing a clean migration to [https://en.wikipedia.org/wiki/SHA-3|256-bit SHA-3] with [./hashpolicy.wiki|full backwards compatibility] to old SHA-1 based repositories. In October 2019, after the last of the major binary package repos offering Fossil upgraded to Fossil 2.<i>x</i>, | | | | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 | Fossil delivered a new release allowing a clean migration to [https://en.wikipedia.org/wiki/SHA-3|256-bit SHA-3] with [./hashpolicy.wiki|full backwards compatibility] to old SHA-1 based repositories. In October 2019, after the last of the major binary package repos offering Fossil upgraded to Fossil 2.<i>x</i>, we switched the default hash mode so that the conversion to SHA-3 is fully automatic. This not only solves the SHAttered problem, it should prevent a reoccurrence of similar problems for the foreseeable future. Meanwhile, the Git community took until August 2018 to publish [https://git-scm.com/docs/hash-function-transition/|their first plan] for solving the same problem by moving to SHA-256, a variant of the |
| ︙ | ︙ | |||
947 948 949 950 951 952 953 |
<li><p>Both Fossil and Git support
[https://en.wikipedia.org/wiki/Patch_(Unix)|<tt>patch(1)</tt>
files] — unified diff formatted output — for accepting drive-by contributions, but it's a
lossy contribution path for both systems. Unlike Git PRs and Fossil
bundles, patch files collapse multiple checkins together, they don't
include check-in comments, and they cannot encode changes made above
the individual file content layer: you lose branching decisions,
| | | | 949 950 951 952 953 954 955 956 957 958 959 960 961 962 |
<li><p>Both Fossil and Git support
[https://en.wikipedia.org/wiki/Patch_(Unix)|<tt>patch(1)</tt>
files] — unified diff formatted output — for accepting drive-by contributions, but it's a
lossy contribution path for both systems. Unlike Git PRs and Fossil
bundles, patch files collapse multiple checkins together, they don't
include check-in comments, and they cannot encode changes made above
the individual file content layer: you lose branching decisions,
tag changes, file renames, and more when using patch files. The
[./patchcmd.md | <tt>fossil patch</tt> command]
also solves these problems, but it is because it works like a Fossil
bundle, only for uncommitted changes; it doesn't use Larry Wall's
<tt>patch</tt> tool to apply unified diff output to the receiving
Fossil checkout.</p></li>
</ol></i></small>
|
Changes to www/fossil_prompt.wiki.
1 | <title>Fossilized Bash Prompt</title> | < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <title>Fossilized Bash Prompt</title> Dan Kennedy has contributed a [./fossil_prompt.sh?mimetype=text/plain | bash script] that manipulates the bash prompt to show the status of the Fossil repository that the user is currently visiting. The prompt shows the branch, version, and time stamp for the current checkout, and the prompt changes colors from blue to red when there are uncommitted changes. To try out this script, simply download it from the link above, then type: <pre> . fossil_prompt.sh </pre> For a permanent installation, you can graft the code into your <tt>.bashrc</tt> file in your home directory. The code is very simple (only 32 non-comment lines, as of this writing) and hence easy to customized. |
Changes to www/gitusers.md.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 | advocate a switch-in-place working mode instead, so that is how most users end up working with Git. Contrast [Fossil’s check-out workflow document][ckwf] to see the practical differences. There is one Git-specific detail we wish to add beyond what that document already covers. This command: | | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
advocate a switch-in-place working mode instead, so that is how most
users end up working with Git. Contrast [Fossil’s check-out workflow
document][ckwf] to see the practical differences.
There is one Git-specific detail we wish to add beyond what that
document already covers. This command:
git checkout some-branch
…is best given as:
fossil update some-branch
…in Fossil. There is a [`fossil checkout`][co] command, but it has
[several differences](./co-vs-up.md) that make it less broadly useful
than [`fossil update`][up] in everyday operation, so we recommend that
Git users moving to Fossil develop a habit of typing `fossil up` rather
than `fossil checkout`. That said, one of those differences does match
up with Git users’ expectations: `fossil checkout` doesn’t pull changes
|
| ︙ | ︙ | |||
107 108 109 110 111 112 113 | choice also tends to make Fossil feel comfortable to Subversion expatriates.) The `fossil pull` command is simply the reverse of `fossil push`, so that `fossil sync` [is functionally equivalent to](./sync.wiki#sync): | | | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
choice also tends to make Fossil feel comfortable to Subversion
expatriates.)
The `fossil pull` command is simply the reverse of
`fossil push`, so that `fossil sync` [is functionally equivalent
to](./sync.wiki#sync):
fossil push ; fossil pull
There is no implicit “and update the local working directory” step in Fossil’s
push, pull, or sync commands, as there is with `git pull`.
Someone coming from the Git perspective may perceive that `fossil up`
has two purposes:
|
| ︙ | ︙ | |||
137 138 139 140 141 142 143 | [gpull]: https://git-scm.com/docs/git-pull [gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well #### <a id="close" name="dotfile"></a> Closing a Check-Out The [`fossil close`][close] command dissociates a check-out directory from the | | | | | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | [gpull]: https://git-scm.com/docs/git-pull [gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well #### <a id="close" name="dotfile"></a> Closing a Check-Out The [`fossil close`][close] command dissociates a check-out directory from the Fossil repository database, _nondestructively_ inverting [`fossil open`][open]. (Contrast Git’s [closest alternative](#worktree), `git worktree remove`, which *is* destructive!) This Fossil command does not remove the managed files, and unless you give the `--force` option, it won’t let you close the check-out with uncommitted changes to those managed files. The `close` command also refuses to run without `--force` when you have certain other precious per-checkout data that Fossil stores in the `.fslckout` file at the root of a check-out directory. This is a SQLite |
| ︙ | ︙ | |||
178 179 180 181 182 183 184 | There are at least three different ways to get [Fossil-style multiple check-out directories][mcw] with Git. The old way is to simply symlink the `.git` directory between working trees: | | | | | | | | | | | | | | | | | | | | | 178 179 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
There are at least three different ways to get [Fossil-style multiple
check-out directories][mcw] with Git.
The old way is to simply symlink the `.git` directory between working
trees:
mkdir ../foo-branch
ln -s ../actual-clone-dir/.git .
git checkout foo-branch
The symlink trick has a number of problems, the largest being that
symlinks weren’t available on Windows until Vista, and until the Windows
10 Creators Update was released in spring of 2017, you had to be an
Administrator to use the feature besides. ([Source][wsyml]) Git 2.5 solved
this problem back when Windows XP was Microsoft’s current offering
by adding the `git-worktree` command:
git worktree add ../foo-branch foo-branch
cd ../foo-branch
That is approximately equivalent to this in Fossil:
mkdir ../foo-branch
cd ../foo-branch
fossil open /path/to/repo.fossil foo-branch
The Fossil alternative is wordier, but since this tends to be one-time setup,
not something you do everyday, the overhead is insignificant. This author keeps a “scratch” check-out
for cases where it’s inappropriate to reuse the “trunk” check-out,
isolating all of my expedient switch-in-place actions to that one
working directory. Since the other peer check-outs track long-lived
branches, and that set rarely changes once a development machine is set
up, I rarely pay the cost of these wordier commands.
That then leads us to the closest equivalent in Git to [closing a Fossil
check-out](#close):
git worktree remove .
Note, however, that unlike `fossil close`, once the Git command
determines that there are no uncommitted changes, it blows away all of
the checked-out files! Fossil’s alternative is shorter, easier to
remember, and safer.
There’s another way to get Fossil-like separate worktrees in Git:
git clone --separate-git-dir repo.git https://example.com/repo
This allows you to have your Git repository directory entirely separate
from your working tree, with `.git` in the check-out directory being a
file that points to `../repo.git`, in this example.
[mcw]: ./ckout-workflows.md#mcw
[wsyml]: https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/
#### <a id="iip"></a> Init in Place
To illustrate the differences that Fossil’s separation of repository
from working directory creates in practice, consider this common Git “init in place”
method for creating a new repository from an existing tree of files,
perhaps because you are placing that project under version control for
the first time:
cd long-established-project
git init
git add *
git commit -m "Initial commit of project."
The closest equivalent in Fossil is:
cd long-established-project
fossil init .fsl
fossil open --force .fsl
fossil add *
fossil ci -m "Initial commit of project."
Note that unlike in Git, you can abbreviate the “`commit`” command in
Fossil as “`ci`” for compatibility with CVS, Subversion, etc.
This creates a `.fsl` repo DB at the root of the project check-out to
emulate the `.git` repo dir. We have to use the `--force` flag on
opening the new repo because Fossil expects you to open a repo into an
|
| ︙ | ︙ | |||
298 299 300 301 302 303 304 | constants are equal. Yet the constants are *not* equal because Fossil reads from a single disk file rather than visit potentially many files in sequence as Git must, so the OS’s buffer cache can result in [still better performance][35pct]. | | | | | | | | | | | | | | | | | | | | | | | | 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 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 |
constants are equal.
Yet the constants are *not* equal because Fossil
reads from a single disk file rather than visit potentially many
files in sequence as Git must, so the OS’s buffer cache can result in
[still better performance][35pct].
Unlike Git’s log, Fossil’s timeline shows info across all branches by
default, a feature for maintaining better situational awareness.
It is possible to restrict the timeline to a single branch using `fossil timeline -b`.
Similarly, to restrict the timeline using the web UI equivalent,
click the name of a branch on the `/timeline` or `/brlist` page. (Or
manually, by adding the `r=` query parameter.) Note that even in this
case, the Fossil timeline still shows other branches where they interact
with the one you’ve referenced in this way; again, better situational
awareness.
#### <a id="emu-log"></a> Emulating `git log`
If you truly need a backwards-in-time-only view of history in Fossil to
emulate `git log`, this is as close as you can currently come:
fossil timeline parents current
Again, though, this isn’t restricted to a single branch, as `git log`
is.
Another useful rough equivalent is:
git log --raw
fossil time -v
This shows what changed in each version, though Fossil’s view is more a
summary than a list of raw changes. To dig deeper into single commits,
you can use Fossil’s [`info` command][infoc] or its [`/info` view][infow].
Inversely, you may more exactly emulate the default `fossil timeline`
output with `git log --name-status`.
#### <a id="whatchanged"></a> What Changed?
A related — though deprecated — command is `git whatchanged`, which gives results similar to
`git log --raw`, so we cover it here.
Though there is no `fossil whatchanged` command, the same sort of
information is available. For example, to pull the current changes from
the remote repository and then inspect them before updating the local
working directory, you might say this in Git:
git fetch
git whatchanged ..@{u}
…which you can approximate in Fossil as:
fossil pull
fossil up -n
fossil diff --from tip
To invert the `diff` to show a more natural patch, the command needs to
be a bit more complicated, since you can’t currently give `--to`
without `--from`.
fossil diff --from current --to tip
Rather than use the “dry run” form of [the `update` command][up], you can
say:
fossil timeline after current
…or if you want to restrict the output to the current branch:
fossil timeline descendants current
#### <a id="ckin-names"></a> Symbolic Check-In Names
Note the use of [human-readable symbolic version names][scin] in Fossil
rather than [Git’s cryptic notations][gcn].
For a more dramatic example of this, let us ask Git, “What changed since the
beginning of last month?” being October 2020 as I write this:
git log master@{2020-10-01}..HEAD
That’s rather obscure! Fossil answers the same question with a simpler
command:
fossil timeline after 2020-10-01
You may need to add `-n 0` to bypass the default output limit of
`fossil timeline`, 20 entries. Without that, this command reads
almost like English.
Some Git users like to write commands like the above so:
git log @{2020-10-01}..@
Is that better? “@” now means two different things: an at-time reference
and a shortcut for `HEAD`!
If you are one of those that like short commands, Fossil’s method is
less cryptic: it lets you shorten words in most cases up to the point
that they become ambiguous. For example, you may abbreviate the last
`fossil` command in the prior section:
fossil tim d c
…beyond which the `timeline` command becomes ambiguous with `ticket`.
Some Fossil users employ shell aliases, symlinks, or scripts to shorten
the command still further:
alias f=fossil
f tim d c
Granted, that’s rather obscure, but you you can also choose something
intermediate like “`f time desc curr`”, which is reasonably clear.
[35pct]: https://www.sqlite.org/fasterthanfs.html
[btree]: https://sqlite.org/btreemodule.html
[gcn]: https://git-scm.com/docs/gitrevisions
|
| ︙ | ︙ | |||
455 456 457 458 459 460 461 462 463 464 465 466 467 | character and such, because it doesn’t recognize Fossil commit messages and apply similar rules as to Git commit messages. [cskin]: ./customskin.md [lsl]: https://chris.beams.io/posts/git-commit/#limit-50 <a id="staging"></a> ## There Is No Staging Area Fossil omits the "Git index" or "staging area" concept. When you type "`fossil commit`" _all_ changes in your check-out are committed, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > | > > > > > > | | < < < < < < | < < < | < | | < < | | > | | | | | | | | 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 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 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 |
character and such, because it doesn’t recognize Fossil commit messages
and apply similar rules as to Git commit messages.
[cskin]: ./customskin.md
[lsl]: https://chris.beams.io/posts/git-commit/#limit-50
<a id="autocommit"></a>
## Fossil Never Auto-Commits
There are several features in Git besides its `commit` command that
produce a new commit to the repository, and by default, they do it
without prompting or even asking for a commit message. These include
Git’s [`rebase`](#rebase), `merge`, and [`cherrypick`](#cpickrev)
commands, plus the [commit splitting](#comsplit) sub-feature
“`git commit -p`”.
Fossil never does this, on firm philosophical grounds: we wish to be
able to test that each potentially repository-changing command does not
break anything _before_ freezing it immutably into the [Merkle
tree](./blockchain.md). Where Fossil has equivalent commands, they
modify the checkout tree alone, requiring a separate `commit` command
afterward, withheld until the user has satisfied themselves that the
command’s result is correct.
We believe this is the main reason Git lacks an [autosync](#autosync)
feature: making push a manual step gives the user a chance to rewrite
history after triggering one of these autocommits locally, should the
automatic commit fail to work out as expected. Fossil chooses the
inverse path under the philosophy that commits are *commitments,* not
something you’re allowed to go back and rewrite later.
This is also why there is no automatic commit message writing feature in
Fossil, as in these autocommit-triggering Git commands. The user is
meant to write the commit message by hand after they are sure it’s
correct, in clear-headed retrospective fashion. Having the tool do it
prospectively before one can test the result is simply backwards.
<a id="staging"></a>
## There Is No Staging Area
Fossil omits the "Git index" or "staging area" concept. When you
type "`fossil commit`" _all_ changes in your check-out are committed,
by default. There is no need for the "-a" option as with Git.
If you only want to commit _some_ of the changes, list the names
of the files or directories you want to commit as arguments, like this:
fossil commit src/feature.c doc/feature.md examples/feature
Note that the last element is a directory name, meaning “any changed
file under the `examples/feature` directory.”
<a id="comsplit"></a>
## Commit Splitting
[Git’s commit splitting features][gcspl] rely on
other features of Git that Fossil purposefully lacks, as covered in the
prior two sections: [autocommit](#autocommit) and [the staging
area](#staging).
While there is no direct Fossil equivalent for
`git add -p`, `git commit -p`, or `git rebase -i`, you can get the same
effect by converting an uncommitted change set to a patch and then
running it through [Patchouli].
Rather than use `fossil diff -i` to produce such a patch, a safer and
more idiomatic method would be:
fossil stash save -m 'my big ball-o-hackage'
fossil stash diff > my-changes.patch
That stores your changes in the stash, then lets you operate on a copy
of that patch. Each time you re-run the second command, it will take the
current state of the working directory into account to produce a
potentially different patch, likely smaller because it leaves out patch
hunks already applied.
In this way, the combination of working tree and stash replaces the need
for Git’s index feature.
We believe we know how to do commit splitting in a way compatible with
the Fossil philosophy, without following Git’s ill-considered design
leads. It amounts to automating the above process through an interactive
variant of [`fossil stash apply`][stash], as currently prototyped in the
third-party tool [`fnc`][fnc] and [its interactive `stash`
command][fncsta]. We merely await someone’s [contribution][ctrb] of this
feature into Fossil proper.
[ctrb]: https://fossil-scm.org/fossil/doc/trunk/www/contribute.wiki
[fnc]: https://fnc.bsdbox.org/
[fncsta]: https://fnc.bsdbox.org/uv/doc/fnc.1.html#stash
[gcspl]: https://git-scm.com/docs/git-rebase#_splitting_commits
[Patchouli]: https://pypi.org/project/patchouli/
<a id="bneed"></a>
## Create Branches at Point of Need, Rather Than Ahead of Need
Fossil prefers that you create new branches as part of the first commit
on that branch:
fossil commit --branch my-branch
If that commit is successful, your local check-out directory is then
switched to the tip of that branch, so subsequent commits don’t need the
“`--branch`” option. You simply say `fossil commit` again to continue
adding commits to the tip of that branch.
To switch back to the parent branch, say something like:
fossil update trunk
(This is approximately equivalent to `git checkout master`.)
Fossil does also support the Git style, creating the branch ahead of
need:
fossil branch new my-branch
fossil up my-branch
...work on first commit...
fossil commit
This is more verbose, giving the same overall effect though the initial
actions are inverted: create a new branch for the first commit, switch
the check-out directory to that branch, and make that first commit. As
above, subsequent commits are descendants of that initial branch commit.
We think you’ll agree that creating a branch as part of the initial
commit is simpler.
Fossil also allows you to move a check-in to a different branch
*after* you commit it, using the "`fossil amend`" command.
For example:
fossil amend current --branch my-branch
This works by inserting a tag into the repository that causes the web UI
to relabel commits from that point forward with the new name. Like Git,
Fossil’s fundamental data structure is the interlinked DAG of commit
hashes; branch names are supplemental data for making it easier for the
humans to understand this DAG, so this command does not change the core
history of the project, only annotate it for better display to the
|
| ︙ | ︙ | |||
589 590 591 592 593 594 595 | [Fossil is an AP-mode system][capt], which in this case means it works *very hard* to ensure that all repos are as close to identical as it can make them under this eventually-consistent design philosophy. Branch *names* sync automatically in Fossil, not just the content of those branches. That means this common Git command: | | | | | 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 |
[Fossil is an AP-mode system][capt], which in this case means it works
*very hard* to ensure that all repos are as close to identical as it can
make them under this eventually-consistent design philosophy.
Branch *names* sync automatically in Fossil, not just the
content of those branches. That means this common Git command:
git push origin master
…is simply this in Fossil:
fossil push
Fossil doesn’t need to be told what to push or where to push it: it just
keeps using the same remote server URL you gave it last
until you [tell it to do something different][rem]. It pushes all
branches, not just one named local branch.
[capt]: ./cap-theorem.md
[rem]: /help?cmd=remote
<a id="autosync"></a>
## Autosync
Fossil’s [autosync][wflow] feature, normally enabled, has no
equivalent in Git. If you want Fossil to behave like Git, you can turn
it off:
fossil set autosync 0
Let’s say that you have a typical server-and-workstations model with two
working clones on different machines, that you have disabled autosync,
and that this common sequence then occurs:
1. Alice commits to her local clone and *separately* pushes the change
up to Condor — their central server — in typical Git fashion.
|
| ︙ | ︙ | |||
690 691 692 693 694 695 696 | We make no guarantee that there will always be a line beginning with “`repo`” and that it will be separated from the repository’s file name by a colon. The simplified example above is also liable to become confused by whitespace in file names.) ``` | | | | | | | | | | | 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 756 757 758 759 |
We make no guarantee that there will always be a line beginning with
“`repo`” and that it will be separated from the repository’s file name
by a colon. The simplified example above is also liable to become
confused by whitespace in file names.)
```
$ repo=$(fossil status | grep ^repo | cut -f2 -d:)
$ url=$(fossil remote)
$ fossil close # Stop here and think if it warns you!
$ mv $repo ${repo}.old
$ fossil clone $url $repo
$ fossil open --force $repo
```
What, then, should you as a Git transplant do instead when you find
yourself reaching for “`git reset`”?
Since the correct answer to that depends on why you think it’s a good
solution to your immediate problem, we’ll take our motivating scenario
from the problem setup above, where we discussed Fossil’s [autosync]
feature. Let us further say Alice’s pique results from a belief that
Bob’s commit is objectively wrong-headed and should be expunged
henceforth. Since Fossil goes out of its way to ensure that [commits are
durable][wdm], it should be no further surprise that there is no easier
method to reset Bob’s clone in favor of Alice’s than the above sequence
in Fossil’s command set. Except in extreme situations, we believe that
sort of thing is unnecessary.
Instead, Bob can say something like this:
```
fossil amend --branch MISTAKE --hide --close -m "mea culpa" tip
fossil up trunk
fossil push
```
Unlike in Git, the “`amend`” command doesn’t modify prior committed
artifacts. Bob’s first command doesn’t delete anything, merely tells
Fossil to hide his mistake from timeline views by inserting a few new
records into the local repository to change how the client interprets
the data it finds there henceforth.(^One to change the tag marking this
|
| ︙ | ︙ | |||
748 749 750 751 752 753 754 | to return her check-out’s parent commit to the previous version lest her next attempted commit land atop this mistake branch. The fact that Bob marked the branch as closed will prevent that from going thru, cluing Alice into what she needs to do to remedy the situation, but that merely shows why it’s a better workflow if Alice makes the amendment herself: ``` | | | | | | 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 | to return her check-out’s parent commit to the previous version lest her next attempted commit land atop this mistake branch. The fact that Bob marked the branch as closed will prevent that from going thru, cluing Alice into what she needs to do to remedy the situation, but that merely shows why it’s a better workflow if Alice makes the amendment herself: ``` fossil amend --branch MISTAKE --hide --close \ -m "shunt Bob’s erroneous commit off" tip fossil up trunk fossil push ``` Then she can fire off an email listing Bob’s assorted failings and go about her work. This asynchronous workflow solves the problem without requiring explicit coordination with Bob. When he gets his email, he can then say “`fossil up trunk`” himself, which by default will trigger an autosync, pulling down Alice’s amendments and getting him back onto her |
| ︙ | ︙ | |||
779 780 781 782 783 784 785 | is "`trunk`". The "`trunk`" branch in Fossil corresponds to the "`master`" branch in stock Git or to [the “`main`” branch in GitHub][mbgh]. Because the `fossil git export` command has to work with both stock Git and with GitHub, Fossil uses Git’s traditional default rather than GitHub’s new default: your Fossil repo’s “trunk” branch becomes “master” when [mirroring to GitHub][mirgh] unless you give the `--mainbranch` | | | 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | is "`trunk`". The "`trunk`" branch in Fossil corresponds to the "`master`" branch in stock Git or to [the “`main`” branch in GitHub][mbgh]. Because the `fossil git export` command has to work with both stock Git and with GitHub, Fossil uses Git’s traditional default rather than GitHub’s new default: your Fossil repo’s “trunk” branch becomes “master” when [mirroring to GitHub][mirgh] unless you give the `--mainbranch` option. We do not know what happens on subsequent exports if you later rename this branch on the GitHub side. [mbgh]: https://github.com/github/renaming [mirgh]: ./mirrortogithub.md |
| ︙ | ︙ | |||
815 816 817 818 819 820 821 | draft was written, it’s been revised multiple times to address less common objections as well. Chances are not good that you are going to come up with a new objection that we haven’t already considered and addressed there. There is only one sub-feature of `git rebase` that is philosophically compatible with Fossil yet which currently has no functional equivalent. | | | > < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | < < | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 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 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 |
draft was written, it’s been revised multiple times to address less
common objections as well. Chances are not good that you are going to
come up with a new objection that we haven’t already considered and
addressed there.
There is only one sub-feature of `git rebase` that is philosophically
compatible with Fossil yet which currently has no functional equivalent.
We [covered this and the workaround for its lack](#comsplit) above.
[3]: ./rebaseharm.md
<a id="cdiff"></a>
## Colorized Diffs
When you run `git diff` on an ANSI X3.64 capable terminal, it uses color
to distinguish insertions, deletions, and replacements, but as of this
writing, `fossil diff` produces traditional uncolored [unified diff
format][udiff] output, suitable for producing a [patch file][pfile].
There are [many methods](./colordiff.md) for solving this.
Viewed this way, Fossil doesn’t lack colorized diffs, it simply has
*one* method where they *aren’t* colorized.
[pfile]: https://en.wikipedia.org/wiki/Patch_(Unix)
[udiff]: https://en.wikipedia.org/wiki/Diff#Unified_format
## <a id="show"></a> Showing Information About Commits
While there is no direct equivalent to Git’s “`show`” command, similar
functionality is present in Fossil under other commands:
#### <a id="patch"></a> Show a Patch for a Commit
git show -p COMMIT_ID
…gives much the same output as
fossil diff --checkin COMMIT_ID
…only without the patch email header. Git comes out of the [LKML] world,
where emailing a patch is a normal thing to do. Fossil is [designed for
cohesive teams][devorg] where drive-by patches are rarer.
You can use any of [Fossil’s special check-in names][scin] in place of
the `COMMIT_ID` in this and later examples. Fossil docs usually say
“`VERSION`” or “`NAME`” where this is allowed, since the version string
or name might not refer to a commit ID, but instead to a forum post, a
wiki document, etc. For instance, the following command answers the question “What did
I just commit?”
fossil diff --checkin tip
…or equivalently using a different symbolic commit name:
fossil diff --from prev
[devorg]: ./fossil-v-git.wiki#devorg
[LKML]: https://lkml.org/
#### <a id="cmsg"></a> Show a Specific Commit Message
git show -s COMMIT_ID
…is
fossil time -n 1 COMMIT_ID
…or with a shorter, more obvious command, though with more verbose output:
fossil info COMMIT_ID
The `fossil info` command isn’t otherwise a good equivalent to
`git show`; it just overlaps its functionality in some areas. Much of
what’s missing is present in the corresponding [`/info` web
view][infow], though.
#### <a id="dstat"></a> Diff Statistics
Fossil’s closest internal equivalent to commands like
`git show --stat` is:
fossil diff -i --from 2020-04-01 --numstat
The `--numstat` output is a bit cryptic, so we recommend delegating
this task to [the widely-available `diffstat` tool][dst], which gives
a histogram in its default output mode rather than bare integers:
fossil diff -i -v --from 2020-04-01 | diffstat
We gave the `-i` flag in both cases to force Fossil to use its internal
diff implementation, bypassing [your local `diff-command` setting][dcset]
since the `--numstat` option has no effect when you have an external diff
command set.
If you leave off the `-v` flag in the second example, the `diffstat`
output won’t include info about any newly-added files.
[dcset]: https://fossil-scm.org/home/help?cmd=diff-command
[dst]: https://invisible-island.net/diffstat/diffstat.html
<a id="btnames"></a>
## Branch and Tag Names
|
| ︙ | ︙ | |||
997 998 999 1000 1001 1002 1003 | The "[`fossil mv`][mv]" and "[`fossil rm`][rm]" commands work like they do in CVS in that they schedule the changes for the next commit by default: they do not actually rename or delete the files in your check-out. If you don’t like that default, you can change it globally: | | | | 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 |
The "[`fossil mv`][mv]" and "[`fossil rm`][rm]" commands work like they
do in CVS in that they schedule the changes for the next commit by
default: they do not actually rename or delete the files in your
check-out.
If you don’t like that default, you can change it globally:
fossil setting --global mv-rm-files 1
Now these commands behave like in Git in any Fossil repository where
this setting hasn’t been overridden locally.
If you want to keep Fossil’s soft `mv/rm` behavior most of the time, you
can cast it away on a per-command basis:
fossil mv --hard old-name new-name
[mv]: /help?cmd=mv
[rm]: /help?cmd=rm
----
|
| ︙ | ︙ | |||
1030 1031 1032 1033 1034 1035 1036 | history to find a “good” version to anchor the start point of a [`fossil bisect`][fbis] operation. My search engine’s first result for “git checkout by date” is [this highly-upvoted accepted Stack Overflow answer][gcod]. The first command it gives is based on Git’s [`rev-parse` feature][grp]: | | | 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 |
history to find a “good” version to anchor the start point of a
[`fossil bisect`][fbis] operation.
My search engine’s first result for “git checkout by date” is [this
highly-upvoted accepted Stack Overflow answer][gcod]. The first command
it gives is based on Git’s [`rev-parse` feature][grp]:
git checkout master@{2020-03-17}
There are a number of weaknesses in this command. From least to most
critical:
1. It’s a bit cryptic. Leave off the refname or punctuation, and it
means something else. You cannot simplify the cryptic incantation in
the typical use case.
|
| ︙ | ︙ | |||
1070 1071 1072 1073 1074 1075 1076 | Consequently, we cannot recommend this command at all. It’s unreliable even in the best case. That same Stack Overflow answer therefore goes on to recommend an entirely different command: | | | | | | | | | 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 |
Consequently, we cannot recommend this command at all. It’s unreliable even in the
best case.
That same Stack Overflow answer therefore goes on to recommend an
entirely different command:
git checkout $(git rev-list -n 1 --first-parent --before="2020-03-17" master)
We believe you get such answers to Git help requests in part
because of its lack of an always-up-to-date [index into its log](#log) and in
part because of its “small tools loosely joined” design philosophy. This
sort of command is therefore composed piece by piece:
<p style="text-align:center">◆ ◆ ◆</p>
“Oh, I know, I’ll search the rev-list, which outputs commit IDs by
parsing the log backwards from `HEAD`! Easy!”
git rev-list --before=2020-03-17
“Blast! Forgot the commit ID!”
git rev-list --before=2020-03-17 master
“Double blast! It just spammed my terminal with revision IDs! I need to
limit it to the single closest match:
git rev-list -n 1 --before=2020-03-17 master
“Okay, it gives me a single revision ID now, but is it what I’m after?
Let’s take a look…”
git show $(git rev-list -n 1 --before=2020-03-17 master)
“Oops, that’s giving me a merge commit, not what I want.
Off to search the web… Okay, it says I need to give either the
`--first-parent` or `--no-merges` flag to show only regular commits,
not merge-commits. Let’s try the first one:”
git show $(git rev-list -n 1 --first-parent --before=2020-03-17 master)
“Better. Let’s check it out:”
git checkout $(git rev-list -n 1 --first-parent --before=2020-03-17 master)
“Success, I guess?”
<p style="text-align:center">◆ ◆ ◆</p>
This vignette is meant to explain some of Git’s popularity: it rewards
the sort of people who enjoy puzzles, many of whom are software
|
| ︙ | ︙ | |||
1130 1131 1132 1133 1134 1135 1136 | second `git show` command above on [Git’s own repository][gitgh], your results may vary because there were four non-merge commits to Git on the 17th of March, 2020. You may be asking with an exasperated huff, “What is your *point*, man?” The point is that the equivalent in Fossil is simply: | | | 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 |
second `git show` command above on [Git’s own repository][gitgh], your
results may vary because there were four non-merge commits to Git on the
17th of March, 2020.
You may be asking with an exasperated huff, “What is your *point*, man?”
The point is that the equivalent in Fossil is simply:
fossil up 2020-03-17
…which will *always* give the commit closest to midnight UTC on the 17th
of March, 2020, no matter whether you do it on a fresh clone or a stale
one. The answer won’t shift about from one clone to the next or from
one local time of day to the next. We owe this reliability and stability
to three Fossil design choices:
|
| ︙ | ︙ | |||
1179 1180 1181 1182 1183 1184 1185 | and your family’s home NAS. #### Git Method We first need to clone the work repo down to our laptop, so we can work on it at home: | | | | | | | | | | | | | | | | | | | | | | | | 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 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 |
and your family’s home NAS.
#### Git Method
We first need to clone the work repo down to our laptop, so we can work on it
at home:
git clone https://dev-server.example.com/repo
cd repo
git remote rename origin work
The last command is optional, strictly speaking. We could continue to
use Git’s default name for the work repo’s origin — sensibly enough
called “`origin`” — but it makes later commands harder to understand, so
we rename it here. This will also make the parallel with Fossil easier
to draw.
The first time we go home after this, we have to reverse-clone the work
repo up to the NAS:
ssh my-nas.local 'git init --bare /SHARES/dayjob/repo.git'
git push --all ssh://my-nas.local//SHARES/dayjob/repo.git
Realize that this is carefully optimized down to these two long
commands. In practice, we’d expect a user typing these commands by hand from memory
to need to give four or more commands here instead.
Packing the “`git init`” call into the “`ssh`” call is something more
often done in scripts and documentation examples than done interactively,
which then necessitates a third command before the push, “`exit`”.
There’s also a good chance that you’ll forget the need for the `--bare`
option here to avoid a fatal complaint from Git that the laptop can’t
push into a non-empty repo. If you fall into this trap, among the many
that Git lays for newbies, you have to nuke the incorrectly initted
repo, search the web or Git man pages to find out about `--bare`, and try again.
Having navigated that little minefield,
we can tell Git that there is a second origin, a “home” repo in
addition to the named “work” repo we set up earlier:
git remote add home ssh://my-nas.local//SHARES/dayjob/repo.git
git config master.remote home
We don’t have to push or pull because the remote repo is a complete
clone of the repo on the laptop at this point, so we can just get to
work now, committing along the way to get our work safely off-machine
and onto our home NAS, like so:
git add
git commit
git push
We didn’t need to give a remote name on the push because we told it the
new upstream is the home NAS earlier.
Now Friday comes along, and one of your office-mates needs a feature
you’re working on. You agree to come into the office later that
afternoon to sync up via the dev server:
git push work master # send your changes from home up
git pull work master # get your coworkers’ changes
Alternately, we could add “`--set-upstream/-u work`” to the first
command if we were coming into work long enough to do several Git-based things, not just pop in and sync.
That would allow the second to be just “`git pull`”, but the cost is
that when returning home, you’d have to manually reset the upstream
again.
This example also shows a consequence of that fact that
[Git doesn’t sync branch names](#syncall): you have to keep repeating
yourself like an obsequious supplicant: “Master, master.” Didn’t we
invent computers to serve humans, rather than the other way around?
#### Fossil Method
Now we’re going to do the same thing using Fossil, with
the commands arranged in blocks corresponding to those above for comparison.
We start the same way, cloning the work repo down to the laptop:
fossil clone https://dev-server.example.com/repo
cd repo
fossil remote add work https://dev-server.example.com/repo
We’ve chosen the new “`fossil clone URI`” syntax rather than separate
`clone` and `open` commands to make the parallel with Git clearer. [See
above](#mwd) for more on that topic.
Our [`remote` command][rem] is longer than the Git equivalent because
Fossil currently has no short command
to rename an existing remote. Worse, unlike with Git, we can’t just keep
using the default remote name because Fossil uses that slot in its
configuration database to store the *current* remote name, so on
switching from work to home, the home URL will overwrite the work URL if
we don’t give it an explicit name first.
Although the Fossil commands are longer, so far, keep it in perspective:
they’re one-time setup costs,
easily amortized to insignificance by the shorter day-to-day commands
below.
On first beginning to work from home, we reverse-clone the Fossil repo
up to the NAS:
rsync repo.fossil my-nas.local:/SHARES/dayjob/
Now we’re beginning to see the advantage of Fossil’s simpler model,
relative to the tricky “`git init && git push`” sequence above.
Fossil’s alternative is almost impossible to get
wrong: copy this to that. *Done.*
We’re relying on the `rsync` feature that creates up to one level of
missing directory (here, `dayjob/`) on the remote. If you know in
advance that the remote directory already exists, you could use a
slightly shorter `scp` command instead. Even with the extra 2 characters
in the `rsync` form, it’s much shorter because a Fossil repository is a
single SQLite database file, not a tree containing a pile of assorted
files. Because of this, it works reliably without any of [the caveats
inherent in using `rsync` to clone a Git repo][grsync].
Now we set up the second remote, which is again simpler in the Fossil
case:
fossil remote add home ssh://my-nas.local//SHARES/dayjob/repo.fossil
fossil remote home
The first command is nearly identical to the Git version, but the second
is considerably simpler. And to be fair, you won’t find the
“`git config`” command above in all Git tutorials. The more common
alternative we found with web searches is even longer:
“`git push --set-upstream home master`”.
Where Fossil really wins is in the next step, making the initial commit
from home:
fossil ci
It’s one short command for Fossil instead of three for Git — or two if
you abbreviate it as “`git commit -a && git push`” — because of Fossil’s
[autosync] feature and deliberate omission of a
[staging feature](#staging).
The “Friday afternoon sync-up” case is simpler, too:
fossil remote work
fossil sync
Back at home, it’s simpler still: we may be able to do away with the second command,
saying just “`fossil remote home`” because the sync will happen as part
of the next commit, thanks once again to Fossil’s autosync feature. If
the working branch now has commits from other developers after syncing
with the central repository, though, you’ll want to say “`fossil up`” to
avoid creating an inadvertent fork in the branch.
|
| ︙ | ︙ |
Changes to www/globs.md.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 | The parser allows whitespace and commas in a pattern by quoting _the entire pattern_ with either single or double quotation marks. Internal quotation marks are treated literally. Moreover, a pattern that begins with a quote mark ends when the first instance of the same mark occurs, _not_ at a whitespace or comma. Thus, this: | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
The parser allows whitespace and commas in a pattern by quoting _the
entire pattern_ with either single or double quotation marks. Internal
quotation marks are treated literally. Moreover, a pattern that begins
with a quote mark ends when the first instance of the same mark occurs,
_not_ at a whitespace or comma. Thus, this:
"foo bar"qux
…constitutes _two_ patterns rather than one with an embedded space, in
contravention of normal shell quoting rules.
A list matches a file when any pattern in that list matches.
A pattern must consume and
|
| ︙ | ︙ |
Changes to www/glossary.md.
| ︙ | ︙ | |||
165 166 167 168 169 170 171 |
recommend keeping them all in a single subdirectory such as
"`~/fossils`" or "`%USERPROFILE%\Fossils`". A flat set of files
suffices for simple purposes, but you may have use for something
more complicated. This author uses a scheme like the following on
mobile machines that shuttle between home and the office:
``` pikchr toggle indent
| < | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
recommend keeping them all in a single subdirectory such as
"`~/fossils`" or "`%USERPROFILE%\Fossils`". A flat set of files
suffices for simple purposes, but you may have use for something
more complicated. This author uses a scheme like the following on
mobile machines that shuttle between home and the office:
``` pikchr toggle indent
box "~/museum/" fit
move right 0.1
line right dotted
move right 0.05
box invis "where one stores valuable fossils" ljust
arrow down 50% from first box.s then right 50%
|
| ︙ | ︙ | |||
431 432 433 434 435 436 437 |
organizational tool well-suited to complicated documentation.
* Your repository’s Home page is a good candidate for the wiki, as is
documentation meant for use only with the current version of the
repository’s contents.
* If you are at all uncertain whether to use the wiki or the embedded
| | | | | 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
organizational tool well-suited to complicated documentation.
* Your repository’s Home page is a good candidate for the wiki, as is
documentation meant for use only with the current version of the
repository’s contents.
* If you are at all uncertain whether to use the wiki or the embedded
documentation feature, prefer the latter, since it is inherently
more powerful, and when you use the [`/fileedit` feature][fef], the
workflow is scarcely different from using the wiki.
(This very file is embedded documentation: clone
[Fossil’s self-hosting repository][fshr] and you will find it as
`www/glossary.md`.)
[edoc]: ./embeddeddoc.wiki
[fef]: ./fileedit-page.md
|
| ︙ | ︙ |
Changes to www/grep.md.
| ︙ | ︙ | |||
43 44 45 46 47 48 49 | Fossil `grep` doesn’t support any of the GNU and BSD `grep` extensions. For instance, it doesn’t support the common `-R` extension to POSIX, which would presumably search a subtree of managed files. If Fossil does one day get this feature, it would have a different option letter, since `-R` in Fossil has a different meaning, by convention. Until then, you can get the same effect on systems with a POSIX shell like so: | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
Fossil `grep` doesn’t support any of the GNU and BSD `grep` extensions.
For instance, it doesn’t support the common `-R` extension to POSIX,
which would presumably search a subtree of managed files. If Fossil does
one day get this feature, it would have a different option letter, since
`-R` in Fossil has a different meaning, by convention. Until then, you
can get the same effect on systems with a POSIX shell like so:
$ fossil grep COMMAND: $(fossil ls src)
If you run that in a check-out of the [Fossil self-hosting source
repository][fshsr], that returns the first line of the built-in
documentation for each Fossil command, across all historical verisons.
Fossil `grep` has extensions relative to these other `grep` standards,
such as `--verbose` to print each checkin ID considered, regardless of
|
| ︙ | ︙ |
Changes to www/hashes.md.
1 2 3 4 5 6 | # Hashes: Fossil Artifact Identification All artifacts in Fossil are identified by a unique hash, currently using [the SHA3 algorithm by default][hpol], but historically using the SHA1 algorithm: | < | > | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # Hashes: Fossil Artifact Identification All artifacts in Fossil are identified by a unique hash, currently using [the SHA3 algorithm by default][hpol], but historically using the SHA1 algorithm: | Algorithm | Raw Bits | Hexadecimal digits | |-----------|----------|--------------------| | SHA3-256 | 256 | 64 | | SHA1 | 160 | 40 | There are many types of artifacts in Fossil: commits (a.k.a. check-ins), tickets, ticket comments, wiki articles, forum postings, file data belonging to check-ins, etc. ([More info...](./concepts.wiki#artifacts)). There is a loose hierarchy of terms used instead of “hash” in various parts of the Fossil UI, which we cover in the sections below. |
| ︙ | ︙ |
Changes to www/hashpolicy.wiki.
| ︙ | ︙ | |||
166 167 168 169 170 171 172 | repositories can be overridden using the "--sha1" option to the "fossil new" command. If you are still on Fossil 2.1 through 2.9 but you want Fossil to go ahead and start using SHA3 hashes, change the hash policy to "sha3" using a command like this: | | | | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | repositories can be overridden using the "--sha1" option to the "fossil new" command. If you are still on Fossil 2.1 through 2.9 but you want Fossil to go ahead and start using SHA3 hashes, change the hash policy to "sha3" using a command like this: <verbatim> fossil hash-policy sha3 </verbatim> The next check-in will use a SHA3 hash, so that when that check-in is pushed to colleagues, their clones will include the new SHA3-named artifact, so their local Fossil instances will automatically convert their clones to "sha3" mode as well. Of course, if some members of your team stubbornly refuse to upgrade past |
| ︙ | ︙ |
Changes to www/hints.wiki.
1 2 | <title>Fossil Tips And Usage Hints</title> | > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
<title>Fossil Tips And Usage Hints</title>
A collection of useful hints and tricks in no particular order:
1. Click on two nodes of any timeline graph in succession
to see a diff between the two versions.
2. Add the "--tk" option to "[/help?cmd=diff | fossil diff]" commands
to get a pop-up
window containing a complete side-by-side diff. (NB: The pop-up
window is run as a separate Tcl/Tk process, so you will need to
have Tcl/Tk installed on your machine for this to work. Visit
[http://www.activestate.com/activetcl] to for a quick download of
|
| ︙ | ︙ | |||
35 36 37 38 39 40 41 |
on in the Fossil repository on 2008-01-01, visit
[/timeline?c=2008-01-01].
7. Further to the previous two hints, there are lots of query parameters
that you can add to timeline pages. The available query parameters
are tersely documented [/help?cmd=/timeline | here].
| | | | > > > > > > > > > > > | 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 |
on in the Fossil repository on 2008-01-01, visit
[/timeline?c=2008-01-01].
7. Further to the previous two hints, there are lots of query parameters
that you can add to timeline pages. The available query parameters
are tersely documented [/help?cmd=/timeline | here].
8. You can run "[/help?cmd=xdiff | fossil xdiff --tk $file1 $file2]"
to get a Tk pop-up window with side-by-side diffs of two files, even if
neither of the two files is part of any Fossil repository. Note that
this command is "xdiff", not "diff". Change <nobr>--tk</nobr> to
<nobr>--by</nobr> to see the diff in your web browser.
9. On web pages showing the content of a file (for example
[/artifact/c7dd1de9f]) you can manually
add a query parameter of the form "ln=FROM,TO" to the URL that
will cause the range of lines indicated to be highlighted. This
is useful in pointing out a few lines of code using a hyperlink
in an email or text message. Example:
[/artifact/c7dd1de9f?ln=28,30].
Adding the "ln" query parameter without any argument simply turns
on line numbers. This feature only works right with files with
a mimetype of text/plain, of course.
10. When editing documentation to be checked in as managed files, you can
preview what the documentation will look like by using the special
"ckout" branch name in the "doc" URL while running "fossil ui".
See the [./embeddeddoc.wiki | embedded documentation] for details.
11. Use the "[/help?cmd=ui|fossil ui /]" command to bring up a menu of
all of your local Fossil repositories in your web browser.
12. If you have a bunch of Fossil repositories living on a remote machine
that you are able to access using ssh using a command like
"ssh login@remote", then you can bring up a user interface for all
those remote repositories using the command:
"[/help?cmd=ui|fossil ui login@remote:/]". This works by tunneling
all HTTP traffic through SSH to the remote machine.
|
Changes to www/image-format-vs-repo-size.md.
| ︙ | ︙ | |||
157 158 159 160 161 162 163 | Since programs that produce and consume binary-compressed data files often make it either difficult or impossible to work with the uncompressed form, we want an automated method for producing the uncompressed form to make Fossil happy while still having the compressed form to keep our content creation applications happy. This `Makefile` should[^makefile] do that for BMP, PNG, SVG, and XLSX files: | | | | | | | | | | | | | | | | | | | | 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 193 194 195 196 197 198 199 |
Since programs that produce and consume binary-compressed data files
often make it either difficult or impossible to work with the
uncompressed form, we want an automated method for producing the
uncompressed form to make Fossil happy while still having the compressed
form to keep our content creation applications happy. This `Makefile`
should[^makefile] do that for BMP, PNG, SVG, and XLSX files:
.SUFFIXES: .bmp .png .svg .svgz
.svgz.svg:
gzip -dc < $< > $@
.svg.svgz:
gzip -9c < $< > $@
.bmp.png:
convert -quality 95 $< $@
.png.bmp:
convert $< $@
SS_FILES := $(wildcard spreadsheet/*)
all: $(SS_FILES) illus.svg image.bmp doc-big.pdf
reconstitute: illus.svgz image.png
( cd spreadsheet ; zip -9 ../spreadsheet.xlsx) * )
qpdf doc-big.pdf doc-small.pdf
$(SS_FILES): spreadsheet.xlsx
unzip $@ -d $<
doc-big.pdf: doc-small.pdf
qpdf --stream-data=uncompress $@ $<
This `Makefile` allows you to treat the compressed version as the
process input, but to actually check in only the changes against the
uncompressed version by typing “`make`” before “`fossil ci`”. This is
not actually an extra step in practice, since if you’ve got a
`Makefile`-based project, you should be building — and testing — it
before checking each change in anyway!
|
| ︙ | ︙ |
Changes to www/index.wiki.
|
| | | | | | > | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
<title>A Coherent Software Configuration Management System</title>
<h3>What Is It?</h3>
<div class="nomargins" style='float:right;border:2px solid #446979;padding:0 15px 10px 0;margin:0 50px 0 10px'>
<ul>
<li> [/uv/download.html | Download]
<li> [./quickstart.wiki | Quick Start]
<li> [./build.wiki | Install]
<li> [https://fossil-scm.org/forum | Support/Forum ]
<li> [./hints.wiki | Tips & Hints]
<li> [./changes.wiki | Change Log]
<li> [../COPYRIGHT-BSD2.txt | License]
<li> [./userlinks.wiki | User Links]
<li> [./hacker-howto.wiki | Hacker How-To]
<li> [./fossil-v-git.wiki | Fossil vs. Git]
<li> [./permutedindex.html | Doc Index]
</ul>
<p style="text-align:center"><img src="fossil3.gif" alt="Fossil logo"></p>
</div>
Fossil is a simple, high-reliability, distributed
[https://en.wikipedia.org/wiki/Software_configuration_management | SCM]
system with these advanced features:
1. <b>Project Management</b> —
In addition to doing [./concepts.wiki | distributed version control]
like Git and Mercurial,
Fossil also supports [./bugtheory.wiki | bug tracking],
[./wikitheory.wiki | wiki], [./forum.wiki | forum],
[./alerts.md|email alerts], [./chat.md | chat], and
[./event.wiki | technotes].
2. <b>Built-in Web Interface</b> —
Fossil has a built-in, [/skins | themeable], [./serverext.wiki | extensible],
and intuitive [./webui.wiki | web interface]
with a rich variety of information pages
([./webpage-ex.md|examples]) promoting situational awareness.
<br><br>
This entire website is just a running instance of Fossil.
The pages you see here are all [./wikitheory.wiki | wiki] or
[./embeddeddoc.wiki | embedded documentation] or (in the case of
the [/uv/download.html|download] page)
[./unvers.wiki | unversioned files].
When you clone Fossil from one of its
[./selfhost.wiki | self-hosting repositories],
you get more than just source code — you get this entire website.
3. <b>All-in-one</b> —
Fossil is a single self-contained, stand-alone executable.
To install, simply download a
[/uv/download.html | precompiled binary]
for Linux, Mac, or Windows and put it on your $PATH.
[./build.wiki | Easy-to-compile source code] is also available.
4. <b>Self-host Friendly</b> — Stand up a project website
in minutes using [./server/ | a variety of techniques].
Fossil is CPU and memory efficient. Most projects can be
hosted comfortably on a $5/month VPS or a Raspberry Pi.
You can also set up an automatic
[./mirrortogithub.md | GitHub mirror].
5. <b>Simple Networking</b> —
Fossil uses ordinary HTTPS (or SSH if you prefer)
for network communications, so it works fine from behind
firewalls and [./quickstart.wiki#proxy|proxies].
The protocol is
[./stats.wiki | bandwidth efficient] to the point that Fossil can be
used comfortably over dial-up, weak 3G, or airliner Wifi.
6. <b>Autosync</b> —
Fossil supports [./concepts.wiki#workflow | "autosync" mode]
which helps to keep projects moving
forward by reducing the amount of needless
[./branching.wiki | forking and merging] often
associated with distributed projects.
7. <b>Robust & Reliable</b> —
Fossil stores content using an [./fileformat.wiki | enduring file format]
in an SQLite database so that transactions are
atomic even if interrupted by a power loss or system crash.
Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit.
8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
<hr>
<h3>Latest Release: 2.24 ([/timeline?c=version-2.24|2024-04-23])</h3>
* [/uv/download.html|Download]
* [./changes.wiki#v2_24|Change Summary]
* [/timeline?p=version-2.24&bt=version-2.23&y=ci|Check-ins in version 2.24]
* [/timeline?df=version-2.24&y=ci|Check-ins derived from the 2.24 release]
* [/timeline?t=release|Timeline of all past releases]
<hr>
<h3>Quick Start</h3>
1. [/uv/download.html|Download] or install using a package manager or
[./build.wiki|compile from sources].
|
| ︙ | ︙ |
Changes to www/inout.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 | <title>Import And Export</title> Fossil has the ability to import and export repositories from and to [http://git-scm.com/ | Git]. And since most other version control systems will also import/export from Git, that means that you can import/export a Fossil repository to most version control systems using Git as an intermediary. <h2>Git → Fossil</h2> To import a Git repository into Fossil, say something like: | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <title>Import And Export</title> Fossil has the ability to import and export repositories from and to [http://git-scm.com/ | Git]. And since most other version control systems will also import/export from Git, that means that you can import/export a Fossil repository to most version control systems using Git as an intermediary. <h2>Git → Fossil</h2> To import a Git repository into Fossil, say something like: <pre> cd git-repo git fast-export --all | fossil import --git new-repo.fossil </pre> The 3rd argument to the "fossil import" command is the name of a new Fossil repository that is created to hold the Git content. The --git option is not actually required. The git-fast-export file format is currently the only VCS interchange format that Fossil understands. But |
| ︙ | ︙ | |||
56 57 58 59 60 61 62 | any dependency on the amount of data involved. <h2>Fossil → Git</h2> To convert a Fossil repository into a Git repository, run commands like this: | | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | any dependency on the amount of data involved. <h2>Fossil → Git</h2> To convert a Fossil repository into a Git repository, run commands like this: <pre> git init new-repo cd new-repo fossil export --git ../repo.fossil | git fast-import </pre> In other words, create a new Git repository, then pipe the output from the "fossil export --git" command into the "git fast-import" command. Note that the "fossil export --git" command only exports the versioned files. Tickets and wiki and events are not exported, since Git does not understand those concepts. |
| ︙ | ︙ | |||
95 96 97 98 99 100 101 | artifacts which are known by both Git and Fossil to exist at a given point in time. To illustrate, consider the example of a remote Fossil repository that a user wants to import into a local Git repository. First, the user would clone the remote repository and import it into a new Git repository: | | | | | | | | 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 134 135 136 137 138 139 140 |
artifacts which are known by both Git and Fossil to exist at a given point in
time.
To illustrate, consider the example of a remote Fossil repository that a
user wants to import into a local Git repository. First, the user would clone
the remote repository and import it into a new Git repository:
<pre>
fossil clone /path/to/remote/repo.fossil repo.fossil
mkdir repo
cd repo
fossil open ../repo.fossil
mkdir ../repo.git
cd ../repo.git
git init .
fossil export --git --export-marks ../repo/fossil.marks \
../repo.fossil | git fast-import \
--export-marks=../repo/git.marks
</pre>
Once the import has completed, the user would need to <tt>git checkout
trunk</tt>. At any point after this, new changes can be imported from the
remote Fossil repository:
<pre>
cd ../repo
fossil pull
cd ../repo.git
fossil export --git --import-marks ../repo/fossil.marks \
--export-marks ../repo/fossil.marks \
../repo.fossil | git fast-import \
--import-marks=../repo/git.marks \
--export-marks=../repo/git.marks
</pre>
Changes in the Git repository can be exported to the Fossil repository and then
pushed to the remote:
<pre>
git fast-export --import-marks=../repo/git.marks \
--export-marks=../repo/git.marks --all | fossil import --git \
--incremental --import-marks ../repo/fossil.marks \
--export-marks ../repo/fossil.marks ../repo.fossil
cd ../repo
fossil push
</pre>
|
Changes to www/javascript.md.
| ︙ | ︙ | |||
319 320 321 322 323 324 325 | diff them” feature. [wt]: https://fossil-scm.org/home/timeline ### <a id="wedit"></a>The New Wiki Editor | | | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | diff them” feature. [wt]: https://fossil-scm.org/home/timeline ### <a id="wedit"></a>The New Wiki Editor The [new wiki editor][fwt] has many new features, a few of which are impossible to get without use of JavaScript. First, it allows in-browser previews without losing client-side editor state, such as where your cursor is. With the old editor, you had to re-locate the place you were last editing on each preview, which would reduce the incentive to use the preview function. In the new wiki editor, you just click the Preview tab to see how Fossil interprets your |
| ︙ | ︙ | |||
353 354 355 356 357 358 359 | this new editor was created, replacing it. If someone rescues that feature, merging it in with the new editor, it will doubtless require JavaScript in order to react to editor button clicks like the “**B**” button, meaning “make \[selected\] text boldface.” There is no standard WYSIWYG editor component in browsers, doubtless because it’s relatively straightforward to create one using JavaScript. | | | | | | | | | | | | | | | | | | | 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 396 397 398 399 400 401 402 403 404 405 406 407 408 | this new editor was created, replacing it. If someone rescues that feature, merging it in with the new editor, it will doubtless require JavaScript in order to react to editor button clicks like the “**B**” button, meaning “make \[selected\] text boldface.” There is no standard WYSIWYG editor component in browsers, doubtless because it’s relatively straightforward to create one using JavaScript. _Graceful Fallback:_ Fossil’s lack of a script-free wiki editor mode is not from lack of desire, but because the person who wrote the new wiki editor didn’t want to maintain three different editors. (New Ajaxy editor, old script-free HTML form based editor, and the old WYSIWYG JavaScript-based editor.) If someone wants to implement a `<noscript>` alternative to the new wiki editor, we will likely accept that [contribution][cg] as long as it doesn’t interfere with the new editor. (The same goes for adding a WYSIWYG mode to the new Ajaxy wiki editor.) _Workaround:_ You don’t have to use the browser-based wiki editor to maintain your repository’s wiki at all. Fossil’s [`wiki` command][fwc] lets you manipulate wiki documents from the command line. For example, consider this Vi based workflow: ```shell $ vi 'My Article.wiki' # begin work on new article ...write, write, write... :w # save changes to disk copy :!fossil wiki create 'My Article' '%' # current file (%) to new article ...write, write, write some more... :w # save again :!fossil wiki commit 'My Article' '%' # update article from disk :q # done writing for today ....days later... $ vi # work sans named file today :r !fossil wiki export 'My Article' - # pull article text into vi buffer ...write, write, write yet more... :w !fossil wiki commit - # vi buffer updates article ``` Extending this concept to other text editors is an exercise left to the reader. [fwc]: /help?cmd=wiki [fwt]: ./wikitheory.wiki ### <a id="fedit"></a>The File Editor Fossil’s [optional file editor feature][fedit] works much like [the new wiki editor](#wedit), only on files committed to the repository. The original designed purpose for this feature is to allow [embedded documentation][edoc] to be interactively edited in the same way that wiki articles can be. (Indeed, the associated `fileedit-glob` feature allows you to restrict the editor to working *only* on files that can be |
| ︙ | ︙ | |||
435 436 437 438 439 440 441 | per [the `/file` docs](/help?cmd=/file). _Potential Better Workaround:_ Someone sufficiently interested could [provide a patch][cg] to add a `<noscript>` wrapped HTML button that would reload the page with this parameter included/excluded to implement the toggle via a server round-trip. | | | | | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | per [the `/file` docs](/help?cmd=/file). _Potential Better Workaround:_ Someone sufficiently interested could [provide a patch][cg] to add a `<noscript>` wrapped HTML button that would reload the page with this parameter included/excluded to implement the toggle via a server round-trip. A related feature is Fossil’s JavaScript-based interactive method for selecting a range of lines by clicking the line numbers when they’re visible. JavaScript lets us copy the resulting URL to the clipboard to share your selection with others. _Workaround:_ These interactive features would be difficult and expensive (in terms of network I/O) to implement without JavaScript. A far simpler alternative is to manually edit the URL, per above. [mainc]: https://fossil-scm.org/home/artifact?ln&name=87d67e745 |
| ︙ | ︙ | |||
463 464 465 466 467 468 469 | in one box, you probably want to examine the same point on that line in the other box. _Graceful Fallback:_ Manually scroll both boxes to sync their views. ### <a id="diffcontext"></a>Diff Context Loading | | | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 | in one box, you probably want to examine the same point on that line in the other box. _Graceful Fallback:_ Manually scroll both boxes to sync their views. ### <a id="diffcontext"></a>Diff Context Loading Fossil’s diff views can dynamically load more lines of context around changed blocks. The UI controls for this feature are injected using JavaScript when the page initializes and make use of XHR requests to fetch data from the fossil instance. _Graceful Fallback:_ The UI controls for this feature do not appear when JS is unavailable, leaving the user with the "legacy" static diff |
| ︙ | ︙ | |||
564 565 566 567 568 569 570 | patch to do this][cg] may well be accepted. Since this is not a *necessary* Fossil feature, an interested user is unlikely to get the core developers to do this work for them. ### <a id="chat"></a>Chat | | | | | 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 |
patch to do this][cg] may well be accepted. Since this is not a
*necessary* Fossil feature, an interested user is unlikely to get the
core developers to do this work for them.
### <a id="chat"></a>Chat
The [chat feature](./chat.md) is deeply dependent
on JavaScript. There is no obvious way to do this sort of thing without
active client-side code of some sort.
_Potential Workaround:_ It would not be especially difficult for someone
sufficiently motivated to build a Fossil chat gateway, connecting to
IRC, Jabber, etc. The messages are stored in the repository’s `chat`
table with monotonically increasing IDs, so a poller that did something
like
SELECT xfrom, xmsg FROM chat WHERE msgid > 1234;
…would pull the messages submitted since the last poll. Making the
gateway bidirectional should be possible as well, as long as it properly
uses SQLite transactions.
### <a id="brlist"></a>List of branches
The [`/brlist`](/brlist) page uses JavaScript to enable
selection of several branches for further study via `/timeline`.
Client-side script interactively responds to checkboxes' events
and constructs a special hyperlink in the submenu.
Clicking this hyperlink loads a `/timeline` page that shows
only these selected branches (and the related check-ins).
_Potential Workaround:_ A user can manually construct an appropriate
|
| ︙ | ︙ |
Changes to www/loadmgmt.md.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
due to excessive requests to expensive pages:
1. An optional cache is available that remembers the 10 most recently
requested `/zip` or `/tarball` pages and returns the precomputed
answer if the same page is requested again.
2. Page requests can be configured to fail with a
| | | > > > | | | | 23 24 25 26 27 28 29 30 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 |
due to excessive requests to expensive pages:
1. An optional cache is available that remembers the 10 most recently
requested `/zip` or `/tarball` pages and returns the precomputed
answer if the same page is requested again.
2. Page requests can be configured to fail with a
“[503 Server Overload][503]” HTTP error if any request is
received while the host load average is too high.
Both of these load-control mechanisms are turned off by default, but
they are recommended for high-traffic sites. Users with [admin
permissions](caps/index.md) are exempt from these restrictions,
provided they are logged in before the load gets too high (login is
disabled under high load).
The webpage cache is activated using the [`fossil cache init`](/help/cache)
command-line on the server. Add a `-R` option to
specify the specific repository for which to enable caching. If running
this command as root, be sure to “`chown`” the cache database to give
the Fossil server write permission for the user ID of the web server;
this is a separate file in the same directory and with the same name as
the repository but with the “`.fossil`” suffix changed to “`.cache`”.
To activate the server load control feature visit the Admin → Access
setup page in the administrative web interface; in the “**Server Load
Average Limit**” box enter the load average threshold above which “503
Server Overload” replies will be issued for expensive requests. On the
self-hosting Fossil server, that value is set to 1.5, but you could
easily set it higher on a multi-core server.
The maximum load average can also be set on the command line using
commands like this:
fossil setting max-loadavg 1.5
fossil all setting max-loadavg 1.5
The second form is especially useful for changing the maximum load
average simultaneously on a large number of repositories.
Note that this load-average limiting feature is only available on
operating systems that support the [`getloadavg()`][gla] API. Most
modern Unix systems have this interface, but Windows does not, so the
feature will not work on Windows.
Because Linux implements `getloadavg()` by accessing the `/proc/loadavg`
virtual file, you will need to make sure `/proc` is available to the
Fossil server. The most common reason for it to not be available is that
you are running a Fossil instance [inside a `chroot(2)`
jail](./chroot.md) and you have not mounted the `/proc` virtual file
system inside that jail. On the [self-hosting Fossil repositories][sh],
this was accomplished by adding a line to the `/etc/fstab` file:
chroot_jail_proc /home/www/proc proc ro 0 0
The `/home/www/proc` pathname should be adjusted so that the `/proc`
component is at the root of the chroot jail, of course.
To see if the load-average limiter is functional, visit the
[`/test_env`][hte] page of the server to view the current load average.
If the value for the load average is greater than zero, that means that
|
| ︙ | ︙ |
Changes to www/makefile.wiki.
| ︙ | ︙ | |||
144 145 146 147 148 149 150 | The VERSION.h header file is generated by a C program: tools/mkversion.c. To run the VERSION.h generator, first compile the tools/mkversion.c source file into a command-line program (named "mkversion.exe") then run: | | | | | | 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 | The VERSION.h header file is generated by a C program: tools/mkversion.c. To run the VERSION.h generator, first compile the tools/mkversion.c source file into a command-line program (named "mkversion.exe") then run: <pre> mkversion.exe manifest.uuid manifest VERSION >VERSION.h </pre> The pathnames in the above command might need to be adjusted to get the directories right. The point is that the manifest.uuid, manifest, and VERSION files in the root of the source tree are the three arguments and the generated VERSION.h file appears on standard output. The builtin_data.h header file is generated by a C program: tools/mkbuiltin.c. The builtin_data.h file contains C-language byte-array definitions for the content of resource files used by Fossil. To generate the builtin_data.h file, first compile the mkbuiltin.c program, then run: <pre> mkbuiltin.exe diff.tcl <i>OtherFiles...</i> >builtin_data.h </pre> At the time of this writing, the "diff.tcl" script (a Tcl/Tk script used to generate implement --tk option on the diff command) is the only resource file processed using mkbuiltin.exe. However, new resources will likely be added using this facility in future versions of Fossil. <h1 id="preprocessing">4.0 Preprocessing</h1> |
| ︙ | ︙ | |||
183 184 185 186 187 188 189 | The mkindex program scans the "src.c" source files looking for special comments that identify routines that implement various Fossil commands, web interface methods, and help text comments. The mkindex program generates some C code that Fossil uses in order to dispatch commands and HTTP requests and to show on-line help. Compile the mkindex program from the mkindex.c source file. Then run: | | | | | | 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 216 217 218 219 | The mkindex program scans the "src.c" source files looking for special comments that identify routines that implement various Fossil commands, web interface methods, and help text comments. The mkindex program generates some C code that Fossil uses in order to dispatch commands and HTTP requests and to show on-line help. Compile the mkindex program from the mkindex.c source file. Then run: <pre> ./mkindex src.c >page_index.h </pre> Note that "src.c" in the above is a stand-in for the (79) regular source files of Fossil - all source files except for the exceptions described in section 2.0 above. The output of the mkindex program is a header file that is #include-ed by the main.c source file during the final compilation step. <h2>4.2 The translate preprocessor</h2> The translate preprocessor looks for lines of source code that begin with "@" and converts those lines into string constants or (depending on context) into special "printf" operations for generating the output of an HTTP request. The translate preprocessor is a simple C program whose sources are in the translate.c source file. The translate preprocess is run on each of the other ordinary source files separately, like this: <pre> ./translate src.c >src_.c </pre> In this case, the "src.c" file represents any single source file from the set of ordinary source files as described in section 2.0 above. Note that each source file is translated separately. By convention, the names of the translated source files are the names of the input sources with a single "_" character at the end. But a new makefile can use any naming convention it wants - the "_" is not critical to the build process. |
| ︙ | ︙ | |||
233 234 235 236 237 238 239 | The makeheaders program is run once. It scans all inputs source files and generates header files for each one. Note that the sqlite3.c and shell.c source files are not scanned by makeheaders. Makeheaders only runs over "ordinary" source files, not the exceptional source files. However, makeheaders also uses some extra header files as input. The general format is like this: | | | | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | The makeheaders program is run once. It scans all inputs source files and generates header files for each one. Note that the sqlite3.c and shell.c source files are not scanned by makeheaders. Makeheaders only runs over "ordinary" source files, not the exceptional source files. However, makeheaders also uses some extra header files as input. The general format is like this: <pre> makeheaders src_.c:src.h sqlite3.h th.h VERSION.h </pre> In the example above the "src_.c" and "src.h" names represent all of the (79) ordinary C source files, each as a separate argument. <h1>5.0 Compilation</h1> After all generated files have been created and all ordinary source files |
| ︙ | ︙ | |||
302 303 304 305 306 307 308 | However, in practice it is instead recommended to add a respective configure option for the target platform and then perform a clean build. This way the Debug flags are consistently applied across the whole build process. For example, use these Debug flags in addition to other flags passed to the configure scripts: On Linux, *NIX and similar platforms: | | | | | | 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 | However, in practice it is instead recommended to add a respective configure option for the target platform and then perform a clean build. This way the Debug flags are consistently applied across the whole build process. For example, use these Debug flags in addition to other flags passed to the configure scripts: On Linux, *NIX and similar platforms: <pre> ./configure --fossil-debug </pre> On Windows: <pre> win\buildmsvc.bat FOSSIL_DEBUG=1 </pre> The resulting fossil binary could then be loaded into a platform-specific debugger. Source files displayed in the debugger correspond to the ones generated from the translation stage of the build process, that is what was actually compiled into the object files. <h1>8.0 See Also</h1> * [./tech_overview.wiki | A Technical Overview Of Fossil] * [./adding_code.wiki | How To Add Features To Fossil] |
Changes to www/mirrortogithub.md.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
2. Create a new project. GitHub will ask you if you want to prepopulate
your project with various things like a README file. Answer "no" to
everything. You want a completely blank project. GitHub will then
supply you with a URL for your project that will look something
like this:
| | | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
2. Create a new project. GitHub will ask you if you want to prepopulate
your project with various things like a README file. Answer "no" to
everything. You want a completely blank project. GitHub will then
supply you with a URL for your project that will look something
like this:
https://github.com/username/project.git
3. Back on your workstation, move to a checkout for your Fossil
project and type:
<blockquote>
<pre>
$ fossil git export /path/to/git/repo --autopush \
https://<font color="orange">username</font>:<font color="red">password</font>@github.com/username/project.git
</pre>
</blockquote>
In place of the <code>/path/to...</code> argument above, put in
some directory name that is <i>outside</i> of your Fossil checkout. If
you keep multiple Fossil checkouts in a directory of their own,
consider using <code>../git-mirror</code> to place the Git export
|
| ︙ | ︙ | |||
56 57 58 59 60 61 62 |
5. And you are done! Assuming everything worked, your project is now
mirrored on GitHub.
6. Whenever you update your project, simply run this command to update
the mirror:
| | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
5. And you are done! Assuming everything worked, your project is now
mirrored on GitHub.
6. Whenever you update your project, simply run this command to update
the mirror:
$ fossil git export
Unlike with the first time you ran that command, you don’t need
the remaining arguments, because Fossil remembers those things.
Subsequent mirror updates should usually happen in a fraction of
a second.
7. To see the status of your mirror, run:
$ fossil git status
## Notes:
* Unless you specify --force, the mirroring only happens if the Fossil
repo has changed, with Fossil reporting "no changes", because Fossil
does not care about the success or failure of the mirror run. If a mirror
run failed (for example, due to an incorrect password, or a transient
|
| ︙ | ︙ | |||
98 99 100 101 102 103 104 |
subsequent invocations of "`fossil git export`" will know where you
left off the last time and what new content needs to be moved over into
Git. Be careful not to mess with the `.mirror_state` directory or
any of its contents. Do not put those files under Git management. Do
not edit or delete them.
* The name of the "trunk" branch is automatically translated into "master"
| | < | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
subsequent invocations of "`fossil git export`" will know where you
left off the last time and what new content needs to be moved over into
Git. Be careful not to mess with the `.mirror_state` directory or
any of its contents. Do not put those files under Git management. Do
not edit or delete them.
* The name of the "trunk" branch is automatically translated into "master"
in the Git mirror unless you give the `--mainbranch` option.
* Only check-ins and simple tags are translated to Git. Git does not
support wiki or tickets or unversioned content or any of the other
features of Fossil that make it so convenient to use, so those other
elements cannot be mirrored in Git.
* In Git, all tags must be unique. If your Fossil repository has the
|
| ︙ | ︙ | |||
140 141 142 143 144 145 146 | ## <a id='ex1'></a>Example GitHub Mirrors As of this writing (2019-03-16) Fossil’s own repository is mirrored on GitHub at: | < | < | < | 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 |
## <a id='ex1'></a>Example GitHub Mirrors
As of this writing (2019-03-16) Fossil’s own repository is mirrored
on GitHub at:
> <https://github.com/drhsqlite/fossil-mirror>
In addition, an official Git mirror of SQLite is available:
> <https://github.com/sqlite/sqlite>
The Fossil source repositories for these mirrors are at
<https://www2.fossil-scm.org/fossil> and <https://www2.sqlite.org/src>,
respectively. Both repositories are hosted on the same VM at
[Linode](https://www.linode.com). On that machine, there is a
[cron job](https://linux.die.net/man/8/cron)
that runs at 17 minutes after the hour, every hour that does:
/usr/bin/fossil sync -u -R /home/www/fossil/fossil.fossil
/usr/bin/fossil sync -R /home/www/fossil/sqlite.fossil
/usr/bin/fossil git export -R /home/www/fossil/fossil.fossil
/usr/bin/fossil git export -R /home/www/fossil/sqlite.fossil
The initial two "sync" commands pull in changes from the primary
Fossil repositories for Fossil and SQLite. The last two lines
export the changes to Git and push the results up to GitHub.
|
Changes to www/mkindex.tcl.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
chat.md {Fossil Chat}
checkin_names.wiki {Check-in And Version Names}
checkin.wiki {Check-in Checklist}
childprojects.wiki {Child Projects}
chroot.md {Server Chroot Jail}
ckout-workflows.md {Check-Out Workflows}
co-vs-up.md {Checkout vs Update}
copyright-release.html {Contributor License Agreement}
concepts.wiki {Fossil Core Concepts}
contact.md {Developer Contact Information}
containers.md {OCI Containers}
contribute.wiki {Contributing Code or Documentation To The Fossil Project}
css-tricks.md {Fossil CSS Tips and Tricks}
customgraph.md {Theming: Customizing the Timeline Graph}
| > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
chat.md {Fossil Chat}
checkin_names.wiki {Check-in And Version Names}
checkin.wiki {Check-in Checklist}
childprojects.wiki {Child Projects}
chroot.md {Server Chroot Jail}
ckout-workflows.md {Check-Out Workflows}
co-vs-up.md {Checkout vs Update}
colordiff.md {Colorized Diffs}
copyright-release.html {Contributor License Agreement}
concepts.wiki {Fossil Core Concepts}
contact.md {Developer Contact Information}
containers.md {OCI Containers}
contribute.wiki {Contributing Code or Documentation To The Fossil Project}
css-tricks.md {Fossil CSS Tips and Tricks}
customgraph.md {Theming: Customizing the Timeline Graph}
|
| ︙ | ︙ | |||
164 165 166 167 168 169 170 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> <li> <a href='history.md'>Purpose and History of Fossil</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> | | | | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
<li> <a href='quickstart.wiki'>Quick-start Guide</a>
<li> <a href='$ROOT/help'>Built-in help for commands and webpages</a>
<li> <a href='history.md'>Purpose and History of Fossil</a>
<li> <a href='build.wiki'>Compiling and installing Fossil</a>
<li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
<li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
<li><a href='$ROOT/wiki?name=Release Build How-To'>Release Build How-To</a>,
a.k.a. how deliverables are built</li>
</li>
<li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a>
<li> <a href='https://fossil-scm.org/fossil-book/'>Fossil book</a>
</ul>
<h2 id="pindex">Other Documents:</h2>
<ul>}
foreach entry $permindex {
foreach {title file bold} $entry break
# if {$bold} {set title <b>$title</b>}
if {[string match /* $file]} {set file ../../..$file}
puts $out "<li><a href=\"$file\">$title</a></li>"
}
puts $out "</ul></div>"
|
Changes to www/newrepo.wiki.
1 2 3 4 5 6 7 8 9 10 11 | <title>How To Create A New Fossil Repository</title> The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how to get up and running with fossil. But once you're running, what can you do with it? This document will walk you through the process of creating a fossil repository, populating it with files, and then sharing it over the web. The first thing we need to do is create a fossil repository file: <verbatim> | | | | | | | | | < | | | | | | | | | | | | | | | | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
<title>How To Create A New Fossil Repository</title>
The [/doc/tip/www/quickstart.wiki|quickstart guide] explains how
to get up and running with fossil. But once you're running, what can
you do with it? This document will walk you through the process of
creating a fossil repository, populating it with files, and then
sharing it over the web.
The first thing we need to do is create a fossil repository file:
<verbatim>
$ fossil new demo.fossil
project-id: 9d8ccff5671796ee04e60af6932aa7788f0a990a
server-id: 145fe7d71e3b513ac37ac283979d73e12ca04bfe
admin-user: stephan (initial password is ******)
</verbatim>
The numbers it spits out are unimportant (they are version
numbers).
Now we have an empty repository file named <tt>demo.fossil</tt>.
There is nothing magical about the extension <tt>.fossil</tt> - it's
just a convention. You may name your files anything you like.
The first thing we normally want to do is to run fossil as a local server so
that you can configure the access rights to the repo:
<verbatim>
$ fossil ui demo.fossil
</verbatim>
The <tt>ui</tt> command starts up a server (with an optional <tt>-port
NUMBER</tt> argument) and launches a web browser pointing at the
fossil server. From there it takes just a few moments to configure the
repo. Most importantly, go to the Admin menu, then the Users link, and
set your account name and password, and grant your account all access
privileges. (I also like to grant Clone access to the anonymous user,
but that's personal preference.)
Once you are done, kill the fossil server (with Ctrl-C or equivalent)
and close the browser window.
<div class="sidebar">
It is not strictly required to configure a repository
this way, but if you are going to share a repo over the net then it
is highly recommended. If you are only going to work with the repo
locally, you can skip the configuration step and do it later if
you decide you want to share your repo.
</div>
The next thing we need to do is <em>open</em> the repository. To do so
we create a working directory and then <tt>cd</tt> to it:
<verbatim>
$ mkdir demo
$ cd demo
$ fossil open ../demo.fossil
</verbatim>
That creates a file called <tt>_FOSSIL_</tt> in the current
directory, and this file contains all kinds of fossil-related
information about your local repository. You can ignore it
for all purposes, but be sure not to accidentally remove it
or otherwise damage it - it belongs to fossil, not you.
The next thing we need to do is add files to our repository. As it
happens, we have a few C source files lying around, which we'll
simply copy into our working directory.
<verbatim>
$ cp ../csnip/*.{c,h} .
$ ls
clob.c clob.h clobz.c mkdep.c test-clob.c
tokenize_path.c tokenize_path.h vappendf.c vappendf.h
</verbatim>
Fossil doesn't know about those files yet. Telling fossil about
a new file is a two-step process. First we <em>add</em> the file
to the repository, then we <em>commit</em> the file. This is a familiar
process for anyone who's worked with SCM systems before:
<verbatim>
$ fossil add *.{c,h}
$ fossil commit -m "egg"
New_Version: d1296b4a08b9f8b943bb6c73698e51eed23f8f91
</verbatim>
We now have a working repository! The file <tt>demo.fossil</tt>
is the central storage, and we can share it amongst an arbitrary
number of trees. As a silly example:
<verbatim>
$ cd ~/fossil
$ mkdir demo2
$ cd demo2
$ fossil open ../demo.fossil
ADD clob.c
ADD clob.h
ADD clobz.c
ADD mkdep.c
ADD test-clob.c
ADD tokenize_path.c
ADD tokenize_path.h
ADD vappendf.c
</verbatim>
You may modify the repository (e.g. add, remove, or commit files) from
both working directories, and doing so might be useful when working on
a branch or experimental code.
Making your repository available over the web is trivial to do. We
assume you have some web space where you can store your fossil file
and run a CGI script. If not, then this option is not for you. If
you do, then here's how...
Copy the fossil repository file to your web server (it doesn't matter
where, really, but it "should" be unreachable by web browser traffic).
In your <tt>cgi-bin</tt> (or equivalent) directory, create a file
which looks like this:
<verbatim>
#!/path/to/fossil
repository: /path/to/my_repo.fossil
</verbatim>
Make that script executable, and you're all ready to go:
<verbatim>
$ chmod +x ~/www/cgi-bin/myrepo.cgi
</verbatim>
Now simply point your browser to
<tt>https://my.domain/cgi-bin/myrepo.cgi</tt> and you should
be able to manage the repository from there.
To check out a copy of your remote repository, use the
<em>clone</em> command:
<verbatim>
$ fossil clone \
https://MyAccountName:MyAccountPassword@my.domain/cgi-bin/myrepo.cgi
</verbatim>
If you do not provide your password in the URL, fossil will
interactively prompt you for it.
A clone is a local copy of a remote repository, and can be opened just
like a local one (as shown above). It is treated identically to your
local repository, with one very important difference. When you commit
changes to a cloned remote repository, they will be pushed back to the
remote repository. If you have <tt>autosync</tt> on then this sync
happens automatically, otherwise you will need to use the
|
| ︙ | ︙ |
Changes to www/password.wiki.
1 | <title>Fossil Password Management</title> | < | 1 2 3 4 5 6 7 8 | <title>Fossil Password Management</title> Fossil handles user authentication using passwords. Passwords are unique to each repository. Passwords are not part of the persistent state of a project. Passwords are not versioned and are not transmitted from one repository to another during a sync. Passwords are local configuration information that can (and usually does) vary from one repository to the next within the same project. |
| ︙ | ︙ | |||
20 21 22 23 24 25 26 | The SHA1 hash in the USER.PW field is a hash of a string composed of the project-code, the user login, and the user cleartext password. Suppose user "alice" with password "asdfg" had an account on the Fossil self-hosting repository. Then the value of USER.PW for alice would be the SHA1 hash of | | | | | | | | 19 20 21 22 23 24 25 26 27 28 29 30 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 | The SHA1 hash in the USER.PW field is a hash of a string composed of the project-code, the user login, and the user cleartext password. Suppose user "alice" with password "asdfg" had an account on the Fossil self-hosting repository. Then the value of USER.PW for alice would be the SHA1 hash of <pre> CE59BB9F186226D80E49D1FA2DB29F935CCA0333/alice/asdfg </pre> Note that by including the project-code and the login as part of the hash, a different USER.PW value results even if two or more users on the repository select the same "asdfg" password or if user alice reuses the same password on multiple projects. Whenever a password is changed using the web interface or using the "user" command-line method, the new password is stored using the SHA1 encoding. Thus, cleartext passwords will gradually migrate to become SHA1 passwords. All remaining cleartext passwords can be converted to SHA1 passwords using the following command: <pre> fossil test-hash-passwords <i>REPOSITORY-NAME</i> </pre> Remember that converting from cleartext to SHA1 passwords is an irreversible operation. The only way to insert a new cleartext password into the USER table is to do so manually using SQL commands. For example: <pre> UPDATE user SET pw='asdfg' WHERE login='alice'; </pre> Note that an password that is an empty string or NULL will disable all login for that user. Thus, to lock a user out of the system, one has only to set their password to an empty string, using either the web interface or direct SQL manipulation of the USER table. Note also that the password field is essentially ignored for the special users named "anonymous", "developer", |
| ︙ | ︙ | |||
115 116 117 118 119 120 121 | This means that when USER.PW holds a cleartext password, the login card will work for both older and newer clients. If the USER.PW on the server only holds the SHA1 hash of the password, then only newer clients will be able to authenticate to the server. The client normally gets the login and password from the "remote URL". | | | | | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | This means that when USER.PW holds a cleartext password, the login card will work for both older and newer clients. If the USER.PW on the server only holds the SHA1 hash of the password, then only newer clients will be able to authenticate to the server. The client normally gets the login and password from the "remote URL". <pre> http://<span style="color:blue">login:password</span>@servername.org/path </pre> For older clients, the password is used for the shared secret as stated in the URL and with no encoding. For newer clients, the shared secret is derived from the password by transformed the password using the SHA1 hash encoding described above. However, if the first character of the password is "*" (ASCII 0x2a) then the "*" is skipped and the rest of the password is used directly as the share secret without the SHA1 encoding. <pre> http://<span style="color:blue">login:*password</span>@servername.org/path </pre> This *-before-the-password trick can be used by newer clients to sync against a legacy server that does not understand the new SHA1 password encoding. |
Changes to www/patchcmd.md.
1 2 3 4 5 6 7 8 9 10 11 | # The "fossil patch" command The "[fossil patch](/help?cmd=patch)" command is designed to transfer uncommitted changes from one check-out to another, including transfering those changes to other machines. For example, if you are working on a Windows desktop and you want to test your changes on a Linux server before you commit, you can use the "fossil patch push" command to make a copy of all your changes on the remote Linux server: | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # The "fossil patch" command The "[fossil patch](/help?cmd=patch)" command is designed to transfer uncommitted changes from one check-out to another, including transfering those changes to other machines. For example, if you are working on a Windows desktop and you want to test your changes on a Linux server before you commit, you can use the "fossil patch push" command to make a copy of all your changes on the remote Linux server: fossil patch push linuxserver:/path/to/checkout In the previous "linuxserver" is the name of the remote machine and "/path/to/checkout" is an existing checkout directory for the same project on the remote machine. The "fossil patch push" command works by first creating a patch file, then transfering that patch file to the remote machine using "ssh", then |
| ︙ | ︙ | |||
33 34 35 36 37 38 39 | The "fossil patch push" and "fossil patch pull" commands will only work if you have "ssh" available on the local machine and if "fossil" is on the default PATH on the remote machine. To check if Fossil is installed correctly on the remote, try a command like this: | | | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
The "fossil patch push" and "fossil patch pull" commands will only work if you have
"ssh" available on the local machine and if "fossil" is on the default
PATH on the remote machine.
To check if Fossil is installed correctly on the remote, try a command
like this:
ssh -T remote "fossil version"
If the command above shows a recent version of Fossil, then you should be
set to go. If you get "fossil not found", or if the version shown is too
old, put a newer fossil executable on the default PATH. The default PATH
can be shown using:
ssh -T remote 'echo $PATH'
### Custom PATH Caveat
On Unix-like systems, the init script for the user's login shell
(e.g. `~/.profile` or `~/.bash_profile`) may be configured to *not do
anything* when running under a non-interactive shell. Thus a fossil
binary installed to a custom directory might not be found. To allow
|
| ︙ | ︙ | |||
90 91 92 93 94 95 96 | The "fossil patch apply" command reads the database that is the patch file and applies it to the local check-out. If a filename is given as an argument, then the database is read from that file. If the argument is "-" then the database is read from standard input. Hence the command: | | | | | | 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
The "fossil patch apply" command reads the database that is the patch file
and applies it to the local check-out. If a filename is given as an
argument, then the database is read from that file. If the argument is "-"
then the database is read from standard input.
Hence the command:
fossil patch push remote:projectA
Is equivalent to:
fossil patch create - | ssh -T remote 'cd projectA;fossil patch apply -'
Likewise, a command like this:
fossil patch pull remote:projB
Could be entered like this:
ssh -T remote 'cd projB;fossil patch create -' | fossil patch apply -
The "fossil patch view" command just opens the database file and prints
a summary of its contents on standard output.
|
Changes to www/permutedindex.html.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> <li> <a href='history.md'>Purpose and History of Fossil</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> | | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <li> <a href='quickstart.wiki'>Quick-start Guide</a> <li> <a href='$ROOT/help'>Built-in help for commands and webpages</a> <li> <a href='history.md'>Purpose and History of Fossil</a> <li> <a href='build.wiki'>Compiling and installing Fossil</a> <li> <a href='../COPYRIGHT-BSD2.txt'>License</a> <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a> <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a> <li><a href='$ROOT/wiki?name=Release Build How-To'>Release Build How-To</a>, a.k.a. how deliverables are built</li> </li> <li> <a href='$ROOT/wiki?name=To+Do+List'>To Do List (Wiki)</a> <li> <a href='https://fossil-scm.org/fossil-book/'>Fossil book</a> </ul> <h2 id="pindex">Other Documents:</h2> <ul> <li><a href="tech_overview.wiki">A Technical Overview Of The Design And Implementation Of Fossil</a></li> |
| ︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <li><a href="serverext.wiki">CGI Server Extensions</a></li> <li><a href="checkin_names.wiki">Check-in And Version Names</a></li> <li><a href="checkin.wiki">Check-in Checklist</a></li> <li><a href="ckout-workflows.md">Check-Out Workflows</a></li> <li><a href="foss-cklist.wiki">Checklist For Successful Open-Source Projects</a></li> <li><a href="co-vs-up.md">Checkout vs Update</a></li> <li><a href="childprojects.wiki">Child Projects</a></li> <li><a href="build.wiki">Compiling and Installing Fossil</a></li> <li><a href="contribute.wiki">Contributing Code or Documentation To The Fossil Project</a></li> <li><a href="copyright-release.html">Contributor License Agreement</a></li> <li><a href="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li> <li><a href="customskin.md">Custom Skins</a></li> <li><a href="custom_ticket.wiki">Customizing The Ticket System</a></li> <li><a href="antibot.wiki">Defense against Spiders and Robots</a></li> | > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <li><a href="serverext.wiki">CGI Server Extensions</a></li> <li><a href="checkin_names.wiki">Check-in And Version Names</a></li> <li><a href="checkin.wiki">Check-in Checklist</a></li> <li><a href="ckout-workflows.md">Check-Out Workflows</a></li> <li><a href="foss-cklist.wiki">Checklist For Successful Open-Source Projects</a></li> <li><a href="co-vs-up.md">Checkout vs Update</a></li> <li><a href="childprojects.wiki">Child Projects</a></li> <li><a href="colordiff.md">Colorized Diffs</a></li> <li><a href="build.wiki">Compiling and Installing Fossil</a></li> <li><a href="contribute.wiki">Contributing Code or Documentation To The Fossil Project</a></li> <li><a href="copyright-release.html">Contributor License Agreement</a></li> <li><a href="private.wiki">Creating, Syncing, and Deleting Private Branches</a></li> <li><a href="customskin.md">Custom Skins</a></li> <li><a href="custom_ticket.wiki">Customizing The Ticket System</a></li> <li><a href="antibot.wiki">Defense against Spiders and Robots</a></li> |
| ︙ | ︙ |
Changes to www/pikchr.md.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 | arrow <-> down 70% from last box.s box same "Pikchr" "Formatter" "(pikchr.c)" fit ``` The diagram above was generated by the following lines of Markdown: ~~~~~ | | | | | | | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | arrow <-> down 70% from last box.s box same "Pikchr" "Formatter" "(pikchr.c)" fit ``` The diagram above was generated by the following lines of Markdown: ~~~~~ ``` pikchr arrow right 200% "Markdown" "Source" box rad 10px "Markdown" "Formatter" "(markdown.c)" fit arrow right 200% "HTML+SVG" "Output" arrow <-> down 70% from last box.s box same "Pikchr" "Formatter" "(pikchr.c)" fit ``` ~~~~~ See the [original Markdown source text of this document][4] for an example of Pikchr in operation. [4]: ./pikchr.md?mimetype=text/plain |
| ︙ | ︙ | |||
89 90 91 92 93 94 95 | content is interpreted as Pikchr script and is replaced by the equivalent SVG. So either of these work: [fcb]: https://spec.commonmark.org/0.29/#fenced-code-blocks ~~~~~~ | | | | | | | | | | | 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 | content is interpreted as Pikchr script and is replaced by the equivalent SVG. So either of these work: [fcb]: https://spec.commonmark.org/0.29/#fenced-code-blocks ~~~~~~ ~~~ pikchr arrow; box "Hello" "World!" fit; arrow ~~~ ``` pikchr arrow; box "Hello" "World!" fit; arrow ``` ~~~~~~ For Fossil Wiki, the Pikchr code goes within `<verbatim type="pikchr"> ... </verbatim>`. Normally `<verbatim>` content is displayed verbatim. The extra `type="pikchr"` attribute causes the content to be interpreted as Pikchr and replaced by SVG. ~~~~~~ <verbatim type="pikchr"> arrow; box "Hello" "World!" fit; arrow </verbatim> ~~~~~~ ## Extra Arguments In "Pikchr" Code Blocks Extra formatting arguments can be included in the fenced code block start tag, or in the "`type=`" attribute of `<verbatim>`, to change the formatting of the diagram. |
| ︙ | ︙ |
Changes to www/pop.wiki.
1 | <title>Principles Of Operation</title> | < | 1 2 3 4 5 6 7 8 |
<title>Principles Of Operation</title>
This page attempts to define the foundational principals upon
which Fossil is built.
* A project consists of source files, wiki pages, and
trouble tickets, and control files (collectively "artifacts").
All historical copies of all artifacts
|
| ︙ | ︙ |
Changes to www/private.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 | <title>Private Branches</title> By default, everything you check into a Fossil repository is shared to all clones of that repository. In Fossil, you don't push and pull individual branches; you push and pull everything all at once. But sometimes users want to keep some private work that is not shared with others. This might be a preliminary or experimental change that needs further refinement before it is shared and which might never be shared at all. To do this in Fossil, simply commit the change with the --private command-line option: | | | | | > > > > > > > > > > > > > < < < < < < < < < < < < < | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 87 88 89 |
<title>Private Branches</title>
By default, everything you check into a Fossil repository is shared
to all clones of that repository. In Fossil, you don't push and pull
individual branches; you push and pull everything all at once.
But sometimes users want to keep some private work that is not
shared with others. This might be a preliminary or experimental change
that needs further refinement before it is shared and which might never
be shared at all. To do this in Fossil, simply commit the change with
the --private command-line option:
<pre>
fossil commit --private
</pre>
The --private option causes Fossil to put the check-in in a new branch
named "private". That branch will not participate in subsequent clone,
sync, push, or pull operations. The branch will remain on the one local
repository where it was created. Note that you only use the --private
option for the first check-in that creates the private branch.
Additional checkins into the private branch remain private automatically.
<h2>Publishing Private Changes</h2>
After additional work, one might desire to publish the changes associated
with a private branch. The usual way to do this is to merge those
changes into a public branch. For example:
<pre>
fossil update trunk
fossil merge private
fossil commit
</pre>
The private branch remains private and is not recorded as a parent
in the merge manifest's P-card, but all of the changes associated with
the private branch are now folded into the public branch and are hence
visible to other users of the project.
A private branch created with Fossil version 1.30 or newer can also be
converted into a public branch using the <code>fossil publish</code>
command. However, there is no way to convert a private branch created with
older versions of Fossil into a public branch.
<div class="sidebar">
To avoid generating a missing artifact
reference on peer repositories without the private branch, the merge parent
is not recorded when merging the private branch into a public branch. As a
consequence, the web UI timeline does not draw a merge line from the private
merge parent to the public merge child. Moreover, repeat private-to-public
merge operations (without the [/help?cmd=merge | --force option]) with files
added on the private branch may only work once, but later abort with
"WARNING: no common ancestor for FILE", as the parent-child relationship is
not recorded. (See the [/doc/trunk/www/branching.wiki | Branching, Forking,
Merging, and Tagging] document for more information.)
</div>
The <code>--integrate</code> option of <code>fossil merge</code> (to close
the merged branch when committing) is ignored for a private branch -- or the
check-in manifest of the resulting merge child would include a
<code>+close</code> tag referring to the leaf check-in on the private branch,
and generate a missing artifact reference on repository clones without that
private branch. It's still possible to close the leaf of the private branch
(after committing the merge child) with the <code>fossil amend --close</code>
command.
<h2>Syncing Private Branches</h2>
A private branch normally stays on the one repository where it was
originally created. But sometimes you want to share private branches
with another repository. For example, you might be building a cross-platform
application and have separate repositories on your Windows laptop,
your Linux desktop, and your iMac. You can transfer private branches
between these machines by using the --private option on the "sync",
"push", "pull", and "clone" commands. For example, if you are running
"fossil server" on your Linux box and you want to clone that repository
to your Mac, including all private branches, use:
<verbatim>
fossil clone --private http://user@linux.localnetwork:8080/ mac-clone.fossil
</verbatim>
You'll have to supply a username and password in order for this to work.
Fossil will not clone (or sync) private branches anonymously.
By default, there are no users that can do private branch syncing. You
will have to give a user
the "Private" capability ("x") if you want them to be able to do this.
|
| ︙ | ︙ | |||
99 100 101 102 103 104 105 | again, this restriction is designed to make it hard to accidentally push private branches beyond their intended audience. <h2>Purging Private Branches</h2> You can remove all private branches from a repository using this command: | | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | again, this restriction is designed to make it hard to accidentally push private branches beyond their intended audience. <h2>Purging Private Branches</h2> You can remove all private branches from a repository using this command: <pre> fossil scrub --private </pre> Note that the above is a permanent and irreversible change. You will be asked to confirm before continuing. Once the private branches are removed, they cannot be retrieved (unless you have synced them to another repository.) So be careful with the command. <h2>Additional Notes</h2> All of the features above apply to <u>all</u> private branches in a single repository at once. There is no mechanism in Fossil (currently) that allows you to push, pull, clone, sync, or scrub an individual private branch within a repository that contains multiple private branches. |
Changes to www/qandc.wiki.
1 | <title>Questions And Criticisms</title> | < < | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 87 88 89 90 91 92 |
<title>Questions And Criticisms</title>
This page is a collection of real questions and criticisms that were
raised against Fossil early in its history (circa 2008).
This page is old and has not been kept up-to-date. See the
[/finfo?name=www/qandc.wiki|change history of this page].
<b>Fossil sounds like a lot of reinvention of the wheel.
Why create your own DVCS when you could have reused mercurial?</b>
<div class="indent">
I wrote fossil because none of the
other available DVCSes met my needs. If the other DVCSes do
meet your needs, then you might not need fossil. But they
don't meet mine, and so fossil is necessary for me.
Features provided by fossil that one does not get with other
DVCSes include:
<ol>
<li> Integrated <a href="wikitheory.wiki">wiki</a>. </li>
<li> Integrated <a href="bugtheory.wiki">bug tracking</a> </li>
<li> Immutable artifacts </li>
<li> Self-contained, stand-alone executable that can be run in
a <a href="http://en.wikipedia.org/wiki/Chroot">chroot jail</a> </li>
<li> Simple, well-defined,
<a href="fileformat.wiki">enduring file format</a> </li>
<li> Integrated <a href="webui.wiki">web interface</a> </li>
</ol>
</div>
<b>Why should I use this rather than Trac?</b>
<div class="indent">
<ol>
<li> Fossil is distributed. You can view and/or edit tickets, wiki, and
code while off network, then sync your changes later. With Trac, you
can only view and edit tickets and wiki while you are connected to
the server. </li>
<li> Fossil is lightweight and fully self-contained. It is very easy
to setup on a low-resource machine. Fossil does not require an
administrator.</li>
<li> Fossil integrates code versioning into the same repository with
wiki and tickets. There is nothing extra to add or install.
Fossil is an all-in-one turnkey solution. </li>
</ol>
</div>
<b>Love the concept here. Anyone using this for real work yet?</b>
<div class="indent">
Fossil is <a href="https://fossil-scm.org/">self-hosting</a>.
In fact, this page was probably delivered
to your web-browser via a working fossil instance. The same virtual
machine that hosts https://fossil-scm.org/
(a <a href="http://www.linode.com/">Linode 720</a>)
also hosts 24 other fossil repositories for various small projects.
The documentation files for
<a href="http://www.sqlite.org/">SQLite</a> are hosted in a
fossil repository <a href="http://www.sqlite.org/docsrc/">here</a>,
for example.
Other projects are also adopting fossil. But fossil does not yet have
the massive user base of git or mercurial.
</div>
<b>Fossil looks like the bug tracker that would be in your
Linksys Router's administration screen.</b>
<div class="indent">
I take a pragmatic approach to software: form follows function.
To me, it is more important to have a reliable, fast, efficient,
enduring, and simple DVCS than one that looks pretty.
On the other hand, if you have patches that improve the appearance
of Fossil without seriously compromising its reliability, performance,
and/or maintainability, I will be happy to accept them. Fossil is
self-hosting. Send email to request a password that will let
you push to the main fossil repository.
</div>
<b>It would be useful to have a separate application that
keeps the bug-tracking database in a versioned file. That file can
then be pushed and pulled along with the rest repository.</b>
<div class="indent">
Fossil already <u>does</u> push and pull bugs along with the files
in your repository.
But fossil does <u>not</u> track bugs as files in the source tree.
That approach to bug tracking was rejected for three reasons:
<ol>
<li> Check-ins in fossil are immutable. So if
|
| ︙ | ︙ | |||
105 106 107 108 109 110 111 |
of tickets to developers with check-in privileges and an installed
copy of the fossil executable. Casual passers-by on the internet should
be permitted to create tickets.
</ol>
These points are reiterated in the opening paragraphs of
the <a href="bugtheory.wiki">Bug-Tracking In Fossil</a> document.
| | | | | | | < | < < | 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
of tickets to developers with check-in privileges and an installed
copy of the fossil executable. Casual passers-by on the internet should
be permitted to create tickets.
</ol>
These points are reiterated in the opening paragraphs of
the <a href="bugtheory.wiki">Bug-Tracking In Fossil</a> document.
</div>
<b>Fossil is already the name of a plan9 versioned
append-only filesystem.</b>
<div class="indent">
I did not know that. Perhaps they selected the name for the same reason that
I did: because a repository with immutable artifacts preserves
an excellent fossil record of a long-running project.
</div>
<b>The idea of storing a repository in a binary blob like an
SQLite database terrifies me.</b>
<div class="indent">
The use of SQLite to store the database is likely more stable and secure
than any other approach, due to the fact that SQLite is transactional.
Fossil also implements several internal
<a href="selfcheck.wiki">self-checks</a> to insure that no information
is ever lost.
</div>
<b>I am dubious of the benefits of including wikis and bug trackers
directly in the VCS - either they are under-featured compared to full
software like Trac, or the VCS is massively bloated compared to
Subversion or Bazaar.</b>
<div class="indent">
I have no doubt that Trac has many features that fossil lacks. But that
is not the point. Fossil has several key features that Trac lacks and that
I need: most notably the fact that
fossil supports disconnected operation.
As for bloat: Fossil is a single self-contained executable.
You do not need any other packages
(diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache,
sqlite, and so forth)
in order to run fossil. Fossil runs just fine in a chroot jail all
by itself. And the self-contained fossil
executable is much less than 1MB in size. (Update 2015-01-12: Fossil has
grown in the years since the previous sentence was written but is still
much less than 2MB according to "size" when compiled using -Os on x64 Linux.)
Fossil is the very opposite of bloat.
</div>
|
Changes to www/quickstart.wiki.
1 | <title>Fossil Quick Start Guide</title> | < < < | | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <title>Fossil Quick Start Guide</title> This is a guide to help you get started using the Fossil [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] quickly and painlessly. <h2 id="install">Installing</h2> Fossil is a single self-contained C program. You need to either download a [https://fossil-scm.org/home/uv/download.html|precompiled binary] or <a href="build.wiki">compile it yourself</a> from sources. Install Fossil by putting the fossil binary someplace on your $PATH. You can test that Fossil is present and working like this: <pre><b>fossil version This is fossil version 2.13 [309af345ab] 2020-09-28 04:02:55 UTC </b></pre> <h2 id="workflow" name="fslclone">General Work Flow</h2> Fossil works with repository files (a database in a single file with the project's complete history) and with checked-out local trees (the working directory you use to do your work). (See [./glossary.md | the glossary] for more background.) |
| ︙ | ︙ | |||
46 47 48 49 50 51 52 | operations. <h2 id="new">Starting A New Project</h2> To start a new project with fossil create a new empty repository this way: ([/help/init | more info]) | < | | < | | < | < < < | | | | | | | | | | < < | < | 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 87 88 89 90 91 92 93 94 95 96 97 98 99 | operations. <h2 id="new">Starting A New Project</h2> To start a new project with fossil create a new empty repository this way: ([/help/init | more info]) <pre><b>fossil init</b> <i>repository-filename</i> </pre> You can name the database anything you like, and you can place it anywhere in the filesystem. The <tt>.fossil</tt> extension is traditional but only required if you are going to use the <tt>[/help/server | fossil server DIRECTORY]</tt> feature.” <h2 id="clone">Cloning An Existing Repository</h2> Most fossil operations interact with a repository that is on the local disk drive, not on a remote system. Hence, before accessing a remote repository it is necessary to make a local copy of that repository. Making a local copy of a remote repository is called "cloning". Clone a remote repository as follows: ([/help/clone | more info]) <pre><b>fossil clone</b> <i>URL repository-filename</i> </pre> The <i>URL</i> specifies the fossil repository you want to clone. The <i>repository-filename</i> is the new local filename into which the cloned repository will be written. For example, to clone the source code of Fossil itself: <pre><b>fossil clone https://fossil-scm.org/ myclone.fossil</b></pre> If your logged-in username is 'exampleuser', you should see output something like this: <pre><b>Round-trips: 8 Artifacts sent: 0 received: 39421 Clone done, sent: 2424 received: 42965725 ip: 10.10.10.0 Rebuilding repository meta-data... 100% complete... Extra delta compression... Vacuuming the database... project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333 server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42 admin-user: exampleuser (password is "yoWgDR42iv")> </b></pre> If the remote repository requires a login, include a userid in the URL like this: <pre><b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b></pre> You will be prompted separately for the password. Use [https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters|"%HH"] escapes for special characters in the userid. For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget") If you are behind a restrictive firewall, you might need to <a href="#proxy">specify an HTTP proxy</a>. |
| ︙ | ︙ | |||
141 142 143 144 145 146 147 | <h2 id="checkout">Checking Out A Local Tree</h2> To work on a project in fossil, you need to check out a local copy of the source tree. Create the directory you want to be the root of your tree and cd into that directory. Then do this: ([/help/open | more info]) | < | < < < | | | | | | < | | | | | | | | | | | | | | | | | | < < | < < < | | | < < < | | | | | | | | | < | < | | | | | | | | | | | | | | | | < | < | | < > | | < > | | | | < | 128 129 130 131 132 133 134 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 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
<h2 id="checkout">Checking Out A Local Tree</h2>
To work on a project in fossil, you need to check out a local
copy of the source tree. Create the directory you want to be
the root of your tree and cd into that directory. Then
do this: ([/help/open | more info])
<pre><b>fossil open</b> <i>repository-filename</i></pre>
for example:
<pre><b>fossil open ../myclone.fossil
BUILD.txt
COPYRIGHT-BSD2.txt
README.md
︙
</tt></b></pre>
(or "fossil open ..\myclone.fossil" on Windows).
This leaves you with the newest version of the tree
checked out.
From anywhere underneath the root of your local tree, you
can type commands like the following to find out the status of
your local tree:
<pre>
<b>[/help/info | fossil info]</b>
<b>[/help/status | fossil status]</b>
<b>[/help/changes | fossil changes]</b>
<b>[/help/diff | fossil diff]</b>
<b>[/help/timeline | fossil timeline]</b>
<b>[/help/ls | fossil ls]</b>
<b>[/help/branch | fossil branch]</b>
</pre>
If you created a new repository using "fossil init" some commands will not
produce much output.
Note that Fossil allows you to make multiple check-outs in
separate directories from the same repository. This enables you,
for example, to do builds from multiple branches or versions at
the same time without having to generate extra clones.
To switch a checkout between different versions and branches,
use:<
<pre>
<b>[/help/update | fossil update]</b>
<b>[/help/checkout | fossil checkout]</b>
</pre>
[/help/update | update] honors the "autosync" option and
does a "soft" switch, merging any local changes into the target
version, whereas [/help/checkout | checkout] does not
automatically sync and does a "hard" switch, overwriting local
changes if told to do so.
<h2 id="changes">Making and Committing Changes</h2>
To add new files to your project or remove existing ones, use these
commands:
<pre>
<b>[/help/add | fossil add]</b> <i>file...</i>
<b>[/help/rm | fossil rm]</b> <i>file...</i>
<b>[/help/addremove | fossil addremove]</b> <i>file...</i>
</pre>
The command:
<pre><b>[/help/changes | fossil changes]</b></pre>
lists files that have changed since the last commit to the repository. For
example, if you edit the file "README.md":
<pre><b>fossil changes
EDITED README.md
</b></pre>
To see exactly what change was made you can use the command
<b>[/help/diff | fossil diff]</b>:
<pre><b>fossil diff
Index: README.md
============================================================
--- README.md
+++ README.md
@@ -1,5 +1,6 @@
+Made some changes to the project
# Original text
</b></pre>
"fossil diff" shows the difference between your tree on disk now and as
the tree was when you last committed changes. If you haven't committed
yet, then it shows the difference relative to the tip-of-trunk commit in
the repository, being what you get when you "fossil open" a repository
without specifying a version, populating the working directory.
To see the most recent changes made to the repository by other users, use "fossil timeline" to
find out the most recent commit, and then "fossil diff" between that commit and the
current tree:
<pre><b>fossil timeline
=== 2021-03-28 ===
03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk)
=== 2021-03-27 ===
23:58:05 [ab975c6632] Update README.md. (user: user-two tags: trunk)
⋮
fossil diff --from current --to ab975c6632
Index: frobnicate.c
============================================================
--- frobnicate.c
+++ frobnicate.c
@@ -1,10 +1,11 @@
+/* made a change to the source file */
# Original text
</b></pre>
"current" is an alias for the checkout version, so the command
"fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results.
To commit your changes to a local-only repository:
<pre><b>fossil commit</b> <i>(... Fossil will start your editor, if defined)</i><b>
# Enter a commit message for this check-in. Lines beginning with # are ignored.
#
# user: exampleuser
# tags: trunk
#
# EDITED README.md
Edited file to add description of code changes
New_Version: 7b9a416ced4a69a60589dde1aedd1a30fde8eec3528d265dbeed5135530440ab
</b></pre>
You will be prompted for check-in comments using whatever editor
is specified by your VISUAL or EDITOR environment variable. If none is
specified Fossil uses line-editing in the terminal.
To commit your changes to a repository that was cloned from a remote
repository, you give the same command, but the results are different.
|
| ︙ | ︙ | |||
331 332 333 334 335 336 337 | When you create a new repository, either by cloning an existing project or create a new project of your own, you usually want to do some local configuration. This is easily accomplished using the web-server that is built into fossil. Start the fossil web server like this: ([/help/ui | more info]) | | | | | | | | | | | | | | | | | 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 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 | When you create a new repository, either by cloning an existing project or create a new project of your own, you usually want to do some local configuration. This is easily accomplished using the web-server that is built into fossil. Start the fossil web server like this: ([/help/ui | more info]) <pre> <b>fossil ui</b> <i>repository-filename</i> </pre> You can omit the <i>repository-filename</i> from the command above if you are inside a checked-out local tree. This starts a web server then automatically launches your web browser and makes it point to this web server. If your system has an unusual configuration, fossil might not be able to figure out how to start your web browser. In that case, first tell fossil where to find your web browser using a command like this: <pre> <b>fossil setting web-browser</b> <i>path-to-web-browser</i> </pre> By default, fossil does not require a login for HTTP connections coming in from the IP loopback address 127.0.0.1. You can, and perhaps should, change this after you create a few users. When you are finished configuring, just press Control-C or use the <b>kill</b> command to shut down the mini-server. <h2 id="sharing">Sharing Changes</h2> When [./concepts.wiki#workflow|autosync] is turned off, the changes you [/help/commit | commit] are only on your local repository. To share those changes with other repositories, do: <pre> <b>[/help/push | fossil push]</b> <i>URL</i> </pre> Where <i>URL</i> is the http: URL of the server repository you want to share your changes with. If you omit the <i>URL</i> argument, fossil will use whatever server you most recently synced with. The [/help/push | push] command only sends your changes to others. To Receive changes from others, use [/help/pull | pull]. Or go both ways at once using [/help/sync | sync]: <pre> <b>[/help/pull | fossil pull]</b> <i>URL</i> <b>[/help/sync | fossil sync]</b> <i>URL</i> </pre> When you pull in changes from others, they go into your repository, not into your checked-out local tree. To get the changes into your local tree, use [/help/update | update]: <pre> <b>[/help/update | fossil update]</b> <i>VERSION</i> </pre> The <i>VERSION</i> can be the name of a branch or tag or any abbreviation to the 40-character artifact identifier for a particular check-in, or it can be a date/time stamp. ([./checkin_names.wiki | more info]) If you omit the <i>VERSION</i>, then fossil moves you to the latest version of the branch your are currently on. The default behavior is for [./concepts.wiki#workflow|autosync] to be turned on. That means that a [/help/pull|pull] automatically occurs when you run [/help/update|update] and a [/help/push|push] happens automatically after you [/help/commit|commit]. So in normal practice, the push, pull, and sync commands are rarely used. But it is important to know about them, all the same. <pre> <b>[/help/checkout | fossil checkout]</b> <i>VERSION</i> </pre> Is similar to update except that it does not honor the autosync setting, nor does it merge in local changes - it prefers to overwrite them and fails if local changes exist unless the <tt>--force</tt> flag is used. <h2 id="branch" name="merge">Branching And Merging</h2> |
| ︙ | ︙ | |||
426 427 428 429 430 431 432 | To merge two branches back together, first [/help/update | update] to the branch you want to merge into. Then do a [/help/merge|merge] of the other branch that you want to incorporate the changes from. For example, to merge "featureX" changes into "trunk" do this: | | | | | | | 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | To merge two branches back together, first [/help/update | update] to the branch you want to merge into. Then do a [/help/merge|merge] of the other branch that you want to incorporate the changes from. For example, to merge "featureX" changes into "trunk" do this: <pre> <b>fossil [/help/update|update] trunk</b> <b>fossil [/help/merge|merge] featureX</b> <i># make sure the merge didn't break anything...</i> <b>fossil [/help/commit|commit] </pre> The argument to the [/help/merge|merge] command can be any of the version identifier forms that work for [/help/update|update]. ([./checkin_names.wiki|more info].) The merge command has options to cherry-pick individual changes, or to back out individual changes, if you don't want to do a full merge. |
| ︙ | ︙ | |||
458 459 460 461 462 463 464 | into trunk previously, you can do so again and Fossil will automatically know to pull in only those changes that have occurred since the previous merge. If a merge or update doesn't work out (perhaps something breaks or there are many merge conflicts) then you back up using: | | | | | | | 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 | into trunk previously, you can do so again and Fossil will automatically know to pull in only those changes that have occurred since the previous merge. If a merge or update doesn't work out (perhaps something breaks or there are many merge conflicts) then you back up using: <pre> <b>[/help/undo | fossil undo]</b> </pre> This will back out the changes that the merge or update made to the working checkout. There is also a [/help/redo|redo] command if you undo by mistake. Undo and redo only work for changes that have not yet been checked in using commit and there is only a single level of undo/redo. <h2 id="server">Setting Up A Server</h2> Fossil can act as a stand-alone web server using one of these commands: <pre> <b>[/help/server | fossil server]</b> <i>repository-filename</i> <b>[/help/ui | fossil ui]</b> <i>repository-filename</i> </pre> The <i>repository-filename</i> can be omitted when these commands are run from within an open check-out, which is a particularly useful shortcut with the <b>fossil ui</b> command. The <b>ui</b> command is intended for accessing the web user interface from a local desktop. (We sometimes call this mode "Fossil UI.") |
| ︙ | ︙ | |||
525 526 527 528 529 530 531 | If you are behind a restrictive firewall that requires you to use an HTTP proxy to reach the internet, then you can configure the proxy in three different ways. You can tell fossil about your proxy using a command-line option on commands that use the network, <b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>. | | | | | | | | | | | | | 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 | If you are behind a restrictive firewall that requires you to use an HTTP proxy to reach the internet, then you can configure the proxy in three different ways. You can tell fossil about your proxy using a command-line option on commands that use the network, <b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>. <pre> <b>fossil clone </b><i>URL</i> <b>--proxy</b> <i>Proxy-URL</i> </pre> It is annoying to have to type in the proxy URL every time you sync your project, though, so you can make the proxy configuration persistent using the [/help/setting | setting] command: <pre> <b>fossil setting proxy </b><i>Proxy-URL</i> </pre> Or, you can set the "<b>http_proxy</b>" environment variable: <pre> <b>export http_proxy=</b><i>Proxy-URL</i> </pre> To stop using the proxy, do: <pre> <b>fossil setting proxy off</b> </pre> Or unset the environment variable. The fossil setting for the HTTP proxy takes precedence over the environment variable and the command-line option overrides both. If you have a persistent proxy setting that you want to override for a one-time sync, that is easily done on the command-line. For example, to sync with a co-worker's repository on your LAN, you might type: <pre> <b>fossil sync http://192.168.1.36:8080/ --proxy off</b> </pre> <h2 id="links">Other Resources</h2> <ul> <li> <a href="./gitusers.md">Hints For Users With Prior Git Experience</a> <li> <a href="./whyusefossil.wiki">Why You Should Use Fossil</a> <li> <a href="./history.md">The History and Purpose of Fossil</a> <li> <a href="./branching.wiki">Branching, Forking, and Tagging</a> <li> <a href="./hints.wiki">Fossil Tips and Usage Hints</a> <li> <a href="./permutedindex.html">Comprehensive Fossil Doc Index</a> </ul> |
Changes to www/quotes.wiki.
1 2 3 4 5 6 | <title>What People Are Saying</title> The following are collected quotes from various forums and blogs about Fossil, Git, and DVCSes in general. This collection is put together by the creator of Fossil, so of course there is selection bias... | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 134 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 | <title>What People Are Saying</title> The following are collected quotes from various forums and blogs about Fossil, Git, and DVCSes in general. This collection is put together by the creator of Fossil, so of course there is selection bias... <h2>On The Usability Of Git</h2> <ol> <li>Git approaches the usability of iptables, which is to say, utterly unusable unless you have the manpage tattooed on you arm. <p class="local-indent"> <i>by mml at [http://news.ycombinator.com/item?id=1433387]</i> </p> <li><nowiki>It's simplest to think of the state of your [git] repository as a point in a high-dimensional "code-space", in which branches are represented as n-dimensional membranes, mapping the spatial loci of successive commits onto the projected manifold of each cloned repository.</nowiki> <p class="local-indent"> <i>by Jonathan Hartley at [https://www.tartley.com/posts/a-guide-to-git-using-spatial-analogies]; <br>Quoted here: [https://lwn.net/Articles/420152/].</i> </p> <li>Git is not a Prius. Git is a Model T. Its plumbing and wiring sticks out all over the place. You have to be a mechanic to operate it successfully or you'll be stuck on the side of the road when it breaks down. And it <b>will</b> break down. <p class="local-indent"> <i>Nick Farina at [http://nfarina.com/post/9868516270/git-is-simpler]</i> </p> <li>Initial revision of "git", The information manager from hell <p class="local-indent"> <i>Linus Torvalds - 2005-04-07 22:13:13<br> Commit comment on the very first source-code check-in for git </p> <li>I've been experimenting a lot with git at work. Damn, it's complicated. It has things to trip you up with that sane people just wouldn't ever both with including the ability to allow you to commit stuff in such a way that you can't find it again afterwards (!!!) Demented workflow complexity on acid? <p>* dkf really wishes he could use fossil instead</p> <p class="local-indent"> <i>by Donal K. Fellow (dkf) on the Tcl/Tk chatroom, 2013-04-09.</i> </p> <li>[G]it is <i>designed</i> to forget things. <p class="local-indent"> <i>[http://www.cs.cmu.edu/~davide/howto/git_lose.html] </p> <li>[I]n nearly 31 years of using a computer i have, in total, lost more data to git (while following the instructions!!!) than any other single piece of software. <p class="local-indent"> <i>Stephan Beal on the [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg17181.html|Fossil mailing list] 2014-09-01.</i> </p> <li>If programmers _really_ wanted to help scientists, they'd build a version control system that was more usable than Git. <p class="local-indent"> <i>Tweet by Greg Wilson @gvwilson on 2015-02-22 17:47</i> </p> <li><img src='xkcd-git.gif' align='top'> <p class="local-indent"><i>Randall Munroe. [http://xkcd.com/1597/]</i><p> </ol> <h2>On The Usability Of Fossil</h2> <ol> <li value=11> Fossil mesmerizes me with simplicity especially after I struggled to get a bug-tracking system to work with mercurial. <p class="local-indent"> <i>rawjeev at [https://stackoverflow.com/a/2100469/142454]</i> </p> <li>Fossil is the best thing to happen to my development workflow this year, as I am pretty sure that using Git has resulted in the premature death of too many of my brain cells. I'm glad to be able to replace Git in every place that I possibly can with Fossil. <p class="local-indent"> <i>Joe Prostko at [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg16716.html] </p> <li>This is my favourite VCS. I can carry it on a USB. And it's a complete system, with it's own server, ticketing system, Wiki pages, and a very, very helpful timeline visualization. And the entire program in a single file! <p class="local-indent"> <i>thunderbong commenting on hacker news: [https://news.ycombinator.com/item?id=9131619]</i> </p> </ol> <h2>On Git Versus Fossil</h2> <ol> <li value=14> After prolonged exposure to fossil, i tend to get the jitters when I work with git... <p class="local-indent"> <i>sriku - at [https://news.ycombinator.com/item?id=16104427]</i> </p> <li> Just want to say thanks for fossil making my life easier.... Also <nowiki>[for]</nowiki> not having a misanthropic command line interface. <p class="local-indent"> <i>Joshua Paine at [http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg02736.html]</i> </p> <li>We use it at a large university to manage code that small teams write. The runs everywhere, ease of installation and portability is something that seems to be a good fit with the environment we have (highly ditrobuted, sometimes very restrictive firewalls, OSX/Win/Linux). We are happy with it and teaching a Msc/Phd student (read complete novice) fossil has just been a smoother ride than Git was. <p class="local-indent"> <i>viablepanic at [https://www.reddit.com/r/programming/comments/bxcto/why_not_fossil_scm/c0p30b4?utm_source=share&utm_medium=web2x&context=3]</i> </p> <li>In the fossil community - and hence in fossil itself - development history is pretty much sacrosanct. The very name "fossil" was to chosen to reflect the unchanging nature of things in that history. <br><br> In git (or rather, the git community), the development history is part of the published aspect of the project, so it provides tools for rearranging that history so you can present what you "should" have done rather than what you actually did. <p class="local-indent"> <i>Mike Meyer on the Fossil mailing list, 2011-10-04</i> </p> <li>github is such a pale shadow of what fossil does. <p class="local-indent"> <i>dkf on the Tcl chatroom, 2013-12-06</i> </p> <li>[With fossil] I actually enjoy keeping track of source files again. <p class="local-indent"> <a href="https://wholesomedonut.prose.sh/using-fossil-not-git">https://wholesomedonut.prose.sh/using-fossil-not-git</a> </p> </ol> |
Changes to www/rebaseharm.md.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 | A rebase is really nothing more than a merge (or a series of merges) that deliberately forgets one of the parents of each merge step. To help illustrate this fact, consider the first rebase example from the [Git documentation][gitrebase]. The merge looks like this: | | | | 28 29 30 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 | A rebase is really nothing more than a merge (or a series of merges) that deliberately forgets one of the parents of each merge step. To help illustrate this fact, consider the first rebase example from the [Git documentation][gitrebase]. The merge looks like this: ~~~ pikchr toggle center scale = 0.8 circle "C0" fit arrow right 50% circle same "C1" arrow same circle same "C2" arrow same circle same "C3" arrow same circle same "C5" circle same "C4" at 1cm above C3 arrow from C2 to C4 chop arrow from C4 to C5 chop ~~~ And the rebase looks like this: ~~~ pikchr toggle center scale = 0.8 circle "C0" fit arrow right 50% circle same "C1" arrow same circle same "C2" arrow same |
| ︙ | ︙ | |||
93 94 95 96 97 98 99 | ### <a id="clean-diffs"></a>2.2 Rebase does not actually provide better feature-branch diffs Another argument, often cited, is that rebasing a feature branch allows one to see just the changes in the feature branch without the concurrent changes in the main line of development. Consider a hypothetical case: | | | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | ### <a id="clean-diffs"></a>2.2 Rebase does not actually provide better feature-branch diffs Another argument, often cited, is that rebasing a feature branch allows one to see just the changes in the feature branch without the concurrent changes in the main line of development. Consider a hypothetical case: ~~~ pikchr toggle center scale = 0.8 circle "C0" fit fill white arrow right 50% circle same "C1" arrow same circle same "C2" arrow same |
| ︙ | ︙ | |||
121 122 123 124 125 126 127 | In the above, a feature branch consisting of check-ins C3 and C5 is run concurrently with the main line in check-ins C4 and C6. Advocates for rebase say that you should rebase the feature branch to the tip of main in order to remove main-line development differences from the feature branch's history: | | | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | In the above, a feature branch consisting of check-ins C3 and C5 is run concurrently with the main line in check-ins C4 and C6. Advocates for rebase say that you should rebase the feature branch to the tip of main in order to remove main-line development differences from the feature branch's history: ~~~ pikchr toggle center # Duplicated below in section 5.0 scale = 0.8 circle "C0" fit fill white arrow right 50% circle same "C1" arrow same circle same "C2" |
| ︙ | ︙ | |||
156 157 158 159 160 161 162 | You could choose to collapse C3\' and C5\' into a single check-in as part of this rebase, but that's a side issue we'll deal with [separately](#collapsing). Because Fossil purposefully lacks rebase, the closest you can get to this same check-in history is the following merge: | | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | You could choose to collapse C3\' and C5\' into a single check-in as part of this rebase, but that's a side issue we'll deal with [separately](#collapsing). Because Fossil purposefully lacks rebase, the closest you can get to this same check-in history is the following merge: ~~~ pikchr toggle center scale = 0.8 circle "C0" fit fill white arrow right 50% circle same "C1" arrow same circle same "C2" arrow same |
| ︙ | ︙ | |||
196 197 198 199 200 201 202 | branch and from the mainline, whereas in the rebase case diff(C6,C5\') shows only the feature branch changes. But that argument is comparing apples to oranges, since the two diffs do not have the same baseline. The correct way to see only the feature branch changes in the merge case is not diff(C2,C7) but rather diff(C6,C7). | | | | > | | | > | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | branch and from the mainline, whereas in the rebase case diff(C6,C5\') shows only the feature branch changes. But that argument is comparing apples to oranges, since the two diffs do not have the same baseline. The correct way to see only the feature branch changes in the merge case is not diff(C2,C7) but rather diff(C6,C7). <div align=center> | Rebase | Merge | What You See | |---------------|-------------|----------------------------------------| | diff(C2,C5\') | diff(C2,C7) | Commingled branch and mainline changes | | diff(C6,C5\') | diff(C6,C7) | Branch changes only | </div> Remember: C7 and C5\' are bit-for-bit identical, so the output of the diff is not determined by whether you select C7 or C5\' as the target of the diff, but rather by your choice of the diff source, C2 or C6. So, to help with the problem of viewing changes associated with a feature branch, perhaps what is needed is not rebase but rather better tools to |
| ︙ | ︙ | |||
254 255 256 257 258 259 260 | branch to the parent repo? Will the many eyeballs even see those errors when they’re intermingled with code implementing some compelling new feature? ## <a id="timestamps"></a>4.0 Rebase causes timestamp confusion Consider the earlier example of rebasing a feature branch: | | | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | branch to the parent repo? Will the many eyeballs even see those errors when they’re intermingled with code implementing some compelling new feature? ## <a id="timestamps"></a>4.0 Rebase causes timestamp confusion Consider the earlier example of rebasing a feature branch: ~~~ pikchr toggle center # Copy of second diagram in section 2.2 above scale = 0.8 circle "C0" fit fill white arrow right 50% circle same "C1" arrow same circle same "C2" |
| ︙ | ︙ |
Changes to www/reviews.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 |
<title>Reviews</title>
<b>External links:</b>
* [https://www.nixtu.info/2010/03/fossil-dvcs-on-go-first-impressions.html |
Fossil DVCS on the Go - First Impressions]
<b>See Also:</b>
* [./quotes.wiki | Short Quotes on Fossil, Git, And DVCSes]
<b>Daniel writes on 2009-01-06:</b>
| | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<title>Reviews</title>
<b>External links:</b>
* [https://www.nixtu.info/2010/03/fossil-dvcs-on-go-first-impressions.html |
Fossil DVCS on the Go - First Impressions]
<b>See Also:</b>
* [./quotes.wiki | Short Quotes on Fossil, Git, And DVCSes]
<b>Daniel writes on 2009-01-06:</b>
<div class="indent">
The reasons I use fossil are that it's the only version control I
have found that I can get working through the VERY annoying MS
firewalls at work.. (albeit through an ntlm proxy) and I just love
single .exe applications!
</div>
<b>Joshua Paine on 2010-10-22:</b>
<div class="indent">
With one of my several hats on, I'm in a small team using git. Another
team member just checked some stuff into trunk that should have been on
a branch. Nothing else had happened since, so in fossil I would have
just edited that commit and put it on a new branch. In git that can't
actually be done without danger once other people have pulled, so I had
to create a new commit rolling back the changes, then branch and cherry
pick the earlier changes, then figure out how to make my new branch
shared instead of private. Just want to say thanks for fossil making my
life easier on most of my projects, and being able to move commits to
another branch after the fact and shared-by-default branches are good
features. Also not having a misanthropic command line interface.
</div>
<b>Stephan Beal writes on 2009-01-11:</b>
<div class="indent">
Sometime in late 2007 I came across a link to fossil on
<a href="http://www.sqlite.org/">sqlite.org</a>. It
was a good thing I bookmarked it, because I was never able to find the
link again (it might have been in a bug report or something). The
reasons I first took a close look at it were (A) it stemmed from the
sqlite project, which I've held in high regards for years (e.g. I
wrote JavaScript bindings for it:
|
| ︙ | ︙ | |||
133 134 135 136 137 138 139 | I remember my first reaction to fossil being, "this will be an excellent solution for small projects (like the dozens we've all got sitting on our hard drives but which don't justify the hassle of version control)." A year of daily use in over 15 source trees has confirmed that, and I continue to heartily recommend fossil to other developers I know who also have their own collection of "unhosted" pet projects. | | | 133 134 135 136 137 138 139 140 | I remember my first reaction to fossil being, "this will be an excellent solution for small projects (like the dozens we've all got sitting on our hard drives but which don't justify the hassle of version control)." A year of daily use in over 15 source trees has confirmed that, and I continue to heartily recommend fossil to other developers I know who also have their own collection of "unhosted" pet projects. </div> |
Changes to www/scgi.wiki.
1 2 3 4 5 6 | <title>Fossil SCGI</title> To run Fossil using SCGI, start the [/help/server|fossil server] command with the --scgi command-line option. You will probably also want to specific an alternative TCP/IP port using --port. For example: | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<title>Fossil SCGI</title>
To run Fossil using SCGI, start the [/help/server|fossil server] command
with the --scgi command-line option. You will probably also want to
specific an alternative TCP/IP port using --port. For example:
<pre>
fossil server $REPOSITORY --port 9000 --scgi
</pre>
Then configure your SCGI-aware web-server to send SCGI requests to port
9000 on the machine where Fossil is running. A typical configuration for
this in Nginx is:
<pre>
location ~ ^/demo_project/ {
include scgi_params;
scgi_pass localhost:9000;
scgi_param SCRIPT_NAME "/demo_project";
scgi_param HTTPS "on";
}
</pre>
Note that Nginx does not normally send either the PATH_INFO or SCRIPT_NAME
variables via SCGI, but Fossil needs one or the other. So the configuration
above needs to add SCRIPT_NAME. If you do not do this, Fossil returns an
error.
|
Changes to www/selfcheck.wiki.
1 2 | <title>Fossil Repository Integrity Self-Checks</title> | < < | 1 2 3 4 5 6 7 8 9 | <title>Fossil Repository Integrity Self-Checks</title> Fossil is designed with features to give it a high level of integrity so that users can have confidence that content will never be mangled or lost by Fossil. This note describes the defensive measures that Fossil uses to help prevent information loss due to bugs. Fossil has been hosting itself and many other projects for |
| ︙ | ︙ |
Changes to www/selfhost.wiki.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 | dozen other smaller projects. This demonstrates that Fossil can run on a low-power host processor. Multiple fossil-based projects can easily be hosted on the same machine, even if that machine is itself one of several dozen virtual machines on single physical box. The CGI script that runs the canonical Fossil self-hosting repository is as follows: | | | | | | | | 28 29 30 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 | dozen other smaller projects. This demonstrates that Fossil can run on a low-power host processor. Multiple fossil-based projects can easily be hosted on the same machine, even if that machine is itself one of several dozen virtual machines on single physical box. The CGI script that runs the canonical Fossil self-hosting repository is as follows: <pre> #!/usr/bin/fossil repository: /fossil/fossil.fossil </pre> Server (3) ran for 10 years as a CGI script on a shared hosting account at <a href="http://www.he.net/">Hurricane Electric</a> in Fremont, CA. This server demonstrated the ability of Fossil to run on an economical shared-host web account with no privileges beyond port 80 HTTP access and CGI. It is not necessary to have a dedicated computer with administrator privileges to run Fossil. As far as we are aware, Fossil is the only full-featured configuration management system that can run in such a restricted environment. The CGI script that ran on the Hurricane Electric server was the same as the CGI script shown above, except that the pathnames are modified to suit the environment: <pre> #!/home/hwaci/bin/fossil repository: /home/hwaci/fossil/fossil.fossil </pre> In recent years, virtual private servers have become a more flexible and less expensive hosting option compared to shared hosting accounts. So on 2017-07-25, server (3) was moved onto a $5/month "droplet" [https://en.wikipedia.org/wiki/Virtual_private_server|VPS] from [https://www.digitalocean.com|Digital Ocean] located in San Francisco. Server (3) is synchronized with the canonical server (1) by running a command similar to the following via cron: <pre> /usr/local/bin/fossil all sync -u </pre> Server (2) is a <a href="http://www.linode.com/">Linode 4096</a> located in Newark, NJ and set up just like the canonical server (1) with the addition of a cron job for synchronization. The same cron job also runs the [/help?cmd=git|fossil git export] command after each sync in order to [./mirrortogithub.md#ex1|mirror all changes to GitHub]. |
Changes to www/server/any/cgi.md.
1 2 3 4 5 6 7 8 9 10 11 12 | # Serving via CGI A Fossil server can be run from most ordinary web servers as a CGI program. This feature allows Fossil to seamlessly integrate into a larger website. The [self-hosting Fossil repository web site](../../selfhost.wiki) is implemented using CGI. See the [How CGI Works](../../aboutcgi.wiki) page for background information on the CGI protocol. To run Fossil as CGI, create a CGI script (here called "repo") in the CGI directory of your web server with content like this: | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# Serving via CGI
A Fossil server can be run from most ordinary web servers as a CGI
program. This feature allows Fossil to seamlessly integrate into a
larger website. The [self-hosting Fossil repository web
site](../../selfhost.wiki) is implemented using CGI. See the
[How CGI Works](../../aboutcgi.wiki) page for background information
on the CGI protocol.
To run Fossil as CGI, create a CGI script (here called "repo") in the
CGI directory of your web server with content like this:
#!/usr/bin/fossil
repository: /home/fossil/repo.fossil
Adjust the paths appropriately. It may be necessary to set certain
permissions on this file or to modify an `.htaccess` file or make other
server-specific changes. Consult the documentation for your particular
web server. The following permissions are *normally* required, but,
again, may be different for a particular configuration:
|
| ︙ | ︙ | |||
55 56 57 58 59 60 61 | for scripts like our “`repo`” example. To serve multiple repositories from a directory using CGI, use the "directory:" tag in the CGI script rather than "repository:". You might also want to add a "notfound:" tag to tell where to redirect if the particular repository requested by the URL is not found: | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > | 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 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 |
for scripts like our “`repo`” example.
To serve multiple repositories from a directory using CGI, use the
"directory:" tag in the CGI script rather than "repository:". You
might also want to add a "notfound:" tag to tell where to redirect if
the particular repository requested by the URL is not found:
#!/usr/bin/fossil
directory: /home/fossil/repos
notfound: http://url-to-go-to-if-repo-not-found/
Once deployed, a URL like: <b>http://mydomain.org/cgi-bin/repo/XYZ</b>
will serve up the repository `/home/fossil/repos/XYZ.fossil` if it
exists.
Additional options available to the CGI script are [documented
separately](../../cgi.wiki).
#### CGI with Apache behind an Nginx proxy
For the case where the Fossil repositories live on a computer, itself behind
an Internet-facing machine that employs Nginx to reverse proxy HTTP(S) requests
and take care of the TLS part of the connections in a transparent manner for
the downstream web servers, the CGI parameter `HTTPS=on` might not be set.
However, Fossil in CGI mode needs it in order to generate the correct links.
Apache can be instructed to pass this parameter further to the CGI scripts for
TLS connections with a stanza like
SetEnvIf X-Forwarded-Proto "https" HTTPS=on
in its config file section for CGI, provided that
proxy_set_header X-Forwarded-Proto $scheme;
has been be added in the relevant proxying section of the Nginx config file.
*[Return to the top-level Fossil server article.](../)*
#### Apache mod_cgi and `CONTENT_LENGTH`
At some point in its 2.4.x family, Apache's `mod_cgi` stopped relaying
the Content-Length header in the HTTP reply from CGIs back to clients.
However, Fossil clients prior to 2024-04-17 depended on seeing the
Content-Length header and were unable to handle HTTP replies without
one. The change in Apache behavior caused "fossil clone" and "fossil
sync" to stop working. There are two possible fixes to this problem:
1. Restore legacy behavior in Apache by adding
the following to the Apache configuration, scoped to the `<Directory>`
entry or entries in which fossil is being served via CGI:
<blockquote><pre>
<Directory ...>
SetEnv ap_trust_cgilike_cl "yes"
</Directory>
</pre></blockquote>
2. Upgrade your Fossil client to any trunk check-in after 2024-04-17,
as Fossil was upgraded to be able to deal with the missing
Content-Length field by
[check-in a8e33fb161f45b65](/info/a8e33fb161f45b65).
|
Changes to www/server/any/http-over-ssh.md.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 | ## 1. Force remote Fossil access through a wrapper script <a id="sshd"></a> Put something like the following into the `sshd_config` file on the Fossil repository server: ``` ssh-config | | | | | | | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
## 1. Force remote Fossil access through a wrapper script <a id="sshd"></a>
Put something like the following into the `sshd_config` file on the
Fossil repository server:
``` ssh-config
Match Group fossil
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
ForceCommand /home/fossil/bin/wrapper
```
This file is usually found in `/etc/ssh`, but some OSes put it
elsewhere.
The first line presumes that we will put all users who need to use our
Fossil repositories into the `fossil` group, as we will do
[below](#perms). You could instead say something like:
``` ssh-config
Match User alice,bob,carol,dave
```
You have to list the users allowed to use Fossil in this case because
your system likely has a system administrator that uses SSH for remote
shell access, so you want to *exclude* that user from the list. For the
same reason, you don’t want to put the `ForceCommand` directive outside
a `Match` block of some sort.
You could instead list the exceptions:
``` ssh-config
Match User !evi
```
This would permit only [Evi the System Administrator][evi] to bypass this
mechanism.
[evi]: https://en.wikipedia.org/wiki/Evi_Nemeth
|
| ︙ | ︙ | |||
66 67 68 69 70 71 72 | instance with certain parameters in order to set up the HTTP-based sync protocol over that SSH tunnel. We need to preserve some of this command and rewrite other parts to make this work. Here is a simpler variant of Andy’s original wrapper script: ``` sh | | | | | | | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | instance with certain parameters in order to set up the HTTP-based sync protocol over that SSH tunnel. We need to preserve some of this command and rewrite other parts to make this work. Here is a simpler variant of Andy’s original wrapper script: ``` sh #!/bin/bash set -- $SSH_ORIGINAL_COMMAND while [ $# -gt 1 ] ; do shift ; done export REMOTE_USER="$USER" ROOT=/home/fossil exec "$ROOT/bin/fossil" http "$ROOT/museum/$(/bin/basename "$1")" ``` The substantive changes are: 1. Move the command rewriting bits to the start. 2. Be explicit about executable paths. You might extend this idea by |
| ︙ | ︙ | |||
102 103 104 105 106 107 108 | is not the case everywhere. If the script fails to run on your system, try changing this line to point at `bash`, `dash`, `ksh`, or `zsh`. Also check the absolute paths for local correctness: is `/bin/basename` installed on your system, for example? Under this scheme, you clone with a command like: | | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
is not the case everywhere. If the script fails to run on your system,
try changing this line to point at `bash`, `dash`, `ksh`, or `zsh`. Also
check the absolute paths for local correctness: is `/bin/basename`
installed on your system, for example?
Under this scheme, you clone with a command like:
$ fossil clone ssh://HOST/repo.fossil
This will clone the remote `/home/fossil/museum/repo.fossil` repository
to your local machine under the same name and open it into a “`repo/`”
subdirectory. Notice that we didn’t have to give the `museum/` part of
the path: it’s implicit per point #3 above.
This presumes your local user name matches the remote user name. Unlike
|
| ︙ | ︙ | |||
127 128 129 130 131 132 133 | the wrapper script from where you placed it and execute it, and that they have read/write access on the directory where the Fossil repositories are stored. You can achieve all of this on a Linux box with: ``` shell | | | | | | | | | | | | | | 127 128 129 130 131 132 133 134 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 |
the wrapper script from where you placed it and execute it, and that
they have read/write access on the directory where the Fossil
repositories are stored.
You can achieve all of this on a Linux box with:
``` shell
sudo adduser fossil
for u in alice bob carol dave ; do
sudo adduser $u
sudo gpasswd -a fossil $u
done
sudo -i -u fossil
chmod 710 .
mkdir -m 750 bin
mkdir -m 770 museum
ln -s /usr/local/bin/fossil bin
```
You then need to copy the Fossil repositories into `~fossil/museum` and
make them readable and writable by group `fossil`. These repositories
presumably already have Fossil users configured, with the necessary
[user capabilities](../../caps/), the point of this article being to
show you how to make Fossil-over-SSH pay attention to those caps.
You must also permit use of `REMOTE_USER` on each shared repository.
Fossil only pays attention to this environment variable in certain
contexts, of which “`fossil http`” is not one. Run this command against
each repo to allow that:
``` shell
echo "INSERT OR REPLACE INTO config VALUES ('remote_user_ok',1,strftime('%s','now'));" |
fossil sql -R museum/repo.fossil
```
Now you can configure SSH authentication for each user. Since Fossil’s
password-saving feature doesn’t work in this case, I suggest setting up
SSH keys via `~USER/.ssh/authorized_keys` since the SSH authentication
occurs on each sync, which Fossil’s default-enabled autosync setting
makes frequent.
|
| ︙ | ︙ |
Changes to www/server/any/inetd.md.
1 2 3 4 5 6 | # Serving via inetd A Fossil server can be launched on-demand by `inetd` by using the [`fossil http`](/help/http) command. To do so, add a line like the following to its configuration file, typically `/etc/inetd.conf`: | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# Serving via inetd
A Fossil server can be launched on-demand by `inetd` by using the
[`fossil http`](/help/http) command. To do so, add a line like the
following to its configuration file, typically `/etc/inetd.conf`:
80 stream tcp nowait.1000 root /usr/bin/fossil /usr/bin/fossil http /home/fossil/repo.fossil
In this example, you are telling `inetd` that when an incoming
connection appears on TCP port 80 that it should launch the program
`/usr/bin/fossil` with the arguments shown. Obviously you will need to
modify the pathnames for your particular setup. The final argument is
either the name of the fossil repository to be served or a directory
containing multiple repositories.
If you use a non-standard TCP port on systems where the port
specification must be a symbolic name and cannot be numeric, add the
desired name and port to `/etc/services`. For example, if you want your
Fossil server running on TCP port 12345 instead of 80, you will need to
add:
fossil 12345/tcp # fossil server
and use the symbolic name “`fossil`” instead of the numeric TCP port
number (“12345” in the above example) in `inetd.conf`.
Notice that we configured `inetd` to launch Fossil as root. See the
top-level section on “[The Fossil Chroot
Jail](../../chroot.md)” for the consequences of this and
|
| ︙ | ︙ |
Changes to www/server/any/none.md.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 | * “`ui`” launches a local web browser pointed at this URL. You can omit the _REPOSITORY_ argument if you run one of the above commands from within a Fossil checkout directory to serve that repository: | | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
* “`ui`” launches a local web browser pointed at this URL.
You can omit the _REPOSITORY_ argument if you run one of the above
commands from within a Fossil checkout directory to serve that
repository:
$ fossil ui # or...
$ fossil server
You can abbreviate Fossil sub-commands as long as they are unambiguous.
“`server`” can currently be as short as “`ser`”.
You can serve a directory containing multiple `*.fossil` files like so:
$ fossil server --port 9000 --repolist /path/to/repo/dir
There is an [example script](/file/tools/fslsrv) in the Fossil
distribution that wraps `fossil server` to produce more complicated
effects. Feel free to take it, study it, and modify it to suit your
local needs.
See the [online documentation](/help/server) for more information on the
|
| ︙ | ︙ |
Changes to www/server/any/scgi.md.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Serving via SCGI There is an alternative to running Fossil as a [standalone HTTP server](./none.md), which is to run it in SimpleCGI (a.k.a. SCGI) mode, which uses the same [`fossil server`](/help/server) command as for HTTP service. Simply add the `--scgi` command-line option and the stand-alone server will speak the SCGI protocol rather than raw HTTP. This can be used with a web server such as [nginx](http://nginx.org) which does not support [Fossil’s CGI mode](./cgi.md). A basic nginx configuration to support SCGI with Fossil looks like this: | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# Serving via SCGI
There is an alternative to running Fossil as a [standalone HTTP
server](./none.md), which is to run it in SimpleCGI (a.k.a. SCGI) mode,
which uses the same [`fossil server`](/help/server) command as for HTTP
service. Simply add the `--scgi` command-line option and the stand-alone
server will speak the SCGI protocol rather than raw HTTP.
This can be used with a web server such as [nginx](http://nginx.org)
which does not support [Fossil’s CGI mode](./cgi.md).
A basic nginx configuration to support SCGI with Fossil looks like this:
location /code/ {
include scgi_params;
scgi_param SCRIPT_NAME "/code";
scgi_pass localhost:9000;
}
The `scgi_params` file comes with nginx, and it simply translates nginx
internal variables to `scgi_param` directives to create SCGI environment
variables for the proxied program; in this case, Fossil. Our explicit
`scgi_param` call to define `SCRIPT_NAME` adds one more variable to this
set, which is necessary for this configuration to work properly, because
our repo isn’t at the root of the URL hierarchy. Without it, when Fossil
generates absolute URLs, they’ll be missing the `/code` part at the
start, which will typically cause [404 errors][404].
The final directive simply tells nginx to proxy all calls to URLs under
`/code` down to an SCGI program on TCP port 9000. We can temporarily
set Fossil up as a server on that port like so:
$ fossil server /path/to/repo.fossil --scgi --localhost --port 9000 &
The `--scgi` option switches Fossil into SCGI mode from its default,
which is [stand-alone HTTP server mode](./none.md). All of the other
options discussed in that linked document — such as the ability to serve
a directory full of Fossil repositories rather than just a single
repository — work the same way in SCGI mode.
|
| ︙ | ︙ |
Changes to www/server/any/xinetd.md.
1 2 3 4 5 6 7 8 9 10 | # Serving via xinetd Some operating systems have replaced the old Unix `inetd` daemon with `xinetd`, which has a similar mission but with a very different configuration file format. The typical configuration file is either `/etc/xinetd.conf` or a subfile in the `/etc/xinetd.d` directory. You need a configuration something like this for Fossil: | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# Serving via xinetd
Some operating systems have replaced the old Unix `inetd` daemon with
`xinetd`, which has a similar mission but with a very different
configuration file format.
The typical configuration file is either `/etc/xinetd.conf` or a subfile
in the `/etc/xinetd.d` directory. You need a configuration something
like this for Fossil:
service http
{
port = 80
socket_type = stream
wait = no
user = root
server = /usr/bin/fossil
server_args = http /home/fossil/repos/
}
This example configures Fossil to serve multiple repositories under the
`/home/fossil/repos/` directory.
Beyond this, see the general commentary in our article on [the `inetd`
method](./inetd.md) as they also apply to service via `xinetd`.
|
| ︙ | ︙ |
Changes to www/server/debian/nginx.md.
| ︙ | ︙ | |||
99 100 101 102 103 104 105 | ## <a id="deps"></a>Installing the Dependencies The first step is to install some non-default packages we’ll need. SSH into your server, then say: | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
## <a id="deps"></a>Installing the Dependencies
The first step is to install some non-default packages we’ll need. SSH into
your server, then say:
$ sudo apt install fossil nginx
You can leave “`fossil`” out of that if you’re building Fossil from
source to get a more up-to-date version than is shipped with the host
OS.
## <a id="scgi"></a>Running Fossil in SCGI Mode
|
| ︙ | ︙ | |||
129 130 131 132 133 134 135 | ## <a id="config"></a>Configuration On Debian and Ubuntu systems the primary user-level configuration file for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this file contain only a list of include statements, one for each site that server hosts: | | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
## <a id="config"></a>Configuration
On Debian and Ubuntu systems the primary user-level configuration file
for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
file contain only a list of include statements, one for each site that
server hosts:
include local/example.com
include local/foo.net
Those files then each define one domain’s configuration. Here,
`/etc/nginx/local/example.com` contains the configuration for
`*.example.com` and its alias `*.example.net`; and `local/foo.net`
contains the configuration for `*.foo.net`.
The configuration for our `example.com` web site, stored in
|
| ︙ | ︙ | |||
195 196 197 198 199 200 201 | As you can see, this is a pure extension of [the basic nginx service configuration for SCGI][scgii], showing off a few ideas you might want to try on your own site, such as static asset proxying. You also need a `local/code` file containing: | | | | | | | | | 195 196 197 198 199 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 |
As you can see, this is a pure extension of [the basic nginx service
configuration for SCGI][scgii], showing off a few ideas you might want to
try on your own site, such as static asset proxying.
You also need a `local/code` file containing:
include scgi_params;
scgi_pass 127.0.0.1:12345;
scgi_param SCRIPT_NAME "/code";
We separate that out because nginx refuses to inherit certain settings
between nested location blocks, so rather than repeat them, we extract
them to this separate file and include it from both locations where it’s
needed. You see this above where we set far-future expiration dates on
files served by Fossil via URLs that contain hashes that change when the
content changes. It tells your browser that the content of these URLs
can never change without the URL itself changing, which makes your
Fossil-based site considerably faster.
Similarly, the `local/generic` file referenced above helps us reduce unnecessary
repetition among the multiple sites this configuration hosts:
root /var/www/$host;
listen 80;
listen [::]:80;
charset utf-8;
There are some configuration directives that nginx refuses to substitute
variables into, citing performance considerations, so there is a limit
to how much repetition you can squeeze out this way. One such example
are the `access_log` and `error_log` directives, which follow an obvious
pattern from one host to the next. Sadly, you must tolerate some
repetition across `server { }` blocks when setting up multiple domains
|
| ︙ | ︙ | |||
244 245 246 247 248 249 250 | encryption for Fossil](#tls), proxying HTTP instead of SCGI provides no benefit. However, it is still worth showing the proper method of proxying Fossil’s HTTP server through nginx if only to make reading nginx documentation on other sites easier: | | | | | | | | | | | < < < | | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
encryption for Fossil](#tls), proxying HTTP instead of SCGI provides no
benefit.
However, it is still worth showing the proper method of proxying
Fossil’s HTTP server through nginx if only to make reading nginx
documentation on other sites easier:
location /code {
rewrite ^/code(/.*) $1 break;
proxy_pass http://127.0.0.1:12345;
}
The most common thing people get wrong when hand-rolling a configuration
like this is to get the slashes wrong. Fossil is sensitive to this. For
instance, Fossil will not collapse double slashes down to a single
slash, as some other HTTP servers will.
## <a id="large-uv"></a> Allowing Large Unversioned Files
By default, nginx only accepts HTTP messages [up to a
meg](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size)
in size. Fossil chunks its sync protocol such that this is not normally
a problem, but when sending [unversioned content][uv], it uses a single
message for the entire file. Therefore, if you will be storing files
larger than this limit as unversioned content, you need to raise the
limit. Within the `location` block:
# Allow large unversioned file uploads, such as PDFs
client_max_body_size 20M;
[uv]: ../../unvers.wiki
## <a id="fail2ban"></a> Integrating `fail2ban`
One of the nice things that falls out of proxying Fossil behind nginx is
that it makes it easier to configure `fail2ban` to recognize attacks on
Fossil and automatically block them. Fossil logs the sorts of errors we
want to detect, but it does so in places like the repository’s admin
log, a SQL table, which `fail2ban` doesn’t know how to query. By putting
Fossil behind an nginx proxy, we convert these failures to log file
form, which `fail2ban` is designed to handle.
First, install `fail2ban`, if you haven’t already:
sudo apt install fail2ban
We’d like `fail2ban` to react to Fossil `/login` failures. The stock
configuration of `fail2ban` only detects a few common sorts of SSH
attacks by default, and its included (but disabled) nginx attack
detectors don’t include one that knows how to detect an attack on
Fossil. We have to teach it by putting the following into
`/etc/fail2ban/filter.d/nginx-fossil-login.conf`:
[Definition]
failregex = ^<HOST> - .*POST .*/login HTTP/..." 401
That teaches `fail2ban` how to recognize the errors logged by Fossil.
Then in `/etc/fail2ban/jail.local`, add this section:
[nginx-fossil-login]
enabled = true
logpath = /var/log/nginx/*-https-access.log
The last line is the key: it tells `fail2ban` where we’ve put all of our
per-repo access logs in the nginx config above.
There’s a [lot more you can do][dof2b], but that gets us out of scope of
this guide.
|
| ︙ | ︙ | |||
336 337 338 339 340 341 342 |
has gotten smarter or our nginx configurations have gotten simpler, so
we have removed the manual instructions we used to have here.
You may wish to include something like this from each `server { }`
block in your configuration to enable TLS in a common, secure way:
```
| | | | | | | | | | | | | | | | | | | | < < | 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 |
has gotten smarter or our nginx configurations have gotten simpler, so
we have removed the manual instructions we used to have here.
You may wish to include something like this from each `server { }`
block in your configuration to enable TLS in a common, secure way:
```
# Tell nginx to accept TLS-encrypted HTTPS on the standard TCP port.
listen 443 ssl;
listen [::]:443 ssl;
# Reference the TLS cert files produced by Certbot.
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Load the Let's Encrypt Diffie-Hellman parameters generated for
# this server. Without this, the server is vulnerable to Logjam.
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Tighten things down further, per Qualys’ and Certbot’s advice.
ssl_session_cache shared:le_nginx_SSL:1m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_session_timeout 1440m;
# Offer OCSP certificate stapling.
ssl_stapling on;
ssl_stapling_verify on;
# Enable HSTS.
include local/enable-hsts;
```
The [HSTS] step is optional and should be applied only after due
consideration, since it has the potential to lock users out of your
site if you later change your mind on the TLS configuration.
The `local/enable-hsts` file it references is simply:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
It’s a separate file because nginx requires that headers like this be
applied separately for each `location { }` block. We’ve therefore
factored this out so you can `include` it everywhere you need it.
The [OCSP] step is optional, but recommended.
|
| ︙ | ︙ |
Changes to www/server/debian/service.md.
| ︙ | ︙ | |||
51 52 53 54 55 56 57 | create a listener socket on a high-numbered (≥ 1024) TCP port, suitable for sharing a Fossil repo to a workgroup on a private LAN. To do this, write the following in `~/.local/share/systemd/user/fossil.service`: ```dosini | | | | | | | | | | | | 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 | create a listener socket on a high-numbered (≥ 1024) TCP port, suitable for sharing a Fossil repo to a workgroup on a private LAN. To do this, write the following in `~/.local/share/systemd/user/fossil.service`: ```dosini [Unit] Description=Fossil user server After=network-online.target [Service] WorkingDirectory=/home/fossil/museum ExecStart=/home/fossil/bin/fossil server --port 9000 repo.fossil Restart=always RestartSec=3 [Install] WantedBy=multi-user.target ``` Unlike with `inetd` and `xinetd`, we don’t need to tell `systemd` which user and group to run this service as, because we’ve installed it under the account we’re logged into, which `systemd` will use as the service’s owner. |
| ︙ | ︙ | |||
88 89 90 91 92 93 94 | follows that it doesn’t need to run as a system service. A user service works perfectly well for this. Because we’ve set this up as a user service, the commands you give to manipulate the service vary somewhat from the sort you’re more likely to find online: | | | | | | | | 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 |
follows that it doesn’t need to run as a system service. A user service
works perfectly well for this.
Because we’ve set this up as a user service, the commands you give to
manipulate the service vary somewhat from the sort you’re more likely to
find online:
$ systemctl --user daemon-reload
$ systemctl --user enable fossil
$ systemctl --user start fossil
$ systemctl --user status fossil -l
$ systemctl --user stop fossil
That is, we don’t need to talk to `systemd` with `sudo` privileges, but
we do need to tell it to look at the user configuration rather than the
system-level configuration.
This scheme isolates the permissions needed by the Fossil server, which
reduces the amount of damage it can do if there is ever a
remotely-triggerable security flaw found in Fossil.
On some `systemd` based OSes, user services only run while that user is
logged in interactively. This is common on systems aiming to provide
desktop environments, where this is the behavior you often want. To
allow background services to continue to run after logout, say:
$ sudo loginctl enable-linger $USER
You can paste the command just like that into your terminal, since
`$USER` will expand to your login name.
[scgi]: ../any/scgi.md
|
| ︙ | ︙ | |||
163 164 165 166 167 168 169 | roughly equivalent to [the ancient `inetd` method](../any/inetd.md). It’s more complicated, but it has some nice properties. We first need to define the privileged socket listener by writing `/etc/systemd/system/fossil.socket`: ```dosini | | | | | | | | | | | | | | | | | | | | | | | | | | 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 193 194 195 196 197 198 199 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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
roughly equivalent to [the ancient `inetd` method](../any/inetd.md).
It’s more complicated, but it has some nice properties.
We first need to define the privileged socket listener by writing
`/etc/systemd/system/fossil.socket`:
```dosini
[Unit]
Description=Fossil socket
[Socket]
Accept=yes
ListenStream=80
NoDelay=true
[Install]
WantedBy=sockets.target
```
Note the change of configuration directory from the `~/.local` directory
to the system level. We need to start this socket listener at the root
level because of the low-numbered TCP port restriction we brought up
above.
This configuration says more or less the same thing as the socket part
of an `inted` entry [exemplified elsewhere in this
documentation](../any/inetd.md).
Next, create the service definition file in that same directory as
`fossil@.service`:
```dosini
[Unit]
Description=Fossil socket server
After=network-online.target
[Service]
WorkingDirectory=/home/fossil/museum
ExecStart=/home/fossil/bin/fossil http repo.fossil
StandardInput=socket
[Install]
WantedBy=multi-user.target
```
Notice that we haven’t told `systemd` which user and group to run Fossil
under. Since this is a system-level service definition, that means it
will run as root, which then causes Fossil to [automatically drop into a
`chroot(2)` jail](../../chroot.md) rooted at the `WorkingDirectory`
we’ve configured above, shortly after each `fossil http` call starts.
The `Restart*` directives we had in the user service configuration above
are unnecessary for this method, since Fossil isn’t supposed to remain
running under it. Each HTTP hit starts one Fossil instance, which
handles that single client’s request and then immediately shuts down.
Next, you need to tell `systemd` to reload its system-level
configuration files and enable the listening socket:
$ sudo systemctl daemon-reload
$ sudo systemctl enable fossil.socket
And now you can manipulate the socket listener:
$ sudo systemctl start fossil.socket
$ sudo systemctl status -l fossil.socket
$ sudo systemctl stop fossil.socket
Notice that we’re working with the *socket*, not the *service*. The fact
that we’ve given them the same base name and marked the service as an
instantiated service with the “`@`” notation allows `systemd` to
automatically start an instance of the service each time a hit comes in
on the socket that `systemd` is monitoring on Fossil’s behalf. To see
this service instantiation at work, visit a long-running Fossil page
(e.g. `/tarball`) and then give a command like this:
$ sudo systemctl --full | grep fossil
This will show information about the `fossil` socket and service
instances, which should show your `/tarball` hit handler, if it’s still
running:
fossil@20-127.0.0.1:80-127.0.0.1:38304.service
You can feed that service instance description to a `systemctl kill`
command to stop that single instance without restarting the whole
`fossil` service, for example.
In all of this, realize that we’re able to manipulate a single socket
listener or single service instance at a time, rather than reload the
|
| ︙ | ︙ |
Changes to www/server/index.html.
1 2 3 | <div class='fossil-doc' data-title="How To Configure A Fossil Server"> <style type="text/css"> | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<div class='fossil-doc' data-title="How To Configure A Fossil Server">
<style type="text/css">
.doc > .content th.fep {
font-family: "Helvetica Neue", "Arial Narrow", "Myriad Pro", "Avenir Next Condensed";
font-stretch: condensed;
min-width: 3em;
padding: 0.4em;
white-space: nowrap;
}
.doc > .content th.host {
font-family: "Helvetica Neue", "Arial Narrow", "Myriad Pro", "Avenir Next Condensed";
font-stretch: condensed;
padding: 0.4em;
text-align: right;
}
.doc > .content td.doc {
text-align: center;
}
</style>
<h2>No Server Required</h2>
|
| ︙ | ︙ | |||
198 199 200 201 202 203 204 | <h2 id="matrix">Activation Tutorials</h2> <p>We've broken the configuration for each method out into a series of sub-articles. Some of these are generic, while others depend on particular operating systems or front-end software:</p> | < | < | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
<h2 id="matrix">Activation Tutorials</h2>
<p>We've broken the configuration for each method out into a series of
sub-articles. Some of these are generic, while others depend on
particular operating systems or front-end software:</p>
<div class="indent"><table>
<tr>
<th class="host">⇩ OS / Method ⇨</th>
<th class="fep">direct</th>
<th class="fep">inetd</th>
<th class="fep">stunnel</th>
<th class="fep">CGI</th>
<th class="fep">SCGI</th>
|
| ︙ | ︙ | |||
278 279 280 281 282 283 284 |
<td class="doc"><a href="windows/cgi.md">✅</a></td>
<td class="doc">❌</td>
<td class="doc">❌</td>
<td class="doc">❌</td>
<td class="doc"><a href="windows/iis.md">✅</a></td>
<td class="doc"><a href="windows/service.md">✅</a></td>
</tr>
| | | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
<td class="doc"><a href="windows/cgi.md">✅</a></td>
<td class="doc">❌</td>
<td class="doc">❌</td>
<td class="doc">❌</td>
<td class="doc"><a href="windows/iis.md">✅</a></td>
<td class="doc"><a href="windows/service.md">✅</a></td>
</tr>
</table></div>
<p>Where there is a check mark in the "<b>Any</b>" row, the method for that is
generic enough that it works across OSes that Fossil is known to work
on. The check marks below that usually just link to this generic
documentation.</p>
<p>The method in the "<b>proxy</b>" column is for the platform's default
|
| ︙ | ︙ |
Changes to www/server/macos/service.md.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 | However, we will still give two different configurations, just as in the `systemd` article: one for a standalone HTTP server, and one using socket activation. For more information on `launchd`, the single best resource we’ve found is [launchd.info](https://launchd.info). The next best is: | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 |
However, we will still give two different configurations, just as in the
`systemd` article: one for a standalone HTTP server, and one using
socket activation.
For more information on `launchd`, the single best resource we’ve found
is [launchd.info](https://launchd.info). The next best is:
$ man launchd.plist
[la]: http://www.grivet-tools.com/blog/2014/launchdaemons-vs-launchagents/
[ldhome]: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html
[wpa]: https://en.wikipedia.org/wiki/Launchd
## Standalone HTTP Server
To configure `launchd` to start Fossil as a standalone HTTP server,
write the following as `com.example.dev.FossilHTTP.plist`:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.dev.FossilHTTP</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/fossil</string>
<string>server</string>
<string>--port</string>
<string>9000</string>
<string>repo.fossil</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/you/museum</string>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/tmp/fossil-error.log</string>
<key>StandardOutPath</key>
<string>/tmp/fossil-info.log</string>
<key>UserName</key>
<string>you</string>
<key>GroupName</key>
<string>staff</string>
<key>InitGroups</key>
<true/>
</dict>
</plist>
```
In this example, we’re assuming your development organization uses the
domain name “`dev.example.org`”, that your short macOS login name is
“`you`”, and that you store your Fossils in “`~/museum`”. Adjust these
elements of the plist file to suit your local situation.
You might be wondering about the use of `UserName`: isn’t Fossil
supposed to drop privileges and enter [a `chroot(2)`
jail](../../chroot.md) when it’s started as root like this? Why do we
need to give it a user name? Won’t Fossil use the owner of the
repository file to set that? All I can tell you is that in testing here,
if you leave the user and group configuration at the tail end of that
plist file out, Fossil will remain running as root!
Install that file and set it to start with:
$ sudo install -o root -g wheel -m 644 com.example.dev.FossilHTTP.plist \
/Library/LaunchDaemons/
$ sudo launchctl load -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist
Because we set the `RunAtLoad` key, this will also launch the daemon.
Stop the daemon with:
$ sudo launchctl unload -w /Library/LaunchDaemons/com.example.dev.FossilHTTP.plist
## Socket Listener
Another useful method to serve a Fossil repo via `launchd` is by setting
up a socket listener:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.dev.FossilSocket</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/fossil</string>
|
| ︙ | ︙ |
Changes to www/server/openbsd/fastcgi.md.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 | ## <a id="fslinstall"></a>Install Fossil Use the OpenBSD package manager `pkg_add` to install Fossil, making sure to select the statically linked binary. ```console | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 134 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 |
## <a id="fslinstall"></a>Install Fossil
Use the OpenBSD package manager `pkg_add` to install Fossil, making sure
to select the statically linked binary.
```console
$ doas pkg_add fossil
quirks-3.325 signed on 2020-06-12T06:24:53Z
Ambiguous: choose package for fossil
0: <None>
1: fossil-2.10v0
2: fossil-2.10v0-static
Your choice: 2
fossil-2.10v0-static: ok
```
This installs Fossil into the chroot. To facilitate local use, create a
symbolic link of the fossil executable into `/usr/local/bin`.
```console
$ doas ln -s /var/www/bin/fossil /usr/local/bin/fossil
```
As a privileged user, create the file `/var/www/cgi-bin/scm` with the
following contents to make the CGI script that `httpd` will execute in
response to `fsl.domain.tld` requests; all paths are relative to the
`/var/www` chroot.
```sh
#!/bin/fossil
directory: /htdocs/fsl.domain.tld
notfound: https://domain.tld
repolist
errorlog: /logs/fossil.log
```
The `directory` directive instructs Fossil to serve all repositories
found in `/var/www/htdocs/fsl.domain.tld`, while `errorlog` sets logging
to be saved to `/var/www/logs/fossil.log`; create the repository
directory and log file—making the latter owned by the `www` user, and
the script executable.
```console
$ doas mkdir /var/www/htdocs/fsl.domain.tld
$ doas touch /var/www/logs/fossil.log
$ doas chown www /var/www/logs/fossil.log
$ doas chmod 660 /var/www/logs/fossil.log
$ doas chmod 755 /var/www/cgi-bin/scm
```
## <a id="chroot"></a>Setup chroot
Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible
from within the chroot, so need to be constructed; `/var`, however, is
mounted with the `nodev` option. Rather than removing this default
setting, create a small memory filesystem and then mount it on to
`/var/www/dev` with [`mount_mfs(8)`][mfs] so that the `random` and
`null` device files can be created. In order to avoid necessitating a
startup script to recreate the device files at boot, create a template
of the needed ``/dev`` tree to automatically populate the memory
filesystem.
```console
$ doas mkdir /var/www/dev
$ doas install -d -g daemon /template/dev
$ cd /template/dev
$ doas /dev/MAKEDEV urandom
$ doas mknod -m 666 null c 2 2
$ doas mount_mfs -s 1M -P /template/dev /dev/sd0b /var/www/dev
$ ls -l
total 0
crw-rw-rw- 1 root daemon 2, 2 Jun 20 08:56 null
lrwxr-xr-x 1 root daemon 7 Jun 18 06:30 random@ -> urandom
crw-r--r-- 1 root wheel 45, 0 Jun 18 06:30 urandom
```
[mfs]: https://man.openbsd.org/mount_mfs.8
To make the mountable memory filesystem permanent, open `/etc/fstab` as
a privileged user and add the following line to automate creation of the
filesystem at startup:
```console
swap /var/www/dev mfs rw,-s=1048576,-P=/template/dev 0 0
```
The same user that executes the fossil binary must have writable access
to the repository directory that resides within the chroot; on OpenBSD
this is `www`. In addition, grant repository directory ownership to the
user who will push to, pull from, and create repositories.
```console
$ doas chown -R user:www /var/www/htdocs/fsl.domain.tld
$ doas chmod 770 /var/www/htdocs/fsl.domain.tld
```
## <a id="httpdconfig"></a>Configure httpd
On OpenBSD, [httpd.conf(5)][httpd] is the configuration file for
`httpd`. To setup the server to serve all Fossil repositores within the
directory specified in the CGI script, and automatically redirect
standard HTTP requests to HTTPS—apart from [Let's Encrypt][LE]
challenges issued in response to [acme-client(1)][acme] certificate
requests—create `/etc/httpd.conf` as a privileged user with the
following contents.
[LE]: https://letsencrypt.org
[acme]: https://man.openbsd.org/acme-client.1
[httpd.conf(5)]: https://man.openbsd.org/httpd.conf.5
```apache
server "fsl.domain.tld" {
listen on * port http
root "/htdocs/fsl.domain.tld"
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
location * {
block return 301 "https://$HTTP_HOST$REQUEST_URI"
}
location "/*" {
fastcgi { param SCRIPT_FILENAME "/cgi-bin/scm" }
}
}
server "fsl.domain.tld" {
listen on * tls port https
root "/htdocs/fsl.domain.tld"
tls {
certificate "/etc/ssl/domain.tld.fullchain.pem"
key "/etc/ssl/private/domain.tld.key"
}
hsts {
max-age 15768000
preload
subdomains
}
connection max request body 104857600
location "/*" {
fastcgi { param SCRIPT_FILENAME "/cgi-bin/scm" }
}
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
```
[The default limit][dlim] for HTTP messages in OpenBSD’s `httpd` server
is 1 MiB. Fossil chunks its sync protocol such that this is not
normally a problem, but when sending [unversioned content][uv], it uses
a single message for the entire file. Therefore, if you will be storing
files larger than this limit as unversioned content, you need to raise
|
| ︙ | ︙ | |||
185 186 187 188 189 190 191 | In order for `httpd` to serve HTTPS, secure a free certificate from Let's Encrypt using `acme-client`. Before issuing the request, however, ensure you have a zone record for the subdomain with your registrar or nameserver. Then open `/etc/acme-client.conf` as a privileged user to configure the request. ```dosini | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
In order for `httpd` to serve HTTPS, secure a free certificate from
Let's Encrypt using `acme-client`. Before issuing the request, however,
ensure you have a zone record for the subdomain with your registrar or
nameserver. Then open `/etc/acme-client.conf` as a privileged user to
configure the request.
```dosini
authority letsencrypt {
api url "https://acme-v02.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-privkey.pem"
}
authority letsencrypt-staging {
api url "https://acme-staging.api.letsencrypt.org/directory"
account key "/etc/acme/letsencrypt-staging-privkey.pem"
}
domain domain.tld {
alternative names { www.domain.tld fsl.domain.tld }
domain key "/etc/ssl/private/domain.tld.key"
domain certificate "/etc/ssl/domain.tld.crt"
domain full chain certificate "/etc/ssl/domain.tld.fullchain.pem"
sign with letsencrypt
}
```
Start `httpd` with the new configuration file, and issue the certificate
request.
```console
$ doas rcctl start httpd
$ doas acme-client -vv domain.tld
acme-client: /etc/acme/letsencrypt-privkey.pem: account key exists (not creating)
acme-client: /etc/acme/letsencrypt-privkey.pem: loaded RSA account key
acme-client: /etc/ssl/private/domain.tld.key: generated RSA domain key
acme-client: https://acme-v01.api.letsencrypt.org/directory: directories
acme-client: acme-v01.api.letsencrypt.org: DNS: 172.65.32.248
...
N(Q????Z???j?j?>W#????b???? H????eb??T??*? DNosz(???n{L}???D???4[?B] (1174 bytes)
acme-client: /etc/ssl/domain.tld.crt: created
acme-client: /etc/ssl/domain.tld.fullchain.pem: created
```
A successful result will output the public certificate, full chain of
trust, and private key into the `/etc/ssl` directory as specified in
`acme-client.conf`.
```console
$ doas ls -lR /etc/ssl
-r--r--r-- 1 root wheel 2.3K Mar 2 01:31:03 2018 domain.tld.crt
-r--r--r-- 1 root wheel 3.9K Mar 2 01:31:03 2018 domain.tld.fullchain.pem
/etc/ssl/private:
-r-------- 1 root wheel 3.2K Mar 2 01:31:03 2018 domain.tld.key
```
Make sure to reopen `/etc/httpd.conf` to uncomment the second server
block responsible for serving HTTPS requests before proceeding.
## <a id="starthttpd"></a>Start `httpd`
With `httpd` configured to serve Fossil repositories out of
`/var/www/htdocs/fsl.domain.tld`, and the certificates and key in place,
enable and start `slowcgi`—OpenBSD's FastCGI wrapper server that will
execute the above Fossil CGI script—before checking that the syntax of
the `httpd.conf` configuration file is correct, and (re)starting the
server (if still running from requesting a Let's Encrypt certificate).
```console
$ doas rcctl enable slowcgi
$ doas rcctl start slowcgi
slowcgi(ok)
$ doas httpd -vnf /etc/httpd.conf
configuration OK
$ doas rcctl start httpd
httpd(ok)
```
## <a id="clientconfig"></a>Configure Client
To facilitate creating new repositories and pushing them to the server,
add the following function to your `~/.cshrc` or `~/.zprofile` or the
config file for whichever shell you are using on your development box.
```sh
finit() {
fossil init $1.fossil && \
chmod 664 $1.fossil && \
fossil open $1.fossil && \
fossil user password $USER $PASSWD && \
fossil remote-url https://$USER:$PASSWD@fsl.domain.tld/$1 && \
rsync --perms $1.fossil $USER@fsl.domain.tld:/var/www/htdocs/fsl.domain.tld/ >/dev/null && \
chmod 644 $1.fossil && \
fossil ui
}
```
This enables a new repository to be made with `finit repo`, which will
create the fossil repository file `repo.fossil` in the current working
directory; by default, the repository user is set to the environment
variable `$USER`. It then opens the repository and sets the user
password to the `$PASSWD` environment variable (which you can either set
|
| ︙ | ︙ |
Changes to www/server/openbsd/service.wiki.
1 2 3 4 5 6 7 8 | <title>Serving via rc on OpenBSD</title> OpenBSD provides [https://man.openbsd.org/rc.subr.8|rc.subr(8)], a framework for writing [https://man.openbsd.org/rc.8|rc(8)] scripts. <h2>Creating the daemon</h2> Create the file /etc/rc.d/fossil with contents like the following. | > | | > | | | | | | | | > | | > > | | > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 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 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 |
<title>Serving via rc on OpenBSD</title>
OpenBSD provides [https://man.openbsd.org/rc.subr.8|rc.subr(8)],
a framework for writing [https://man.openbsd.org/rc.8|rc(8)] scripts.
<h2>Creating the daemon</h2>
Create the file /etc/rc.d/fossil with contents like the following.
<pre>
#!/bin/ksh
daemon="/usr/local/bin/fossil" # fossil executable
daemon_user="_fossil" # user to run fossil as
daemon_flags="server /home/_fossil/example --repolist --port 8888" # fossil command
. /etc/rc.d/rc.subr
# pexp="$daemon server .*" # See below.
rc_reload=NO # Unsupported by Fossil; 'rcctl reload fossil' kills the process.
rc_bg=YES # Run in the background, since fossil serve does not daemonize itself
rc_cmd $1
</pre>
<h3>pexp</h3>
You may need to uncomment the "pexp=". rc.subr typically
finds the daemon process based by matching the process name and argument list.
Without the "pexp=" line, rc.subr would look for this exact command:
<pre>
/usr/local/bin/fossil server /home/_fossil/example --repolist --port 8888
</pre>
Depending on the arguments and their order, fossil may rewrite the arguments
for display in the process listing ([https://man.openbsd.org/ps.1|ps(1)]),
so rc.subr may fail to find the process through the default match. The example
above does not get rewritten, but the same commands in a different order can
be rewritten.
For example, when I switch the order of the arguments in "daemon_flags",
<pre>
/usr/local/bin/fossil server --repolist --port 8888 /home/_fossil/example
</pre>
the process command is changed to this.
<pre>
/usr/local/bin/fossil server /home/_fossil/example /home/_fossil/example 8888 /home/_fossil/example
</pre>
The commented "pexp=" line instructs rc.subr to choose the process whose
command and arguments text starts with this:
<pre>
/usr/local/bin/fossil server
</pre>
<h2>Enabling the daemon</h2>
Once you have created /etc/rc.d/fossil, run these commands.
<pre>
rcctl enable fossil # add fossil to pkg_scripts in /etc/rc.conf.local
rcctl start fossil # start the daemon now
</pre>
The daemon should now be running and set to start at boot.
<h2>Multiple daemons</h2>
You may want to serve multiple fossil instances with different options.
For example,
* If different users own different repositories, you may want different users
to serve different repositories.
* You may want to serve different repositories on different ports so you can
control them differently with, for example, HTTP reverse proxies or
[https://man.openbsd.org/pf.4|pf(4)].
To run multiple fossil daemons, create multiple files in /etc/rc.d, and
enable each of them. Here are two approaches for creating
the files in /etc/rc.d: Symbolic links and copies.
<h3>Symbolic links</h3>
Suppose you want to run one fossil daemon as user "user1" on port 8881
and another as user "user2" on port 8882. Create the files with
[https://man.openbsd.org/ln.1|ln(1)], and configure them to run different
fossil commands.
<pre>
cd /etc/rc.d
ln -s fossil fossil1
ln -s fossil fossil2
rcctl enable fossil1 fossil2
rcctl set fossil1 user user1
rcctl set fossil2 user user2
rcctl set fossil1 flags 'server /home/user1/repo1.fossil --port 8881'
rcctl set fossil2 flags 'server /home/user2/repo2.fossil --port 8882'
rcctl start fossil1 fossil2
</pre>
<h3>Copies</h3>
You may want to run fossil daemons that are too different to configure
just with [https://man.openbsd.org/rcctl.8|rcctl(8)].
In particular, you can't change the "pexp" with rcctl.
If you want to run fossil commands that are more different,
you may prefer to create separate files in /etc/rc.d.
Replace "ln -s" above with "cp" to accomplish this.
<pre>
cp /etc/rc.d/fossil /etc/rc.d/fossil-user1
cp /etc/rc.d/fossil /etc/rc.d/fossil-user2
</pre>
You can still use commands like "rcctl set fossil-user1 flags", but you
can also edit the "/etc/rc.d/fossil-user1" file.
|
Changes to www/server/windows/iis.md.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 | ## Background Fossil Service Setup You will need to have the Fossil HTTP server running in the background, serving some local repository, bound to localhost on a fixed high-numbered TCP port. For the purposes of testing, simply start it by hand in your command shell of choice: | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
## Background Fossil Service Setup
You will need to have the Fossil HTTP server running in the background,
serving some local repository, bound to localhost on a fixed
high-numbered TCP port. For the purposes of testing, simply start it by
hand in your command shell of choice:
fossil serve --port 9000 --localhost repo.fossil
That command assumes you’ve got `fossil.exe` in your `%PATH%` and you’re
in a directory holding `repo.fossil`. See [the platform-independent
instructions](../any/none.md) for further details.
For a more robust setup, we recommend that you [install Fossil as a
Windows service](./service.md), which will allow Fossil to start at
|
| ︙ | ︙ |
Changes to www/server/windows/service.md.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Fossil as a Windows Service If you need Fossil to start automatically on Windows, it is suggested to install Fossil as a Windows Service. ## Assumptions 1. You have Administrative access to a Windows 2012r2 or above server. 2. You have PowerShell 5.1 or above installed. ## Place Fossil on Server However you obtained your copy of Fossil, it is recommended that you follow | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # Fossil as a Windows Service If you need Fossil to start automatically on Windows, it is suggested to install Fossil as a Windows Service. ## Assumptions 1. You have Administrative access to a Windows 2012r2 or above server. 2. You have PowerShell 5.1 or above installed. ## Place Fossil on Server However you obtained your copy of Fossil, it is recommended that you follow Windows conventions and place it within `\Program Files\FossilSCM`, the proper location for the official 64-bit binary. This way Fossil is at an expected location and you will have minimal issues with Windows interfering in your ability to run Fossil as a service. You will need Administrative rights to place fossil at the recommended location. If you will only be running Fossil as a service, you do not need to add this location to the path, though you may do so if you wish. ## Installing Fossil as a Service |
| ︙ | ︙ |
Changes to www/server/windows/stunnel.md.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 | ## Assumptions 1. You have Administrative access to a Windows 2012r2 or above server. 2. You have PowerShell 5.1 or above installed. 3. You have acquired a certificate either from a Public CA or an Internal CA. | < < < < < < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ## Assumptions 1. You have Administrative access to a Windows 2012r2 or above server. 2. You have PowerShell 5.1 or above installed. 3. You have acquired a certificate either from a Public CA or an Internal CA. ## Configure Fossil Service for https Due to the need for the `--https` option for successfully using Fossil with stunnel, we will use [Advanced service installation using PowerShell](./service.md#PowerShell). We will need to change the command to install the Fossil Service to configure it properly for use with stunnel as an https proxy. Run the following: |
| ︙ | ︙ |
Changes to www/serverext.wiki.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 | An administrator activates the CGI extension mechanism by specifying an "Extension Root Directory" or "extroot" as part of the [./server/index.html|server setup]. If the Fossil server is itself run as [./server/any/cgi.md|CGI], then add a line to the [./cgi.wiki#extroot|CGI script file] that says: | | | | | | | | 29 30 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 | An administrator activates the CGI extension mechanism by specifying an "Extension Root Directory" or "extroot" as part of the [./server/index.html|server setup]. If the Fossil server is itself run as [./server/any/cgi.md|CGI], then add a line to the [./cgi.wiki#extroot|CGI script file] that says: <pre> extroot: <i>DIRECTORY</i> </pre> Or, if the Fossil server is being run using the "[./server/any/none.md|fossil server]" or "[./server/any/none.md|fossil ui]" or "[./server/any/inetd.md|fossil http]" commands, then add an extra "--extroot <i>DIRECTORY</i>" option to that command. The <i>DIRECTORY</i> is the DOCUMENT_ROOT for the CGI. Files in the DOCUMENT_ROOT are accessed via URLs like this: <pre> https://example-project.org/ext/<i>FILENAME</i> </pre> In other words, access files in DOCUMENT_ROOT by appending the filename relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext] page of the Fossil server. Files that are readable but not executable are returned as static content. Files that are executable are run as CGI. <h3>2.1 Example #1</h3> The source code repository for SQLite is a Fossil server that is run as CGI. The URL for the source code repository is [https://sqlite.org/src]. The CGI script looks like this: <verbatim> #!/usr/bin/fossil repository: /fossil/sqlite.fossil errorlog: /logs/errors.txt extroot: /sqlite-src-ext </verbatim> The "extroot: /sqlite-src-ext" line tells Fossil that it should look for extension CGIs in the /sqlite-src-ext directory. (All of this is happening inside of a chroot jail, so putting the document root in a top-level directory is a reasonable thing to do.) When a URL like "https://sqlite.org/src/ext/checklist" is received by the |
| ︙ | ︙ | |||
99 100 101 102 103 104 105 | main web server which in turn relays the result back to the original client. <h3>2.2 Example #2</h3> The [https://fossil-scm.org/home|Fossil self-hosting repository] is also a CGI that looks like this: | | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | main web server which in turn relays the result back to the original client. <h3>2.2 Example #2</h3> The [https://fossil-scm.org/home|Fossil self-hosting repository] is also a CGI that looks like this: <verbatim> #!/usr/bin/fossil repository: /fossil/fossil.fossil errorlog: /logs/errors.txt extroot: /fossil-extroot </verbatim> The extroot for this Fossil server is /fossil-extroot and in that directory is an executable file named "fileup1" - another [https://wapp.tcl.tk|Wapp] script. (The extension mechanism is not required to use Wapp. You can use any kind of program you like. But the creator of SQLite and Fossil is fond of [https://www.tcl.tk|Tcl/Tk] and so he tends to gravitate toward Tcl-based technologies like Wapp.) The fileup1 script is a demo program that lets |
| ︙ | ︙ | |||
199 200 201 202 203 204 205 | header and footer, then the inserted header will include a Content Security Policy (CSP) restriction on the use of javascript within the webpage. Any <script>...</script> elements within the CGI output must include a nonce or else they will be suppressed by the web browser. The FOSSIL_NONCE variable contains the value of that nonce. So, in other words, to get javascript to work, it must be enclosed in: | | | | | | | | 199 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 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | header and footer, then the inserted header will include a Content Security Policy (CSP) restriction on the use of javascript within the webpage. Any <script>...</script> elements within the CGI output must include a nonce or else they will be suppressed by the web browser. The FOSSIL_NONCE variable contains the value of that nonce. So, in other words, to get javascript to work, it must be enclosed in: <verbatim> <script nonce='$FOSSIL_NONCE'>...</script> </verbatim> Except, of course, the $FOSSIL_NONCE is replaced by the value of the FOSSIL_NONCE environment variable. <h3>3.1 Input Content</h3> If the HTTP request includes content (for example if this is a POST request) then the CONTENT_LENGTH value will be positive and the data for the content will be readable on standard input. <h2>4.0 CGI Outputs</h2> CGI programs construct a reply by writing to standard output. The first few lines of output are parameters intended for the web server that invoked the CGI. These are followed by a blank line and then the content. Typical parameter output looks like this: <verbatim> Status: 200 OK Content-Type: text/html </verbatim> CGI programs can return any content type they want - they are not restricted to text replies. It is OK for a CGI program to return (for example) image/png. The fields of the CGI response header can be any valid HTTP header fields. Those that Fossil does not understand are simply relayed back to up the line to the requester. Fossil takes special action with some content types. If the Content-Type is "text/x-fossil-wiki" or "text/x-markdown" then Fossil converts the content from [/wiki_rules|Fossil-Wiki] or [/md_rules|Markdown] into HTML, adding its own header and footer text according to the repository skin. Content of type "text/html" is normally passed straight through unchanged. However, if the text/html content is of the form: <verbatim> <div class='fossil-doc' data-title='DOCUMENT TITLE'> ... HTML content there ... </div> </verbatim> In other words, if the outer-most markup of the HTML is a <div> element with a single class of "fossil-doc", then Fossil will adds its own header and footer to the HTML. The page title contained in the added header will be extracted from the "data-title" attribute. |
| ︙ | ︙ |
Changes to www/ssl-server.md.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 | obtaining a CA-signed certificate. ## Usage To put any of the Fossil server commands into SSL/TLS mode, simply add the "--cert" command-line option. | < | < | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
obtaining a CA-signed certificate.
## Usage
To put any of the Fossil server commands into SSL/TLS mode, simply
add the "--cert" command-line option.
fossil ui --cert unsafe-builtin
The --cert option is what tells Fossil to use TLS encryption.
Normally, the argument to --cert is the name of a file containing
the certificate (the "fullchain.pem" file) for the website. In this
example, the magic name "unsafe-builtin" is used, which causes Fossil
to use a self-signed cert rather than a real cert obtained from a
[Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority)
|
| ︙ | ︙ | |||
86 87 88 89 90 91 92 | key and cert. Fossil wants to read certs and public keys in the [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail). PEM is a pure ASCII text format. The private key consists of text like this: | < | | | < | | | < | < < | < | 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 |
key and cert.
Fossil wants to read certs and public keys in the
[PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail).
PEM is a pure ASCII text format. The private key consists of text
like this:
-----BEGIN PRIVATE KEY-----
*base-64 encoding of the private key*
-----END PRIVATE KEY-----
Similarly, a PEM-encoded cert will look like this:
-----BEGIN CERTIFICATE-----
*base-64 encoding of the certificate*
-----END CERTIFICATE-----
In both formats, text outside of the delimiters is ignored. That means
that if you have a PEM-formatted private key and a separate PEM-formatted
certificate, you can concatenate the two into a single file and the
individual components will still be easily accessible.
If you have a single file that holds both your private key and your
cert, you can hand it off to the "[fossil server](/help?cmd=server)"
command using the --cert option. Like this:
fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil
The command above is sufficient to run a fully-encrypted web site for
the "myproject.fossil" Fossil repository. This command must be run as
root, since it wants to listen on TCP port 443, and only root processes are
allowed to do that. This is safe, however, since before reading any
information off of the wire, Fossil will put itself inside a chroot jail
at /home/www and drop all root privileges.
### Keeping The Cert And Private Key In Separate Files
If you do not want to combine your cert and private key into a single
big PEM file, you can keep them separate using the --pkey option to
Fossil.
fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil
## The ACME Protocol
The [ACME Protocol][2] is used to prove to a CA that you control a
website. CAs require proof that you control a domain before they
will issue a cert for that domain. The usual means of dealing
with ACME is to run the separate [certbot](https://certbot.eff.org) tool.
|
| ︙ | ︙ | |||
171 172 173 174 175 176 177 | the repository file. If the "server" or "http" command are run against a directory full of Fossil repositories, then the ".well-known" sub-directory should be in that top-level directory. Thus, to set up a project website, you should first run Fossil in ordinary unencrypted HTTP mode like this: | < | < | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
the repository file. If the "server" or "http" command are run against
a directory full of Fossil repositories, then the ".well-known" sub-directory
should be in that top-level directory.
Thus, to set up a project website, you should first run Fossil in ordinary
unencrypted HTTP mode like this:
fossil server --port 80 --acme /home/www/myproject.fossil
Then you create your public/private key pair and run certbot, giving it
a --webroot of /home/www. Certbot will create the sub-directory
named "/home/www/.well-known" and put token files there, which the CA
will verify. Then certbot will store your new cert in a particular file.
Once certbot has obtained your cert, then you can concatenate that
cert with your private key and run Fossil in SSL/TLS mode as shown above.
[2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment
|
Changes to www/ssl.wiki.
| ︙ | ︙ | |||
80 81 82 83 84 85 86 | passing the <tt>--with-openssl</tt> option to the <tt>configure</tt> script. Type <tt>./configure --help</tt> for details. Another option is to download the source code to OpenSSL and build Fossil against that private version of OpenSSL: <pre> | | | | | | | | | | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | passing the <tt>--with-openssl</tt> option to the <tt>configure</tt> script. Type <tt>./configure --help</tt> for details. Another option is to download the source code to OpenSSL and build Fossil against that private version of OpenSSL: <pre> cd compat # relative to the Fossil source tree root tar xf /path/to/openssl-*.tar.gz ln -fs openssl-x.y.z openssl cd openssl ./config # or, e.g. ./Configure darwin64-x86_64-cc make -j11 cd ../.. ./configure --with-openssl=tree make -j11 </pre> That will get you a Fossil binary statically linked to this in-tree version of OpenSSL. Beware, taking this path typically opens you up to new problems, which are conveniently covered in the next section! |
| ︙ | ︙ | |||
119 120 121 122 123 124 125 | to accept the certificate the first time you communicate with the server. Verify the certificate fingerprint is correct, then answer "always" if you want Fossil to remember your decision. If you are cloning from or syncing to Fossil servers that use a certificate signed by a well-known CA or one of its delegates, Fossil still has to know which CA roots to trust. When this fails, you get an | | | | | | | | | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 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 |
to accept the certificate the first time you communicate with the
server. Verify the certificate fingerprint is correct, then answer
"always" if you want Fossil to remember your decision.
If you are cloning from or syncing to Fossil servers that use a
certificate signed by a well-known CA or one of its delegates, Fossil
still has to know which CA roots to trust. When this fails, you get an
error message that looks like this:
<pre>
Unable to verify SSL cert from fossil-scm.org
subject: CN = sqlite.org
issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
sha256: bf26092dd97df6e4f7bf1926072e7e8d200129e1ffb8ef5276c1e5dd9bc95d52
accept this cert and continue (y/N)?
</pre>
In older versions, the message was much longer and began with this line:
<pre>
SSL verification failed: unable to get local issuer certificate
</pre>
Fossil relies on the OpenSSL library to have some way to check a trusted
list of CA signing keys. There are two common ways this fails:
# The OpenSSL library Fossil is linked to doesn't have a CA
signing key set at all, so that it initially trusts no certificates
at all.
# The OpenSSL library does have a CA cert set, but your Fossil server's
TLS certificate was signed by a CA that isn't in that set.
A common reason to fall into the second trap is that you're using
certificates signed by a local private CA, as often happens in large
enterprises. You can solve this sort of problem by getting your local
CA's signing certificate in PEM format and pointing OpenSSL at it:
<pre>
fossil set --global ssl-ca-location /path/to/local-ca.pem
</pre>
The use of <tt>--global</tt> with this option is common, since you may
have multiple repositories served under certificates signed by that same
CA. However, if you have a mix of publicly-signed and locally-signed
certificates, you might want to drop the <tt>--global</tt> flag and set
this option on a per-repository basis instead.
|
| ︙ | ︙ | |||
180 181 182 183 184 185 186 | may find it acceptable to use the same Mozilla NSS cert set. I do not know of a way to easily get this from Mozilla themselves, but I did find a [https://curl.se/docs/caextract.html | third party source] for the <tt>cacert.pem</tt> file. I suggest placing the file into your Windows user home directory so that you can then point Fossil at it like so: <pre> | | > > > > > > > > > > > > > > > > | 180 181 182 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 216 | may find it acceptable to use the same Mozilla NSS cert set. I do not know of a way to easily get this from Mozilla themselves, but I did find a [https://curl.se/docs/caextract.html | third party source] for the <tt>cacert.pem</tt> file. I suggest placing the file into your Windows user home directory so that you can then point Fossil at it like so: <pre> fossil set --global ssl-ca-location %userprofile%\cacert.pem </pre> This can also happen if you've linked Fossil to a version of OpenSSL [#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can work in that case, too. <blockquote> OpenSSL 3.2.0 or greater is able to use the stock CA certificates managed by Windows, and Fossil 2.25 (still in development as of 2024-07-15) takes advantage of this feature. This <em>possibly</em> eliminates the need to manually install the Mozilla certificate package, for example when connecting to Fossil servers secured by the widely-used Let's Encrypt certificates. Run the following command to check if the feature is supported: <pre> fossil tls-config show -v </pre> (See the "OpenSSL-winstore" section, requires Fossil 2.25 or greater.) </blockquote> When you build Fossil on Linux platforms against the binary OpenSSL package provided with the OS, you typically get a root cert store along with the platform OpenSSL package, either built-in or as a hard dependency. <h4>Client-Side Certificates</h4> |
| ︙ | ︙ | |||
224 225 226 227 228 229 230 | If you attempt to connect to a server which requests a client certificate, but don't provide one, fossil will show an error message which explains what to do to authenticate with the server. <h2 id="server">Server-Side Configuration</h2> | | < | | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | If you attempt to connect to a server which requests a client certificate, but don't provide one, fossil will show an error message which explains what to do to authenticate with the server. <h2 id="server">Server-Side Configuration</h2> Before Fossil's built-in HTTP server gained [./ssl-server.md | TLS support], system administrators that wanted to add this had to put it behind a reverse proxy that would do the translation. Since advantages remain for delegating TLS to another layer in the stack, instructions for doing so continue to be included in our documentation, such as: * <a id="stunnel" href="./server/any/stunnel.md">Serving via stunnel</a> * <a id="althttpd" href="./server/any/althttpd.md">Serving via stunnel + althttpd</a> |
| ︙ | ︙ |
Changes to www/stats.wiki.
1 | <title>Fossil Performance</title> | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <title>Fossil Performance</title> The questions will inevitably arise: How does Fossil perform? Does it use a lot of disk space or bandwidth? Is it scalable? In an attempt to answers these questions, this report looks at several projects that use fossil for configuration management and examines how well they are working. The following table is a summary of the results. (Last updated on 2018-06-04.) Explanation and analysis follows the table. <table> <tr> <th>Project</th> <th>Number Of Artifacts</th> <th>Number Of Check-ins</th> <th>Project Duration<br>(as of 2018-06-04)</th> <th>Uncompressed Size</th> <th>Repository Size</th> |
| ︙ | ︙ |
Changes to www/sync.wiki.
| ︙ | ︙ | |||
46 47 48 49 50 51 52 | peer-to-peer communication and without any kind of central authority. If you are already familiar with CRDTs and were wondering if Fossil used them, the answer is "yes". We just don't call them by that name. | | | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | peer-to-peer communication and without any kind of central authority. If you are already familiar with CRDTs and were wondering if Fossil used them, the answer is "yes". We just don't call them by that name. <h2 id="transport">2.0 Transport</h2> All communication between client and server is via HTTP requests. The server is listening for incoming HTTP requests. The client issues one or more HTTP requests and receives replies for each request. The server might be running as an independent server |
| ︙ | ︙ | |||
80 81 82 83 84 85 86 | to represent the listener and initiator of the interaction, respectively. Nothing in this protocol requires that the server actually be a back-room processor housed in a datacenter, nor does the client need to be a desktop or handheld device. For the purposes of this article "client" simply means the repository that initiates the conversation and "server" is the repository that responds. Nothing more. | | | | > > > > > > > > > > > > > > > > > > > > | | < | < < | < | | > | < | > | < | | | < | < | < | | | | | 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 134 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 180 181 182 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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
to represent the listener and initiator of the interaction, respectively.
Nothing in this protocol requires that the server actually be a back-room
processor housed in a datacenter, nor does the client need to be a desktop
or handheld device. For the purposes of this article "client" simply means
the repository that initiates the conversation and "server" is the repository
that responds. Nothing more.
<h4 id="https">2.0.1 HTTPS Transport</h4>
HTTPS differs from HTTP only in that the HTTPS protocol is
encrypted as it travels over the wire. The underlying protocol
is the same. This document describes only the underlying, unencrypted
messages that go client to server and back to client.
Whether or not those messages are encrypted does not come into play
in this document.
Fossil includes built-in
[./ssl-server.md|support for HTTPS encryption] in both client and server.
<h4 id="ssh">2.0.2 SSH Transport</h4>
When doing a sync using an "<code>ssh:…</code>" URL, the same HTTP transport protocol
is used. Fossil simply uses [https://en.wikipedia.org/wiki/Secure_Shell|ssh]
to start an instance of the [/help?cmd=test-http|fossil test-http] command
running on the remote machine. It then sends HTTP requests and gets back HTTP
replies over the SSH connection, rather than sending and receiving over an
internet socket. To see the specific "ssh" command that the Fossil client
runs in order to set up a connection, add either of the the "--httptrace" or
"--sshtrace" options to the "fossil sync" command line.
This method is dependent on the remote <var>PATH</var> set by the SSH
daemon, which may not be the same as your interactive shell's
<var>PATH</var> on that same server. It is common to find
<var>$HOME/bin</var> in the latter but not the former, for instance,
leading to failures to sync over <code>ssh:…</code> URLs when you
install the <code>fossil</code> binary in a nonstandard location, as
with
<verbatim>./configure --prefix=$HOME && make install</verbatim>
The simpler of the two solutions to this problem is to install Fossil
where sshd expects to find it, but when that isn't an option, you can
instead give a URL like this:
<verbatim>fossil clone ssh://myserver.example.com/path/to/repo.fossil?fossil=/home/me/bin/fossil</verbatim>
That gives the local Fossil instance the absolute path to the binary on
the remote machine for use when calling that Fossil instance through the
SSH tunnel.
<h4 id="file">2.0.3 FILE Transport</h4>
When doing a sync using a "file:..." URL, the same HTTP protocol is
still used. But instead of sending each HTTP request over a socket or
via SSH, the HTTP request is written into a temporary file. The client
then invokes the [/help?cmd=http|fossil http] command in a subprocess
to process the request and and generate a reply. The client then reads
the HTTP reply out of a temporary file on disk, and deletes the two
temporary files. To see the specific "fossil http" command that is run
in order to implement the "file:" transport, add the "--httptrace"
option to the "fossil sync" command.
<h3 id="srv-id">2.1 Server Identification</h3>
The server is identified by a URL argument that accompanies the
push, pull, or sync command on the client. (As a convenience to
users, the URL can be omitted on the client command and the same URL
from the most recent push, pull, or sync will be reused. This saves
typing in the common case where the client does multiple syncs to
the same server.)
The client modifies the URL by appending the method name "<b>/xfer</b>"
to the end. For example, if the URL specified on the client command
line is
<pre>https://fossil-scm.org/fossil</pre>
Then the URL that is really used to do the synchronization will
be:
<pre>https://fossil-scm.org/fossil/xfer</pre>
<h3 id="req-format">2.2 HTTP Request Format</h3>
The client always sends a POST request to the server. The
general format of the POST request is as follows:
<pre>
POST /fossil/xfer HTTP/1.0
Host: fossil-scm.hwaci.com:80
Content-Type: application/x-fossil
Content-Length: 4216
</pre>
<i><pre>content...</pre></i>
In the example above, the pathname given after the POST keyword
on the first line is a copy of the URL pathname. The Host: parameter
is also taken from the URL. The content type is always either
"application/x-fossil" or "application/x-fossil-debug". The "x-fossil"
content type is the default. The only difference is that "x-fossil"
content is compressed using zlib whereas "x-fossil-debug" is sent
uncompressed.
A typical reply from the server might look something like this:
<pre>
HTTP/1.0 200 OK
Date: Mon, 10 Sep 2007 12:21:01 GMT
Connection: close
Cache-control: private
Content-Type: application/x-fossil; charset=US-ASCII
Content-Length: 265
</pre>
<i><pre>content...</pre></i>
The content type of the reply is always the same as the content type
of the request.
<h2 id="content">3.0 Fossil Synchronization Content</h2>
A synchronization request between a client and server consists of
one or more HTTP requests as described in the previous section. This
section details the "x-fossil" content type.
<h3 id="lines">3.1 Line-oriented Format</h3>
The x-fossil content type consists of zero or more "cards". Cards
are separated by the newline character ("\n"). Leading and trailing
whitespace on a card is ignored. Blank cards are ignored.
Each card is divided into zero or more space separated tokens.
The first token on each card is the operator. Subsequent tokens
are arguments. The set of operators understood by servers is slightly
different from the operators understood by clients, though the two
are very similar.
<h3 id="login">3.2 Login Cards</h3>
Every message from client to server begins with one or more login
cards. Each login card has the following format:
<pre><b>login</b> <i>userid nonce signature</i></pre>
The userid is the name of the user that is requesting service
from the server. The nonce is the SHA1 hash of the remainder of
the message - all text that follows the newline character that
terminates the login card. The signature is the SHA1 hash of
the concatenation of the nonce and the users password.
For each login card, the server looks up the user and verifies
that the nonce matches the SHA1 hash of the remainder of the
message. It then checks the signature hash to make sure the
signature matches. If everything
checks out, then the client is granted all privileges of the
specified user.
Privileges are cumulative. There can be multiple successful
login cards. The session privilege is the union of all
privileges from all login cards.
<h3 id="file">3.3 File Cards</h3>
Artifacts are transferred using either "file" cards, or "cfile"
or "uvfile" cards.
The name "file" card comes from the fact that most artifacts correspond to
files that are under version control.
The "cfile" name is an abbreviation for "compressed file".
The "uvfile" name is an abbreviation for "unversioned file".
<h4 id="ordinary-fc">3.3.1 Ordinary File Cards</h4>
For sync protocols, artifacts are transferred using "file"
cards. File cards come in two different formats depending
on whether the artifact is sent directly or as a
[./delta_format.wiki|delta] from some
other artifact.
<pre>
<b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i>
<b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
</pre>
File cards are followed by in-line "payload" data.
The content of the artifact
or the artifact delta is the first <i>size</i> bytes of the
x-fossil content that immediately follow the newline that
terminates the file card.
|
| ︙ | ︙ | |||
267 268 269 270 271 272 273 | the ID of another artifact that is the source of the delta. File cards are sent in both directions: client to server and server to client. A delta might be sent before the source of the delta, so both client and server should remember deltas and be able to apply them when their source arrives. | | | | | | | < < | < | < | < | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 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 | the ID of another artifact that is the source of the delta. File cards are sent in both directions: client to server and server to client. A delta might be sent before the source of the delta, so both client and server should remember deltas and be able to apply them when their source arrives. <h4 id="compressed-fc">3.3.2 Compressed File Cards</h4> A client that sends a clone protocol version "3" or greater will receive artifacts as "cfile" cards while cloning. This card was introduced to improve the speed of the transfer of content by sending the compressed artifact directly from the server database to the client. Compressed File cards are similar to File cards, sharing the same in-line "payload" data characteristics and also the same treatment of direct content or delta content. Cfile cards come in two different formats depending on whether the artifact is sent directly or as a delta from some other artifact. <pre> <b>cfile</b> <i>artifact-id usize csize</i> <b>\n</b> <i>content</i> <b>cfile</b> <i>artifact-id delta-artifact-id usize csize</i> <b>\n</b> <i>content</i> </pre> The first argument of the cfile card is the ID of the artifact that is being transferred. The artifact ID is the lower-case hexadecimal representation of the name hash for the artifact. The second argument of the cfile card is the original size in bytes of the artifact. The last argument of the cfile card is the number of compressed bytes of payload that immediately follow the cfile card. If the cfile card has only three arguments, that means the payload is the complete content of the artifact. If the cfile card has four arguments, then the payload is a delta and the second argument is the ID of another artifact that is the source of the delta and the third argument is the original size of the delta artifact. Unlike file cards, cfile cards are only sent in one direction during a clone from server to client for clone protocol version "3" or greater. <h4 id="private">3.3.3 Private artifacts</h4> "Private" content consist of artifacts that are not normally synced. However, private content will be synced when the the [/help?cmd=sync|fossil sync] command includes the "--private" option. Private content is marked by a "private" card: <pre><b>private</b></pre> The private card has no arguments and must directly precede a file card that contains the private content. <h4 id="uv-fc">3.3.4 Unversioned File Cards</h4> Unversioned content is sent in both directions (client to server and server to client) using "uvfile" cards in the following format: <pre><b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i></pre> The <i>name</i> field is the name of the unversioned file. The <i>mtime</i> is the last modification time of the file in seconds since 1970. The <i>hash</i> field is the hash of the content for the unversioned file, or "<b>-</b>" for deleted content. The <i>size</i> field is the (uncompressed) size of the content in bytes. The <i>flags</i> field is an integer which is interpreted |
| ︙ | ︙ | |||
350 351 352 353 354 355 356 | A server will only accept uvfile cards if the login user has the "y" write-unversioned permission. Servers send uvfile cards in response to uvgimme cards received from the client. Clients send uvfile cards when they determine that the server needs the content based on uvigot cards previously received from the server. | | | | | | | | | | | | 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 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | A server will only accept uvfile cards if the login user has the "y" write-unversioned permission. Servers send uvfile cards in response to uvgimme cards received from the client. Clients send uvfile cards when they determine that the server needs the content based on uvigot cards previously received from the server. <h3 id="push" name="pull">3.4 Push and Pull Cards</h3> Among the first cards in a client-to-server message are the push and pull cards. The push card tells the server that the client is pushing content. The pull card tells the server that the client wants to pull content. In the event of a sync, both cards are sent. The format is as follows: <pre> <b>push</b> <i>servercode projectcode</i> <b>pull</b> <i>servercode projectcode</i> </pre> The <i>servercode</i> argument is the repository ID for the client. The <i>projectcode</i> is the identifier of the software project that the client repository contains. The projectcode for the client and server must match in order for the transaction to proceed. The server will also send a push card back to the client during a clone. This is how the client determines what project code to put in the new repository it is constructing. The <i>servercode</i> argument is currently unused. <h3 id="clones">3.5 Clone Cards</h3> A clone card works like a pull card in that it is sent from client to server in order to tell the server that the client wants to pull content. The clone card comes in two formats. Older clients use the no-argument format and newer clients use the two-argument format. <pre> <b>clone</b> <b>clone</b> <i>protocol-version sequence-number</i> </pre> <h4>3.5.1 Protocol 3</h4> The latest clients send a two-argument clone message with a protocol version of "3". (Future versions of Fossil might use larger protocol version numbers.) Version "3" of the protocol enhanced version "2" by introducing the "cfile" card which is intended to speed up clone operations. Instead of sending "file" cards, the server will send "cfile" cards <h4>3.5.2 Protocol 2</h4> The sequence-number sent is the number of artifacts received so far. For the first clone message, the sequence number is 0. The server will respond by sending file cards for some number of artifacts up to the maximum message size. The server will also send a single "clone_seqno" card to the client so that the client can know where the server left off. <pre> <b>clone_seqno</b> <i>sequence-number</i> </pre> The clone message in subsequent HTTP requests for the same clone operation will use the sequence-number from the clone_seqno of the previous reply. In response to an initial clone message, the server also sends the client a push message so that the client can discover the projectcode for |
| ︙ | ︙ | |||
432 433 434 435 436 437 438 | The legacy protocol works well for smaller repositories (50MB with 50,000 artifacts) but is too slow and unwieldy for larger repositories. The version 2 protocol is an effort to improve performance. Further performance improvements with higher-numbered clone protocols are possible in future versions of Fossil. | | | | | | | 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 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 | The legacy protocol works well for smaller repositories (50MB with 50,000 artifacts) but is too slow and unwieldy for larger repositories. The version 2 protocol is an effort to improve performance. Further performance improvements with higher-numbered clone protocols are possible in future versions of Fossil. <h3 id="igot">3.6 Igot Cards</h3> An igot card can be sent from either client to server or from server to client in order to indicate that the sender holds a copy of a particular artifact. The format is: <pre> <b>igot</b> <i>artifact-id</i> ?<i>flag</i>? </pre> The first argument of the igot card is the ID of the artifact that the sender possesses. The receiver of an igot card will typically check to see if it also holds the same artifact and if not it will request the artifact using a gimme card in either the reply or in the next message. If the second argument exists and is "1", then the artifact identified by the first argument is private on the sender and should be ignored unless a "--private" [/help?cmd=sync|sync] is occurring. The name "igot" comes from the English slang expression "I got" meaning "I have". <h4>3.6.1 Unversioned Igot Cards</h4> Zero or more "uvigot" cards are sent from server to client when synchronizing unversioned content. The format of a uvigot card is as follows: <pre> <b>uvigot</b> <i>name mtime hash size</i> </pre> The <i>name</i> argument is the name of an unversioned file. The <i>mtime</i> is the last modification time of the unversioned file in seconds since 1970. The <i>hash</i> is the SHA1 or SHA3-256 hash of the unversioned file content, or "<b>-</b>" if the file has been deleted. The <i>size</i> is the uncompressed size of the file in bytes. |
| ︙ | ︙ | |||
487 488 489 490 491 492 493 | When a client receives a "uvigot" card, it checks to see if the file needs to be transferred from client to server or from server to client. If a client-to-server transmission is needed, the client schedules that transfer to occur on a subsequent HTTP request. If a server-to-client transfer is needed, then the client sends a "uvgimme" card back to the server to request the file content. | | | | | | | | | | | | | 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 576 577 578 579 580 | When a client receives a "uvigot" card, it checks to see if the file needs to be transferred from client to server or from server to client. If a client-to-server transmission is needed, the client schedules that transfer to occur on a subsequent HTTP request. If a server-to-client transfer is needed, then the client sends a "uvgimme" card back to the server to request the file content. <h3 id="gimme">3.7 Gimme Cards</h3> A gimme card is sent from either client to server or from server to client. The gimme card asks the receiver to send a particular artifact back to the sender. The format of a gimme card is this: <pre> <b>gimme</b> <i>artifact-id</i> </pre> The argument to the gimme card is the ID of the artifact that the sender wants. The receiver will typically respond to a gimme card by sending a file card in its reply or in the next message. The "gimme" name means "give me". The imperative "give me" is pronounced as if it were a single word "gimme" in some dialects of English (including the dialect spoken by the original author of Fossil). <h4>3.7.1 Unversioned Gimme Cards</h4> Sync synchronizing unversioned content, the client may send "uvgimme" cards to the server. A uvgimme card requests that the server send unversioned content to the client. The format of a uvgimme card is as follows: <pre> <b>uvgimme</b> <i>name</i> </pre> The <i>name</i> is the name of the unversioned file found on the server that the client would like to have. When a server sees a uvgimme card, it normally responses with a uvfile card, though it might also send another uvigot card if the HTTP reply is already oversized. <h3 id="cookie">3.8 Cookie Cards</h3> A cookie card can be used by a server to record a small amount of state information on a client. The server sends a cookie to the client. The client sends the same cookie back to the server on its next request. The cookie card has a single argument which is its payload. <pre> <b>cookie</b> <i>payload</i> </pre> The client is not required to return the cookie to the server on its next request. Or the client might send a cookie from a different server on the next request. So the server must not depend on the cookie and the server must structure the cookie payload in such a way that it can tell if the cookie it sees is its own cookie or a cookie from another server. (Typically the server will embed its servercode as part of the cookie.) <h3 id="reqconfig">3.9 Request-Configuration Cards</h3> A request-configuration or "reqconfig" card is sent from client to server in order to request that the server send back "configuration" data. "Configuration" data is information about users or website appearance or other administrative details which are not part of the persistent and versioned state of the project. For example, the "name" of the project, the default Cascading Style Sheet (CSS) for the web-interface, and the project logo displayed on the web-interface are all configuration data elements. The reqconfig card is normally sent in response to the "fossil configuration pull" command. The format is as follows: <pre> <b>reqconfig</b> <i>configuration-name</i> </pre> As of 2018-06-04, the configuration-name must be one of the following values: <table border=0 align="center"> <tr><td valign="top"> <ul> |
| ︙ | ︙ | |||
601 602 603 604 605 606 607 | <li> ignore-glob <li> keep-glob <li> crlf-glob <ul></td><td valign="top"><ul> <li> crnl-glob <li> encoding-glob <li> empty-dirs | | | 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 | <li> ignore-glob <li> keep-glob <li> crlf-glob <ul></td><td valign="top"><ul> <li> crnl-glob <li> encoding-glob <li> empty-dirs <li> <s title="removed 2020-08, version 2.12.1">allow-symlinks</s> <li> dotfiles <li> parent-project-code <li> parent-projet-name <li> hash-policy <li> mv-rm-files <li> ticket-table <li> ticket-common |
| ︙ | ︙ | |||
651 652 653 654 655 656 657 | values instead of a single value. The content of these configuration items is returned in a "config" card that contains pure SQL text that is intended to be evaluated by the client. The @user and @concealed configuration items contain sensitive information and are ignored for clients without sufficient privilege. | | | | | | | | 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 | values instead of a single value. The content of these configuration items is returned in a "config" card that contains pure SQL text that is intended to be evaluated by the client. The @user and @concealed configuration items contain sensitive information and are ignored for clients without sufficient privilege. <h3 id="config">3.10 Configuration Cards</h3> A "config" card is used to send configuration information from client to server (in response to a "fossil configuration push" command) or from server to client (in response to a "fossil configuration pull" or "fossil clone" command). The format is as follows: <pre> <b>config</b> <i>configuration-name size</i> <b>\n</b> <i>content</i> </pre> The server will only accept a config card if the user has "Admin" privilege. A client will only accept a config card if it had sent a corresponding reqconfig card in its request. The content of the configuration item is used to overwrite the corresponding configuration data in the receiver. <h3 id="pragma">3.11 Pragma Cards</h3> The client may try to influence the behavior of the server by issuing a pragma card: <pre> <b>pragma</i> <i>name value...</i> </pre> The "pragma" card has at least one argument which is the pragma name. The pragma name defines what the pragma does. A pragma might have zero or more "value" arguments depending on the pragma name. New pragma names may be added to the protocol from time to time |
| ︙ | ︙ | |||
761 762 763 764 765 766 767 | a successful commit. This instructs the server to release any lock on any check-in previously held by that client. The ci-unlock pragma helps to avoid false-positive lock warnings that might arise if a check-in is aborted and then restarted on a branch. </ol> | | | | | | | | | | 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 | a successful commit. This instructs the server to release any lock on any check-in previously held by that client. The ci-unlock pragma helps to avoid false-positive lock warnings that might arise if a check-in is aborted and then restarted on a branch. </ol> <h3 id="comment">3.12 Comment Cards</h3> Any card that begins with "#" (ASCII 0x23) is a comment card and is silently ignored. <h3 id="error">3.13 Message and Error Cards</h3> If the server discovers anything wrong with a request, it generates an error card in its reply. When the client sees the error card, it displays an error message to the user and aborts the sync operation. An error card looks like this: <pre> <b>error</b> <i>error-message</i> </pre> The error message is English text that is encoded in order to be a single token. A space (ASCII 0x20) is represented as "\s" (ASCII 0x5C, 0x73). A newline (ASCII 0x0a) is "\n" (ASCII 0x6C, x6E). A backslash (ASCII 0x5C) is represented as two backslashes "\\". Apart from space and newline, no other whitespace characters nor any unprintable characters are allowed in the error message. The server can also send a message card that also prints a message on the client console, but which is not an error: <pre> <b>message</b> <i>message-text</i> </pre> The message-text uses the same format as an error message. <h3 id="unknown">3.14 Unknown Cards</h3> If either the client or the server sees a card that is not described above, then it generates an error and aborts. <h2 id="phantoms" name="clusters">4.0 Phantoms And Clusters</h2> When a repository knows that an artifact exists and knows the ID of that artifact, but it does not know the artifact content, then it stores that artifact as a "phantom". A repository will typically create a phantom when it receives an igot card for an artifact that it does not hold or when it receives a file card that references a delta source that it does not hold. When a server is generating its reply or when a client is |
| ︙ | ︙ | |||
835 836 837 838 839 840 841 | Any artifact that does not match the specifications of a cluster exactly is not a cluster. There must be no extra whitespace in the artifact. There must be one or more M cards. There must be a single Z card with a correct MD5 checksum. And all cards must be in strict lexicographical order. | | | | | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | Any artifact that does not match the specifications of a cluster exactly is not a cluster. There must be no extra whitespace in the artifact. There must be one or more M cards. There must be a single Z card with a correct MD5 checksum. And all cards must be in strict lexicographical order. <h3 id="unclustered">4.1 The Unclustered Table</h3> Every repository maintains a table named "<b>unclustered</b>" which records the identity of every artifact and phantom it holds that is not mentioned in a cluster. The entries in the unclustered table can be thought of as leaves on a tree of artifacts. Some of the unclustered artifacts will be other clusters. Those clusters may contain other clusters, which might contain still more clusters, and so forth. Beginning with the artifacts in the unclustered table, one can follow the chain of clusters to find every artifact in the repository. <h2 id="strategies">5.0 Synchronization Strategies</h2> <h3 id="pull-strategy">5.1 Pull</h3> A typical pull operation proceeds as shown below. Details of the actual implementation may very slightly but the gist of a pull is captured in the following steps: <ol> <li>The client sends login and pull cards. |
| ︙ | ︙ | |||
900 901 902 903 904 905 906 | amount of overlap between clusters in the common configuration where there is a single server and many clients. The same synchronization protocol will continue to work even if there are multiple servers or if servers and clients sometimes change roles. The only negative effects of these unusual arrangements is that more than the minimum number of clusters might be generated. | | | 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 | amount of overlap between clusters in the common configuration where there is a single server and many clients. The same synchronization protocol will continue to work even if there are multiple servers or if servers and clients sometimes change roles. The only negative effects of these unusual arrangements is that more than the minimum number of clusters might be generated. <h3 id="push-stragegy">5.2 Push</h3> A typical push operation proceeds roughly as shown below. As with a pull, the actual implementation may vary slightly. <ol> <li>The client sends login and push cards. <li>The client sends file cards for any artifacts that it holds that have |
| ︙ | ︙ | |||
934 935 936 937 938 939 940 | As with a pull, the steps of a push operation repeat until the server knows all artifacts that exist on the client. Also, as with pull, the client attempts to keep the size of the request from growing too large by suppressing file cards once the size of the request reaches 1MB. | | | | 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 | As with a pull, the steps of a push operation repeat until the server knows all artifacts that exist on the client. Also, as with pull, the client attempts to keep the size of the request from growing too large by suppressing file cards once the size of the request reaches 1MB. <h3 id="sync-strategy">5.3 Sync</h3> A sync is just a pull and a push that happen at the same time. The first three steps of a pull are combined with the first five steps of a push. Steps (4) through (7) of a pull are combined with steps (5) through (8) of a push. And steps (8) through (10) of a pull are combined with step (9) of a push. <h3 id="uv-strategy">5.4 Unversioned File Sync</h3> "Unversioned files" are files held in the repository where only the most recent version of the file is kept rather than the entire change history. Unversioned files are intended to be used to store ephemeral content, such as compiled binaries of the most recent release. |
| ︙ | ︙ | |||
984 985 986 987 988 989 990 |
cards and answers "uvgimme" cards with "uvfile" cards in its reply.
</ol>
The last two steps might be repeated multiple
times if there is more unversioned content to be transferred than will
fit comfortably in a single HTTP request.
| | | 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 |
cards and answers "uvgimme" cards with "uvfile" cards in its reply.
</ol>
The last two steps might be repeated multiple
times if there is more unversioned content to be transferred than will
fit comfortably in a single HTTP request.
<h2 id="summary">6.0 Summary</h2>
Here are the key points of the synchronization protocol:
<ol>
<li>The client sends one or more PUSH HTTP requests to the server.
The request and reply content type is "application/x-fossil".
<li>HTTP request content is compressed using zlib.
|
| ︙ | ︙ | |||
1028 1029 1030 1031 1032 1033 1034 | <li>Clusters are created automatically on the server during a pull. <li>Repositories keep track of all artifacts that are not named in any cluster and send igot messages for those artifacts. <li>Repositories keep track of all the phantoms they hold and send gimme messages for those artifacts. </ol> | | < | 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 | <li>Clusters are created automatically on the server during a pull. <li>Repositories keep track of all artifacts that are not named in any cluster and send igot messages for those artifacts. <li>Repositories keep track of all the phantoms they hold and send gimme messages for those artifacts. </ol> <h2 id="troubleshooting">7.0 Troubleshooting And Debugging Hints</h2> If you run the [/help?cmd=sync|fossil sync] command (or [/help?cmd=pull|pull] or [/help?cmd=push|push] or [/help?cmd=clone|clone]) with the --httptrace option, Fossil will keep a copy of each HTTP request and reply in files named: <ul> |
| ︙ | ︙ |
Changes to www/tech_overview.wiki.
|
| | < < < | 1 2 3 4 5 6 7 8 | <title>A Technical Overview of Fossil's Design & Implementation</title> <h2>1.0 Introduction</h2> At its lowest level, a Fossil repository consists of an unordered set of immutable "artifacts". You might think of these artifacts as "files", since in many cases the artifacts are exactly that. But other "structural artifacts" are also included in the mix. |
| ︙ | ︙ | |||
51 52 53 54 55 56 57 | file that people are normally referring to when they say "a Fossil repository". The checkout database is found in the working checkout for a project and contains state information that is unique to that working checkout. Fossil does not always use all three database files. The web interface, for example, typically only uses the repository database. And the | | | < | | | > > > | | < < < | | < < < | | < | 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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
file that people are normally referring to when they say
"a Fossil repository". The checkout database is found in the working
checkout for a project and contains state information that is unique
to that working checkout.
Fossil does not always use all three database files. The web interface,
for example, typically only uses the repository database. And the
[/help/settings | fossil settings] command only opens the configuration database
when the --global option is used. But other commands use all three
databases at once. For example, the [/help/status | fossil status]
command will first locate the checkout database, then use the checkout
database to find the repository database, then open the configuration
database. Whenever multiple databases are used at the same time,
they are all opened on the same SQLite database connection using
SQLite's [http://www.sqlite.org/lang_attach.html | ATTACH] command.
The chart below provides a quick summary of how each of these
database files are used by Fossil, with detailed discussion following.
<table align="center">
<tr valign="bottom">
<th style="text-align:center">Configuration Database<br>"~/.fossil" or<br>
"~/.config/fossil.db"
<th style="text-align:center">Repository Database<br>"<i>project</i>.fossil"
<th style="text-align:center">Checkout Database<br>"_FOSSIL_" or ".fslckout"
<tr valign="top">
<td><ul>
<li>Global [/help/settings |settings]
<li>List of active repositories used by the [/help/all | all] command
</ul></td>
<td><ul>
<li>[./fileformat.wiki | Global state of the project]
encoded using delta-compression
<li>Local [/help/settings|settings]
<li>Web interface display preferences
<li>User credentials and permissions
<li>Metadata about the global state to facilitate rapid
queries
</ul></td>
<td><ul>
<li>The repository database used by this checkout
<li>The version currently checked out
<li>Other versions [/help/merge | merged] in but not
yet [/help/commit | committed]
<li>Changes from the [/help/add | add], [/help/delete | delete],
and [/help/rename | rename] commands that have not yet been committed
<li>"mtime" values and other information used to efficiently detect
local edits
<li>The "[/help/stash | stash]"
<li>Information needed to "[/help/undo|undo]" or "[/help/redo|redo]"
</ul></td>
</tr>
</table>
<h3 id="configdb">2.1 The Configuration Database</h3>
The configuration database holds cross-repository preferences and a list of all
repositories for a single user.
|
| ︙ | ︙ | |||
127 128 129 130 131 132 133 | operations such as "sync" or "rebuild" on all repositories managed by a user. <h4 id="configloc">2.1.1 Location Of The Configuration Database</h4> On Unix systems, the configuration database is named by the following algorithm: | | | | > | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
operations such as "sync" or "rebuild" on all repositories managed by a user.
<h4 id="configloc">2.1.1 Location Of The Configuration Database</h4>
On Unix systems, the configuration database is named by the following
algorithm:
<table>
<tr><td>1. if environment variable FOSSIL_HOME exists
<td> → <td>$FOSSIL_HOME/.fossil
<tr><td>2. if file ~/.fossil exists
<td> →<td>~/.fossil
<tr><td>3. if environment variable XDG_CONFIG_HOME exists
<td> →<td>$XDG_CONFIG_HOME/fossil.db
<tr><td>4. if the directory ~/.config exists
<td> →<td>~/.config/fossil.db
<tr><td>5. Otherwise<td> →<td>~/.fossil
</table>
Another way of thinking of this algorithm is the following:
* Use "$FOSSIL_HOME/.fossil" if the FOSSIL_HOME variable is defined
* Use the XDG-compatible name (usually ~/.config/fossil.db) on XDG systems
if the ~/.fossil file does not already exist
* Otherwise, use the traditional unix name of "~/.fossil"
|
| ︙ | ︙ | |||
161 162 163 164 165 166 167 | * %FOSSIL_HOME%/_fossil * %LOCALAPPDATA%/_fossil * %APPDATA%/_fossil * %USERPROFILES%/_fossil * %HOMEDRIVE%%HOMEPATH%/_fossil | | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | * %FOSSIL_HOME%/_fossil * %LOCALAPPDATA%/_fossil * %APPDATA%/_fossil * %USERPROFILES%/_fossil * %HOMEDRIVE%%HOMEPATH%/_fossil The second case is the one that usually determines the name. Note that the FOSSIL_HOME environment variable can always be set to determine the location of the configuration database. Note also that the configuration database file itself is called ".fossil" or "fossil.db" on unix but "_fossil" on windows. The [/help?cmd=info|fossil info] command will show the location of the configuration database on a line that starts with "config-db:". |
| ︙ | ︙ |
Changes to www/th1.md.
| ︙ | ︙ | |||
52 53 54 55 56 57 58 | that a TH1 script is really just a list of text commands, not a context-free language with a grammar like C/C++. This can be confusing to long-time C/C++ programmers because TH1 does look a lot like C/C++, but the semantics of TH1 are closer to FORTH or Lisp than they are to C. Consider the `if` command in TH1. | | | | | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
that a TH1 script is really just a list of text commands, not a context-free
language with a grammar like C/C++. This can be confusing to long-time
C/C++ programmers because TH1 does look a lot like C/C++, but the semantics
of TH1 are closer to FORTH or Lisp than they are to C.
Consider the `if` command in TH1.
if {$current eq "dev"} {
puts "hello"
} else {
puts "world"
}
The example above is a single command. The first token, and the name
of the command, is `if`.
The second token is `$current eq "dev"` - an expression. (The outer {...}
are removed from each token by the command parser.) The third token
is the `puts "hello"`, with its whitespace and newlines. The fourth token
is `else` and the fifth and last token is `puts "world"`.
|
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
All of this also explains the emphasis on *unescaped* characters above:
the curly braces `{ }` are string quoting characters in Tcl/TH1, not
block delimiters as in C. This is how we can have a command that extends
over multiple lines. It is also why the `else` keyword must be cuddled
up with the closing brace for the `if` clause's scriptlet. The following
is invalid Tcl/TH1:
| | | | | | | | | | | 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 |
All of this also explains the emphasis on *unescaped* characters above:
the curly braces `{ }` are string quoting characters in Tcl/TH1, not
block delimiters as in C. This is how we can have a command that extends
over multiple lines. It is also why the `else` keyword must be cuddled
up with the closing brace for the `if` clause's scriptlet. The following
is invalid Tcl/TH1:
if {$current eq "dev"} {
puts "hello"
}
else {
puts "world"
}
If you try to run this under either Tcl or TH1, the interpreter will
tell you that there is no `else` command, because with the newline on
the third line, you terminated the `if` command.
Occasionally in Tcl/TH1 scripts, you may need to use a backslash at the
end of a line to allow a command to extend over multiple lines without
being considered two separate commands. Here's an example from one of
Fossil's test scripts:
return [lindex [regexp -line -inline -nocase -- \
{^uuid:\s+([0-9A-F]{40}) } [eval [getFossilCommand \
$repository "" info trunk]]] end]
Those backslashes allow the command to wrap nicely within a standard
terminal width while telling the interpreter to consider those three
lines as a single command.
Summary of Core TH1 Commands
|
| ︙ | ︙ | |||
279 280 281 282 283 284 285 | document. <a id="capexpr"></a>TH1 capexpr Command ----------------------------------------------------- | < < | | | | | | | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
document.
<a id="capexpr"></a>TH1 capexpr Command
-----------------------------------------------------
* capexpr CAPABILITY-EXPR
The capability expression is a list. Each term of the list is a
cluster of [capability letters](./caps/ref.html).
The overall expression is true if any
one term is true. A single term is true if all letters within that
term are true. Or, if the term begins with "!", then the term is true
if none of the terms or true. Or, if the term begins with "@" then
the term is true if all of the capability letters in that term are
available to the "anonymous" user. Or, if the term is "*" then it is
always true.
Examples:
```
capexpr {j o r} True if any one of j, o, or r are available
capexpr {oh} True if both o and h are available
capexpr {@2 @3 4 5 6} 2 or 3 available for anonymous or one of
4, 5 or 6 is available for the user
capexpr L True if the user is logged in
capexpr !L True if the user is not logged in
```
The `L` pseudo-capability is intended only to be used on its own or with
the `!` prefix for implementing login/logout menus via the `mainmenu`
site configuration option:
```
|
| ︙ | ︙ | |||
681 682 683 684 685 686 687 | 1. **w** -- _Wiki_ To be clear, only one of the document classes identified by each STRING needs to be searchable in order for that argument to be true. But all arguments must be true for this routine to return true. Hence, to see if ALL document classes are searchable: | | | | 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 |
1. **w** -- _Wiki_
To be clear, only one of the document classes identified by each STRING
needs to be searchable in order for that argument to be true. But all
arguments must be true for this routine to return true. Hence, to see
if ALL document classes are searchable:
if {[searchable c d t w]} {...}
But to see if ANY document class is searchable:
if {[searchable cdtw]} {...}
This command is useful for enabling or disabling a "Search" entry on the
menu bar.
<a id="setParameter"></a>TH1 setParameter Command
---------------------------------------------------
|
| ︙ | ︙ |
Changes to www/theory1.wiki.
1 | <title>Thoughts On The Design Of The Fossil DVCS</title> | < | 1 2 3 4 5 6 7 8 | <title>Thoughts On The Design Of The Fossil DVCS</title> Two questions (or criticisms) that arise frequently regarding Fossil can be summarized as follows: 1. Why is Fossil based on SQLite instead of a distributed NoSQL database? 2. Why is Fossil written in C instead of a modern high-level language? |
| ︙ | ︙ |
Changes to www/tickets.wiki.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 | <h3>2.1 Ticket Table Schema</h3> The two ticket tables are called TICKET and TICKETCHNG. The default schema (as of this writing) for these two tables is shown below: | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <h3>2.1 Ticket Table Schema</h3> The two ticket tables are called TICKET and TICKETCHNG. The default schema (as of this writing) for these two tables is shown below: <verbatim> CREATE TABLE ticket( -- Do not change any column that begins with tkt_ tkt_id INTEGER PRIMARY KEY, tkt_uuid TEXT UNIQUE, tkt_mtime DATE, tkt_ctime DATE, -- Add as many fields as required below this line |
| ︙ | ︙ | |||
76 77 78 79 80 81 82 | -- Add as many fields as required below this line login TEXT, username TEXT, mimetype TEXT, icomment TEXT ); CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime); | | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | -- Add as many fields as required below this line login TEXT, username TEXT, mimetype TEXT, icomment TEXT ); CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime); </verbatim> Generally speaking, there is one row in the TICKETCHNG table for each change to each ticket. In other words, there is one row in the TICKETCHNG table for each low-level ticket change artifact. The TICKET table, on the other hand, contains a summary of the current status of each ticket. |
| ︙ | ︙ |
Changes to www/unvers.wiki.
1 | <title>Unversioned Content</title> | < > | | | | | | > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <title>Unversioned Content</title> "Unversioned content" or "unversioned files" are files stored in a Fossil repository without history, meaning it retains the newest version of each such file, and that alone. Unversioned files may have an optional directory name prefix. Though it omits history, Fossil does sync unversioned content between repositories. In the event of a conflict during a sync, it retains the most recent version of each unversioned file, discarding older versions. Unversioned files are useful for storing ephemeral content such as builds or frequently changing web pages. We store the [https://fossil-scm.org/home/uv/download.html|download] page of the self-hosting Fossil repository as unversioned content, for example. <h2>Accessing Unversioned Files</h2> Unversioned files are <u>not</u> a part of a check-out. Unversioned files are intended to be accessible as web pages using URLs of the form: "<tt>https://example.com/cgi-script/<b>uv</b>/<i>FILENAME</i></tt>". In other words, the URI method "<b>uv</b>" (short for "unversioned") followed by the name of the unversioned file will retrieve the content of the file. The MIME type is inferred from the filename suffix. The content of unversioned files can also be retrieved using the [/help?cmd=unversioned|fossil unvers cat <i>FILENAME...</i>] or [/help?cmd=unversioned|fossil unvers export <i>FILENAME</i>] commands. A list of all unversioned files on a server can be seen using the [/help?cmd=/uvlist|/uvlist] URL. ([/uvlist|example].) <h2>Syncing Unversioned Files</h2> Unversioned content does not sync between repositories by default. One must request it via commands such as: <pre> fossil sync <b>-u</b> fossil clone <b>-u</b> <i>URL local-repo-name</i> fossil unversioned sync </pre> The [/help?cmd=sync|fossil sync] and [/help?cmd=clone|fossil clone] commands will synchronize unversioned content if and only if they're given the "-u" (or "--unversioned") command-line option. The [/help?cmd=unversioned|fossil unversioned sync] command synchronizes the unversioned content without synchronizing anything else. |
| ︙ | ︙ | |||
70 71 72 73 74 75 76 | <i>(This section outlines the current implementation of unversioned files. This is not an interface spec and hence subject to change.)</i> Unversioned content is stored in the repository in the "unversioned" table: | | | | | | 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 | <i>(This section outlines the current implementation of unversioned files. This is not an interface spec and hence subject to change.)</i> Unversioned content is stored in the repository in the "unversioned" table: <pre> CREATE TABLE unversioned( uvid INTEGER PRIMARY KEY AUTOINCREMENT, -- unique ID for this file name TEXT UNIQUE, -- Name of the file rcvid INTEGER, -- From whence this file was received mtime DATETIME, -- Last change (seconds since 1970) hash TEXT, -- SHA1 or SHA3-256 hash of uncompressed content sz INTEGER, -- Size of uncompressed content encoding INT, -- 0: plaintext 1: zlib compressed content BLOB -- File content ); </pre> Fossil does not create the table ahead of need. If there are no unversioned files in the repository, the "unversioned" table will not exist. Consequently, one simple way to purge all unversioned content from a repository is to run: <pre> fossil sql "DROP TABLE unversioned; VACUUM;" </pre> Lacking history for unversioned files, Fossil does not attempt delta compression on them. Fossil servers exchange unversioned content whole; it does not attempt to "diff" your local version against the remote and send only the changes. We point this out because one use-case for unversioned content |
| ︙ | ︙ |
Changes to www/webui.wiki.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 | As an example of how useful this web interface can be, the entire [./index.wiki | Fossil website], including the document you are now reading, is rendered using the Fossil web interface, with no enhancements, and little customization. | | | | | | 30 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 | As an example of how useful this web interface can be, the entire [./index.wiki | Fossil website], including the document you are now reading, is rendered using the Fossil web interface, with no enhancements, and little customization. <div class="indent"> <b>Key point:</b> <i>The Fossil website is just a running instance of Fossil! </div> Note also that because Fossil is a distributed system, you can run the web interface on your local machine while off network (for example, while on an airplane) including making changes to wiki pages and/or trouble ticket, then synchronize with your co-workers after you reconnect. When you clone a Fossil repository, you don't just get the project source code, you get the entire project management website. <h2>Very Simple Startup</h2> To start using the built-in Fossil web interface on an existing Fossil repository, simply type this: <pre>fossil ui existing-repository.fossil</pre> Substitute the name of your repository, of course. The "ui" command will start a web server running (it figures out an available TCP port to use on its own) and then automatically launches your web browser to point at that server. If you run the "ui" command from within an open check-out, you can omit the repository name: <pre>fossil ui</pre> The latter case is a very useful short-cut when you are working on a Fossil project and you want to quickly do some work with the web interface. Notice that Fossil automatically finds an unused TCP port to run the server on and automatically points your web browser to the correct URL. So there is never any fumbling around trying to find an open port or to type arcane strings into your browser URL entry box. |
| ︙ | ︙ | |||
151 152 153 154 155 156 157 | available to a distributed team by simply copying the single repository file up to a web server that supports CGI or SCGI. To run Fossil as CGI, just put the <b>sample-project.fossil</b> file in a directory where CGI scripts have both read and write permission on the file and the directory that contains the file, then add a CGI script that looks something like this: | < | | < < | | < | 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 | available to a distributed team by simply copying the single repository file up to a web server that supports CGI or SCGI. To run Fossil as CGI, just put the <b>sample-project.fossil</b> file in a directory where CGI scripts have both read and write permission on the file and the directory that contains the file, then add a CGI script that looks something like this: <verbatim>#!/usr/local/bin/fossil repository: /home/www/sample-project.fossil</verbatim> Adjust the script above so that the paths are correct for your system, of course, and also make sure the Fossil binary is installed on the server. But that is <u>all</u> you have to do. You now have everything you need to host a distributed software development project in less than five minutes using a two-line CGI script. Instructions for setting up an SCGI server are [./scgi.wiki | available separately]. You don't have a CGI- or SCGI-capable web server running on your server machine? Not a problem. The Fossil interface can also be launched via inetd or xinetd. An inetd configuration line sufficient to launch the Fossil web interface looks like this: <verbatim>80 stream tcp nowait.1000 root /usr/local/bin/fossil \ /usr/local/bin/fossil http /home/www/sample-project.fossil</verbatim> As always, you'll want to adjust the pathnames to whatever is appropriate for your system. The xinetd setup uses a different syntax but follows the same idea. |
Changes to www/whyusefossil.wiki.
|
| | | | | < | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<title>Why You Should Use Fossil</title>
<h4>(Or if not Fossil, at least some kind of modern
version control such as Git, Mercurial, or Subversion.)</h4>
<h5>I. Benefits of Version Control</h5>
<ol type='A'>
<li><p><b>Immutable file and version identification</b>
<ol type='i'>
<li>Simplified and unambiguous communication between developers
<li>Detect accidental or surreptitious changes
<li>Locate the origin of discovered files
</ol>
|
| ︙ | ︙ | |||
35 36 37 38 39 40 41 |
<li>Everyone always has the latest code
<li>Failed disk-drives cause no loss of work
<li>Avoid wasting time doing manual file copying
<li>Avoid human errors during manual backups
</ol>
</ol>
| | | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<li>Everyone always has the latest code
<li>Failed disk-drives cause no loss of work
<li>Avoid wasting time doing manual file copying
<li>Avoid human errors during manual backups
</ol>
</ol>
<h5 id="definitions">II. Definitions</h5>
<div class="indent">Moved to [./glossary.md | a separate document].</div>
<h5>III. Basic Fossil commands</h5>
<ul>
<li><p><b>clone</b> →
Make a copy of a repository. The original repository
is usually (but not always) on a remote machine and the copy is on
the local machine. The copy remembers the network location from
which it was copied and (by default) tries to keep itself synchronized
|
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
<li><p><b>rm/mv</b> →
Short for 'remove' and 'move', these commands are like "add"
in that they specify pending changes to the structure of the check-out.
As with "add", no changes are made to the repository until the next
"commit".
</ul>
| | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
<li><p><b>rm/mv</b> →
Short for 'remove' and 'move', these commands are like "add"
in that they specify pending changes to the structure of the check-out.
As with "add", no changes are made to the repository until the next
"commit".
</ul>
<h5>IV. The history of a project is a Directed Acyclic Graph (DAG)</h5>
<ul>
<li><p>Fossil (and other distributed VCSes like Git and Mercurial, but
not Subversion) represent
the history of a project as a directed acyclic graph (DAG).
<ul>
<li><p>Each check-in is a node in the graph
|
| ︙ | ︙ | |||
140 141 142 143 144 145 146 |
humans, so best practice is to give each branch a unique name.
<li><p>The name of a branch can be changed by adding special tags
to the first check-in of a branch. The name assigned by this
special tag automatically propagates to all direct children.
</ul>
</ul>
| | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
humans, so best practice is to give each branch a unique name.
<li><p>The name of a branch can be changed by adding special tags
to the first check-in of a branch. The name assigned by this
special tag automatically propagates to all direct children.
</ul>
</ul>
<h5>V. Why version control is important (reprise)</h5>
<ol>
<li><p>Every check-in and every individual file has a unique name - its
SHA1 or SHA3-256 hash. Team members can unambiguously identify
any specific
version of the overall project or any specific version of an
individual file.
|
| ︙ | ︙ |