Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Sync with trunk. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | diff-word-wrap |
| Files: | files | file ages | folders |
| SHA3-256: |
931e7065bbc2731d778478e3cde45c8b |
| User & Date: | florian 2025-07-21 12:20:00.000 |
Context
|
2025-08-21
| ||
| 12:07 | Sync with trunk. ... (check-in: a0377ebb9d user: florian tags: diff-word-wrap) | |
|
2025-07-21
| ||
| 12:20 | Sync with trunk. ... (check-in: 931e7065bb user: florian tags: diff-word-wrap) | |
|
2025-07-15
| ||
| 20:11 | Update the built-in SQLite to the latest trunk version, for testing. ... (check-in: 01855974c9 user: drh tags: trunk) | |
|
2024-12-17
| ||
| 06:56 | Sync with trunk. ... (check-in: 5fbb14f73a user: florian tags: diff-word-wrap) | |
Changes
Changes to .fossil-settings/crlf-glob.
1 2 3 4 5 | compat/zlib/* setup/fossil.iss test/th1-docs-input.txt test/th1-hooks-input.txt win/buildmsvc.bat | > > | 1 2 3 4 5 6 7 | compat/zlib/* setup/fossil.iss test/th1-docs-input.txt test/th1-hooks-input.txt win/build32.bat win/build64.bat win/buildmsvc.bat |
Changes to .fossil-settings/ignore-glob.
1 2 3 4 5 6 7 8 9 | compat/openssl* compat/tcl* compat/zlib/contrib/ada/* compat/zlib/doc/* fossil fossil.exe win/fossil.exe *shell-see.* *sqlite3-see.* | > | 1 2 3 4 5 6 7 8 9 10 | compat/openssl* compat/tcl* compat/zlib/contrib/ada/* compat/zlib/doc/* fossil fossil.exe win/fossil.exe *shell-see.* *sqlite3-see.* bld |
Changes to Dockerfile.
1 2 3 4 5 6 7 | # syntax=docker/dockerfile:1.3 # See www/containers.md for documentation on how to use this file. ## --------------------------------------------------------------------- ## STAGE 1: Build static Fossil binary ## --------------------------------------------------------------------- | | | | | | | | 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 |
# syntax=docker/dockerfile:1.3
# See www/containers.md for documentation on how to use this file.
## ---------------------------------------------------------------------
## STAGE 1: Build static Fossil binary
## ---------------------------------------------------------------------
### We don't pin a more stable version of our base layer because we want
### to build with the latest tools and libraries available in case they
### fixed something that matters to us since the last build. Everything
### below depends on this layer, and so, alas, we toss this container's
### cache on Alpine's release schedule, roughly once a month.
FROM alpine:latest AS bld
WORKDIR /fsl
### Bake the build-time userland into a base layer so it only changes
### when the upstream image is updated or we change the package set.
RUN set -x \
&& apk update \
&& apk upgrade --no-cache \
&& apk add --no-cache \
gcc make \
linux-headers musl-dev \
openssl-dev openssl-libs-static \
zlib-dev zlib-static
### Build Fossil as a separate layer so we don't have to rebuild the
### userland for each iteration of Fossil's dev cycle.
###
### We must cope with a bizarre ADD misfeature here: it unpacks tarballs
### automatically when you give it a local file name but not if you give
### it a /tarball URL! It matters because we default to a URL in case
### you're building outside a Fossil checkout, but when building via the
### container-image target, we avoid a costly hit on fossil-scm.org by
### leveraging its DVCS nature via the "tarball" command and passing the
### resulting file's name in.
ARG FSLCFG=""
ARG FSLVER="trunk"
ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}"
ENV FSLSTB=/fsl/src.tar.gz
ADD $FSLURL $FSLSTB
RUN set -x \
&& if [ -d $FSLSTB ] ; \
|
| ︙ | ︙ |
Changes to Makefile.classic.
| ︙ | ︙ | |||
93 94 95 96 97 98 99 | # You should not need to change anything below this line ############################################################################### # # Automatic platform-specific options. HOST_OS_CMD = uname -s HOST_OS = $(HOST_OS_CMD:sh) | | | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | # You should not need to change anything below this line ############################################################################### # # Automatic platform-specific options. HOST_OS_CMD = uname -s HOST_OS = $(HOST_OS_CMD:sh) LIB.SunOS= -lsocket -lnsl -lrt LIB += $(LIB.$(HOST_OS)) TCC.DragonFly += -DUSE_PREAD TCC.FreeBSD += -DUSE_PREAD TCC.NetBSD += -DUSE_PREAD TCC.OpenBSD += -DUSE_PREAD TCC += $(TCC.$(HOST_OS)) |
| ︙ | ︙ |
Makefile.in became a regular file.
| ︙ | ︙ |
Changes to VERSION.
|
| | | 1 | 2.27 |
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 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 |
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.49.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
}
# Space characters have never been allowed in either the source
# tree nor the build directory. But the resulting error messages
# could be confusing. The following checks make the reason for the
# failure clear.
#
if {[string first " " $autosetup(srcdir)] != -1} {
user-error "The pathname of the source tree\
may not contain space characters"
}
if {[string first " " $autosetup(builddir)] != -1} {
user-error "The pathname of the build directory\
may not contain space characters"
}
set outOfTreeBuild 0
if {![file exists fossil.1]} {
puts "This appears to be an out-of-tree build."
set outOfTreeBuild 1
}
# sqlite wants these types if possible
cc-with {-includes {stdint.h inttypes.h}} {
cc-check-types uint32_t uint16_t int16_t uint8_t
}
# Use pread/pwrite system calls in place of seek + read/write if possible
define USE_PREAD [cc-check-functions pread]
# If we have cscope here, we'll use it in the "tags" target
if {[cc-check-progs cscope]} {
define COLLECT_CSCOPE_DATA "cscope -bR $::autosetup(srcdir)/src/*.\[ch\]"
} else {
define COLLECT_CSCOPE_DATA ""
}
# Find tclsh for the test suite.
#
# We can't use jimsh for this: the test suite uses features of Tcl that
# Jim doesn't support, either statically or due to the way it's built by
# autosetup. For example, Jim supports `file normalize`, but only if
# you build it with HAVE_REALPATH, which won't ever be defined in this
# context because autosetup doesn't try to discover platform-specific
# details like that before it decides to build jimsh0. Besides which,
# autosetup won't build jimsh0 at all if it can find tclsh itself.
# Ironically, this means we may right now be running under either jimsh0
# or a version of tclsh that we find unsuitable below!
cc-check-progs tclsh
set hbtd /usr/local/Cellar/tcl-tk
if {[string equal false [get-define TCLSH]]} {
msg-result "WARNING: 'make test' will not run here."
} else {
set v [exec sh -c "echo 'puts \$tcl_version' | tclsh"]
if {[expr {$v >= 8.6}]} {
msg-result "Found Tclsh version $v in the PATH."
define TCLSH tclsh
} elseif {[file isdirectory $hbtd]} {
# This is a macOS system with the Homebrew version of Tcl/Tk
# installed. Select the newest version. It won't normally be
# in the PATH to avoid shadowing /usr/bin/tclsh, and even if it
# were in the PATH, it's bad practice to put /usr/local/bin (the
# Homebrew default) ahead of /usr/bin, especially given that
# it's user-writeable by default with Homebrew. Thus, we can be
# pretty sure the only way to call it is with an absolute path.
set v [exec ls -tr $hbtd | tail -1]
set path "$hbtd/$v/bin/tclsh"
define TCLSH $path
msg-result "Using Homebrew Tcl/Tk version $path."
} else {
msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'."
define TCLSH false ;# force "make test" failure via /usr/bin/false
}
}
define CFLAGS [get-env CFLAGS "-g -Os"]
define EXTRA_CFLAGS "-Wall"
define EXTRA_LDFLAGS ""
define USE_SYSTEM_SQLITE 0
define USE_LINENOISE 0
define USE_MMAN_H 0
define USE_SEE 0
define SQLITE3_ORIGIN 0
# SQLITE3_ORIGIN 0 = src/sqlite3, 1=src/sqlite3-see.c, 2=client-provided
define SQLITE_OPTIONS_EXT ""
# SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c
# Maintain the C89/C90-style order of variable declarations before statements.
# Check if the compiler supports the respective warning flag.
if {[cctest -cflags -Wdeclaration-after-statement]} {
define-append EXTRA_CFLAGS -Wdeclaration-after-statement
}
# This procedure is a customized version of "cc-check-function-in-lib",
# that does not modify the LIBS variable. Its use prevents prematurely
# pulling in libraries that will be added later anyhow (e.g. "-ldl").
proc check-function-in-lib {function libs {otherlibs {}}} {
if {[string length $otherlibs]} {
msg-checking "Checking for $function in $libs with $otherlibs..."
} else {
msg-checking "Checking for $function in $libs..."
}
set found 0
cc-with [list -libs $otherlibs] {
if {[cctest_function $function]} {
msg-result "none needed"
define lib_$function ""
incr found
} else {
foreach lib $libs {
cc-with [list -libs -l$lib] {
if {[cctest_function $function]} {
msg-result -l$lib
define lib_$function -l$lib
incr found
break
}
}
}
}
}
if {$found} {
define [feature-define-name $function]
} else {
msg-result "no"
}
return $found
}
if {![opt-bool internal-sqlite]} {
proc find_system_sqlite {} {
# On some systems (slackware), libsqlite3 requires -ldl to link. So
# search for the system SQLite once with -ldl, and once without. If
|
| ︙ | ︙ | |||
203 204 205 206 207 208 209 |
lappend cmdline {*}[get-define LDFLAGS]
lappend cmdline {*}[get-define LIBS]
set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]]
lappend cmdline {*}[set sqlite-version]
set ok 1
set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
if {$err} {
| | | | | | | > | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | > > > > > | > > | > | | | | > > > > > > > > > | > > | | | < < | | | | | | | | | | | | | | | | | | | | | | > | > > > > > > > > > < < | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | > > > > | | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | < < < < | < < < < < < < < < < < < < < < < < < < < < | | | | > > > | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | < | > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > | > > > > > | > > > > | < > > | > > | | > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
lappend cmdline {*}[get-define LDFLAGS]
lappend cmdline {*}[get-define LIBS]
set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]]
lappend cmdline {*}[set sqlite-version]
set ok 1
set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
if {$err} {
configlog "Failed: [join $cmdline]"
if {[string length $result]>0} {configlog $result}
configlog "============"
set ok 0
} elseif {$::autosetup(debug)} {
configlog "Compiled OK: [join $cmdline]"
configlog "============"
}
if {!$ok} {
user-error "unable to compile SQLite compatibility test program"
}
set err [catch {exec-with-stderr ./conftest__} result errinfo]
if {[get-define build] eq [get-define host]} {
set err [catch {exec-with-stderr ./conftest__} result errinfo]
if {$err} {
user-error $result
}
}
file delete ./conftest__
}
test_system_sqlite
}
proc is_mingw {} {
return [expr {
[string match *mingw* [get-define host]] &&
![file exists "/dev/null"]
}]
}
if {[is_mingw]} {
define-append EXTRA_CFLAGS -DBROKEN_MINGW_CMDLINE
define-append LIBS -lkernel32 -lws2_32
} else {
#
# NOTE: All platforms except MinGW should use the linenoise
# package. It is currently unsupported on Win32.
#
define USE_LINENOISE 1
}
if {[string match *-solaris* [get-define host]]} {
define-append EXTRA_CFLAGS {-D__EXTENSIONS__}
}
if {[opt-bool fossil-debug]} {
define CFLAGS {-g -O0 -Wall}
define-append CFLAGS -DFOSSIL_DEBUG
msg-result "Debugging support enabled"
}
if {[opt-bool no-opt]} {
define CFLAGS {-g -O0 -Wall}
msg-result "Builting without compiler optimization"
if {[opt-bool fossil-debug]} {
define-append CFLAGS -DFOSSIL_DEBUG
}
}
if {[opt-bool with-mman]} {
define-append EXTRA_CFLAGS -DUSE_MMAN_H
define USE_MMAN_H 1
msg-result "Enabling \"sys/mman.h\" support"
}
if {[opt-bool with-see]} {
define-append EXTRA_CFLAGS -DUSE_SEE
define USE_SEE 1
define SQLITE3_ORIGIN 1
msg-result "Enabling encryption support"
}
if {[opt-bool json]} {
# Reminder/FIXME (stephan): FOSSIL_ENABLE_JSON
# is required in the CFLAGS because json*.c
# have #ifdef guards around the whole file without
# reading config.h first.
define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON
define FOSSIL_ENABLE_JSON
msg-result "JSON support enabled"
}
if {[opt-bool with-exec-rel-paths]} {
define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_EXEC_REL_PATHS
define FOSSIL_ENABLE_EXEC_REL_PATHS
msg-result "Relative paths in external diff/gdiff enabled"
}
if {[opt-bool with-th1-docs]} {
define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_DOCS
define FOSSIL_ENABLE_TH1_DOCS
msg-result "TH1 embedded documentation support enabled"
}
if {[opt-bool with-th1-hooks]} {
define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_HOOKS
define FOSSIL_ENABLE_TH1_HOOKS
msg-result "TH1 hooks support enabled"
}
#if {[opt-bool markdown]} {
# # no-op. Markdown is now enabled by default.
# msg-result "Markdown support enabled"
#}
if {[opt-bool static]} {
# XXX: This will not work on all systems.
define-append EXTRA_LDFLAGS -static
msg-result "Trying to link statically"
} else {
define-append EXTRA_CFLAGS -DFOSSIL_DYNAMIC_BUILD=1
define FOSSIL_DYNAMIC_BUILD
}
# Check for libraries that need to be sorted out early
cc-check-function-in-lib iconv iconv
cc-check-function-in-lib sin m
cc-check-function-in-lib dlopen dl
# Helper for OpenSSL checking
proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} {
msg-checking "Checking for $msg..."
set rc 0
if {[is_mingw]} {
lappend libs -lgdi32 -lwsock32 -lcrypt32
}
if {[info exists ::zlib_lib]} {
lappend libs $::zlib_lib
}
msg-quiet cc-with [list -cflags $cflags -libs $libs] {
if {[cc-check-includes openssl/ssl.h] && \
[cc-check-functions SSL_new]} {
incr rc
}
}
if {!$rc && ![is_mingw]} {
# On some systems, OpenSSL appears to require -ldl to link.
lappend libs -ldl
msg-quiet cc-with [list -cflags $cflags -libs $libs] {
if {[cc-check-includes openssl/ssl.h] && \
[cc-check-functions SSL_new]} {
incr rc
}
}
}
if {$rc} {
msg-result "ok"
return 1
} else {
msg-result "no"
return 0
}
}
#
# Check for zlib, using the given location if specified
#
proc handle-zlib {} {
set ::zlibpath [opt-val with-zlib]; # used by downstream tcl tests
if {$::zlibpath eq "tree"} {
set ::zlibdir [file dirname $::autosetup(dir)]/compat/zlib
if {![file isdirectory $::zlibdir]} {
user-error "The zlib in source tree directory does not exist"
} elseif { ([llength [glob -nocomplain -directory $::zlibdir libz*]] == 0) } {
user-error "With --with-zlib=tree, $::zlibdir must be configured and built first."
}
cc-with [list -cflags "-I$::zlibdir -L$::zlibdir"]
define-append EXTRA_CFLAGS -I$::zlibdir
define-append LIBS $::zlibdir/libz.a
set ::zlib_lib $::zlibdir/libz.a
msg-result "Using zlib in source tree"
} else {
set cftry {""}
set ldtry {""}
if {$::zlibpath ni {auto ""}} {
lappend cftry "-I$::zlibpath"
lappend cftry "-I$::zlibpath/include"
lappend ldtry "-L$::zlibpath"
lappend ldtry "-L$::zlibpath/lib"
}
# Reverse the list of tests so we check most-specific to least, else
# platform devel files will shadow local --with-zlib overrides.
foreach c [lreverse $cftry] {
if {[cc-with [list -cflags $c] {cc-check-includes zlib.h}]} {
if {$c eq ""} {
msg-result "Found zlib.h in default include path"
} else {
define-append EXTRA_CFLAGS "$c"
msg-result "Found zlib.h via $c"
}
set cfound $c
break
}
}
if {![info exists cfound]} {
user-error "zlib.h not found; either install it or specify its location via --with-zlib"
}
foreach lcheck [lreverse $ldtry] {
if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} {
if {$lcheck eq ""} {
msg-result "Linked to zlib via default library path"
} else {
define-append EXTRA_LDFLAGS "$lcheck"
msg-result "Linked to zlib via $lcheck"
}
if {![check-function-in-lib compressBound z]} {
puts "Notice: disabling zlib compression in the SQL shell"
define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB}
}
break
}
}
set ::zlib_lib -lz
}
}; # handle-zlib
handle-zlib
#
# Handle the --with-openssl flag and, incidentally, update @LIBS@ for
# zlib if openssl is _not_ used (if it is, we get zlib via libssl).
#
# This function should be called as late as possible in the configure
# script to avoid that its updates to @LIBS@ break tests which follow
# it when a custom local build of openssl is used, as discussed in
# <https://fossil-scm.org/forum/forumpost/15e3d9cdc137030c>.
#
proc handle-with-openssl {} {
set ssldirs [opt-val with-openssl]
if {$ssldirs ne "none"} {
set found 0
if {$ssldirs eq "tree"} {
set ssldir [file dirname $::autosetup(dir)]/compat/openssl
if {![file isdirectory $ssldir]} {
user-error "The OpenSSL in source tree directory does not exist"
}
set msg "openssl in $ssldir"
set cflags "-I$ssldir/include"
set ldflags "-L$ssldir"
set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread"
set found [check-for-openssl "openssl in source tree" "$cflags $ldflags" $ssllibs]
} else {
if {$ssldirs in {auto ""}} {
catch {
# TODO?: use autosetup's pkg-config support
set cflags [exec pkg-config openssl --cflags-only-I]
set ldflags [exec pkg-config openssl --libs-only-L]
set found [check-for-openssl "ssl via pkg-config" "$cflags $ldflags"]
} msg
if {!$found} {
set ssldirs "{} /usr/sfw /usr/local/ssl /usr/lib/ssl /usr/ssl \
/usr/pkg /usr/local /usr /usr/local/opt/openssl \
/opt/homebrew/opt/openssl"
}
}
if {!$found} {
foreach dir $ssldirs {
if {$dir eq ""} {
set msg "system openssl"
set cflags ""
set ldflags ""
} else {
set msg "openssl in $dir"
set cflags "-I$dir/include"
if {[file readable $dir/libssl.a]} {
set ldflags -L$dir
} elseif {[file readable $dir/lib/libssl.a]} {
set ldflags -L$dir/lib
} elseif {[file isdir $dir/lib]} {
set ldflags "-L$dir -L$dir/lib"
} else {
set ldflags -L$dir
}
}
if {[check-for-openssl $msg "$cflags $ldflags"]} {
incr found
break
}
if {$dir ne ""} {
set ldflags ""
set msg "static build of openssl in $dir"
set ssllibs "$dir/libssl.a $dir/libcrypto.a -lpthread"
if {[check-for-openssl $msg "$cflags $ldflags" $ssllibs]} {
incr found
break
}
# This test should arguably fail here if --with-openssl=X
# points to an invalid X.
}
}
}
}
if {$found} {
define FOSSIL_ENABLE_SSL
define-append EXTRA_CFLAGS $cflags
define-append EXTRA_LDFLAGS $ldflags
if {[info exists ssllibs]} {
define-append LIBS $ssllibs
} else {
define-append LIBS -lssl -lcrypto
}
if {[info exists ::zlib_lib]} {
define-append LIBS $::zlib_lib
}
if {[is_mingw]} {
define-append LIBS -lgdi32 -lwsock32 -lcrypt32
}
msg-result "HTTPS support enabled"
# Silence OpenSSL deprecation warnings on Mac OS X 10.7.
if {[string match *-darwin* [get-define host]]} {
if {[cctest -cflags {-Wdeprecated-declarations}]} {
define-append EXTRA_CFLAGS -Wdeprecated-declarations
}
}
} else {
user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support"
}
} else {
if {[info exists ::zlib_lib]} {
define-append LIBS $::zlib_lib
}
}
}; # handle-with-openssl
#
# CFLAGS_INCLUDE is ONLY for -I... flags and their order is
# significant so that --with-sqlite=PATH's header can shadow our
# own. <s>One caveat with this is that we cannot point
# --with-sqlite=PATH to the root of sqlite3's own build tree because
# that dir has a config.h which ends up shadowing src/config.h,
# breaking our build.</s> (That is no longer true: that that config.h
# was renamed to sqlite_cfg.h at some point.)
#
define CFLAGS_INCLUDE {}
########################################################################
# --with-sqlite=PATH checks for the first it finds of the following...
# - PATH/sqlite3.c and PATH/sqlite3.h
# - PATH/sqlite3.o (and assumes sqlite3.h is with it)
# - PATH/lib/libsqlite3* and PATH/include/sqlite3.h
proc handle-with-sqlite {} {
set sq3path [opt-val with-sqlite]
define SQLITE3_SRC.2 {}
define SQLITE3_OBJ.2 {}
define SQLITE3_SHELL_SRC.2 {$(SQLITE3_SHELL_SRC.0)}
if {$sq3path in {tree ""}} {
msg-result "Using sqlite3.c from this source tree."
} else {
# SQLITE3_ORIGIN:
# 0 = local source tree
# 1 = use external lib or sqlite3.o
# 2 = use external sqlite3.c and (if found) shell.c
define USE_SYSTEM_SQLITE 1
define SQLITE3_ORIGIN 2
if {$sq3path != "auto"} {
if {([file exists $sq3path/sqlite3.c]) &&
([file exists $sq3path/sqlite3.h]) } {
# Prefer sqlite3.[ch] if found.
define SQLITE3_SRC.2 $sq3path/sqlite3.c
define SQLITE3_OBJ.2 {$(SQLITE3_OBJ.0)}
define USE_SYSTEM_SQLITE 2
define SQLITE3_ORIGIN 2
if {[file exists $sq3path/shell.c]} {
define SQLITE3_SHELL_SRC.2 $sq3path/shell.c
}
define-append CFLAGS_INCLUDE -I$sq3path
define-append EXTRA_LDFLAGS -lpthread
# ^^^ additional -lXXX flags are conservative estimates
msg-result "Using sqlite3.c and sqlite3.h from $sq3path"
} elseif {[file exists $sq3path/sqlite3.o]} {
# Use sqlite3.o if found.
define SQLITE3_OBJ.2 $sq3path/sqlite3.o
define-append CFLAGS_INCLUDE -I$sq3path
define-append EXTRA_LDFLAGS $sq3path/sqlite3.o -lpthread
# ^^^ additional -lXXX flags are conservative estimates
msg-result "Using sqlite3.o from $sq3path"
} elseif { ([llength [glob -nocomplain -directory $sq3path/lib libsqlite3*]] != 0) \
&& ([file exists $sq3path/include/sqlite3.h]) } {
# e.g. --with-sqlite=/usr/local. Try $sq3path/lib/libsqlite3*
# and $sq3path/include/sqlite3.h
define-append CFLAGS_INCLUDE -I$sq3path/include
define-append EXTRA_LDFLAGS -L$sq3path/lib -lsqlite3 -lpthread
# ^^^ additional -lXXX flags are conservative estimates
msg-result "Using -lsqlite3 from $sq3path"
} else {
# Assume $sq3path holds both the lib and header
cc-with [list -cflags "-I$sq3path -L$sq3path"]
define-append CFLAGS_INCLUDE -I$sq3path
define-append EXTRA_LDFLAGS -L$sq3path -lsqlite3 -lpthread
# ^^^ additional -lXXX flags are conservative estimates
msg-result "Using -lsqlite3 from $sq3path"
}
} elseif {![cc-check-includes sqlite3.h] || ![check-function-in-lib sqlite3_open_v2 sqlite3]} {
user-error "libsqlite3 not found please install it or specify the location with --with-sqlite"
}
}
}; # handle-with-sqlite
handle-with-sqlite
define-append CFLAGS_INCLUDE {-I. -I$(SRCDIR) -I$(SRCDIR_extsrc)}; # must be after handle-with-sqlite
#
# Handle the --with-tcl flag.
#
proc handle-with-tcl {} {
set tclpath [opt-val with-tcl]
if {$tclpath eq ""} {
return
}
set tclprivatestubs [opt-bool with-tcl-private-stubs]
# Note parse-tclconfig-sh is in autosetup/local.tcl
if {$tclpath eq "1"} {
set tcldir [file dirname $::autosetup(dir)]/compat/tcl-8.6
if {$tclprivatestubs} {
set tclconfig(TCL_INCLUDE_SPEC) -I$tcldir/generic
set tclconfig(TCL_VERSION) {Private Stubs}
set tclconfig(TCL_PATCH_LEVEL) {}
set tclconfig(TCL_PREFIX) $tcldir
set tclconfig(TCL_LD_FLAGS) { }
} else {
# Use the system Tcl. Look in some likely places.
array set tclconfig [parse-tclconfig-sh \
$tcldir/unix $tcldir/win \
/usr /usr/local /usr/share /opt/local]
set msg "on your system"
}
} else {
array set tclconfig [parse-tclconfig-sh $tclpath]
set msg "at $tclpath"
}
if {[opt-bool static]} {
set tclconfig(TCL_LD_FLAGS) { }
}
if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} {
user-error "Cannot find Tcl $msg"
}
set tclstubs [opt-bool with-tcl-stubs]
if {$tclprivatestubs} {
define FOSSIL_ENABLE_TCL_PRIVATE_STUBS
define USE_TCL_STUBS
} elseif {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} {
set libs "$tclconfig(TCL_STUB_LIB_SPEC)"
define FOSSIL_ENABLE_TCL_STUBS
define USE_TCL_STUBS
} else {
set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)"
}
set cflags $tclconfig(TCL_INCLUDE_SPEC)
if {!$tclprivatestubs} {
set foundtcl 0; # Did we find a working Tcl library?
cc-with [list -cflags $cflags -libs $libs] {
if {$tclstubs} {
if {[cc-check-functions Tcl_InitStubs]} {
set foundtcl 1
}
} else {
if {[cc-check-functions Tcl_CreateInterp]} {
set foundtcl 1
}
}
}
if {!$foundtcl && [string match *-lieee* $libs]} {
# On some systems, using "-lieee" from TCL_LIB_SPEC appears
# to cause issues.
msg-result "Removing \"-lieee\" and retrying for Tcl..."
set libs [string map [list -lieee ""] $libs]
cc-with [list -cflags $cflags -libs $libs] {
if {$tclstubs} {
if {[cc-check-functions Tcl_InitStubs]} {
set foundtcl 1
}
} else {
if {[cc-check-functions Tcl_CreateInterp]} {
set foundtcl 1
}
}
}
}
if {!$foundtcl && ![string match *-lpthread* $libs]} {
# On some systems, TCL_LIB_SPEC appears to be missing
# "-lpthread". Try adding it.
msg-result "Adding \"-lpthread\" and retrying for Tcl..."
set libs "$libs -lpthread"
cc-with [list -cflags $cflags -libs $libs] {
if {$tclstubs} {
if {[cc-check-functions Tcl_InitStubs]} {
set foundtcl 1
}
} else {
if {[cc-check-functions Tcl_CreateInterp]} {
set foundtcl 1
}
}
}
}
if {!$foundtcl} {
if {$tclstubs} {
user-error "Cannot find a usable Tcl stubs library $msg"
} else {
user-error "Cannot find a usable Tcl library $msg"
}
}
}
set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
if {!$tclprivatestubs} {
define-append LIBS $libs
}
define-append EXTRA_CFLAGS $cflags
define-append CFLAGS $cflags
if {[info exists ::zlibpath] && $::zlibpath eq "tree"} {
#
# NOTE: When using zlib in the source tree, prevent Tcl from
# pulling in the system one.
#
set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \
$tclconfig(TCL_LD_FLAGS)]
}
#
# NOTE: Remove "-ldl" from the TCL_LD_FLAGS because it will be
# be checked for near the bottom of this file.
#
set tclconfig(TCL_LD_FLAGS) [string map [list -ldl ""] \
$tclconfig(TCL_LD_FLAGS)]
define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS)
define FOSSIL_ENABLE_TCL
}; # handle-with-tcl
handle-with-tcl
# Network functions require libraries on some systems
cc-check-function-in-lib gethostbyname nsl
if {![cc-check-function-in-lib socket {socket network}]} {
# Last resort, may be Windows
if {[is_mingw]} {
define-append LIBS -lwsock32
}
}
# Some systems (ex: SunOS) require -lrt in order to use nanosleep
cc-check-function-in-lib nanosleep rt
# The SMTP module requires special libraries and headers for MX DNS
# record lookups and such.
cc-check-includes arpa/nameser.h
cc-include-needs bind/resolv.h netinet/in.h
cc-check-includes bind/resolv.h
cc-check-includes resolv.h
if { !(([cc-check-function-in-lib dn_expand resolv] ||
[cc-check-function-in-lib ns_name_uncompress {bind resolv}] ||
[cc-check-function-in-lib __ns_name_uncompress {bind resolv}]) &&
([cc-check-function-in-lib ns_parserr {bind resolv}] ||
[cc-check-function-in-lib __ns_parserr {bind resolv}]) &&
([cc-check-function-in-lib res_query {bind resolv}] ||
[cc-check-function-in-lib __res_query {bind resolv}]))} {
msg-result "WARNING: SMTP feature will not be able to look up local MX."
}
cc-check-function-in-lib res_9_ns_initparse resolv
# Other nonstandard function checks
cc-check-functions utime
cc-check-functions usleep
cc-check-functions strchrnul
cc-check-functions pledge
cc-check-functions backtrace
# Termux on Android adds "getpass(char *)" to unistd.h, so check this so we
# guard against including it again; use cctest as cc-check-functions and
# 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
}
# 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.
#
# Returns 1 if supported, else 0. Defines MAKE_COMPILATION_DB to "yes"
# if supported, "no" if not.
proc check-compile-commands {} {
msg-checking "compile_commands.json support... "
if {[cctest -lang c -cflags {/dev/null -MJ} -source {}]} {
# This test reportedly incorrectly succeeds on one of
# Martin G.'s older systems.
msg-result "compiler supports compile_commands.json"
define MAKE_COMPILATION_DB yes
return 1
} else {
msg-result "compiler does not support compile_commands.json"
define MAKE_COMPILATION_DB no
return 0
}
}
define MAKE_COMPILATION_DB no
if {!$outOfTreeBuild} {
if {[opt-bool compile-commands]} {
check-compile-commands
} else {
puts "Use --compile-commands to enable check for compile-commands-capable compiler."
}
} else {
puts "Disabling compile_commands.json check for out-of-tree build."
# This is an attempt to resolve the problem reported at
# https://fossil-scm.org/forum/forumpost/d19061d09a8179d0
}
# Add -fsanitize compile and link options late: we don't want the C
# checks above to run with those sanitizers enabled. It can not only
# be pointless, it can actually break correct tests.
set fsan [opt-val with-sanitizer]
if {[string length $fsan]} {
define-append EXTRA_CFLAGS -fsanitize=$fsan
define-append EXTRA_LDFLAGS -fsanitize=$fsan
if {[string first "undefined" $fsan] != -1} {
# We need to link with libubsan if we're compiling under
# GCC with -fsanitize=undefined.
cc-check-function-in-lib __ubsan_handle_add_overflow ubsan
}
}
########################################################################
# @proj-check-emsdk
#
# Emscripten is used for doing in-tree builds of web-based WASM stuff,
# as opposed to WASI-based WASM or WASM binaries we import from other
# places. This is only set up for Unix-style OSes and is untested
# anywhere but Linux. Requires that the --with-emsdk flag be
# registered with autosetup.
#
# It looks for the SDK in the location specified by --with-emsdk.
# Values of "" or "auto" mean to check for the environment var EMSDK
# (which gets set by the emsdk_env.sh script from the SDK) or that
# same var passed to configure.
#
# If the given directory is found, it expects to find emsdk_env.sh in
# that directory, as well as the emcc compiler somewhere under there.
#
# If the --with-emsdk flag is explicitly provided and the SDK is not
# found then a fatal error is generated, otherwise failure to find the
# SDK is not fatal.
#
# Defines the following:
#
# - EMSDK_HOME = top dir of the emsdk or "".
# - EMSDK_ENV_SH = path to EMSDK_HOME/emsdk_env.sh or ""
# - BIN_EMCC = $EMSDK_HOME/upstream/emscripten/emcc or ""
# - HAVE_EMSDK = 0 or 1 (this function's return value)
#
# Returns 1 if EMSDK_ENV_SH is found, else 0. If EMSDK_HOME is not empty
# but BIN_EMCC is then emcc was not found in the EMSDK_HOME, in which
# case we have to rely on the fact that sourcing $EMSDK_ENV_SH from a
# shell will add emcc to the $PATH.
proc proj-check-emsdk {} {
set emsdkHome [opt-val with-emsdk]
define EMSDK_HOME ""
define EMSDK_ENV_SH ""
define BIN_EMCC ""
set hadValue [llength $emsdkHome]
msg-checking "Emscripten SDK? "
if {$emsdkHome in {"" "auto"}} {
# Check the environment. $EMSDK gets set by sourcing emsdk_env.sh.
set emsdkHome [get-env EMSDK ""]
}
set rc 0
if {$emsdkHome ne ""} {
define EMSDK_HOME $emsdkHome
set emsdkEnv "$emsdkHome/emsdk_env.sh"
if {[file exists $emsdkEnv]} {
msg-result "$emsdkHome"
define EMSDK_ENV_SH $emsdkEnv
set rc 1
set emcc "$emsdkHome/upstream/emscripten/emcc"
if {[file exists $emcc]} {
define BIN_EMCC $emcc
}
} else {
msg-result "emsdk_env.sh not found in $emsdkHome"
}
} else {
msg-result "not found"
}
if {$hadValue && 0 == $rc} {
# Fail if it was explicitly requested but not found
proj-fatal "Cannot find the Emscripten SDK"
}
define HAVE_EMSDK $rc
return $rc
}
if {[proj-check-emsdk]} {
define EMCC_WRAPPER $::autosetup(dir)/../tools/emcc.sh
define EMCC_OPT [get-env EMCC_OPT "-Oz"]; # optional flags to pass to emcc
make-template tools/emcc.sh.in
catch {exec chmod u+x tools/emcc.sh}
} else {
define EMCC_WRAPPER ""
define EMCC_OPT ""
catch {exec rm -f tools/emcc.sh}
}
handle-with-openssl
# Finally, append libraries that must be last. This matters more on some
# OSes than others, but is most broadly required for static linking.
if {[opt-bool static]} {
# Linux can only infer the dependency on pthread from OpenSSL when
# doing dynamic linkage.
define-append LIBS -lpthread
}
apply {{} {
# This started out as a workaround for getting the ordering of -ldl
# correct in conjunction with openssl in some environments. Then it
# evolved into a more generic preemptive portability workaround to
# ensure that certain libraries are always appended to the global
# LIBS list if they exist on the system. Based on a /chat discussion
# on 2025-02-27 in the context of check-in [8d3b9bf4d4].
#
# Note that [move-lib-to-end] and [lib-actually-exists] are in
# autosetup/local.tcl.
set libs [get-define LIBS]
#puts "**** 1 LIBS: $libs"
foreach ll {-ldl -lpthread -lm} {
if {![move-lib-to-end $ll $libs libs]} {
# $ll was not in the list
if {[lib-actually-exists $ll]} {
# Add it to the list "just in case." This will be a no-op on
# systems where the lib is not actually used.
lappend libs $ll
}
}
}
#puts "**** 2 LIBS: $libs"
define LIBS [join $libs " "]
}}
# Tag container builds with a prefix of the checkin ID of the version
# of Fossil each one contains. This not only allows multiple images
# to coexist and multiple containers to be created unamgiguosly from
# them, it also changes the URL we fetch the source tarball from, so
# repeated builds of a given version generate and fetch the source
# tarball once only, keeping it in the local Docker/Podman cache.
set ci [readfile "$::autosetup(srcdir)/manifest.uuid"]
define FOSSIL_CI_PFX [string range $ci 0 11]
make-template Makefile.in
make-config-header autoconfig.h -auto {USE_* FOSSIL_*}
|
Changes to autosetup/local.tcl.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 |
if {[regexp {^(TCL_[^=]*)=(.*)$} $line -> name value]} {
set value [regsub -all {\$\{.*\}} $value ""]
set tclconfig($name) [string trim $value ']
}
}
return [array get tclconfig]
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
if {[regexp {^(TCL_[^=]*)=(.*)$} $line -> name value]} {
set value [regsub -all {\$\{.*\}} $value ""]
set tclconfig($name) [string trim $value ']
}
}
return [array get tclconfig]
}
#
# Given a library link flag, e.g. -lfoo, returns 1 if that library can
# actually be linked to, else returns 0.
proc lib-actually-exists {linkFlag} {
cctest -link 1 -code "void libActuallyExists(void){}" -libs $linkFlag
}
#
# Given a library flag, e.g. -lfoo, a list of libs, e.g. {-lfoo -lbar
# -lbaz}, and a target variable name, this function appends all
# entries of $libList which do not match $flag to $tgtVar, then
# appends $flag to the end of $tgtVar. Returns the number of matches
# found.
proc move-lib-to-end {flag libList tgtVar} {
upvar $tgtVar tgt
set tgt {}
set found 0
foreach e $libList {
if {$flag eq $e} {
incr found
} else {
lappend tgt $e
}
}
if {$found} {
lappend tgt $flag
}
return $found
}
|
Added extsrc/linenoise-win32.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/* this code is not standalone
* it is included into linenoise.c
* for windows.
* It is deliberately kept separate so that
* applications that have no need for windows
* support can omit this
*/
static DWORD orig_consolemode = 0;
static int flushOutput(struct current *current);
static void outputNewline(struct current *current);
static void refreshStart(struct current *current)
{
(void)current;
}
static void refreshEnd(struct current *current)
{
(void)current;
}
static void refreshStartChars(struct current *current)
{
assert(current->output == NULL);
/* We accumulate all output here */
current->output = sb_alloc();
#ifdef USE_UTF8
current->ubuflen = 0;
#endif
}
static void refreshNewline(struct current *current)
{
DRL("<nl>");
outputNewline(current);
}
static void refreshEndChars(struct current *current)
{
assert(current->output);
flushOutput(current);
sb_free(current->output);
current->output = NULL;
}
static int enableRawMode(struct current *current) {
DWORD n;
INPUT_RECORD irec;
current->outh = GetStdHandle(STD_OUTPUT_HANDLE);
current->inh = GetStdHandle(STD_INPUT_HANDLE);
if (!PeekConsoleInput(current->inh, &irec, 1, &n)) {
return -1;
}
if (getWindowSize(current) != 0) {
return -1;
}
if (GetConsoleMode(current->inh, &orig_consolemode)) {
SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT);
}
#ifdef USE_UTF8
/* XXX is this the right thing to do? */
SetConsoleCP(65001);
#endif
return 0;
}
static void disableRawMode(struct current *current)
{
SetConsoleMode(current->inh, orig_consolemode);
}
void linenoiseClearScreen(void)
{
/* XXX: This is ugly. Should just have the caller pass a handle */
struct current current;
current.outh = GetStdHandle(STD_OUTPUT_HANDLE);
if (getWindowSize(¤t) == 0) {
COORD topleft = { 0, 0 };
DWORD n;
FillConsoleOutputCharacter(current.outh, ' ',
current.cols * current.rows, topleft, &n);
FillConsoleOutputAttribute(current.outh,
FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN,
current.cols * current.rows, topleft, &n);
SetConsoleCursorPosition(current.outh, topleft);
}
}
static void cursorToLeft(struct current *current)
{
COORD pos;
DWORD n;
pos.X = 0;
pos.Y = (SHORT)current->y;
FillConsoleOutputAttribute(current->outh,
FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n);
current->x = 0;
}
#ifdef USE_UTF8
static void flush_ubuf(struct current *current)
{
COORD pos;
DWORD nwritten;
pos.Y = (SHORT)current->y;
pos.X = (SHORT)current->x;
SetConsoleCursorPosition(current->outh, pos);
WriteConsoleW(current->outh, current->ubuf, current->ubuflen, &nwritten, 0);
current->x += current->ubufcols;
current->ubuflen = 0;
current->ubufcols = 0;
}
static void add_ubuf(struct current *current, int ch)
{
/* This code originally by: Author: Mark E. Davis, 1994. */
static const int halfShift = 10; /* used for shifting by 10 bits */
static const DWORD halfBase = 0x0010000UL;
static const DWORD halfMask = 0x3FFUL;
#define UNI_SUR_HIGH_START 0xD800
#define UNI_SUR_HIGH_END 0xDBFF
#define UNI_SUR_LOW_START 0xDC00
#define UNI_SUR_LOW_END 0xDFFF
#define UNI_MAX_BMP 0x0000FFFF
if (ch > UNI_MAX_BMP) {
/* convert from unicode to utf16 surrogate pairs
* There is always space for one extra word in ubuf
*/
ch -= halfBase;
current->ubuf[current->ubuflen++] = (WORD)((ch >> halfShift) + UNI_SUR_HIGH_START);
current->ubuf[current->ubuflen++] = (WORD)((ch & halfMask) + UNI_SUR_LOW_START);
}
else {
current->ubuf[current->ubuflen++] = ch;
}
current->ubufcols += utf8_width(ch);
if (current->ubuflen >= UBUF_MAX_CHARS) {
flush_ubuf(current);
}
}
#endif
static int flushOutput(struct current *current)
{
const char *pt = sb_str(current->output);
int len = sb_len(current->output);
#ifdef USE_UTF8
/* convert utf8 in current->output into utf16 in current->ubuf
*/
while (len) {
int ch;
int n = utf8_tounicode(pt, &ch);
pt += n;
len -= n;
add_ubuf(current, ch);
}
flush_ubuf(current);
#else
DWORD nwritten;
COORD pos;
pos.Y = (SHORT)current->y;
pos.X = (SHORT)current->x;
SetConsoleCursorPosition(current->outh, pos);
WriteConsoleA(current->outh, pt, len, &nwritten, 0);
current->x += len;
#endif
sb_clear(current->output);
return 0;
}
static int outputChars(struct current *current, const char *buf, int len)
{
if (len < 0) {
len = strlen(buf);
}
assert(current->output);
sb_append_len(current->output, buf, len);
return 0;
}
static void outputNewline(struct current *current)
{
/* On the last row output a newline to force a scroll */
if (current->y + 1 == current->rows) {
outputChars(current, "\n", 1);
}
flushOutput(current);
current->x = 0;
current->y++;
}
static void setOutputHighlight(struct current *current, const int *props, int nprops)
{
int colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
int bold = 0;
int reverse = 0;
int i;
for (i = 0; i < nprops; i++) {
switch (props[i]) {
case 0:
colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
bold = 0;
reverse = 0;
break;
case 1:
bold = FOREGROUND_INTENSITY;
break;
case 7:
reverse = 1;
break;
case 30:
colour = 0;
break;
case 31:
colour = FOREGROUND_RED;
break;
case 32:
colour = FOREGROUND_GREEN;
break;
case 33:
colour = FOREGROUND_RED | FOREGROUND_GREEN;
break;
case 34:
colour = FOREGROUND_BLUE;
break;
case 35:
colour = FOREGROUND_RED | FOREGROUND_BLUE;
break;
case 36:
colour = FOREGROUND_BLUE | FOREGROUND_GREEN;
break;
case 37:
colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
break;
}
}
flushOutput(current);
if (reverse) {
SetConsoleTextAttribute(current->outh, BACKGROUND_INTENSITY);
}
else {
SetConsoleTextAttribute(current->outh, colour | bold);
}
}
static void eraseEol(struct current *current)
{
COORD pos;
DWORD n;
pos.X = (SHORT) current->x;
pos.Y = (SHORT) current->y;
FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n);
}
static void setCursorXY(struct current *current)
{
COORD pos;
pos.X = (SHORT) current->x;
pos.Y = (SHORT) current->y;
SetConsoleCursorPosition(current->outh, pos);
}
static void setCursorPos(struct current *current, int x)
{
current->x = x;
setCursorXY(current);
}
static void cursorUp(struct current *current, int n)
{
current->y -= n;
setCursorXY(current);
}
static void cursorDown(struct current *current, int n)
{
current->y += n;
setCursorXY(current);
}
static int fd_read(struct current *current)
{
while (1) {
INPUT_RECORD irec;
DWORD n;
if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) {
break;
}
if (!ReadConsoleInputW(current->inh, &irec, 1, &n)) {
break;
}
if (irec.EventType == KEY_EVENT) {
KEY_EVENT_RECORD *k = &irec.Event.KeyEvent;
if (k->bKeyDown || k->wVirtualKeyCode == VK_MENU) {
if (k->dwControlKeyState & ENHANCED_KEY) {
switch (k->wVirtualKeyCode) {
case VK_LEFT:
return SPECIAL_LEFT;
case VK_RIGHT:
return SPECIAL_RIGHT;
case VK_UP:
return SPECIAL_UP;
case VK_DOWN:
return SPECIAL_DOWN;
case VK_INSERT:
return SPECIAL_INSERT;
case VK_DELETE:
return SPECIAL_DELETE;
case VK_HOME:
return SPECIAL_HOME;
case VK_END:
return SPECIAL_END;
case VK_PRIOR:
return SPECIAL_PAGE_UP;
case VK_NEXT:
return SPECIAL_PAGE_DOWN;
case VK_RETURN:
return k->uChar.UnicodeChar;
}
}
/* Note that control characters are already translated in AsciiChar */
else if (k->wVirtualKeyCode == VK_CONTROL)
continue;
else {
return k->uChar.UnicodeChar;
}
}
}
}
return -1;
}
static int getWindowSize(struct current *current)
{
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(current->outh, &info)) {
return -1;
}
current->cols = info.dwSize.X;
current->rows = info.dwSize.Y;
if (current->cols <= 0 || current->rows <= 0) {
current->cols = 80;
return -1;
}
current->y = info.dwCursorPosition.Y;
current->x = info.dwCursorPosition.X;
return 0;
}
|
Changes to extsrc/linenoise.c.
| ︙ | ︙ | |||
890 891 892 893 894 895 896 897 898 899 900 901 902 903 |
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
| > | 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 |
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 */
int pending; /* pending char fd_read_char() */
#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
|
| ︙ | ︙ | |||
1119 1120 1121 1122 1123 1124 1125 |
/* 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 */
| | > > > | | | | 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 |
/* 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. Because we aren't changing any output
* settings we don't need to use TCSADRAIN and I have seen that hang on
* OpenBSD when running under a pty
*/
if (tcsetattr(current->fd,TCSANOW,&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,TCSANOW,&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, TCSANOW, &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.
|
| ︙ | ︙ | |||
1228 1229 1230 1231 1232 1233 1234 |
void linenoiseClearScreen(void)
{
IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
}
/**
| | | > > > > > > | | > > > > | | > > > > > > > > > | > > > | 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 |
void linenoiseClearScreen(void)
{
IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
}
/**
* Reads a char from 'current->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(struct current *current, int timeout)
{
struct pollfd p;
unsigned char c;
if (current->pending) {
c = current->pending;
current->pending = 0;
return c;
}
p.fd = current->fd;
p.events = POLLIN;
if (poll(&p, 1, timeout) == 0) {
/* timeout */
return -1;
}
if (read(current->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 (current->pending) {
buf[0] = current->pending;
current->pending = 0;
}
else 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, -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;
/* Unfortunately we don't have any persistent state, so assume
* a process will only ever interact with one terminal at a time.
*/
static int query_cursor_failed;
if (query_cursor_failed) {
/* If it ever fails, don't try again */
return 0;
}
/* 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, 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:
/* Push back the character that caused the error */
current->pending = ch;
break;
}
/* failed */
break;
}
query_cursor_failed = 1;
return 0;
}
/**
* Updates current->cols with the current window size (width)
*/
static int getWindowSize(struct current *current)
|
| ︙ | ︙ | |||
1384 1385 1386 1387 1388 1389 1390 | * 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. */ | | | | | 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 |
* 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(struct current *current)
{
int c = fd_read_char(current, 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(current, 50);
if (c2 < 0) {
return c2;
}
if (c == '[' || c == 'O') {
/* Potential arrow key */
switch (c2) {
case 'A':
|
| ︙ | ︙ | |||
1420 1421 1422 1423 1424 1425 1426 |
return SPECIAL_END;
case 'H':
return SPECIAL_HOME;
}
}
if (c == '[' && c2 >= '1' && c2 <= '8') {
/* extended escape */
| | | | 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 |
return SPECIAL_END;
case 'H':
return SPECIAL_HOME;
}
}
if (c == '[' && c2 >= '1' && c2 <= '8') {
/* extended escape */
c = fd_read_char(current, 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(current, 50);
}
}
return SPECIAL_NONE;
}
#endif
|
| ︙ | ︙ | |||
2232 2233 2234 2235 2236 2237 2238 |
rbuf[p_ind] = 0;
rlen = strlen(rbuf);
}
continue;
}
#ifdef USE_TERMIOS
if (c == CHAR_ESCAPE) {
| | | 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 |
rbuf[p_ind] = 0;
rlen = strlen(rbuf);
}
continue;
}
#ifdef USE_TERMIOS
if (c == CHAR_ESCAPE) {
c = check_special(current);
}
#endif
if (c == ctrl('R')) {
/* Search for the previous (earlier) match */
if (searchpos > 0) {
searchpos--;
}
|
| ︙ | ︙ | |||
2344 2345 2346 2347 2348 2349 2350 |
/* 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 */
| | | 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 |
/* 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);
}
#endif
if (c == -1) {
/* Return on errors */
return sb_len(current->buf);
}
|
| ︙ | ︙ |
Changes to extsrc/pikchr-worker.js.
| ︙ | ︙ | |||
34 35 36 37 38 39 40 |
{
pikchr: source code for the pikchr,
darkMode: boolean true to adjust colors for a dark color scheme,
cssClass: CSS class name to add to the SVG
}
| | | | | 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 |
{
pikchr: source code for the pikchr,
darkMode: boolean true to adjust colors for a dark color scheme,
cssClass: CSS class name to add to the SVG
}
Workers-to-Main message types:
- stdout, stderr: indicate stdout/stderr output from the wasm
layer. The data property is the string of the output, noting
that the emscripten binding emits these one line at a time. Thus,
if a C-side puts() emits multiple lines in a single call, the JS
side will see that as multiple calls. Example:
{type:'stdout', data: 'Hi, world.'}
- module: Status text. This is intended to alert the main thread
about module loading status so that, e.g., the main thread can
update a progress widget and DTRT when the module is finished
loading and available for work. Status messages come in the form
{type:'module', data:{
type:'status',
data: {text:string|null, step:1-based-integer}
}
with an incrementing step value for each subsequent message. When
the module loading is complete, a message with a text value of
null is posted.
- pikchr:
{type: 'pikchr',
data:{
pikchr: input text,
result: rendered result (SVG on success, HTML on error),
isError: bool, true if .pikchr holds an error report,
flags: integer: flags used to configure the pikchr rendering,
|
| ︙ | ︙ | |||
160 161 162 163 164 165 166 |
pikchrModule.stackRestore(stack);
wMsg('working','end');
}
return;
};
console.warn("Unknown pikchr-worker message type:",ev);
};
| | | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
pikchrModule.stackRestore(stack);
wMsg('working','end');
}
return;
};
console.warn("Unknown pikchr-worker message type:",ev);
};
/**
emscripten module for use with build mode -sMODULARIZE.
*/
const pikchrModule = {
print: function(){wMsg('stdout', Array.prototype.slice.call(arguments));},
printErr: stderr,
/**
|
| ︙ | ︙ | |||
204 205 206 207 208 209 210 |
wMsg('module',{
type:'status',
data:{step: ++f.last.step, text: text||null}
});
}
};
| | > | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
wMsg('module',{
type:'status',
data:{step: ++f.last.step, text: text||null}
});
}
};
importScripts('pikchr-v2813665466.js');
/**
initPikchrModule() is installed via pikchr.js due to
building with:
emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule
*/
initPikchrModule(pikchrModule).then(function(thisModule){
//globalThis.M = pikchrModule; console.warn("pikchrModule=globalThis.M=",globalThis.M);
wMsg('pikchr-ready', pikchrModule.ccall('pikchr_version','string'));
});
})();
|
Changes to extsrc/pikchr.c.
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. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* This file is automatically generated by Lemon from input grammar ** source file "pikchr.y". */ /* ** 2000-05-29 ** ** 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. ** ************************************************************************* ** Driver template for the LEMON parser generator. ** ** The "lemon" program processes an LALR(1) input grammar file, then uses ** this template to construct a parser. The "lemon" program inserts text ** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the ** interstitial "-" characters) contained in this template is changed into ** the value of the %name directive from the grammar. Otherwise, the content ** of this template is copied straight through into the generate parser ** source file. ** ** The following is the concatenation of all %include directives from the ** input grammar file: */ /************ Begin %include sections from the grammar ************************/ #line 1 "VERSION.h" #define MANIFEST_UUID "8a43b020141f772a0ac45291a7fd73041d2efba5e3665c6bd2f334ad9b2e9845" #define MANIFEST_VERSION "[8a43b02014]" #define MANIFEST_DATE "2025-03-19 16:19:43" #define MANIFEST_YEAR "2025" #define MANIFEST_ISODATE "20250319161943" #define MANIFEST_NUMERIC_DATE 20250319 #define MANIFEST_NUMERIC_TIME 161943 #define RELEASE_VERSION "1.0" #define RELEASE_VERSION_NUMBER 10000 #define RELEASE_RESOURCE_VERSION 1,0,0,0 #define COMPILER "gcc-13.3.0" #line 2 "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. |
| ︙ | ︙ | |||
123 124 125 126 127 128 129 130 131 132 133 134 135 136 | #include <ctype.h> #include <math.h> #include <assert.h> #define count(X) (sizeof(X)/sizeof(X[0])) #ifndef M_PI # define M_PI 3.1415926535897932385 #endif /* Limit the number of tokens in a single script to avoid run-away ** macro expansion attacks. See forum post ** https://pikchr.org/home/forumpost/ef8684c6955a411a */ #ifndef PIKCHR_TOKEN_LIMIT # define PIKCHR_TOKEN_LIMIT 100000 | > > > > > > > > > > > > | 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 | #include <ctype.h> #include <math.h> #include <assert.h> #define count(X) (sizeof(X)/sizeof(X[0])) #ifndef M_PI # define M_PI 3.1415926535897932385 #endif /* ** Typesafe version of ctype.h macros. Cygwin requires this, I'm told. */ #define IsUpper(X) isupper((unsigned char)(X)) #define IsLower(X) islower((unsigned char)(X)) #define ToLower(X) tolower((unsigned char)(X)) #define IsDigit(X) isdigit((unsigned char)(X)) #define IsXDigit(X) isxdigit((unsigned char)(X)) #define IsSpace(X) isspace((unsigned char)(X)) #define IsAlnum(X) isalnum((unsigned char)(X)) /* Limit the number of tokens in a single script to avoid run-away ** macro expansion attacks. See forum post ** https://pikchr.org/home/forumpost/ef8684c6955a411a */ #ifndef PIKCHR_TOKEN_LIMIT # define PIKCHR_TOKEN_LIMIT 100000 |
| ︙ | ︙ | |||
472 473 474 475 476 477 478 | static int pik_bbox_contains_point(PBox*,PPoint*); static void pik_bbox_init(PBox*); static void pik_bbox_addbox(PBox*,PBox*); static void pik_bbox_add_xy(PBox*,PNum,PNum); static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); static void pik_add_txt(Pik*,PToken*,int); static int pik_text_length(const PToken *pToken, const int isMonospace); | | | | 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 | static int pik_bbox_contains_point(PBox*,PPoint*); static void pik_bbox_init(PBox*); static void pik_bbox_addbox(PBox*,PBox*); static void pik_bbox_add_xy(PBox*,PNum,PNum); static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); static void pik_add_txt(Pik*,PToken*,int); static int pik_text_length(const PToken *pToken, const int isMonospace); static void pik_size_to_fit(Pik*,PObj*,PToken*,int); static int pik_text_position(int,PToken*); static PNum pik_property_of(PObj*,PToken*); static PNum pik_func(Pik*,PToken*,PNum,PNum); static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt); static PPoint pik_position_at_hdg(PNum dist, PToken *pD, PPoint pt); static void pik_same(Pik *p, PObj*, PToken*); static PPoint pik_nth_vertex(Pik *p, PToken *pNth, PToken *pErr, PObj *pObj); static PToken pik_next_semantic_token(PToken *pThis); static void pik_compute_layout_settings(Pik*); 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 549 "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 |
| ︙ | ︙ | |||
520 521 522 523 524 525 526 | #define T_CODEBLOCK 19 #define T_FILL 20 #define T_COLOR 21 #define T_THICKNESS 22 #define T_PRINT 23 #define T_STRING 24 #define T_COMMA 25 | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | #define T_CODEBLOCK 19 #define T_FILL 20 #define T_COLOR 21 #define T_THICKNESS 22 #define T_PRINT 23 #define T_STRING 24 #define T_COMMA 25 #define T_ISODATE 26 #define T_CLASSNAME 27 #define T_LB 28 #define T_RB 29 #define T_UP 30 #define T_DOWN 31 #define T_LEFT 32 #define T_RIGHT 33 #define T_CLOSE 34 #define T_CHOP 35 #define T_FROM 36 #define T_TO 37 #define T_THEN 38 #define T_HEADING 39 #define T_GO 40 #define T_AT 41 #define T_WITH 42 #define T_SAME 43 #define T_AS 44 #define T_FIT 45 #define T_BEHIND 46 #define T_UNTIL 47 #define T_EVEN 48 #define T_DOT_E 49 #define T_HEIGHT 50 #define T_WIDTH 51 #define T_RADIUS 52 #define T_DIAMETER 53 #define T_DOTTED 54 #define T_DASHED 55 #define T_CW 56 #define T_CCW 57 #define T_LARROW 58 #define T_RARROW 59 #define T_LRARROW 60 #define T_INVIS 61 #define T_THICK 62 #define T_THIN 63 #define T_SOLID 64 #define T_CENTER 65 #define T_LJUST 66 #define T_RJUST 67 #define T_ABOVE 68 #define T_BELOW 69 #define T_ITALIC 70 #define T_BOLD 71 #define T_MONO 72 #define T_ALIGNED 73 #define T_BIG 74 #define T_SMALL 75 #define T_AND 76 #define T_LT 77 #define T_GT 78 #define T_ON 79 #define T_WAY 80 #define T_BETWEEN 81 #define T_THE 82 #define T_NTH 83 #define T_VERTEX 84 #define T_TOP 85 #define T_BOTTOM 86 #define T_START 87 #define T_END 88 #define T_IN 89 #define T_THIS 90 #define T_DOT_U 91 #define T_LAST 92 #define T_NUMBER 93 #define T_FUNC1 94 #define T_FUNC2 95 #define T_DIST 96 #define T_DOT_XY 97 #define T_X 98 #define T_Y 99 #define T_DOT_L 100 #endif /**************** End token definitions ***************************************/ /* The next sections is a series of control #defines. ** various aspects of the generated parser. ** YYCODETYPE is the data type used to store the integer codes ** that represent terminal and non-terminal symbols. |
| ︙ | ︙ | |||
658 659 660 661 662 663 664 | ** YY_MAX_DSTRCTR Maximum symbol value that has a destructor */ #ifndef INTERFACE # define INTERFACE 1 #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned char | | | < | | > > | < | 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 |
** 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 138
#define YYACTIONTYPE unsigned short int
#define pik_parserTOKENTYPE PToken
typedef union {
int yyinit;
pik_parserTOKENTYPE yy0;
PList* yy23;
PRel yy28;
PObj* yy54;
PNum yy129;
PPoint yy187;
short int yy272;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
#endif
#define pik_parserARG_SDECL
#define pik_parserARG_PDECL
#define pik_parserARG_PARAM
|
| ︙ | ︙ | |||
691 692 693 694 695 696 697 | #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 | | | | | 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 | #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 101 #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 101 #define YY_MAX_DSTRCTR 104 /************* 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 |
| ︙ | ︙ | |||
784 785 786 787 788 789 790 | ** yy_shift_ofst[] For each state, the offset into yy_action for ** shifting terminals. ** yy_reduce_ofst[] For each state, the offset into yy_action for ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. ** *********** Begin parsing tables **********************************************/ | | | | | | | | | | | | | | | | | | | > > | < < | | | | | | | | | | > | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | < | | | | | | | | | | | | | | | | | | | | < | < | | | < | | | | < | | | | > | | | | | | | | < | | | | | | > | | | | | > > > | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | < | | | | | > | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
** yy_shift_ofst[] For each state, the offset into yy_action for
** shifting terminals.
** yy_reduce_ofst[] For each state, the offset into yy_action for
** shifting non-terminals after a reduce.
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
#define YY_ACTTAB_COUNT (1305)
static const YYACTIONTYPE yy_action[] = {
/* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148,
/* 10 */ 575, 64, 63, 62, 61, 453, 113, 120, 161, 119,
/* 20 */ 427, 428, 339, 357, 81, 121, 447, 454, 29, 575,
/* 30 */ 530, 13, 50, 450, 322, 323, 9, 8, 33, 149,
/* 40 */ 32, 7, 71, 127, 163, 335, 66, 28, 444, 27,
/* 50 */ 339, 339, 339, 339, 425, 426, 340, 341, 342, 343,
/* 60 */ 344, 345, 346, 347, 348, 474, 64, 63, 62, 61,
/* 70 */ 54, 51, 73, 306, 148, 474, 492, 161, 119, 297,
/* 80 */ 112, 113, 120, 161, 119, 427, 428, 339, 30, 81,
/* 90 */ 109, 447, 454, 29, 474, 528, 161, 119, 450, 322,
/* 100 */ 323, 9, 8, 33, 149, 32, 7, 71, 127, 163,
/* 110 */ 335, 66, 535, 36, 27, 339, 339, 339, 339, 425,
/* 120 */ 426, 340, 341, 342, 343, 344, 345, 346, 347, 348,
/* 130 */ 394, 435, 310, 59, 60, 64, 63, 62, 61, 313,
/* 140 */ 74, 376, 148, 69, 2, 533, 161, 119, 124, 113,
/* 150 */ 120, 161, 119, 80, 535, 31, 308, 79, 83, 107,
/* 160 */ 535, 441, 440, 535, 394, 435, 299, 59, 60, 120,
/* 170 */ 161, 119, 149, 463, 376, 376, 330, 84, 2, 122,
/* 180 */ 78, 78, 38, 156, 156, 156, 48, 37, 559, 328,
/* 190 */ 128, 152, 560, 561, 434, 441, 440, 350, 350, 350,
/* 200 */ 350, 350, 350, 350, 350, 350, 350, 350, 577, 77,
/* 210 */ 577, 35, 106, 46, 436, 437, 438, 439, 579, 375,
/* 220 */ 298, 117, 393, 155, 154, 153, 47, 4, 434, 69,
/* 230 */ 394, 435, 3, 59, 60, 411, 412, 413, 414, 398,
/* 240 */ 399, 376, 62, 61, 2, 108, 106, 5, 436, 437,
/* 250 */ 438, 439, 375, 375, 117, 117, 393, 155, 154, 153,
/* 260 */ 76, 441, 440, 67, 6, 142, 140, 64, 63, 62,
/* 270 */ 61, 380, 157, 424, 427, 428, 339, 379, 159, 45,
/* 280 */ 423, 72, 131, 148, 531, 161, 119, 1, 55, 125,
/* 290 */ 113, 120, 161, 119, 434, 147, 146, 64, 63, 62,
/* 300 */ 61, 397, 43, 11, 339, 339, 339, 339, 425, 426,
/* 310 */ 355, 65, 106, 149, 436, 437, 438, 439, 74, 375,
/* 320 */ 148, 117, 393, 155, 154, 153, 497, 113, 120, 161,
/* 330 */ 119, 22, 21, 12, 142, 140, 64, 63, 62, 61,
/* 340 */ 24, 356, 145, 141, 431, 64, 63, 62, 61, 391,
/* 350 */ 149, 448, 454, 29, 378, 158, 85, 55, 450, 394,
/* 360 */ 432, 138, 59, 60, 147, 146, 120, 161, 119, 163,
/* 370 */ 102, 43, 139, 42, 27, 430, 14, 15, 301, 302,
/* 380 */ 303, 446, 305, 16, 44, 74, 18, 148, 152, 19,
/* 390 */ 20, 36, 68, 496, 113, 120, 161, 119, 114, 359,
/* 400 */ 22, 21, 23, 142, 140, 64, 63, 62, 61, 24,
/* 410 */ 107, 145, 141, 431, 26, 57, 377, 149, 58, 118,
/* 420 */ 120, 161, 119, 392, 463, 384, 55, 64, 63, 62,
/* 430 */ 61, 382, 569, 147, 146, 160, 383, 435, 39, 70,
/* 440 */ 43, 106, 152, 445, 445, 88, 445, 445, 375, 445,
/* 450 */ 117, 393, 155, 154, 153, 120, 161, 119, 445, 17,
/* 460 */ 445, 10, 479, 479, 445, 445, 435, 441, 440, 22,
/* 470 */ 21, 445, 403, 64, 63, 62, 61, 152, 24, 445,
/* 480 */ 145, 141, 431, 133, 75, 126, 354, 445, 445, 123,
/* 490 */ 445, 404, 405, 406, 408, 80, 441, 440, 308, 79,
/* 500 */ 434, 411, 412, 413, 414, 394, 445, 445, 59, 60,
/* 510 */ 64, 63, 62, 61, 445, 445, 376, 445, 445, 42,
/* 520 */ 436, 437, 438, 439, 156, 156, 156, 394, 445, 434,
/* 530 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 445,
/* 540 */ 445, 42, 445, 394, 473, 391, 59, 60, 445, 436,
/* 550 */ 437, 438, 439, 49, 376, 445, 74, 42, 148, 445,
/* 560 */ 88, 445, 445, 445, 490, 113, 120, 161, 119, 445,
/* 570 */ 120, 161, 119, 132, 130, 394, 143, 475, 59, 60,
/* 580 */ 445, 473, 64, 63, 62, 61, 376, 106, 149, 42,
/* 590 */ 445, 445, 152, 445, 375, 391, 117, 393, 155, 154,
/* 600 */ 153, 394, 144, 52, 59, 60, 445, 445, 445, 106,
/* 610 */ 445, 445, 376, 445, 445, 42, 375, 445, 117, 393,
/* 620 */ 155, 154, 153, 445, 445, 106, 64, 63, 62, 61,
/* 630 */ 445, 445, 375, 445, 117, 393, 155, 154, 153, 394,
/* 640 */ 445, 445, 59, 60, 88, 445, 445, 53, 445, 445,
/* 650 */ 376, 445, 445, 42, 120, 161, 119, 106, 445, 445,
/* 660 */ 445, 110, 110, 445, 375, 445, 117, 393, 155, 154,
/* 670 */ 153, 394, 445, 445, 59, 60, 152, 107, 445, 445,
/* 680 */ 445, 445, 102, 106, 445, 42, 445, 120, 161, 119,
/* 690 */ 375, 451, 117, 393, 155, 154, 153, 394, 445, 445,
/* 700 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 152,
/* 710 */ 445, 40, 445, 394, 445, 396, 59, 60, 445, 445,
/* 720 */ 445, 106, 445, 445, 376, 88, 445, 41, 375, 445,
/* 730 */ 117, 393, 155, 154, 153, 120, 161, 119, 74, 445,
/* 740 */ 148, 445, 111, 111, 107, 445, 484, 113, 120, 161,
/* 750 */ 119, 445, 445, 106, 120, 161, 119, 152, 478, 445,
/* 760 */ 375, 86, 117, 393, 155, 154, 153, 445, 445, 445,
/* 770 */ 149, 120, 161, 119, 445, 445, 152, 445, 445, 106,
/* 780 */ 445, 64, 63, 62, 61, 445, 375, 445, 117, 393,
/* 790 */ 155, 154, 153, 152, 395, 106, 64, 63, 62, 61,
/* 800 */ 98, 445, 375, 445, 117, 393, 155, 154, 153, 445,
/* 810 */ 120, 161, 119, 445, 74, 445, 148, 56, 445, 74,
/* 820 */ 445, 148, 483, 113, 120, 161, 119, 480, 113, 120,
/* 830 */ 161, 119, 152, 74, 445, 148, 445, 89, 445, 445,
/* 840 */ 445, 134, 113, 120, 161, 119, 149, 120, 161, 119,
/* 850 */ 445, 149, 74, 445, 148, 445, 445, 445, 378, 158,
/* 860 */ 517, 113, 120, 161, 119, 149, 74, 445, 148, 152,
/* 870 */ 445, 74, 445, 148, 137, 113, 120, 161, 119, 525,
/* 880 */ 113, 120, 161, 119, 149, 74, 445, 148, 64, 63,
/* 890 */ 62, 61, 445, 527, 113, 120, 161, 119, 149, 445,
/* 900 */ 445, 391, 445, 149, 445, 445, 445, 445, 445, 445,
/* 910 */ 74, 445, 148, 445, 445, 162, 445, 149, 524, 113,
/* 920 */ 120, 161, 119, 118, 445, 74, 445, 148, 445, 445,
/* 930 */ 445, 445, 445, 526, 113, 120, 161, 119, 445, 74,
/* 940 */ 445, 148, 149, 445, 445, 445, 445, 523, 113, 120,
/* 950 */ 161, 119, 74, 445, 148, 445, 445, 149, 445, 445,
/* 960 */ 522, 113, 120, 161, 119, 445, 74, 445, 148, 445,
/* 970 */ 445, 149, 445, 445, 521, 113, 120, 161, 119, 74,
/* 980 */ 445, 148, 445, 445, 149, 445, 445, 520, 113, 120,
/* 990 */ 161, 119, 445, 74, 445, 148, 445, 445, 149, 445,
/* 1000 */ 445, 519, 113, 120, 161, 119, 445, 445, 445, 445,
/* 1010 */ 445, 149, 445, 445, 445, 445, 445, 445, 74, 445,
/* 1020 */ 148, 445, 445, 445, 445, 149, 150, 113, 120, 161,
/* 1030 */ 119, 74, 445, 148, 445, 445, 445, 445, 445, 151,
/* 1040 */ 113, 120, 161, 119, 445, 74, 445, 148, 445, 445,
/* 1050 */ 149, 445, 445, 136, 113, 120, 161, 119, 74, 445,
/* 1060 */ 148, 445, 445, 149, 445, 445, 135, 113, 120, 161,
/* 1070 */ 119, 445, 88, 445, 445, 445, 445, 149, 445, 445,
/* 1080 */ 445, 90, 120, 161, 119, 445, 445, 445, 445, 82,
/* 1090 */ 149, 120, 161, 119, 445, 87, 466, 445, 34, 99,
/* 1100 */ 445, 445, 445, 445, 152, 120, 161, 119, 100, 120,
/* 1110 */ 161, 119, 445, 152, 445, 445, 445, 445, 120, 161,
/* 1120 */ 119, 445, 445, 445, 101, 445, 445, 152, 445, 445,
/* 1130 */ 445, 152, 91, 445, 120, 161, 119, 103, 445, 445,
/* 1140 */ 152, 445, 120, 161, 119, 445, 445, 120, 161, 119,
/* 1150 */ 445, 92, 445, 445, 445, 445, 152, 445, 445, 445,
/* 1160 */ 93, 120, 161, 119, 152, 445, 104, 445, 445, 152,
/* 1170 */ 120, 161, 119, 445, 94, 445, 120, 161, 119, 445,
/* 1180 */ 445, 445, 445, 152, 120, 161, 119, 445, 445, 105,
/* 1190 */ 445, 445, 152, 445, 445, 445, 445, 95, 152, 120,
/* 1200 */ 161, 119, 96, 445, 445, 97, 152, 120, 161, 119,
/* 1210 */ 445, 445, 120, 161, 119, 120, 161, 119, 445, 445,
/* 1220 */ 445, 152, 445, 445, 445, 445, 445, 445, 445, 152,
/* 1230 */ 549, 445, 445, 548, 152, 445, 445, 152, 547, 445,
/* 1240 */ 120, 161, 119, 120, 161, 119, 546, 445, 120, 161,
/* 1250 */ 119, 445, 445, 445, 445, 445, 120, 161, 119, 445,
/* 1260 */ 445, 445, 152, 445, 445, 152, 445, 445, 445, 115,
/* 1270 */ 152, 445, 116, 445, 445, 445, 445, 445, 152, 120,
/* 1280 */ 161, 119, 120, 161, 119, 445, 445, 445, 445, 445,
/* 1290 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445,
/* 1300 */ 445, 152, 445, 445, 152,
};
static const YYCODETYPE yy_lookahead[] = {
/* 0 */ 0, 115, 116, 117, 136, 103, 104, 105, 107, 107,
/* 10 */ 10, 4, 5, 6, 7, 113, 114, 115, 116, 117,
/* 20 */ 20, 21, 22, 17, 24, 101, 102, 103, 104, 29,
/* 30 */ 107, 25, 25, 109, 34, 35, 36, 37, 38, 137,
/* 40 */ 40, 41, 42, 43, 120, 45, 46, 109, 124, 125,
/* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
/* 60 */ 60, 61, 62, 63, 64, 0, 4, 5, 6, 7,
/* 70 */ 4, 5, 105, 25, 107, 10, 115, 116, 117, 17,
/* 80 */ 113, 114, 115, 116, 117, 20, 21, 22, 128, 24,
/* 90 */ 101, 102, 103, 104, 29, 115, 116, 117, 109, 34,
/* 100 */ 35, 36, 37, 38, 137, 40, 41, 42, 43, 120,
/* 110 */ 45, 46, 49, 10, 125, 50, 51, 52, 53, 54,
/* 120 */ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
/* 130 */ 1, 2, 29, 4, 5, 4, 5, 6, 7, 8,
/* 140 */ 105, 12, 107, 3, 15, 115, 116, 117, 113, 114,
/* 150 */ 115, 116, 117, 24, 91, 130, 27, 28, 118, 105,
/* 160 */ 97, 32, 33, 100, 1, 2, 19, 4, 5, 115,
/* 170 */ 116, 117, 137, 119, 12, 12, 2, 118, 15, 1,
/* 180 */ 126, 127, 106, 20, 21, 22, 110, 111, 106, 2,
/* 190 */ 107, 137, 110, 111, 65, 32, 33, 65, 66, 67,
/* 200 */ 68, 69, 70, 71, 72, 73, 74, 75, 132, 133,
/* 210 */ 134, 131, 83, 39, 85, 86, 87, 88, 135, 90,
/* 220 */ 17, 92, 93, 94, 95, 96, 39, 15, 65, 89,
/* 230 */ 1, 2, 16, 4, 5, 30, 31, 32, 33, 98,
/* 240 */ 99, 12, 6, 7, 15, 83, 83, 41, 85, 86,
/* 250 */ 87, 88, 90, 90, 92, 92, 93, 94, 95, 96,
/* 260 */ 49, 32, 33, 44, 41, 2, 3, 4, 5, 6,
/* 270 */ 7, 27, 28, 42, 20, 21, 22, 27, 28, 16,
/* 280 */ 42, 105, 48, 107, 115, 116, 117, 13, 25, 113,
/* 290 */ 114, 115, 116, 117, 65, 32, 33, 4, 5, 6,
/* 300 */ 7, 17, 39, 25, 50, 51, 52, 53, 54, 55,
/* 310 */ 17, 100, 83, 137, 85, 86, 87, 88, 105, 90,
/* 320 */ 107, 92, 93, 94, 95, 96, 113, 114, 115, 116,
/* 330 */ 117, 68, 69, 76, 2, 3, 4, 5, 6, 7,
/* 340 */ 77, 17, 79, 80, 81, 4, 5, 6, 7, 17,
/* 350 */ 137, 102, 103, 104, 27, 28, 105, 25, 109, 1,
/* 360 */ 81, 80, 4, 5, 32, 33, 115, 116, 117, 120,
/* 370 */ 12, 39, 82, 15, 125, 81, 3, 36, 20, 21,
/* 380 */ 22, 0, 24, 3, 39, 105, 3, 107, 137, 3,
/* 390 */ 3, 10, 3, 113, 114, 115, 116, 117, 97, 78,
/* 400 */ 68, 69, 25, 2, 3, 4, 5, 6, 7, 77,
/* 410 */ 105, 79, 80, 81, 15, 15, 12, 137, 15, 92,
/* 420 */ 115, 116, 117, 17, 119, 29, 25, 4, 5, 6,
/* 430 */ 7, 29, 127, 32, 33, 91, 29, 2, 11, 3,
/* 440 */ 39, 83, 137, 138, 138, 105, 138, 138, 90, 138,
/* 450 */ 92, 93, 94, 95, 96, 115, 116, 117, 138, 36,
/* 460 */ 138, 121, 122, 123, 138, 138, 2, 32, 33, 68,
/* 470 */ 69, 138, 1, 4, 5, 6, 7, 137, 77, 138,
/* 480 */ 79, 80, 81, 12, 49, 14, 17, 138, 138, 18,
/* 490 */ 138, 20, 21, 22, 23, 24, 32, 33, 27, 28,
/* 500 */ 65, 30, 31, 32, 33, 1, 138, 138, 4, 5,
/* 510 */ 4, 5, 6, 7, 138, 138, 12, 138, 138, 15,
/* 520 */ 85, 86, 87, 88, 20, 21, 22, 1, 138, 65,
/* 530 */ 4, 5, 4, 5, 6, 7, 138, 138, 12, 138,
/* 540 */ 138, 15, 138, 1, 2, 17, 4, 5, 138, 85,
/* 550 */ 86, 87, 88, 25, 12, 138, 105, 15, 107, 138,
/* 560 */ 105, 138, 138, 138, 113, 114, 115, 116, 117, 138,
/* 570 */ 115, 116, 117, 47, 48, 1, 2, 122, 4, 5,
/* 580 */ 138, 39, 4, 5, 6, 7, 12, 83, 137, 15,
/* 590 */ 138, 138, 137, 138, 90, 17, 92, 93, 94, 95,
/* 600 */ 96, 1, 2, 25, 4, 5, 138, 138, 138, 83,
/* 610 */ 138, 138, 12, 138, 138, 15, 90, 138, 92, 93,
/* 620 */ 94, 95, 96, 138, 138, 83, 4, 5, 6, 7,
/* 630 */ 138, 138, 90, 138, 92, 93, 94, 95, 96, 1,
/* 640 */ 138, 138, 4, 5, 105, 138, 138, 25, 138, 138,
/* 650 */ 12, 138, 138, 15, 115, 116, 117, 83, 138, 138,
/* 660 */ 138, 122, 123, 138, 90, 138, 92, 93, 94, 95,
/* 670 */ 96, 1, 138, 138, 4, 5, 137, 105, 138, 138,
/* 680 */ 138, 138, 12, 83, 138, 15, 138, 115, 116, 117,
/* 690 */ 90, 119, 92, 93, 94, 95, 96, 1, 138, 138,
/* 700 */ 4, 5, 4, 5, 6, 7, 138, 138, 12, 137,
/* 710 */ 138, 15, 138, 1, 138, 17, 4, 5, 138, 138,
/* 720 */ 138, 83, 138, 138, 12, 105, 138, 15, 90, 138,
/* 730 */ 92, 93, 94, 95, 96, 115, 116, 117, 105, 138,
/* 740 */ 107, 138, 122, 123, 105, 138, 113, 114, 115, 116,
/* 750 */ 117, 138, 138, 83, 115, 116, 117, 137, 119, 138,
/* 760 */ 90, 105, 92, 93, 94, 95, 96, 138, 138, 138,
/* 770 */ 137, 115, 116, 117, 138, 138, 137, 138, 138, 83,
/* 780 */ 138, 4, 5, 6, 7, 138, 90, 138, 92, 93,
/* 790 */ 94, 95, 96, 137, 17, 83, 4, 5, 6, 7,
/* 800 */ 105, 138, 90, 138, 92, 93, 94, 95, 96, 138,
/* 810 */ 115, 116, 117, 138, 105, 138, 107, 25, 138, 105,
/* 820 */ 138, 107, 113, 114, 115, 116, 117, 113, 114, 115,
/* 830 */ 116, 117, 137, 105, 138, 107, 138, 105, 138, 138,
/* 840 */ 138, 113, 114, 115, 116, 117, 137, 115, 116, 117,
/* 850 */ 138, 137, 105, 138, 107, 138, 138, 138, 27, 28,
/* 860 */ 113, 114, 115, 116, 117, 137, 105, 138, 107, 137,
/* 870 */ 138, 105, 138, 107, 113, 114, 115, 116, 117, 113,
/* 880 */ 114, 115, 116, 117, 137, 105, 138, 107, 4, 5,
/* 890 */ 6, 7, 138, 113, 114, 115, 116, 117, 137, 138,
/* 900 */ 138, 17, 138, 137, 138, 138, 138, 138, 138, 138,
/* 910 */ 105, 138, 107, 138, 138, 84, 138, 137, 113, 114,
/* 920 */ 115, 116, 117, 92, 138, 105, 138, 107, 138, 138,
/* 930 */ 138, 138, 138, 113, 114, 115, 116, 117, 138, 105,
/* 940 */ 138, 107, 137, 138, 138, 138, 138, 113, 114, 115,
/* 950 */ 116, 117, 105, 138, 107, 138, 138, 137, 138, 138,
/* 960 */ 113, 114, 115, 116, 117, 138, 105, 138, 107, 138,
/* 970 */ 138, 137, 138, 138, 113, 114, 115, 116, 117, 105,
/* 980 */ 138, 107, 138, 138, 137, 138, 138, 113, 114, 115,
/* 990 */ 116, 117, 138, 105, 138, 107, 138, 138, 137, 138,
/* 1000 */ 138, 113, 114, 115, 116, 117, 138, 138, 138, 138,
/* 1010 */ 138, 137, 138, 138, 138, 138, 138, 138, 105, 138,
/* 1020 */ 107, 138, 138, 138, 138, 137, 113, 114, 115, 116,
/* 1030 */ 117, 105, 138, 107, 138, 138, 138, 138, 138, 113,
/* 1040 */ 114, 115, 116, 117, 138, 105, 138, 107, 138, 138,
/* 1050 */ 137, 138, 138, 113, 114, 115, 116, 117, 105, 138,
/* 1060 */ 107, 138, 138, 137, 138, 138, 113, 114, 115, 116,
/* 1070 */ 117, 138, 105, 138, 138, 138, 138, 137, 138, 138,
/* 1080 */ 138, 105, 115, 116, 117, 138, 138, 138, 138, 122,
/* 1090 */ 137, 115, 116, 117, 138, 105, 129, 138, 131, 105,
/* 1100 */ 138, 138, 138, 138, 137, 115, 116, 117, 105, 115,
/* 1110 */ 116, 117, 138, 137, 138, 138, 138, 138, 115, 116,
/* 1120 */ 117, 138, 138, 138, 105, 138, 138, 137, 138, 138,
/* 1130 */ 138, 137, 105, 138, 115, 116, 117, 105, 138, 138,
/* 1140 */ 137, 138, 115, 116, 117, 138, 138, 115, 116, 117,
/* 1150 */ 138, 105, 138, 138, 138, 138, 137, 138, 138, 138,
/* 1160 */ 105, 115, 116, 117, 137, 138, 105, 138, 138, 137,
/* 1170 */ 115, 116, 117, 138, 105, 138, 115, 116, 117, 138,
/* 1180 */ 138, 138, 138, 137, 115, 116, 117, 138, 138, 105,
/* 1190 */ 138, 138, 137, 138, 138, 138, 138, 105, 137, 115,
/* 1200 */ 116, 117, 105, 138, 138, 105, 137, 115, 116, 117,
/* 1210 */ 138, 138, 115, 116, 117, 115, 116, 117, 138, 138,
/* 1220 */ 138, 137, 138, 138, 138, 138, 138, 138, 138, 137,
/* 1230 */ 105, 138, 138, 105, 137, 138, 138, 137, 105, 138,
/* 1240 */ 115, 116, 117, 115, 116, 117, 105, 138, 115, 116,
/* 1250 */ 117, 138, 138, 138, 138, 138, 115, 116, 117, 138,
/* 1260 */ 138, 138, 137, 138, 138, 137, 138, 138, 138, 105,
/* 1270 */ 137, 138, 105, 138, 138, 138, 138, 138, 137, 115,
/* 1280 */ 116, 117, 115, 116, 117, 138, 138, 138, 138, 138,
/* 1290 */ 138, 138, 138, 138, 138, 138, 138, 138, 138, 138,
/* 1300 */ 138, 137, 138, 138, 137, 101, 101, 101, 101, 101,
/* 1310 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
/* 1320 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
/* 1330 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
/* 1340 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
/* 1350 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
/* 1360 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
/* 1370 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
/* 1380 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
/* 1390 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
/* 1400 */ 101, 101, 101, 101, 101, 101,
};
#define YY_SHIFT_COUNT (163)
#define YY_SHIFT_MIN (0)
#define YY_SHIFT_MAX (884)
static const unsigned short int yy_shift_ofst[] = {
/* 0 */ 471, 129, 163, 229, 229, 229, 229, 229, 229, 229,
/* 10 */ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229,
/* 20 */ 229, 229, 229, 229, 229, 229, 229, 358, 526, 638,
/* 30 */ 358, 471, 542, 542, 0, 65, 471, 670, 638, 670,
/* 40 */ 504, 504, 504, 574, 600, 638, 638, 638, 638, 638,
/* 50 */ 638, 696, 638, 638, 712, 638, 638, 638, 638, 638,
/* 60 */ 638, 638, 638, 638, 638, 254, 162, 162, 162, 162,
/* 70 */ 162, 435, 263, 332, 401, 464, 464, 205, 48, 1305,
/* 80 */ 1305, 1305, 1305, 132, 132, 528, 578, 62, 131, 341,
/* 90 */ 423, 293, 7, 469, 622, 698, 792, 777, 884, 506,
/* 100 */ 506, 506, 63, 506, 506, 506, 831, 506, 327, 103,
/* 110 */ 174, 187, 6, 66, 141, 236, 236, 244, 250, 140,
/* 120 */ 211, 381, 147, 178, 203, 216, 212, 219, 206, 223,
/* 130 */ 231, 238, 234, 274, 284, 278, 257, 324, 279, 281,
/* 140 */ 290, 294, 373, 380, 383, 345, 386, 387, 389, 301,
/* 150 */ 321, 377, 301, 399, 400, 403, 406, 396, 402, 407,
/* 160 */ 404, 344, 436, 427,
};
#define YY_REDUCE_COUNT (82)
#define YY_REDUCE_MIN (-132)
#define YY_REDUCE_MAX (1167)
static const short yy_reduce_ofst[] = {
/* 0 */ -76, -98, -33, 35, 176, 213, 280, 451, 633, 709,
/* 10 */ 714, 728, 747, 761, 766, 780, 805, 820, 834, 847,
/* 20 */ 861, 874, 888, 913, 926, 940, 953, 54, 340, 967,
/* 30 */ 305, -11, 539, 620, 76, 76, 249, 639, 455, 572,
/* 40 */ 251, 656, 695, 732, 976, 990, 994, 1003, 1019, 1027,
/* 50 */ 1032, 1046, 1055, 1061, 1069, 1084, 1092, 1097, 1100, 1125,
/* 60 */ 1128, 1133, 1141, 1164, 1167, 82, -114, -39, -20, 30,
/* 70 */ 169, 83, -132, -132, -132, -99, -77, -62, -40, 25,
/* 80 */ 40, 59, 80,
};
static const YYACTIONTYPE yy_default[] = {
/* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443,
/* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
/* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576,
/* 30 */ 443, 449, 580, 485, 581, 581, 449, 443, 443, 443,
/* 40 */ 443, 443, 443, 443, 443, 443, 443, 443, 477, 443,
|
| ︙ | ︙ | |||
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 |
0, /* CODEBLOCK => nothing */
0, /* FILL => nothing */
0, /* COLOR => nothing */
0, /* THICKNESS => nothing */
0, /* PRINT => nothing */
0, /* STRING => nothing */
0, /* COMMA => nothing */
0, /* CLASSNAME => nothing */
0, /* LB => nothing */
0, /* RB => nothing */
0, /* UP => nothing */
0, /* DOWN => nothing */
0, /* LEFT => nothing */
0, /* RIGHT => nothing */
| > | 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 |
0, /* CODEBLOCK => nothing */
0, /* FILL => nothing */
0, /* COLOR => nothing */
0, /* THICKNESS => nothing */
0, /* PRINT => nothing */
0, /* STRING => nothing */
0, /* COMMA => nothing */
0, /* ISODATE => nothing */
0, /* CLASSNAME => nothing */
0, /* LB => nothing */
0, /* RB => nothing */
0, /* UP => nothing */
0, /* DOWN => nothing */
0, /* LEFT => nothing */
0, /* RIGHT => nothing */
|
| ︙ | ︙ | |||
1345 1346 1347 1348 1349 1350 1351 | /* 19 */ "CODEBLOCK", /* 20 */ "FILL", /* 21 */ "COLOR", /* 22 */ "THICKNESS", /* 23 */ "PRINT", /* 24 */ "STRING", /* 25 */ "COMMA", | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | < < | | | | | | | | | | | | | | | | | | | | | | | | | > > | 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 |
/* 19 */ "CODEBLOCK",
/* 20 */ "FILL",
/* 21 */ "COLOR",
/* 22 */ "THICKNESS",
/* 23 */ "PRINT",
/* 24 */ "STRING",
/* 25 */ "COMMA",
/* 26 */ "ISODATE",
/* 27 */ "CLASSNAME",
/* 28 */ "LB",
/* 29 */ "RB",
/* 30 */ "UP",
/* 31 */ "DOWN",
/* 32 */ "LEFT",
/* 33 */ "RIGHT",
/* 34 */ "CLOSE",
/* 35 */ "CHOP",
/* 36 */ "FROM",
/* 37 */ "TO",
/* 38 */ "THEN",
/* 39 */ "HEADING",
/* 40 */ "GO",
/* 41 */ "AT",
/* 42 */ "WITH",
/* 43 */ "SAME",
/* 44 */ "AS",
/* 45 */ "FIT",
/* 46 */ "BEHIND",
/* 47 */ "UNTIL",
/* 48 */ "EVEN",
/* 49 */ "DOT_E",
/* 50 */ "HEIGHT",
/* 51 */ "WIDTH",
/* 52 */ "RADIUS",
/* 53 */ "DIAMETER",
/* 54 */ "DOTTED",
/* 55 */ "DASHED",
/* 56 */ "CW",
/* 57 */ "CCW",
/* 58 */ "LARROW",
/* 59 */ "RARROW",
/* 60 */ "LRARROW",
/* 61 */ "INVIS",
/* 62 */ "THICK",
/* 63 */ "THIN",
/* 64 */ "SOLID",
/* 65 */ "CENTER",
/* 66 */ "LJUST",
/* 67 */ "RJUST",
/* 68 */ "ABOVE",
/* 69 */ "BELOW",
/* 70 */ "ITALIC",
/* 71 */ "BOLD",
/* 72 */ "MONO",
/* 73 */ "ALIGNED",
/* 74 */ "BIG",
/* 75 */ "SMALL",
/* 76 */ "AND",
/* 77 */ "LT",
/* 78 */ "GT",
/* 79 */ "ON",
/* 80 */ "WAY",
/* 81 */ "BETWEEN",
/* 82 */ "THE",
/* 83 */ "NTH",
/* 84 */ "VERTEX",
/* 85 */ "TOP",
/* 86 */ "BOTTOM",
/* 87 */ "START",
/* 88 */ "END",
/* 89 */ "IN",
/* 90 */ "THIS",
/* 91 */ "DOT_U",
/* 92 */ "LAST",
/* 93 */ "NUMBER",
/* 94 */ "FUNC1",
/* 95 */ "FUNC2",
/* 96 */ "DIST",
/* 97 */ "DOT_XY",
/* 98 */ "X",
/* 99 */ "Y",
/* 100 */ "DOT_L",
/* 101 */ "statement_list",
/* 102 */ "statement",
/* 103 */ "unnamed_statement",
/* 104 */ "basetype",
/* 105 */ "expr",
/* 106 */ "numproperty",
/* 107 */ "edge",
/* 108 */ "isodate",
/* 109 */ "direction",
/* 110 */ "dashproperty",
/* 111 */ "colorproperty",
/* 112 */ "locproperty",
/* 113 */ "position",
/* 114 */ "place",
/* 115 */ "object",
/* 116 */ "objectname",
/* 117 */ "nth",
/* 118 */ "textposition",
/* 119 */ "rvalue",
/* 120 */ "lvalue",
/* 121 */ "even",
/* 122 */ "relexpr",
/* 123 */ "optrelexpr",
/* 124 */ "document",
/* 125 */ "print",
/* 126 */ "prlist",
/* 127 */ "pritem",
/* 128 */ "prsep",
/* 129 */ "attribute_list",
/* 130 */ "savelist",
/* 131 */ "alist",
/* 132 */ "attribute",
/* 133 */ "go",
/* 134 */ "boolproperty",
/* 135 */ "withclause",
/* 136 */ "between",
/* 137 */ "place2",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
#ifndef NDEBUG
/* For tracing reduce actions, the names of all rules are required.
*/
static const char *const yyRuleName[] = {
|
| ︙ | ︙ | |||
1741 1742 1743 1744 1745 1746 1747 |
** being destroyed before it is finished parsing.
**
** 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 ***************************************/
| | | | | | | | | | | | 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 |
** being destroyed before it is finished parsing.
**
** 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 101: /* statement_list */
{
#line 524 "pikchr.y"
pik_elist_free(p,(yypminor->yy23));
#line 1805 "pikchr.c"
}
break;
case 102: /* statement */
case 103: /* unnamed_statement */
case 104: /* basetype */
{
#line 526 "pikchr.y"
pik_elem_free(p,(yypminor->yy54));
#line 1814 "pikchr.c"
}
break;
/********* End destructor definitions *****************************************/
default: break; /* If no destructor action specified: do nothing */
}
}
|
| ︙ | ︙ | |||
1989 1990 1991 1992 1993 1994 1995 |
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 ******************************************/
| | | | 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 |
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 559 "pikchr.y"
pik_error(p, 0, "parser stack overflow");
#line 2052 "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
|
| ︙ | ︙ | |||
2058 2059 2060 2061 2062 2063 2064 |
yytos->minor.yy0 = yyMinor;
yyTraceShift(yypParser, yyNewState, "Shift");
}
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 |
yytos->minor.yy0 = yyMinor;
yyTraceShift(yypParser, yyNewState, "Shift");
}
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
124, /* (0) document ::= statement_list */
101, /* (1) statement_list ::= statement */
101, /* (2) statement_list ::= statement_list EOL statement */
102, /* (3) statement ::= */
102, /* (4) statement ::= direction */
102, /* (5) statement ::= lvalue ASSIGN rvalue */
102, /* (6) statement ::= PLACENAME COLON unnamed_statement */
102, /* (7) statement ::= PLACENAME COLON position */
102, /* (8) statement ::= unnamed_statement */
102, /* (9) statement ::= print prlist */
102, /* (10) statement ::= ASSERT LP expr EQ expr RP */
102, /* (11) statement ::= ASSERT LP position EQ position RP */
102, /* (12) statement ::= DEFINE ID CODEBLOCK */
119, /* (13) rvalue ::= PLACENAME */
127, /* (14) pritem ::= FILL */
127, /* (15) pritem ::= COLOR */
127, /* (16) pritem ::= THICKNESS */
127, /* (17) pritem ::= rvalue */
127, /* (18) pritem ::= STRING */
128, /* (19) prsep ::= COMMA */
103, /* (20) unnamed_statement ::= basetype attribute_list */
104, /* (21) basetype ::= CLASSNAME */
104, /* (22) basetype ::= STRING textposition */
104, /* (23) basetype ::= LB savelist statement_list RB */
130, /* (24) savelist ::= */
122, /* (25) relexpr ::= expr */
122, /* (26) relexpr ::= expr PERCENT */
123, /* (27) optrelexpr ::= */
129, /* (28) attribute_list ::= relexpr alist */
132, /* (29) attribute ::= numproperty relexpr */
132, /* (30) attribute ::= dashproperty expr */
132, /* (31) attribute ::= dashproperty */
132, /* (32) attribute ::= colorproperty rvalue */
132, /* (33) attribute ::= go direction optrelexpr */
132, /* (34) attribute ::= go direction even position */
132, /* (35) attribute ::= CLOSE */
132, /* (36) attribute ::= CHOP */
132, /* (37) attribute ::= FROM position */
132, /* (38) attribute ::= TO position */
132, /* (39) attribute ::= THEN */
132, /* (40) attribute ::= THEN optrelexpr HEADING expr */
132, /* (41) attribute ::= THEN optrelexpr EDGEPT */
132, /* (42) attribute ::= GO optrelexpr HEADING expr */
132, /* (43) attribute ::= GO optrelexpr EDGEPT */
132, /* (44) attribute ::= AT position */
132, /* (45) attribute ::= SAME */
132, /* (46) attribute ::= SAME AS object */
132, /* (47) attribute ::= STRING textposition */
132, /* (48) attribute ::= FIT */
132, /* (49) attribute ::= BEHIND object */
135, /* (50) withclause ::= DOT_E edge AT position */
135, /* (51) withclause ::= edge AT position */
106, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
134, /* (53) boolproperty ::= CW */
134, /* (54) boolproperty ::= CCW */
134, /* (55) boolproperty ::= LARROW */
134, /* (56) boolproperty ::= RARROW */
134, /* (57) boolproperty ::= LRARROW */
134, /* (58) boolproperty ::= INVIS */
134, /* (59) boolproperty ::= THICK */
134, /* (60) boolproperty ::= THIN */
134, /* (61) boolproperty ::= SOLID */
118, /* (62) textposition ::= */
118, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
113, /* (64) position ::= expr COMMA expr */
113, /* (65) position ::= place PLUS expr COMMA expr */
113, /* (66) position ::= place MINUS expr COMMA expr */
113, /* (67) position ::= place PLUS LP expr COMMA expr RP */
113, /* (68) position ::= place MINUS LP expr COMMA expr RP */
113, /* (69) position ::= LP position COMMA position RP */
113, /* (70) position ::= LP position RP */
113, /* (71) position ::= expr between position AND position */
113, /* (72) position ::= expr LT position COMMA position GT */
113, /* (73) position ::= expr ABOVE position */
113, /* (74) position ::= expr BELOW position */
113, /* (75) position ::= expr LEFT OF position */
113, /* (76) position ::= expr RIGHT OF position */
113, /* (77) position ::= expr ON HEADING EDGEPT OF position */
113, /* (78) position ::= expr HEADING EDGEPT OF position */
113, /* (79) position ::= expr EDGEPT OF position */
113, /* (80) position ::= expr ON HEADING expr FROM position */
113, /* (81) position ::= expr HEADING expr FROM position */
114, /* (82) place ::= edge OF object */
137, /* (83) place2 ::= object */
137, /* (84) place2 ::= object DOT_E edge */
137, /* (85) place2 ::= NTH VERTEX OF object */
115, /* (86) object ::= nth */
115, /* (87) object ::= nth OF|IN object */
116, /* (88) objectname ::= THIS */
116, /* (89) objectname ::= PLACENAME */
116, /* (90) objectname ::= objectname DOT_U PLACENAME */
117, /* (91) nth ::= NTH CLASSNAME */
117, /* (92) nth ::= NTH LAST CLASSNAME */
117, /* (93) nth ::= LAST CLASSNAME */
117, /* (94) nth ::= LAST */
117, /* (95) nth ::= NTH LB RB */
117, /* (96) nth ::= NTH LAST LB RB */
117, /* (97) nth ::= LAST LB RB */
105, /* (98) expr ::= expr PLUS expr */
105, /* (99) expr ::= expr MINUS expr */
105, /* (100) expr ::= expr STAR expr */
105, /* (101) expr ::= expr SLASH expr */
105, /* (102) expr ::= MINUS expr */
105, /* (103) expr ::= PLUS expr */
105, /* (104) expr ::= LP expr RP */
105, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */
105, /* (106) expr ::= NUMBER */
105, /* (107) expr ::= ID */
105, /* (108) expr ::= FUNC1 LP expr RP */
105, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */
105, /* (110) expr ::= DIST LP position COMMA position RP */
105, /* (111) expr ::= place2 DOT_XY X */
105, /* (112) expr ::= place2 DOT_XY Y */
105, /* (113) expr ::= object DOT_L numproperty */
105, /* (114) expr ::= object DOT_L dashproperty */
105, /* (115) expr ::= object DOT_L colorproperty */
120, /* (116) lvalue ::= ID */
120, /* (117) lvalue ::= FILL */
120, /* (118) lvalue ::= COLOR */
120, /* (119) lvalue ::= THICKNESS */
119, /* (120) rvalue ::= expr */
125, /* (121) print ::= PRINT */
126, /* (122) prlist ::= pritem */
126, /* (123) prlist ::= prlist prsep pritem */
109, /* (124) direction ::= UP */
109, /* (125) direction ::= DOWN */
109, /* (126) direction ::= LEFT */
109, /* (127) direction ::= RIGHT */
123, /* (128) optrelexpr ::= relexpr */
129, /* (129) attribute_list ::= alist */
131, /* (130) alist ::= */
131, /* (131) alist ::= alist attribute */
132, /* (132) attribute ::= boolproperty */
132, /* (133) attribute ::= WITH withclause */
133, /* (134) go ::= GO */
133, /* (135) go ::= */
121, /* (136) even ::= UNTIL EVEN WITH */
121, /* (137) even ::= EVEN WITH */
110, /* (138) dashproperty ::= DOTTED */
110, /* (139) dashproperty ::= DASHED */
111, /* (140) colorproperty ::= FILL */
111, /* (141) colorproperty ::= COLOR */
113, /* (142) position ::= place */
136, /* (143) between ::= WAY BETWEEN */
136, /* (144) between ::= BETWEEN */
136, /* (145) between ::= OF THE WAY BETWEEN */
114, /* (146) place ::= place2 */
107, /* (147) edge ::= CENTER */
107, /* (148) edge ::= EDGEPT */
107, /* (149) edge ::= TOP */
107, /* (150) edge ::= BOTTOM */
107, /* (151) edge ::= START */
107, /* (152) edge ::= END */
107, /* (153) edge ::= RIGHT */
107, /* (154) edge ::= LEFT */
115, /* (155) object ::= objectname */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
** of symbols on the right-hand side of that rule. */
static const signed char yyRuleInfoNRhs[] = {
-1, /* (0) document ::= statement_list */
-1, /* (1) statement_list ::= statement */
|
| ︙ | ︙ | |||
2417 2418 2419 2420 2421 2422 2423 |
** { ... } // User supplied code
** #line <lineno> <thisfile>
** break;
*/
/********** Begin reduce actions **********************************************/
YYMINORTYPE yylhsminor;
case 0: /* document ::= statement_list */
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 |
** { ... } // User supplied code
** #line <lineno> <thisfile>
** break;
*/
/********** Begin reduce actions **********************************************/
YYMINORTYPE yylhsminor;
case 0: /* document ::= statement_list */
#line 563 "pikchr.y"
{pik_render(p,yymsp[0].minor.yy23);}
#line 2479 "pikchr.c"
break;
case 1: /* statement_list ::= statement */
#line 566 "pikchr.y"
{ yylhsminor.yy23 = pik_elist_append(p,0,yymsp[0].minor.yy54); }
#line 2484 "pikchr.c"
yymsp[0].minor.yy23 = yylhsminor.yy23;
break;
case 2: /* statement_list ::= statement_list EOL statement */
#line 568 "pikchr.y"
{ yylhsminor.yy23 = pik_elist_append(p,yymsp[-2].minor.yy23,yymsp[0].minor.yy54); }
#line 2490 "pikchr.c"
yymsp[-2].minor.yy23 = yylhsminor.yy23;
break;
case 3: /* statement ::= */
#line 571 "pikchr.y"
{ yymsp[1].minor.yy54 = 0; }
#line 2496 "pikchr.c"
break;
case 4: /* statement ::= direction */
#line 572 "pikchr.y"
{ pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy54=0; }
#line 2501 "pikchr.c"
yymsp[0].minor.yy54 = yylhsminor.yy54;
break;
case 5: /* statement ::= lvalue ASSIGN rvalue */
#line 573 "pikchr.y"
{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy129,&yymsp[-1].minor.yy0); yylhsminor.yy54=0;}
#line 2507 "pikchr.c"
yymsp[-2].minor.yy54 = yylhsminor.yy54;
break;
case 6: /* statement ::= PLACENAME COLON unnamed_statement */
#line 575 "pikchr.y"
{ yylhsminor.yy54 = yymsp[0].minor.yy54; pik_elem_setname(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0); }
#line 2513 "pikchr.c"
yymsp[-2].minor.yy54 = yylhsminor.yy54;
break;
case 7: /* statement ::= PLACENAME COLON position */
#line 577 "pikchr.y"
{ yylhsminor.yy54 = pik_elem_new(p,0,0,0);
if(yylhsminor.yy54){ yylhsminor.yy54->ptAt = yymsp[0].minor.yy187; pik_elem_setname(p,yylhsminor.yy54,&yymsp[-2].minor.yy0); }}
#line 2520 "pikchr.c"
yymsp[-2].minor.yy54 = yylhsminor.yy54;
break;
case 8: /* statement ::= unnamed_statement */
#line 579 "pikchr.y"
{yylhsminor.yy54 = yymsp[0].minor.yy54;}
#line 2526 "pikchr.c"
yymsp[0].minor.yy54 = yylhsminor.yy54;
break;
case 9: /* statement ::= print prlist */
#line 580 "pikchr.y"
{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy54=0;}
#line 2532 "pikchr.c"
break;
case 10: /* statement ::= ASSERT LP expr EQ expr RP */
#line 585 "pikchr.y"
{yymsp[-5].minor.yy54=pik_assert(p,yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy129);}
#line 2537 "pikchr.c"
break;
case 11: /* statement ::= ASSERT LP position EQ position RP */
#line 587 "pikchr.y"
{yymsp[-5].minor.yy54=pik_position_assert(p,&yymsp[-3].minor.yy187,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy187);}
#line 2542 "pikchr.c"
break;
case 12: /* statement ::= DEFINE ID CODEBLOCK */
#line 588 "pikchr.y"
{yymsp[-2].minor.yy54=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
#line 2547 "pikchr.c"
break;
case 13: /* rvalue ::= PLACENAME */
#line 599 "pikchr.y"
{yylhsminor.yy129 = pik_lookup_color(p,&yymsp[0].minor.yy0);}
#line 2552 "pikchr.c"
yymsp[0].minor.yy129 = yylhsminor.yy129;
break;
case 14: /* pritem ::= FILL */
case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15);
case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16);
#line 604 "pikchr.y"
{pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));}
#line 2560 "pikchr.c"
break;
case 17: /* pritem ::= rvalue */
#line 607 "pikchr.y"
{pik_append_num(p,"",yymsp[0].minor.yy129);}
#line 2565 "pikchr.c"
break;
case 18: /* pritem ::= STRING */
#line 608 "pikchr.y"
{pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);}
#line 2570 "pikchr.c"
break;
case 19: /* prsep ::= COMMA */
#line 609 "pikchr.y"
{pik_append(p, " ", 1);}
#line 2575 "pikchr.c"
break;
case 20: /* unnamed_statement ::= basetype attribute_list */
#line 614 "pikchr.y"
{yylhsminor.yy54 = yymsp[-1].minor.yy54; pik_after_adding_attributes(p,yylhsminor.yy54);}
#line 2580 "pikchr.c"
yymsp[-1].minor.yy54 = yylhsminor.yy54;
break;
case 21: /* basetype ::= CLASSNAME */
#line 616 "pikchr.y"
{yylhsminor.yy54 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); }
#line 2586 "pikchr.c"
yymsp[0].minor.yy54 = yylhsminor.yy54;
break;
case 22: /* basetype ::= STRING textposition */
#line 618 "pikchr.y"
{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy272; yylhsminor.yy54 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); }
#line 2592 "pikchr.c"
yymsp[-1].minor.yy54 = yylhsminor.yy54;
break;
case 23: /* basetype ::= LB savelist statement_list RB */
#line 620 "pikchr.y"
{ p->list = yymsp[-2].minor.yy23; yymsp[-3].minor.yy54 = pik_elem_new(p,0,0,yymsp[-1].minor.yy23); if(yymsp[-3].minor.yy54) yymsp[-3].minor.yy54->errTok = yymsp[0].minor.yy0; }
#line 2598 "pikchr.c"
break;
case 24: /* savelist ::= */
#line 625 "pikchr.y"
{yymsp[1].minor.yy23 = p->list; p->list = 0;}
#line 2603 "pikchr.c"
break;
case 25: /* relexpr ::= expr */
#line 632 "pikchr.y"
{yylhsminor.yy28.rAbs = yymsp[0].minor.yy129; yylhsminor.yy28.rRel = 0;}
#line 2608 "pikchr.c"
yymsp[0].minor.yy28 = yylhsminor.yy28;
break;
case 26: /* relexpr ::= expr PERCENT */
#line 633 "pikchr.y"
{yylhsminor.yy28.rAbs = 0; yylhsminor.yy28.rRel = yymsp[-1].minor.yy129/100;}
#line 2614 "pikchr.c"
yymsp[-1].minor.yy28 = yylhsminor.yy28;
break;
case 27: /* optrelexpr ::= */
#line 635 "pikchr.y"
{yymsp[1].minor.yy28.rAbs = 0; yymsp[1].minor.yy28.rRel = 1.0;}
#line 2620 "pikchr.c"
break;
case 28: /* attribute_list ::= relexpr alist */
#line 637 "pikchr.y"
{pik_add_direction(p,0,&yymsp[-1].minor.yy28);}
#line 2625 "pikchr.c"
break;
case 29: /* attribute ::= numproperty relexpr */
#line 641 "pikchr.y"
{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28); }
#line 2630 "pikchr.c"
break;
case 30: /* attribute ::= dashproperty expr */
#line 642 "pikchr.y"
{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy129); }
#line 2635 "pikchr.c"
break;
case 31: /* attribute ::= dashproperty */
#line 643 "pikchr.y"
{ pik_set_dashed(p,&yymsp[0].minor.yy0,0); }
#line 2640 "pikchr.c"
break;
case 32: /* attribute ::= colorproperty rvalue */
#line 644 "pikchr.y"
{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129); }
#line 2645 "pikchr.c"
break;
case 33: /* attribute ::= go direction optrelexpr */
#line 645 "pikchr.y"
{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28);}
#line 2650 "pikchr.c"
break;
case 34: /* attribute ::= go direction even position */
#line 646 "pikchr.y"
{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187);}
#line 2655 "pikchr.c"
break;
case 35: /* attribute ::= CLOSE */
#line 647 "pikchr.y"
{ pik_close_path(p,&yymsp[0].minor.yy0); }
#line 2660 "pikchr.c"
break;
case 36: /* attribute ::= CHOP */
#line 648 "pikchr.y"
{ p->cur->bChop = 1; }
#line 2665 "pikchr.c"
break;
case 37: /* attribute ::= FROM position */
#line 649 "pikchr.y"
{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); }
#line 2670 "pikchr.c"
break;
case 38: /* attribute ::= TO position */
#line 650 "pikchr.y"
{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); }
#line 2675 "pikchr.c"
break;
case 39: /* attribute ::= THEN */
#line 651 "pikchr.y"
{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
#line 2680 "pikchr.c"
break;
case 40: /* attribute ::= THEN optrelexpr HEADING expr */
case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42);
#line 653 "pikchr.y"
{pik_move_hdg(p,&yymsp[-2].minor.yy28,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129,0,&yymsp[-3].minor.yy0);}
#line 2686 "pikchr.c"
break;
case 41: /* attribute ::= THEN optrelexpr EDGEPT */
case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43);
#line 654 "pikchr.y"
{pik_move_hdg(p,&yymsp[-1].minor.yy28,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);}
#line 2692 "pikchr.c"
break;
case 44: /* attribute ::= AT position */
#line 659 "pikchr.y"
{ pik_set_at(p,0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); }
#line 2697 "pikchr.c"
break;
case 45: /* attribute ::= SAME */
#line 661 "pikchr.y"
{pik_same(p,0,&yymsp[0].minor.yy0);}
#line 2702 "pikchr.c"
break;
case 46: /* attribute ::= SAME AS object */
#line 662 "pikchr.y"
{pik_same(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);}
#line 2707 "pikchr.c"
break;
case 47: /* attribute ::= STRING textposition */
#line 663 "pikchr.y"
{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy272);}
#line 2712 "pikchr.c"
break;
case 48: /* attribute ::= FIT */
#line 664 "pikchr.y"
{pik_size_to_fit(p,0,&yymsp[0].minor.yy0,3); }
#line 2717 "pikchr.c"
break;
case 49: /* attribute ::= BEHIND object */
#line 665 "pikchr.y"
{pik_behind(p,yymsp[0].minor.yy54);}
#line 2722 "pikchr.c"
break;
case 50: /* withclause ::= DOT_E edge AT position */
case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51);
#line 673 "pikchr.y"
{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); }
#line 2728 "pikchr.c"
break;
case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
#line 677 "pikchr.y"
{yylhsminor.yy0 = yymsp[0].minor.yy0;}
#line 2733 "pikchr.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 53: /* boolproperty ::= CW */
#line 688 "pikchr.y"
{p->cur->cw = 1;}
#line 2739 "pikchr.c"
break;
case 54: /* boolproperty ::= CCW */
#line 689 "pikchr.y"
{p->cur->cw = 0;}
#line 2744 "pikchr.c"
break;
case 55: /* boolproperty ::= LARROW */
#line 690 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=0; }
#line 2749 "pikchr.c"
break;
case 56: /* boolproperty ::= RARROW */
#line 691 "pikchr.y"
{p->cur->larrow=0; p->cur->rarrow=1; }
#line 2754 "pikchr.c"
break;
case 57: /* boolproperty ::= LRARROW */
#line 692 "pikchr.y"
{p->cur->larrow=1; p->cur->rarrow=1; }
#line 2759 "pikchr.c"
break;
case 58: /* boolproperty ::= INVIS */
#line 693 "pikchr.y"
{p->cur->sw = -0.00001;}
#line 2764 "pikchr.c"
break;
case 59: /* boolproperty ::= THICK */
#line 694 "pikchr.y"
{p->cur->sw *= 1.5;}
#line 2769 "pikchr.c"
break;
case 60: /* boolproperty ::= THIN */
#line 695 "pikchr.y"
{p->cur->sw *= 0.67;}
#line 2774 "pikchr.c"
break;
case 61: /* boolproperty ::= SOLID */
#line 696 "pikchr.y"
{p->cur->sw = pik_value(p,"thickness",9,0);
p->cur->dotted = p->cur->dashed = 0.0;}
#line 2780 "pikchr.c"
break;
case 62: /* textposition ::= */
#line 699 "pikchr.y"
{yymsp[1].minor.yy272 = 0;}
#line 2785 "pikchr.c"
break;
case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */
#line 702 "pikchr.y"
{yylhsminor.yy272 = (short int)pik_text_position(yymsp[-1].minor.yy272,&yymsp[0].minor.yy0);}
#line 2790 "pikchr.c"
yymsp[-1].minor.yy272 = yylhsminor.yy272;
break;
case 64: /* position ::= expr COMMA expr */
#line 705 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[0].minor.yy129;}
#line 2796 "pikchr.c"
yymsp[-2].minor.yy187 = yylhsminor.yy187;
break;
case 65: /* position ::= place PLUS expr COMMA expr */
#line 707 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-4].minor.yy187.x+yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y+yymsp[0].minor.yy129;}
#line 2802 "pikchr.c"
yymsp[-4].minor.yy187 = yylhsminor.yy187;
break;
case 66: /* position ::= place MINUS expr COMMA expr */
#line 708 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-4].minor.yy187.x-yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y-yymsp[0].minor.yy129;}
#line 2808 "pikchr.c"
yymsp[-4].minor.yy187 = yylhsminor.yy187;
break;
case 67: /* position ::= place PLUS LP expr COMMA expr RP */
#line 710 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-6].minor.yy187.x+yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y+yymsp[-1].minor.yy129;}
#line 2814 "pikchr.c"
yymsp[-6].minor.yy187 = yylhsminor.yy187;
break;
case 68: /* position ::= place MINUS LP expr COMMA expr RP */
#line 712 "pikchr.y"
{yylhsminor.yy187.x=yymsp[-6].minor.yy187.x-yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y-yymsp[-1].minor.yy129;}
#line 2820 "pikchr.c"
yymsp[-6].minor.yy187 = yylhsminor.yy187;
break;
case 69: /* position ::= LP position COMMA position RP */
#line 713 "pikchr.y"
{yymsp[-4].minor.yy187.x=yymsp[-3].minor.yy187.x; yymsp[-4].minor.yy187.y=yymsp[-1].minor.yy187.y;}
#line 2826 "pikchr.c"
break;
case 70: /* position ::= LP position RP */
#line 714 "pikchr.y"
{yymsp[-2].minor.yy187=yymsp[-1].minor.yy187;}
#line 2831 "pikchr.c"
break;
case 71: /* position ::= expr between position AND position */
#line 716 "pikchr.y"
{yylhsminor.yy187 = pik_position_between(yymsp[-4].minor.yy129,yymsp[-2].minor.yy187,yymsp[0].minor.yy187);}
#line 2836 "pikchr.c"
yymsp[-4].minor.yy187 = yylhsminor.yy187;
break;
case 72: /* position ::= expr LT position COMMA position GT */
#line 718 "pikchr.y"
{yylhsminor.yy187 = pik_position_between(yymsp[-5].minor.yy129,yymsp[-3].minor.yy187,yymsp[-1].minor.yy187);}
#line 2842 "pikchr.c"
yymsp[-5].minor.yy187 = yylhsminor.yy187;
break;
case 73: /* position ::= expr ABOVE position */
#line 719 "pikchr.y"
{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y += yymsp[-2].minor.yy129;}
#line 2848 "pikchr.c"
yymsp[-2].minor.yy187 = yylhsminor.yy187;
break;
case 74: /* position ::= expr BELOW position */
#line 720 "pikchr.y"
{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y -= yymsp[-2].minor.yy129;}
#line 2854 "pikchr.c"
yymsp[-2].minor.yy187 = yylhsminor.yy187;
break;
case 75: /* position ::= expr LEFT OF position */
#line 721 "pikchr.y"
{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x -= yymsp[-3].minor.yy129;}
#line 2860 "pikchr.c"
yymsp[-3].minor.yy187 = yylhsminor.yy187;
break;
case 76: /* position ::= expr RIGHT OF position */
#line 722 "pikchr.y"
{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x += yymsp[-3].minor.yy129;}
#line 2866 "pikchr.c"
yymsp[-3].minor.yy187 = yylhsminor.yy187;
break;
case 77: /* position ::= expr ON HEADING EDGEPT OF position */
#line 724 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-5].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);}
#line 2872 "pikchr.c"
yymsp[-5].minor.yy187 = yylhsminor.yy187;
break;
case 78: /* position ::= expr HEADING EDGEPT OF position */
#line 726 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-4].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);}
#line 2878 "pikchr.c"
yymsp[-4].minor.yy187 = yylhsminor.yy187;
break;
case 79: /* position ::= expr EDGEPT OF position */
#line 728 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);}
#line 2884 "pikchr.c"
yymsp[-3].minor.yy187 = yylhsminor.yy187;
break;
case 80: /* position ::= expr ON HEADING expr FROM position */
#line 730 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_angle(yymsp[-5].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);}
#line 2890 "pikchr.c"
yymsp[-5].minor.yy187 = yylhsminor.yy187;
break;
case 81: /* position ::= expr HEADING expr FROM position */
#line 732 "pikchr.y"
{yylhsminor.yy187 = pik_position_at_angle(yymsp[-4].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);}
#line 2896 "pikchr.c"
yymsp[-4].minor.yy187 = yylhsminor.yy187;
break;
case 82: /* place ::= edge OF object */
#line 744 "pikchr.y"
{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);}
#line 2902 "pikchr.c"
yymsp[-2].minor.yy187 = yylhsminor.yy187;
break;
case 83: /* place2 ::= object */
#line 745 "pikchr.y"
{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,0);}
#line 2908 "pikchr.c"
yymsp[0].minor.yy187 = yylhsminor.yy187;
break;
case 84: /* place2 ::= object DOT_E edge */
#line 746 "pikchr.y"
{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);}
#line 2914 "pikchr.c"
yymsp[-2].minor.yy187 = yylhsminor.yy187;
break;
case 85: /* place2 ::= NTH VERTEX OF object */
#line 747 "pikchr.y"
{yylhsminor.yy187 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy54);}
#line 2920 "pikchr.c"
yymsp[-3].minor.yy187 = yylhsminor.yy187;
break;
case 86: /* object ::= nth */
#line 759 "pikchr.y"
{yylhsminor.yy54 = pik_find_nth(p,0,&yymsp[0].minor.yy0);}
#line 2926 "pikchr.c"
yymsp[0].minor.yy54 = yylhsminor.yy54;
break;
case 87: /* object ::= nth OF|IN object */
#line 760 "pikchr.y"
{yylhsminor.yy54 = pik_find_nth(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);}
#line 2932 "pikchr.c"
yymsp[-2].minor.yy54 = yylhsminor.yy54;
break;
case 88: /* objectname ::= THIS */
#line 762 "pikchr.y"
{yymsp[0].minor.yy54 = p->cur;}
#line 2938 "pikchr.c"
break;
case 89: /* objectname ::= PLACENAME */
#line 763 "pikchr.y"
{yylhsminor.yy54 = pik_find_byname(p,0,&yymsp[0].minor.yy0);}
#line 2943 "pikchr.c"
yymsp[0].minor.yy54 = yylhsminor.yy54;
break;
case 90: /* objectname ::= objectname DOT_U PLACENAME */
#line 765 "pikchr.y"
{yylhsminor.yy54 = pik_find_byname(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);}
#line 2949 "pikchr.c"
yymsp[-2].minor.yy54 = yylhsminor.yy54;
break;
case 91: /* nth ::= NTH CLASSNAME */
#line 767 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); }
#line 2955 "pikchr.c"
yymsp[-1].minor.yy0 = yylhsminor.yy0;
break;
case 92: /* nth ::= NTH LAST CLASSNAME */
#line 768 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); }
#line 2961 "pikchr.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 93: /* nth ::= LAST CLASSNAME */
#line 769 "pikchr.y"
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
#line 2967 "pikchr.c"
break;
case 94: /* nth ::= LAST */
#line 770 "pikchr.y"
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
#line 2972 "pikchr.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 95: /* nth ::= NTH LB RB */
#line 771 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);}
#line 2978 "pikchr.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 96: /* nth ::= NTH LAST LB RB */
#line 772 "pikchr.y"
{yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);}
#line 2984 "pikchr.c"
yymsp[-3].minor.yy0 = yylhsminor.yy0;
break;
case 97: /* nth ::= LAST LB RB */
#line 773 "pikchr.y"
{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
#line 2990 "pikchr.c"
break;
case 98: /* expr ::= expr PLUS expr */
#line 775 "pikchr.y"
{yylhsminor.yy129=yymsp[-2].minor.yy129+yymsp[0].minor.yy129;}
#line 2995 "pikchr.c"
yymsp[-2].minor.yy129 = yylhsminor.yy129;
break;
case 99: /* expr ::= expr MINUS expr */
#line 776 "pikchr.y"
{yylhsminor.yy129=yymsp[-2].minor.yy129-yymsp[0].minor.yy129;}
#line 3001 "pikchr.c"
yymsp[-2].minor.yy129 = yylhsminor.yy129;
break;
case 100: /* expr ::= expr STAR expr */
#line 777 "pikchr.y"
{yylhsminor.yy129=yymsp[-2].minor.yy129*yymsp[0].minor.yy129;}
#line 3007 "pikchr.c"
yymsp[-2].minor.yy129 = yylhsminor.yy129;
break;
case 101: /* expr ::= expr SLASH expr */
#line 778 "pikchr.y"
{
if( yymsp[0].minor.yy129==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy129 = 0.0; }
else{ yylhsminor.yy129 = yymsp[-2].minor.yy129/yymsp[0].minor.yy129; }
}
#line 3016 "pikchr.c"
yymsp[-2].minor.yy129 = yylhsminor.yy129;
break;
case 102: /* expr ::= MINUS expr */
#line 782 "pikchr.y"
{yymsp[-1].minor.yy129=-yymsp[0].minor.yy129;}
#line 3022 "pikchr.c"
break;
case 103: /* expr ::= PLUS expr */
#line 783 "pikchr.y"
{yymsp[-1].minor.yy129=yymsp[0].minor.yy129;}
#line 3027 "pikchr.c"
break;
case 104: /* expr ::= LP expr RP */
#line 784 "pikchr.y"
{yymsp[-2].minor.yy129=yymsp[-1].minor.yy129;}
#line 3032 "pikchr.c"
break;
case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */
#line 785 "pikchr.y"
{yymsp[-2].minor.yy129=pik_get_var(p,&yymsp[-1].minor.yy0);}
#line 3037 "pikchr.c"
break;
case 106: /* expr ::= NUMBER */
#line 786 "pikchr.y"
{yylhsminor.yy129=pik_atof(&yymsp[0].minor.yy0);}
#line 3042 "pikchr.c"
yymsp[0].minor.yy129 = yylhsminor.yy129;
break;
case 107: /* expr ::= ID */
#line 787 "pikchr.y"
{yylhsminor.yy129=pik_get_var(p,&yymsp[0].minor.yy0);}
#line 3048 "pikchr.c"
yymsp[0].minor.yy129 = yylhsminor.yy129;
break;
case 108: /* expr ::= FUNC1 LP expr RP */
#line 788 "pikchr.y"
{yylhsminor.yy129 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy129,0.0);}
#line 3054 "pikchr.c"
yymsp[-3].minor.yy129 = yylhsminor.yy129;
break;
case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */
#line 789 "pikchr.y"
{yylhsminor.yy129 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy129,yymsp[-1].minor.yy129);}
#line 3060 "pikchr.c"
yymsp[-5].minor.yy129 = yylhsminor.yy129;
break;
case 110: /* expr ::= DIST LP position COMMA position RP */
#line 790 "pikchr.y"
{yymsp[-5].minor.yy129 = pik_dist(&yymsp[-3].minor.yy187,&yymsp[-1].minor.yy187);}
#line 3066 "pikchr.c"
break;
case 111: /* expr ::= place2 DOT_XY X */
#line 791 "pikchr.y"
{yylhsminor.yy129 = yymsp[-2].minor.yy187.x;}
#line 3071 "pikchr.c"
yymsp[-2].minor.yy129 = yylhsminor.yy129;
break;
case 112: /* expr ::= place2 DOT_XY Y */
#line 792 "pikchr.y"
{yylhsminor.yy129 = yymsp[-2].minor.yy187.y;}
#line 3077 "pikchr.c"
yymsp[-2].minor.yy129 = yylhsminor.yy129;
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 793 "pikchr.y"
{yylhsminor.yy129=pik_property_of(yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);}
#line 3085 "pikchr.c"
yymsp[-2].minor.yy129 = yylhsminor.yy129;
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);
/* (120) rvalue ::= expr */ yytestcase(yyruleno==120);
|
| ︙ | ︙ | |||
3128 3129 3130 3131 3132 3133 3134 |
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 ****************************************/
| | | | 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 |
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 551 "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 3196 "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
|
| ︙ | ︙ | |||
3405 3406 3407 3408 3409 3410 3411 | assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); return yyFallback[iToken]; #else (void)iToken; return 0; #endif } | | | 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 | assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ); return yyFallback[iToken]; #else (void)iToken; return 0; #endif } #line 798 "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 ** |
| ︙ | ︙ | |||
3632 3633 3634 3635 3636 3637 3638 | } /* Hack: Arcs are here rendered as quadratic Bezier curves rather ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters ** that control arcs are obscure and I could not figure out what they ** mean based on available documentation. (2) Arcs are rarely used, ** and so do not seem that important. */ | | | | | | | > > > > | > > > > > > > > > > | > | | 3685 3686 3687 3688 3689 3690 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 |
}
/* Hack: Arcs are here rendered as quadratic Bezier curves rather
** than true arcs. Multiple reasons: (1) the legacy-PIC parameters
** that control arcs are obscure and I could not figure out what they
** mean based on available documentation. (2) Arcs are rarely used,
** and so do not seem that important.
*/
static PPoint arcControlPoint(int cw, PPoint f, PPoint t){
PPoint m;
PNum dx, dy;
m.x = 0.5*(f.x+t.x);
m.y = 0.5*(f.y+t.y);
dx = t.x - f.x;
dy = t.y - f.y;
if( cw ){
m.x -= 0.5*dy;
m.y += 0.5*dx;
}else{
m.x += 0.5*dy;
m.y -= 0.5*dx;
}
return m;
}
static void arcCheck(Pik *p, PObj *pObj){
PPoint f, m, t;
PNum sw;
int i;
if( p->nTPath>2 ){
pik_error(p, &pObj->errTok, "arc geometry error");
return;
}
f = p->aTPath[0];
t = p->aTPath[1];
m = arcControlPoint(pObj->cw, f, t);
sw = pObj->sw;
for(i=1; i<16; i++){
PNum t1, t2, a, b, c, x, y;
t1 = 0.0625*i;
t2 = 1.0 - t1;
a = t2*t2;
b = 2*t1*t2;
c = t1*t1;
x = a*f.x + b*m.x + c*t.x;
y = a*f.y + b*m.y + c*t.y;
pik_bbox_addellipse(&pObj->bbox, x, y, sw, sw);
}
}
static void arcRender(Pik *p, PObj *pObj){
PPoint f, m, t;
if( pObj->nPath<2 ) return;
if( pObj->sw<0.0 ) return;
f = pObj->aPath[0];
t = pObj->aPath[1];
m = arcControlPoint(pObj->cw,f,t);
if( pObj->larrow ){
pik_draw_arrowhead(p,&m,&f,pObj);
}
if( pObj->rarrow ){
pik_draw_arrowhead(p,&m,&t,pObj);
}
pik_append_xy(p,"<path d=\"M", f.x, f.y);
|
| ︙ | ︙ | |||
4336 4337 4338 4339 4340 4341 4342 |
pObj->sw = 0.0;
}
static PPoint textOffset(Pik *p, PObj *pObj, int cp){
/* Automatically slim-down the width and height of text
** statements so that the bounding box tightly encloses the text,
** then get boxOffset() to do the offset computation.
*/
| | | 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 |
pObj->sw = 0.0;
}
static PPoint textOffset(Pik *p, PObj *pObj, int cp){
/* Automatically slim-down the width and height of text
** statements so that the bounding box tightly encloses the text,
** then get boxOffset() to do the offset computation.
*/
pik_size_to_fit(p, pObj, &pObj->errTok,3);
return boxOffset(p, pObj, cp);
}
static void textRender(Pik *p, PObj *pObj){
pik_append_txt(p, pObj, 0);
}
|
| ︙ | ︙ | |||
6345 6346 6347 6348 6349 6350 6351 | ** ** The eWhich parameter is: ** ** 1: Fit horizontally only ** 2: Fit vertically only ** 3: Fit both ways */ | | < | | 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 |
**
** The eWhich parameter is:
**
** 1: Fit horizontally only
** 2: Fit vertically only
** 3: Fit both ways
*/
static void pik_size_to_fit(Pik *p, PObj *pObj, PToken *pFit, int eWhich){
PNum w, h;
PBox bbox;
if( p->nErr ) return;
if( pObj==0 ) pObj = p->cur;
if( pObj->nTxt==0 ){
pik_error(0, pFit, "no text to fit to");
return;
}
if( pObj->type->xFit==0 ) return;
pik_bbox_init(&bbox);
|
| ︙ | ︙ | |||
6493 6494 6495 6496 6497 6498 6499 |
const char *zClr;
int c1, c2;
unsigned int i;
mid = (first+last)/2;
zClr = aColor[mid].zName;
for(i=0; i<pId->n; i++){
c1 = zClr[i]&0x7f;
| | | | 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 |
const char *zClr;
int c1, c2;
unsigned int i;
mid = (first+last)/2;
zClr = aColor[mid].zName;
for(i=0; i<pId->n; i++){
c1 = zClr[i]&0x7f;
if( IsUpper(c1) ) c1 = ToLower(c1);
c2 = pId->z[i]&0x7f;
if( IsUpper(c2) ) c2 = ToLower(c2);
c = c2 - c1;
if( c ) break;
}
if( c==0 && aColor[mid].zName[pId->n] ) c = -1;
if( c==0 ) return (double)aColor[mid].val;
if( c>0 ){
first = mid+1;
|
| ︙ | ︙ | |||
6894 6895 6896 6897 6898 6899 6900 |
/* A height or width less than or equal to zero means "autofit".
** Change the height or width to be big enough to contain the text,
*/
if( pObj->h<=0.0 ){
if( pObj->nTxt==0 ){
pObj->h = 0.0;
}else if( pObj->w<=0.0 ){
| | | | | 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 |
/* A height or width less than or equal to zero means "autofit".
** Change the height or width to be big enough to contain the text,
*/
if( pObj->h<=0.0 ){
if( pObj->nTxt==0 ){
pObj->h = 0.0;
}else if( pObj->w<=0.0 ){
pik_size_to_fit(p, pObj, &pObj->errTok, 3);
}else{
pik_size_to_fit(p, pObj, &pObj->errTok, 2);
}
}
if( pObj->w<=0.0 ){
if( pObj->nTxt==0 ){
pObj->w = 0.0;
}else{
pik_size_to_fit(p, pObj, &pObj->errTok, 1);
}
}
ofst = pik_elem_offset(p, pObj, pObj->eWith);
dx = (pObj->with.x - ofst.x) - pObj->ptAt.x;
dy = (pObj->with.y - ofst.y) - pObj->ptAt.y;
if( dx!=0 || dy!=0 ){
pik_elem_move(pObj, dx, dy);
|
| ︙ | ︙ | |||
7233 7234 7235 7236 7237 7238 7239 |
p->wSVG = pik_round(p->wSVG*pikScale);
p->hSVG = pik_round(p->hSVG*pikScale);
pik_append_num(p, " width=\"", p->wSVG);
pik_append_num(p, "\" height=\"", p->hSVG);
pik_append(p, "\"", 1);
}
pik_append_dis(p, " viewBox=\"0 0 ",w,"");
| | > | 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 |
p->wSVG = pik_round(p->wSVG*pikScale);
p->hSVG = pik_round(p->hSVG*pikScale);
pik_append_num(p, " width=\"", p->wSVG);
pik_append_num(p, "\" height=\"", p->hSVG);
pik_append(p, "\"", 1);
}
pik_append_dis(p, " viewBox=\"0 0 ",w,"");
pik_append_dis(p, " ",h,"\"");
pik_append(p, " data-pikchr-date=\"" MANIFEST_ISODATE "\">\n", -1);
pik_elist_render(p, pList);
pik_append(p,"</svg>\n", -1);
}else{
p->wSVG = -1;
p->hSVG = -1;
}
pik_elist_free(p, pList);
|
| ︙ | ︙ | |||
7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 |
{ "mono", 4, T_MONO, 0, 0 },
{ "monospace", 9, T_MONO, 0, 0 },
{ "n", 1, T_EDGEPT, 0, CP_N },
{ "ne", 2, T_EDGEPT, 0, CP_NE },
{ "north", 5, T_EDGEPT, 0, CP_N },
{ "nw", 2, T_EDGEPT, 0, CP_NW },
{ "of", 2, T_OF, 0, 0 },
{ "previous", 8, T_LAST, 0, 0, },
{ "print", 5, T_PRINT, 0, 0 },
{ "rad", 3, T_RADIUS, 0, 0 },
{ "radius", 6, T_RADIUS, 0, 0 },
{ "right", 5, T_RIGHT, DIR_RIGHT, CP_E },
{ "rjust", 5, T_RJUST, 0, 0 },
{ "s", 1, T_EDGEPT, 0, CP_S },
| > | 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 |
{ "mono", 4, T_MONO, 0, 0 },
{ "monospace", 9, T_MONO, 0, 0 },
{ "n", 1, T_EDGEPT, 0, CP_N },
{ "ne", 2, T_EDGEPT, 0, CP_NE },
{ "north", 5, T_EDGEPT, 0, CP_N },
{ "nw", 2, T_EDGEPT, 0, CP_NW },
{ "of", 2, T_OF, 0, 0 },
{ "pikchr_date",11, T_ISODATE, 0, 0, },
{ "previous", 8, T_LAST, 0, 0, },
{ "print", 5, T_PRINT, 0, 0 },
{ "rad", 3, T_RADIUS, 0, 0 },
{ "radius", 6, T_RADIUS, 0, 0 },
{ "right", 5, T_RIGHT, DIR_RIGHT, CP_E },
{ "rjust", 5, T_RJUST, 0, 0 },
{ "s", 1, T_EDGEPT, 0, CP_S },
|
| ︙ | ︙ | |||
7603 7604 7605 7606 7607 7608 7609 |
pToken->eType = T_ERROR;
return 1;
}
default: {
c = z[0];
if( c=='.' ){
unsigned char c1 = z[1];
| | | | | | | 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 |
pToken->eType = T_ERROR;
return 1;
}
default: {
c = z[0];
if( c=='.' ){
unsigned char c1 = z[1];
if( IsLower(c1) ){
const PikWord *pFound;
for(i=2; (c = z[i])>='a' && c<='z'; i++){}
pFound = pik_find_word((const char*)z+1, i-1,
pik_keywords, count(pik_keywords));
if( pFound && (pFound->eEdge>0 ||
pFound->eType==T_EDGEPT ||
pFound->eType==T_START ||
pFound->eType==T_END )
){
/* Dot followed by something that is a 2-D place value */
pToken->eType = T_DOT_E;
}else if( pFound && (pFound->eType==T_X || pFound->eType==T_Y) ){
/* Dot followed by "x" or "y" */
pToken->eType = T_DOT_XY;
}else{
/* Any other "dot" */
pToken->eType = T_DOT_L;
}
return 1;
}else if( IsDigit(c1) ){
i = 0;
/* no-op. Fall through to number handling */
}else if( IsUpper(c1) ){
for(i=2; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){}
pToken->eType = T_DOT_U;
return 1;
}else{
pToken->eType = T_ERROR;
return 1;
}
}
if( (c>='0' && c<='9') || c=='.' ){
int nDigit;
int isInt = 1;
if( c!='.' ){
nDigit = 1;
for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
if( i==1 && (c=='x' || c=='X') ){
for(i=2; (c = z[i])!=0 && IsXDigit(c); i++){}
pToken->eType = T_NUMBER;
return i;
}
}else{
isInt = 0;
nDigit = 0;
i = 0;
|
| ︙ | ︙ | |||
7698 7699 7700 7701 7702 7703 7704 |
|| (c=='p' && c2=='x')
|| (c=='p' && c2=='c')
){
i += 2;
}
pToken->eType = T_NUMBER;
return i;
| | | | | | | 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 |
|| (c=='p' && c2=='x')
|| (c=='p' && c2=='c')
){
i += 2;
}
pToken->eType = T_NUMBER;
return i;
}else if( IsLower(c) ){
const PikWord *pFound;
for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){}
pFound = pik_find_word((const char*)z, i,
pik_keywords, count(pik_keywords));
if( pFound ){
pToken->eType = pFound->eType;
pToken->eCode = pFound->eCode;
pToken->eEdge = pFound->eEdge;
return i;
}
pToken->n = i;
if( pik_find_class(pToken)!=0 ){
pToken->eType = T_CLASSNAME;
}else{
pToken->eType = T_ID;
}
return i;
}else if( c>='A' && c<='Z' ){
for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){}
pToken->eType = T_PLACENAME;
return i;
}else if( c=='$' && z[1]>='1' && z[1]<='9' && !IsDigit(z[2]) ){
pToken->eType = T_PARAMETER;
pToken->eCode = z[1] - '1';
return 2;
}else if( c=='_' || c=='$' || c=='@' ){
for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){}
pToken->eType = T_ID;
return i;
}else{
pToken->eType = T_ERROR;
return 1;
}
}
|
| ︙ | ︙ | |||
7812 7813 7814 7815 7816 7817 7818 |
if( z[i]==')' ){
args[nArg].n = i - iStart;
/* Remove leading and trailing whitespace from each argument.
** If what remains is one of $1, $2, ... $9 then transfer the
** corresponding argument from the outer context */
for(j=0; j<=nArg; j++){
PToken *t = &args[j];
| | | | 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 |
if( z[i]==')' ){
args[nArg].n = i - iStart;
/* Remove leading and trailing whitespace from each argument.
** If what remains is one of $1, $2, ... $9 then transfer the
** corresponding argument from the outer context */
for(j=0; j<=nArg; j++){
PToken *t = &args[j];
while( t->n>0 && IsSpace(t->z[0]) ){ t->n--; t->z++; }
while( t->n>0 && IsSpace(t->z[t->n-1]) ){ t->n--; }
if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){
if( pOuter ) *t = pOuter[t->z[1]-'1'];
else t->n = 0;
}
}
return i+1;
}
|
| ︙ | ︙ | |||
7894 7895 7896 7897 7898 7899 7900 |
pik_tokenize(p, &pMac->macroBody, pParser, args);
p->nCtx--;
pMac->inUse = 0;
}else{
#if 0
printf("******** Token %s (%d): \"%.*s\" **************\n",
yyTokenName[token.eType], token.eType,
| | > > > > > > > > > > > > > > > | 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 |
pik_tokenize(p, &pMac->macroBody, pParser, args);
p->nCtx--;
pMac->inUse = 0;
}else{
#if 0
printf("******** Token %s (%d): \"%.*s\" **************\n",
yyTokenName[token.eType], token.eType,
(int)(IsSpace(token.z[0]) ? 0 : sz), token.z);
#endif
token.n = (unsigned short)(sz & 0xffff);
if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){
pik_error(p, &token, "script is too complex");
break;
}
if( token.eType==T_ISODATE ){
token.z = "\"" MANIFEST_ISODATE "\"";
token.n = sizeof(MANIFEST_ISODATE)+1;
token.eType = T_STRING;
}
pik_parser(pParser, token.eType, token);
}
}
}
/*
** Return the version name.
*/
const char *pikchr_version(void)
/* Emscripten workaround, else it chokes on the inlined version */;
const char *pikchr_version(void){
return RELEASE_VERSION " " MANIFEST_ISODATE;
}
/*
** Parse the PIKCHR script contained in zText[]. Return a rendering. Or
** if an error is encountered, return the error text. The error message
** is HTML formatted. So regardless of what happens, the return text
** is safe to be insertd into an HTML output stream.
**
|
| ︙ | ︙ | |||
8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 |
if( zHtmlHdr==0 ){
fprintf(stderr, "the \"%s\" option must come first\n",argv[i]);
exit(1);
}
bSvgOnly = 1;
mFlags |= PIKCHR_PLAINTEXT_ERRORS;
}else
{
fprintf(stderr,"unknown option: \"%s\"\n", argv[i]);
usage(argv[0]);
}
continue;
}
zIn = readFile(argv[i]);
| > > > > | 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 |
if( zHtmlHdr==0 ){
fprintf(stderr, "the \"%s\" option must come first\n",argv[i]);
exit(1);
}
bSvgOnly = 1;
mFlags |= PIKCHR_PLAINTEXT_ERRORS;
}else
if( strcmp(z,"version")==0 || strcmp(z,"v")==0 ){
printf("pikchr %s\n", pikchr_version());
return 0;
}else
{
fprintf(stderr,"unknown option: \"%s\"\n", argv[i]);
usage(argv[0]);
}
continue;
}
zIn = readFile(argv[i]);
|
| ︙ | ︙ | |||
8240 8241 8242 8243 8244 8245 8246 | return TCL_OK; } #endif /* PIKCHR_TCL */ | | | 8328 8329 8330 8331 8332 8333 8334 8335 | return TCL_OK; } #endif /* PIKCHR_TCL */ #line 8335 "pikchr.c" |
Changes to extsrc/pikchr.js.
1 2 |
var initPikchrModule = (() => {
| | | | > > > > > > > > > > > > > > | > | | | > > > > > > > > > > > > > | < | < < | | | | > | > > > | > | | > | | > > | | | > > > > > > | | | | | | | < < < < < < | < | | < < < < < < < < < < | | | < > | < < < | < > | | > > > > > > > | | | | | | | | | > > | | | > > > > > > > > < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < | | > | | | | | < | > > > < < < < | < | > | < | | | | < | > | < | | | | > > > > > > > > > > > > > | < | < | < | < | | | | | | | | | | | | < | < | > > | | < | > > > > > > > > > > > > | | > > > | > > > > > > > > > > > > > > | > > | < < < < < < | < | | < < | | < | > | | > | > | | < < | | | | > > | | | | > | > > > > > > > > > > > > > > | | > | > > > > > > > > > > | > > | < | | < | | < | | > | > | > | > > > > | | < < < < < < < < < < < < < < | | | | < < < < > | < < < | | | < | | > | | | > > | | > > > > > > > > > > > > > > > > > | | | | > > > < | < | | > > < < | > | > > | > > < < < | | | | > > > > > > > > > > > > > > > > > > > > > > | | > | | > | < > | | > | | > | | > | < < < < > | < < < < < < | | | < < < < > < < < > > > > | < < > > > | > > > > > > > > > > > | | < > > > | > > > > > > > | < > > > | < | > > > | < < > | > > | < > > | | | < < | < < < > > > | < < < < < > > > > > > > > > > > | < < < < < > | | | < > > | > > > > > > > > > | | | | | | | | | | | | | | > | | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > | < > > | | | | | < | | < < < < < < < < < | | > > | | < > | < < | | < > | | < > > > > > > > > > > | | > | < | | | > > | > | | | > | > > | | | | | | | | | | | | < | < | | | | | | | | | > > > > > > > | > < < | | 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 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 |
var initPikchrModule = (() => {
var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined;
return (
function(moduleArg = {}) {
var moduleRtn;
// include: shell.js
// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
// 2. A function parameter, function(moduleArg) => Promise<Module>
// 3. pre-run appended it, var Module = {}; ..generated code..
// 4. External script tag defines var Module.
// We need to check if Module already exists (e.g. case 3 above).
// Substitution will be replaced with actual code on later stage of the build,
// this way Closure Compiler will not mangle it (e.g. case 4. above).
// Note that if you want to run closure, and also to use Module
// after the generated code, you will need to define var Module = {};
// before the code. Then that object will be used in the code, and you
// can continue to use Module afterwards as well.
var Module = moduleArg;
// Set up the promise that indicates the Module is initialized
var readyPromiseResolve, readyPromiseReject;
var readyPromise = new Promise((resolve, reject) => {
readyPromiseResolve = resolve;
readyPromiseReject = reject;
});
// Determine the runtime environment we are in. You can customize this by
// setting the ENVIRONMENT setting at compile time (see settings.js).
var ENVIRONMENT_IS_WEB = true;
var ENVIRONMENT_IS_WORKER = false;
// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
// Sometimes an existing Module object exists with properties
// meant to overwrite the default module functionality. Here
// we collect those properties and reapply _after_ we configure
// the current environment's defaults to avoid having to be so
// defensive during initialization.
var moduleOverrides = Object.assign({}, Module);
var arguments_ = [];
var thisProgram = "./this.program";
var quit_ = (status, toThrow) => {
throw toThrow;
};
// `/` should be present at the end if `scriptDirectory` is not empty
var scriptDirectory = "";
function locateFile(path) {
if (Module["locateFile"]) {
return Module["locateFile"](path, scriptDirectory);
}
return scriptDirectory + path;
}
// Hooks that are implemented differently in different runtime environments.
var readAsync, readBinary;
// Note that this includes Node.js workers when relevant (pthreads is enabled).
// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and
// ENVIRONMENT_IS_NODE.
if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
if (ENVIRONMENT_IS_WORKER) {
// Check worker, not web, since window could be polyfilled
scriptDirectory = self.location.href;
} else if (typeof document != "undefined" && document.currentScript) {
// web
scriptDirectory = document.currentScript.src;
}
// When MODULARIZE, this JS may be executed later, after document.currentScript
// is gone, so we saved it, and we use it here instead of any other info.
if (_scriptName) {
scriptDirectory = _scriptName;
}
// blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them.
// otherwise, slice off the final part of the url to find the script directory.
// if scriptDirectory does not contain a slash, lastIndexOf will return -1,
// and scriptDirectory will correctly be replaced with an empty string.
// If scriptDirectory contains a query (starting with ?) or a fragment (starting with #),
// they are removed because they could contain a slash.
if (scriptDirectory.startsWith("blob:")) {
scriptDirectory = "";
} else {
scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1);
}
{
// include: web_or_worker_shell_read.js
readAsync = url => fetch(url, {
credentials: "same-origin"
}).then(response => {
if (response.ok) {
return response.arrayBuffer();
}
return Promise.reject(new Error(response.status + " : " + response.url));
});
}
} else // end include: web_or_worker_shell_read.js
{}
var out = Module["print"] || console.log.bind(console);
var err = Module["printErr"] || console.error.bind(console);
// Merge back in the overrides
Object.assign(Module, moduleOverrides);
// Free the object hierarchy contained in the overrides, this lets the GC
// reclaim data used.
moduleOverrides = null;
// Emit code to handle expected values on the Module object. This applies Module.x
// to the proper local x. This has two benefits: first, we only emit it if it is
// expected to arrive, and second, by using a local everywhere else that can be
// minified.
if (Module["arguments"]) arguments_ = Module["arguments"];
if (Module["thisProgram"]) thisProgram = Module["thisProgram"];
// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message
// end include: shell.js
// include: preamble.js
// === Preamble library stuff ===
// Documentation for the public APIs defined in this file must be updated in:
// site/source/docs/api_reference/preamble.js.rst
// A prebuilt local version of the documentation is available at:
// site/build/text/docs/api_reference/preamble.js.txt
// You can also build docs locally as HTML or other formats in site/
// An online HTML version (which may be of a different version of Emscripten)
// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html
var wasmBinary = Module["wasmBinary"];
// Wasm globals
var wasmMemory;
//========================================
// Runtime essentials
//========================================
// whether we are quitting the application. no code should run after this.
// set in exit() and abort()
var ABORT = false;
// set by exit() and abort(). Passed to 'onExit' handler.
// NOTE: This is also used as the process return code code in shell environments
// but only when noExitRuntime is false.
var EXITSTATUS;
// Memory management
var /** @type {!Int8Array} */ HEAP8, /** @type {!Uint8Array} */ HEAPU8, /** @type {!Int16Array} */ HEAP16, /** @type {!Uint16Array} */ HEAPU16, /** @type {!Int32Array} */ HEAP32, /** @type {!Uint32Array} */ HEAPU32, /** @type {!Float32Array} */ HEAPF32, /** @type {!Float64Array} */ HEAPF64;
// include: runtime_shared.js
function updateMemoryViews() {
var b = wasmMemory.buffer;
Module["HEAP8"] = HEAP8 = new Int8Array(b);
Module["HEAP16"] = HEAP16 = new Int16Array(b);
Module["HEAPU8"] = HEAPU8 = new Uint8Array(b);
Module["HEAPU16"] = HEAPU16 = new Uint16Array(b);
Module["HEAP32"] = HEAP32 = new Int32Array(b);
Module["HEAPU32"] = HEAPU32 = new Uint32Array(b);
Module["HEAPF32"] = HEAPF32 = new Float32Array(b);
Module["HEAPF64"] = HEAPF64 = new Float64Array(b);
}
// end include: runtime_shared.js
// include: runtime_stack_check.js
// end include: runtime_stack_check.js
var __ATPRERUN__ = [];
// functions called before the runtime is initialized
var __ATINIT__ = [];
// functions called during shutdown
var __ATPOSTRUN__ = [];
// functions called after the main() is called
var runtimeInitialized = false;
function preRun() {
var preRuns = Module["preRun"];
if (preRuns) {
if (typeof preRuns == "function") preRuns = [ preRuns ];
preRuns.forEach(addOnPreRun);
}
callRuntimeCallbacks(__ATPRERUN__);
}
function initRuntime() {
runtimeInitialized = true;
callRuntimeCallbacks(__ATINIT__);
}
function postRun() {
var postRuns = Module["postRun"];
if (postRuns) {
if (typeof postRuns == "function") postRuns = [ postRuns ];
postRuns.forEach(addOnPostRun);
}
callRuntimeCallbacks(__ATPOSTRUN__);
}
function addOnPreRun(cb) {
__ATPRERUN__.unshift(cb);
}
function addOnInit(cb) {
__ATINIT__.unshift(cb);
}
function addOnPostRun(cb) {
__ATPOSTRUN__.unshift(cb);
}
// include: runtime_math.js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
// end include: runtime_math.js
// A counter of dependencies for calling run(). If we need to
// do asynchronous work before running, increment this and
// decrement it. Incrementing must happen in a place like
// Module.preRun (used by emcc to add file preloading).
// Note that you can add dependencies in preRun, even though
// it happens right before run - run will be postponed until
// the dependencies are met.
var runDependencies = 0;
var runDependencyWatcher = null;
var dependenciesFulfilled = null;
function addRunDependency(id) {
runDependencies++;
Module["monitorRunDependencies"]?.(runDependencies);
}
function removeRunDependency(id) {
runDependencies--;
Module["monitorRunDependencies"]?.(runDependencies);
if (runDependencies == 0) {
if (runDependencyWatcher !== null) {
clearInterval(runDependencyWatcher);
runDependencyWatcher = null;
}
if (dependenciesFulfilled) {
var callback = dependenciesFulfilled;
dependenciesFulfilled = null;
callback();
}
}
}
/** @param {string|number=} what */ function abort(what) {
Module["onAbort"]?.(what);
what = "Aborted(" + what + ")";
// TODO(sbc): Should we remove printing and leave it up to whoever
// catches the exception?
err(what);
ABORT = true;
what += ". Build with -sASSERTIONS for more info.";
// Use a wasm runtime error, because a JS error might be seen as a foreign
// exception, which means we'd run destructors on it. We need the error to
// simply make the program stop.
// FIXME This approach does not work in Wasm EH because it currently does not assume
// all RuntimeErrors are from traps; it decides whether a RuntimeError is from
// a trap or not based on a hidden field within the object. So at the moment
// we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that
// allows this in the wasm spec.
// Suppress closure compiler warning here. Closure compiler's builtin extern
// definition for WebAssembly.RuntimeError claims it takes no arguments even
// though it can.
// TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed.
/** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what);
readyPromiseReject(e);
// Throw the error whether or not MODULARIZE is set because abort is used
// in code paths apart from instantiation where an exception is expected
// to be thrown when abort is called.
throw e;
}
// include: memoryprofiler.js
// end include: memoryprofiler.js
// include: URIUtils.js
// Prefix of data URIs emitted by SINGLE_FILE and related options.
var dataURIPrefix = "data:application/octet-stream;base64,";
/**
* Indicates whether filename is a base64 data URI.
* @noinline
*/ var isDataURI = filename => filename.startsWith(dataURIPrefix);
// end include: URIUtils.js
// include: runtime_exceptions.js
// end include: runtime_exceptions.js
function findWasmBinary() {
var f = "pikchr-v2813665466.wasm";
if (!isDataURI(f)) {
return locateFile(f);
}
return f;
}
var wasmBinaryFile;
function getBinarySync(file) {
if (file == wasmBinaryFile && wasmBinary) {
return new Uint8Array(wasmBinary);
}
if (readBinary) {
return readBinary(file);
}
throw "both async and sync fetching of the wasm failed";
}
function getBinaryPromise(binaryFile) {
// If we don't have the binary yet, load it asynchronously using readAsync.
if (!wasmBinary) {
// Fetch the binary using readAsync
return readAsync(binaryFile).then(response => new Uint8Array(/** @type{!ArrayBuffer} */ (response)), // Fall back to getBinarySync if readAsync fails
() => getBinarySync(binaryFile));
}
// Otherwise, getBinarySync should be able to get it synchronously
return Promise.resolve().then(() => getBinarySync(binaryFile));
}
function instantiateArrayBuffer(binaryFile, imports, receiver) {
return getBinaryPromise(binaryFile).then(binary => WebAssembly.instantiate(binary, imports)).then(receiver, reason => {
err(`failed to asynchronously prepare wasm: ${reason}`);
abort(reason);
});
}
function instantiateAsync(binary, binaryFile, imports, callback) {
if (!binary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(binaryFile) && typeof fetch == "function") {
return fetch(binaryFile, {
credentials: "same-origin"
}).then(response => {
// Suppress closure warning here since the upstream definition for
// instantiateStreaming only allows Promise<Repsponse> rather than
// an actual Response.
// TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed.
/** @suppress {checkTypes} */ var result = WebAssembly.instantiateStreaming(response, imports);
return result.then(callback, function(reason) {
// We expect the most common failure cause to be a bad MIME type for the binary,
// in which case falling back to ArrayBuffer instantiation should work.
err(`wasm streaming compile failed: ${reason}`);
err("falling back to ArrayBuffer instantiation");
return instantiateArrayBuffer(binaryFile, imports, callback);
});
});
}
return instantiateArrayBuffer(binaryFile, imports, callback);
}
function getWasmImports() {
// prepare imports
return {
"a": wasmImports
};
}
// Create the wasm instance.
// Receives the wasm imports, returns the exports.
function createWasm() {
var info = getWasmImports();
// Load the wasm module and create an instance of using native support in the JS engine.
// handle a generated wasm instance, receiving its exports and
// performing other necessary setup
/** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) {
wasmExports = instance.exports;
wasmMemory = wasmExports["d"];
updateMemoryViews();
addOnInit(wasmExports["e"]);
removeRunDependency("wasm-instantiate");
return wasmExports;
}
// wait for the pthread pool (if any)
addRunDependency("wasm-instantiate");
// Prefer streaming instantiation if available.
function receiveInstantiationResult(result) {
// 'result' is a ResultObject object which has both the module and instance.
// receiveInstance() will swap in the exports (to Module.asm) so they can be called
// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line.
// When the regression is fixed, can restore the above PTHREADS-enabled path.
receiveInstance(result["instance"]);
}
// User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback
// to manually instantiate the Wasm module themselves. This allows pages to
// run the instantiation parallel to any other async startup actions they are
// performing.
// Also pthreads and wasm workers initialize the wasm instance through this
// path.
if (Module["instantiateWasm"]) {
try {
return Module["instantiateWasm"](info, receiveInstance);
} catch (e) {
err(`Module.instantiateWasm callback failed with error: ${e}`);
// If instantiation fails, reject the module ready promise.
readyPromiseReject(e);
}
}
wasmBinaryFile ??= findWasmBinary();
// If instantiation fails, reject the module ready promise.
instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult).catch(readyPromiseReject);
return {};
}
// include: runtime_debug.js
// end include: runtime_debug.js
// === Body ===
// end include: preamble.js
/** @constructor */ function ExitStatus(status) {
this.name = "ExitStatus";
this.message = `Program terminated with exit(${status})`;
this.status = status;
}
var callRuntimeCallbacks = callbacks => {
// Pass the module as the first argument.
callbacks.forEach(f => f(Module));
};
/**
* @param {number} ptr
* @param {string} type
*/ function getValue(ptr, type = "i8") {
if (type.endsWith("*")) type = "*";
switch (type) {
case "i1":
return HEAP8[ptr];
case "i8":
return HEAP8[ptr];
case "i16":
return HEAP16[((ptr) >> 1)];
case "i32":
return HEAP32[((ptr) >> 2)];
case "i64":
abort("to do getValue(i64) use WASM_BIGINT");
case "float":
return HEAPF32[((ptr) >> 2)];
case "double":
return HEAPF64[((ptr) >> 3)];
case "*":
return HEAPU32[((ptr) >> 2)];
default:
abort(`invalid type for getValue: ${type}`);
}
}
var noExitRuntime = Module["noExitRuntime"] || true;
/**
* @param {number} ptr
* @param {number} value
* @param {string} type
*/ function setValue(ptr, value, type = "i8") {
if (type.endsWith("*")) type = "*";
switch (type) {
case "i1":
HEAP8[ptr] = value;
break;
case "i8":
HEAP8[ptr] = value;
break;
case "i16":
HEAP16[((ptr) >> 1)] = value;
break;
case "i32":
HEAP32[((ptr) >> 2)] = value;
break;
case "i64":
abort("to do setValue(i64) use WASM_BIGINT");
case "float":
HEAPF32[((ptr) >> 2)] = value;
break;
case "double":
HEAPF64[((ptr) >> 3)] = value;
break;
case "*":
HEAPU32[((ptr) >> 2)] = value;
break;
default:
abort(`invalid type for setValue: ${type}`);
}
}
var stackRestore = val => __emscripten_stack_restore(val);
var stackSave = () => _emscripten_stack_get_current();
var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder : undefined;
/**
* Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given
* array that contains uint8 values, returns a copy of that string as a
* Javascript String object.
* heapOrArray is either a regular array, or a JavaScript typed array view.
* @param {number=} idx
* @param {number=} maxBytesToRead
* @return {string}
*/ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => {
var endIdx = idx + maxBytesToRead;
var endPtr = idx;
// TextDecoder needs to know the byte length in advance, it doesn't stop on
// null terminator by itself. Also, use the length info to avoid running tiny
// strings through TextDecoder, since .subarray() allocates garbage.
// (As a tiny code save trick, compare endPtr against endIdx using a negation,
// so that undefined/NaN means Infinity)
while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr;
if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr));
}
var str = "";
// If building with TextDecoder, we have already computed the string length
// above, so test loop end condition against that
while (idx < endPtr) {
// For UTF8 byte structure, see:
// http://en.wikipedia.org/wiki/UTF-8#Description
// https://www.ietf.org/rfc/rfc2279.txt
// https://tools.ietf.org/html/rfc3629
var u0 = heapOrArray[idx++];
if (!(u0 & 128)) {
str += String.fromCharCode(u0);
continue;
}
var u1 = heapOrArray[idx++] & 63;
if ((u0 & 224) == 192) {
str += String.fromCharCode(((u0 & 31) << 6) | u1);
continue;
}
var u2 = heapOrArray[idx++] & 63;
if ((u0 & 240) == 224) {
u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
} else {
u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63);
}
if (u0 < 65536) {
str += String.fromCharCode(u0);
} else {
var ch = u0 - 65536;
str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023));
}
}
return str;
};
/**
* Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the
* emscripten HEAP, returns a copy of that string as a Javascript String object.
*
* @param {number} ptr
* @param {number=} maxBytesToRead - An optional length that specifies the
* maximum number of bytes to read. You can omit this parameter to scan the
* string until the first 0 byte. If maxBytesToRead is passed, and the string
* at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the
* string will cut short at that byte index (i.e. maxBytesToRead will not
* produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing
* frequent uses of UTF8ToString() with and without maxBytesToRead may throw
* JS JIT optimizations off, so it is worth to consider consistently using one
* @return {string}
*/ var UTF8ToString = (ptr, maxBytesToRead) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : "";
var ___assert_fail = (condition, filename, line, func) => {
abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [ filename ? UTF8ToString(filename) : "unknown filename", line, func ? UTF8ToString(func) : "unknown function" ]);
};
var abortOnCannotGrowMemory = requestedSize => {
abort("OOM");
};
var _emscripten_resize_heap = requestedSize => {
var oldSize = HEAPU8.length;
// With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned.
requestedSize >>>= 0;
abortOnCannotGrowMemory(requestedSize);
};
var runtimeKeepaliveCounter = 0;
var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0;
var _proc_exit = code => {
EXITSTATUS = code;
if (!keepRuntimeAlive()) {
Module["onExit"]?.(code);
ABORT = true;
}
quit_(code, new ExitStatus(code));
};
/** @suppress {duplicate } */ /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => {
EXITSTATUS = status;
_proc_exit(status);
};
var _exit = exitJS;
var getCFunc = ident => {
var func = Module["_" + ident];
// closure exported function
return func;
};
var writeArrayToMemory = (array, buffer) => {
HEAP8.set(array, buffer);
};
var lengthBytesUTF8 = str => {
var len = 0;
for (var i = 0; i < str.length; ++i) {
// Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code
// unit, not a Unicode code point of the character! So decode
// UTF16->UTF32->UTF8.
// See http://unicode.org/faq/utf_bom.html#utf16-3
var c = str.charCodeAt(i);
// possibly a lead surrogate
if (c <= 127) {
len++;
} else if (c <= 2047) {
len += 2;
} else if (c >= 55296 && c <= 57343) {
len += 4;
++i;
} else {
len += 3;
}
}
return len;
};
var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => {
// Parameter maxBytesToWrite is not optional. Negative values, 0, null,
// undefined and false each don't write out any bytes.
if (!(maxBytesToWrite > 0)) return 0;
var startIdx = outIdx;
var endIdx = outIdx + maxBytesToWrite - 1;
// -1 for string null terminator.
for (var i = 0; i < str.length; ++i) {
// Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code
// unit, not a Unicode code point of the character! So decode
// UTF16->UTF32->UTF8.
// See http://unicode.org/faq/utf_bom.html#utf16-3
// For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description
// and https://www.ietf.org/rfc/rfc2279.txt
// and https://tools.ietf.org/html/rfc3629
var u = str.charCodeAt(i);
// possibly a lead surrogate
if (u >= 55296 && u <= 57343) {
var u1 = str.charCodeAt(++i);
u = 65536 + ((u & 1023) << 10) | (u1 & 1023);
}
if (u <= 127) {
if (outIdx >= endIdx) break;
heap[outIdx++] = u;
} else if (u <= 2047) {
if (outIdx + 1 >= endIdx) break;
heap[outIdx++] = 192 | (u >> 6);
heap[outIdx++] = 128 | (u & 63);
} else if (u <= 65535) {
if (outIdx + 2 >= endIdx) break;
heap[outIdx++] = 224 | (u >> 12);
heap[outIdx++] = 128 | ((u >> 6) & 63);
heap[outIdx++] = 128 | (u & 63);
} else {
if (outIdx + 3 >= endIdx) break;
heap[outIdx++] = 240 | (u >> 18);
heap[outIdx++] = 128 | ((u >> 12) & 63);
heap[outIdx++] = 128 | ((u >> 6) & 63);
heap[outIdx++] = 128 | (u & 63);
}
}
// Null-terminate the pointer to the buffer.
heap[outIdx] = 0;
return outIdx - startIdx;
};
var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite);
var stackAlloc = sz => __emscripten_stack_alloc(sz);
var stringToUTF8OnStack = str => {
var size = lengthBytesUTF8(str) + 1;
var ret = stackAlloc(size);
stringToUTF8(str, ret, size);
return ret;
};
/**
* @param {string|null=} returnType
* @param {Array=} argTypes
* @param {Arguments|Array=} args
* @param {Object=} opts
*/ var ccall = (ident, returnType, argTypes, args, opts) => {
// For fast lookup of conversion functions
var toC = {
"string": str => {
var ret = 0;
if (str !== null && str !== undefined && str !== 0) {
// null string
ret = stringToUTF8OnStack(str);
}
return ret;
},
"array": arr => {
var ret = stackAlloc(arr.length);
writeArrayToMemory(arr, ret);
return ret;
}
};
function convertReturnValue(ret) {
if (returnType === "string") {
return UTF8ToString(ret);
}
if (returnType === "boolean") return Boolean(ret);
return ret;
}
var func = getCFunc(ident);
var cArgs = [];
var stack = 0;
if (args) {
for (var i = 0; i < args.length; i++) {
var converter = toC[argTypes[i]];
if (converter) {
if (stack === 0) stack = stackSave();
cArgs[i] = converter(args[i]);
} else {
cArgs[i] = args[i];
}
}
}
var ret = func(...cArgs);
function onDone(ret) {
if (stack !== 0) stackRestore(stack);
return convertReturnValue(ret);
}
ret = onDone(ret);
return ret;
};
/**
* @param {string=} returnType
* @param {Array=} argTypes
* @param {Object=} opts
*/ var cwrap = (ident, returnType, argTypes, opts) => {
// When the function takes numbers and returns a number, we can just return
// the original function
var numericArgs = !argTypes || argTypes.every(type => type === "number" || type === "boolean");
var numericRet = returnType !== "string";
if (numericRet && numericArgs && !opts) {
return getCFunc(ident);
}
return (...args) => ccall(ident, returnType, argTypes, args, opts);
};
var wasmImports = {
/** @export */ a: ___assert_fail,
/** @export */ b: _emscripten_resize_heap,
/** @export */ c: _exit
};
var wasmExports = createWasm();
var ___wasm_call_ctors = () => (___wasm_call_ctors = wasmExports["e"])();
var _pikchr_version = Module["_pikchr_version"] = () => (_pikchr_version = Module["_pikchr_version"] = wasmExports["g"])();
var _pikchr = Module["_pikchr"] = (a0, a1, a2, a3, a4) => (_pikchr = Module["_pikchr"] = wasmExports["h"])(a0, a1, a2, a3, a4);
var __emscripten_stack_restore = a0 => (__emscripten_stack_restore = wasmExports["i"])(a0);
var __emscripten_stack_alloc = a0 => (__emscripten_stack_alloc = wasmExports["j"])(a0);
var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports["k"])();
// include: postamble.js
// === Auto-generated postamble setup entry stuff ===
Module["stackSave"] = stackSave;
Module["stackRestore"] = stackRestore;
Module["stackAlloc"] = stackAlloc;
Module["ccall"] = ccall;
Module["cwrap"] = cwrap;
Module["setValue"] = setValue;
Module["getValue"] = getValue;
var calledRun;
var calledPrerun;
dependenciesFulfilled = function runCaller() {
// If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false)
if (!calledRun) run();
if (!calledRun) dependenciesFulfilled = runCaller;
};
// try this again later, after new deps are fulfilled
function run() {
if (runDependencies > 0) {
return;
}
if (!calledPrerun) {
calledPrerun = 1;
preRun();
// a preRun added a dependency, run will be called later
if (runDependencies > 0) {
return;
}
}
function doRun() {
// run may have just been called through dependencies being fulfilled just in this very frame,
// or while the async setStatus time below was happening
if (calledRun) return;
calledRun = 1;
Module["calledRun"] = 1;
if (ABORT) return;
initRuntime();
readyPromiseResolve(Module);
Module["onRuntimeInitialized"]?.();
postRun();
}
if (Module["setStatus"]) {
Module["setStatus"]("Running...");
setTimeout(() => {
setTimeout(() => Module["setStatus"](""), 1);
doRun();
}, 1);
} else {
doRun();
}
}
if (Module["preInit"]) {
if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ];
while (Module["preInit"].length > 0) {
Module["preInit"].pop()();
}
}
run();
// end include: postamble.js
// include: postamble_modularize.js
// In MODULARIZE mode we wrap the generated code in a factory function
// and return either the Module itself, or a promise of the module.
// We assign to the `moduleRtn` global here and configure closure to see
// this as and extern so it won't get minified.
moduleRtn = readyPromise;
return moduleRtn;
}
);
})();
if (typeof exports === 'object' && typeof module === 'object')
module.exports = initPikchrModule;
else if (typeof define === 'function' && define['amd'])
define([], () => initPikchrModule);
|
Changes to extsrc/pikchr.wasm.
cannot compute difference between binary files
Changes to extsrc/shell.c.
| ︙ | ︙ | |||
232 233 234 235 236 237 238 239 240 241 242 243 244 245 | #define isatty(x) 1 #endif /* ctype macros that work with signed characters */ #define IsSpace(X) isspace((unsigned char)X) #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) #if defined(_WIN32) || defined(WIN32) #if SQLITE_OS_WINRT #include <intrin.h> #endif #undef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN | > > | 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | #define isatty(x) 1 #endif /* ctype macros that work with signed characters */ #define IsSpace(X) isspace((unsigned char)X) #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) #define IsAlnum(X) isalnum((unsigned char)X) #define IsAlpha(X) isalpha((unsigned char)X) #if defined(_WIN32) || defined(WIN32) #if SQLITE_OS_WINRT #include <intrin.h> #endif #undef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN |
| ︙ | ︙ | |||
353 354 355 356 357 358 359 360 361 362 363 364 365 366 | ** in the CLI, or other context clues in other applications) for all ** other output channels. ** ** The default behavior, if neither of the above is defined is to ** use O_U8TEXT when writing to the Windows console (or anything ** else for which _isatty() returns true) and to use O_BINARY or O_TEXT ** for all other output channels. */ #if defined(SQLITE_U8TEXT_ONLY) # define UseWtextForOutput(fd) 1 # define UseWtextForInput(fd) 1 # define IsConsole(fd) _isatty(_fileno(fd)) #elif defined(SQLITE_U8TEXT_STDIO) # define UseWtextForOutput(fd) ((fd)==stdout || (fd)==stderr) | > > > > > | 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | ** in the CLI, or other context clues in other applications) for all ** other output channels. ** ** The default behavior, if neither of the above is defined is to ** use O_U8TEXT when writing to the Windows console (or anything ** else for which _isatty() returns true) and to use O_BINARY or O_TEXT ** for all other output channels. ** ** The SQLITE_USE_W32_FOR_CONSOLE_IO macro is also available. If ** defined, it forces the use of Win32 APIs for all console I/O, both ** input and output. This is necessary for some non-Microsoft run-times ** that implement stdio differently from Microsoft/Visual-Studio. */ #if defined(SQLITE_U8TEXT_ONLY) # define UseWtextForOutput(fd) 1 # define UseWtextForInput(fd) 1 # define IsConsole(fd) _isatty(_fileno(fd)) #elif defined(SQLITE_U8TEXT_STDIO) # define UseWtextForOutput(fd) ((fd)==stdout || (fd)==stderr) |
| ︙ | ︙ | |||
455 456 457 458 459 460 461 |
/* When reading from the command-prompt in Windows, it is necessary
** to use _O_WTEXT input mode to read UTF-16 characters, then translate
** that into UTF-8. Otherwise, non-ASCII characters all get translated
** into '?'.
*/
wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
if( b1==0 ) return 0;
| | | | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
/* When reading from the command-prompt in Windows, it is necessary
** to use _O_WTEXT input mode to read UTF-16 characters, then translate
** that into UTF-8. Otherwise, non-ASCII characters all get translated
** into '?'.
*/
wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
if( b1==0 ) return 0;
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
DWORD nRead = 0;
if( IsConsole(in)
&& ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz-1, &nRead, 0)
){
b1[nRead] = 0;
}else
#endif
{
_setmode(_fileno(in), IsConsole(in) ? _O_WTEXT : _O_U8TEXT);
if( fgetws(b1, sz/4, in)==0 ){
|
| ︙ | ︙ | |||
533 534 535 536 537 538 539 |
*/
int sz = (int)strlen(z);
wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) );
if( b1==0 ) return 0;
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
b1[sz] = 0;
| | > | | | 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 |
*/
int sz = (int)strlen(z);
wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) );
if( b1==0 ) return 0;
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
b1[sz] = 0;
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
DWORD nWr = 0;
if( IsConsole(out)
&& WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),b1,sz,&nWr,0)
){
/* If writing to the console, then the WriteConsoleW() is all we
** need to do. */
}else
#endif
{
/* As long as SQLITE_USE_W32_FOR_CONSOLE_IO is not defined, or for
** non-console I/O even if that macro is defined, write using the
** standard library. */
_setmode(_fileno(out), _O_U8TEXT);
if( UseBinaryWText(out) ){
piecemealOutput(b1, sz, out);
}else{
fputws(b1, out);
}
}
|
| ︙ | ︙ | |||
841 842 843 844 845 846 847 | */ static char *Argv0; /* ** Prompt strings. Initialized in main. Settable with ** .prompt main continue */ | | | 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 | */ static char *Argv0; /* ** Prompt strings. Initialized in main. Settable with ** .prompt main continue */ #define PROMPT_LEN_MAX 128 /* First line prompt. default: "sqlite> " */ static char mainPrompt[PROMPT_LEN_MAX]; /* Continuation prompt. default: " ...> " */ static char continuePrompt[PROMPT_LEN_MAX]; /* This is variant of the standard-library strncpy() routine with the ** one change that the destination string is always zero-terminated, even |
| ︙ | ︙ | |||
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 |
}else{
i++;
}
}
return n;
}
#endif
/*
** Output string zUtf to stdout as w characters. If w is negative,
** then right-justify the text. W is the width in UTF-8 characters, not
** in bytes. This is different from the %*.*s specification in printf
** since with %*.*s the width is measured in bytes, not characters.
**
** Take into account zero-width and double-width Unicode characters.
** In other words, a zero-width character does not count toward the
** the w limit. A double-width character counts as two.
*/
static void utf8_width_print(FILE *out, int w, const char *zUtf){
const unsigned char *a = (const unsigned char*)zUtf;
unsigned char c;
int i = 0;
int n = 0;
int aw = w<0 ? -w : w;
if( zUtf==0 ) zUtf = "";
while( (c = a[i])!=0 ){
if( (c&0xc0)==0xc0 ){
int u;
int len = decodeUtf8(a+i, &u);
int x = cli_wcwidth(u);
if( x+n>aw ){
break;
}
i += len;
n += x;
}else if( n>=aw ){
break;
}else{
n++;
i++;
}
}
| > > > > > > > > > > > > > > > > > > > > | 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 |
}else{
i++;
}
}
return n;
}
#endif
/*
** Check to see if z[] is a valid VT100 escape. If it is, then
** return the number of bytes in the escape sequence. Return 0 if
** z[] is not a VT100 escape.
**
** This routine assumes that z[0] is \033 (ESC).
*/
static int isVt100(const unsigned char *z){
int i;
if( z[1]!='[' ) return 0;
i = 2;
while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
if( z[i]<0x40 || z[i]>0x7e ) return 0;
return i+1;
}
/*
** Output string zUtf to stdout as w characters. If w is negative,
** then right-justify the text. W is the width in UTF-8 characters, not
** in bytes. This is different from the %*.*s specification in printf
** since with %*.*s the width is measured in bytes, not characters.
**
** Take into account zero-width and double-width Unicode characters.
** In other words, a zero-width character does not count toward the
** the w limit. A double-width character counts as two.
*/
static void utf8_width_print(FILE *out, int w, const char *zUtf){
const unsigned char *a = (const unsigned char*)zUtf;
unsigned char c;
int i = 0;
int n = 0;
int k;
int aw = w<0 ? -w : w;
if( zUtf==0 ) zUtf = "";
while( (c = a[i])!=0 ){
if( (c&0xc0)==0xc0 ){
int u;
int len = decodeUtf8(a+i, &u);
int x = cli_wcwidth(u);
if( x+n>aw ){
break;
}
i += len;
n += x;
}else if( c==0x1b && (k = isVt100(&a[i]))>0 ){
i += k;
}else if( n>=aw ){
break;
}else{
n++;
i++;
}
}
|
| ︙ | ︙ | |||
1236 1237 1238 1239 1240 1241 1242 |
const char *z2 = z;
while( *z2 ){ z2++; }
return 0x3fffffff & (int)(z2 - z);
}
/*
** Return the length of a string in characters. Multibyte UTF8 characters
| | > | > > > > > > > > | 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 |
const char *z2 = z;
while( *z2 ){ z2++; }
return 0x3fffffff & (int)(z2 - z);
}
/*
** Return the length of a string in characters. Multibyte UTF8 characters
** count as a single character for single-width characters, or as two
** characters for double-width characters.
*/
static int strlenChar(const char *z){
int n = 0;
while( *z ){
if( (0x80&z[0])==0 ){
n++;
z++;
}else{
int u = 0;
int len = decodeUtf8((const u8*)z, &u);
z += len;
n += cli_wcwidth(u);
}
}
return n;
}
/*
** Return open FILE * if zFile exists, can be opened for read
** and is an ordinary file or a character stream source.
|
| ︙ | ︙ | |||
1498 1499 1500 1501 1502 1503 1504 |
** that quoting is required.
**
** Return '"' if quoting is required. Return 0 if no quoting is required.
*/
static char quoteChar(const char *zName){
int i;
if( zName==0 ) return '"';
| | | | 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 |
** that quoting is required.
**
** Return '"' if quoting is required. Return 0 if no quoting is required.
*/
static char quoteChar(const char *zName){
int i;
if( zName==0 ) return '"';
if( !IsAlpha(zName[0]) && zName[0]!='_' ) return '"';
for(i=0; zName[i]; i++){
if( !IsAlnum(zName[i]) && zName[i]!='_' ) return '"';
}
return sqlite3_keyword_check(zName, i) ? '"' : 0;
}
/*
** Construct a fake object name and column list to describe the structure
** of the view, virtual table, or table valued function zSchema.zName.
|
| ︙ | ︙ | |||
1592 1593 1594 1595 1596 1597 1598 | char z[400]; if( n<1 ) n = 1; if( n>350 ) n = 350; sqlite3_snprintf(sizeof(z), z, "%#+.*e", n, r); sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); } | < < < < < < < < < < < < < < < < < < < < < < < < | 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 | char z[400]; if( n<1 ) n = 1; if( n>350 ) n = 350; sqlite3_snprintf(sizeof(z), z, "%#+.*e", n, r); sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); } /* ** SQL function: shell_add_schema(S,X) ** ** Add the schema name X to the CREATE statement in S and return the result. ** Examples: ** ** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); |
| ︙ | ︙ | |||
1698 1699 1700 1701 1702 1703 1704 | ** below by the ../tool/mkshellc.tcl script. Before processing that included ** code, we need to override some macros to make the included program code ** work here in the middle of this regular program. */ #define SQLITE_EXTENSION_INIT1 #define SQLITE_EXTENSION_INIT2(X) (void)(X) | < | | > > > > > > | | > < < < < < | | < < < < < < < < < < < < | < < < < | | < | | < | | < < < < < < < < < < < < < < < < < < < < < < < | < < < | < | < < < < < < | < < < | < < < < < < < | | | | < | < > < | | < < < < < < < < | | | < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < > > > > | < < > > | < < < < < < | < < | | | < < | < < > > | > | < | > > < < < < | < | < < < | < < < | | | > > > | < | < > > > > > > | < > | < < > | < | | | < < < < | | | | < < < | | | < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | | > | > > | < < < < | < | | < < < < < < < < < < | | < < | | < | | | < < < < | | < | < | < < < < < < < < < < < < < < < < < < | | < < | 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 |
** below by the ../tool/mkshellc.tcl script. Before processing that included
** code, we need to override some macros to make the included program code
** work here in the middle of this regular program.
*/
#define SQLITE_EXTENSION_INIT1
#define SQLITE_EXTENSION_INIT2(X) (void)(X)
/************************* Begin ../ext/misc/windirent.h ******************/
/*
** 2025-06-05
**
** 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 implementation of opendir(), readdir(), and closedir() for Windows,
** based on the FindFirstFile(), FindNextFile(), and FindClose() APIs
** of Win32.
**
** #include this file inside any C-code module that needs to use
** opendir()/readdir()/closedir(). This file is a no-op on non-Windows
** machines. On Windows, static functions are defined that implement
** those standard interfaces.
*/
#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H)
#define SQLITE_WINDIRENT_H
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#ifndef FILENAME_MAX
# define FILENAME_MAX (260)
#endif
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISLNK
#define S_ISLNK(m) (0)
#endif
typedef unsigned short mode_t;
/* The dirent object for Windows is abbreviated. The only field really
** usable by applications is d_name[].
*/
struct dirent {
int d_ino; /* Inode number (synthesized) */
unsigned d_attributes; /* File attributes */
char d_name[FILENAME_MAX]; /* Null-terminated filename */
};
/* The internals of DIR are opaque according to standards. So it
** does not matter what we put here. */
typedef struct DIR DIR;
struct DIR {
intptr_t d_handle; /* Handle for findfirst()/findnext() */
struct dirent cur; /* Current entry */
};
/* Ignore hidden and system files */
#define WindowsFileToIgnore(a) \
((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
/*
** Close a previously opened directory
*/
static int closedir(DIR *pDir){
int rc = 0;
if( pDir==0 ){
return EINVAL;
}
if( pDir->d_handle!=0 && pDir->d_handle!=(-1) ){
rc = _findclose(pDir->d_handle);
}
sqlite3_free(pDir);
return rc;
}
/*
** Open a new directory. The directory name should be UTF-8 encoded.
** appropriate translations happen automatically.
*/
static DIR *opendir(const char *zDirName){
DIR *pDir;
wchar_t *b1;
sqlite3_int64 sz;
struct _wfinddata_t data;
pDir = sqlite3_malloc64( sizeof(DIR) );
if( pDir==0 ) return 0;
memset(pDir, 0, sizeof(DIR));
memset(&data, 0, sizeof(data));
sz = strlen(zDirName);
b1 = sqlite3_malloc64( (sz+3)*sizeof(b1[0]) );
if( b1==0 ){
closedir(pDir);
return NULL;
}
sz = MultiByteToWideChar(CP_UTF8, 0, zDirName, sz, b1, sz);
b1[sz++] = '\\';
b1[sz++] = '*';
b1[sz] = 0;
if( sz+1>sizeof(data.name)/sizeof(data.name[0]) ){
closedir(pDir);
sqlite3_free(b1);
return NULL;
}
memcpy(data.name, b1, (sz+1)*sizeof(b1[0]));
sqlite3_free(b1);
pDir->d_handle = _wfindfirst(data.name, &data);
if( pDir->d_handle<0 ){
closedir(pDir);
return NULL;
}
while( WindowsFileToIgnore(data) ){
memset(&data, 0, sizeof(data));
if( _wfindnext(pDir->d_handle, &data)==-1 ){
closedir(pDir);
return NULL;
}
}
pDir->cur.d_ino = 0;
pDir->cur.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
pDir->cur.d_name, FILENAME_MAX, 0, 0);
return pDir;
}
/*
** Read the next entry from a directory.
**
** The returned struct-dirent object is managed by DIR. It is only
** valid until the next readdir() or closedir() call. Only the
** d_name[] field is meaningful. The d_name[] value has been
** translated into UTF8.
*/
static struct dirent *readdir(DIR *pDir){
struct _wfinddata_t data;
if( pDir==0 ) return 0;
if( (pDir->cur.d_ino++)==0 ){
return &pDir->cur;
}
do{
memset(&data, 0, sizeof(data));
if( _wfindnext(pDir->d_handle, &data)==-1 ){
return NULL;
}
}while( WindowsFileToIgnore(data) );
pDir->cur.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
pDir->cur.d_name, FILENAME_MAX, 0, 0);
return &pDir->cur;
}
#endif /* defined(_WIN32) && defined(_MSC_VER) */
/************************* End ../ext/misc/windirent.h ********************/
/************************* Begin ../ext/misc/memtrace.c ******************/
/*
** 2019-01-21
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
|
| ︙ | ︙ | |||
2417 2418 2419 2420 2421 2422 2423 | ** 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" | | | 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 |
** 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 minimum-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');
**
|
| ︙ | ︙ | |||
3638 3639 3640 3641 3642 3643 3644 | ** ** This SQLite extension implements the UINT collating sequence. ** ** UINT works like BINARY for text, except that embedded strings ** of digits compare in numeric order. ** ** * Leading zeros are handled properly, in the sense that | | | 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 | ** ** This SQLite extension implements the UINT collating sequence. ** ** UINT works like BINARY for text, except that embedded strings ** of digits compare in numeric order. ** ** * Leading zeros are handled properly, in the sense that ** they do not mess of the magnitude comparison of embedded ** strings of digits. "x00123y" is equal to "x123y". ** ** * Only unsigned integers are recognized. Plus and minus ** signs are ignored. Decimal points and exponential notation ** are ignored. ** ** * Embedded integers can be of arbitrary length. Comparison |
| ︙ | ︙ | |||
3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 |
/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAMETER
# define UNUSED_PARAMETER(X) (void)(X)
#endif
/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
char sign; /* 0 for positive, 1 for negative */
char oom; /* True if an OOM is encountered */
char isNull; /* True if holds a NULL rather than a number */
| > > > | 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 |
/* Mark a function parameter as unused, to suppress nuisance compiler
** warnings. */
#ifndef UNUSED_PARAMETER
# define UNUSED_PARAMETER(X) (void)(X)
#endif
#ifndef IsSpace
#define IsSpace(X) isspace((unsigned char)X)
#endif
/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
char sign; /* 0 for positive, 1 for negative */
char oom; /* True if an OOM is encountered */
char isNull; /* True if holds a NULL rather than a number */
|
| ︙ | ︙ | |||
3793 3794 3795 3796 3797 3798 3799 | p->oom = 0; p->isInit = 1; p->isNull = 0; p->nDigit = 0; p->nFrac = 0; p->a = sqlite3_malloc64( n+1 ); if( p->a==0 ) goto new_from_text_failed; | | | 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 |
p->oom = 0;
p->isInit = 1;
p->isNull = 0;
p->nDigit = 0;
p->nFrac = 0;
p->a = sqlite3_malloc64( n+1 );
if( p->a==0 ) goto new_from_text_failed;
for(i=0; IsSpace(zIn[i]); i++){}
if( zIn[i]=='-' ){
p->sign = 1;
i++;
}else if( zIn[i]=='+' ){
i++;
}
while( i<n && zIn[i]=='0' ) i++;
|
| ︙ | ︙ | |||
4448 4449 4450 4451 4452 4453 4454 |
decimal_add(pA, pB);
decimal_result(context, pA);
}
decimal_free(pA);
decimal_free(pB);
}
| | | 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 |
decimal_add(pA, pB);
decimal_result(context, pA);
}
decimal_free(pA);
decimal_free(pB);
}
/* Aggregate function: decimal_sum(X)
**
** Works like sum() except that it uses decimal arithmetic for unlimited
** precision.
*/
static void decimalSumStep(
sqlite3_context *context,
int argc,
|
| ︙ | ︙ | |||
4809 4810 4811 4812 4813 4814 4815 | if( bExact ) return -1; return iFirst; } /* ** Generate an error for a percentile function. ** | | | 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 |
if( bExact ) return -1;
return iFirst;
}
/*
** Generate an error for a percentile function.
**
** The error format string must have exactly one occurrence 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;
|
| ︙ | ︙ | |||
5287 5288 5289 5290 5291 5292 5293 |
for( nac=0; nac<4; ++nac ){
char c = (nac<nti)? *pIn++ : b64Numerals[0];
u8 bdp = BX_DV_PROTO(c);
switch( bdp ){
case ND:
/* Treat dark non-digits as pad, but they terminate decode too. */
ncIn = 0;
| | | | > > > | 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 |
for( nac=0; nac<4; ++nac ){
char c = (nac<nti)? *pIn++ : b64Numerals[0];
u8 bdp = BX_DV_PROTO(c);
switch( bdp ){
case ND:
/* Treat dark non-digits as pad, but they terminate decode too. */
ncIn = 0;
deliberate_fall_through; /* FALLTHRU */
case WS:
/* Treat whitespace as pad and terminate this group.*/
nti = nac;
deliberate_fall_through; /* FALLTHRU */
case PC:
bdp = 0;
--nbo;
deliberate_fall_through; /* FALLTHRU */
default: /* bdp is the digit value. */
qv = qv<<6 | bdp;
break;
}
}
switch( nbo ){
case 3:
pOut[2] = (qv) & 0xff;
deliberate_fall_through; /* FALLTHRU */
case 2:
pOut[1] = (qv>>8) & 0xff;
deliberate_fall_through; /* FALLTHRU */
case 1:
pOut[0] = (qv>>16) & 0xff;
break;
}
pOut += nbo;
}
return pOut;
}
/* This function does the work for the SQLite base64(x) UDF. */
|
| ︙ | ︙ | |||
5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 |
qv = 85 * qv + (c - cdo);
--nti;
}
nbo -= nti; /* Adjust for early (non-digit) end of group. */
switch( nbo ){
case 4:
*pOut++ = (qv >> 24)&0xff;
case 3:
*pOut++ = (qv >> 16)&0xff;
case 2:
*pOut++ = (qv >> 8)&0xff;
case 1:
*pOut++ = qv&0xff;
case 0:
break;
}
}
return pOut;
}
| > > > > | 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 |
qv = 85 * qv + (c - cdo);
--nti;
}
nbo -= nti; /* Adjust for early (non-digit) end of group. */
switch( nbo ){
case 4:
*pOut++ = (qv >> 24)&0xff;
/* FALLTHRU */
case 3:
*pOut++ = (qv >> 16)&0xff;
/* FALLTHRU */
case 2:
*pOut++ = (qv >> 8)&0xff;
/* FALLTHRU */
case 1:
*pOut++ = qv&0xff;
/* FALLTHRU */
case 0:
break;
}
}
return pOut;
}
|
| ︙ | ︙ | |||
5942 5943 5944 5945 5946 5947 5948 |
**
** Here is a query to show various boundry values for the binary64
** number format:
**
** WITH c(name,bin) AS (VALUES
** ('minimum positive value', x'0000000000000001'),
** ('maximum subnormal value', x'000fffffffffffff'),
| | | 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 |
**
** Here is a query to show various boundry values for the binary64
** number format:
**
** WITH c(name,bin) AS (VALUES
** ('minimum positive value', x'0000000000000001'),
** ('maximum subnormal value', x'000fffffffffffff'),
** ('minimum positive normal value', x'0010000000000000'),
** ('maximum value', x'7fefffffffffffff'))
** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v)
** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin);
**
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
|
| ︙ | ︙ | |||
6250 6251 6252 6253 6254 6255 6256 | ** CREATE TABLE generate_series( ** value, ** start HIDDEN, ** stop HIDDEN, ** step HIDDEN ** ); ** | | < | 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 | ** CREATE TABLE generate_series( ** value, ** start HIDDEN, ** stop HIDDEN, ** step HIDDEN ** ); ** ** The virtual table also has a rowid which is an alias for the value. ** ** Function arguments in queries against this virtual table are translated ** into equality constraints against successive hidden columns. In other ** words, the following pairs of queries are equivalent to each other: ** ** SELECT * FROM generate_series(0,100,5); ** SELECT * FROM generate_series WHERE start=0 AND stop=100 AND step=5; |
| ︙ | ︙ | |||
6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 | ** */ /* #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. */ | > | 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 | ** */ /* #include "sqlite3ext.h" */ SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <limits.h> #include <math.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. */ |
| ︙ | ︙ | |||
6329 6330 6331 6332 6333 6334 6335 |
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
| | | 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 |
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 overflow. */
if( ix>=2 ){
sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
smBase += ix2*smStep;
ix -= ix2;
}
return smBase + ((sqlite3_int64)ix)*smStep;
}
|
| ︙ | ︙ | |||
6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 |
sqlite3_vtab **ppVtab,
char **pzErrUnused
){
sqlite3_vtab *pNew;
int rc;
/* Column numbers */
#define SERIES_COLUMN_VALUE 0
#define SERIES_COLUMN_START 1
#define SERIES_COLUMN_STOP 2
#define SERIES_COLUMN_STEP 3
(void)pUnused;
(void)argcUnused;
| > | 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 |
sqlite3_vtab **ppVtab,
char **pzErrUnused
){
sqlite3_vtab *pNew;
int rc;
/* Column numbers */
#define SERIES_COLUMN_ROWID (-1)
#define SERIES_COLUMN_VALUE 0
#define SERIES_COLUMN_START 1
#define SERIES_COLUMN_STOP 2
#define SERIES_COLUMN_STEP 3
(void)pUnused;
(void)argcUnused;
|
| ︙ | ︙ | |||
6553 6554 6555 6556 6557 6558 6559 | #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 /* | | < | < | 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 |
#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
/*
** The rowid is the same as the value.
*/
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
series_cursor *pCur = (series_cursor*)cur;
*pRowid = pCur->ss.iValueNow;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
|
| ︙ | ︙ | |||
6672 6673 6674 6675 6676 6677 6678 |
}
if( idxNum & 0x3380 ){
/* Extract the maximum range of output values determined by
** constraints on the "value" column.
*/
if( idxNum & 0x0080 ){
| > > > > > > > > | > > > > > > > > > | | | | | | > > > > > > > > > | | | | | | > < | < | | 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 |
}
if( idxNum & 0x3380 ){
/* Extract the maximum range of output values determined by
** constraints on the "value" column.
*/
if( idxNum & 0x0080 ){
if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[i++]);
if( r==ceil(r) ){
iMin = iMax = (sqlite3_int64)r;
}else{
returnNoRows = 1;
}
}else{
iMin = iMax = sqlite3_value_int64(argv[i++]);
}
}else{
if( idxNum & 0x0300 ){
if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[i++]);
if( idxNum & 0x0200 && r==ceil(r) ){
iMin = (sqlite3_int64)ceil(r+1.0);
}else{
iMin = (sqlite3_int64)ceil(r);
}
}else{
iMin = sqlite3_value_int64(argv[i++]);
if( idxNum & 0x0200 ){
if( iMin==LARGEST_INT64 ){
returnNoRows = 1;
}else{
iMin++;
}
}
}
}
if( idxNum & 0x3000 ){
if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[i++]);
if( (idxNum & 0x2000)!=0 && r==floor(r) ){
iMax = (sqlite3_int64)(r-1.0);
}else{
iMax = (sqlite3_int64)floor(r);
}
}else{
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 ){
pCur->ss.iTerm = iMax;
}
}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 ){
pCur->ss.iTerm = iMin;
}
}
}
/* Apply LIMIT and OFFSET constraints, if any */
if( idxNum & 0x20 ){
if( iOffset>0 ){
|
| ︙ | ︙ | |||
6746 6747 6748 6749 6750 6751 6752 |
}
}
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.
| | | 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 |
}
}
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://sqlite.org/src/info/fac496b61722daf2 */
returnNoRows = 1;
break;
}
}
if( returnNoRows ){
pCur->ss.iBase = 1;
pCur->ss.iTerm = 0;
|
| ︙ | ︙ | |||
6849 6850 6851 6852 6853 6854 6855 |
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
aIdx[4] = i;
idxNum |= 0x40;
}
continue;
}
if( pConstraint->iColumn<SERIES_COLUMN_START ){
| | > > > | 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 |
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
aIdx[4] = i;
idxNum |= 0x40;
}
continue;
}
if( pConstraint->iColumn<SERIES_COLUMN_START ){
if( (pConstraint->iColumn==SERIES_COLUMN_VALUE ||
pConstraint->iColumn==SERIES_COLUMN_ROWID)
&& pConstraint->usable
){
switch( op ){
case SQLITE_INDEX_CONSTRAINT_EQ:
case SQLITE_INDEX_CONSTRAINT_IS: {
idxNum |= 0x0080;
idxNum &= ~0x3300;
aIdx[5] = i;
aIdx[6] = -1;
|
| ︙ | ︙ | |||
7690 7691 7692 7693 7694 7695 7696 | return 0; } /* Free and reclaim all the memory used by a previously compiled ** regular expression. Applications should invoke this routine once ** for every call to re_compile() to avoid memory leaks. */ | | > | 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 |
return 0;
}
/* Free and reclaim all the memory used by a previously compiled
** regular expression. Applications should invoke this routine once
** for every call to re_compile() to avoid memory leaks.
*/
static void re_free(void *p){
ReCompiled *pRe = (ReCompiled*)p;
if( pRe ){
sqlite3_free(pRe->aOp);
sqlite3_free(pRe->aArg);
sqlite3_free(pRe);
}
}
|
| ︙ | ︙ | |||
7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 | ** ** name: Path to file or directory (text value). ** mode: Value of stat.st_mode for directory entry (an integer). ** mtime: Value of stat.st_mtime for directory entry (an integer). ** data: For a regular file, a blob containing the file data. For a ** symlink, a text value containing the text of the link. For a ** directory, NULL. ** ** If a non-NULL value is specified for the optional $dir parameter and ** $path is a relative path, then $path is interpreted relative to $dir. ** And the paths returned in the "name" column of the table are also ** relative to directory $dir. ** ** Notes on building this extension for Windows: | > | 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 | ** ** name: Path to file or directory (text value). ** mode: Value of stat.st_mode for directory entry (an integer). ** mtime: Value of stat.st_mtime for directory entry (an integer). ** data: For a regular file, a blob containing the file data. For a ** symlink, a text value containing the text of the link. For a ** directory, NULL. ** level: Directory hierarchy level. Topmost is 1. ** ** If a non-NULL value is specified for the optional $dir parameter and ** $path is a relative path, then $path is interpreted relative to $dir. ** And the paths returned in the "name" column of the table are also ** relative to directory $dir. ** ** Notes on building this extension for Windows: |
| ︙ | ︙ | |||
8011 8012 8013 8014 8015 8016 8017 8018 | #include <sys/stat.h> #include <fcntl.h> #if !defined(_WIN32) && !defined(WIN32) # include <unistd.h> # include <dirent.h> # include <utime.h> # include <sys/time.h> #else | > | < < | < | < < < < | < | | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 |
#include <sys/stat.h>
#include <fcntl.h>
#if !defined(_WIN32) && !defined(WIN32)
# include <unistd.h>
# include <dirent.h>
# include <utime.h>
# include <sys/time.h>
# define STRUCT_STAT struct stat
#else
/* # include "windirent.h" */
# include <direct.h>
# define STRUCT_STAT struct _stat
# define chmod(path,mode) fileio_chmod(path,mode)
# define mkdir(path,mode) fileio_mkdir(path)
#endif
#include <time.h>
#include <errno.h>
/* When used as part of the CLI, the sqlite3_stdio.h module will have
** been included before this one. In that case use the sqlite3_stdio.h
** #defines. If not, create our own for fopen().
*/
#ifndef _SQLITE3_STDIO_H_
# define sqlite3_fopen fopen
#endif
/*
** Structure of the fsdir() table-valued function
*/
/* 0 1 2 3 4 5 6 */
#define FSDIR_SCHEMA "(name,mode,mtime,data,level,path HIDDEN,dir HIDDEN)"
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
#define FSDIR_COLUMN_MODE 1 /* Access mode */
#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
#define FSDIR_COLUMN_DATA 3 /* File content */
#define FSDIR_COLUMN_LEVEL 4 /* Level. Topmost is 1 */
#define FSDIR_COLUMN_PATH 5 /* Path to top of search */
#define FSDIR_COLUMN_DIR 6 /* Path is relative to this directory */
/*
** UTF8 chmod() function for Windows
*/
#if defined(_WIN32) || defined(WIN32)
static int fileio_chmod(const char *zPath, int pmode){
sqlite3_int64 sz = strlen(zPath);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
if( b1==0 ) return -1;
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
b1[sz] = 0;
rc = _wchmod(b1, pmode);
sqlite3_free(b1);
return rc;
}
#endif
/*
** UTF8 mkdir() function for Windows
*/
#if defined(_WIN32) || defined(WIN32)
static int fileio_mkdir(const char *zPath){
sqlite3_int64 sz = strlen(zPath);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
if( b1==0 ) return -1;
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
b1[sz] = 0;
rc = _wmkdir(b1);
sqlite3_free(b1);
return rc;
}
#endif
/*
** Set the result stored by context ctx to a blob containing the
** contents of file zName. Or, leave the result unchanged (NULL)
** if the file does not exist or is unreadable.
**
|
| ︙ | ︙ | |||
8179 8180 8181 8182 8183 8184 8185 | /* ** This function attempts to normalize the time values found in the stat() ** buffer to UTC. This is necessary on Win32, where the runtime library ** appears to return these values as local times. */ static void statTimesToUtc( const char *zPath, | | | 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 |
/*
** This function attempts to normalize the time values found in the stat()
** buffer to UTC. This is necessary on Win32, where the runtime library
** appears to return these values as local times.
*/
static void statTimesToUtc(
const char *zPath,
STRUCT_STAT *pStatBuf
){
HANDLE hFindFile;
WIN32_FIND_DATAW fd;
LPWSTR zUnicodeName;
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
if( zUnicodeName ){
|
| ︙ | ︙ | |||
8207 8208 8209 8210 8211 8212 8213 | /* ** This function is used in place of stat(). On Windows, special handling ** is required in order for the included time to be returned as UTC. On all ** other systems, this function simply calls stat(). */ static int fileStat( const char *zPath, | | > > | > > > > | | < < | 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 |
/*
** This function is used in place of stat(). On Windows, special handling
** is required in order for the included time to be returned as UTC. On all
** other systems, this function simply calls stat().
*/
static int fileStat(
const char *zPath,
STRUCT_STAT *pStatBuf
){
#if defined(_WIN32)
sqlite3_int64 sz = strlen(zPath);
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
if( b1==0 ) return 1;
sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
b1[sz] = 0;
rc = _wstat(b1, pStatBuf);
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
return rc;
#else
return stat(zPath, pStatBuf);
#endif
}
/*
** This function is used in place of lstat(). On Windows, special handling
** is required in order for the included time to be returned as UTC. On all
** other systems, this function simply calls lstat().
*/
static int fileLinkStat(
const char *zPath,
STRUCT_STAT *pStatBuf
){
#if defined(_WIN32)
return fileStat(zPath, pStatBuf);
#else
return lstat(zPath, pStatBuf);
#endif
}
/*
** Argument zFile is the name of a file that will be created and/or written
|
| ︙ | ︙ | |||
8260 8261 8262 8263 8264 8265 8266 |
if( zCopy==0 ){
rc = SQLITE_NOMEM;
}else{
int nCopy = (int)strlen(zCopy);
int i = 1;
while( rc==SQLITE_OK ){
| | | 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 |
if( zCopy==0 ){
rc = SQLITE_NOMEM;
}else{
int nCopy = (int)strlen(zCopy);
int i = 1;
while( rc==SQLITE_OK ){
STRUCT_STAT sStat;
int rc2;
for(; zCopy[i]!='/' && i<nCopy; i++);
if( i==nCopy ) break;
zCopy[i] = '\0';
rc2 = fileStat(zCopy, &sStat);
|
| ︙ | ︙ | |||
8310 8311 8312 8313 8314 8315 8316 |
{
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
** path and either the permissions already match or can be changed
** to do so using chmod(), it is not an error. */
| | | 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 |
{
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
** path and either the permissions already match or can be changed
** to do so using chmod(), it is not an error. */
STRUCT_STAT sStat;
if( errno!=EEXIST
|| 0!=fileStat(zFile, &sStat)
|| !S_ISDIR(sStat.st_mode)
|| ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777))
){
return 1;
}
|
| ︙ | ︙ | |||
8356 8357 8358 8359 8360 8361 8362 |
LONGLONG intervals;
HANDLE hFile;
LPWSTR zUnicodeName;
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
GetSystemTime(¤tTime);
SystemTimeToFileTime(¤tTime, &lastAccess);
| | | 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 |
LONGLONG intervals;
HANDLE hFile;
LPWSTR zUnicodeName;
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
GetSystemTime(¤tTime);
SystemTimeToFileTime(¤tTime, &lastAccess);
intervals = (mtime*10000000) + 116444736000000000;
lastWrite.dwLowDateTime = (DWORD)intervals;
lastWrite.dwHighDateTime = intervals >> 32;
zUnicodeName = sqlite3_win32_utf8_to_unicode(zFile);
if( zUnicodeName==0 ){
return 1;
}
hFile = CreateFileW(
|
| ︙ | ︙ | |||
8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 |
char *zDir; /* Name of directory (nul-terminated) */
};
struct fsdir_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
int nLvl; /* Number of entries in aLvl[] array */
int iLvl; /* Index of current entry */
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
const char *zBase;
int nBase;
| > | | 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 |
char *zDir; /* Name of directory (nul-terminated) */
};
struct fsdir_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
int nLvl; /* Number of entries in aLvl[] array */
int mxLvl; /* Maximum level */
int iLvl; /* Index of current entry */
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
const char *zBase;
int nBase;
STRUCT_STAT sStat; /* Current lstat() results */
char *zPath; /* Path to current entry */
sqlite3_int64 iRowid; /* Current rowid */
};
typedef struct fsdir_tab fsdir_tab;
struct fsdir_tab {
sqlite3_vtab base; /* Base class - must be first */
|
| ︙ | ︙ | |||
8624 8625 8626 8627 8628 8629 8630 |
** Advance an fsdir_cursor to its next row of output.
*/
static int fsdirNext(sqlite3_vtab_cursor *cur){
fsdir_cursor *pCur = (fsdir_cursor*)cur;
mode_t m = pCur->sStat.st_mode;
pCur->iRowid++;
| | | 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 |
** Advance an fsdir_cursor to its next row of output.
*/
static int fsdirNext(sqlite3_vtab_cursor *cur){
fsdir_cursor *pCur = (fsdir_cursor*)cur;
mode_t m = pCur->sStat.st_mode;
pCur->iRowid++;
if( S_ISDIR(m) && pCur->iLvl+3<pCur->mxLvl ){
/* Descend into this directory */
int iNew = pCur->iLvl + 1;
FsdirLevel *pLvl;
if( iNew>=pCur->nLvl ){
int nNew = iNew+1;
sqlite3_int64 nByte = nNew*sizeof(FsdirLevel);
FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc64(pCur->aLvl, nByte);
|
| ︙ | ︙ | |||
8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 |
sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
if( aBuf!=aStatic ) sqlite3_free(aBuf);
#endif
}else{
readFileContents(ctx, pCur->zPath);
}
}
case FSDIR_COLUMN_PATH:
default: {
/* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
** always return their values as NULL */
break;
}
}
| > > > > | 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642 8643 |
sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
if( aBuf!=aStatic ) sqlite3_free(aBuf);
#endif
}else{
readFileContents(ctx, pCur->zPath);
}
break;
}
case FSDIR_COLUMN_LEVEL:
sqlite3_result_int(ctx, pCur->iLvl+2);
break;
case FSDIR_COLUMN_PATH:
default: {
/* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
** always return their values as NULL */
break;
}
}
|
| ︙ | ︙ | |||
8766 8767 8768 8769 8770 8771 8772 | fsdir_cursor *pCur = (fsdir_cursor*)cur; return (pCur->zPath==0); } /* ** xFilter callback. ** | | | > > > > | > > | | > > > > > > > > | 8663 8664 8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 8679 8680 8681 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 8697 8698 8699 8700 8701 8702 8703 8704 8705 8706 8707 8708 8709 8710 8711 8712 8713 8714 8715 8716 |
fsdir_cursor *pCur = (fsdir_cursor*)cur;
return (pCur->zPath==0);
}
/*
** xFilter callback.
**
** idxNum bit Meaning
** 0x01 PATH=N
** 0x02 DIR=N
** 0x04 LEVEL<N
** 0x08 LEVEL<=N
*/
static int fsdirFilter(
sqlite3_vtab_cursor *cur,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
const char *zDir = 0;
fsdir_cursor *pCur = (fsdir_cursor*)cur;
int i;
(void)idxStr;
fsdirResetCursor(pCur);
if( idxNum==0 ){
fsdirSetErrmsg(pCur, "table function fsdir requires an argument");
return SQLITE_ERROR;
}
assert( (idxNum & 0x01)!=0 && argc>0 );
zDir = (const char*)sqlite3_value_text(argv[0]);
if( zDir==0 ){
fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
return SQLITE_ERROR;
}
i = 1;
if( (idxNum & 0x02)!=0 ){
assert( argc>i );
pCur->zBase = (const char*)sqlite3_value_text(argv[i++]);
}
if( (idxNum & 0x0c)!=0 ){
assert( argc>i );
pCur->mxLvl = sqlite3_value_int(argv[i++]);
if( idxNum & 0x08 ) pCur->mxLvl++;
if( pCur->mxLvl<=0 ) pCur->mxLvl = 1000000000;
}else{
pCur->mxLvl = 1000000000;
}
if( pCur->zBase ){
pCur->nBase = (int)strlen(pCur->zBase)+1;
pCur->zPath = sqlite3_mprintf("%s/%s", pCur->zBase, zDir);
}else{
pCur->zPath = sqlite3_mprintf("%s", zDir);
}
|
| ︙ | ︙ | |||
8820 8821 8822 8823 8824 8825 8826 | ** 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. ** | | | | > > > > | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > | > > | | | 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802 8803 8804 8805 8806 8807 8808 8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 8826 8827 8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838 8839 |
** 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:
**
** 0x01 The path value is supplied by argv[0]
** 0x02 dir is in argv[1]
** 0x04 maxdepth is in argv[1] or [2]
*/
static int fsdirBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
int idxLevel = -1; /* Index in pIdxInfo->aConstraint of LEVEL< or <= */
int idxLevelEQ = 0; /* 0x08 for LEVEL<= or LEVEL=. 0x04 for LEVEL< */
int omitLevel = 0; /* omit the LEVEL constraint */
int seenPath = 0; /* True if an unusable PATH= constraint is seen */
int seenDir = 0; /* True if an unusable DIR= constraint is seen */
const struct sqlite3_index_constraint *pConstraint;
(void)tab;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
switch( pConstraint->iColumn ){
case FSDIR_COLUMN_PATH: {
if( pConstraint->usable ){
idxPath = i;
seenPath = 0;
}else if( idxPath<0 ){
seenPath = 1;
}
break;
}
case FSDIR_COLUMN_DIR: {
if( pConstraint->usable ){
idxDir = i;
seenDir = 0;
}else if( idxDir<0 ){
seenDir = 1;
}
break;
}
case FSDIR_COLUMN_LEVEL: {
if( pConstraint->usable && idxLevel<0 ){
idxLevel = i;
idxLevelEQ = 0x08;
omitLevel = 0;
}
break;
}
}
}else
if( pConstraint->iColumn==FSDIR_COLUMN_LEVEL
&& pConstraint->usable
&& idxLevel<0
){
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
idxLevel = i;
idxLevelEQ = 0x08;
omitLevel = 1;
}else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
idxLevel = i;
idxLevelEQ = 0x04;
omitLevel = 1;
}
}
}
if( seenPath || seenDir ){
/* If input parameters are unusable, disallow this plan */
return SQLITE_CONSTRAINT;
}
if( idxPath<0 ){
pIdxInfo->idxNum = 0;
/* The pIdxInfo->estimatedCost should have been initialized to a huge
** number. Leave it unchanged. */
pIdxInfo->estimatedRows = 0x7fffffff;
}else{
pIdxInfo->aConstraintUsage[idxPath].omit = 1;
pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
pIdxInfo->idxNum = 0x01;
pIdxInfo->estimatedCost = 1.0e9;
i = 2;
if( idxDir>=0 ){
pIdxInfo->aConstraintUsage[idxDir].omit = 1;
pIdxInfo->aConstraintUsage[idxDir].argvIndex = i++;
pIdxInfo->idxNum |= 0x02;
pIdxInfo->estimatedCost /= 1.0e4;
}
if( idxLevel>=0 ){
pIdxInfo->aConstraintUsage[idxLevel].omit = omitLevel;
pIdxInfo->aConstraintUsage[idxLevel].argvIndex = i++;
pIdxInfo->idxNum |= idxLevelEQ;
pIdxInfo->estimatedCost /= 1.0e4;
}
}
return SQLITE_OK;
}
/*
|
| ︙ | ︙ | |||
9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 |
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* completion_vtab is a subclass of sqlite3_vtab which will
** serve as the underlying representation of a completion virtual table
*/
typedef struct completion_vtab completion_vtab;
struct completion_vtab {
sqlite3_vtab base; /* Base class - must be first */
| > > > > > | 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 |
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <ctype.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
#ifndef IsAlnum
#define IsAlnum(X) isalnum((unsigned char)X)
#endif
/* completion_vtab is a subclass of sqlite3_vtab which will
** serve as the underlying representation of a completion virtual table
*/
typedef struct completion_vtab completion_vtab;
struct completion_vtab {
sqlite3_vtab base; /* Base class - must be first */
|
| ︙ | ︙ | |||
9345 9346 9347 9348 9349 9350 9351 |
if( pCur->nLine>0 ){
pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zLine==0 ) return SQLITE_NOMEM;
}
}
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
int i = pCur->nLine;
| | | 9294 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 |
if( pCur->nLine>0 ){
pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zLine==0 ) return SQLITE_NOMEM;
}
}
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
int i = pCur->nLine;
while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
i--;
}
pCur->nPrefix = pCur->nLine - i;
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
}
|
| ︙ | ︙ | |||
14752 14753 14754 14755 14756 14757 14758 |
}
/* Register the auth callback with dbv */
if( rc==SQLITE_OK ){
sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew);
}
| | | 14701 14702 14703 14704 14705 14706 14707 14708 14709 14710 14711 14712 14713 14714 14715 |
}
/* Register the auth callback with dbv */
if( rc==SQLITE_OK ){
sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew);
}
/* If an error has occurred, free the new object and return NULL. Otherwise,
** return the new sqlite3expert handle. */
if( rc!=SQLITE_OK ){
sqlite3_expert_destroy(pNew);
pNew = 0;
}
return pNew;
}
|
| ︙ | ︙ | |||
16274 16275 16276 16277 16278 16279 16280 |
**
** To enable all output (which is the default setting):
**
** PRAGMA vfstrace('+all');
**
** Individual APIs can be enabled or disabled by name, with or without
** the initial "x" character. For example, to set up for tracing lock
| | | 16223 16224 16225 16226 16227 16228 16229 16230 16231 16232 16233 16234 16235 16236 16237 |
**
** To enable all output (which is the default setting):
**
** PRAGMA vfstrace('+all');
**
** Individual APIs can be enabled or disabled by name, with or without
** the initial "x" character. For example, to set up for tracing lock
** primitives only:
**
** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock');
**
** The argument to the vfstrace pragma ignores capitalization and any
** characters other than alphabetics, '+', and '-'.
*/
#include <stdlib.h>
|
| ︙ | ︙ | |||
16342 16343 16344 16345 16346 16347 16348 16349 16350 16351 16352 16353 16354 16355 | #define VTR_DLERR 0x00200000 #define VTR_DLSYM 0x00400000 #define VTR_DLCLOSE 0x00800000 #define VTR_RAND 0x01000000 #define VTR_SLEEP 0x02000000 #define VTR_CURTIME 0x04000000 #define VTR_LASTERR 0x08000000 /* ** Method declarations for vfstrace_file. */ static int vfstraceClose(sqlite3_file*); static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64); | > | 16291 16292 16293 16294 16295 16296 16297 16298 16299 16300 16301 16302 16303 16304 16305 | #define VTR_DLERR 0x00200000 #define VTR_DLSYM 0x00400000 #define VTR_DLCLOSE 0x00800000 #define VTR_RAND 0x01000000 #define VTR_SLEEP 0x02000000 #define VTR_CURTIME 0x04000000 #define VTR_LASTERR 0x08000000 #define VTR_FETCH 0x10000000 /* Also coverse xUnfetch */ /* ** Method declarations for vfstrace_file. */ static int vfstraceClose(sqlite3_file*); static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64); |
| ︙ | ︙ | |||
16734 16735 16736 16737 16738 16739 16740 |
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
case SQLITE_FCNTL_PRAGMA: {
const char *const* a = (const char*const*)pArg;
if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
const u8 *zArg = (const u8*)a[2];
| | | 16684 16685 16686 16687 16688 16689 16690 16691 16692 16693 16694 16695 16696 16697 16698 |
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
case SQLITE_FCNTL_PRAGMA: {
const char *const* a = (const char*const*)pArg;
if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
const u8 *zArg = (const u8*)a[2];
if( zArg[0]>='0' && zArg[0]<='9' ){
pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0);
}else{
static const struct {
const char *z;
unsigned int m;
} aKw[] = {
{ "all", 0xffffffff },
|
| ︙ | ︙ | |||
16771 16772 16773 16774 16775 16776 16777 16778 16779 16780 16781 16782 16783 16784 |
{ "dlsym", VTR_DLSYM },
{ "dlclose", VTR_DLCLOSE },
{ "randomness", VTR_RAND },
{ "sleep", VTR_SLEEP },
{ "currenttime", VTR_CURTIME },
{ "currenttimeint64", VTR_CURTIME },
{ "getlasterror", VTR_LASTERR },
};
int onOff = 1;
while( zArg[0] ){
int jj, n;
while( zArg[0]!=0 && zArg[0]!='-' && zArg[0]!='+'
&& !isalpha(zArg[0]) ) zArg++;
if( zArg[0]==0 ) break;
| > | 16721 16722 16723 16724 16725 16726 16727 16728 16729 16730 16731 16732 16733 16734 16735 |
{ "dlsym", VTR_DLSYM },
{ "dlclose", VTR_DLCLOSE },
{ "randomness", VTR_RAND },
{ "sleep", VTR_SLEEP },
{ "currenttime", VTR_CURTIME },
{ "currenttimeint64", VTR_CURTIME },
{ "getlasterror", VTR_LASTERR },
{ "fetch", VTR_FETCH },
};
int onOff = 1;
while( zArg[0] ){
int jj, n;
while( zArg[0]!=0 && zArg[0]!='-' && zArg[0]!='+'
&& !isalpha(zArg[0]) ) zArg++;
if( zArg[0]==0 ) break;
|
| ︙ | ︙ | |||
16998 16999 17000 17001 17002 17003 17004 |
vfstraceOnOff(pInfo, VTR_SHMUNMAP);
vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)",
pInfo->zVfsName, p->zFName, delFlag);
rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
| > > > > > > > > > > | > > > > > > > > > > > | 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 |
vfstraceOnOff(pInfo, VTR_SHMUNMAP);
vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)",
pInfo->zVfsName, p->zFName, delFlag);
rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
static int vfstraceFetch(sqlite3_file *pFile, i64 iOff, int nAmt, void **pptr){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_FETCH);
vfstrace_printf(pInfo, "%s.xFetch(%s,iOff=%lld,nAmt=%d,p=%p)",
pInfo->zVfsName, p->zFName, iOff, nAmt, *pptr);
rc = p->pReal->pMethods->xFetch(p->pReal, iOff, nAmt, pptr);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
static int vfstraceUnfetch(sqlite3_file *pFile, i64 iOff, void *ptr){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_FETCH);
vfstrace_printf(pInfo, "%s.xUnfetch(%s,iOff=%lld,p=%p)",
pInfo->zVfsName, p->zFName, iOff, ptr);
rc = p->pReal->pMethods->xUnfetch(p->pReal, iOff, ptr);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Open an vfstrace file handle.
*/
static int vfstraceOpen(
sqlite3_vfs *pVfs,
|
| ︙ | ︙ | |||
17045 17046 17047 17048 17049 17050 17051 17052 17053 17054 17055 17056 17057 17058 |
pNew->xDeviceCharacteristics = vfstraceDeviceCharacteristics;
if( pNew->iVersion>=2 ){
pNew->xShmMap = pSub->xShmMap ? vfstraceShmMap : 0;
pNew->xShmLock = pSub->xShmLock ? vfstraceShmLock : 0;
pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0;
pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0;
}
pFile->pMethods = pNew;
}
vfstrace_print_errcode(pInfo, " -> %s", rc);
if( pOutFlags ){
vfstrace_printf(pInfo, ", outFlags=0x%x\n", *pOutFlags);
}else{
vfstrace_printf(pInfo, "\n");
| > > > > | 17017 17018 17019 17020 17021 17022 17023 17024 17025 17026 17027 17028 17029 17030 17031 17032 17033 17034 |
pNew->xDeviceCharacteristics = vfstraceDeviceCharacteristics;
if( pNew->iVersion>=2 ){
pNew->xShmMap = pSub->xShmMap ? vfstraceShmMap : 0;
pNew->xShmLock = pSub->xShmLock ? vfstraceShmLock : 0;
pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0;
pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0;
}
if( pNew->iVersion>=3 ){
pNew->xFetch = pSub->xFetch ? vfstraceFetch : 0;
pNew->xUnfetch = pSub->xUnfetch ? vfstraceUnfetch : 0;
}
pFile->pMethods = pNew;
}
vfstrace_print_errcode(pInfo, " -> %s", rc);
if( pOutFlags ){
vfstrace_printf(pInfo, ", outFlags=0x%x\n", *pOutFlags);
}else{
vfstrace_printf(pInfo, "\n");
|
| ︙ | ︙ | |||
17160 17161 17162 17163 17164 17165 17166 |
/*
** Close the dynamic library handle pHandle.
*/
static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_DLCLOSE);
| | | 17136 17137 17138 17139 17140 17141 17142 17143 17144 17145 17146 17147 17148 17149 17150 |
/*
** Close the dynamic library handle pHandle.
*/
static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_DLCLOSE);
vfstrace_printf(pInfo, "%s.xDlClose()\n", pInfo->zVfsName);
pRoot->xDlClose(pRoot, pHandle);
}
/*
** Populate the buffer pointed to by zBufOut with nByte bytes of
** random data.
*/
|
| ︙ | ︙ | |||
18609 18610 18611 18612 18613 18614 18615 18616 18617 18618 18619 18620 18621 18622 |
int rc = sqlite3_create_module(db, "sqlite_dbdata", &dbdata_module, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1);
}
return rc;
}
int sqlite3_dbdata_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
(void)pzErrMsg;
return sqlite3DbdataRegister(db);
| > > > | 18585 18586 18587 18588 18589 18590 18591 18592 18593 18594 18595 18596 18597 18598 18599 18600 18601 |
int rc = sqlite3_create_module(db, "sqlite_dbdata", &dbdata_module, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1);
}
return rc;
}
#ifdef _WIN32
#endif
int sqlite3_dbdata_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
(void)pzErrMsg;
return sqlite3DbdataRegister(db);
|
| ︙ | ︙ | |||
18657 18658 18659 18660 18661 18662 18663 18664 18665 18666 18667 18668 18669 18670 | #endif int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*); /* typedef unsigned int u32; */ /* typedef unsigned char u8; */ /* typedef sqlite3_int64 i64; */ typedef struct RecoverTable RecoverTable; typedef struct RecoverColumn RecoverColumn; /* ** When recovering rows of data that can be associated with table ** definitions recovered from the sqlite_schema table, each table is ** represented by an instance of the following object. | > > > > > > > > > > | 18636 18637 18638 18639 18640 18641 18642 18643 18644 18645 18646 18647 18648 18649 18650 18651 18652 18653 18654 18655 18656 18657 18658 18659 | #endif int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*); /* typedef unsigned int u32; */ /* typedef unsigned char u8; */ /* typedef sqlite3_int64 i64; */ /* ** Work around C99 "flex-array" syntax for pre-C99 compilers, so as ** to avoid complaints from -fsanitize=strict-bounds. */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) # define FLEXARRAY #else # define FLEXARRAY 1 #endif typedef struct RecoverTable RecoverTable; typedef struct RecoverColumn RecoverColumn; /* ** When recovering rows of data that can be associated with table ** definitions recovered from the sqlite_schema table, each table is ** represented by an instance of the following object. |
| ︙ | ︙ | |||
18764 18765 18766 18767 18768 18769 18770 |
** false if it is clear:
**
** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0
*/
typedef struct RecoverBitmap RecoverBitmap;
struct RecoverBitmap {
i64 nPg; /* Size of bitmap */
| | > > > | 18753 18754 18755 18756 18757 18758 18759 18760 18761 18762 18763 18764 18765 18766 18767 18768 18769 18770 18771 |
** false if it is clear:
**
** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0
*/
typedef struct RecoverBitmap RecoverBitmap;
struct RecoverBitmap {
i64 nPg; /* Size of bitmap */
u32 aElem[FLEXARRAY]; /* Array of 32-bit bitmasks */
};
/* Size in bytes of a RecoverBitmap object sufficient to cover 32 pages */
#define SZ_RECOVERBITMAP_32 (16)
/*
** State variables (part of the sqlite3_recover structure) used while
** recovering data for tables identified in the recovered schema (state
** RECOVER_STATE_WRITING).
*/
typedef struct RecoverStateW1 RecoverStateW1;
|
| ︙ | ︙ | |||
19006 19007 19008 19009 19010 19011 19012 |
**
** Otherwise, an attempt is made to allocate and return a bitmap object
** large enough to store a bit for all page numbers between 1 and nPg,
** inclusive. The bitmap is initially zeroed.
*/
static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){
int nElem = (nPg+1+31) / 32;
| | | 18998 18999 19000 19001 19002 19003 19004 19005 19006 19007 19008 19009 19010 19011 19012 |
**
** Otherwise, an attempt is made to allocate and return a bitmap object
** large enough to store a bit for all page numbers between 1 and nPg,
** inclusive. The bitmap is initially zeroed.
*/
static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){
int nElem = (nPg+1+31) / 32;
int nByte = SZ_RECOVERBITMAP_32 + nElem*sizeof(u32);
RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte);
if( pRet ){
pRet->nPg = nPg;
}
return pRet;
}
|
| ︙ | ︙ | |||
21198 21199 21200 21201 21202 21203 21204 |
** This function does the work of a single sqlite3_recover_step() call. It
** is guaranteed that the handle is not in an error state when this
** function is called.
*/
static void recoverStep(sqlite3_recover *p){
assert( p && p->errCode==SQLITE_OK );
switch( p->eState ){
| | > > < > > > > > > > > > | > | | | | | | | | | > > > | | < > > > | 21190 21191 21192 21193 21194 21195 21196 21197 21198 21199 21200 21201 21202 21203 21204 21205 21206 21207 21208 21209 21210 21211 21212 21213 21214 21215 21216 21217 21218 21219 21220 21221 21222 21223 21224 21225 21226 21227 21228 21229 21230 21231 21232 21233 21234 21235 21236 21237 21238 21239 21240 21241 21242 21243 21244 21245 21246 21247 21248 21249 21250 |
** This function does the work of a single sqlite3_recover_step() call. It
** is guaranteed that the handle is not in an error state when this
** function is called.
*/
static void recoverStep(sqlite3_recover *p){
assert( p && p->errCode==SQLITE_OK );
switch( p->eState ){
case RECOVER_STATE_INIT: {
int bUseWrapper = 1;
/* This is the very first call to sqlite3_recover_step() on this object.
*/
recoverSqlCallback(p, "BEGIN");
recoverSqlCallback(p, "PRAGMA writable_schema = on");
recoverSqlCallback(p, "PRAGMA foreign_keys = off");
recoverEnterMutex();
/* Open the output database. And register required virtual tables and
** user functions with the new handle. */
recoverOpenOutput(p);
/* Attempt to open a transaction and read page 1 of the input database.
** Two attempts may be made - one with a wrapper installed to ensure
** that the database header is sane, and then if that attempt returns
** SQLITE_NOTADB, then again with no wrapper. The second attempt is
** required for encrypted databases. */
if( p->errCode==SQLITE_OK ){
do{
p->errCode = SQLITE_OK;
if( bUseWrapper ) recoverInstallWrapper(p);
/* Open a transaction on the input database. */
sqlite3_file_control(p->dbIn, p->zDb, SQLITE_FCNTL_RESET_CACHE, 0);
recoverExec(p, p->dbIn, "PRAGMA writable_schema = on");
recoverExec(p, p->dbIn, "BEGIN");
if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1;
recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema");
recoverTransferSettings(p);
recoverOpenRecovery(p);
recoverCacheSchema(p);
if( bUseWrapper ) recoverUninstallWrapper(p);
}while( p->errCode==SQLITE_NOTADB
&& (bUseWrapper--)
&& SQLITE_OK==sqlite3_exec(p->dbIn, "ROLLBACK", 0, 0, 0)
);
}
recoverLeaveMutex();
recoverExec(p, p->dbOut, "BEGIN");
recoverWriteSchema1(p);
p->eState = RECOVER_STATE_WRITING;
break;
}
case RECOVER_STATE_WRITING: {
if( p->w1.pTbls==0 ){
recoverWriteDataInit(p);
}
if( SQLITE_DONE==recoverWriteDataStep(p) ){
recoverWriteDataCleanup(p);
|
| ︙ | ︙ | |||
21567 21568 21569 21570 21571 21572 21573 21574 21575 21576 21577 21578 21579 21580 | u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ u8 eRestoreState; /* See comments above doAutoDetectRestore() */ u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ | > | 21575 21576 21577 21578 21579 21580 21581 21582 21583 21584 21585 21586 21587 21588 21589 | u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ u8 nEqpLevel; /* Depth of the EQP output graph */ u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ u8 eRestoreState; /* See comments above doAutoDetectRestore() */ u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ u8 eEscMode; /* Escape mode for text output */ ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ |
| ︙ | ︙ | |||
21667 21668 21669 21670 21671 21672 21673 21674 21675 21676 21677 21678 21679 21680 |
/* Bits in the ShellState.flgProgress variable */
#define SHELL_PROGRESS_QUIET 0x01 /* Omit announcing every progress callback */
#define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress
** callback limit is reached, and for each
** top-level SQL statement */
#define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */
/*
** These are the allowed shellFlgs values
*/
#define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */
#define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */
#define SHFLG_Backslash 0x00000004 /* The --backslash option is used */
#define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */
| > > > > > > > > > | 21676 21677 21678 21679 21680 21681 21682 21683 21684 21685 21686 21687 21688 21689 21690 21691 21692 21693 21694 21695 21696 21697 21698 |
/* Bits in the ShellState.flgProgress variable */
#define SHELL_PROGRESS_QUIET 0x01 /* Omit announcing every progress callback */
#define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress
** callback limit is reached, and for each
** top-level SQL statement */
#define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */
/* Allowed values for ShellState.eEscMode. The default value should
** be 0, so to change the default, reorder the names.
*/
#define SHELL_ESC_ASCII 0 /* Substitute ^Y for X where Y=X+0x40 */
#define SHELL_ESC_SYMBOL 1 /* Substitute U+2400 graphics */
#define SHELL_ESC_OFF 2 /* Send characters verbatim */
static const char *shell_EscModeNames[] = { "ascii", "symbol", "off" };
/*
** These are the allowed shellFlgs values
*/
#define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */
#define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */
#define SHFLG_Backslash 0x00000004 /* The --backslash option is used */
#define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */
|
| ︙ | ︙ | |||
22004 22005 22006 22007 22008 22009 22010 | zStr[i*2] = '\0'; sqlite3_fprintf(out, "X'%s'", zStr); sqlite3_free(zStr); } /* | < | < | < | > | < < < < < < < < < < < | | < > | > | > > > | | | > > > > > > > > > > > > > > > > | > | | > > > > > > | < | | < > > > | > < | < | < | | < < < < < < < < < < < < < < < < < | < < < < < < | < < < < < < < < < | < < | < < < < < < < < < | 22022 22023 22024 22025 22026 22027 22028 22029 22030 22031 22032 22033 22034 22035 22036 22037 22038 22039 22040 22041 22042 22043 22044 22045 22046 22047 22048 22049 22050 22051 22052 22053 22054 22055 22056 22057 22058 22059 22060 22061 22062 22063 22064 22065 22066 22067 22068 22069 22070 22071 22072 22073 22074 22075 22076 22077 22078 22079 22080 22081 22082 22083 22084 22085 22086 22087 22088 22089 22090 22091 22092 22093 22094 22095 22096 22097 22098 22099 22100 22101 22102 22103 22104 22105 22106 22107 22108 22109 22110 22111 22112 22113 22114 22115 22116 22117 22118 22119 22120 22121 22122 22123 22124 22125 22126 22127 |
zStr[i*2] = '\0';
sqlite3_fprintf(out, "X'%s'", zStr);
sqlite3_free(zStr);
}
/*
** Output the given string as a quoted string using SQL quoting conventions:
**
** (1) Single quotes (') within the string are doubled
** (2) The whle string is enclosed in '...'
** (3) Control characters other than \n, \t, and \r\n are escaped
** using \u00XX notation and if such substitutions occur,
** the whole string is enclosed in unistr('...') instead of '...'.
**
** Step (3) is omitted if the control-character escape mode is OFF.
**
** See also: output_quoted_escaped_string() which does the same except
** that it does not make exceptions for \n, \t, and \r\n in step (3).
*/
static void output_quoted_string(ShellState *p, const char *zInX){
int i;
int needUnistr = 0;
int needDblQuote = 0;
const unsigned char *z = (const unsigned char*)zInX;
unsigned char c;
FILE *out = p->out;
sqlite3_fsetmode(out, _O_BINARY);
if( z==0 ) return;
for(i=0; (c = z[i])!=0; i++){
if( c=='\'' ){ needDblQuote = 1; }
if( c>0x1f ) continue;
if( c=='\t' || c=='\n' ) continue;
if( c=='\r' && z[i+1]=='\n' ) continue;
needUnistr = 1;
break;
}
if( (needDblQuote==0 && needUnistr==0)
|| (needDblQuote==0 && p->eEscMode==SHELL_ESC_OFF)
){
sqlite3_fprintf(out, "'%s'",z);
}else if( p->eEscMode==SHELL_ESC_OFF ){
char *zEncoded = sqlite3_mprintf("%Q", z);
sqlite3_fputs(zEncoded, out);
sqlite3_free(zEncoded);
}else{
if( needUnistr ){
sqlite3_fputs("unistr('", out);
}else{
sqlite3_fputs("'", out);
}
while( *z ){
for(i=0; (c = z[i])!=0; i++){
if( c=='\'' ) break;
if( c>0x1f ) continue;
if( c=='\t' || c=='\n' ) continue;
if( c=='\r' && z[i+1]=='\n' ) continue;
break;
}
if( i ){
sqlite3_fprintf(out, "%.*s", i, z);
z += i;
}
if( c==0 ) break;
if( c=='\'' ){
sqlite3_fputs("''", out);
}else{
sqlite3_fprintf(out, "\\u%04x", c);
}
z++;
}
if( needUnistr ){
sqlite3_fputs("')", out);
}else{
sqlite3_fputs("'", out);
}
}
setCrlfMode(p);
}
/*
** Output the given string as a quoted string using SQL quoting conventions.
** Additionallly , escape the "\n" and "\r" characters so that they do not
** get corrupted by end-of-line translation facilities in some operating
** systems.
**
** This is like output_quoted_string() but with the addition of the \r\n
** escape mechanism.
*/
static void output_quoted_escaped_string(ShellState *p, const char *z){
char *zEscaped;
sqlite3_fsetmode(p->out, _O_BINARY);
if( p->eEscMode==SHELL_ESC_OFF ){
zEscaped = sqlite3_mprintf("%Q", z);
}else{
zEscaped = sqlite3_mprintf("%#Q", z);
}
sqlite3_fputs(zEscaped, p->out);
sqlite3_free(zEscaped);
setCrlfMode(p);
}
/*
** Find earliest of chars within s specified in zAny.
** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated.
*/
|
| ︙ | ︙ | |||
22274 22275 22276 22277 22278 22279 22280 22281 22282 22283 22284 22285 22286 22287 |
}else{
ace[1] = (char)c;
sqlite3_fputs(ace+1, out);
}
}
sqlite3_fputs(zq, out);
}
/*
** Output the given string with characters that are special to
** HTML escaped.
*/
static void output_html_string(FILE *out, const char *z){
int i;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 22262 22263 22264 22265 22266 22267 22268 22269 22270 22271 22272 22273 22274 22275 22276 22277 22278 22279 22280 22281 22282 22283 22284 22285 22286 22287 22288 22289 22290 22291 22292 22293 22294 22295 22296 22297 22298 22299 22300 22301 22302 22303 22304 22305 22306 22307 22308 22309 22310 22311 22312 22313 22314 22315 22316 22317 22318 22319 22320 22321 22322 22323 22324 22325 22326 22327 22328 22329 22330 22331 22332 22333 22334 22335 22336 22337 22338 22339 22340 22341 22342 22343 22344 22345 22346 22347 22348 22349 22350 22351 22352 22353 22354 22355 22356 22357 22358 22359 22360 22361 22362 |
}else{
ace[1] = (char)c;
sqlite3_fputs(ace+1, out);
}
}
sqlite3_fputs(zq, out);
}
/*
** Escape the input string if it is needed and in accordance with
** eEscMode.
**
** Escaping is needed if the string contains any control characters
** other than \t, \n, and \r\n
**
** If no escaping is needed (the common case) then set *ppFree to NULL
** and return the original string. If escapingn is needed, write the
** escaped string into memory obtained from sqlite3_malloc64() or the
** equivalent, and return the new string and set *ppFree to the new string
** as well.
**
** The caller is responsible for freeing *ppFree if it is non-NULL in order
** to reclaim memory.
*/
static const char *escapeOutput(
ShellState *p,
const char *zInX,
char **ppFree
){
i64 i, j;
i64 nCtrl = 0;
unsigned char *zIn;
unsigned char c;
unsigned char *zOut;
/* No escaping if disabled */
if( p->eEscMode==SHELL_ESC_OFF ){
*ppFree = 0;
return zInX;
}
/* Count the number of control characters in the string. */
zIn = (unsigned char*)zInX;
for(i=0; (c = zIn[i])!=0; i++){
if( c<=0x1f
&& c!='\t'
&& c!='\n'
&& (c!='\r' || zIn[i+1]!='\n')
){
nCtrl++;
}
}
if( nCtrl==0 ){
*ppFree = 0;
return zInX;
}
if( p->eEscMode==SHELL_ESC_SYMBOL ) nCtrl *= 2;
zOut = sqlite3_malloc64( i + nCtrl + 1 );
shell_check_oom(zOut);
for(i=j=0; (c = zIn[i])!=0; i++){
if( c>0x1f
|| c=='\t'
|| c=='\n'
|| (c=='\r' && zIn[i+1]=='\n')
){
continue;
}
if( i>0 ){
memcpy(&zOut[j], zIn, i);
j += i;
}
zIn += i+1;
i = -1;
switch( p->eEscMode ){
case SHELL_ESC_SYMBOL:
zOut[j++] = 0xe2;
zOut[j++] = 0x90;
zOut[j++] = 0x80+c;
break;
case SHELL_ESC_ASCII:
zOut[j++] = '^';
zOut[j++] = 0x40+c;
break;
}
}
if( i>0 ){
memcpy(&zOut[j], zIn, i);
j += i;
}
zOut[j] = 0;
*ppFree = (char*)zOut;
return (char*)zOut;
}
/*
** Output the given string with characters that are special to
** HTML escaped.
*/
static void output_html_string(FILE *out, const char *z){
int i;
|
| ︙ | ︙ | |||
22718 22719 22720 22721 22722 22723 22724 22725 |
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
int len = strlen30(azCol[i] ? azCol[i] : "");
if( len>w ) w = len;
}
if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out);
for(i=0; i<nArg; i++){
sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i],
| > > > | > | 22793 22794 22795 22796 22797 22798 22799 22800 22801 22802 22803 22804 22805 22806 22807 22808 22809 22810 22811 22812 |
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
int len = strlen30(azCol[i] ? azCol[i] : "");
if( len>w ) w = len;
}
if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out);
for(i=0; i<nArg; i++){
char *pFree = 0;
const char *pDisplay;
pDisplay = escapeOutput(p, azArg[i] ? azArg[i] : p->nullValue, &pFree);
sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i],
pDisplay, p->rowSeparator);
if( pFree ) sqlite3_free(pFree);
}
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 };
|
| ︙ | ︙ | |||
22789 22790 22791 22792 22793 22794 22795 22796 22797 22798 22799 22800 22801 22802 22803 22804 22805 22806 22807 22808 22809 22810 |
case MODE_Pretty: { /* .schema and .fullschema with --indent */
char *z;
int j;
int nParen = 0;
char cEnd = 0;
char c;
int nLine = 0;
assert( nArg==1 );
if( azArg[0]==0 ) break;
if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0
|| sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0
){
sqlite3_fprintf(p->out, "%s;\n", azArg[0]);
break;
}
z = sqlite3_mprintf("%s", azArg[0]);
shell_check_oom(z);
j = 0;
for(i=0; IsSpace(z[i]); i++){}
for(; (c = z[i])!=0; i++){
if( IsSpace(c) ){
if( z[j-1]=='\r' ) z[j-1] = '\n';
| > > > > | 22868 22869 22870 22871 22872 22873 22874 22875 22876 22877 22878 22879 22880 22881 22882 22883 22884 22885 22886 22887 22888 22889 22890 22891 22892 22893 |
case MODE_Pretty: { /* .schema and .fullschema with --indent */
char *z;
int j;
int nParen = 0;
char cEnd = 0;
char c;
int nLine = 0;
int isIndex;
int isWhere = 0;
assert( nArg==1 );
if( azArg[0]==0 ) break;
if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0
|| sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0
){
sqlite3_fprintf(p->out, "%s;\n", azArg[0]);
break;
}
isIndex = sqlite3_strlike("CREATE INDEX%", azArg[0], 0)==0
|| sqlite3_strlike("CREATE UNIQUE INDEX%", azArg[0], 0)==0;
z = sqlite3_mprintf("%s", azArg[0]);
shell_check_oom(z);
j = 0;
for(i=0; IsSpace(z[i]); i++){}
for(; (c = z[i])!=0; i++){
if( IsSpace(c) ){
if( z[j-1]=='\r' ) z[j-1] = '\n';
|
| ︙ | ︙ | |||
22826 22827 22828 22829 22830 22831 22832 |
cEnd = ']';
}else if( c=='-' && z[i+1]=='-' ){
cEnd = '\n';
}else if( c=='(' ){
nParen++;
}else if( c==')' ){
nParen--;
| | > > > > > > > > > > > > > > > | > > > > | > | 22909 22910 22911 22912 22913 22914 22915 22916 22917 22918 22919 22920 22921 22922 22923 22924 22925 22926 22927 22928 22929 22930 22931 22932 22933 22934 22935 22936 22937 22938 22939 22940 22941 22942 22943 22944 22945 22946 22947 22948 22949 22950 22951 22952 22953 22954 22955 22956 22957 22958 22959 22960 22961 22962 22963 22964 22965 22966 22967 22968 22969 22970 22971 22972 22973 22974 22975 22976 |
cEnd = ']';
}else if( c=='-' && z[i+1]=='-' ){
cEnd = '\n';
}else if( c=='(' ){
nParen++;
}else if( c==')' ){
nParen--;
if( nLine>0 && nParen==0 && j>0 && !isWhere ){
printSchemaLineN(p->out, z, j, "\n");
j = 0;
}
}else if( (c=='w' || c=='W')
&& nParen==0 && isIndex
&& sqlite3_strnicmp("WHERE",&z[i],5)==0
&& !IsAlnum(z[i+5]) && z[i+5]!='_' ){
isWhere = 1;
}else if( isWhere && (c=='A' || c=='a')
&& nParen==0
&& sqlite3_strnicmp("AND",&z[i],3)==0
&& !IsAlnum(z[i+3]) && z[i+3]!='_' ){
printSchemaLineN(p->out, z, j, "\n ");
j = 0;
}
z[j++] = c;
if( nParen==1 && cEnd==0
&& (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1)))
&& !isWhere
){
if( c=='\n' ) j--;
printSchemaLineN(p->out, z, j, "\n ");
j = 0;
nLine++;
while( IsSpace(z[i+1]) ){ i++; }
}
}
z[j] = 0;
}
printSchemaLine(p->out, z, ";\n");
sqlite3_free(z);
break;
}
case MODE_List: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
char *z = azCol[i];
char *pFree;
const char *zOut = escapeOutput(p, z, &pFree);
sqlite3_fprintf(p->out, "%s%s", zOut,
i==nArg-1 ? p->rowSeparator : p->colSeparator);
if( pFree ) sqlite3_free(pFree);
}
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
char *z = azArg[i];
char *pFree;
const char *zOut;
if( z==0 ) z = p->nullValue;
zOut = escapeOutput(p, z, &pFree);
sqlite3_fputs(zOut, p->out);
if( pFree ) sqlite3_free(pFree);
sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out);
}
break;
}
case MODE_Www:
case MODE_Html: {
if( p->cnt==0 && p->cMode==MODE_Www ){
|
| ︙ | ︙ | |||
23978 23979 23980 23981 23982 23983 23984 23985 23986 23987 23988 23989 23990 23991 |
** Compute characters to display on the first line of z[]. Stop at the
** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained
** from malloc()) of that first line, which caller should free sometime.
** Write anything to display on the next line into *pzTail. If this is
** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
*/
static char *translateForDisplayAndDup(
const unsigned char *z, /* Input text to be transformed */
const unsigned char **pzTail, /* OUT: Tail of the input for next line */
int mxWidth, /* Max width. 0 means no limit */
u8 bWordWrap /* If true, avoid breaking mid-word */
){
int i; /* Input bytes consumed */
int j; /* Output bytes generated */
| > | 24081 24082 24083 24084 24085 24086 24087 24088 24089 24090 24091 24092 24093 24094 24095 |
** Compute characters to display on the first line of z[]. Stop at the
** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained
** from malloc()) of that first line, which caller should free sometime.
** Write anything to display on the next line into *pzTail. If this is
** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
*/
static char *translateForDisplayAndDup(
ShellState *p, /* To access current settings */
const unsigned char *z, /* Input text to be transformed */
const unsigned char **pzTail, /* OUT: Tail of the input for next line */
int mxWidth, /* Max width. 0 means no limit */
u8 bWordWrap /* If true, avoid breaking mid-word */
){
int i; /* Input bytes consumed */
int j; /* Output bytes generated */
|
| ︙ | ︙ | |||
24012 24013 24014 24015 24016 24017 24018 24019 24020 24021 24022 24023 24024 24025 24026 |
}
if( c>=' ' ){
n++;
i++;
j++;
continue;
}
if( c=='\t' ){
do{
n++;
j++;
}while( (n&7)!=0 && n<mxWidth );
i++;
continue;
}
| > > | > > > > > > | | | 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 |
}
if( c>=' ' ){
n++;
i++;
j++;
continue;
}
if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break;
if( c=='\t' ){
do{
n++;
j++;
}while( (n&7)!=0 && n<mxWidth );
i++;
continue;
}
if( c==0x1b && p->eEscMode==SHELL_ESC_OFF && (k = isVt100(&z[i]))>0 ){
i += k;
j += k;
}else{
n++;
j += 3;
i++;
}
}
if( n>=mxWidth && bWordWrap ){
/* Perhaps try to back up to a better place to break the line */
for(k=i; k>i/2; k--){
if( IsSpace(z[k-1]) ) break;
}
if( k<=i/2 ){
for(k=i; k>i/2; k--){
if( IsAlnum(z[k-1])!=IsAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break;
}
}
if( k<=i/2 ){
k = i;
}else{
i = k;
while( z[i]==' ' ) i++;
|
| ︙ | ︙ | |||
24067 24068 24069 24070 24071 24072 24073 24074 24075 24076 24077 24078 24079 24080 24081 |
continue;
}
if( c>=' ' ){
n++;
zOut[j++] = z[i++];
continue;
}
if( z[i]=='\t' ){
do{
n++;
zOut[j++] = ' ';
}while( (n&7)!=0 && n<mxWidth );
i++;
continue;
}
| > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > | > | 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 24230 24231 24232 24233 24234 24235 24236 24237 24238 24239 24240 24241 24242 24243 24244 24245 24246 24247 24248 24249 24250 24251 24252 24253 24254 24255 |
continue;
}
if( c>=' ' ){
n++;
zOut[j++] = z[i++];
continue;
}
if( c==0 ) break;
if( z[i]=='\t' ){
do{
n++;
zOut[j++] = ' ';
}while( (n&7)!=0 && n<mxWidth );
i++;
continue;
}
switch( p->eEscMode ){
case SHELL_ESC_SYMBOL:
zOut[j++] = 0xe2;
zOut[j++] = 0x90;
zOut[j++] = 0x80 + c;
break;
case SHELL_ESC_ASCII:
zOut[j++] = '^';
zOut[j++] = 0x40 + c;
break;
case SHELL_ESC_OFF: {
int nn;
if( c==0x1b && (nn = isVt100(&z[i]))>0 ){
memcpy(&zOut[j], &z[i], nn);
j += nn;
i += nn - 1;
}else{
zOut[j++] = c;
}
break;
}
}
i++;
}
zOut[j] = 0;
return (char*)zOut;
}
/* Return true if the text string z[] contains characters that need
** unistr() escaping.
*/
static int needUnistr(const unsigned char *z){
unsigned char c;
if( z==0 ) return 0;
while( (c = *z)>0x1f || c=='\t' || c=='\n' || (c=='\r' && z[1]=='\n') ){ z++; }
return c!=0;
}
/* Extract the value of the i-th current column for pStmt as an SQL literal
** value. Memory is obtained from sqlite3_malloc64() and must be freed by
** the caller.
*/
static char *quoted_column(sqlite3_stmt *pStmt, int i){
switch( sqlite3_column_type(pStmt, i) ){
case SQLITE_NULL: {
return sqlite3_mprintf("NULL");
}
case SQLITE_INTEGER:
case SQLITE_FLOAT: {
return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i));
}
case SQLITE_TEXT: {
const unsigned char *zText = sqlite3_column_text(pStmt,i);
return sqlite3_mprintf(needUnistr(zText)?"%#Q":"%Q",zText);
}
case SQLITE_BLOB: {
int j;
sqlite3_str *pStr = sqlite3_str_new(0);
const unsigned char *a = sqlite3_column_blob(pStmt,i);
int n = sqlite3_column_bytes(pStmt,i);
sqlite3_str_append(pStr, "x'", 2);
|
| ︙ | ︙ | |||
24187 24188 24189 24190 24191 24192 24193 |
int wx = p->colWidth[i];
if( wx==0 ){
wx = p->cmOpts.iWrap;
}
if( wx<0 ) wx = -wx;
uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
if( uz==0 ) uz = (u8*)"";
| | | 24333 24334 24335 24336 24337 24338 24339 24340 24341 24342 24343 24344 24345 24346 24347 |
int wx = p->colWidth[i];
if( wx==0 ){
wx = p->cmOpts.iWrap;
}
if( wx<0 ) wx = -wx;
uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
if( uz==0 ) uz = (u8*)"";
azData[i] = translateForDisplayAndDup(p, uz, &zNotUsed, wx, bw);
}
do{
int useNextLine = bNextLine;
bNextLine = 0;
if( (nRow+2)*nColumn >= nAlloc ){
nAlloc *= 2;
azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));
|
| ︙ | ︙ | |||
24211 24212 24213 24214 24215 24216 24217 24218 24219 24220 24221 24222 24223 24224 24225 |
wx = p->cmOpts.iWrap;
}
if( wx<0 ) wx = -wx;
if( useNextLine ){
uz = azNextLine[i];
if( uz==0 ) uz = (u8*)zEmpty;
}else if( p->cmOpts.bQuote ){
sqlite3_free(azQuoted[i]);
azQuoted[i] = quoted_column(pStmt,i);
uz = (const unsigned char*)azQuoted[i];
}else{
uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
if( uz==0 ) uz = (u8*)zShowNull;
}
azData[nRow*nColumn + i]
| > | | 24357 24358 24359 24360 24361 24362 24363 24364 24365 24366 24367 24368 24369 24370 24371 24372 24373 24374 24375 24376 24377 24378 24379 24380 |
wx = p->cmOpts.iWrap;
}
if( wx<0 ) wx = -wx;
if( useNextLine ){
uz = azNextLine[i];
if( uz==0 ) uz = (u8*)zEmpty;
}else if( p->cmOpts.bQuote ){
assert( azQuoted!=0 );
sqlite3_free(azQuoted[i]);
azQuoted[i] = quoted_column(pStmt,i);
uz = (const unsigned char*)azQuoted[i];
}else{
uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
if( uz==0 ) uz = (u8*)zShowNull;
}
azData[nRow*nColumn + i]
= translateForDisplayAndDup(p, uz, &azNextLine[i], wx, bw);
if( azNextLine[i] ){
bNextLine = 1;
abRowDiv[nRow-1] = 0;
bMultiLineRowExists = 1;
}
}
}while( bNextLine || sqlite3_step(pStmt)==SQLITE_ROW );
|
| ︙ | ︙ | |||
25142 25143 25144 25145 25146 25147 25148 | ".load FILE ?ENTRY? Load an extension library", #endif #if !defined(SQLITE_SHELL_FIDDLE) ".log FILE|on|off Turn logging on or off. FILE can be stderr/stdout", #else ".log on|off Turn logging on or off.", #endif | | > | 25289 25290 25291 25292 25293 25294 25295 25296 25297 25298 25299 25300 25301 25302 25303 25304 25305 25306 25307 25308 25309 25310 25311 25312 25313 25314 25315 25316 25317 25318 25319 25320 25321 | ".load FILE ?ENTRY? Load an extension library", #endif #if !defined(SQLITE_SHELL_FIDDLE) ".log FILE|on|off Turn logging on or off. FILE can be stderr/stdout", #else ".log on|off Turn logging on or off.", #endif ".mode ?MODE? ?OPTIONS? Set output mode", " MODE is one of:", " ascii Columns/rows delimited by 0x1F and 0x1E", " box Tables using unicode box-drawing characters", " csv Comma-separated values", " column Output in columns. (See .width)", " html HTML <table> code", " insert SQL insert statements for TABLE", " json Results in a JSON array", " line One value per line", " list Values delimited by \"|\"", " markdown Markdown table format", " qbox Shorthand for \"box --wrap 60 --quote\"", " quote Escape answers as for SQL", " table ASCII-art table", " tabs Tab-separated values", " tcl TCL list elements", " OPTIONS: (for columnar modes or insert mode):", " --escape T ctrl-char escape; T is one of: symbol, ascii, off", " --wrap N Wrap output lines to no longer than N characters", " --wordwrap B Wrap or not at word boundaries per B (on/off)", " --ww Shorthand for \"--wordwrap 1\"", " --quote Quote output text as SQL literals", " --noquote Do not quote output text", " TABLE The name of SQL table used for \"insert\" mode", #ifndef SQLITE_SHELL_FIDDLE |
| ︙ | ︙ | |||
25197 25198 25199 25200 25201 25202 25203 25204 25205 25206 25207 25208 25209 25210 | " --new Initialize FILE to an empty database", " --nofollow Do not follow symbolic links", " --readonly Open FILE readonly", " --zip FILE is a ZIP archive", #ifndef SQLITE_SHELL_FIDDLE ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", " Options:", " --bom Prefix output with a UTF8 byte-order mark", " -e Send output to the system text editor", " --plain Use text/plain for -w option", " -w Send output to a web browser", " -x Send output as CSV to a spreadsheet", #endif | > | 25345 25346 25347 25348 25349 25350 25351 25352 25353 25354 25355 25356 25357 25358 25359 | " --new Initialize FILE to an empty database", " --nofollow Do not follow symbolic links", " --readonly Open FILE readonly", " --zip FILE is a ZIP archive", #ifndef SQLITE_SHELL_FIDDLE ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", " If FILE is 'off' then output is disabled.", " Options:", " --bom Prefix output with a UTF8 byte-order mark", " -e Send output to the system text editor", " --plain Use text/plain for -w option", " -w Send output to a web browser", " -x Send output as CSV to a spreadsheet", #endif |
| ︙ | ︙ | |||
25322 25323 25324 25325 25326 25327 25328 | #ifndef SQLITE_SHELL_FIDDLE ".www Display output of the next command in web browser", " --plain Show results as text/plain, not as HTML", #endif }; /* | | < | | > > > > > > > > > > > > > > > > | | > | | | | < < < | | > | < | < < < < | < | < | | < < < < < < < | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | < | 25471 25472 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 25498 25499 25500 25501 25502 25503 25504 25505 25506 25507 25508 25509 25510 25511 25512 25513 25514 25515 25516 25517 25518 25519 25520 25521 25522 25523 25524 25525 25526 25527 25528 25529 25530 25531 25532 25533 25534 25535 25536 25537 25538 25539 25540 25541 25542 25543 25544 25545 25546 25547 25548 25549 25550 25551 25552 25553 25554 25555 25556 25557 25558 25559 25560 25561 25562 25563 25564 25565 25566 25567 25568 25569 25570 25571 25572 25573 25574 25575 25576 25577 25578 25579 25580 25581 25582 |
#ifndef SQLITE_SHELL_FIDDLE
".www Display output of the next command in web browser",
" --plain Show results as text/plain, not as HTML",
#endif
};
/*
** Output help text for commands that match zPattern.
**
** * If zPattern is NULL, then show all documented commands, but
** only give a one-line summary of each.
**
** * If zPattern is "-a" or "-all" or "--all" then show all help text
** for all commands except undocumented commands.
**
** * If zPattern is "0" then show all help for undocumented commands.
** Undocumented commands begin with "," instead of "." in the azHelp[]
** array.
**
** * If zPattern is a prefix for one or more documented commands, then
** show help for those commands. If only a single command matches the
** prefix, show the full text of the help. If multiple commands match,
** Only show just the first line of each.
**
** * Otherwise, show the complete text of any documented command for which
** zPattern is a LIKE match for any text within that command help
** text.
**
** Return the number commands that match zPattern.
*/
static int showHelp(FILE *out, const char *zPattern){
int i = 0;
int j = 0;
int n = 0;
char *zPat;
if( zPattern==0 ){
/* Show just the first line for all help topics */
zPattern = "[a-z]";
}else if( cli_strcmp(zPattern,"-a")==0
|| cli_strcmp(zPattern,"-all")==0
|| cli_strcmp(zPattern,"--all")==0
){
/* Show everything except undocumented commands */
zPattern = ".";
}else if( cli_strcmp(zPattern,"0")==0 ){
/* Show complete help text of undocumented commands */
int show = 0;
for(i=0; i<ArraySize(azHelp); i++){
if( azHelp[i][0]=='.' ){
show = 0;
}else if( azHelp[i][0]==',' ){
show = 1;
sqlite3_fprintf(out, ".%s\n", &azHelp[i][1]);
n++;
}else if( show ){
sqlite3_fprintf(out, "%s\n", azHelp[i]);
}
}
return n;
}
/* Seek documented commands for which zPattern is an exact prefix */
zPat = sqlite3_mprintf(".%s*", zPattern);
shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( sqlite3_strglob(zPat, azHelp[i])==0 ){
sqlite3_fprintf(out, "%s\n", azHelp[i]);
j = i+1;
n++;
}
}
sqlite3_free(zPat);
if( n ){
if( n==1 ){
/* when zPattern is a prefix of exactly one command, then include
** the details of that command, which should begin at offset j */
while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
sqlite3_fprintf(out, "%s\n", azHelp[j]);
j++;
}
}
return n;
}
/* Look for documented commands that contain zPattern anywhere.
** Show complete text of all documented commands that match. */
zPat = sqlite3_mprintf("%%%s%%", zPattern);
shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( azHelp[i][0]==',' ){
while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i;
continue;
}
if( azHelp[i][0]=='.' ) j = i;
if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
sqlite3_fprintf(out, "%s\n", azHelp[j]);
while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){
j++;
sqlite3_fprintf(out, "%s\n", azHelp[j]);
}
i = j;
n++;
}
}
sqlite3_free(zPat);
return n;
}
/* Forward reference */
static int process_input(ShellState *p);
/*
|
| ︙ | ︙ | |||
25667 25668 25669 25670 25671 25672 25673 25674 25675 25676 25677 25678 25679 25680 |
sqlite3_value **argv
){
int sleep = sqlite3_value_int(argv[0]);
(void)argcUnused;
sqlite3_sleep(sleep/1000);
sqlite3_result_int(context, sleep);
}
/* Flags for open_db().
**
** The default behavior of open_db() is to exit(1) if the database fails to
** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
** but still returns without calling exit.
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 25817 25818 25819 25820 25821 25822 25823 25824 25825 25826 25827 25828 25829 25830 25831 25832 25833 25834 25835 25836 25837 25838 25839 25840 25841 25842 25843 25844 25845 25846 25847 25848 25849 25850 25851 25852 25853 25854 25855 25856 25857 25858 25859 25860 25861 25862 25863 |
sqlite3_value **argv
){
int sleep = sqlite3_value_int(argv[0]);
(void)argcUnused;
sqlite3_sleep(sleep/1000);
sqlite3_result_int(context, sleep);
}
/*
** SQL function: shell_module_schema(X)
**
** Return a fake schema for the table-valued function or eponymous virtual
** table X.
*/
static void shellModuleSchema(
sqlite3_context *pCtx,
int nVal,
sqlite3_value **apVal
){
const char *zName;
char *zFake;
ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
FILE *pSavedLog = p->pLog;
UNUSED_PARAMETER(nVal);
zName = (const char*)sqlite3_value_text(apVal[0]);
/* Temporarily disable the ".log" when calling shellFakeSchema() because
** shellFakeSchema() might generate failures for some ephemeral virtual
** tables due to missing arguments. Example: fts4aux.
** https://sqlite.org/forum/forumpost/42fe6520b803be51 */
p->pLog = 0;
zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
p->pLog = pSavedLog;
if( zFake ){
sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
-1, sqlite3_free);
free(zFake);
}
}
/* Flags for open_db().
**
** The default behavior of open_db() is to exit(1) if the database fails to
** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
** but still returns without calling exit.
**
|
| ︙ | ︙ | |||
25811 25812 25813 25814 25815 25816 25817 |
shellStrtod, 0, 0);
sqlite3_create_function(p->db, "dtostr", 1, SQLITE_UTF8, 0,
shellDtostr, 0, 0);
sqlite3_create_function(p->db, "dtostr", 2, SQLITE_UTF8, 0,
shellDtostr, 0, 0);
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0);
| | | 25994 25995 25996 25997 25998 25999 26000 26001 26002 26003 26004 26005 26006 26007 26008 |
shellStrtod, 0, 0);
sqlite3_create_function(p->db, "dtostr", 1, SQLITE_UTF8, 0,
shellDtostr, 0, 0);
sqlite3_create_function(p->db, "dtostr", 2, SQLITE_UTF8, 0,
shellDtostr, 0, 0);
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0);
sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, p,
shellModuleSchema, 0, 0);
sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
shellPutsFunc, 0, 0);
sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0,
shellUSleepFunc, 0, 0);
#ifndef SQLITE_NOHAVE_SYSTEM
sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0,
|
| ︙ | ︙ | |||
25935 25936 25937 25938 25939 25940 25941 | char zBuf[1000]; #if HAVE_LINENOISE==2 UNUSED_PARAMETER(pUserData); #endif if( nLine>(i64)sizeof(zBuf)-30 ) return; if( zLine[0]=='.' || zLine[0]=='#') return; | | | 26118 26119 26120 26121 26122 26123 26124 26125 26126 26127 26128 26129 26130 26131 26132 |
char zBuf[1000];
#if HAVE_LINENOISE==2
UNUSED_PARAMETER(pUserData);
#endif
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"
" FROM completion(%Q,%Q) ORDER BY 1",
&zLine[iStart], zLine);
shell_check_oom(zSql);
|
| ︙ | ︙ | |||
26814 26815 26816 26817 26818 26819 26820 |
const u8 *aData = sqlite3_column_blob(pStmt, 1);
int seenPageLabel = 0;
for(i=0; i<pgSz; i+=16){
const u8 *aLine = aData+i;
for(j=0; j<16 && aLine[j]==0; j++){}
if( j==16 ) continue;
if( !seenPageLabel ){
| | | 26997 26998 26999 27000 27001 27002 27003 27004 27005 27006 27007 27008 27009 27010 27011 |
const u8 *aData = sqlite3_column_blob(pStmt, 1);
int seenPageLabel = 0;
for(i=0; i<pgSz; i+=16){
const u8 *aLine = aData+i;
for(j=0; j<16 && aLine[j]==0; j++){}
if( j==16 ) continue;
if( !seenPageLabel ){
sqlite3_fprintf(p->out, "| page %lld offset %lld\n",pgno,(pgno-1)*pgSz);
seenPageLabel = 1;
}
sqlite3_fprintf(p->out, "| %5d:", i);
for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]);
sqlite3_fprintf(p->out, " ");
for(j=0; j<16; j++){
unsigned char c = (unsigned char)aLine[j];
|
| ︙ | ︙ | |||
27458 27459 27460 27461 27462 27463 27464 |
pAr->bGlob = 1;
break;
case AR_SWITCH_VERBOSE:
pAr->bVerbose = 1;
break;
case AR_SWITCH_APPEND:
pAr->bAppend = 1;
| | | 27641 27642 27643 27644 27645 27646 27647 27648 27649 27650 27651 27652 27653 27654 27655 |
pAr->bGlob = 1;
break;
case AR_SWITCH_VERBOSE:
pAr->bVerbose = 1;
break;
case AR_SWITCH_APPEND:
pAr->bAppend = 1;
deliberate_fall_through; /* FALLTHRU */
case AR_SWITCH_FILE:
pAr->zFile = zArg;
break;
case AR_SWITCH_DIRECTORY:
pAr->zDir = zArg;
break;
}
|
| ︙ | ︙ | |||
28175 28176 28177 28178 28179 28180 28181 28182 28183 28184 28185 28186 28187 28188 |
);
sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */
sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF);
sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids);
sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist);
sqlite3_recover_run(p);
if( sqlite3_recover_errcode(p)!=SQLITE_OK ){
const char *zErr = sqlite3_recover_errmsg(p);
int errCode = sqlite3_recover_errcode(p);
sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode);
}
rc = sqlite3_recover_finish(p);
| > | 28358 28359 28360 28361 28362 28363 28364 28365 28366 28367 28368 28369 28370 28371 28372 |
);
sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */
sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF);
sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids);
sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist);
sqlite3_fprintf(pState->out, ".dbconfig defensive off\n");
sqlite3_recover_run(p);
if( sqlite3_recover_errcode(p)!=SQLITE_OK ){
const char *zErr = sqlite3_recover_errmsg(p);
int errCode = sqlite3_recover_errcode(p);
sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode);
}
rc = sqlite3_recover_finish(p);
|
| ︙ | ︙ | |||
28846 28847 28848 28849 28850 28851 28852 28853 28854 28855 28856 28857 28858 28859 |
}else
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbconfig", n)==0 ){
static const struct DbConfigChoices {
const char *zName;
int op;
} aDbConfig[] = {
{ "defensive", SQLITE_DBCONFIG_DEFENSIVE },
{ "dqs_ddl", SQLITE_DBCONFIG_DQS_DDL },
{ "dqs_dml", SQLITE_DBCONFIG_DQS_DML },
{ "enable_fkey", SQLITE_DBCONFIG_ENABLE_FKEY },
{ "enable_qpsg", SQLITE_DBCONFIG_ENABLE_QPSG },
{ "enable_trigger", SQLITE_DBCONFIG_ENABLE_TRIGGER },
{ "enable_view", SQLITE_DBCONFIG_ENABLE_VIEW },
| > > > | 29030 29031 29032 29033 29034 29035 29036 29037 29038 29039 29040 29041 29042 29043 29044 29045 29046 |
}else
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbconfig", n)==0 ){
static const struct DbConfigChoices {
const char *zName;
int op;
} aDbConfig[] = {
{ "attach_create", SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE },
{ "attach_write", SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE },
{ "comments", SQLITE_DBCONFIG_ENABLE_COMMENTS },
{ "defensive", SQLITE_DBCONFIG_DEFENSIVE },
{ "dqs_ddl", SQLITE_DBCONFIG_DQS_DDL },
{ "dqs_dml", SQLITE_DBCONFIG_DQS_DML },
{ "enable_fkey", SQLITE_DBCONFIG_ENABLE_FKEY },
{ "enable_qpsg", SQLITE_DBCONFIG_ENABLE_QPSG },
{ "enable_trigger", SQLITE_DBCONFIG_ENABLE_TRIGGER },
{ "enable_view", SQLITE_DBCONFIG_ENABLE_VIEW },
|
| ︙ | ︙ | |||
29267 29268 29269 29270 29271 29272 29273 |
}
open_db(p, 0);
rc = sqlite3_exec(p->db,
"SELECT sql FROM"
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
" FROM sqlite_schema UNION ALL"
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) "
| | > | 29454 29455 29456 29457 29458 29459 29460 29461 29462 29463 29464 29465 29466 29467 29468 29469 |
}
open_db(p, 0);
rc = sqlite3_exec(p->db,
"SELECT sql FROM"
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
" FROM sqlite_schema UNION ALL"
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) "
"WHERE type!='meta' AND sql NOTNULL"
" AND name NOT LIKE 'sqlite__%' ESCAPE '_' "
"ORDER BY x",
callback, &data, 0
);
if( rc==SQLITE_OK ){
sqlite3_stmt *pStmt;
rc = sqlite3_prepare_v2(p->db,
"SELECT rowid FROM sqlite_schema"
|
| ︙ | ︙ | |||
29897 29898 29899 29900 29901 29902 29903 29904 29905 29906 29907 29908 29909 29910 29911 29912 29913 29914 29915 29916 29917 29918 29919 29920 29921 29922 29923 29924 29925 29926 29927 29928 29929 29930 29931 29932 29933 29934 29935 29936 29937 29938 29939 29940 29941 29942 29943 29944 |
}
}else
if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){
const char *zMode = 0;
const char *zTabname = 0;
int i, n2;
ColModeOpts cmOpts = ColModeOpts_default;
for(i=1; i<nArg; i++){
const char *z = azArg[i];
if( optionMatch(z,"wrap") && i+1<nArg ){
cmOpts.iWrap = integerValue(azArg[++i]);
}else if( optionMatch(z,"ww") ){
cmOpts.bWordWrap = 1;
}else if( optionMatch(z,"wordwrap") && i+1<nArg ){
cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]);
}else if( optionMatch(z,"quote") ){
cmOpts.bQuote = 1;
}else if( optionMatch(z,"noquote") ){
cmOpts.bQuote = 0;
}else if( zMode==0 ){
zMode = z;
/* Apply defaults for qbox pseudo-mode. If that
* overwrites already-set values, user was informed of this.
*/
if( cli_strcmp(z, "qbox")==0 ){
ColModeOpts cmo = ColModeOpts_default_qbox;
zMode = "box";
cmOpts = cmo;
}
}else if( zTabname==0 ){
zTabname = z;
}else if( z[0]=='-' ){
sqlite3_fprintf(stderr,"unknown option: %s\n", z);
eputz("options:\n"
" --noquote\n"
" --quote\n"
" --wordwrap on/off\n"
" --wrap N\n"
" --ww\n");
rc = 1;
goto meta_command_exit;
}else{
sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z);
rc = 1;
goto meta_command_exit;
}
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > > | > > > > > > | 30085 30086 30087 30088 30089 30090 30091 30092 30093 30094 30095 30096 30097 30098 30099 30100 30101 30102 30103 30104 30105 30106 30107 30108 30109 30110 30111 30112 30113 30114 30115 30116 30117 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 |
}
}else
if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){
const char *zMode = 0;
const char *zTabname = 0;
int i, n2;
int chng = 0; /* 0x01: change to cmopts. 0x02: Any other change */
ColModeOpts cmOpts = ColModeOpts_default;
for(i=1; i<nArg; i++){
const char *z = azArg[i];
if( optionMatch(z,"wrap") && i+1<nArg ){
cmOpts.iWrap = integerValue(azArg[++i]);
chng |= 1;
}else if( optionMatch(z,"ww") ){
cmOpts.bWordWrap = 1;
chng |= 1;
}else if( optionMatch(z,"wordwrap") && i+1<nArg ){
cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]);
chng |= 1;
}else if( optionMatch(z,"quote") ){
cmOpts.bQuote = 1;
chng |= 1;
}else if( optionMatch(z,"noquote") ){
cmOpts.bQuote = 0;
chng |= 1;
}else if( optionMatch(z,"escape") && i+1<nArg ){
/* See similar code at tag-20250224-1 */
const char *zEsc = azArg[++i];
int k;
for(k=0; k<ArraySize(shell_EscModeNames); k++){
if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){
p->eEscMode = k;
chng |= 2;
break;
}
}
if( k>=ArraySize(shell_EscModeNames) ){
sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\""
" - choices:", zEsc);
for(k=0; k<ArraySize(shell_EscModeNames); k++){
sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]);
}
sqlite3_fprintf(stderr, "\n");
rc = 1;
goto meta_command_exit;
}
}else if( zMode==0 ){
zMode = z;
/* Apply defaults for qbox pseudo-mode. If that
* overwrites already-set values, user was informed of this.
*/
chng |= 1;
if( cli_strcmp(z, "qbox")==0 ){
ColModeOpts cmo = ColModeOpts_default_qbox;
zMode = "box";
cmOpts = cmo;
}
}else if( zTabname==0 ){
zTabname = z;
}else if( z[0]=='-' ){
sqlite3_fprintf(stderr,"unknown option: %s\n", z);
eputz("options:\n"
" --escape MODE\n"
" --noquote\n"
" --quote\n"
" --wordwrap on/off\n"
" --wrap N\n"
" --ww\n");
rc = 1;
goto meta_command_exit;
}else{
sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z);
rc = 1;
goto meta_command_exit;
}
}
if( !chng ){
if( p->mode==MODE_Column
|| (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
){
sqlite3_fprintf(p->out,
"current output mode: %s --wrap %d --wordwrap %s "
"--%squote --escape %s\n",
modeDescr[p->mode], p->cmOpts.iWrap,
p->cmOpts.bWordWrap ? "on" : "off",
p->cmOpts.bQuote ? "" : "no",
shell_EscModeNames[p->eEscMode]
);
}else{
sqlite3_fprintf(p->out,
"current output mode: %s --escape %s\n",
modeDescr[p->mode],
shell_EscModeNames[p->eEscMode]
);
}
}
if( zMode==0 ){
zMode = modeDescr[p->mode];
if( (chng&1)==0 ) cmOpts = p->cmOpts;
}
n2 = strlen30(zMode);
if( cli_strncmp(zMode,"lines",n2)==0 ){
p->mode = MODE_Line;
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
}else if( cli_strncmp(zMode,"columns",n2)==0 ){
p->mode = MODE_Column;
|
| ︙ | ︙ | |||
29984 29985 29986 29987 29988 29989 29990 29991 29992 29993 29994 29995 29996 29997 |
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
}else if( cli_strncmp(zMode,"tabs",n2)==0 ){
p->mode = MODE_List;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
}else if( cli_strncmp(zMode,"insert",n2)==0 ){
p->mode = MODE_Insert;
set_table_name(p, zTabname ? zTabname : "table");
}else if( cli_strncmp(zMode,"quote",n2)==0 ){
p->mode = MODE_Quote;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
}else if( cli_strncmp(zMode,"ascii",n2)==0 ){
p->mode = MODE_Ascii;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
| > > > > > | 30210 30211 30212 30213 30214 30215 30216 30217 30218 30219 30220 30221 30222 30223 30224 30225 30226 30227 30228 |
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
}else if( cli_strncmp(zMode,"tabs",n2)==0 ){
p->mode = MODE_List;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
}else if( cli_strncmp(zMode,"insert",n2)==0 ){
p->mode = MODE_Insert;
set_table_name(p, zTabname ? zTabname : "table");
if( p->eEscMode==SHELL_ESC_OFF ){
ShellSetFlag(p, SHFLG_Newlines);
}else{
ShellClearFlag(p, SHFLG_Newlines);
}
}else if( cli_strncmp(zMode,"quote",n2)==0 ){
p->mode = MODE_Quote;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
}else if( cli_strncmp(zMode,"ascii",n2)==0 ){
p->mode = MODE_Ascii;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
|
| ︙ | ︙ | |||
30145 30146 30147 30148 30149 30150 30151 |
&& (cli_strncmp(azArg[0], "output", n)==0
|| cli_strncmp(azArg[0], "once", n)==0))
|| (c=='e' && n==5 && cli_strcmp(azArg[0],"excel")==0)
|| (c=='w' && n==3 && cli_strcmp(azArg[0],"www")==0)
){
char *zFile = 0;
int i;
| | | | | 30376 30377 30378 30379 30380 30381 30382 30383 30384 30385 30386 30387 30388 30389 30390 30391 30392 |
&& (cli_strncmp(azArg[0], "output", n)==0
|| cli_strncmp(azArg[0], "once", n)==0))
|| (c=='e' && n==5 && cli_strcmp(azArg[0],"excel")==0)
|| (c=='w' && n==3 && cli_strcmp(azArg[0],"www")==0)
){
char *zFile = 0;
int i;
int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */
int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */
int bPlain = 0; /* --plain option */
static const char *zBomUtf8 = "\357\273\277";
const char *zBom = 0;
failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( c=='e' ){
eMode = 'x';
bOnce = 2;
|
| ︙ | ︙ | |||
30176 30177 30178 30179 30180 30181 30182 |
}else if( c=='o' && cli_strcmp(z,"-x")==0 ){
eMode = 'x'; /* spreadsheet */
}else if( c=='o' && cli_strcmp(z,"-e")==0 ){
eMode = 'e'; /* text editor */
}else if( c=='o' && cli_strcmp(z,"-w")==0 ){
eMode = 'w'; /* Web browser */
}else{
| | > > > > > > > | > > | 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 |
}else if( c=='o' && cli_strcmp(z,"-x")==0 ){
eMode = 'x'; /* spreadsheet */
}else if( c=='o' && cli_strcmp(z,"-e")==0 ){
eMode = 'e'; /* text editor */
}else if( c=='o' && cli_strcmp(z,"-w")==0 ){
eMode = 'w'; /* Web browser */
}else{
sqlite3_fprintf(p->out,
"ERROR: unknown option: \"%s\". Usage:\n", azArg[i]);
showHelp(p->out, azArg[0]);
rc = 1;
goto meta_command_exit;
}
}else if( zFile==0 && eMode==0 ){
if( cli_strcmp(z, "off")==0 ){
#ifdef _WIN32
zFile = sqlite3_mprintf("nul");
#else
zFile = sqlite3_mprintf("/dev/null");
#endif
}else{
zFile = sqlite3_mprintf("%s", z);
}
if( zFile && zFile[0]=='|' ){
while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]);
break;
}
}else{
sqlite3_fprintf(p->out,
"ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]);
showHelp(p->out, azArg[0]);
rc = 1;
sqlite3_free(zFile);
goto meta_command_exit;
}
}
if( zFile==0 ){
zFile = sqlite3_mprintf("stdout");
}
shell_check_oom(zFile);
if( bOnce ){
p->outCount = 2;
}else{
p->outCount = 0;
}
output_reset(p);
#ifndef SQLITE_NOHAVE_SYSTEM
|
| ︙ | ︙ | |||
30242 30243 30244 30245 30246 30247 30248 30249 30250 30251 30252 30253 30254 30255 30256 30257 30258 30259 30260 |
#ifdef SQLITE_OMIT_POPEN
eputz("Error: pipes are not supported in this OS\n");
rc = 1;
output_redir(p, stdout);
#else
FILE *pfPipe = sqlite3_popen(zFile + 1, "w");
if( pfPipe==0 ){
sqlite3_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
rc = 1;
}else{
output_redir(p, pfPipe);
if( zBom ) sqlite3_fputs(zBom, pfPipe);
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
#endif
}else{
FILE *pfFile = output_file_open(zFile);
if( pfFile==0 ){
if( cli_strcmp(zFile,"off")!=0 ){
| > > | | 30482 30483 30484 30485 30486 30487 30488 30489 30490 30491 30492 30493 30494 30495 30496 30497 30498 30499 30500 30501 30502 30503 30504 30505 30506 30507 30508 30509 30510 |
#ifdef SQLITE_OMIT_POPEN
eputz("Error: pipes are not supported in this OS\n");
rc = 1;
output_redir(p, stdout);
#else
FILE *pfPipe = sqlite3_popen(zFile + 1, "w");
if( pfPipe==0 ){
assert( stderr!=NULL );
sqlite3_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
rc = 1;
}else{
output_redir(p, pfPipe);
if( zBom ) sqlite3_fputs(zBom, pfPipe);
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
#endif
}else{
FILE *pfFile = output_file_open(zFile);
if( pfFile==0 ){
if( cli_strcmp(zFile,"off")!=0 ){
assert( stderr!=NULL );
sqlite3_fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile);
}
rc = 1;
} else {
output_redir(p, pfFile);
if( zBom ) sqlite3_fputs(zBom, pfFile);
if( bPlain && eMode=='w' ){
sqlite3_fputs(
|
| ︙ | ︙ | |||
30358 30359 30360 30361 30362 30363 30364 30365 30366 30367 30368 30369 30370 30371 |
if( rx!=SQLITE_OK ){
sqlite3_fprintf(p->out, "Error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
pStmt = 0;
rc = 1;
}
}
sqlite3_step(pStmt);
sqlite3_finalize(pStmt);
}else
/* .parameter unset NAME
** Remove the NAME binding from the parameter binding table, if it
** exists.
| > | 30600 30601 30602 30603 30604 30605 30606 30607 30608 30609 30610 30611 30612 30613 30614 |
if( rx!=SQLITE_OK ){
sqlite3_fprintf(p->out, "Error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
pStmt = 0;
rc = 1;
}
}
bind_prepared_stmt(p, pStmt);
sqlite3_step(pStmt);
sqlite3_finalize(pStmt);
}else
/* .parameter unset NAME
** Remove the NAME binding from the parameter binding table, if it
** exists.
|
| ︙ | ︙ | |||
30688 30689 30690 30691 30692 30693 30694 |
if( !bGlob ){
appendText(&sSelect, " ESCAPE '\\' ", 0);
}
appendText(&sSelect, " AND ", 0);
sqlite3_free(zQarg);
}
if( bNoSystemTabs ){
| | | 30931 30932 30933 30934 30935 30936 30937 30938 30939 30940 30941 30942 30943 30944 30945 |
if( !bGlob ){
appendText(&sSelect, " ESCAPE '\\' ", 0);
}
appendText(&sSelect, " AND ", 0);
sqlite3_free(zQarg);
}
if( bNoSystemTabs ){
appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0);
}
appendText(&sSelect, "sql IS NOT NULL"
" ORDER BY snum, rowid", 0);
if( bDebug ){
sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.z);
}else{
rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
|
| ︙ | ︙ | |||
31119 31120 31121 31122 31123 31124 31125 |
zSql = "SELECT lower(name) as tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
" UNION ALL SELECT 'sqlite_schema'"
" ORDER BY 1 collate nocase";
}else{
zSql = "SELECT lower(name) as tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
| | | 31362 31363 31364 31365 31366 31367 31368 31369 31370 31371 31372 31373 31374 31375 31376 |
zSql = "SELECT lower(name) as tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
" UNION ALL SELECT 'sqlite_schema'"
" ORDER BY 1 collate nocase";
}else{
zSql = "SELECT lower(name) as tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
" AND name NOT LIKE 'sqlite__%' ESCAPE '_'"
" ORDER BY 1 collate nocase";
}
sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
initText(&sQuery);
initText(&sSql);
appendText(&sSql, "WITH [sha3sum$query](a,b) AS(",0);
zSep = "VALUES(";
|
| ︙ | ︙ | |||
31184 31185 31186 31187 31188 31189 31190 |
}
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE)
{
int lrc;
char *zRevText = /* Query for reversible to-blob-to-text check */
"SELECT lower(name) as tname FROM sqlite_schema\n"
"WHERE type='table' AND coalesce(rootpage,0)>1\n"
| | | 31427 31428 31429 31430 31431 31432 31433 31434 31435 31436 31437 31438 31439 31440 31441 |
}
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE)
{
int lrc;
char *zRevText = /* Query for reversible to-blob-to-text check */
"SELECT lower(name) as tname FROM sqlite_schema\n"
"WHERE type='table' AND coalesce(rootpage,0)>1\n"
"AND name NOT LIKE 'sqlite__%%' ESCAPE '_'%s\n"
"ORDER BY 1 collate nocase";
zRevText = sqlite3_mprintf(zRevText, zLike? " AND name LIKE $tspec" : "");
zRevText = sqlite3_mprintf(
/* lower-case query is first run, producing upper-case query. */
"with tabcols as materialized(\n"
"select tname, cname\n"
"from ("
|
| ︙ | ︙ | |||
31380 31381 31382 31383 31384 31385 31386 |
appendText(&s, zDbName, '\'');
appendText(&s, "||'.'||name FROM ", 0);
}
appendText(&s, zDbName, '"');
appendText(&s, ".sqlite_schema ", 0);
if( c=='t' ){
appendText(&s," WHERE type IN ('table','view')"
| | | 31623 31624 31625 31626 31627 31628 31629 31630 31631 31632 31633 31634 31635 31636 31637 |
appendText(&s, zDbName, '\'');
appendText(&s, "||'.'||name FROM ", 0);
}
appendText(&s, zDbName, '"');
appendText(&s, ".sqlite_schema ", 0);
if( c=='t' ){
appendText(&s," WHERE type IN ('table','view')"
" AND name NOT LIKE 'sqlite__%' ESCAPE '_'"
" AND name LIKE ?1", 0);
}else{
appendText(&s," WHERE type='index'"
" AND tbl_name LIKE ?1", 0);
}
}
rc = sqlite3_finalize(pStmt);
|
| ︙ | ︙ | |||
31474 31475 31476 31477 31478 31479 31480 |
int ctrlCode; /* Integer code for that option */
int unSafe; /* Not valid unless --unsafe-testing */
const char *zUsage; /* Usage notes */
} aCtrl[] = {
{"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" },
{"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" },
/*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/
| | | 31717 31718 31719 31720 31721 31722 31723 31724 31725 31726 31727 31728 31729 31730 31731 |
int ctrlCode; /* Integer code for that option */
int unSafe; /* Not valid unless --unsafe-testing */
const char *zUsage; /* Usage notes */
} 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, "SIZE INT-ARRAY"},
{"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" },
|
| ︙ | ︙ | |||
31592 31593 31594 31595 31596 31597 31598 31599 31600 31601 31602 31603 31604 31605 |
{ 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;
unsigned int m;
int ii;
int nOff;
| > > | 31835 31836 31837 31838 31839 31840 31841 31842 31843 31844 31845 31846 31847 31848 31849 31850 |
{ 0x00400000, 1, "ReleaseReg" },
{ 0x00800000, 1, "FlttnUnionAll" },
{ 0x01000000, 1, "IndexedEXpr" },
{ 0x02000000, 1, "Coroutines" },
{ 0x04000000, 1, "NullUnusedCols" },
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0x20000000, 1, "StarQuery" },
{ 0x40000000, 1, "ExistsToJoin" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;
unsigned int newOpt;
unsigned int m;
int ii;
int nOff;
|
| ︙ | ︙ | |||
31811 31812 31813 31814 31815 31816 31817 31818 31819 31820 31821 31822 31823 31824 |
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++;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 32056 32057 32058 32059 32060 32061 32062 32063 32064 32065 32066 32067 32068 32069 32070 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 32108 32109 32110 32111 32112 |
isOk = 1;
}else{
rc2 = booleanValue(azArg[2]);
isOk = 3;
}
sqlite3_test_control(testctrl, &rc2);
break;
case SQLITE_TESTCTRL_BITVEC_TEST: {
/* Examples:
** .testctrl bitvec_test 100 6,1 -- Show BITVEC constants
** .testctrl bitvec_test 1000 1,12,7,3 -- Simple test
** ---- --------
** size of Bitvec -----^ ^--- aOp array. 0 added at end.
**
** See comments on sqlite3BitvecBuiltinTest() for more information
** about the aOp[] array.
*/
int iSize;
const char *zTestArg;
int nOp;
int ii, jj, x;
int *aOp;
if( nArg!=4 ){
sqlite3_fprintf(stderr,
"ERROR - should be: \".testctrl bitvec_test SIZE INT-ARRAY\"\n"
);
rc = 1;
goto meta_command_exit;
}
isOk = 3;
iSize = (int)integerValue(azArg[2]);
zTestArg = azArg[3];
nOp = (int)strlen(zTestArg)+1;
aOp = malloc( sizeof(int)*(nOp+1) );
shell_check_oom(aOp);
memset(aOp, 0, sizeof(int)*(nOp+1) );
for(ii = jj = x = 0; zTestArg[ii]!=0; ii++){
if( IsDigit(zTestArg[ii]) ){
x = x*10 + zTestArg[ii] - '0';
}else{
aOp[jj++] = x;
x = 0;
}
}
aOp[jj] = x;
x = sqlite3_test_control(testctrl, iSize, aOp);
sqlite3_fprintf(p->out, "result: %d\n", x);
free(aOp);
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++;
|
| ︙ | ︙ | |||
32146 32147 32148 32149 32150 32151 32152 |
CONTINUE_PROMPT_AWAITS(pst, "/*");
qss = QSS_SETV(qss, cWait);
goto TermScan;
}
break;
case '[':
cin = ']';
| | | 32434 32435 32436 32437 32438 32439 32440 32441 32442 32443 32444 32445 32446 32447 32448 |
CONTINUE_PROMPT_AWAITS(pst, "/*");
qss = QSS_SETV(qss, cWait);
goto TermScan;
}
break;
case '[':
cin = ']';
deliberate_fall_through; /* FALLTHRU */
case '`': case '\'': case '"':
cWait = cin;
qss = QSS_HasDark | cWait;
CONTINUE_PROMPT_AWAITC(pst, cin);
goto TermScan;
case '(':
CONTINUE_PAREN_INCR(pst, 1);
|
| ︙ | ︙ | |||
32181 32182 32183 32184 32185 32186 32187 |
goto PlainScan;
case '`': case '\'': case '"':
if(*zLine==cWait){
/* Swallow doubled end-delimiter.*/
++zLine;
continue;
}
| | | 32469 32470 32471 32472 32473 32474 32475 32476 32477 32478 32479 32480 32481 32482 32483 |
goto PlainScan;
case '`': case '\'': case '"':
if(*zLine==cWait){
/* Swallow doubled end-delimiter.*/
++zLine;
continue;
}
deliberate_fall_through; /* FALLTHRU */
case ']':
CONTINUE_PROMPT_AWAITC(pst, 0);
qss = QSS_SETV(qss, 0);
goto PlainScan;
default: assert(0);
}
}
|
| ︙ | ︙ | |||
32215 32216 32217 32218 32219 32220 32221 | } /* ** The CLI needs a working sqlite3_complete() to work properly. So error ** out of the build if compiling with SQLITE_OMIT_COMPLETE. */ #ifdef SQLITE_OMIT_COMPLETE | | | 32503 32504 32505 32506 32507 32508 32509 32510 32511 32512 32513 32514 32515 32516 32517 |
}
/*
** The CLI needs a working sqlite3_complete() to work properly. So error
** out of the build if compiling with SQLITE_OMIT_COMPLETE.
*/
#ifdef SQLITE_OMIT_COMPLETE
# error the CLI application is incompatible with SQLITE_OMIT_COMPLETE.
#endif
/*
** Return true if zSql is a complete SQL statement. Return false if it
** ends in the middle of a string literal or C-style comment.
*/
static int line_is_complete(char *zSql, int nSql){
|
| ︙ | ︙ | |||
32394 32395 32396 32397 32398 32399 32400 |
i64 nZ = 0;
UNUSED_PARAMETER(in);
UNUSED_PARAMETER(isContinuation);
if(!z || !*z){
return 0;
}
| | | 32682 32683 32684 32685 32686 32687 32688 32689 32690 32691 32692 32693 32694 32695 32696 |
i64 nZ = 0;
UNUSED_PARAMETER(in);
UNUSED_PARAMETER(isContinuation);
if(!z || !*z){
return 0;
}
while(*z && IsSpace(*z)) ++z;
zBegin = z;
for(; *z && '\n'!=*z; ++nZ, ++z){}
if(nZ>0 && '\r'==zBegin[nZ-1]){
--nZ;
}
shellState.wasm.zPos = z;
zLine = realloc(zPrior, nZ+1);
|
| ︙ | ︙ | |||
32699 32700 32701 32702 32703 32704 32705 32706 32707 32708 32709 32710 32711 32712 | " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" #if !defined(SQLITE_OMIT_DESERIALIZE) " -deserialize open the database using sqlite3_deserialize()\n" #endif " -echo print inputs before execution\n" " -init FILENAME read/process named file\n" " -[no]header turn headers on or off\n" #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) " -heap SIZE Size of heap for memsys3 or memsys5\n" #endif " -help show this message\n" " -html set output mode to HTML\n" | > | 32987 32988 32989 32990 32991 32992 32993 32994 32995 32996 32997 32998 32999 33000 33001 | " -column set output mode to 'column'\n" " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" #if !defined(SQLITE_OMIT_DESERIALIZE) " -deserialize open the database using sqlite3_deserialize()\n" #endif " -echo print inputs before execution\n" " -escape T ctrl-char escape; T is one of: symbol, ascii, off\n" " -init FILENAME read/process named file\n" " -[no]header turn headers on or off\n" #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) " -heap SIZE Size of heap for memsys3 or memsys5\n" #endif " -help show this message\n" " -html set output mode to HTML\n" |
| ︙ | ︙ | |||
32746 32747 32748 32749 32750 32751 32752 |
" -vfs NAME use NAME as the default VFS\n"
" -vfstrace enable tracing of all VFS calls\n"
#ifdef SQLITE_HAVE_ZLIB
" -zip open the file as a ZIP Archive\n"
#endif
;
static void usage(int showDetail){
| | | 33035 33036 33037 33038 33039 33040 33041 33042 33043 33044 33045 33046 33047 33048 33049 |
" -vfs NAME use NAME as the default VFS\n"
" -vfstrace enable tracing of all VFS calls\n"
#ifdef SQLITE_HAVE_ZLIB
" -zip open the file as a ZIP Archive\n"
#endif
;
static void usage(int showDetail){
sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n"
"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 ){
sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions);
}else{
eputz("Use the -help option for additional information\n");
}
|
| ︙ | ︙ | |||
33132 33133 33134 33135 33136 33137 33138 33139 33140 33141 33142 33143 33144 33145 |
}else if( cli_strcmp(z,"-nonce")==0 ){
free(data.zNonce);
data.zNonce = strdup(cmdline_option_value(argc, argv, ++i));
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
ShellSetFlag(&data,SHFLG_TestingMode);
}else if( cli_strcmp(z,"-safe")==0 ){
/* no-op - catch this on the second pass */
}
}
#ifndef SQLITE_SHELL_FIDDLE
if( !bEnableVfstrace ) verify_uninitialized();
#endif
| > > > | 33421 33422 33423 33424 33425 33426 33427 33428 33429 33430 33431 33432 33433 33434 33435 33436 33437 |
}else if( cli_strcmp(z,"-nonce")==0 ){
free(data.zNonce);
data.zNonce = strdup(cmdline_option_value(argc, argv, ++i));
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
ShellSetFlag(&data,SHFLG_TestingMode);
}else if( cli_strcmp(z,"-safe")==0 ){
/* no-op - catch this on the second pass */
}else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
/* skip over the argument */
i++;
}
}
#ifndef SQLITE_SHELL_FIDDLE
if( !bEnableVfstrace ) verify_uninitialized();
#endif
|
| ︙ | ︙ | |||
33231 33232 33233 33234 33235 33236 33237 33238 33239 33240 33241 33242 33243 33244 |
}else if( cli_strcmp(z,"-table")==0 ){
data.mode = MODE_Table;
}else if( cli_strcmp(z,"-box")==0 ){
data.mode = MODE_Box;
}else if( cli_strcmp(z,"-csv")==0 ){
data.mode = MODE_Csv;
memcpy(data.colSeparator,",",2);
#ifdef SQLITE_HAVE_ZLIB
}else if( cli_strcmp(z,"-zip")==0 ){
data.openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( cli_strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
#ifndef SQLITE_OMIT_DESERIALIZE
| > > > > > > > > > > > > > > > > > > > | 33523 33524 33525 33526 33527 33528 33529 33530 33531 33532 33533 33534 33535 33536 33537 33538 33539 33540 33541 33542 33543 33544 33545 33546 33547 33548 33549 33550 33551 33552 33553 33554 33555 |
}else if( cli_strcmp(z,"-table")==0 ){
data.mode = MODE_Table;
}else if( cli_strcmp(z,"-box")==0 ){
data.mode = MODE_Box;
}else if( cli_strcmp(z,"-csv")==0 ){
data.mode = MODE_Csv;
memcpy(data.colSeparator,",",2);
}else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
/* See similar code at tag-20250224-1 */
const char *zEsc = argv[++i];
int k;
for(k=0; k<ArraySize(shell_EscModeNames); k++){
if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){
data.eEscMode = k;
break;
}
}
if( k>=ArraySize(shell_EscModeNames) ){
sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\""
" - choices:", zEsc);
for(k=0; k<ArraySize(shell_EscModeNames); k++){
sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]);
}
sqlite3_fprintf(stderr, "\n");
exit(1);
}
#ifdef SQLITE_HAVE_ZLIB
}else if( cli_strcmp(z,"-zip")==0 ){
data.openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( cli_strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
#ifndef SQLITE_OMIT_DESERIALIZE
|
| ︙ | ︙ | |||
33392 33393 33394 33395 33396 33397 33398 33399 33400 33401 33402 33403 33404 33405 33406 |
if( !readStdin ){
/* Run all arguments that do not begin with '-' as if they were separate
** 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);
| > < | 33703 33704 33705 33706 33707 33708 33709 33710 33711 33712 33713 33714 33715 33716 33717 33718 33719 33720 33721 33722 33723 33724 33725 |
if( !readStdin ){
/* Run all arguments that do not begin with '-' as if they were separate
** command-line inputs, except for the argToSkip argument which contains
** the database filename.
*/
for(i=0; i<nCmd; i++){
echo_group_input(&data, azCmd[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);
rc = shell_exec(&data, azCmd[i], &zErrMsg);
if( zErrMsg || rc ){
if( zErrMsg!=0 ){
shellEmitError(zErrMsg);
}else{
sqlite3_fprintf(stderr,
"Error: unable to process SQL: %s\n", azCmd[i]);
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.c.
more than 10,000 changes
Changes to extsrc/sqlite3.h.
| ︙ | ︙ | |||
129 130 131 132 133 134 135 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since [version 3.6.18] ([dateof:3.6.18]), ** SQLite source code has been stored in the | | | | | | | | | 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 | ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since [version 3.6.18] ([dateof:3.6.18]), ** SQLite source code has been stored in the ** <a href="http://fossil-scm.org/">Fossil configuration management ** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to ** a string which identifies a particular check-in of SQLite ** within its configuration management system. ^The SQLITE_SOURCE_ID ** string contains the date and time of the check-in (UTC) and a SHA1 ** or SHA3-256 hash of the entire source tree. If the source code has ** 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.51.0" #define SQLITE_VERSION_NUMBER 3051000 #define SQLITE_SOURCE_ID "2025-07-15 19:00:01 9f184f8dfa5ef6d57e10376adc30e0060ceda07d283c23dfdfe3dbdd6608f839" /* ** 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 ** but are associated with the library instead of the header file. ^(Cautious ** programmers might include assert() statements in their application to ** verify that values returned by these interfaces match the macros in ** the header, and thus ensure that the application is ** compiled with matching library and header files. ** ** <blockquote><pre> ** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER ); ** assert( strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,80)==0 ); ** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 ); ** </pre></blockquote>)^ ** ** ^The sqlite3_version[] string constant contains the text of the ** [SQLITE_VERSION] macro. ^The sqlite3_libversion() function returns a ** pointer to the sqlite3_version[] string constant. The sqlite3_libversion() ** function is provided for use in DLLs since DLL users usually do not have ** direct access to string constants within the DLL. ^The ** sqlite3_libversion_number() function returns an integer equal to ** [SQLITE_VERSION_NUMBER]. ^(The sqlite3_sourceid() function returns ** a pointer to a string constant whose value is the same as the ** [SQLITE_SOURCE_ID] C preprocessor macro. Except if SQLite is built ** using an edited copy of [the amalgamation], then the last four characters |
| ︙ | ︙ | |||
366 367 368 369 370 371 372 | ** ** The sqlite3_exec() interface is a convenience wrapper around ** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], ** that allows an application to run multiple statements of SQL ** without having to use a lot of C code. ** ** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, | | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | ** ** The sqlite3_exec() interface is a convenience wrapper around ** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], ** that allows an application to run multiple statements of SQL ** without having to use a lot of C code. ** ** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, ** semicolon-separated SQL statements passed into its 2nd argument, ** in the context of the [database connection] passed in as its 1st ** argument. ^If the callback function of the 3rd argument to ** sqlite3_exec() is not NULL, then it is invoked for each result row ** coming out of the evaluated SQL statements. ^The 4th argument to ** sqlite3_exec() is relayed through to the 1st argument of each ** callback invocation. ^If the callback pointer to sqlite3_exec() ** is NULL, then no callback is ever invoked and result rows are |
| ︙ | ︙ | |||
399 400 401 402 403 404 405 | ** ^The 2nd argument to the sqlite3_exec() callback function is the ** number of columns in the result. ^The 3rd argument to the sqlite3_exec() ** callback is an array of pointers to strings obtained as if from ** [sqlite3_column_text()], one for each column. ^If an element of a ** result row is NULL then the corresponding string pointer for the ** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the ** sqlite3_exec() callback is an array of pointers to strings where each | | | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | ** ^The 2nd argument to the sqlite3_exec() callback function is the ** number of columns in the result. ^The 3rd argument to the sqlite3_exec() ** callback is an array of pointers to strings obtained as if from ** [sqlite3_column_text()], one for each column. ^If an element of a ** result row is NULL then the corresponding string pointer for the ** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the ** sqlite3_exec() callback is an array of pointers to strings where each ** entry represents the name of a corresponding result column as obtained ** from [sqlite3_column_name()]. ** ** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer ** to an empty string, or a pointer that contains only whitespace and/or ** SQL comments, then no SQL statements are evaluated and the database ** is not changed. ** |
| ︙ | ︙ | |||
585 586 587 588 589 590 591 | ** though future versions of SQLite might change so that an error is ** raised if any of the disallowed bits are passed into sqlite3_open_v2(). ** Applications should not depend on the historical behavior. ** ** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into ** [sqlite3_open_v2()] does *not* cause the underlying database file ** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into | | | 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 | ** though future versions of SQLite might change so that an error is ** raised if any of the disallowed bits are passed into sqlite3_open_v2(). ** Applications should not depend on the historical behavior. ** ** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into ** [sqlite3_open_v2()] does *not* cause the underlying database file ** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into ** [sqlite3_open_v2()] has historically been a no-op and might become an ** error in future versions of SQLite. */ #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ #define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ |
| ︙ | ︙ | |||
679 680 681 682 683 684 685 | /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from | | | 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 | /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from ** least restrictive to most restrictive. ** ** The argument to xLock() is always SHARED or higher. The argument to ** xUnlock is either SHARED or NONE. */ #define SQLITE_LOCK_NONE 0 /* xUnlock() only */ #define SQLITE_LOCK_SHARED 1 /* xLock() or xUnlock() */ #define SQLITE_LOCK_RESERVED 2 /* xLock() only */ |
| ︙ | ︙ | |||
995 996 997 998 999 1000 1001 | ** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening ** a write transaction to indicate that, unless it is rolled back for some ** reason, the entire database file will be overwritten by the current ** transaction. This is used by VACUUM operations. ** ** <li>[[SQLITE_FCNTL_VFSNAME]] ** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of | | | | 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 | ** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening ** a write transaction to indicate that, unless it is rolled back for some ** reason, the entire database file will be overwritten by the current ** transaction. This is used by VACUUM operations. ** ** <li>[[SQLITE_FCNTL_VFSNAME]] ** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of ** all [VFSes] in the VFS stack. The names of all VFS shims and the ** final bottom-level VFS are written into memory obtained from ** [sqlite3_malloc()] and the result is stored in the char* variable ** that the fourth parameter of [sqlite3_file_control()] points to. ** The caller is responsible for freeing the memory when done. As with ** all file-control actions, there is no guarantee that this will actually ** do anything. Callers should initialize the char* variable to a NULL ** pointer in case this file-control is not implemented. This file-control ** is intended for diagnostic use only. ** ** <li>[[SQLITE_FCNTL_VFS_POINTER]] ** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level ** [VFSes] currently in use. ^(The argument X in ** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be ** of type "[sqlite3_vfs] **". This opcode will set *X ** to a pointer to the top-level VFS.)^ ** ^When there are multiple VFS shims in the stack, this opcode finds the ** upper-most shim only. ** ** <li>[[SQLITE_FCNTL_PRAGMA]] ** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA] ** file control is sent to the open [sqlite3_file] object corresponding |
| ︙ | ︙ | |||
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 | ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS ** to block for up to M milliseconds before failing when attempting to ** obtain a file lock using the xLock or xShmLock methods of the VFS. ** The parameter is a pointer to a 32-bit signed integer that contains ** the value that M is to be set to. Before returning, the 32-bit signed ** integer is overwritten with the previous value of M. ** ** <li>[[SQLITE_FCNTL_DATA_VERSION]] ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to ** a database file. The argument is a pointer to a 32-bit unsigned integer. ** The "data version" for the pager is written into the pointer. The ** "data version" changes whenever any change occurs to the corresponding ** database file, either through SQL statements on the same database | > > > > > > | 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 | ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]] ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS ** to block for up to M milliseconds before failing when attempting to ** obtain a file lock using the xLock or xShmLock methods of the VFS. ** The parameter is a pointer to a 32-bit signed integer that contains ** the value that M is to be set to. Before returning, the 32-bit signed ** integer is overwritten with the previous value of M. ** ** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]] ** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the ** VFS to block when taking a SHARED lock to connect to a wal mode database. ** This is used to implement the functionality associated with ** SQLITE_SETLK_BLOCK_ON_CONNECT. ** ** <li>[[SQLITE_FCNTL_DATA_VERSION]] ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to ** a database file. The argument is a pointer to a 32-bit unsigned integer. ** The "data version" for the pager is written into the pointer. The ** "data version" changes whenever any change occurs to the corresponding ** database file, either through SQL statements on the same database |
| ︙ | ︙ | |||
1193 1194 1195 1196 1197 1198 1199 | ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. ** ** <li>[[SQLITE_FCNTL_EXTERNAL_READER]] ** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect ** whether or not there is a database client in another process with a wal-mode | | | 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 | ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. ** ** <li>[[SQLITE_FCNTL_EXTERNAL_READER]] ** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect ** whether or not there is a database client in another process with a wal-mode ** transaction open on the database or not. It is only available on unix. The ** (void*) argument passed with this file-control should be a pointer to a ** value of type (int). The integer value is set to 1 if the database is a wal ** mode database and there exists at least one client in another process that ** currently has an SQL transaction open on the database. It is set to 0 if ** the database is not a wal-mode db, or if there is no such connection in any ** other process. This opcode cannot be used to detect transactions opened ** by clients within the current process, only within other processes. |
| ︙ | ︙ | |||
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 | #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 #define SQLITE_FCNTL_NULL_IO 43 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO | > | 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 | #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 #define SQLITE_FCNTL_NULL_IO 43 #define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| ︙ | ︙ | |||
1617 1618 1619 1620 1621 1622 1623 | ** the library (perhaps it is unable to allocate a needed resource such ** as a mutex) it returns an [error code] other than [SQLITE_OK]. ** ** ^The sqlite3_initialize() routine is called internally by many other ** SQLite interfaces so that an application usually does not need to ** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] ** calls sqlite3_initialize() so the SQLite library will be automatically | | | 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 | ** the library (perhaps it is unable to allocate a needed resource such ** as a mutex) it returns an [error code] other than [SQLITE_OK]. ** ** ^The sqlite3_initialize() routine is called internally by many other ** SQLite interfaces so that an application usually does not need to ** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] ** calls sqlite3_initialize() so the SQLite library will be automatically ** initialized when [sqlite3_open()] is called if it has not been initialized ** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] ** compile-time option, then the automatic calls to sqlite3_initialize() ** are omitted and the application must call sqlite3_initialize() directly ** prior to using any other SQLite interface. For maximum portability, ** it is recommended that applications always invoke sqlite3_initialize() ** directly prior to using any other SQLite interface. Future releases ** of SQLite may require this. In other words, the behavior exhibited |
| ︙ | ︙ | |||
1874 1875 1876 1877 1878 1879 1880 | ** ** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt> ** <dd> ^(The SQLITE_CONFIG_GETMALLOC option takes a single argument which ** is a pointer to an instance of the [sqlite3_mem_methods] structure. ** The [sqlite3_mem_methods] ** structure is filled with the currently defined memory allocation routines.)^ ** This option can be used to overload the default memory allocation | | | | | | 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 | ** ** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt> ** <dd> ^(The SQLITE_CONFIG_GETMALLOC option takes a single argument which ** is a pointer to an instance of the [sqlite3_mem_methods] structure. ** The [sqlite3_mem_methods] ** structure is filled with the currently defined memory allocation routines.)^ ** This option can be used to overload the default memory allocation ** routines with a wrapper that simulates memory allocation failure or ** tracks memory usage, for example. </dd> ** ** [[SQLITE_CONFIG_SMALL_MALLOC]] <dt>SQLITE_CONFIG_SMALL_MALLOC</dt> ** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes a single argument of ** type int, interpreted as a boolean, which if true provides a hint to ** SQLite that it should avoid large memory allocations if possible. ** SQLite will run faster if it is free to make large memory allocations, ** but some applications might prefer to run slower in exchange for ** guarantees about memory fragmentation that are possible if large ** allocations are avoided. This hint is normally off. ** </dd> ** ** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt> ** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes a single argument of type int, ** interpreted as a boolean, which enables or disables the collection of ** memory allocation statistics. ^(When memory allocation statistics are ** disabled, the following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_hard_heap_limit64()] ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] |
| ︙ | ︙ | |||
1933 1934 1935 1936 1937 1938 1939 | ** ^When pMem is not NULL, SQLite will strive to use the memory provided ** to satisfy page cache needs, falling back to [sqlite3_malloc()] if ** a page cache line is larger than sz bytes or if all of the pMem buffer ** is exhausted. ** ^If pMem is NULL and N is non-zero, then each database connection ** does an initial bulk allocation for page cache memory ** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or | | | 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 | ** ^When pMem is not NULL, SQLite will strive to use the memory provided ** to satisfy page cache needs, falling back to [sqlite3_malloc()] if ** a page cache line is larger than sz bytes or if all of the pMem buffer ** is exhausted. ** ^If pMem is NULL and N is non-zero, then each database connection ** does an initial bulk allocation for page cache memory ** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or ** of -1024*N bytes if N is negative. ^If additional ** page cache memory is needed beyond what is provided by the initial ** allocation, then SQLite goes to [sqlite3_malloc()] separately for each ** additional cache line. </dd> ** ** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt> ** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer ** that SQLite will use for all of its dynamic memory allocation needs |
| ︙ | ︙ | |||
1962 1963 1964 1965 1966 1967 1968 | ** The minimum allocation size is capped at 2**12. Reasonable values ** for the minimum allocation size are 2**5 through 2**8.</dd> ** ** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt> ** <dd> ^(The SQLITE_CONFIG_MUTEX option takes a single argument which is a ** pointer to an instance of the [sqlite3_mutex_methods] structure. ** The argument specifies alternative low-level mutex routines to be used | | | 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 | ** The minimum allocation size is capped at 2**12. Reasonable values ** for the minimum allocation size are 2**5 through 2**8.</dd> ** ** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt> ** <dd> ^(The SQLITE_CONFIG_MUTEX option takes a single argument which is a ** pointer to an instance of the [sqlite3_mutex_methods] structure. ** The argument specifies alternative low-level mutex routines to be used ** in place of the mutex routines built into SQLite.)^ ^SQLite makes a copy of ** the content of the [sqlite3_mutex_methods] structure before the call to ** [sqlite3_config()] returns. ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** the entire mutexing subsystem is omitted from the build and hence calls to ** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will ** return [SQLITE_ERROR].</dd> ** |
| ︙ | ︙ | |||
1985 1986 1987 1988 1989 1990 1991 | ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** the entire mutexing subsystem is omitted from the build and hence calls to ** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will ** return [SQLITE_ERROR].</dd> ** ** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt> ** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine | | | | | | | > > > | | | 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 |
** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
** the entire mutexing subsystem is omitted from the build and hence calls to
** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will
** return [SQLITE_ERROR].</dd>
**
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
** the default size of [lookaside memory] on each [database connection].
** The first argument is the
** size of each lookaside buffer slot ("sz") and the second is the number of
** slots allocated to each database connection ("cnt").)^
** ^(SQLITE_CONFIG_LOOKASIDE sets the <i>default</i> lookaside size.
** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can
** be used to change the lookaside configuration on individual connections.)^
** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the
** default lookaside configuration at compile-time.
** </dd>
**
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
** a pointer to an [sqlite3_pcache_methods2] object. This object specifies
** the interface to a custom page cache implementation.)^
** ^SQLite makes a copy of the [sqlite3_pcache_methods2] object.</dd>
**
** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>
** <dd> ^(The SQLITE_CONFIG_GETPCACHE2 option takes a single argument which
** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies off
** the current page cache implementation into that object.)^ </dd>
**
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
** <dd> The SQLITE_CONFIG_LOG option is used to configure the SQLite
** global [error log].
** (^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
** function with a call signature of void(*)(void*,int,const char*),
** and a pointer to void. ^If the function pointer is not NULL, it is
** invoked by [sqlite3_log()] to process each logging event. ^If the
** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op.
** ^The void pointer that is the second argument to SQLITE_CONFIG_LOG is
** passed through as the first parameter to the application-defined logger
** function whenever that function is invoked. ^The second parameter to
** the logger function is a copy of the first parameter to the corresponding
** [sqlite3_log()] call and is intended to be a [result code] or an
** [extended result code]. ^The third parameter passed to the logger is
** a log message after formatting via [sqlite3_snprintf()].
** The SQLite logging interface is not reentrant; the logger function
** supplied by the application must not invoke any SQLite interface.
** In a multi-threaded application, the application-defined logger
** function must be threadsafe. </dd>
**
** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
** <dd>^(The SQLITE_CONFIG_URI option takes a single argument of type int.
|
| ︙ | ︙ | |||
2207 2208 2209 2210 2211 2212 2213 | #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 | > > | > > > > > > | | > > > > > > > | < | | > | > > > > > > > > > > | | | < | < | | > > > > > > > > > > | > | 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 |
#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 parameter to the [sqlite3_db_config()] interface.
**
** The [sqlite3_db_config()] interface is a var-args function. It takes a
** variable number of parameters, though always at least two. The number of
** parameters passed into sqlite3_db_config() depends on which of these
** constants is given as the second parameter. This documentation page
** refers to parameters beyond the second as "arguments". Thus, when this
** page says "the N-th argument" it means "the N-th parameter past the
** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()".
**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
** should check the return code from [sqlite3_db_config()] to make sure that
** the call worked. ^The [sqlite3_db_config()] interface will return a
** non-zero [error code] if a discontinued or unsupported configuration option
** is invoked.
**
** <dl>
** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
** configuration of the [lookaside memory allocator] within a database
** connection.
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
** in the [DBCONFIG arguments|usual format].
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
** should have a total of five parameters.
** <ol>
** <li><p>The first argument ("buf") is a
** pointer to a memory buffer to use for lookaside memory.
** The first argument may be NULL in which case SQLite will allocate the
** lookaside buffer itself using [sqlite3_malloc()].
** <li><P>The second argument ("sz") is the
** size of each lookaside buffer slot. Lookaside is disabled if "sz"
** is less than 8. The "sz" argument should be a multiple of 8 less than
** 65536. If "sz" does not meet this constraint, it is reduced in size until
** it does.
** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so
** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
** parameter is usually chosen so that the product of "sz" and "cnt" is less
** than 1,000,000.
** </ol>
** <p>If the "buf" argument is not NULL, then it must
** point to a memory buffer with a size that is greater than
** or equal to the product of "sz" and "cnt".
** The buffer must be aligned to an 8-byte boundary.
** The lookaside memory
** configuration for a database connection can only be changed when that
** connection is not currently using lookaside memory, or in other words
** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero.
** Any attempt to change the lookaside memory configuration when lookaside
** memory is in use leaves the configuration unchanged and returns
** [SQLITE_BUSY].
** If the "buf" argument is NULL and an attempt
** to allocate memory based on "sz" and "cnt" fails, then
** lookaside is silently disabled.
** <p>
** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the
** default lookaside configuration at initialization. The
** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside
** configuration at compile-time. Typical values for lookaside are 1200 for
** "sz" and 40 to 100 for "cnt".
** </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
** <dd> ^This option is used to enable or disable the enforcement of
** [foreign key constraints]. This is the same setting that is
** enabled or disabled by the [PRAGMA foreign_keys] statement.
** The first argument is an integer which is 0 to disable FK enforcement,
** positive to enable FK enforcement or negative to leave FK enforcement
** unchanged. The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether FK enforcement is off or on
** following this call. The second parameter may be a NULL pointer, in
** which case the FK enforcement setting is not reported back. </dd>
**
|
| ︙ | ︙ | |||
2265 2266 2267 2268 2269 2270 2271 | ** is written 0 or 1 to indicate whether triggers are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back. ** ** <p>Originally this option disabled all triggers. ^(However, since ** SQLite version 3.35.0, TEMP triggers are still allowed even if ** this option is off. So, in other words, this option now only disables | | | | | | | > > > > | | | | | | | > | | > | > | | | | 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 | ** is written 0 or 1 to indicate whether triggers are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back. ** ** <p>Originally this option disabled all triggers. ^(However, since ** SQLite version 3.35.0, TEMP triggers are still allowed even if ** this option is off. So, in other words, this option now only disables ** triggers in the main database schema or in the schemas of [ATTACH]-ed ** databases.)^ </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_VIEW]] ** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt> ** <dd> ^This option is used to enable or disable [CREATE VIEW | views]. ** There must be two additional arguments. ** The first argument is an integer which is 0 to disable views, ** positive to enable views or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether views are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in ** which case the view setting is not reported back. ** ** <p>Originally this option disabled all views. ^(However, since ** SQLite version 3.35.0, TEMP views are still allowed even if ** this option is off. So, in other words, this option now only disables ** views in the main database schema or in the schemas of ATTACH-ed ** databases.)^ </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> ** <dd> ^This option is used to enable or disable the ** [fts3_tokenizer()] function which is part of the ** [FTS3] full-text search engine extension. ** There must be two additional arguments. ** The first argument is an integer which is 0 to disable fts3_tokenizer() or ** positive to enable fts3_tokenizer() or negative to leave the setting ** unchanged. ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled ** following this call. The second parameter may be a NULL pointer, in ** which case the new setting is not reported back. </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]] ** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt> ** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()] ** interface independently of the [load_extension()] SQL function. ** The [sqlite3_enable_load_extension()] API enables or disables both the ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. ** There must be two additional arguments. ** When the first argument to this interface is 1, then only the C-API is ** enabled and the SQL function remains disabled. If the first argument to ** this interface is 0, then both the C-API and the SQL function are disabled. ** If the first argument is -1, then no changes are made to the state of either ** the C-API or the SQL function. ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface ** is disabled or enabled following this call. The second parameter may ** be a NULL pointer, in which case the new setting is not reported back. ** </dd> ** ** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt> ** <dd> ^This option is used to change the name of the "main" database ** schema. This option does not follow the ** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format]. ** This option takes exactly one additional argument so that the ** [sqlite3_db_config()] call has a total of three parameters. The ** extra argument must be a pointer to a constant UTF8 string which ** will become the new schema name in place of "main". ^SQLite does ** not make a copy of the new main schema name string, so the application ** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME ** is unchanged until after the database connection closes. ** </dd> ** ** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] ** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt> ** <dd> Usually, when a database in [WAL mode] is closed or detached from a ** database handle, SQLite checks if if there are other connections to the ** same database, and if there are no other database connection (if the ** connection being closed is the last open connection to the database), ** then SQLite performs a [checkpoint] before closing the connection and ** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can ** be used to override that behavior. The first argument passed to this ** operation (the third parameter to [sqlite3_db_config()]) is an integer ** which is positive to disable checkpoints-on-close, or zero (the default) ** to enable them, and negative to leave the setting unchanged. ** The second argument (the fourth parameter) is a pointer to an integer ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. ** </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_QPSG]] <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt> ** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates ** the [query planner stability guarantee] (QPSG). When the QPSG is active, |
| ︙ | ︙ | |||
2421 2422 2423 2424 2425 2426 2427 | ** integer into which is written 0 or 1 to indicate whether the writable_schema ** is enabled or disabled following this call. ** </dd> ** ** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]] ** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt> ** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates | | | 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 | ** integer into which is written 0 or 1 to indicate whether the writable_schema ** is enabled or disabled following this call. ** </dd> ** ** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]] ** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt> ** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates ** the legacy behavior of the [ALTER TABLE RENAME] command such that it ** behaves as it did prior to [version 3.24.0] (2018-06-04). See the ** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for ** additional information. This feature can also be turned on and off ** using the [PRAGMA legacy_alter_table] statement. ** </dd> ** ** [[SQLITE_DBCONFIG_DQS_DML]] |
| ︙ | ︙ | |||
2470 2471 2472 2473 2474 2475 2476 | ** can also be controlled using the [PRAGMA trusted_schema] statement. ** </dd> ** ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] ** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt> ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates ** the legacy file format flag. When activated, this flag causes all newly | | | 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 | ** can also be controlled using the [PRAGMA trusted_schema] statement. ** </dd> ** ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] ** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt> ** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates ** the legacy file format flag. When activated, this flag causes all newly ** created database files to have a schema format version number (the 4-byte ** integer found at offset 44 into the database header) of 1. This in turn ** means that the resulting database file will be readable and writable by ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, ** newly created databases are generally not understandable by SQLite versions ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there ** is now scarcely any need to generate database files that are compatible ** all the way back to version 3.0.0, and so this setting is of little |
| ︙ | ︙ | |||
2496 2497 2498 2499 2500 2501 2502 | ** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt> ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() ** statistics. For statistics to be collected, the flag must be set on ** the database handle both when the SQL statement is prepared and when it ** is stepped. The flag is set (collection of statistics is enabled) | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** <dt>SQLITE_DBCONFIG_STMT_SCANSTATUS</dt> ** <dd>The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in ** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears ** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() ** statistics. For statistics to be collected, the flag must be set on ** the database handle both when the SQL statement is prepared and when it ** is stepped. The flag is set (collection of statistics is enabled) ** by default. <p>This option takes two arguments: an integer and a pointer to ** an integer. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the statement scanstatus option. If the second argument ** is not NULL, then the value of the statement scanstatus setting after ** processing the first argument is written into the integer that the second ** argument points to. ** </dd> ** ** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] ** <dt>SQLITE_DBCONFIG_REVERSE_SCANORDER</dt> ** <dd>The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order ** in which tables and indexes are scanned so that the scans start at the end ** and work toward the beginning rather than starting at the beginning and ** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the ** same as setting [PRAGMA reverse_unordered_selects]. <p>This option takes ** two arguments which are an integer and a pointer to an integer. The first ** argument is 1, 0, or -1 to enable, disable, or leave unchanged the ** reverse scan order flag, respectively. If the second argument is not NULL, ** then 0 or 1 is written into the integer that the second argument points to ** depending on if the reverse scan order flag is set after processing the ** first argument. ** </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]] ** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE</dt> ** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables ** the ability of the [ATTACH DATABASE] SQL command to create a new database ** file if the database filed named in the ATTACH command does not already ** exist. This ability of ATTACH to create a new database is enabled by ** default. Applications can disable or reenable the ability for ATTACH to ** create new database files using this DBCONFIG option.<p> ** This option takes two arguments which are an integer and a pointer ** to an integer. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the attach-create flag, respectively. If the second ** argument is not NULL, then 0 or 1 is written into the integer that the ** second argument points to depending on if the attach-create flag is set ** after processing the first argument. ** </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]] ** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE</dt> ** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the ** ability of the [ATTACH DATABASE] SQL command to open a database for writing. ** This capability is enabled by default. Applications can disable or ** reenable this capability using the current DBCONFIG option. If ** this capability is disabled, the [ATTACH] command will still work, ** but the database will be opened read-only. If this option is disabled, ** then the ability to create a new database using [ATTACH] is also disabled, ** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE] ** option.<p> ** This option takes two arguments which are an integer and a pointer ** to an integer. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the ability to ATTACH another database for writing, ** respectively. If the second argument is not NULL, then 0 or 1 is written ** into the integer to which the second argument points, depending on whether ** the ability to ATTACH a read/write database is enabled or disabled ** after processing the first argument. ** </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]] ** <dt>SQLITE_DBCONFIG_ENABLE_COMMENTS</dt> ** <dd>The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the ** ability to include comments in SQL text. Comments are enabled by default. ** An application can disable or reenable comments in SQL text using this ** DBCONFIG option.<p> ** This option takes two arguments which are an integer and a pointer ** to an integer. The first argument is 1, 0, or -1 to enable, disable, or ** leave unchanged the ability to use comments in SQL text, ** respectively. If the second argument is not NULL, then 0 or 1 is written ** into the integer that the second argument points to depending on if ** comments are allowed in SQL text after processing the first argument. ** </dd> ** ** </dl> ** ** [[DBCONFIG arguments]] <h3>Arguments To SQLITE_DBCONFIG Options</h3> ** ** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the ** overall call to [sqlite3_db_config()] has a total of four parameters. ** The first argument (the third parameter to sqlite3_db_config()) is an integer. ** The second argument is a pointer to an integer. If the first argument is 1, ** then the option becomes enabled. If the first integer argument is 0, then the ** option is disabled. If the first argument is -1, then the option setting ** is unchanged. The second argument, the pointer to an integer, may be NULL. ** If the second argument is not NULL, then a value of 0 or 1 is written into ** the integer to which the second argument points, depending on whether the ** setting is disabled or enabled after applying any changes specified by ** the first argument. ** ** <p>While most SQLITE_DBCONFIG options use the argument format ** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME] ** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the ** documentation of those exceptional options for details. */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ |
| ︙ | ︙ | |||
2541 2542 2543 2544 2545 2546 2547 | #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ | > > > | | 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 | #define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */ #define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ #define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ #define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */ #define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result |
| ︙ | ︙ | |||
2793 2794 2795 2796 2797 2798 2799 | ** independent tokens (they are part of the token in which they are ** embedded) and thus do not count as a statement terminator. ^Whitespace ** and comments that follow the final semicolon are ignored. ** ** ^These routines return 0 if the statement is incomplete. ^If a ** memory allocation fails, then SQLITE_NOMEM is returned. ** | | | 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 | ** independent tokens (they are part of the token in which they are ** embedded) and thus do not count as a statement terminator. ^Whitespace ** and comments that follow the final semicolon are ignored. ** ** ^These routines return 0 if the statement is incomplete. ^If a ** memory allocation fails, then SQLITE_NOMEM is returned. ** ** ^These routines do not parse the SQL statements and thus ** will not detect syntactically incorrect SQL. ** ** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior ** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked ** automatically by sqlite3_complete16(). If that initialization fails, ** then the return value from sqlite3_complete16() will be non-zero ** regardless of whether or not the input SQL is complete.)^ |
| ︙ | ︙ | |||
2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 | ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ ** ** See also: [PRAGMA busy_timeout] */ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** METHOD: sqlite3 ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 | ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ ** ** See also: [PRAGMA busy_timeout] */ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Set the Setlk Timeout ** METHOD: sqlite3 ** ** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If ** the VFS supports blocking locks, it sets the timeout in ms used by ** eligible locks taken on wal mode databases by the specified database ** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does ** not support blocking locks, this function is a no-op. ** ** Passing 0 to this function disables blocking locks altogether. Passing ** -1 to this function requests that the VFS blocks for a long time - ** indefinitely if possible. The results of passing any other negative value ** are undefined. ** ** Internally, each SQLite database handle stores two timeout values - the ** busy-timeout (used for rollback mode databases, or if the VFS does not ** support blocking locks) and the setlk-timeout (used for blocking locks ** on wal-mode databases). The sqlite3_busy_timeout() method sets both ** values, this function sets only the setlk-timeout value. Therefore, ** to configure separate busy-timeout and setlk-timeout values for a single ** database handle, call sqlite3_busy_timeout() followed by this function. ** ** Whenever the number of connections to a wal mode database falls from ** 1 to 0, the last connection takes an exclusive lock on the database, ** then checkpoints and deletes the wal file. While it is doing this, any ** new connection that tries to read from the database fails with an ** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is ** passed to this API, the new connection blocks until the exclusive lock ** has been released. */ SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); /* ** CAPI3REF: Flags for sqlite3_setlk_timeout() */ #define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 /* ** CAPI3REF: Convenience Routines For Running Queries ** METHOD: sqlite3 ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. ** ** Definition: A <b>result table</b> is a memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the ** complete query results from one or more queries. ** ** The table conceptually has a number of rows and columns. But ** these numbers are not part of the result table itself. These ** numbers are obtained separately. Let N be the number of rows ** and M be the number of columns. |
| ︙ | ︙ | |||
3045 3046 3047 3048 3049 3050 3051 | ** ^The sqlite3_malloc64(N) routine works just like ** sqlite3_malloc(N) except that N is an unsigned 64-bit integer instead ** of a signed 32-bit integer. ** ** ^Calling sqlite3_free() with a pointer previously returned ** by sqlite3_malloc() or sqlite3_realloc() releases that memory so ** that it might be reused. ^The sqlite3_free() routine is | | | | | | 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 | ** ^The sqlite3_malloc64(N) routine works just like ** sqlite3_malloc(N) except that N is an unsigned 64-bit integer instead ** of a signed 32-bit integer. ** ** ^Calling sqlite3_free() with a pointer previously returned ** by sqlite3_malloc() or sqlite3_realloc() releases that memory so ** that it might be reused. ^The sqlite3_free() routine is ** a no-op if it is called with a NULL pointer. Passing a NULL pointer ** to sqlite3_free() is harmless. After being freed, memory ** should neither be read nor written. Even reading previously freed ** memory might result in a segmentation fault or other severe error. ** Memory corruption, a segmentation fault, or other severe error ** might result if sqlite3_free() is called with a non-NULL pointer that ** was not obtained from sqlite3_malloc() or sqlite3_realloc(). ** ** ^The sqlite3_realloc(X,N) interface attempts to resize a ** prior memory allocation X to be at least N bytes. ** ^If the X parameter to sqlite3_realloc(X,N) ** is a NULL pointer then its behavior is identical to calling ** sqlite3_malloc(N). ** ^If the N parameter to sqlite3_realloc(X,N) is zero or ** negative then the behavior is exactly the same as calling ** sqlite3_free(X). ** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation ** of at least N bytes in size or NULL if insufficient memory is available. ** ^If M is the size of the prior allocation, then min(N,M) bytes of the ** prior allocation are copied into the beginning of the buffer returned ** by sqlite3_realloc(X,N) and the prior allocation is freed. ** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the ** prior allocation is not freed. ** ** ^The sqlite3_realloc64(X,N) interface works the same as ** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead ** of a 32-bit signed integer. ** ** ^If X is a memory allocation previously obtained from sqlite3_malloc(), ** sqlite3_malloc64(), sqlite3_realloc(), or sqlite3_realloc64(), then ** sqlite3_msize(X) returns the size of that memory allocation in bytes. ** ^The value returned by sqlite3_msize(X) might be larger than the number |
| ︙ | ︙ | |||
3119 3120 3121 3122 3123 3124 3125 | ** ^The [sqlite3_memory_used()] routine returns the number of bytes ** of memory currently outstanding (malloced but not freed). ** ^The [sqlite3_memory_highwater()] routine returns the maximum ** value of [sqlite3_memory_used()] since the high-water mark ** was last reset. ^The values returned by [sqlite3_memory_used()] and ** [sqlite3_memory_highwater()] include any overhead ** added by SQLite in its implementation of [sqlite3_malloc()], | | | 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 | ** ^The [sqlite3_memory_used()] routine returns the number of bytes ** of memory currently outstanding (malloced but not freed). ** ^The [sqlite3_memory_highwater()] routine returns the maximum ** value of [sqlite3_memory_used()] since the high-water mark ** was last reset. ^The values returned by [sqlite3_memory_used()] and ** [sqlite3_memory_highwater()] include any overhead ** added by SQLite in its implementation of [sqlite3_malloc()], ** but not overhead added by any underlying system library ** routines that [sqlite3_malloc()] may call. ** ** ^The memory high-water mark is reset to the current value of ** [sqlite3_memory_used()] if and only if the parameter to ** [sqlite3_memory_highwater()] is true. ^The value returned ** by [sqlite3_memory_highwater(1)] is the high-water mark ** prior to the reset. |
| ︙ | ︙ | |||
3571 3572 3573 3574 3575 3576 3577 | ** <dd>The new database connection will use the "serialized" ** [threading mode].)^ This means the multiple threads can safely ** attempt to use the same database connection at the same time. ** (Mutexes will block any actual concurrency, but in this mode ** there is no harm in trying.) ** ** ^(<dt>[SQLITE_OPEN_SHAREDCACHE]</dt> | | | | 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 | ** <dd>The new database connection will use the "serialized" ** [threading mode].)^ This means the multiple threads can safely ** attempt to use the same database connection at the same time. ** (Mutexes will block any actual concurrency, but in this mode ** there is no harm in trying.) ** ** ^(<dt>[SQLITE_OPEN_SHAREDCACHE]</dt> ** <dd>The database is opened with [shared cache] enabled, overriding ** the default shared cache setting provided by ** [sqlite3_enable_shared_cache()].)^ ** The [use of shared cache mode is discouraged] and hence shared cache ** capabilities may be omitted from many builds of SQLite. In such cases, ** this option is a no-op. ** ** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt> ** <dd>The database is opened with [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 |
| ︙ | ︙ | |||
3914 3915 3916 3917 3918 3919 3920 | ** CAPI3REF: Create and Destroy VFS Filenames ** ** These interfaces are provided for use by [VFS shim] implementations and ** are not useful outside of that context. ** ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of ** database filename D with corresponding journal file J and WAL file W and | | | 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 | ** CAPI3REF: Create and Destroy VFS Filenames ** ** These interfaces are provided for use by [VFS shim] implementations and ** are not useful outside of that context. ** ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of ** database filename D with corresponding journal file J and WAL file W and ** an array P of N URI Key/Value pairs. The result from ** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that ** is safe to pass to routines like: ** <ul> ** <li> [sqlite3_uri_parameter()], ** <li> [sqlite3_uri_boolean()], ** <li> [sqlite3_uri_int64()], ** <li> [sqlite3_uri_key()], |
| ︙ | ︙ | |||
3997 3998 3999 4000 4001 4002 4003 | ** (See how SQLite handles [invalid UTF] for exceptions to this rule.) ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ ** ** ^The sqlite3_errstr(E) interface returns the English-language text | | | | 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 | ** (See how SQLite handles [invalid UTF] for exceptions to this rule.) ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ ** ** ^The sqlite3_errstr(E) interface returns the English-language text ** that describes the [result code] E, as UTF-8, or NULL if E is not a ** result code for which a text error message is available. ** ^(Memory to hold the error message string is managed internally ** and must not be freed by the application)^. ** ** ^If the most recent error references a specific token in the input ** SQL, the sqlite3_error_offset() interface returns the byte offset ** of the start of that token. ^The byte offset returned by ** sqlite3_error_offset() assumes that the input SQL is UTF-8. ** ^If the most recent error does not reference a specific token in the input ** SQL, then the sqlite3_error_offset() function returns -1. ** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between ** the time of the first error and the call to these interfaces. ** When that happens, the second error will be reported since these |
| ︙ | ︙ | |||
4104 4105 4106 4107 4108 4109 4110 |
/*
** CAPI3REF: Run-Time Limit Categories
** KEYWORDS: {limit category} {*limit categories}
**
** These constants define various performance limits
** that can be lowered at run-time using [sqlite3_limit()].
| | | | 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 |
/*
** CAPI3REF: Run-Time Limit Categories
** KEYWORDS: {limit category} {*limit categories}
**
** These constants define various performance limits
** that can be lowered at run-time using [sqlite3_limit()].
** A concise description of these limits follows, and additional information
** is available at [limits | Limits in SQLite].
**
** <dl>
** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt>
** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^
**
** [[SQLITE_LIMIT_SQL_LENGTH]] ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt>
** <dd>The maximum length of an SQL statement, in bytes.</dd>)^
|
| ︙ | ︙ | |||
4170 4171 4172 4173 4174 4175 4176 | #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 #define SQLITE_LIMIT_WORKER_THREADS 11 /* ** CAPI3REF: Prepare Flags ** | | | 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 | #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 #define SQLITE_LIMIT_WORKER_THREADS 11 /* ** CAPI3REF: Prepare Flags ** ** These constants define various flags that can be passed into the ** "prepFlags" parameter of the [sqlite3_prepare_v3()] and ** [sqlite3_prepare16_v3()] interfaces. ** ** New flags may be added in future releases of SQLite. ** ** <dl> ** [[SQLITE_PREPARE_PERSISTENT]] ^(<dt>SQLITE_PREPARE_PERSISTENT</dt> |
| ︙ | ︙ | |||
4257 4258 4259 4260 4261 4262 4263 | ** up to the first zero terminator or until the nByte bytes have been read, ** whichever comes first. ^If nByte is zero, then no prepared ** statement is generated. ** If the caller knows that the supplied string is nul-terminated, then ** there is a small performance advantage to passing an nByte parameter that ** is the number of bytes in the input string <i>including</i> ** the nul-terminator. | | | 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 | ** up to the first zero terminator or until the nByte bytes have been read, ** whichever comes first. ^If nByte is zero, then no prepared ** statement is generated. ** If the caller knows that the supplied string is nul-terminated, then ** there is a small performance advantage to passing an nByte parameter that ** is the number of bytes in the input string <i>including</i> ** the nul-terminator. ** Note that nByte measures the length of the input in bytes, not ** characters, even for the UTF-16 interfaces. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only ** compile the first statement in zSql, so *pzTail is left pointing to ** what remains uncompiled. ** |
| ︙ | ︙ | |||
4391 4392 4393 4394 4395 4396 4397 | ** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345 ** and parameter :xyz is unbound, then sqlite3_sql() will return ** the original string, "SELECT $abc,:xyz" but sqlite3_expanded_sql() ** will return "SELECT 2345,NULL".)^ ** ** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory ** is available to hold the result, or if the result would exceed the | | | 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 | ** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345 ** and parameter :xyz is unbound, then sqlite3_sql() will return ** the original string, "SELECT $abc,:xyz" but sqlite3_expanded_sql() ** will return "SELECT 2345,NULL".)^ ** ** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory ** is available to hold the result, or if the result would exceed the ** maximum string length determined by the [SQLITE_LIMIT_LENGTH]. ** ** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of ** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time ** option causes sqlite3_expanded_sql() to always return NULL. ** ** ^The strings returned by sqlite3_sql(P) and sqlite3_normalized_sql(P) ** are managed by SQLite and are automatically freed when the prepared |
| ︙ | ︙ | |||
4579 4580 4581 4582 4583 4584 4585 | typedef struct sqlite3_value sqlite3_value; /* ** CAPI3REF: SQL Function Context Object ** ** The context in which an SQL function executes is stored in an ** sqlite3_context object. ^A pointer to an sqlite3_context object | | | | 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 |
typedef struct sqlite3_value sqlite3_value;
/*
** CAPI3REF: SQL Function Context Object
**
** The context in which an SQL function executes is stored in an
** sqlite3_context object. ^A pointer to an sqlite3_context object
** is always the first parameter to [application-defined SQL functions].
** The application-defined SQL function implementation will pass this
** pointer through into calls to [sqlite3_result_int | sqlite3_result()],
** [sqlite3_aggregate_context()], [sqlite3_user_data()],
** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()],
** and/or [sqlite3_set_auxdata()].
*/
typedef struct sqlite3_context sqlite3_context;
/*
** CAPI3REF: Binding Values To Prepared Statements
** KEYWORDS: {host parameter} {host parameters} {host parameter name}
** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding}
** METHOD: sqlite3_stmt
**
** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants,
** literals may be replaced by a [parameter] that matches one of the following
** templates:
**
** <ul>
** <li> ?
** <li> ?NNN
** <li> :VVV
** <li> @VVV
|
| ︙ | ︙ | |||
4640 4641 4642 4643 4644 4645 4646 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then ** it should be a pointer to a well-formed unicode string that is ** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 ** otherwise. ** ** [[byte-order determination rules]] ^The byte-order of ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) | | | | 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 | ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then ** it should be a pointer to a well-formed unicode string that is ** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16 ** otherwise. ** ** [[byte-order determination rules]] ^The byte-order of ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF) ** found in the first character, which is removed, or in the absence of a BOM ** the byte order is the native byte order of the host ** machine for sqlite3_bind_text16() or the byte order specified in ** the 6th parameter for sqlite3_bind_text64().)^ ** ^If UTF16 input text contains invalid unicode ** characters, then SQLite might change those invalid characters ** into the unicode replacement character: U+FFFD. ** ** ^(In those routines that have a fourth argument, its value is the ** number of bytes in the parameter. To be clear: the value is the ** number of <u>bytes</u> in the value, not the number of characters.)^ ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() ** is negative, then the length of the string is ** the number of bytes up to the first zero terminator. ** If the fourth parameter to sqlite3_bind_blob() is negative, then ** the behavior is undefined. ** If a non-negative fourth parameter is provided to sqlite3_bind_text() ** or sqlite3_bind_text16() or sqlite3_bind_text64() then ** that parameter must be the byte offset ** where the NUL terminator would occur assuming the string were NUL ** terminated. If any NUL characters occur at byte offsets less than ** the value of the fourth parameter then the resulting string value will ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** ** ^The fifth argument to the BLOB and string binding interfaces controls ** or indicates the lifetime of the object referenced by the third parameter. ** These three options exist: |
| ︙ | ︙ | |||
4872 4873 4874 4875 4876 4877 4878 | SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); /* ** CAPI3REF: Source Of Data In A Query Result ** METHOD: sqlite3_stmt ** ** ^These routines provide a means to determine the database, table, and | | | 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 | SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); /* ** CAPI3REF: Source Of Data In A Query Result ** METHOD: sqlite3_stmt ** ** ^These routines provide a means to determine the database, table, and ** table column that is the origin of a particular result column in a ** [SELECT] statement. ** ^The name of the database or table or column can be returned as ** either a UTF-8 or UTF-16 string. ^The _database_ routines return ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. ** ^The returned string is valid until the [prepared statement] is destroyed ** using [sqlite3_finalize()] or until the statement is automatically |
| ︙ | ︙ | |||
5010 5011 5012 5013 5014 5015 5016 | ** more threads at the same moment in time. ** ** For all versions of SQLite up to and including 3.6.23.1, a call to ** [sqlite3_reset()] was required after sqlite3_step() returned anything ** other than [SQLITE_ROW] before any subsequent invocation of ** sqlite3_step(). Failure to reset the prepared statement using ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from | | | 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 | ** more threads at the same moment in time. ** ** For all versions of SQLite up to and including 3.6.23.1, a call to ** [sqlite3_reset()] was required after sqlite3_step() returned anything ** other than [SQLITE_ROW] before any subsequent invocation of ** sqlite3_step(). Failure to reset the prepared statement using ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), ** sqlite3_step() began ** calling [sqlite3_reset()] automatically in this circumstance rather ** than returning [SQLITE_MISUSE]. This is not considered a compatibility ** break because any application that ever receives an SQLITE_MISUSE error ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option ** can be used to restore the legacy behavior. ** |
| ︙ | ︙ | |||
5316 5317 5318 5319 5320 5321 5322 | /* ** CAPI3REF: Destroy A Prepared Statement Object ** DESTRUCTOR: sqlite3_stmt ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors | | | 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 | /* ** CAPI3REF: Destroy A Prepared Statement Object ** DESTRUCTOR: sqlite3_stmt ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors ** or if the statement has never been evaluated, then sqlite3_finalize() returns ** SQLITE_OK. ^If the most recent evaluation of statement S failed, then ** sqlite3_finalize(S) returns the appropriate [error code] or ** [extended error code]. ** ** ^The sqlite3_finalize(S) routine can be called at any point during ** the life cycle of [prepared statement] S: ** before statement S is ever evaluated, after |
| ︙ | ︙ | |||
5441 5442 5443 5444 5445 5446 5447 | ** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY] ** flag, which if present prevents the function from being invoked from ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, ** index expressions, or the WHERE clause of partial indexes. ** ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for ** all application-defined SQL functions that do not need to be | | | | 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 | ** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY] ** flag, which if present prevents the function from being invoked from ** within VIEWs, TRIGGERs, CHECK constraints, generated column expressions, ** index expressions, or the WHERE clause of partial indexes. ** ** For best security, the [SQLITE_DIRECTONLY] flag is recommended for ** all application-defined SQL functions that do not need to be ** used inside of triggers, views, CHECK constraints, or other elements of ** the database schema. This flag is especially recommended for SQL ** functions that have side effects or reveal internal application state. ** Without this flag, an attacker might be able to modify the schema of ** a database file to include invocations of the function with parameters ** chosen by the attacker, which the application will then execute when ** the database file is opened and read. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the |
| ︙ | ︙ | |||
5473 5474 5475 5476 5477 5478 5479 | ** which case a regular aggregate function is created, or must both be ** non-NULL, in which case the new function may be used as either an aggregate ** or aggregate window function. More details regarding the implementation ** of aggregate window functions are ** [user-defined window functions|available here]. ** ** ^(If the final parameter to sqlite3_create_function_v2() or | | | 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 | ** which case a regular aggregate function is created, or must both be ** non-NULL, in which case the new function may be used as either an aggregate ** or aggregate window function. More details regarding the implementation ** of aggregate window functions are ** [user-defined window functions|available here]. ** ** ^(If the final parameter to sqlite3_create_function_v2() or ** sqlite3_create_window_function() is not NULL, then it is the destructor for ** the application data pointer. The destructor is invoked when the function ** is deleted, either by being overloaded or when the database connection ** closes.)^ ^The destructor is also invoked if the call to ** sqlite3_create_function_v2() fails. ^When the destructor callback is ** invoked, it is passed a single argument which is a copy of the application ** data pointer which was the fifth parameter to sqlite3_create_function_v2(). ** |
| ︙ | ︙ | |||
5548 5549 5550 5551 5552 5553 5554 | void (*xInverse)(sqlite3_context*,int,sqlite3_value**), void(*xDestroy)(void*) ); /* ** CAPI3REF: Text Encodings ** | | | 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 | void (*xInverse)(sqlite3_context*,int,sqlite3_value**), void(*xDestroy)(void*) ); /* ** CAPI3REF: Text Encodings ** ** These constants define integer codes that represent the various ** text encodings supported by SQLite. */ #define SQLITE_UTF8 1 /* IMP: R-37514-35566 */ #define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */ #define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */ #define SQLITE_UTF16 4 /* Use native byte order */ #define SQLITE_ANY 5 /* Deprecated */ |
| ︙ | ︙ | |||
5640 5641 5642 5643 5644 5645 5646 | ** ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> ** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_result_subtype()] to cause a sub-type to be associated with its ** result. ** Every function that invokes [sqlite3_result_subtype()] should have this ** property. If it does not, then the call to [sqlite3_result_subtype()] | | | 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 | ** ** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd> ** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call ** [sqlite3_result_subtype()] to cause a sub-type to be associated with its ** result. ** 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 a 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 |
| ︙ | ︙ | |||
5767 5768 5769 5770 5771 5772 5773 | ** then the conversion is performed. Otherwise no conversion occurs. ** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** ** ^Within the [xUpdate] method of a [virtual table], the ** sqlite3_value_nochange(X) interface returns true if and only if ** the column corresponding to X is unchanged by the UPDATE operation ** that the xUpdate method call was invoked to implement and if | | | 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 | ** then the conversion is performed. Otherwise no conversion occurs. ** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** ** ^Within the [xUpdate] method of a [virtual table], the ** sqlite3_value_nochange(X) interface returns true if and only if ** the column corresponding to X is unchanged by the UPDATE operation ** that the xUpdate method call was invoked to implement and if ** the prior [xColumn] method call that was invoked to extract ** the value for that column returned without setting a result (probably ** because it queried [sqlite3_vtab_nochange()] and found that the column ** was unchanging). ^Within an [xUpdate] method, any value for which ** sqlite3_value_nochange(X) is true will in all other respects appear ** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other ** than within an [xUpdate] method call for an UPDATE statement, then ** the return value is arbitrary and meaningless. |
| ︙ | ︙ | |||
5873 5874 5875 5876 5877 5878 5879 | SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); /* ** CAPI3REF: Copy And Free SQL Values ** METHOD: sqlite3_value ** ** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value] | | | 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 | SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); /* ** CAPI3REF: Copy And Free SQL Values ** METHOD: sqlite3_value ** ** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value] ** object V and returns a pointer to that copy. ^The [sqlite3_value] returned ** is a [protected sqlite3_value] object even if the input is not. ** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a ** memory allocation fails. ^If V is a [pointer value], then the result ** of sqlite3_value_dup(V) is a NULL value. ** ** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object ** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer |
| ︙ | ︙ | |||
5911 5912 5913 5914 5915 5916 5917 | ** first time from within xFinal().)^ ** ** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer ** when first called if N is less than or equal to zero or if a memory ** allocation error occurs. ** ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is | | | 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 | ** first time from within xFinal().)^ ** ** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer ** when first called if N is less than or equal to zero or if a memory ** allocation error occurs. ** ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is ** determined by the N parameter on the first successful call. Changing the ** value of N in any subsequent call to sqlite3_aggregate_context() within ** the same aggregate function instance will not resize the memory ** allocation.)^ Within the xFinal callback, it is customary to set ** N=0 in calls to sqlite3_aggregate_context(C,N) so that no ** pointless memory allocations occur. ** ** ^SQLite automatically frees the memory allocated by |
| ︙ | ︙ | |||
6073 6074 6075 6076 6077 6078 6079 | ** There is no way to enumerate the client data pointers ** associated with a database connection. The N parameter can be thought ** of as a secret key such that only code that knows the secret key is able ** to access the associated data. ** ** Security Warning: These interfaces should not be exposed in scripting ** languages or in other circumstances where it might be possible for an | | | 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 | ** There is no way to enumerate the client data pointers ** associated with a database connection. The N parameter can be thought ** of as a secret key such that only code that knows the secret key is able ** to access the associated data. ** ** Security Warning: These interfaces should not be exposed in scripting ** languages or in other circumstances where it might be possible for an ** attacker to invoke them. Any agent that can invoke these interfaces ** can probably also take control of the process. ** ** Database connection client data is only available for SQLite ** version 3.44.0 ([dateof:3.44.0]) and later. ** ** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()]. */ |
| ︙ | ︙ | |||
6187 6188 6189 6190 6191 6192 6193 | ** the string length itself by searching the 2nd parameter for the first ** zero character. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined ** function result. If the 3rd parameter is non-negative, then it ** must be the byte offset into the string where the NUL terminator would | | | 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 | ** the string length itself by searching the 2nd parameter for the first ** zero character. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined ** function result. If the 3rd parameter is non-negative, then it ** must be the byte offset into the string where the NUL terminator would ** appear if the string were NUL terminated. If any NUL characters occur ** in the string at a byte offset that is less than the value of the 3rd ** parameter, then the resulting string will contain embedded NULs and the ** result of expressions operating on strings with embedded NULs is undefined. ** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that ** function as the destructor on the text or BLOB result when it has ** finished using that result. |
| ︙ | ︙ | |||
6245 6246 6247 6248 6249 6250 6251 | ** [application-defined SQL function] using [sqlite3_value_pointer()]. ** ^If the D parameter is not NULL, then it is a pointer to a destructor ** for the P parameter. ^SQLite invokes D with P as its only argument ** when SQLite is finished with P. The T parameter should be a static ** string and preferably a string literal. The sqlite3_result_pointer() ** routine is part of the [pointer passing interface] added for SQLite 3.20.0. ** | | | 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 |
** [application-defined SQL function] using [sqlite3_value_pointer()].
** ^If the D parameter is not NULL, then it is a pointer to a destructor
** for the P parameter. ^SQLite invokes D with P as its only argument
** when SQLite is finished with P. The T parameter should be a static
** string and preferably a string literal. The sqlite3_result_pointer()
** routine is part of the [pointer passing interface] added for SQLite 3.20.0.
**
** If these routines are called from within a different thread
** than the one containing the application-defined function that received
** the [sqlite3_context] pointer, the results are undefined.
*/
SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*,
sqlite3_uint64,void(*)(void*));
SQLITE_API void sqlite3_result_double(sqlite3_context*, double);
|
| ︙ | ︙ | |||
6651 6652 6653 6654 6655 6656 6657 | SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Return The Schema Name For A Database Connection ** METHOD: sqlite3 ** ** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name | | | 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 | SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Return The Schema Name For A Database Connection ** METHOD: sqlite3 ** ** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name ** for the N-th database on database connection D, or a NULL pointer if N is ** out of range. An N value of 0 means the main database file. An N of 1 is ** the "temp" schema. Larger values of N correspond to various ATTACH-ed ** databases. ** ** Space to hold the string that is returned by sqlite3_db_name() is managed ** by SQLite itself. The string might be deallocated by any operation that ** changes the schema, including [ATTACH] or [DETACH] or calls to |
| ︙ | ︙ | |||
6746 6747 6748 6749 6750 6751 6752 | ** <dd>The SQLITE_TXN_NONE state means that no transaction is currently ** pending.</dd> ** ** [[SQLITE_TXN_READ]] <dt>SQLITE_TXN_READ</dt> ** <dd>The SQLITE_TXN_READ state means that the database is currently ** in a read transaction. Content has been read from the database file ** but nothing in the database file has changed. The transaction state | | | | 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 | ** <dd>The SQLITE_TXN_NONE state means that no transaction is currently ** pending.</dd> ** ** [[SQLITE_TXN_READ]] <dt>SQLITE_TXN_READ</dt> ** <dd>The SQLITE_TXN_READ state means that the database is currently ** in a read transaction. Content has been read from the database file ** but nothing in the database file has changed. The transaction state ** will be advanced to SQLITE_TXN_WRITE if any changes occur and there are ** no other conflicting concurrent write transactions. The transaction ** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or ** [COMMIT].</dd> ** ** [[SQLITE_TXN_WRITE]] <dt>SQLITE_TXN_WRITE</dt> ** <dd>The SQLITE_TXN_WRITE state means that the database is currently ** in a write transaction. Content has been written to the database file ** but has not yet committed. The transaction state will change to ** SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].</dd> */ #define SQLITE_TXN_NONE 0 #define SQLITE_TXN_READ 1 #define SQLITE_TXN_WRITE 2 /* ** CAPI3REF: Find the next prepared statement |
| ︙ | ︙ | |||
6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 | ** to be invoked whenever a row is updated, inserted or deleted in ** a [rowid table]. ** ^Any callback set by a previous call to this function ** for the same database connection is overridden. ** ** ^The second argument is a pointer to the function to invoke when a ** row is updated, inserted or deleted in a rowid table. ** ^The first argument to the callback is a copy of the third argument ** to sqlite3_update_hook(). ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], ** or [SQLITE_UPDATE], depending on the operation that caused the callback ** to be invoked. ** ^The third and fourth arguments to the callback contain pointers to the ** database and table name containing the affected row. | > > | 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 | ** to be invoked whenever a row is updated, inserted or deleted in ** a [rowid table]. ** ^Any callback set by a previous call to this function ** for the same database connection is overridden. ** ** ^The second argument is a pointer to the function to invoke when a ** row is updated, inserted or deleted in a rowid table. ** ^The update hook is disabled by invoking sqlite3_update_hook() ** with a NULL pointer as the second parameter. ** ^The first argument to the callback is a copy of the third argument ** to sqlite3_update_hook(). ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], ** or [SQLITE_UPDATE], depending on the operation that caused the callback ** to be invoked. ** ^The third and fourth arguments to the callback contain pointers to the ** database and table name containing the affected row. |
| ︙ | ︙ | |||
7034 7035 7036 7037 7038 7039 7040 | */ SQLITE_API int sqlite3_db_release_memory(sqlite3*); /* ** CAPI3REF: Impose A Limit On Heap Size ** ** These interfaces impose limits on the amount of heap memory that will be | | | 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 | */ SQLITE_API int sqlite3_db_release_memory(sqlite3*); /* ** CAPI3REF: Impose A Limit On Heap Size ** ** These interfaces impose limits on the amount of heap memory that will be ** used by all database connections within a single process. ** ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the ** soft limit on the amount of heap memory that may be allocated by SQLite. ** ^SQLite strives to keep heap memory utilization below the soft heap ** limit by reducing the number of pages held in the page cache ** as heap memory usages approaches the limit. ** ^The soft heap limit is "soft" because even though SQLite strives to stay |
| ︙ | ︙ | |||
7092 7093 7094 7095 7096 7097 7098 | ** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). ** <li> The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than ** from the heap. ** </ul>)^ ** ** The circumstances under which SQLite will enforce the heap limits may | | | 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 | ** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). ** <li> The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than ** from the heap. ** </ul>)^ ** ** The circumstances under which SQLite will enforce the heap limits may ** change in future releases of SQLite. */ SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N); /* ** CAPI3REF: Deprecated Soft Heap Limit Interface ** DEPRECATED |
| ︙ | ︙ | |||
7207 7208 7209 7210 7211 7212 7213 | ** So for example, if "samplelib" cannot be loaded, then names like ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might ** be tried also. ** ** ^The entry point is zProc. ** ^(zProc may be 0, in which case SQLite will try to come up with an ** entry point name on its own. It first tries "sqlite3_extension_init". | | | | 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 | ** So for example, if "samplelib" cannot be loaded, then names like ** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might ** be tried also. ** ** ^The entry point is zProc. ** ^(zProc may be 0, in which case SQLite will try to come up with an ** entry point name on its own. It first tries "sqlite3_extension_init". ** If that does not work, it constructs a name "sqlite3_X_init" where ** X consists of the lower-case equivalent of all ASCII alphabetic ** characters in the filename from the last "/" to the first following ** "." and omitting any initial "lib".)^ ** ^The sqlite3_load_extension() interface returns ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. ** ^If an error occurs and pzErrMsg is not 0, then the ** [sqlite3_load_extension()] interface shall attempt to ** fill *pzErrMsg with error message text stored in memory |
| ︙ | ︙ | |||
7279 7280 7281 7282 7283 7284 7285 | ** each new [database connection] that is created. The idea here is that ** xEntryPoint() is the entry point for a statically linked [SQLite extension] ** that is to be automatically loaded into all new database connections. ** ** ^(Even though the function prototype shows that xEntryPoint() takes ** no arguments and returns void, SQLite invokes xEntryPoint() with three ** arguments and expects an integer result as if the signature of the | | | 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 | ** each new [database connection] that is created. The idea here is that ** xEntryPoint() is the entry point for a statically linked [SQLite extension] ** that is to be automatically loaded into all new database connections. ** ** ^(Even though the function prototype shows that xEntryPoint() takes ** no arguments and returns void, SQLite invokes xEntryPoint() with three ** arguments and expects an integer result as if the signature of the ** entry point were as follows: ** ** <blockquote><pre> ** int xEntryPoint( ** sqlite3 *db, ** const char **pzErrMsg, ** const struct sqlite3_api_routines *pThunk ** ); |
| ︙ | ︙ | |||
7443 7444 7445 7446 7447 7448 7449 | ** about what parameters to pass to xFilter. ^If argvIndex>0 then ** the right-hand side of the corresponding aConstraint[] is evaluated ** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit ** is true, then the constraint is assumed to be fully handled by the ** virtual table and might not be checked again by the byte code.)^ ^(The ** aConstraintUsage[].omit flag is an optimization hint. When the omit flag ** is left in its default setting of false, the constraint will always be | | | 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 | ** about what parameters to pass to xFilter. ^If argvIndex>0 then ** the right-hand side of the corresponding aConstraint[] is evaluated ** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit ** is true, then the constraint is assumed to be fully handled by the ** virtual table and might not be checked again by the byte code.)^ ^(The ** aConstraintUsage[].omit flag is an optimization hint. When the omit flag ** is left in its default setting of false, the constraint will always be ** checked separately in byte code. If the omit flag is changed to true, then ** the constraint may or may not be checked in byte code. In other words, ** when the omit flag is true there is no guarantee that the constraint will ** not be checked again using byte code.)^ ** ** ^The idxNum and idxStr values are recorded and passed into the ** [xFilter] method. ** ^[sqlite3_free()] is used to free idxStr if and only if |
| ︙ | ︙ | |||
7469 7470 7471 7472 7473 7474 7475 | ** ** ^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] | | | 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 | ** ** ^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 as 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 |
| ︙ | ︙ | |||
7610 7611 7612 7613 7614 7615 7616 | ** ** ^The module name is registered on the [database connection] specified ** by the first parameter. ^The name of the module is given by the ** second parameter. ^The third parameter is a pointer to ** the implementation of the [virtual table module]. ^The fourth ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module | | | 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 | ** ** ^The module name is registered on the [database connection] specified ** by the first parameter. ^The name of the module is given by the ** second parameter. ^The third parameter is a pointer to ** the implementation of the [virtual table module]. ^The fourth ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is being created or reinitialized. ** ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite ** no longer needs the pClientData pointer. ^The destructor will also ** be invoked if the call to sqlite3_create_module_v2() fails. ** ^The sqlite3_create_module() |
| ︙ | ︙ | |||
7775 7776 7777 7778 7779 7780 7781 | ** and write access. ^If the flags parameter is zero, the BLOB is opened for ** read-only access. ** ** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored ** in *ppBlob. Otherwise an [error code] is returned and, unless the error ** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided ** the API is not misused, it is always safe to call [sqlite3_blob_close()] | | | 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 | ** and write access. ^If the flags parameter is zero, the BLOB is opened for ** read-only access. ** ** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored ** in *ppBlob. Otherwise an [error code] is returned and, unless the error ** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided ** the API is not misused, it is always safe to call [sqlite3_blob_close()] ** on *ppBlob after this function returns. ** ** This function fails with SQLITE_ERROR if any of the following are true: ** <ul> ** <li> ^(Database zDb does not exist)^, ** <li> ^(Table zTable does not exist within database zDb)^, ** <li> ^(Table zTable is a WITHOUT ROWID table)^, ** <li> ^(Column zColumn does not exist)^, |
| ︙ | ︙ | |||
7895 7896 7897 7898 7899 7900 7901 | /* ** CAPI3REF: Return The Size Of An Open BLOB ** METHOD: sqlite3_blob ** ** ^Returns the size in bytes of the BLOB accessible via the ** successfully opened [BLOB handle] in its only argument. ^The | | | 8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 | /* ** CAPI3REF: Return The Size Of An Open BLOB ** METHOD: sqlite3_blob ** ** ^Returns the size in bytes of the BLOB accessible via the ** successfully opened [BLOB handle] in its only argument. ^The ** incremental blob I/O routines can only read or overwrite existing ** blob content; they cannot change the size of a blob. ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. */ |
| ︙ | ︙ | |||
8045 8046 8047 8048 8049 8050 8051 | ** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function ** before calling sqlite3_initialize() or any other public sqlite3_ ** function that calls sqlite3_initialize(). ** ** ^The sqlite3_mutex_alloc() routine allocates a new ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc() ** routine returns NULL if it is unable to allocate the requested | | | 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 | ** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function ** before calling sqlite3_initialize() or any other public sqlite3_ ** function that calls sqlite3_initialize(). ** ** ^The sqlite3_mutex_alloc() routine allocates a new ** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc() ** routine returns NULL if it is unable to allocate the requested ** mutex. The argument to sqlite3_mutex_alloc() must be one of these ** integer constants: ** ** <ul> ** <li> SQLITE_MUTEX_FAST ** <li> SQLITE_MUTEX_RECURSIVE ** <li> SQLITE_MUTEX_STATIC_MAIN ** <li> SQLITE_MUTEX_STATIC_MEM |
| ︙ | ︙ | |||
8278 8279 8280 8281 8282 8283 8284 | #define SQLITE_MUTEX_STATIC_MASTER 2 /* ** CAPI3REF: Retrieve the mutex for a database connection ** METHOD: sqlite3 ** | | | 8441 8442 8443 8444 8445 8446 8447 8448 8449 8450 8451 8452 8453 8454 8455 | #define SQLITE_MUTEX_STATIC_MASTER 2 /* ** CAPI3REF: Retrieve the mutex for a database connection ** METHOD: sqlite3 ** ** ^This interface returns a pointer to the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument ** when the [threading mode] is Serialized. ** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); |
| ︙ | ︙ | |||
8401 8402 8403 8404 8405 8406 8407 | #define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking ** ** These routines provide access to the set of SQL language keywords | | | 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 | #define SQLITE_TESTCTRL_USELONGDOUBLE 34 /* NOT USED */ #define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking ** ** These routines provide access to the set of SQL language keywords ** recognized by SQLite. Applications can use 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 |
| ︙ | ︙ | |||
8569 8570 8571 8572 8573 8574 8575 | ** ^The length returned by [sqlite3_str_length(X)] does not include the ** zero-termination byte. ** ** ^The [sqlite3_str_value(X)] method returns a pointer to the current ** content of the dynamic string under construction in X. The value ** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X ** and might be freed or altered by any subsequent method on the same | | | 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 | ** ^The length returned by [sqlite3_str_length(X)] does not include the ** zero-termination byte. ** ** ^The [sqlite3_str_value(X)] method returns a pointer to the current ** content of the dynamic string under construction in X. The value ** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X ** and might be freed or altered by any subsequent method on the same ** [sqlite3_str] object. Applications must not use the pointer returned by ** [sqlite3_str_value(X)] after any subsequent method call on the same ** object. ^Applications may change the content of the string returned ** by [sqlite3_str_value(X)] as long as they do not write into any bytes ** outside the range of 0 to [sqlite3_str_length(X)] and do not read or ** write any byte after any subsequent sqlite3_str method call. */ SQLITE_API int sqlite3_str_errcode(sqlite3_str*); |
| ︙ | ︙ | |||
8655 8656 8657 8658 8659 8660 8661 | ** ** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] ** buffer and where forced to overflow to [sqlite3_malloc()]. The ** returned value includes allocations that overflowed because they | | | 8818 8819 8820 8821 8822 8823 8824 8825 8826 8827 8828 8829 8830 8831 8832 | ** ** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] ** buffer and where forced to overflow to [sqlite3_malloc()]. The ** returned value includes allocations that overflowed because they ** were too large (they were larger than the "sz" parameter to ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because ** no space was left in the page cache.</dd>)^ ** ** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> ** <dd>This parameter records the largest memory allocation request ** handed to the [pagecache memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. |
| ︙ | ︙ | |||
8739 8740 8741 8742 8743 8744 8745 | ** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt> ** <dd>This parameter returns the number of lookaside memory slots currently ** checked out.</dd>)^ ** ** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt> ** <dd>This parameter returns the number of malloc attempts that were ** satisfied using lookaside memory. Only the high-water value is meaningful; | | | | | | > | | > | 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 | ** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt> ** <dd>This parameter returns the number of lookaside memory slots currently ** checked out.</dd>)^ ** ** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt> ** <dd>This parameter returns the number of malloc attempts that were ** satisfied using lookaside memory. Only the high-water value is meaningful; ** the current value is always zero.</dd>)^ ** ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] ** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt> ** <dd>This parameter returns the number of malloc attempts that might have ** been satisfied using lookaside memory but failed due to the amount of ** memory requested being larger than the lookaside slot size. ** Only the high-water value is meaningful; ** the current value is always zero.</dd>)^ ** ** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] ** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt> ** <dd>This parameter returns the number of malloc attempts that might have ** been satisfied using lookaside memory but failed due to all lookaside ** memory already being in use. ** Only the high-water value is meaningful; ** the current value is always zero.</dd>)^ ** ** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt> ** <dd>This parameter returns the approximate number of bytes of heap ** memory used by all pager caches associated with the database connection.)^ ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. ** </dd> ** ** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]] ** ^(<dt>SQLITE_DBSTATUS_CACHE_USED_SHARED</dt> ** <dd>This parameter is similar to DBSTATUS_CACHE_USED, except that if a ** pager cache is shared between two or more connections the bytes of heap ** memory used by that pager cache is divided evenly between the attached ** connections.)^ In other words, if none of the pager caches associated ** with the database connection are shared, this request returns the same ** value as DBSTATUS_CACHE_USED. Or, if one or more of the pager caches are ** shared, the value returned by this call will be smaller than that returned ** by DBSTATUS_CACHE_USED. ^The highwater mark associated with ** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0.</dd> ** ** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt> ** <dd>This parameter returns the approximate number of bytes of heap ** memory used to store the schema for all databases associated ** with the connection - main, temp, and any [ATTACH]-ed databases.)^ ** ^The full amount of memory used by the schemas is reported, even if the ** schema memory is shared with other database connections due to ** [shared cache mode] being enabled. ** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. ** </dd> ** ** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt> ** <dd>This parameter returns the approximate number of bytes of heap ** and lookaside memory used by all prepared statements associated with ** the database connection.)^ ** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0. ** </dd> |
| ︙ | ︙ | |||
8818 8819 8820 8821 8822 8823 8824 | ** </dd> ** ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt> ** <dd>This parameter returns the number of dirty cache entries that have ** been written to disk in the middle of a transaction due to the page ** cache overflowing. Transactions are more efficient if they are written ** to disk all at once. When pages spill mid-transaction, that introduces | | | 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 | ** </dd> ** ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt> ** <dd>This parameter returns the number of dirty cache entries that have ** been written to disk in the middle of a transaction due to the page ** cache overflowing. Transactions are more efficient if they are written ** to disk all at once. When pages spill mid-transaction, that introduces ** additional overhead. This parameter can be used to help identify ** inefficiencies that can be resolved by increasing the cache size. ** </dd> ** ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt> ** <dd>This parameter returns zero for the current value if and only if ** all foreign key constraints (deferred or immediate) have been ** resolved.)^ ^The highwater mark is always 0. |
| ︙ | ︙ | |||
8889 8890 8891 8892 8893 8894 8895 | ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.</dd> ** ** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt> ** <dd>^This is the number of sort operations that have occurred. ** A non-zero value in this counter may indicate an opportunity to | | | | | | | | 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 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 | ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.</dd> ** ** [[SQLITE_STMTSTATUS_SORT]] <dt>SQLITE_STMTSTATUS_SORT</dt> ** <dd>^This is the number of sort operations that have occurred. ** A non-zero value in this counter may indicate an opportunity to ** improve performance through careful use of indices.</dd> ** ** [[SQLITE_STMTSTATUS_AUTOINDEX]] <dt>SQLITE_STMTSTATUS_AUTOINDEX</dt> ** <dd>^This is the number of rows inserted into transient indices that ** were created automatically in order to help joins run faster. ** A non-zero value in this counter may indicate an opportunity to ** improve performance by adding permanent indices that do not ** need to be reinitialized each time the statement is run.</dd> ** ** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt> ** <dd>^This is the number of virtual machine operations executed ** by the prepared statement if that number is less than or equal ** to 2147483647. The number of virtual machine operations can be ** used as a proxy for the total work done by the prepared statement. ** If the number of virtual machine operations exceeds 2147483647 ** then the value returned by this statement status code is undefined.</dd> ** ** [[SQLITE_STMTSTATUS_REPREPARE]] <dt>SQLITE_STMTSTATUS_REPREPARE</dt> ** <dd>^This is the number of times that the prepare statement has been ** automatically regenerated due to schema changes or changes to ** [bound parameters] that might affect the query plan.</dd> ** ** [[SQLITE_STMTSTATUS_RUN]] <dt>SQLITE_STMTSTATUS_RUN</dt> ** <dd>^This is the number of times that the prepared statement has ** been run. A single "run" for the purposes of this counter is one ** or more calls to [sqlite3_step()] followed by a call to [sqlite3_reset()]. ** The counter is incremented on the first [sqlite3_step()] call of each ** cycle.</dd> ** ** [[SQLITE_STMTSTATUS_FILTER_MISS]] ** [[SQLITE_STMTSTATUS_FILTER HIT]] ** <dt>SQLITE_STMTSTATUS_FILTER_HIT<br> ** SQLITE_STMTSTATUS_FILTER_MISS</dt> ** <dd>^SQLITE_STMTSTATUS_FILTER_HIT is the number of times that a join ** step was bypassed because a Bloom filter returned not-found. The ** corresponding SQLITE_STMTSTATUS_FILTER_MISS value is the number of ** times that the Bloom filter returned a find, and thus the join step ** had to be processed as normal.</dd> ** ** [[SQLITE_STMTSTATUS_MEMUSED]] <dt>SQLITE_STMTSTATUS_MEMUSED</dt> ** <dd>^This is the approximate number of bytes of heap memory ** used to store the prepared statement. ^This value is not actually ** a counter, and so the resetFlg parameter to sqlite3_stmt_status() ** is ignored when the opcode is SQLITE_STMTSTATUS_MEMUSED. ** </dd> |
| ︙ | ︙ | |||
9031 9032 9033 9034 9035 9036 9037 | ** call to xShutdown(). ** ** [[the xCreate() page cache methods]] ** ^SQLite invokes the xCreate() method to construct a new cache instance. ** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must | | | | | | | 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 | ** call to xShutdown(). ** ** [[the xCreate() page cache methods]] ** ^SQLite invokes the xCreate() method to construct a new cache instance. ** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must ** be allocated by the cache. ^szPage will always be a power of two. ^The ** second parameter szExtra is a number of bytes of extra storage ** associated with each page cache entry. ^The szExtra parameter will be ** a number less than 250. SQLite will use the ** extra szExtra bytes on each page to store metadata about the underlying ** database page on disk. The value passed into szExtra depends ** on the SQLite version, the target platform, and how SQLite was compiled. ** ^The third argument to xCreate(), bPurgeable, is true if the cache being ** created will be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. The cache implementation ** does not have to do anything special based upon the value of bPurgeable; ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will ** never invoke xUnpin() except to deliberately delete a page. ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to ** false will always have the "discard" flag set to true. ** ^Hence, a cache created with bPurgeable set to false will ** never contain any unpinned pages. ** ** [[the xCachesize() page cache method]] ** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored) for the cache ** instance passed as the first argument. This is the value configured using ** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable ** parameter, the implementation is not required to do anything with this ** value; it is advisory only. ** ** [[the xPagecount() page cache methods]] ** The xPagecount() method must return the number of pages currently |
| ︙ | ︙ | |||
9078 9079 9080 9081 9082 9083 9084 | ** is 1. After it has been retrieved using xFetch, the page is considered ** to be "pinned". ** ** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. If the requested page is not already in the cache, then the ** cache implementation should use the value of the createFlag | | | | | | 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 | ** is 1. After it has been retrieved using xFetch, the page is considered ** to be "pinned". ** ** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. If the requested page is not already in the cache, then the ** cache implementation should use the value of the createFlag ** parameter to help it determine what action to take: ** ** <table border=1 width=85% align=center> ** <tr><th> createFlag <th> Behavior when page is not already in cache ** <tr><td> 0 <td> Do not allocate a new page. Return NULL. ** <tr><td> 1 <td> Allocate a new page if it is easy and convenient to do so. ** Otherwise return NULL. ** <tr><td> 2 <td> Make every effort to allocate a new page. Only return ** NULL if allocating a new page is effectively impossible. ** </table> ** ** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite ** will only use a createFlag of 2 after a prior call with a createFlag of 1 ** failed.)^ In between the xFetch() calls, SQLite may ** attempt to unpin one or more cache pages by spilling the content of ** pinned pages to disk and synching the operating system disk cache. ** ** [[the xUnpin() page cache method]] ** ^xUnpin() is called by SQLite with a pointer to a currently pinned page ** as its second argument. If the third parameter, discard, is non-zero, ** then the page must be evicted from the cache. ** ^If the discard parameter is ** zero, then the page may be discarded or retained at the discretion of the ** page cache implementation. ^The page cache implementation ** may choose to evict unpinned pages at any time. ** ** The cache must not perform any reference counting. A single ** call to xUnpin() unpins the page regardless of the number of prior calls ** to xFetch(). ** ** [[the xRekey() page cache methods]] ** The xRekey() method is used to change the key value associated with the ** page passed as the second argument. If the cache ** previously contains an entry associated with newKey, it must be ** discarded. ^Any prior cache entry associated with newKey is guaranteed not ** to be pinned. ** ** When SQLite calls the xTruncate() method, the cache must discard all ** existing cache entries with page numbers (keys) greater than or equal ** to the value of the iLimit parameter passed to xTruncate(). If any ** of these pages are pinned, they become implicitly unpinned, meaning that ** they can be safely discarded. ** ** [[the xDestroy() page cache method]] ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] ** handle invalid, and will not use it with any other sqlite3_pcache_methods2 |
| ︙ | ︙ | |||
9298 9299 9300 9301 9302 9303 9304 | ** lasts for the duration of the sqlite3_backup_step() call. ** ^Because the source database is not locked between calls to ** sqlite3_backup_step(), the source database may be modified mid-way ** through the backup process. ^If the source database is modified by an ** external process or via a database connection other than the one being ** used by the backup operation, then the backup will be automatically ** restarted by the next call to sqlite3_backup_step(). ^If the source | | | | 9463 9464 9465 9466 9467 9468 9469 9470 9471 9472 9473 9474 9475 9476 9477 9478 9479 9480 9481 9482 9483 9484 9485 9486 9487 9488 9489 9490 9491 9492 9493 9494 | ** lasts for the duration of the sqlite3_backup_step() call. ** ^Because the source database is not locked between calls to ** sqlite3_backup_step(), the source database may be modified mid-way ** through the backup process. ^If the source database is modified by an ** external process or via a database connection other than the one being ** used by the backup operation, then the backup will be automatically ** restarted by the next call to sqlite3_backup_step(). ^If the source ** database is modified by using the same database connection as is used ** by the backup operation, then the backup database is automatically ** updated at the same time. ** ** [[sqlite3_backup_finish()]] <b>sqlite3_backup_finish()</b> ** ** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the ** application wishes to abandon the backup operation, the application ** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). ** ^The sqlite3_backup_finish() interfaces releases all ** resources associated with the [sqlite3_backup] object. ** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any ** active write-transaction on the destination database is rolled back. ** The [sqlite3_backup] object is invalid ** and may not be used following a call to sqlite3_backup_finish(). ** ** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no ** sqlite3_backup_step() errors occurred, regardless of whether or not ** sqlite3_backup_step() completed. ** ^If an out-of-memory condition or IO error occurred during any prior ** sqlite3_backup_step() call on the same [sqlite3_backup] object, then ** sqlite3_backup_finish() returns the corresponding [error code]. ** ** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() ** is not a permanent error and does not affect the return value of |
| ︙ | ︙ | |||
9417 9418 9419 9420 9421 9422 9423 | ** ^When a connection (known as the blocked connection) fails to obtain a ** shared-cache lock and SQLITE_LOCKED is returned to the caller, the ** identity of the database connection (the blocking connection) that ** has locked the required resource is stored internally. ^After an ** application receives an SQLITE_LOCKED error, it may call the ** sqlite3_unlock_notify() method with the blocked connection handle as ** the first argument to register for a callback that will be invoked | | | | 9582 9583 9584 9585 9586 9587 9588 9589 9590 9591 9592 9593 9594 9595 9596 9597 9598 9599 9600 9601 9602 9603 9604 9605 9606 9607 9608 9609 9610 9611 9612 9613 9614 9615 9616 | ** ^When a connection (known as the blocked connection) fails to obtain a ** shared-cache lock and SQLITE_LOCKED is returned to the caller, the ** identity of the database connection (the blocking connection) that ** has locked the required resource is stored internally. ^After an ** application receives an SQLITE_LOCKED error, it may call the ** sqlite3_unlock_notify() method with the blocked connection handle as ** the first argument to register for a callback that will be invoked ** when the blocking connection's current transaction is concluded. ^The ** callback is invoked from within the [sqlite3_step] or [sqlite3_close] ** call that concludes the blocking connection's transaction. ** ** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, ** there is a chance that the blocking connection will have already ** concluded its transaction by the time sqlite3_unlock_notify() is invoked. ** If this happens, then the specified callback is invoked immediately, ** from within the call to sqlite3_unlock_notify().)^ ** ** ^If the blocked connection is attempting to obtain a write-lock on a ** shared-cache table, and more than one other connection currently holds ** a read-lock on the same table, then SQLite arbitrarily selects one of ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing ** unlock-notify callback is canceled. ^The blocked connection's ** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes ** any sqlite3_xxx API functions from within an unlock-notify callback, a ** crash or deadlock may be the result. ** |
| ︙ | ︙ | |||
9835 9836 9837 9838 9839 9840 9841 | ** <dd>Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, ** where X is an integer. If X is zero, then the [virtual table] whose ** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not ** support constraints. In this configuration (which is the default) if ** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire ** statement is rolled back as if [ON CONFLICT | OR ABORT] had been | | | 10000 10001 10002 10003 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 | ** <dd>Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, ** where X is an integer. If X is zero, then the [virtual table] whose ** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not ** support constraints. In this configuration (which is the default) if ** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire ** statement is rolled back as if [ON CONFLICT | OR ABORT] had been ** specified as part of the user's SQL statement, regardless of the actual ** ON CONFLICT mode specified. ** ** If X is non-zero, then the virtual table implementation guarantees ** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before ** any modifications to internal or persistent data structures have been made. ** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite ** is able to roll back a statement or database transaction, and abandon |
| ︙ | ︙ | |||
9869 9870 9871 9872 9873 9874 9875 | ** prohibits that virtual table from being used from within triggers and ** views. ** </dd> ** ** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt> ** <dd>Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the | | | 10034 10035 10036 10037 10038 10039 10040 10041 10042 10043 10044 10045 10046 10047 10048 | ** prohibits that virtual table from being used from within triggers and ** views. ** </dd> ** ** [[SQLITE_VTAB_INNOCUOUS]]<dt>SQLITE_VTAB_INNOCUOUS</dt> ** <dd>Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the ** [xConnect] or [xCreate] methods of a [virtual table] implementation ** identify that virtual table as being safe to use from within triggers ** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the ** virtual table can do no serious harm even if it is controlled by a ** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS ** flag unless absolutely necessary. ** </dd> ** |
| ︙ | ︙ | |||
10037 10038 10039 10040 10041 10042 10043 | ** <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 | | | | 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 | ** <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 the 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 ** specified above, then it must not set the "orderByConsumed" flag in the ** [sqlite3_index_info] object or an incorrect answer may result. ** ** ^A virtual table implementation is always free to return rows in any order ** it wants, as long as the "orderByConsumed" flag is not set. ^When the ** "orderByConsumed" flag is unset, the query planner will add extra ** [bytecode] to ensure that the final results returned by the SQL query are ** ordered correctly. The use of the "orderByConsumed" flag and the ** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful ** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed" ** flag might help queries against a virtual table to run faster. Being ** overly aggressive and setting the "orderByConsumed" flag when it is not ** valid to do so, on the other hand, might cause SQLite to return incorrect |
| ︙ | ︙ | |||
10144 10145 10146 10147 10148 10149 10150 | ** The result of invoking these interfaces from any other context ** is undefined and probably harmful. ** ** The X parameter in a call to sqlite3_vtab_in_first(X,P) or ** sqlite3_vtab_in_next(X,P) should be one of the parameters to the ** xFilter method which invokes these routines, and specifically ** a parameter that was previously selected for all-at-once IN constraint | | | 10309 10310 10311 10312 10313 10314 10315 10316 10317 10318 10319 10320 10321 10322 10323 | ** The result of invoking these interfaces from any other context ** is undefined and probably harmful. ** ** The X parameter in a call to sqlite3_vtab_in_first(X,P) or ** sqlite3_vtab_in_next(X,P) should be one of the parameters to the ** xFilter method which invokes these routines, and specifically ** a parameter that was previously selected for all-at-once IN constraint ** processing using the [sqlite3_vtab_in()] interface in the ** [xBestIndex|xBestIndex method]. ^(If the X parameter is not ** an xFilter argument that was selected for all-at-once IN constraint ** processing, then these routines return [SQLITE_ERROR].)^ ** ** ^(Use these routines to access all values on the right-hand side ** of the IN constraint using code like the following: ** |
| ︙ | ︙ | |||
10199 10200 10201 10202 10203 10204 10205 | ** attempts to set *V to the value of the right-hand operand of ** that constraint if the right-hand operand is known. ^If the ** right-hand operand is not known, then *V is set to a NULL pointer. ** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if ** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V) ** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th ** constraint is not available. ^The sqlite3_vtab_rhs_value() interface | | | 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375 10376 10377 10378 | ** attempts to set *V to the value of the right-hand operand of ** that constraint if the right-hand operand is known. ^If the ** right-hand operand is not known, then *V is set to a NULL pointer. ** ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if ** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V) ** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th ** constraint is not available. ^The sqlite3_vtab_rhs_value() interface ** can return a result code other than SQLITE_OK or SQLITE_NOTFOUND if ** something goes wrong. ** ** The sqlite3_vtab_rhs_value() interface is usually only successful if ** the right-hand operand of a constraint is a literal value in the original ** SQL statement. If the right-hand operand is an expression or a reference ** to some other column or a [host parameter], then sqlite3_vtab_rhs_value() ** will probably return [SQLITE_NOTFOUND]. |
| ︙ | ︙ | |||
10227 10228 10229 10230 10231 10232 10233 |
SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
/*
** CAPI3REF: Conflict resolution modes
** KEYWORDS: {conflict resolution mode}
**
** These constants are returned by [sqlite3_vtab_on_conflict()] to
| | | | 10392 10393 10394 10395 10396 10397 10398 10399 10400 10401 10402 10403 10404 10405 10406 10407 |
SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
/*
** CAPI3REF: Conflict resolution modes
** KEYWORDS: {conflict resolution mode}
**
** These constants are returned by [sqlite3_vtab_on_conflict()] to
** inform a [virtual table] implementation of the [ON CONFLICT] mode
** for the SQL statement being evaluated.
**
** Note that the [SQLITE_IGNORE] constant is also used as a potential
** return value from the [sqlite3_set_authorizer()] callback and that
** [SQLITE_ABORT] is also a [result code].
*/
#define SQLITE_ROLLBACK 1
/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */
|
| ︙ | ︙ | |||
10268 10269 10270 10271 10272 10273 10274 | ** [[SQLITE_SCANSTAT_NVISIT]] <dt>SQLITE_SCANSTAT_NVISIT</dt> ** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be set ** to the total number of rows examined by all iterations of the X-th loop.</dd> ** ** [[SQLITE_SCANSTAT_EST]] <dt>SQLITE_SCANSTAT_EST</dt> ** <dd>^The "double" variable pointed to by the V parameter will be set to the ** query planner's estimate for the average number of rows output from each | | | | | | | | | | 10433 10434 10435 10436 10437 10438 10439 10440 10441 10442 10443 10444 10445 10446 10447 10448 10449 10450 10451 10452 10453 10454 10455 10456 10457 10458 10459 10460 10461 10462 10463 10464 10465 10466 10467 10468 10469 10470 10471 10472 10473 10474 10475 10476 10477 10478 10479 | ** [[SQLITE_SCANSTAT_NVISIT]] <dt>SQLITE_SCANSTAT_NVISIT</dt> ** <dd>^The [sqlite3_int64] variable pointed to by the V parameter will be set ** to the total number of rows examined by all iterations of the X-th loop.</dd> ** ** [[SQLITE_SCANSTAT_EST]] <dt>SQLITE_SCANSTAT_EST</dt> ** <dd>^The "double" variable pointed to by the V parameter will be set to the ** query planner's estimate for the average number of rows output from each ** iteration of the X-th loop. If the query planner's estimate was accurate, ** then this value will approximate the quotient NVISIT/NLOOP and the ** product of this value for all prior loops with the same SELECTID will ** be the NLOOP value for the current loop.</dd> ** ** [[SQLITE_SCANSTAT_NAME]] <dt>SQLITE_SCANSTAT_NAME</dt> ** <dd>^The "const char *" variable pointed to by the V parameter will be set ** to a zero-terminated UTF-8 string containing the name of the index or table ** used for the X-th loop.</dd> ** ** [[SQLITE_SCANSTAT_EXPLAIN]] <dt>SQLITE_SCANSTAT_EXPLAIN</dt> ** <dd>^The "const char *" variable pointed to by the V parameter will be set ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] ** description for the X-th loop.</dd> ** ** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECTID</dt> ** <dd>^The "int" variable pointed to by the V parameter will be set to the ** id for the X-th query plan element. The id value is unique within the ** statement. The select-id is the same value as is output in the first ** column of an [EXPLAIN QUERY PLAN] query.</dd> ** ** [[SQLITE_SCANSTAT_PARENTID]] <dt>SQLITE_SCANSTAT_PARENTID</dt> ** <dd>The "int" variable pointed to by the V parameter will be set to the ** id of the parent of the current query element, if applicable, or ** to zero if the query element has no parent. This is the same value as ** returned in the second column of an [EXPLAIN QUERY PLAN] query.</dd> ** ** [[SQLITE_SCANSTAT_NCYCLE]] <dt>SQLITE_SCANSTAT_NCYCLE</dt> ** <dd>The sqlite3_int64 output value is set to the number of cycles, ** according to the processor time-stamp counter, that elapsed while the ** query element was being processed. This value is not available for ** all query elements - if it is unavailable the output variable is ** set to -1.</dd> ** </dl> */ #define SQLITE_SCANSTAT_NLOOP 0 #define SQLITE_SCANSTAT_NVISIT 1 #define SQLITE_SCANSTAT_EST 2 #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 |
| ︙ | ︙ | |||
10341 10342 10343 10344 10345 10346 10347 | ** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements ** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of ** the EXPLAIN QUERY PLAN output) are available. Invoking API ** sqlite3_stmt_scanstatus() is equivalent to calling ** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. ** ** Parameter "idx" identifies the specific query element to retrieve statistics | | | | 10506 10507 10508 10509 10510 10511 10512 10513 10514 10515 10516 10517 10518 10519 10520 10521 | ** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements ** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of ** the EXPLAIN QUERY PLAN output) are available. Invoking API ** sqlite3_stmt_scanstatus() is equivalent to calling ** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. ** ** Parameter "idx" identifies the specific query element to retrieve statistics ** for. Query elements are numbered starting from zero. A value of -1 may ** retrieve statistics for the entire query. ^If idx is out of range ** - less than -1 or greater than or equal to the total number of query ** elements used to implement the statement - a non-zero value is returned and ** the variable that pOut points to is unchanged. ** ** See also: [sqlite3_stmt_scanstatus_reset()] */ SQLITE_API int sqlite3_stmt_scanstatus( |
| ︙ | ︙ | |||
10385 10386 10387 10388 10389 10390 10391 | SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); /* ** CAPI3REF: Flush caches to disk mid-transaction ** METHOD: sqlite3 ** ** ^If a write-transaction is open on [database connection] D when the | | | 10550 10551 10552 10553 10554 10555 10556 10557 10558 10559 10560 10561 10562 10563 10564 | SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*); /* ** CAPI3REF: Flush caches to disk mid-transaction ** METHOD: sqlite3 ** ** ^If a write-transaction is open on [database connection] D when the ** [sqlite3_db_cacheflush(D)] interface is invoked, any dirty ** pages in the pager-cache that are not currently in use are written out ** to disk. A dirty page may be in use if a database cursor created by an ** active SQL statement is reading from it, or if it is page 1 of a database ** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)] ** interface flushes caches for all schemas - "main", "temp", and ** any [attached] databases. ** |
| ︙ | ︙ | |||
10499 10500 10501 10502 10503 10504 10505 | ** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate ** callback was invoked as a result of a direct insert, update, or delete ** operation; or 1 for inserts, updates, or deletes invoked by top-level ** triggers; or 2 for changes resulting from triggers called by top-level ** triggers; and so forth. ** ** When the [sqlite3_blob_write()] API is used to update a blob column, | | | | 10664 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675 10676 10677 10678 10679 | ** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate ** callback was invoked as a result of a direct insert, update, or delete ** operation; or 1 for inserts, updates, or deletes invoked by top-level ** triggers; or 2 for changes resulting from triggers called by top-level ** triggers; and so forth. ** ** When the [sqlite3_blob_write()] API is used to update a blob column, ** the pre-update hook is invoked with SQLITE_DELETE, because ** the new values are not yet available. In this case, when a ** callback made with op==SQLITE_DELETE is actually a write using the ** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns ** the index of the column being written. In other cases, where the ** pre-update hook is being invoked for some other reason, including a ** regular DELETE, sqlite3_preupdate_blobwrite() returns -1. ** ** See also: [sqlite3_update_hook()] |
| ︙ | ︙ | |||
10744 10745 10746 10747 10748 10749 10750 | ** [SQLITE_ENABLE_SNAPSHOT] option. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Serialize a database ** | | | > | | | 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 | ** [SQLITE_ENABLE_SNAPSHOT] option. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Serialize a database ** ** The sqlite3_serialize(D,S,P,F) interface returns a pointer to ** memory that is a serialization of the S database on ** [database connection] D. If S is a NULL pointer, the main database is used. ** If P is not a NULL pointer, then the size of the database in bytes ** is written into *P. ** ** For an ordinary on-disk database file, the serialization is just a ** copy of the disk file. For an in-memory database or a "TEMP" database, ** the serialization is the same sequence of bytes which would be written ** to disk if that database were backed up to disk. ** ** The usual case is that sqlite3_serialize() copies the serialization of ** the database into memory obtained from [sqlite3_malloc64()] and returns ** a pointer to that memory. The caller is responsible for freeing the ** returned value to avoid a memory leak. However, if the F argument ** contains the SQLITE_SERIALIZE_NOCOPY bit, then no memory allocations ** are made, and the sqlite3_serialize() function will return a pointer ** to the contiguous memory representation of the database that SQLite ** is currently using for that database, or NULL if no such contiguous ** memory representation of the database exists. A contiguous memory ** representation of the database will usually only exist if there has ** been a prior call to [sqlite3_deserialize(D,S,...)] with the same ** values of D and S. ** The size of the database is written into *P even if the ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy ** of the database exists. |
| ︙ | ︙ | |||
10832 10833 10834 10835 10836 10837 10838 | ** Applications must not modify the buffer P or invalidate it before ** the database connection D is closed. ** ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the ** database is currently in a read transaction or is involved in a backup ** operation. ** | | | 10998 10999 11000 11001 11002 11003 11004 11005 11006 11007 11008 11009 11010 11011 11012 | ** Applications must not modify the buffer P or invalidate it before ** the database connection D is closed. ** ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the ** database is currently in a read transaction or is involved in a backup ** operation. ** ** It is not possible to deserialize into the TEMP database. If the ** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the ** function returns SQLITE_ERROR. ** ** The deserialized database should not be in [WAL mode]. If the database ** is in WAL mode, then any attempt to use the database file will result ** in an [SQLITE_CANTOPEN] error. The application can set the ** [file format version numbers] (bytes 18 and 19) of the input database P |
| ︙ | ︙ | |||
10854 10855 10856 10857 10858 10859 10860 | ** This interface is omitted if SQLite is compiled with the ** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API int sqlite3_deserialize( sqlite3 *db, /* The database connection */ const char *zSchema, /* Which DB to reopen with the deserialization */ unsigned char *pData, /* The serialized database content */ | | | | 11020 11021 11022 11023 11024 11025 11026 11027 11028 11029 11030 11031 11032 11033 11034 11035 11036 11037 11038 11039 11040 11041 11042 | ** This interface is omitted if SQLite is compiled with the ** [SQLITE_OMIT_DESERIALIZE] option. */ SQLITE_API int sqlite3_deserialize( sqlite3 *db, /* The database connection */ const char *zSchema, /* Which DB to reopen with the deserialization */ unsigned char *pData, /* The serialized database content */ sqlite3_int64 szDb, /* Number of bytes in the deserialization */ sqlite3_int64 szBuf, /* Total size of buffer pData[] */ unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */ ); /* ** CAPI3REF: Flags for sqlite3_deserialize() ** ** The following are allowed values for the 6th argument (the F argument) to ** the [sqlite3_deserialize(D,S,P,N,M,F)] interface. ** ** The SQLITE_DESERIALIZE_FREEONCLOSE means that the database serialization ** in the P argument is held in memory obtained from [sqlite3_malloc64()] ** and that SQLite should take ownership of this memory and automatically ** free it when it has finished using it. Without this flag, the caller ** is responsible for freeing any dynamically allocated memory. |
| ︙ | ︙ | |||
11387 11388 11389 11390 11391 11392 11393 | ** When a session object is disabled (see the [sqlite3session_enable()] API), ** it does not accumulate records when rows are inserted, updated or deleted. ** This may appear to have some counter-intuitive effects if a single row ** is written to more than once during a session. For example, if a row ** is inserted while a session object is enabled, then later deleted while ** the same session object is disabled, no INSERT record will appear in the ** changeset, even though the delete took place while the session was disabled. | | | | > | 11553 11554 11555 11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 | ** When a session object is disabled (see the [sqlite3session_enable()] API), ** it does not accumulate records when rows are inserted, updated or deleted. ** This may appear to have some counter-intuitive effects if a single row ** is written to more than once during a session. For example, if a row ** is inserted while a session object is enabled, then later deleted while ** the same session object is disabled, no INSERT record will appear in the ** changeset, even though the delete took place while the session was disabled. ** Or, if one field of a row is updated while a session is enabled, and ** then another field of the same row is updated while the session is disabled, ** the resulting changeset will contain an UPDATE change that updates both ** fields. */ SQLITE_API int sqlite3session_changeset( sqlite3_session *pSession, /* Session object */ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ void **ppChangeset /* OUT: Buffer containing changeset */ ); |
| ︙ | ︙ | |||
11461 11462 11463 11464 11465 11466 11467 | ** </ul> ** ** To clarify, if this function is called and then a changeset constructed ** using [sqlite3session_changeset()], then after applying that changeset to ** database zFrom the contents of the two compatible tables would be ** identical. ** | > | | | 11628 11629 11630 11631 11632 11633 11634 11635 11636 11637 11638 11639 11640 11641 11642 11643 11644 | ** </ul> ** ** To clarify, if this function is called and then a changeset constructed ** using [sqlite3session_changeset()], then after applying that changeset to ** database zFrom the contents of the two compatible tables would be ** identical. ** ** Unless the call to this function is a no-op as described above, it is an ** error if database zFrom does not exist or does not contain the required ** compatible table. ** ** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite ** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg ** may be set to point to a buffer containing an English language error ** message. It is the responsibility of the caller to free this buffer using ** sqlite3_free(). */ |
| ︙ | ︙ | |||
11597 11598 11599 11600 11601 11602 11603 | /* ** CAPI3REF: Flags for sqlite3changeset_start_v2 ** ** The following flags may passed via the 4th parameter to ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: ** | | | 11765 11766 11767 11768 11769 11770 11771 11772 11773 11774 11775 11776 11777 11778 11779 | /* ** CAPI3REF: Flags for sqlite3changeset_start_v2 ** ** The following flags may passed via the 4th parameter to ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: ** ** <dt>SQLITE_CHANGESETSTART_INVERT <dd> ** Invert the changeset while iterating through it. This is equivalent to ** inverting a changeset using sqlite3changeset_invert() before applying it. ** It is an error to specify this flag with a patchset. */ #define SQLITE_CHANGESETSTART_INVERT 0x0002 |
| ︙ | ︙ | |||
11912 11913 11914 11915 11916 11917 11918 | void *pA, /* Pointer to buffer containing changeset A */ int nB, /* Number of bytes in buffer pB */ void *pB, /* Pointer to buffer containing changeset B */ int *pnOut, /* OUT: Number of bytes in output changeset */ void **ppOut /* OUT: Buffer containing output changeset */ ); | < < < < < < < < < < < < < | 12080 12081 12082 12083 12084 12085 12086 12087 12088 12089 12090 12091 12092 12093 | void *pA, /* Pointer to buffer containing changeset A */ int nB, /* Number of bytes in buffer pB */ void *pB, /* Pointer to buffer containing changeset B */ int *pnOut, /* OUT: Number of bytes in output changeset */ void **ppOut /* OUT: Buffer containing output changeset */ ); /* ** CAPI3REF: Changegroup Handle ** ** A changegroup is an object used to combine two or more ** [changesets] or [patchsets] */ typedef struct sqlite3_changegroup sqlite3_changegroup; |
| ︙ | ︙ | |||
12155 12156 12157 12158 12159 12160 12161 | ** CAPI3REF: Apply A Changeset To A Database ** ** Apply a changeset or patchset to a database. These functions attempt to ** update the "main" database attached to handle db with the changes found in ** the changeset passed via the second and third arguments. ** ** The fourth argument (xFilter) passed to these functions is the "filter | > > | | | | | | | > > > > > > > | | | | | | 12310 12311 12312 12313 12314 12315 12316 12317 12318 12319 12320 12321 12322 12323 12324 12325 12326 12327 12328 12329 12330 12331 12332 12333 12334 12335 12336 12337 12338 12339 12340 12341 12342 12343 12344 12345 12346 12347 12348 12349 12350 12351 12352 12353 12354 12355 12356 12357 12358 12359 12360 12361 12362 12363 12364 | ** CAPI3REF: Apply A Changeset To A Database ** ** Apply a changeset or patchset to a database. These functions attempt to ** update the "main" database attached to handle db with the changes found in ** the changeset passed via the second and third arguments. ** ** The fourth argument (xFilter) passed to these functions is the "filter ** callback". This may be passed NULL, in which case all changes in the ** changeset are applied to the database. For sqlite3changeset_apply() and ** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once ** for each table affected by at least one change in the changeset. In this ** case the table name is passed as the second argument, and a copy of ** the context pointer passed as the sixth argument to apply() or apply_v2() ** as the first. If the "filter callback" returns zero, then no attempt is ** made to apply any changes to the table. Otherwise, if the return value is ** non-zero, all changes related to the table are attempted. ** ** For sqlite3_changeset_apply_v3(), the xFilter callback is invoked once ** per change. The second argument in this case is an sqlite3_changeset_iter ** that may be queried using the usual APIs for the details of the current ** change. If the "filter callback" returns zero in this case, then no attempt ** is made to apply the current change. If it returns non-zero, the change ** is applied. ** ** For each table that is not excluded by the filter callback, this function ** tests that the target database contains a compatible table. A table is ** considered compatible if all of the following are true: ** ** <ul> ** <li> The table has the same name as the name recorded in the ** changeset, and ** <li> The table has at least as many columns as recorded in the ** changeset, and ** <li> The table has primary key columns in the same position as ** recorded in the changeset. ** </ul> ** ** If there is no compatible table, it is not an error, but none of the ** changes associated with the table are applied. A warning message is issued ** via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most ** one such warning is issued for each table in the changeset. ** ** For each change for which there is a compatible table, an attempt is made ** to modify the table contents according to each UPDATE, INSERT or DELETE ** change that is not excluded by a filter callback. If a change cannot be ** applied cleanly, the conflict handler function passed as the fifth argument ** to sqlite3changeset_apply() may be invoked. A description of exactly when ** the conflict handler is invoked for each type of change is below. ** ** Unlike the xFilter argument, xConflict may not be passed NULL. The results ** of passing anything other than a valid function pointer as the xConflict ** argument are undefined. ** ** Each time the conflict handler function is invoked, it must return one ** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or |
| ︙ | ︙ | |||
12328 12329 12330 12331 12332 12333 12334 12335 12336 12337 12338 12339 12340 12341 |
sqlite3 *db, /* Apply change to "main" db of this handle */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset, /* Changeset blob */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
| > > > > > > > > > > > > > > > > > | 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 |
sqlite3 *db, /* Apply change to "main" db of this handle */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset, /* Changeset blob */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
int flags /* SESSION_CHANGESETAPPLY_* flags */
);
SQLITE_API int sqlite3changeset_apply_v3(
sqlite3 *db, /* Apply change to "main" db of this handle */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset, /* Changeset blob */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
sqlite3_changeset_iter *p /* Handle describing change */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
| ︙ | ︙ | |||
12747 12748 12749 12750 12751 12752 12753 12754 12755 12756 12757 12758 12759 12760 |
sqlite3 *db, /* Apply change to "main" db of this handle */
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
void *pIn, /* First arg for xInput */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase,
| > > > > > > > > > > > > > > > > > | 12928 12929 12930 12931 12932 12933 12934 12935 12936 12937 12938 12939 12940 12941 12942 12943 12944 12945 12946 12947 12948 12949 12950 12951 12952 12953 12954 12955 12956 12957 12958 |
sqlite3 *db, /* Apply change to "main" db of this handle */
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
void *pIn, /* First arg for xInput */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase,
int flags
);
SQLITE_API int sqlite3changeset_apply_v3_strm(
sqlite3 *db, /* Apply change to "main" db of this handle */
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
void *pIn, /* First arg for xInput */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
sqlite3_changeset_iter *p
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase,
|
| ︙ | ︙ |
Changes to skins/blitz/footer.txt.
1 2 3 4 5 |
</div> <!-- end div container -->
</div> <!-- end div middle max-full-width -->
<footer>
<div class="container">
<div class="pull-right">
| | | 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://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/footer.txt.
1 2 3 |
<footer>
<div class="container">
<div class="pull-right">
| | | 1 2 3 4 5 6 7 8 |
<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/default/css.txt.
| ︙ | ︙ | |||
354 355 356 357 358 359 360 |
padding: 0 4px;
}
.content pre, table.numbered-lines > tbody > tr {
hyphens: none;
line-height: 1.25;
}
| | > | 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
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,
|
| ︙ | ︙ |
Changes to skins/default/header.txt.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 |
set logourl $baseurl
}
return $logourl
}
set logourl [getLogoUrl $baseurl]
</th1>
<a href="$logourl">
| | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
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">
|
| ︙ | ︙ |
Changes to skins/eagle/css.txt.
| ︙ | ︙ | |||
178 179 180 181 182 183 184 |
/* the format for the timeline data table */
table.timelineTable {
cellspacing: 0;
border: 0;
cellpadding: 0;
font-family: "courier new";
border-spacing: 0px 2px;
| | | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
/* the format for the timeline data table */
table.timelineTable {
cellspacing: 0;
border: 0;
cellpadding: 0;
font-family: "courier new";
border-spacing: 0px 2px;
/* border-collapse: collapse; */
}
.timelineSelected {
background-color: #7EA2D9;
}
.timelineSecondary {
background-color: #7EA27E;
|
| ︙ | ︙ |
Changes to skins/eagle/footer.txt.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
}
proc getVersion { version } {
set length [string length $version]
return [string range $version 1 [expr {$length - 2}]]
}
set version [getVersion $manifest_version]
set tclVersion [getTclVersion]
| | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
}
proc getVersion { version } {
set length [string length $version]
return [string range $version 1 [expr {$length - 2}]]
}
set version [getVersion $manifest_version]
set tclVersion [getTclVersion]
set fossilUrl https://fossil-scm.org
set fossilDate [string range $manifest_date 0 9]T[string range $manifest_date 11 end]
</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>
|
| ︙ | ︙ |
Changes to skins/eagle/header.txt.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 |
set logourl [getLogoUrl $baseurl]
} else {
# Link logo to the top of the current repo
set logourl $baseurl
}
</th1>
<a href="$logourl">
| | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
set logourl [getLogoUrl $baseurl]
} else {
# Link logo to the top of the current repo
set logourl $baseurl
}
</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 {
|
| ︙ | ︙ |
Changes to skins/original/footer.txt.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
}
proc getVersion { version } {
set length [string length $version]
return [string range $version 1 [expr {$length - 2}]]
}
set version [getVersion $manifest_version]
set tclVersion [getTclVersion]
| | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
}
proc getVersion { version } {
set length [string length $version]
return [string range $version 1 [expr {$length - 2}]]
}
set version [getVersion $manifest_version]
set tclVersion [getTclVersion]
set fossilUrl https://fossil-scm.org
set fossilDate [string range $manifest_date 0 9]T[string range $manifest_date 11 end]
</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>
|
| ︙ | ︙ |
Changes to skins/original/header.txt.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 |
set logourl $baseurl
}
return $logourl
}
set logourl [getLogoUrl $baseurl]
</th1>
<a href="$logourl">
| | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
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">$<title></div>
<div class="status"><nobr><th1>
if {[info exists login]} {
puts "Logged in as $login"
} else {
|
| ︙ | ︙ |
Changes to skins/xekri/css.txt.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
color: #eee;
font-family: Monospace;
font-size: 1em;
min-height: 100%;
}
body {
margin: 0;
padding: 0;
text-size-adjust: none;
}
a {
color: #40a0ff;
| > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
color: #eee;
font-family: Monospace;
font-size: 1em;
min-height: 100%;
}
body {
background-color: #333;
margin: 0;
padding: 0;
text-size-adjust: none;
}
a {
color: #40a0ff;
|
| ︙ | ︙ | |||
880 881 882 883 884 885 886 887 888 889 890 891 892 893 |
white-space: nowrap;
}
/* the format for the timeline version links */
a.timelineHistLink {
}
/**************************************
* User Edit
*/
/* layout definition for the capabilities box on the user edit detail page */
div.ueditCapBox {
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
white-space: nowrap;
}
/* the format for the timeline version links */
a.timelineHistLink {
}
/* Timeline graph style taken from Ardoise, with
** minor adjustments (2025-03-28) */
.tl-canvas {
margin: 0 6px 0 10px
}
.tl-rail {
width: 18px
}
.tl-mergeoffset {
width: 2px
}
.tl-nodemark {
margin-top: .8em
}
.tl-node {
width: 10px;
height: 10px;
border: 1px solid #bbb;
background: #111;
cursor: pointer
}
.tl-node.leaf:after {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: 4px;
height: 4px;
background: #bbb
}
.tl-node.closed-leaf svg {
position: absolute;
top: 0px;
left: 0px;
width: 10px;
height: 10px;
color: #bbb;
}
.tl-node.sel:after {
content: '';
position: absolute;
top: 1px;
left: 1px;
width: 8px;
height: 8px;
background: #ff8000
}
.tl-arrow {
width: 0;
height: 0;
transform: scale(.999);
border: 0 solid transparent
}
.tl-arrow.u {
margin-top: -1px;
border-width: 0 3px;
border-bottom: 7px solid
}
.tl-arrow.u.sm {
border-bottom: 5px solid #bbb
}
.tl-line {
background: #bbb;
width: 2px
}
.tl-arrow.merge {
height: 1px;
border-width: 2px 0
}
.tl-arrow.merge.l {
border-right: 3px solid #bbb
}
.tl-arrow.merge.r {
border-left: 3px solid #bbb
}
.tl-line.merge {
width: 1px
}
.tl-arrow.cherrypick {
height: 1px;
border-width: 2px 0;
}
.tl-arrow.cherrypick.l {
border-right: 3px solid #bbb;
}
.tl-arrow.cherrypick.r {
border-left: 3px solid #bbb;
}
.tl-line.cherrypick.h {
width: 0px;
border-top: 1px dashed #bbb;
border-left: 0px dashed #bbb;
background: rgba(255,255,255,0);
}
.tl-line.cherrypick.v {
width: 0px;
border-top: 0px dashed #bbb;
border-left: 1px dashed #bbb;
background: rgba(255,255,255,0);
}
/**************************************
* User Edit
*/
/* layout definition for the capabilities box on the user edit detail page */
div.ueditCapBox {
|
| ︙ | ︙ |
Changes to skins/xekri/header.txt.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 |
set logourl [getLogoUrl $baseurl]
} else {
# Link logo to the top of the current repo
set logourl $baseurl
}
</th1>
<a href="$logourl">
| | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
set logourl [getLogoUrl $baseurl]
} else {
# Link logo to the top of the current repo
set logourl $baseurl
}
</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"
|
| ︙ | ︙ |
Changes to src/add.c.
| ︙ | ︙ | |||
76 77 78 79 80 81 82 |
/* Cached setting "manifest" */
static int cachedManifest = -1;
static int numManifests;
if( cachedManifest == -1 ){
int i;
Blob repo;
| | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
/* Cached setting "manifest" */
static int cachedManifest = -1;
static int numManifests;
if( cachedManifest == -1 ){
int i;
Blob repo;
cachedManifest = db_get_manifest_setting(0);
numManifests = 0;
for(i=0; i<count(aManifestflags); i++){
if( cachedManifest&aManifestflags[i].flg ) {
azManifests[numManifests++] = aManifestflags[i].fname;
}
}
blob_zero(&repo);
|
| ︙ | ︙ | |||
894 895 896 897 898 899 900 | ** ** The original name of the file is zOrig. The new filename is zNew. */ static void mv_one_file( int vid, const char *zOrig, const char *zNew, | | > > > > > > > | 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 |
**
** The original name of the file is zOrig. The new filename is zNew.
*/
static void mv_one_file(
int vid,
const char *zOrig,
const char *zNew,
int dryRunFlag,
int moveFiles
){
int x = db_int(-1, "SELECT deleted FROM vfile WHERE pathname=%Q %s",
zNew, filename_collation());
if( x>=0 ){
if( x==0 ){
if( !filenames_are_case_sensitive() && fossil_stricmp(zOrig,zNew)==0 ){
/* Case change only */
}else{
fossil_fatal("cannot rename '%s' to '%s' since another file named '%s'"
" is currently under management", zOrig, zNew, zNew);
}
}else{
fossil_fatal("cannot rename '%s' to '%s' since the delete of '%s' has "
"not yet been committed", zOrig, zNew, zNew);
}
}
if( moveFiles ){
if( file_size(zNew, ExtFILE) != -1 ){
fossil_fatal("cannot rename '%s' to '%s' on disk since another file"
" named '%s' already exists", zOrig, zNew, zNew);
}
}
fossil_print("RENAME %s %s\n", zOrig, zNew);
if( !dryRunFlag ){
db_multi_exec(
"UPDATE vfile SET pathname='%q' WHERE pathname='%q' %s AND vid=%d",
zNew, zOrig, filename_collation(), vid
);
|
| ︙ | ︙ | |||
1133 1134 1135 1136 1137 1138 1139 |
db_finalize(&q);
}
}
db_prepare(&q, "SELECT f, t FROM mv ORDER BY f");
while( db_step(&q)==SQLITE_ROW ){
const char *zFrom = db_column_text(&q, 0);
const char *zTo = db_column_text(&q, 1);
| | | 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 |
db_finalize(&q);
}
}
db_prepare(&q, "SELECT f, t FROM mv ORDER BY f");
while( db_step(&q)==SQLITE_ROW ){
const char *zFrom = db_column_text(&q, 0);
const char *zTo = db_column_text(&q, 1);
mv_one_file(vid, zFrom, zTo, dryRunFlag, moveFiles);
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);
|
| ︙ | ︙ |
Changes to src/ajax.c.
| ︙ | ︙ | |||
305 306 307 308 309 310 311 |
int ln = atoi(PD("ln","0"));
int iframeHeight = atoi(PD("iframe_height","40"));
Blob content = empty_blob;
const char * zRenderMode = 0;
ajax_get_fnci_args( &zFilename, 0 );
| | | 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
int ln = atoi(PD("ln","0"));
int iframeHeight = atoi(PD("iframe_height","40"));
Blob content = empty_blob;
const char * zRenderMode = 0;
ajax_get_fnci_args( &zFilename, 0 );
if(!ajax_route_bootstrap(0,1)){
return;
}
if(zFilename==0){
/* The filename is only used for mimetype determination,
** so we can default it... */
zFilename = "foo.txt";
}
|
| ︙ | ︙ |
Changes to src/alerts.c.
| ︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 59 60 61 62 | @ -- a - Announcements @ -- c - Check-ins @ -- f - Forum posts @ -- k - ** Special: Unsubscribed using /oneclickunsub @ -- n - New forum threads @ -- r - Replies to my own forum posts @ -- t - Ticket changes @ -- w - Wiki changes @ -- x - Edits to forum posts @ -- Probably different codes will be added in the future. In the future @ -- we might also add a separate table that allows subscribing to email @ -- notifications for specific branches or tags or tickets. @ -- @ CREATE TABLE repository.subscriber( | > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | @ -- a - Announcements @ -- c - Check-ins @ -- f - Forum posts @ -- k - ** Special: Unsubscribed using /oneclickunsub @ -- n - New forum threads @ -- r - Replies to my own forum posts @ -- t - Ticket changes @ -- u - Changes of users' permissions (admins only) @ -- w - Wiki changes @ -- x - Edits to forum posts @ -- Probably different codes will be added in the future. In the future @ -- we might also add a separate table that allows subscribing to email @ -- notifications for specific branches or tags or tickets. @ -- @ CREATE TABLE repository.subscriber( |
| ︙ | ︙ | |||
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
if( g.perm.Admin ){
if( fossil_strcmp(g.zPath,"subscribers") ){
style_submenu_element("Subscribers","%R/subscribers");
}
if( fossil_strcmp(g.zPath,"subscribe") ){
style_submenu_element("Add New Subscriber","%R/subscribe");
}
}
}
/*
** WEBPAGE: setup_notification
**
** Administrative page for configuring and controlling email notification.
** Normally accessible via the /Admin/Notification menu.
*/
void setup_notification(void){
static const char *const azSendMethods[] = {
"off", "Disabled",
| > > > | | > | > < > | > | 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 |
if( g.perm.Admin ){
if( fossil_strcmp(g.zPath,"subscribers") ){
style_submenu_element("Subscribers","%R/subscribers");
}
if( fossil_strcmp(g.zPath,"subscribe") ){
style_submenu_element("Add New Subscriber","%R/subscribe");
}
if( fossil_strcmp(g.zPath,"setup_notification") ){
style_submenu_element("Notification Setup","%R/setup_notification");
}
}
}
/*
** WEBPAGE: setup_notification
**
** Administrative page for configuring and controlling email notification.
** Normally accessible via the /Admin/Notification menu.
*/
void setup_notification(void){
static const char *const azSendMethods[] = {
"off", "Disabled",
"relay", "SMTP relay",
"db", "Store in a database",
"dir", "Store in a directory",
"pipe", "Pipe to a command",
};
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
db_begin_transaction();
alert_submenu_common();
style_submenu_element("Send Announcement","%R/announce");
style_set_current_feature("alerts");
style_header("Email Notification Setup");
@ <form action="%R/setup_notification" method="post"><div>
@ <h1>Status   <input type="submit" name="submit" value="Refresh"></h1>
@ </form>
@ <table class="label-value">
if( alert_enabled() ){
stats_for_email();
}else{
@ <th>Disabled</th>
}
@ </table>
@ <hr>
@ <form action="%R/setup_notification" method="post"><div>
@ <h1> Configuration </h1>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ <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 "/".
|
| ︙ | ︙ | |||
388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
multiple_choice_attribute("Email Send Method", "email-send-method", "esm",
"off", count(azSendMethods)/2, azSendMethods);
@ <p>How to send email. Requires auxiliary information from the fields
@ that follow. Hint: Use the <a href="%R/announce">/announce</a> page
@ to send test message to debug this setting.
@ (Property: "email-send-method")</p>
alert_schema(1);
entry_attribute("Pipe Email Text Into This Command", 60, "email-send-command",
"ecmd", "sendmail -ti", 0);
@ <p>When the send method is "pipe to a command", this is the command
@ that is run. Email messages are piped into the standard input of this
@ command. The command is expected to extract the sender address,
@ recipient addresses, and subject from the header of the piped email
@ text. (Property: "email-send-command")</p>
| > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < | 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 |
multiple_choice_attribute("Email Send Method", "email-send-method", "esm",
"off", count(azSendMethods)/2, azSendMethods);
@ <p>How to send email. Requires auxiliary information from the fields
@ that follow. Hint: Use the <a href="%R/announce">/announce</a> page
@ to send test message to debug this setting.
@ (Property: "email-send-method")</p>
alert_schema(1);
entry_attribute("SMTP Relay Host", 60, "email-send-relayhost",
"esrh", "localhost", 0);
@ <p>When the send method is "SMTP relay", each email message is
@ transmitted via the SMTP protocol (rfc5321) to a "Mail Submission
@ Agent" or "MSA" (rfc4409) at the hostname shown here. Optionally
@ append a colon and TCP port number (ex: smtp.example.com:587).
@ The default TCP port number is 25.
@ Usage Hint: If Fossil is running inside of a chroot jail, then it might
@ not be able to resolve hostnames. Work around this by using a raw IP
@ address or create a "/etc/hosts" file inside the chroot jail.
@ (Property: "email-send-relayhost")</p>
@
entry_attribute("Store Emails In This Database", 60, "email-send-db",
"esdb", "", 0);
@ <p>When the send method is "store in a database", each email message is
@ stored in an SQLite database file with the name given here.
@ (Property: "email-send-db")</p>
entry_attribute("Pipe Email Text Into This Command", 60, "email-send-command",
"ecmd", "sendmail -ti", 0);
@ <p>When the send method is "pipe to a command", this is the command
@ that is run. Email messages are piped into the standard input of this
@ command. The command is expected to extract the sender address,
@ recipient addresses, and subject from the header of the piped email
@ text. (Property: "email-send-command")</p>
entry_attribute("Store Emails In This Directory", 60, "email-send-dir",
"esdir", "", 0);
@ <p>When the send method is "store in a directory", each email message is
@ stored as a separate file in the directory shown here.
@ (Property: "email-send-dir")</p>
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ | |||
627 628 629 630 631 632 633 |
}
}else if( fossil_strcmp(p->zDest, "pipe")==0 ){
emailerGetSetting(p, &p->zCmd, "email-send-command");
}else if( fossil_strcmp(p->zDest, "dir")==0 ){
emailerGetSetting(p, &p->zDir, "email-send-dir");
}else if( fossil_strcmp(p->zDest, "blob")==0 ){
blob_init(&p->out, 0, 0);
| | > > > | | > > > > > | 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 |
}
}else if( fossil_strcmp(p->zDest, "pipe")==0 ){
emailerGetSetting(p, &p->zCmd, "email-send-command");
}else if( fossil_strcmp(p->zDest, "dir")==0 ){
emailerGetSetting(p, &p->zDir, "email-send-dir");
}else if( fossil_strcmp(p->zDest, "blob")==0 ){
blob_init(&p->out, 0, 0);
}else if( fossil_strcmp(p->zDest, "relay")==0
|| fossil_strcmp(p->zDest, "debug-relay")==0
){
const char *zRelay = 0;
emailerGetSetting(p, &zRelay, "email-send-relayhost");
if( zRelay ){
u32 smtpFlags = SMTP_DIRECT;
if( mFlags & ALERT_TRACE ) smtpFlags |= SMTP_TRACE_STDOUT;
blob_init(&p->out, 0, 0);
p->pSmtp = smtp_session_new(domain_of_addr(p->zFrom), zRelay,
smtpFlags, 0);
if( p->pSmtp==0 || p->pSmtp->zErr ){
emailerError(p, "Could not start SMTP session: %s",
p->pSmtp ? p->pSmtp->zErr : "reason unknown");
}else if( p->zDest[0]=='d' ){
smtp_session_config(p->pSmtp, SMTP_TRACE_BLOB, &p->out);
}
}
}
return p;
}
/*
** Scan the header of the email message in pMsg looking for the
|
| ︙ | ︙ | |||
965 966 967 968 969 970 971 |
blob_appendf(pOut, "From: %s <%s@%s>\r\n",
zFromName, alert_mailbox_name(zFromName), alert_hostname(p->zFrom));
blob_appendf(pOut, "Sender: <%s>\r\n", p->zFrom);
}else{
blob_appendf(pOut, "From: <%s>\r\n", p->zFrom);
}
blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0)));
| < < < | 982 983 984 985 986 987 988 989 990 991 992 993 994 995 |
blob_appendf(pOut, "From: %s <%s@%s>\r\n",
zFromName, alert_mailbox_name(zFromName), alert_hostname(p->zFrom));
blob_appendf(pOut, "Sender: <%s>\r\n", p->zFrom);
}else{
blob_appendf(pOut, "From: <%s>\r\n", p->zFrom);
}
blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0)));
if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){
/* Message-id format: "<$(date)x$(random)@$(from-host)>" where $(date) is
** the current unix-time in hex, $(random) is a 64-bit random number,
** and $(from) is the domain part of the email-self setting. */
sqlite3_randomness(sizeof(r1), &r1);
r2 = time(0);
blob_appendf(pOut, "Message-Id: <%llxx%016llx@%s>\r\n",
|
| ︙ | ︙ | |||
1013 1014 1015 1016 1017 1018 1019 1020 |
}else if( p->zDir ){
char *zFile = file_time_tempname(p->zDir, ".email");
blob_write_to_file(&all, zFile);
fossil_free(zFile);
}else if( p->pSmtp ){
char **azTo = 0;
int nTo = 0;
email_header_to(pHdr, &nTo, &azTo);
| > | | > > > > > > > | 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 |
}else if( p->zDir ){
char *zFile = file_time_tempname(p->zDir, ".email");
blob_write_to_file(&all, zFile);
fossil_free(zFile);
}else if( p->pSmtp ){
char **azTo = 0;
int nTo = 0;
SmtpSession *pSmtp = p->pSmtp;
email_header_to(pHdr, &nTo, &azTo);
if( nTo>0 && !pSmtp->bFatal ){
smtp_send_msg(pSmtp,p->zFrom,nTo,(const char**)azTo,blob_str(&all));
if( pSmtp->zErr && !pSmtp->bFatal ){
smtp_send_msg(pSmtp,p->zFrom,nTo,(const char**)azTo,blob_str(&all));
}
if( pSmtp->zErr ){
fossil_errorlog("SMTP: (%s) %s", pSmtp->bFatal ? "fatal" : "retry",
pSmtp->zErr);
}
email_header_to_free(nTo, azTo);
}
}else if( strcmp(p->zDest, "stdout")==0 ){
char **azTo = 0;
int nTo = 0;
int i;
email_header_to(pHdr, &nTo, &azTo);
|
| ︙ | ︙ | |||
1122 1123 1124 1125 1126 1127 1128 | */ /* ** SETTING: email-listid width=40 ** If this setting is not an empty string, then it becomes the argument to ** a "List-ID:" header that is added to all out-bound notification emails. */ /* | | | | 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 | */ /* ** SETTING: email-listid width=40 ** If this setting is not an empty string, then it becomes the argument to ** a "List-ID:" header that is added to all out-bound notification emails. */ /* ** SETTING: email-send-relayhost width=40 sensitive default=127.0.0.1 ** This is the hostname and TCP port to which output email messages ** are sent when email-send-method is "relay". There should be an ** SMTP server configured as a Mail Submission Agent listening on the ** designated host and port and all times. */ /* ** COMMAND: alerts* abbrv-subcom ** ** Usage: %fossil alerts SUBCOMMAND ARGS... ** ** Subcommands: ** ** pending Show all pending alerts. Useful for debugging. ** |
| ︙ | ︙ | |||
1256 1257 1258 1259 1260 1261 1262 |
}
db_set(pSetting->name/*works-like:""*/, g.argv[4], isGlobal);
g.argc = 3;
}
pSetting = setting_info(&nSetting);
for(; nSetting>0; nSetting--, pSetting++ ){
if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
| | | | 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 |
}
db_set(pSetting->name/*works-like:""*/, g.argv[4], isGlobal);
g.argc = 3;
}
pSetting = setting_info(&nSetting);
for(; nSetting>0; nSetting--, pSetting++ ){
if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
print_setting(pSetting, 0, 0);
}
}else
if( strncmp(zCmd, "status", nCmd)==0 ){
Stmt q;
int iCutoff;
int nSetting, n;
static const char *zFmt = "%-29s %d\n";
const Setting *pSetting = setting_info(&nSetting);
db_open_config(1, 0);
verify_all_options();
if( g.argc!=3 ) usage("status");
pSetting = setting_info(&nSetting);
for(; nSetting>0; nSetting--, pSetting++ ){
if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
print_setting(pSetting, 0, 0);
}
n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep");
fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n);
n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest");
fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n);
db_prepare(&q,
"SELECT"
|
| ︙ | ︙ | |||
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 |
if( suname && suname[0]==0 ) suname = 0;
if( PB("sa") ) ssub[nsub++] = 'a';
if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
ssub[nsub] = 0;
zCode = db_text(0,
"INSERT INTO subscriber(semail,suname,"
" sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip,lastContact)"
"VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q,now()/86400)"
| > | 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 |
if( suname && suname[0]==0 ) suname = 0;
if( PB("sa") ) ssub[nsub++] = 'a';
if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
if( g.perm.Admin && PB("su") ) ssub[nsub++] = 'u';
if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
ssub[nsub] = 0;
zCode = db_text(0,
"INSERT INTO subscriber(semail,suname,"
" sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip,lastContact)"
"VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q,now()/86400)"
|
| ︙ | ︙ | |||
1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 |
** subscription options to "on" */
cgi_set_parameter_nocopy("sa","1",1);
if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1);
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1);
if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1);
}
@ <p>To receive email notifications for changes to this
@ repository, fill out the form below and press the "Submit" button.</p>
form_begin(0, "%R/subscribe");
@ <table class="subscribe">
@ <tr>
| > | 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 |
** subscription options to "on" */
cgi_set_parameter_nocopy("sa","1",1);
if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1);
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1);
if( g.perm.Admin ) cgi_set_parameter_nocopy("su","1",1);
if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1);
}
@ <p>To receive email notifications for changes to this
@ repository, fill out the form below and press the "Submit" button.</p>
form_begin(0, "%R/subscribe");
@ <table class="subscribe">
@ <tr>
|
| ︙ | ︙ | |||
1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 |
@ <label><input type="checkbox" name="st" %s(PCK("st"))> \
@ Ticket changes</label><br>
}
if( g.perm.RdWiki ){
@ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \
@ Wiki</label><br>
}
di = PB("di");
@ </td></tr>
@ <tr>
@ <td class="form_label">Delivery:</td>
@ <td><select size="1" name="di">
@ <option value="0" %s(di?"":"selected")>Individual Emails</option>
@ <option value="1" %s(di?"selected":"")>Daily Digest</option>
| > > > > | 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 |
@ <label><input type="checkbox" name="st" %s(PCK("st"))> \
@ Ticket changes</label><br>
}
if( g.perm.RdWiki ){
@ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \
@ Wiki</label><br>
}
if( g.perm.Admin ){
@ <label><input type="checkbox" name="su" %s(PCK("su"))> \
@ User permission changes</label>
}
di = PB("di");
@ </td></tr>
@ <tr>
@ <td class="form_label">Delivery:</td>
@ <td><select size="1" name="di">
@ <option value="0" %s(di?"":"selected")>Individual Emails</option>
@ <option value="1" %s(di?"selected":"")>Daily Digest</option>
|
| ︙ | ︙ | |||
1818 1819 1820 1821 1822 1823 1824 |
** in length.) This uniquely identifies the subscriber without
** revealing the complete subscriber code, and hence without
** verifying the email address.
*/
void alert_page(void){
const char *zName = 0; /* Value of the name= query parameter */
Stmt q; /* For querying the database */
| | | 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 |
** in length.) This uniquely identifies the subscriber without
** revealing the complete subscriber code, and hence without
** verifying the email address.
*/
void alert_page(void){
const char *zName = 0; /* Value of the name= query parameter */
Stmt q; /* For querying the database */
int sa, sc, sf, st, su, sw, sx; /* Types of notifications requested */
int sn, sr;
int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
int isLogin; /* True if logged in as an individual */
const char *ssub = 0; /* Subscription flags */
const char *semail = 0; /* Email address */
const char *smip; /* */
const char *suname = 0; /* Corresponding user.login value */
|
| ︙ | ︙ | |||
1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 |
semail = P("semail");
if( PB("sa") ) newSsub[nsub++] = 'a';
if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c';
if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't';
if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w';
if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
newSsub[nsub] = 0;
ssub = newSsub;
blob_init(&update, "UPDATE subscriber SET", -1);
blob_append_sql(&update,
" sdonotcall=%d,"
| > | 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 |
semail = P("semail");
if( PB("sa") ) newSsub[nsub++] = 'a';
if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c';
if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't';
if( g.perm.Admin && PB("su") ) newSsub[nsub++] = 'u';
if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w';
if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
newSsub[nsub] = 0;
ssub = newSsub;
blob_init(&update, "UPDATE subscriber SET", -1);
blob_append_sql(&update,
" sdonotcall=%d,"
|
| ︙ | ︙ | |||
1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 |
}
sa = strchr(ssub,'a')!=0;
sc = strchr(ssub,'c')!=0;
sf = strchr(ssub,'f')!=0;
sn = strchr(ssub,'n')!=0;
sr = strchr(ssub,'r')!=0;
st = strchr(ssub,'t')!=0;
sw = strchr(ssub,'w')!=0;
sx = strchr(ssub,'x')!=0;
smip = db_column_text(&q, 5);
mtime = db_column_text(&q, 7);
sctime = db_column_text(&q, 8);
if( !g.perm.Admin && !sverified ){
if( nName==64 ){
| > | 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 |
}
sa = strchr(ssub,'a')!=0;
sc = strchr(ssub,'c')!=0;
sf = strchr(ssub,'f')!=0;
sn = strchr(ssub,'n')!=0;
sr = strchr(ssub,'r')!=0;
st = strchr(ssub,'t')!=0;
su = strchr(ssub,'u')!=0;
sw = strchr(ssub,'w')!=0;
sx = strchr(ssub,'x')!=0;
smip = db_column_text(&q, 5);
mtime = db_column_text(&q, 7);
sctime = db_column_text(&q, 8);
if( !g.perm.Admin && !sverified ){
if( nName==64 ){
|
| ︙ | ︙ | |||
2095 2096 2097 2098 2099 2100 2101 |
}
if( g.perm.RdTkt ){
@ <label><input type="checkbox" name="st" %s(st?"checked":"")>\
@ Ticket changes</label><br>
}
if( g.perm.RdWiki ){
@ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
| | > > > > > > > > | 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 |
}
if( g.perm.RdTkt ){
@ <label><input type="checkbox" name="st" %s(st?"checked":"")>\
@ Ticket changes</label><br>
}
if( g.perm.RdWiki ){
@ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
@ Wiki</label><br>
}
if( g.perm.Admin ){
/* Corner-case bug: if an admin assigns 'u' to a non-admin, that
** subscription will get removed if the user later edits their
** subscriptions, as non-admins are not permitted to add that
** subscription. */
@ <label><input type="checkbox" name="su" %s(su?"checked":"")>\
@ User permission changes</label>
}
@ </td></tr>
if( strchr(ssub,'k')!=0 ){
@ <tr><td></td><td> ↑
@ Note: User did a one-click unsubscribe</td></tr>
}
@ <tr>
|
| ︙ | ︙ | |||
2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 |
**
** c A new check-in
** f An original forum post
** n New forum threads
** r Replies to my forum posts
** x An edit to a prior forum post
** t A new ticket or a change to an existing ticket
** w A change to a wiki page
** x Edits to forum posts
*/
struct EmailEvent {
| > | | 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 |
**
** c A new check-in
** f An original forum post
** n New forum threads
** r Replies to my forum posts
** x An edit to a prior forum post
** t A new ticket or a change to an existing ticket
** u A user was added or received new permissions
** w A change to a wiki page
** x Edits to forum posts
*/
struct EmailEvent {
int type; /* 'c', 'f', 'n', 'r', 't', 'u', 'w', 'x' */
int needMod; /* Pending moderator approval */
Blob hdr; /* Header content, for forum entries */
Blob txt; /* Text description to appear in an alert */
char *zFromName; /* Human name of the sender */
char *zPriors; /* Upthread sender IDs for forum posts */
EmailEvent *pNext; /* Next in chronological order */
};
|
| ︙ | ︙ | |||
2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 |
"on the %s Fossil repository at %s:\n\n",
zRepoName, zUrl
);
if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n");
if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n");
if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n");
if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n");
if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n");
blob_appendf(pBody, "\n"
"If you take no action, your subscription will expire and you will be\n"
"unsubscribed in about %d days. To make other changes or to unsubscribe\n"
"immediately, visit the following webpage:\n\n"
" %s/alerts/%s\n\n",
ALERT_RENEWAL_MSG_FREQUENCY, zUrl, zCode
| > | 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 |
"on the %s Fossil repository at %s:\n\n",
zRepoName, zUrl
);
if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n");
if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n");
if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n");
if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n");
if( strchr(zSub, 'u') ) blob_appendf(pBody, " * User permission elevation\n");
if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n");
blob_appendf(pBody, "\n"
"If you take no action, your subscription will expire and you will be\n"
"unsubscribed in about %d days. To make other changes or to unsubscribe\n"
"immediately, visit the following webpage:\n\n"
" %s/alerts/%s\n\n",
ALERT_RENEWAL_MSG_FREQUENCY, zUrl, zCode
|
| ︙ | ︙ | |||
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 |
char xType = '*';
if( strpbrk(zCap,"as")==0 ){
switch( p->type ){
case 'x': case 'f':
case 'n': case 'r': xType = '5'; break;
case 't': xType = 'q'; break;
case 'w': xType = 'l'; break;
}
if( strchr(zCap,xType)==0 ) continue;
}
}else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){
/* Setup and admin users can get any notification that does not
** require moderation */
}else{
/* Other users only see the alert if they have sufficient
** privilege to view the event itself */
char xType = '*';
switch( p->type ){
case 'c': xType = 'o'; break;
case 'x': case 'f':
case 'n': case 'r': xType = '2'; break;
case 't': xType = 'r'; break;
case 'w': xType = 'j'; break;
}
if( strchr(zCap,xType)==0 ) continue;
}
if( blob_size(&p->hdr)>0 ){
/* This alert should be sent as a separate email */
Blob fhdr, fbody;
blob_init(&fhdr, 0, 0);
blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
| > > > > | | | | | | | | > | 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 |
char xType = '*';
if( strpbrk(zCap,"as")==0 ){
switch( p->type ){
case 'x': case 'f':
case 'n': case 'r': xType = '5'; break;
case 't': xType = 'q'; break;
case 'w': xType = 'l'; break;
/* Note: case 'u' is not handled here */
}
if( strchr(zCap,xType)==0 ) continue;
}
}else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){
/* Setup and admin users can get any notification that does not
** require moderation */
}else{
/* Other users only see the alert if they have sufficient
** privilege to view the event itself */
char xType = '*';
switch( p->type ){
case 'c': xType = 'o'; break;
case 'x': case 'f':
case 'n': case 'r': xType = '2'; break;
case 't': xType = 'r'; break;
case 'w': xType = 'j'; break;
/* Note: case 'u' is not handled here */
}
if( strchr(zCap,xType)==0 ) continue;
}
if( blob_size(&p->hdr)>0 ){
/* This alert should be sent as a separate email */
Blob fhdr, fbody;
blob_init(&fhdr, 0, 0);
blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
if( pSender->zListId && pSender->zListId[0] ){
blob_appendf(&fhdr, "List-Id: %s\r\n", pSender->zListId);
blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
zUrl, zCode);
blob_appendf(&fhdr,
"List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
zUrl, zCode);
/* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
** zUrl, zCode); */
}
alert_send(pSender,&fhdr,&fbody,p->zFromName);
nSent++;
blob_reset(&fhdr);
blob_reset(&fbody);
}else{
/* Events other than forum posts are gathered together into
** a single email message */
|
| ︙ | ︙ | |||
3198 3199 3200 3201 3202 3203 3204 |
}
nHit++;
blob_append(&body, "\n", 1);
blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
}
}
if( nHit==0 ) continue;
| > > | | > | | | > | 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 |
}
nHit++;
blob_append(&body, "\n", 1);
blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
}
}
if( nHit==0 ) continue;
if( pSender->zListId && pSender->zListId[0] ){
blob_appendf(&hdr, "List-Id: %s\r\n", pSender->zListId);
blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
zUrl, zCode);
blob_appendf(&hdr,
"List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
zUrl, zCode);
}
alert_send(pSender,&hdr,&body,0);
nSent++;
blob_truncate(&hdr, 0);
blob_truncate(&body, 0);
}
blob_reset(&hdr);
blob_reset(&body);
|
| ︙ | ︙ | |||
3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 |
" WHERE lastContact<=%d AND lastContact>%d"
" AND NOT sdonotcall"
" AND length(sdigest)>0",
iNewWarn, iOldWarn
);
while( db_step(&q)==SQLITE_ROW ){
Blob hdr, body;
blob_init(&hdr, 0, 0);
blob_init(&body, 0, 0);
alert_renewal_msg(&hdr, &body,
| > | > > > > > > > > > | 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 |
" WHERE lastContact<=%d AND lastContact>%d"
" AND NOT sdonotcall"
" AND length(sdigest)>0",
iNewWarn, iOldWarn
);
while( db_step(&q)==SQLITE_ROW ){
Blob hdr, body;
const char *zCode = db_column_text(&q,0);
blob_init(&hdr, 0, 0);
blob_init(&body, 0, 0);
alert_renewal_msg(&hdr, &body,
zCode,
db_column_int(&q,1),
db_column_text(&q,2),
db_column_text(&q,3),
zRepoName, zUrl);
if( pSender->zListId && pSender->zListId[0] ){
blob_appendf(&hdr, "List-Id: %s\r\n", pSender->zListId);
blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
zUrl, zCode);
blob_appendf(&hdr,
"List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
blob_appendf(&body, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
zUrl, zCode);
}
alert_send(pSender,&hdr,&body,0);
blob_reset(&hdr);
blob_reset(&body);
}
db_finalize(&q);
if( (flags & SENDALERT_PRESERVE)==0 ){
if( iOldWarn>0 ){
|
| ︙ | ︙ | |||
3413 3414 3415 3416 3417 3418 3419 |
char *zErr;
const char *zTo = PT("to");
char *zSubject = PT("subject");
int bAll = PB("all");
int bAA = PB("aa");
int bMods = PB("mods");
const char *zSub = db_get("email-subname", "[Fossil Repo]");
| > > | > > > > > > > > > > | | 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 |
char *zErr;
const char *zTo = PT("to");
char *zSubject = PT("subject");
int bAll = PB("all");
int bAA = PB("aa");
int bMods = PB("mods");
const char *zSub = db_get("email-subname", "[Fossil Repo]");
const char *zName = P("name"); /* Debugging options */
const char *zDest = 0; /* How to send the announcement */
int bTest = 0;
Blob hdr, body;
if( fossil_strcmp(zName, "test2")==0 ){
bTest = 2;
zDest = "blob";
}else if( fossil_strcmp(zName, "test3")==0 ){
bTest = 3;
if( fossil_strcmp(db_get("email-send-method",""),"relay")==0 ){
zDest = "debug-relay";
}
}
blob_init(&body, 0, 0);
blob_init(&hdr, 0, 0);
blob_appendf(&body, "%s", PT("msg")/*safe-for-%s*/);
pSender = alert_sender_new(zDest, 0);
if( zTo[0] ){
blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", zTo, zSub, zSubject);
alert_send(pSender, &hdr, &body, 0);
}
if( bAll || bAA || bMods ){
Stmt q;
int nUsed = blob_size(&body);
|
| ︙ | ︙ | |||
3456 3457 3458 3459 3460 3461 3462 |
blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
zURL, zCode);
}
alert_send(pSender, &hdr, &body, 0);
}
db_finalize(&q);
}
| | | | | > > > > > > | > | | < > | > | > > > > | < > > > | | | > > > > > > > | 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 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 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 |
blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
zURL, zCode);
}
alert_send(pSender, &hdr, &body, 0);
}
db_finalize(&q);
}
if( bTest && blob_size(&pSender->out) ){
/* If the URL is "/announce/test2" then no email is actually sent.
** Instead, the text of the email that would have been sent is
** displayed in the result window.
**
** If the URL is "/announce/test3" and the email-send-method is "relay"
** then the announcement is sent as it normally would be, but a
** transcript of the SMTP conversation with the MTA is shown here.
*/
blob_trim(&pSender->out);
@ <pre style='border: 2px solid blue; padding: 1ex;'>
@ %h(blob_str(&pSender->out))
@ </pre>
blob_reset(&pSender->out);
}
zErr = pSender->zErr;
pSender->zErr = 0;
alert_sender_free(pSender);
return zErr;
}
/*
** WEBPAGE: announce
**
** A web-form, available to users with the "Send-Announcement" or "A"
** capability, that allows one to send announcements to whomever
** has subscribed to receive announcements. The administrator can
** also send a message to an arbitrary email address and/or to all
** subscribers regardless of whether or not they have elected to
** receive announcements.
*/
void announce_page(void){
const char *zAction = "announce";
const char *zName = PD("name","");
/*
** Debugging Notes:
**
** /announce/test1 -> Shows query parameter values
** /announce/test2 -> Shows the formatted message but does
** not send it.
** /announce/test3 -> Sends the message, but also shows
** the SMTP transcript.
*/
login_check_credentials();
if( !g.perm.Announce ){
login_needed(0);
return;
}
if( !g.perm.Setup ){
zName = 0; /* Disable debugging feature for non-admin users */
}
style_set_current_feature("alerts");
if( fossil_strcmp(zName,"test1")==0 ){
/* Visit the /announce/test1 page to see the CGI variables */
zAction = "announce/test1";
@ <p style='border: 1px solid black; padding: 1ex;'>
cgi_print_all(0, 0, 0);
@ </p>
}else if( P("submit")!=0 && cgi_csrf_safe(2) ){
char *zErr = alert_send_announcement();
style_header("Announcement Sent");
if( zErr ){
@ <h1>Error</h1>
@ <p>The following error was reported by the
@ announcement-sending subsystem:
@ <blockquote><pre>
@ %h(zErr)
@ </pre></blockquote>
}else{
@ <p>The announcement has been sent.
@ <a href="%h(PD("REQUEST_URI","/"))">Send another</a></p>
}
style_finish_page();
return;
} else if( !alert_enabled() ){
style_header("Cannot Send Announcement");
@ <p>Either you have no subscribers yet, or email alerts are not yet
@ <a href="https://fossil-scm.org/fossil/doc/trunk/www/alerts.md">set up</a>
@ for this repository.</p>
return;
}
style_header("Send Announcement");
alert_submenu_common();
if( fossil_strcmp(zName,"test2")==0 ){
zAction = "announce/test2";
}else if( fossil_strcmp(zName,"test3")==0 ){
zAction = "announce/test3";
}
@ <form method="POST" action="%R/%s(zAction)">
login_insert_csrf_secret();
@ <table class="subscribe">
if( g.perm.Admin ){
int aa = PB("aa");
int all = PB("all");
int aMod = PB("mods");
|
| ︙ | ︙ | |||
3561 3562 3563 3564 3565 3566 3567 |
@ <tr>
@ <td class="form_label">Message:</td>
@ <td><textarea name="msg" cols="80" rows="10" wrap="virtual">\
@ %h(PT("msg"))</textarea>
@ </tr>
@ <tr>
@ <td></td>
| | > > > > > > > > > > > > > | 3653 3654 3655 3656 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 |
@ <tr>
@ <td class="form_label">Message:</td>
@ <td><textarea name="msg" cols="80" rows="10" wrap="virtual">\
@ %h(PT("msg"))</textarea>
@ </tr>
@ <tr>
@ <td></td>
if( fossil_strcmp(zName,"test2")==0 ){
@ <td><input type="submit" name="submit" value="Dry Run">
}else{
@ <td><input type="submit" name="submit" value="Send Message">
}
@ </tr>
@ </table>
@ </form>
if( g.perm.Setup ){
@ <hr>
@ <p>Trouble-shooting Options:</p>
@ <ol>
@ <li> <a href="%R/announce">Normal Processing</a>
@ <li> Only <a href="%R/announce/test1">show POST parameters</a>
@ - Do not send the announcement.
@ <li> <a href="%R/announce/test2">Show the email text</a> but do
@ not actually send it.
@ <li> Send the message and also <a href="%R/announce/test3">show the
@ SMTP traffic</a> when using "relay" mode.
@ </ol>
}
style_finish_page();
}
|
Changes to src/allrepo.c.
| ︙ | ︙ | |||
49 50 51 52 53 54 55 |
int i;
for(i=iStart; i<g.argc; i++){
blob_appendf(pExtra, " %s", g.argv[i]);
}
}
/*
| | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
int i;
for(i=iStart; i<g.argc; i++){
blob_appendf(pExtra, " %s", g.argv[i]);
}
}
/*
** COMMAND: all abbrv-subcom
**
** Usage: %fossil all SUBCOMMAND ...
**
** The ~/.fossil file records the location of all repositories for a
** user. This command performs certain operations on all repositories
** that can be useful before or after a period of disconnected operation.
**
|
| ︙ | ︙ | |||
314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
}
}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);
| > | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
}
}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_argument(&extra, "changed", 0);
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);
|
| ︙ | ︙ |
Changes to src/backlink.c.
| ︙ | ︙ | |||
429 430 431 432 433 434 435 | ); backlink_extract(blob_str(&in),mimetype,srcid,srctype,mtime,0); blob_reset(&in); } /* | | | | 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
);
backlink_extract(blob_str(&in),mimetype,srcid,srctype,mtime,0);
blob_reset(&in);
}
/*
** COMMAND: test-relink-wiki
**
** Usage: %fossil test-relink-wiki WIKI-PAGE-NAME
**
** Run the backlink_wiki_refresh() procedure on the wiki page
** named. WIKI-PAGE-NAME can be a glob pattern or a prefix
** of the wiki page.
*/
void test_wiki_relink_cmd(void){
Stmt q;
|
| ︙ | ︙ |
Changes to src/backoffice.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 | ** Backoffice processes should die off after doing whatever work they need ** to do. In this way, we avoid having lots of idle processes in the ** process table, doing nothing on rarely accessed repositories, and ** if the Fossil binary is updated on a system, the backoffice processes ** will restart using the new binary automatically. ** ** At any point in time there should be at most two backoffice processes. | | | | 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 | ** Backoffice processes should die off after doing whatever work they need ** to do. In this way, we avoid having lots of idle processes in the ** process table, doing nothing on rarely accessed repositories, and ** if the Fossil binary is updated on a system, the backoffice processes ** will restart using the new binary automatically. ** ** At any point in time there should be at most two backoffice processes. ** There is a main process that is doing the actual work, and there is ** a second stand-by process that is waiting for the main process to finish ** and that will become the main process after a delay. ** ** After any successful web page reply, the backoffice_check_if_needed() ** routine is called. That routine checks to see if both one or both of ** the backoffice processes are already running. That routine remembers the ** status in a global variable. ** ** Later, after the repository database is closed, the ** backoffice_run_if_needed() routine is called. If the prior call ** to backoffice_check_if_needed() indicated that backoffice processing ** might be required, the run_if_needed() attempts to kick off a backoffice ** process. ** ** All work performed by the backoffice is in the backoffice_work() ** routine. */ #if defined(_WIN32) # if defined(_WIN32_WINNT) # undef _WIN32_WINNT # endif # define _WIN32_WINNT 0x501 |
| ︙ | ︙ | |||
483 484 485 486 487 488 489 | 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; | | | 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
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-pending";
backoffice_error_check_one(&once);
idSelf = backofficeProcessId();
while(1){
tmNow = time(0);
db_begin_write();
backofficeReadLease(&x);
if( x.tmNext>=tmNow
|
| ︙ | ︙ | |||
508 509 510 511 512 513 514 515 516 517 518 519 520 521 |
}
if( x.tmCurrent<tmNow && backofficeProcessDone(x.idCurrent) ){
/* This process can start doing backoffice work immediately */
x.idCurrent = idSelf;
x.tmCurrent = tmNow + BKOFCE_LEASE_TIME;
x.idNext = 0;
x.tmNext = 0;
backofficeWriteLease(&x);
db_end_transaction(0);
backofficeTrace("/***** Begin Backoffice Processing %d *****/\n",
GETPID());
backoffice_work();
break;
}
| > | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
}
if( x.tmCurrent<tmNow && backofficeProcessDone(x.idCurrent) ){
/* This process can start doing backoffice work immediately */
x.idCurrent = idSelf;
x.tmCurrent = tmNow + BKOFCE_LEASE_TIME;
x.idNext = 0;
x.tmNext = 0;
g.zPhase = "backoffice-work";
backofficeWriteLease(&x);
db_end_transaction(0);
backofficeTrace("/***** Begin Backoffice Processing %d *****/\n",
GETPID());
backoffice_work();
break;
}
|
| ︙ | ︙ | |||
541 542 543 544 545 546 547 |
/* The sleep was interrupted by a signal from another thread. */
backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID());
db_end_transaction(0);
break;
}
}else{
if( (sqlite3_uint64)(lastWarning+warningDelay) < tmNow ){
| > > | | > | 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
/* The sleep was interrupted by a signal from another thread. */
backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID());
db_end_transaction(0);
break;
}
}else{
if( (sqlite3_uint64)(lastWarning+warningDelay) < tmNow ){
sqlite3_int64 runningFor = BKOFCE_LEASE_TIME + tmNow - x.tmCurrent;
if( warningDelay>=240 && runningFor<1800 ){
fossil_warning(
"backoffice process %lld still running after %d seconds",
x.idCurrent, runningFor);
}
lastWarning = tmNow;
warningDelay *= 2;
}
if( backofficeSleep(1000) ){
/* The sleep was interrupted by a signal from another thread. */
backofficeTrace("/***** Backoffice Interrupt %d *****/\n", GETPID());
db_end_transaction(0);
|
| ︙ | ︙ | |||
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 |
}
blob_init(&log, 0, 0);
backofficeBlob = &log;
blob_appendf(&log, "%s %s", db_text(0, "SELECT datetime('now')"), zName);
}
/* Here is where the actual work of the backoffice happens */
nThis = alert_backoffice(0);
if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; }
nThis = hook_backoffice();
if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; }
/* Close the log */
if( backofficeFILE ){
if( nTotal || backofficeLogDetail ){
if( nTotal==0 ) backoffice_log("no-op");
#if !defined(_WIN32)
gettimeofday(&sEnd,0);
| > > > | 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 |
}
blob_init(&log, 0, 0);
backofficeBlob = &log;
blob_appendf(&log, "%s %s", db_text(0, "SELECT datetime('now')"), zName);
}
/* Here is where the actual work of the backoffice happens */
g.zPhase = "backoffice-alerts";
nThis = alert_backoffice(0);
if( nThis ){ backoffice_log("%d alerts", nThis); nTotal += nThis; }
g.zPhase = "backoffice-hooks";
nThis = hook_backoffice();
if( nThis ){ backoffice_log("%d hooks", nThis); nTotal += nThis; }
g.zPhase = "backoffice-close";
/* Close the log */
if( backofficeFILE ){
if( nTotal || backofficeLogDetail ){
if( nTotal==0 ) backoffice_log("no-op");
#if !defined(_WIN32)
gettimeofday(&sEnd,0);
|
| ︙ | ︙ | |||
689 690 691 692 693 694 695 | ** ** --min N When polling, invoke backoffice at least ** once every N seconds even if the repository ** never changes. 0 or negative means disable ** this feature. Default: 3600 (once per hour). ** ** --poll N Repeat backoffice calls for repositories that | | | 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 | ** ** --min N When polling, invoke backoffice at least ** once every N seconds even if the repository ** never changes. 0 or negative means disable ** this feature. Default: 3600 (once per hour). ** ** --poll N Repeat backoffice calls for repositories that ** change in approximately N-second intervals. ** N less than 1 turns polling off (the default). ** Recommended polling interval: 60 seconds. ** ** --trace Enable debugging output on stderr ** ** Options intended for internal use only which may change or be ** discontinued in future releases: |
| ︙ | ︙ |
Changes to src/bisect.c.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 |
** Find the shortest path between bad and good.
*/
void bisect_path(void){
PathNode *p;
bisect.bad = db_lget_int("bisect-bad", 0);
bisect.good = db_lget_int("bisect-good", 0);
if( bisect.good>0 && bisect.bad==0 ){
| | | | | 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 |
** Find the shortest path between bad and good.
*/
void bisect_path(void){
PathNode *p;
bisect.bad = db_lget_int("bisect-bad", 0);
bisect.good = db_lget_int("bisect-good", 0);
if( bisect.good>0 && bisect.bad==0 ){
path_shortest(bisect.good, bisect.good, 0, 0, 0, 0);
}else if( bisect.bad>0 && bisect.good==0 ){
path_shortest(bisect.bad, bisect.bad, 0, 0, 0, 0);
}else if( bisect.bad==0 && bisect.good==0 ){
fossil_fatal("neither \"good\" nor \"bad\" versions have been identified");
}else{
Bag skip;
int bDirect = bisect_option("direct-only");
char *zLog = db_lget("bisect-log","");
Blob log, id;
bag_init(&skip);
blob_init(&log, zLog, -1);
while( blob_token(&log, &id) ){
if( blob_str(&id)[0]=='s' ){
bag_insert(&skip, atoi(blob_str(&id)+1));
}
}
blob_reset(&log);
p = path_shortest(bisect.good, bisect.bad, bDirect, 0, &skip, 0);
bag_clear(&skip);
if( p==0 ){
char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad);
char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good);
fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
zGood, zBad);
}
|
| ︙ | ︙ | |||
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 |
** Append a new skip entry to the bisect log.
*/
static void bisect_append_skip(int rid){
db_multi_exec(
"UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid
);
}
/*
** Create a TEMP table named "bilog" that contains the complete history
** of the current bisect.
**
** If iCurrent>0 then it is the RID of the current check-out and is included
** in the history table.
**
** If zDesc is not NULL, then it is the bid= query parameter to /timeline
** that describes a bisect. Use the information in zDesc rather than in
** the bisect-log variable.
**
** If bDetail is true, then also include information about every node
** in between the inner-most GOOD and BAD nodes.
*/
int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){
char *zLog;
Blob log, id;
| > > > > > > > > > > > > > > > > < > | 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 |
** Append a new skip entry to the bisect log.
*/
static void bisect_append_skip(int rid){
db_multi_exec(
"UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid
);
}
/*
** Append a VALUES entry to the bilog table insert
*/
static void bisect_log_append(Blob *pSql,int iSeq,const char *zStat,int iRid){
if( (iSeq%6)==3 ){
blob_append_sql(pSql, ",\n ");
}else if( iSeq>1 ){
blob_append_sql(pSql, ",");
}
if( zStat ){
blob_append_sql(pSql, "(%d,%Q,%d)", iSeq, zStat, iRid);
}else{
blob_append_sql(pSql, "(NULL,NULL,%d)", iRid);
}
}
/*
** Create a TEMP table named "bilog" that contains the complete history
** of the current bisect.
**
** If iCurrent>0 then it is the RID of the current check-out and is included
** in the history table.
**
** If zDesc is not NULL, then it is the bid= query parameter to /timeline
** that describes a bisect. Use the information in zDesc rather than in
** the bisect-log variable.
**
** If bDetail is true, then also include information about every node
** in between the inner-most GOOD and BAD nodes.
*/
int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){
char *zLog;
Blob log, id;
int cnt = 0;
int lastGood = -1;
int lastBad = -1;
Blob ins = BLOB_INITIALIZER;
if( zDesc!=0 ){
blob_init(&log, 0, 0);
while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){
int i;
char c;
int rid;
|
| ︙ | ︙ | |||
251 252 253 254 255 256 257 |
db_multi_exec(
"CREATE TEMP TABLE bilog("
" rid INTEGER PRIMARY KEY," /* Sequence of events */
" stat TEXT," /* Type of occurrence */
" seq INTEGER UNIQUE" /* Check-in number */
");"
);
| | < | | < | < < | < < < | < < < | < < | < < | > | 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 |
db_multi_exec(
"CREATE TEMP TABLE bilog("
" rid INTEGER PRIMARY KEY," /* Sequence of events */
" stat TEXT," /* Type of occurrence */
" seq INTEGER UNIQUE" /* Check-in number */
");"
);
blob_append_sql(&ins, "INSERT OR IGNORE INTO bilog(seq,stat,rid) VALUES");
while( blob_token(&log, &id) ){
int rid;
cnt++;
if( blob_str(&id)[0]=='s' ){
rid = atoi(blob_str(&id)+1);
bisect_log_append(&ins, cnt, "SKIP", rid);
}else{
rid = atoi(blob_str(&id));
if( rid>0 ){
bisect_log_append(&ins, cnt, "GOOD", rid);
lastGood = rid;
}else{
bisect_log_append(&ins, cnt, "BAD", -rid);
lastBad = -rid;
}
}
}
if( iCurrent>0 ){
bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent);
}
if( bDetail && lastGood>0 && lastBad>0 ){
PathNode *p;
p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0, 0);
while( p ){
bisect_log_append(&ins, ++cnt, 0, p->rid);
p = p->u.pTo;
}
path_reset();
}
db_exec_sql(blob_sql_text(&ins));
blob_reset(&ins);
return 1;
}
/* Return a permalink description of a bisect. Space is obtained from
** fossil_malloc() and should be freed by the caller.
**
** A bisect description consists of characters 'y' and 'n' and lowercase
|
| ︙ | ︙ |
Changes to src/blob.c.
| ︙ | ︙ | |||
1992 1993 1994 1995 1996 1997 1998 |
/* Make sure the blob contains two terminating 0-bytes */
blob_append(pBlob, "\000\000", 3);
zUtf8 = blob_str(pBlob) + bomSize;
zUtf8 = fossil_unicode_to_utf8(zUtf8);
blob_reset(pBlob);
blob_set_dynamic(pBlob, zUtf8);
}else if( useMbcs && invalid_utf8(pBlob) ){
| | | 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 |
/* Make sure the blob contains two terminating 0-bytes */
blob_append(pBlob, "\000\000", 3);
zUtf8 = blob_str(pBlob) + bomSize;
zUtf8 = fossil_unicode_to_utf8(zUtf8);
blob_reset(pBlob);
blob_set_dynamic(pBlob, zUtf8);
}else if( useMbcs && invalid_utf8(pBlob) ){
#if defined(_WIN32)
zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
blob_reset(pBlob);
blob_append(pBlob, zUtf8, -1);
fossil_mbcs_free(zUtf8);
#else
blob_cp1252_to_utf8(pBlob);
#endif /* _WIN32 */
|
| ︙ | ︙ |
Changes to src/branch.c.
| ︙ | ︙ | |||
596 597 598 599 600 601 602 | ** COMMAND: branch ** ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS? ** ** Run various subcommands to manage branches of the open repository or ** of the repository identified by the -R or --repository option. ** | | | | | | | | 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 | ** COMMAND: branch ** ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS? ** ** Run various subcommands to manage branches of the open repository or ** of the repository identified by the -R or --repository option. ** ** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? ** ** Adds or cancels the "closed" tag to one or more branches. ** It accepts arbitrary unambiguous symbolic names but ** will only resolve check-in names and skips any which resolve ** to non-leaf check-ins. ** ** Options: ** -n|--dry-run Do not commit changes, but dump artifact ** to stdout ** -v|--verbose Output more information ** --date-override DATE DATE to use instead of 'now' ** --user-override USER USER to use instead of the current default ** ** > fossil branch current ** ** Print the name of the branch for the current check-out ** ** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? ** ** Adds or cancels the "hidden" tag for the specified branches or ** or check-in IDs. Accepts the same options as the close ** subcommand. ** ** > fossil branch info BRANCH-NAME ** ** Print information about a branch ** ** > fossil branch list|ls ?OPTIONS? ?GLOB? ** > fossil branch lsh ?OPTIONS? ?LIMIT? ** ** List all branches. ** ** Options: ** -a|--all List all branches. Default show only open branches ** -c|--closed List closed branches ** -m|--merged List branches merged into the current branch |
| ︙ | ︙ | |||
651 652 653 654 655 656 657 | ** If GLOB is given, show only branches matching the pattern. ** ** The "lsh" variant of this subcommand shows recently changed branches, ** and accepts an optional LIMIT argument (defaults to 5) to cap output, ** but no GLOB argument. All other options are supported, with -t being ** an implied no-op. ** | | > > > > > > < < < < < < | 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 |
** If GLOB is given, show only branches matching the pattern.
**
** The "lsh" variant of this subcommand shows recently changed branches,
** and accepts an optional LIMIT argument (defaults to 5) to cap output,
** but no GLOB argument. All other options are supported, with -t being
** an implied no-op.
**
** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
**
** Create a new branch BRANCH-NAME off of check-in BASIS.
**
** This command is available for people who want to create a branch
** in advance. But the use of this command is discouraged. The
** preferred idiom in Fossil is to create new branches at the point
** of need, using the "--branch NAME" option to the "fossil commit"
** command.
**
** Options:
** --private Branch is private (i.e., remains local)
** --bgcolor COLOR Use COLOR instead of automatic background
** --nosign Do not sign the manifest for the check-in
** that creates 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
**
** Options:
** -R|--repository REPO Run commands on repository REPO
*/
void branch_cmd(void){
int n;
const char *zCmd = "list";
db_find_and_open_repository(0, 0);
|
| ︙ | ︙ | |||
868 869 870 871 872 873 874 |
const char *zMergeTo = db_column_text(&q, 3);
int nCkin = db_column_int(&q, 4);
const char *zLastCkin = db_column_text(&q, 5);
const char *zBgClr = db_column_text(&q, 6);
char *zAge = human_readable_age(rNow - rMtime);
sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0);
if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0;
| > | | 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 |
const char *zMergeTo = db_column_text(&q, 3);
int nCkin = db_column_int(&q, 4);
const char *zLastCkin = db_column_text(&q, 5);
const char *zBgClr = db_column_text(&q, 6);
char *zAge = human_readable_age(rNow - rMtime);
sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0);
if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0;
if( zBgClr ) zBgClr = reasonable_bg_color(zBgClr, 0);
if( zBgClr==0 ){
if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){
zBgClr = 0;
}else{
zBgClr = hash_color(zBranch);
}
}
if( zBgClr && zBgClr[0] && show_colors ){
|
| ︙ | ︙ |
Changes to src/browse.c.
| ︙ | ︙ | |||
203 204 205 206 207 208 209 |
int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
linkTrunk = trunkRid && rid != trunkRid;
linkTip = rid != symbolic_name_to_rid("tip", "ci");
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
isBranchCI = branch_includes_uuid(zCI, zUuid);
if( bDocDir ) zCI = mprintf("%S", zUuid);
| | | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
linkTrunk = trunkRid && rid != trunkRid;
linkTip = rid != symbolic_name_to_rid("tip", "ci");
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
isBranchCI = branch_includes_uuid(zCI, zUuid);
if( bDocDir ) zCI = mprintf("%S", zUuid);
Th_StoreUnsafe("current_checkin", zCI);
}else{
zCI = 0;
}
}
assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
if( zD==0 ){
|
| ︙ | ︙ | |||
769 770 771 772 773 774 775 |
linkTip = rid != symbolic_name_to_rid("tip", "ci");
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
zNow = db_text("", "SELECT datetime(mtime,toLocal())"
" FROM event WHERE objid=%d", rid);
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
isBranchCI = branch_includes_uuid(zCI, zUuid);
| | | 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 |
linkTip = rid != symbolic_name_to_rid("tip", "ci");
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
zNow = db_text("", "SELECT datetime(mtime,toLocal())"
" FROM event WHERE objid=%d", rid);
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
isBranchCI = branch_includes_uuid(zCI, zUuid);
Th_StoreUnsafe("current_checkin", zCI);
}else{
zCI = 0;
}
}
if( zCI==0 ){
rNow = db_double(0.0, "SELECT max(mtime) FROM event");
zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
|
| ︙ | ︙ |
Changes to src/builtin.c.
| ︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
}
}
return -1;
}
/*
** Return a pointer to built-in content
*/
const unsigned char *builtin_file(const char *zFilename, int *piSize){
int i = builtin_file_index(zFilename);
if( i>=0 ){
if( piSize ) *piSize = aBuiltinFiles[i].nByte;
return aBuiltinFiles[i].pData;
}else{
if( piSize ) *piSize = 0;
return 0;
}
}
const char *builtin_text(const char *zFilename){
return (char*)builtin_file(zFilename, 0);
}
| > > > > > > > > > > > > > > > > | 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 |
}
}
return -1;
}
/*
** Return a pointer to built-in content
**
** If the filename contains "-vNNNNNNNN" just before the final file
** suffix, where each N is a random digit, then omit that part of the
** filename before doing the lookup. The extra -vNNNNNNNN was added
** to defeat overly aggressive caching by web browsers. There must be
** at least 8 digits in NNNNNNNN but more than 8 are allowed.
*/
const unsigned char *builtin_file(const char *zFilename, int *piSize){
int i = builtin_file_index(zFilename);
if( i>=0 ){
if( piSize ) *piSize = aBuiltinFiles[i].nByte;
return aBuiltinFiles[i].pData;
}else{
char *zV = strstr(zFilename, "-v");
if( zV!=0 ){
for(i=0; fossil_isdigit(zV[i+2]); i++){}
if( i>=8 && zV[i+2]=='.' ){
char *zNew = mprintf("%.*s%s", (int)(zV-zFilename), zFilename, zV+i+2);
const unsigned char *pRes = builtin_file(zNew, piSize);
fossil_free(zNew);
return pRes;
}
}
if( piSize ) *piSize = 0;
return 0;
}
}
const char *builtin_text(const char *zFilename){
return (char*)builtin_file(zFilename, 0);
}
|
| ︙ | ︙ |
Changes to src/cache.c.
| ︙ | ︙ | |||
253 254 255 256 257 258 259 |
** database already exists.
*/
void cache_initialize(void){
sqlite3_close(cacheOpen(1));
}
/*
| | | 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
** database already exists.
*/
void cache_initialize(void){
sqlite3_close(cacheOpen(1));
}
/*
** COMMAND: cache* abbrv-subcom
**
** Usage: %fossil cache SUBCOMMAND
**
** Manage the cache used for potentially expensive web pages such as
** /zip and /tarball. SUBCOMMAND can be:
**
** clear Remove all entries from the cache.
|
| ︙ | ︙ | |||
397 398 399 400 401 402 403 |
/*
** WEBPAGE: cachestat
**
** Show information about the webpage cache. Requires Setup privilege.
*/
void cache_page(void){
| | > > > > > > | | < < | > > < > > > > | | | | | > | | > > | < | | > > > > > > | > > > | > | | > > | | > > > | > | | < | 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 |
/*
** WEBPAGE: cachestat
**
** Show information about the webpage cache. Requires Setup privilege.
*/
void cache_page(void){
sqlite3 *db = 0;
sqlite3_stmt *pStmt;
int doInit;
char *zDbName = cacheName();
int nEntry = 0;
int mxEntry = 0;
char zBuf[100];
login_check_credentials();
if( !g.perm.Setup ){ login_needed(0); return; }
style_set_current_feature("cache");
style_header("Web Cache Status");
style_submenu_element("Refresh","%R/cachestat");
doInit = P("init")!=0 && cgi_csrf_safe(2);
db = cacheOpen(doInit);
if( db!=0 ){
if( P("clear")!=0 && cgi_csrf_safe(2) ){
sqlite3_exec(db, "DELETE FROM cache; DELETE FROM blob; VACUUM;",0,0,0);
}
cache_register_sizename(db);
pStmt = cacheStmt(db,
"SELECT key, sz, nRef, datetime(tm,'unixepoch')"
" FROM cache"
" ORDER BY (tm + 3600*min(nRef,48)) DESC"
);
if( pStmt ){
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const unsigned char *zName = sqlite3_column_text(pStmt,0);
char *zHash = cache_hash_of_key((const char*)zName);
if( nEntry==0 ){
@ <h2>Current Cache Entries:</h2>
@ <ol>
}
@ <li><p>%z(href("%R/cacheget?key=%T",zName))%h(zName)</a><br>
@ size: %,lld(sqlite3_column_int64(pStmt,1)),
@ hit-count: %d(sqlite3_column_int(pStmt,2)),
@ last-access: %s(sqlite3_column_text(pStmt,3))Z \
if( zHash ){
@ → %z(href("%R/timeline?c=%S",zHash))checkin info</a>\
fossil_free(zHash);
}
@ </p></li>
nEntry++;
}
sqlite3_finalize(pStmt);
if( nEntry ){
@ </ol>
}
}
}
@ <h2>About The Web-Cache</h2>
@ <p>
@ The web-cache is a separate database file that holds cached copies
@ tarballs, ZIP archives, and other pages that are expensive to compute
@ and are likely to be reused.
@ <form method="post">
login_insert_csrf_secret();
@ <ul>
if( db==0 ){
@ <li> Web-cache is currently disabled.
@ <input type="submit" name="init" value="Enable">
}else{
bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE));
mxEntry = db_get_int("max-cache-entry",10);
@ <li> Filename of the cache database: <b>%h(zDbName)</b>
@ <li> Size of the cache database: %s(zBuf)
@ <li> Maximum number of entries: %d(mxEntry)
@ <li> Number of cache entries used: %d(nEntry)
@ <li> Change the max-cache-entry setting on the
@ <a href="%R/setup_settings">Settings</a> page to adjust the
@ maximum number of entries in the cache.
@ <li><input type="submit" name="clear" value="Clear the cache">
@ <li> Disable the cache by manually deleting the cache database file.
}
@ </ul>
@ </form>
fossil_free(zDbName);
if( db ) sqlite3_close(db);
style_finish_page();
}
/*
** WEBPAGE: cacheget
**
** Usage: /cacheget?key=KEY
|
| ︙ | ︙ |
Changes to src/cgi.c.
| ︙ | ︙ | |||
70 71 72 73 74 75 76 77 78 79 80 81 82 83 | # 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 | > | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | # endif # include <winsock2.h> # include <ws2tcpip.h> #else # include <sys/socket.h> # include <sys/un.h> # include <netinet/in.h> # include <netdb.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 |
| ︙ | ︙ | |||
101 102 103 104 105 106 107 | #define P(x) cgi_parameter((x),0) #define PD(x,y) cgi_parameter((x),(y)) #define PT(x) cgi_parameter_trimmed((x),0) #define PDT(x,y) cgi_parameter_trimmed((x),(y)) #define PB(x) cgi_parameter_boolean(x) #define PCK(x) cgi_parameter_checked(x,1) #define PIF(x,y) cgi_parameter_checked(x,y) | | | | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | #define P(x) cgi_parameter((x),0) #define PD(x,y) cgi_parameter((x),(y)) #define PT(x) cgi_parameter_trimmed((x),0) #define PDT(x,y) cgi_parameter_trimmed((x),(y)) #define PB(x) cgi_parameter_boolean(x) #define PCK(x) cgi_parameter_checked(x,1) #define PIF(x,y) cgi_parameter_checked(x,y) #define P_NoBot(x) cgi_parameter_no_attack((x),0) #define PD_NoBot(x,y) cgi_parameter_no_attack((x),(y)) /* ** Shortcut for the cgi_printf() routine. Instead of using the ** ** @ ... ** ** notation provided by the translate.c utility, you can also |
| ︙ | ︙ | |||
502 503 504 505 506 507 508 |
if( etag_tag()[0]!=0
&& iReplyStatus==200
&& strcmp(zContentType,"text/html")!=0
){
/* Do not cache HTML replies as those will have been generated and
** will likely, therefore, contains a nonce and we want that nonce to
** be different every time. */
| | | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 |
if( etag_tag()[0]!=0
&& iReplyStatus==200
&& strcmp(zContentType,"text/html")!=0
){
/* Do not cache HTML replies as those will have been generated and
** will likely, therefore, contains a nonce and we want that nonce to
** be different every time. */
blob_appendf(&hdr, "ETag: \"%s\"\r\n", etag_tag());
blob_appendf(&hdr, "Cache-Control: max-age=%d\r\n", etag_maxage());
if( etag_mtime()>0 ){
blob_appendf(&hdr, "Last-Modified: %s\r\n",
cgi_rfc822_datestamp(etag_mtime()));
}
}else if( g.isConst ){
/* isConst means that the reply is guaranteed to be invariant, even
|
| ︙ | ︙ | |||
635 636 637 638 639 640 641 642 643 644 645 646 647 648 |
cgi_reset_content();
cgi_printf("<html>\n<p>Redirect to %h</p>\n</html>\n", zLocation);
cgi_set_status(iStat, zStat);
free(zLocation);
cgi_reply();
fossil_exit(0);
}
NORETURN void cgi_redirect(const char *zURL){
cgi_redirect_with_status(zURL, 302, "Moved Temporarily");
}
NORETURN void cgi_redirect_with_method(const char *zURL){
cgi_redirect_with_status(zURL, 307, "Temporary Redirect");
}
NORETURN void cgi_redirectf(const char *zFormat, ...){
| > > > | 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 |
cgi_reset_content();
cgi_printf("<html>\n<p>Redirect to %h</p>\n</html>\n", zLocation);
cgi_set_status(iStat, zStat);
free(zLocation);
cgi_reply();
fossil_exit(0);
}
NORETURN void cgi_redirect_perm(const char *zURL){
cgi_redirect_with_status(zURL, 301, "Moved Permanently");
}
NORETURN void cgi_redirect(const char *zURL){
cgi_redirect_with_status(zURL, 302, "Moved Temporarily");
}
NORETURN void cgi_redirect_with_method(const char *zURL){
cgi_redirect_with_status(zURL, 307, "Temporary Redirect");
}
NORETURN void cgi_redirectf(const char *zFormat, ...){
|
| ︙ | ︙ | |||
686 687 688 689 690 691 692 693 | } return zRef; } /* ** Return true if the current request is coming from the same origin. */ | > > > > | > > > > > > > | > | > > > > > > > > | | 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 |
}
return zRef;
}
/*
** Return true if the current request is coming from the same origin.
**
** If the request comes from a different origin and bErrorLog is true, then
** put a warning message on the error log as this was a possible hack
** attempt.
*/
int cgi_same_origin(int bErrorLog){
const char *zRef;
char *zToFree = 0;
int nBase;
int rc;
if( g.zBaseURL==0 ) return 0;
zRef = P("HTTP_REFERER");
if( zRef==0 ) return 0;
if( strchr(zRef,'%')!=0 ){
zToFree = strdup(zRef);
dehttpize(zToFree);
zRef = zToFree;
}
nBase = (int)strlen(g.zBaseURL);
if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ){
rc = 0;
}else if( zRef[nBase]!=0 && zRef[nBase]!='/' ){
rc = 0;
}else{
rc = 1;
}
if( rc==0 && bErrorLog && fossil_strcmp(P("REQUST_METHOD"),"POST")==0 ){
fossil_errorlog("warning: POST from different origin");
}
fossil_free(zToFree);
return rc;
}
/*
** Return true if the current CGI request is a POST request
*/
static int cgi_is_post_request(void){
const char *zMethod = P("REQUEST_METHOD");
|
| ︙ | ︙ | |||
731 732 733 734 735 736 737 |
** 1: Request comes from the same origin
** 2: (1) plus it is a POST request
** 3: (2) plus there is a valid "csrf" token in the request
*/
int cgi_csrf_safe(int securityLevel){
if( g.okCsrf<0 ) return 0;
if( g.okCsrf==0 ){
| | | 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 |
** 1: Request comes from the same origin
** 2: (1) plus it is a POST request
** 3: (2) plus there is a valid "csrf" token in the request
*/
int cgi_csrf_safe(int securityLevel){
if( g.okCsrf<0 ) return 0;
if( g.okCsrf==0 ){
if( !cgi_same_origin(1) ){
g.okCsrf = -1;
}else{
g.okCsrf = 1;
if( cgi_is_post_request() ){
g.okCsrf = 2;
if( fossil_strcmp(P("csrf"), g.zCsrfToken)==0 ){
g.okCsrf = 3;
|
| ︙ | ︙ | |||
1598 1599 1600 1601 1602 1603 1604 |
cgi_set_status(418,"I'm a teapot");
cgi_reply();
fossil_errorlog("Xpossible hack attempt - 418 response on \"%s\"", zName);
exit(0);
}
/*
| | | | | | | | > > | | | 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 |
cgi_set_status(418,"I'm a teapot");
cgi_reply();
fossil_errorlog("Xpossible hack attempt - 418 response on \"%s\"", zName);
exit(0);
}
/*
** If looks_like_attack() returns true for the given string, call
** cgi_begone_spider() and does not return, else this function has no
** side effects. The range of checks performed by this function may
** be extended in the future.
**
** Checks are omitted for any logged-in user.
**
** This is the primary defense against attack. Fossil should easily be
** proof against SQL injection and XSS attacks even without without this
** routine. Rather, this is an attempt to avoid denial-of-service caused
** by persistent spiders that hammer the server with dozens or hundreds of
** probes per seconds as they look for vulnerabilities. In other
** words, this is an effort to reduce the CPU load imposed by malicious
** spiders. Though those routine might help make attacks harder, it is
** not itself an impenetrably barrier against attack and should not be
** relied upon as the only defense.
*/
void cgi_value_spider_check(const char *zTxt, const char *zName){
if( g.zLogin==0 && looks_like_attack(zTxt) ){
cgi_begone_spider(zName);
}
}
/*
** A variant of cgi_parameter() with the same semantics except that if
** cgi_parameter(zName,zDefault) returns a value other than zDefault
** then it passes that value to cgi_value_spider_check().
*/
const char *cgi_parameter_no_attack(const char *zName, const char *zDefault){
const char *zTxt = cgi_parameter(zName, zDefault);
if( zTxt!=zDefault ){
cgi_value_spider_check(zTxt, zName);
}
return zTxt;
}
|
| ︙ | ︙ | |||
1776 1777 1778 1779 1780 1781 1782 |
*/
void cgi_load_environment(void){
/* The following is a list of environment variables that Fossil considers
** to be "relevant". */
static const char *const azCgiVars[] = {
"COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "SCGI",
"HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", "HTTP_ACCEPT_ENCODING",
| | | 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 |
*/
void cgi_load_environment(void){
/* The following is a list of environment variables that Fossil considers
** to be "relevant". */
static const char *const azCgiVars[] = {
"COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "SCGI",
"HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", "HTTP_ACCEPT_ENCODING",
"HTTP_ACCEPT_LANGUAGE", "HTTP_AUTHENTICATION",
"HTTP_CONNECTION", "HTTP_HOST",
"HTTP_IF_NONE_MATCH", "HTTP_IF_MODIFIED_SINCE",
"HTTP_USER_AGENT", "HTTP_REFERER", "PATH_INFO", "PATH_TRANSLATED",
"QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT",
"REMOTE_USER", "REQUEST_METHOD", "REQUEST_SCHEME",
"REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_NAME",
"SERVER_PROTOCOL", "HOME", "FOSSIL_HOME", "USERNAME", "USER",
|
| ︙ | ︙ | |||
2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 |
zInput++;
while( fossil_isspace(*zInput) ){ zInput++; }
}
if( zLeftOver ){ *zLeftOver = zInput; }
return zResult;
}
/*
** Determine the IP address on the other side of a connection.
** Return a pointer to a string. Or return 0 if unable.
**
** The string is held in a static buffer that is overwritten on
** each call.
*/
char *cgi_remote_ip(int fd){
| > > > > > > > > > > > | > | < < | | > | > | < < < < < < | 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 |
zInput++;
while( fossil_isspace(*zInput) ){ zInput++; }
}
if( zLeftOver ){ *zLeftOver = zInput; }
return zResult;
}
/*
** All possible forms of an IP address. Needed to work around GCC strict
** aliasing rules.
*/
typedef union {
struct sockaddr sa; /* Abstract superclass */
struct sockaddr_in sa4; /* IPv4 */
struct sockaddr_in6 sa6; /* IPv6 */
struct sockaddr_storage sas; /* Should be the maximum of the above 3 */
} address;
/*
** Determine the IP address on the other side of a connection.
** Return a pointer to a string. Or return 0 if unable.
**
** The string is held in a static buffer that is overwritten on
** each call.
*/
char *cgi_remote_ip(int fd){
address remoteAddr;
socklen_t size = sizeof(remoteAddr);
static char zHost[NI_MAXHOST];
if( getpeername(0, &remoteAddr.sa, &size) ){
return 0;
}
if( getnameinfo(&remoteAddr.sa, size, zHost, sizeof(zHost), 0, 0,
NI_NUMERICHOST) ){
return 0;
}
return zHost;
}
/*
** This routine handles a single HTTP request which is coming in on
** g.httpIn and which replies on g.httpOut
**
** The HTTP request is read from g.httpIn and is used to initialize
|
| ︙ | ︙ | |||
2104 2105 2106 2107 2108 2109 2110 |
if( zToken==0 ){
malformed_request("malformed HTTP header");
}
if( fossil_strcmp(zToken,"GET")!=0
&& fossil_strcmp(zToken,"POST")!=0
&& fossil_strcmp(zToken,"HEAD")!=0
){
| | | 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 |
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");
|
| ︙ | ︙ | |||
2464 2465 2466 2467 2468 2469 2470 |
nHdr -= m+1;
}
fossil_free(zToFree);
fgetc(g.httpIn); /* Read past the "," separating header from content */
cgi_init();
}
| < | 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 |
nHdr -= m+1;
}
fossil_free(zToFree);
fgetc(g.httpIn); /* Read past the "," separating header from content */
cgi_init();
}
#if INTERFACE
/*
** Bitmap values for the flags parameter to cgi_http_server().
*/
#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? */
|
| ︙ | ︙ | |||
2507 2508 2509 2510 2511 2512 2513 |
const char *zIpAddr, /* Bind to this IP address, if not null */
int flags /* HTTP_SERVER_* flags */
){
#if defined(_WIN32)
/* Use win32_http_server() instead */
fossil_exit(1);
#else
| > > | | > | > | > > > > > > > > | | > > > | | | | | | | | | | | | | | | | | | | | | > > > | | | | | | | | < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | > > > | > > > | > > > > > > > > > | | > > > > > | > > > > | > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > | > > > > | < < < > | > > > | > > > > > > > > | > | > > > > > > > > > > > > | > > > > > | | > > > | < < < > | > > > > | > | > > > > > > | | > | > > > > > > > > > > | | > | | | > > > > > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | | | < | 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 |
const char *zIpAddr, /* Bind to this IP address, if not null */
int flags /* HTTP_SERVER_* flags */
){
#if defined(_WIN32)
/* Use win32_http_server() instead */
fossil_exit(1);
#else
int listen4 = -1; /* Main socket; IPv4 or unix-domain */
int listen6 = -1; /* Aux socket for corresponding IPv6 */
int mxListen = -1; /* Maximum of listen4 and listen6 */
int connection; /* An incoming connection */
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_in6 inaddr6; /* Address for IPv6 */
struct sockaddr_in inaddr4; /* Address for IPv4 */
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 */
const char *zRequestType; /* Type of requests to listen for */
if( flags & HTTP_SERVER_SCGI ){
zRequestType = "SCGI";
}else if( g.httpUseSSL ){
zRequestType = "TLS-encrypted HTTPS";
}else{
zRequestType = "HTTP";
}
if( flags & HTTP_SERVER_UNIXSOCKET ){
/* CASE 1: A unix socket named g.zSockName. After creation, set the
** permissions on the new socket to g.zSockMode and make the
** owner of the socket be g.zSockOwner.
*/
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);
listen4 = socket(AF_UNIX, SOCK_STREAM, 0);
if( listen4<0 ){
fossil_fatal("unable to create a unix socket named %s",
g.zSockName);
}
mxListen = listen4;
listen6 = -1;
/* 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, listen4, g.zSockMode, 0);
}else{
file_set_mode(g.zSockName, listen4, "0660", 1);
}
rc = bind(listen4, (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, listen4, g.zSockOwner);
}
fossil_print("Listening for %s requests on unix socket %s\n",
zRequestType, g.zSockName);
fflush(stdout);
}else if( zIpAddr && strchr(zIpAddr,':')!=0 ){
/* CASE 2: TCP on IPv6 IP address specified by zIpAddr and on port iPort.
*/
assert( mnPort==mxPort );
memset(&inaddr6, 0, sizeof(inaddr6));
inaddr6.sin6_family = AF_INET6;
inaddr6.sin6_port = htons(iPort);
if( inet_pton(AF_INET6, zIpAddr, &inaddr6.sin6_addr)==0 ){
fossil_fatal("not a valid IPv6 address: %s", zIpAddr);
}
listen6 = socket(AF_INET6, SOCK_STREAM, 0);
if( listen6>0 ){
opt = 1;
setsockopt(listen6, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
rc = bind(listen6, (struct sockaddr*)&inaddr6, sizeof(inaddr6));
if( rc<0 ){
close(listen6);
listen6 = -1;
}
}
if( listen6<0 ){
fossil_fatal("cannot open a listening socket on [%s]:%d",
zIpAddr, mnPort);
}
mxListen = listen6;
listen4 = -1;
fossil_print("Listening for %s requests on [%s]:%d\n",
zRequestType, zIpAddr, iPort);
fflush(stdout);
}else if( zIpAddr && zIpAddr[0] ){
/* CASE 3: TCP on IPv4 IP address specified by zIpAddr and on port iPort.
*/
assert( mnPort==mxPort );
memset(&inaddr4, 0, sizeof(inaddr4));
inaddr4.sin_family = AF_INET;
inaddr4.sin_port = htons(iPort);
if( strcmp(zIpAddr, "localhost")==0 ) zIpAddr = "127.0.0.1";
inaddr4.sin_addr.s_addr = inet_addr(zIpAddr);
if( inaddr4.sin_addr.s_addr == INADDR_NONE ){
fossil_fatal("not a valid IPv4 address: %s", zIpAddr);
}
listen4 = socket(AF_INET, SOCK_STREAM, 0);
if( listen4>0 ){
setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4));
if( rc<0 ){
close(listen4);
listen4 = -1;
}
}
if( listen4<0 ){
fossil_fatal("cannot open a listening socket on %s:%d",
zIpAddr, mnPort);
}
mxListen = listen4;
listen6 = -1;
fossil_print("Listening for %s requests on TCP port %s:%d\n",
zRequestType, zIpAddr, iPort);
fflush(stdout);
}else{
/* CASE 4: Listen on all available IP addresses, or on only loopback
** addresses (if HTTP_SERVER_LOCALHOST). The TCP port is the
** first available in the range of mnPort..mxPort. Listen
** on both IPv4 and IPv6, if possible. The TCP port scan is done
** on IPv4.
*/
while( iPort<=mxPort ){
const char *zProto;
memset(&inaddr4, 0, sizeof(inaddr4));
inaddr4.sin_family = AF_INET;
inaddr4.sin_port = htons(iPort);
if( flags & HTTP_SERVER_LOCALHOST ){
inaddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}else{
inaddr4.sin_addr.s_addr = htonl(INADDR_ANY);
}
listen4 = socket(AF_INET, SOCK_STREAM, 0);
if( listen4>0 ){
setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4));
if( rc<0 ){
close(listen4);
listen4 = -1;
}
}
if( listen4<0 ){
iPort++;
continue;
}
mxListen = listen4;
/* If we get here, that means we found an open TCP port at iPort for
** IPv4. Try to set up a corresponding IPv6 socket on the same port.
*/
memset(&inaddr6, 0, sizeof(inaddr6));
inaddr6.sin6_family = AF_INET6;
inaddr6.sin6_port = htons(iPort);
if( flags & HTTP_SERVER_LOCALHOST ){
inaddr6.sin6_addr = in6addr_loopback;
}else{
inaddr6.sin6_addr = in6addr_any;
}
listen6 = socket(AF_INET6, SOCK_STREAM, 0);
if( listen6>0 ){
setsockopt(listen6, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(listen6, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
rc = bind(listen6, (struct sockaddr*)&inaddr6, sizeof(inaddr6));
if( rc<0 ){
close(listen6);
listen6 = -1;
}
}
if( listen6<0 ){
zProto = "IPv4 only";
}else{
zProto = "IPv4 and IPv6";
if( listen6>listen4 ) mxListen = listen6;
}
fossil_print("Listening for %s requests on TCP port %s%d, %s\n",
zRequestType,
(flags & HTTP_SERVER_LOCALHOST)!=0 ? "localhost:" : "",
iPort, zProto);
fflush(stdout);
break;
}
if( iPort>mxPort ){
fossil_fatal("no available TCP ports in the range %d..%d",
mnPort, mxPort);
}
}
/* If we get to this point, that means there is at least one listening
** socket on either listen4 or listen6 and perhaps on both. */
assert( listen4>0 || listen6>0 );
if( listen4>0 ) listen(listen4,10);
if( listen6>0 ) listen(listen6,10);
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 " &" */
if( (size_t)ShellExecuteW(0, L"open", wUrl, 0, 0, 1)<33 ){
fossil_warning("cannot start browser\n");
}
}else
#endif
if( fossil_system(zBrowser)<0 ){
fossil_warning("cannot start browser: %s\n", zBrowser);
}
}
/* What for incomming requests. For each request, fork() a child process
** to deal with that request. The child process returns. The parent
** keeps on listening and never returns.
*/
while( 1 ){
#if FOSSIL_MAX_CONNECTIONS>0
while( nchildren>=FOSSIL_MAX_CONNECTIONS ){
if( wait(0)>=0 ) nchildren--;
}
#endif
delay.tv_sec = 0;
delay.tv_usec = 100000;
FD_ZERO(&readfds);
assert( listen4>0 || listen6>0 );
if( listen4>0 ) FD_SET( listen4, &readfds);
if( listen6>0 ) FD_SET( listen6, &readfds);
select( mxListen+1, &readfds, 0, 0, &delay);
if( listen4>0 && FD_ISSET(listen4, &readfds) ){
lenaddr = sizeof(inaddr4);
connection = accept(listen4, (struct sockaddr*)&inaddr4, &lenaddr);
}else if( listen6>0 && FD_ISSET(listen6, &readfds) ){
lenaddr = sizeof(inaddr6);
connection = accept(listen6, (struct sockaddr*)&inaddr6, &lenaddr);
}else{
connection = -1;
}
if( connection>=0 ){
if( flags & HTTP_SERVER_NOFORK ){
child = 0;
}else{
child = fork();
}
if( child!=0 ){
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 ){
close(2);
fd = dup(connection);
if( fd!=2 ) nErr++;
}
close(connection);
if( listen4>0 ) close(listen4);
if( listen6>0 ) close(listen6);
g.nPendingRequest = nchildren+1;
g.nRequest = nRequest+1;
return nErr;
}
}
/* Bury dead children */
if( nchildren ){
while(1){
int iStatus = 0;
pid_t x = waitpid(-1, &iStatus, WNOHANG);
|
| ︙ | ︙ |
Changes to src/chat.c.
| ︙ | ︙ | |||
252 253 254 255 256 257 258 |
@ window.addEventListener('load', function(){
@ document.body.classList.add('chat');
@ /*^^^for skins which add their own BODY tag */;
@ window.fossil.config.chat = {
@ fromcli: %h(PB("cli")?"true":"false"),
@ alertSound: "%h(zAlert)",
@ initSize: %d(db_get_int("chat-initial-history",50)),
| | > | 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
@ window.addEventListener('load', function(){
@ document.body.classList.add('chat');
@ /*^^^for skins which add their own BODY tag */;
@ window.fossil.config.chat = {
@ fromcli: %h(PB("cli")?"true":"false"),
@ alertSound: "%h(zAlert)",
@ initSize: %d(db_get_int("chat-initial-history",50)),
@ imagesInline: !!%d(db_get_boolean("chat-inline-images",1)),
@ pollTimeout: %d(db_get_int("chat-poll-timeout",420))
@ };
ajax_emit_js_preview_modes(0);
chat_emit_alert_list();
@ }, false);
@ </script>
builtin_request_js("fossil.page.chat.js");
style_finish_page();
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 |
Blob *pComment,
char *zInit,
CheckinInfo *p,
int parent_rid,
int dryRunFlag
){
Blob prompt;
#if defined(_WIN32) || defined(__CYGWIN__)
int bomSize;
const unsigned char *bom = get_utf8_bom(&bomSize);
blob_init(&prompt, (const char *) bom, bomSize);
if( zInit && zInit[0]){
blob_append(&prompt, zInit, -1);
}
#else
blob_init(&prompt, zInit, -1);
#endif
blob_append(&prompt,
"\n"
| > | | | > > > > > > > > > > > > > > > > > > > > > > > | 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 |
Blob *pComment,
char *zInit,
CheckinInfo *p,
int parent_rid,
int dryRunFlag
){
Blob prompt;
int wikiFlags;
#if defined(_WIN32) || defined(__CYGWIN__)
int bomSize;
const unsigned char *bom = get_utf8_bom(&bomSize);
blob_init(&prompt, (const char *) bom, bomSize);
if( zInit && zInit[0]){
blob_append(&prompt, zInit, -1);
}
#else
blob_init(&prompt, zInit, -1);
#endif
blob_append(&prompt,
"\n"
"# Enter the commit message. Formatting rules:\n"
"# * Lines beginning with # are ignored.\n",
-1
);
wikiFlags = wiki_convert_flags(1);
if( wikiFlags & WIKI_LINKSONLY ){
blob_append(&prompt,"# * Hyperlinks inside of [...]\n", -1);
if( wikiFlags & WIKI_NEWLINE ){
blob_append(&prompt,
"# * Newlines are significant and are displayed as written\n", -1);
}else{
blob_append(&prompt,
"# * Newlines are interpreted as ordinary spaces\n",
-1
);
}
blob_append(&prompt,
"# * All other text will be displayed as written\n", -1);
}else{
blob_append(&prompt,
"# * Hyperlinks: [target] or [target|display-text]\n"
"# * Blank lines cause a paragraph break\n"
"# * Other text rendered as if it where HTML\n", -1
);
}
blob_append(&prompt, "#\n", 2);
if( dryRunFlag ){
blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes "
"will be made to the repository\n#\n");
}
blob_appendf(&prompt, "# user: %s\n",
p->zUserOvrd ? p->zUserOvrd : login_name());
if( p->zBranch && p->zBranch[0] ){
|
| ︙ | ︙ | |||
2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 |
*/
static int tagCmp(const void *a, const void *b){
char **pA = (char**)a;
char **pB = (char**)b;
return fossil_strcmp(pA[0], pB[0]);
}
/*
** COMMAND: ci#
** COMMAND: commit
**
** Usage: %fossil commit ?OPTIONS? ?FILE...?
** or: %fossil ci ?OPTIONS? ?FILE...?
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | < | | < < > | | | < < < < < < < < < < < < < < < < | < > | > > > | > > | | < | > > | > > | | < < | < < < | 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 |
*/
static int tagCmp(const void *a, const void *b){
char **pA = (char**)a;
char **pB = (char**)b;
return fossil_strcmp(pA[0], pB[0]);
}
/*
** SETTING: verify-comments width=8 default=on
**
** This setting determines how much sanity checking, if any, the
** "fossil commit" and "fossil amend" commands do against check-in
** comments. Recognized values:
**
** on (Default) Check for bad syntax and/or broken hyperlinks
** in check-in comments and offer the user a chance to
** continue editing for interactive sessions, or simply
** abort the commit if the comment was entered using -m or -M
**
** off Do not do syntax checking of any kind
**
** preview Do all the same checks as "on" but also always preview the
** check-in comment to the user during interactive sessions
** even if no obvious errors are found, and provide an
** opportunity to accept or re-edit
*/
#if INTERFACE
#define COMCK_MARKUP 0x01 /* Check for mistakes */
#define COMCK_PREVIEW 0x02 /* Always preview, even if no issues found */
#endif /* INTERFACE */
/*
** Check for possible formatting errors in the comment string pComment.
**
** If issues are found, write an appropriate error notice, probably also
** including the complete text of the comment formatted to highlight the
** problem, to stdout and return non-zero. The return value is some
** combination of the COMCK_* flags, depending on what went wrong.
**
** If no issues are seen, do not output anything and return zero.
*/
int verify_comment(Blob *pComment, int mFlags){
Blob in, html;
int mResult;
int rc = mFlags & COMCK_PREVIEW;
int wFlags;
if( mFlags==0 ) return 0;
blob_init(&in, blob_str(pComment), -1);
blob_init(&html, 0, 0);
wFlags = wiki_convert_flags(0);
wFlags &= ~WIKI_NOBADLINKS;
wFlags |= WIKI_MARK;
mResult = wiki_convert(&in, &html, wFlags);
if( mResult & RENDER_ANYERROR ) rc |= COMCK_MARKUP;
if( rc ){
int htot = ((wFlags & WIKI_NEWLINE)!=0 ? 0 : HTOT_FLOW)|HTOT_TRIM;
Blob txt;
if( terminal_is_vt100() ) htot |= HTOT_VT100;
blob_init(&txt, 0, 0);
html_to_plaintext(blob_str(&html), &txt, htot);
if( rc & COMCK_MARKUP ){
fossil_print("Possible format errors in the check-in comment:\n\n ");
}else{
fossil_print("Preview of the check-in comment:\n\n ");
}
if( wFlags & WIKI_NEWLINE ){
Blob line;
char *zIndent = "";
while( blob_line(&txt, &line) ){
fossil_print("%s%b", zIndent, &line);
zIndent = " ";
}
fossil_print("\n");
}else{
comment_print(blob_str(&txt), 0, 3, -1, get_comment_format());
}
fossil_print("\n");
fflush(stdout);
blob_reset(&txt);
}
blob_reset(&html);
blob_reset(&in);
return rc;
}
/*
** COMMAND: ci#
** COMMAND: commit
**
** Usage: %fossil commit ?OPTIONS? ?FILE...?
** or: %fossil ci ?OPTIONS? ?FILE...?
**
** Create a new check-in containing all of the changes in the current
** check-out. All changes are committed unless some subset of files
** is specified on the command line, in which case only the named files
** become part of the new check-in.
**
** You will be prompted to enter a check-in comment unless the comment
** has been specified on the command-line using "-m" or "-M". The
** text editor used is determined by the "editor" setting, or by the
** "VISUAL" or "EDITOR" environment variables. Commit message text is
** interpreted as fossil-wiki format. Potentially misformatted check-in
** comment text is detected and reported unless the --no-verify-comment
** option is used.
**
** The --branch option followed by a branch name causes the new
** check-in to be placed in a newly-created branch with name specified.
**
** A check-in is not permitted to fork unless the --allow-fork option
** appears. An empty check-in (i.e. with nothing changed) is not
** allowed unless the --allow-empty option appears. A check-in may not
** be older than its ancestor unless the --allow-older option appears.
** If any files in the check-in appear to contain unresolved merge
** conflicts, the check-in will not be allowed unless the
** --allow-conflict option is present. In addition, the entire
** check-in process may be aborted if a file contains content that
** appears to be binary, Unicode text, or text with CR/LF line endings
** unless the interactive user chooses to proceed. If there is no
** interactive user or these warnings should be skipped for some other
** reason, the --no-warnings option may be used. A check-in is not
** allowed against a closed leaf.
**
** The --private option creates a private check-in that is never synced.
** Children of private check-ins are automatically private.
**
** The --tag option applies the symbolic tag name to the check-in.
** The --tag option can be repeated to assign multiple tags to a check-in.
** For example: "... --tag release --tag version-1.2.3 ..."
**
** Options:
** --allow-conflict Allow unresolved merge conflicts
** --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 Make DATETIME the time of the check-in.
** Useful when importing historical check-ins
** from another version control system.
** --delta Use a delta manifest in the commit process
** --editor NAME Text editor to use for check-in comment.
** --hash Verify file status using hashing rather
** than relying on filesystem mtimes
** --if-changes Make this command a silent no-op if there
** are no changes
** --ignore-clock-skew If a clock skew is detected, ignore it and
** behave as if the user had entered 'yes' to
** the question of whether to proceed despite
** the skew.
** --ignore-oversize Do not warn the user about oversized files
** --integrate Close all merged-in branches
** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as the check-in comment
** -M|--message-file FILE Read the check-in comment from FILE
** -n|--dry-run Do not actually create a new check-in. Just
** show what would have happened. For debugging.
** -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
** --no-verify-comment Do not validate the check-in comment
** --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 Never sync the resulting check-in and make
** all descendants private too.
** --proxy PROXY Use PROXY as http proxy during sync operation
** --tag TAG-NAME Add TAG-NAME to the check-in. May be repeated.
** --trace Debug tracing
** --user-override USER Record USER as the login that created the
** new check-in, rather that the current user.
**
** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]]
*/
void commit_cmd(void){
int hasChanges; /* True if unsaved changes exist */
int vid; /* blob-id of parent version */
int nrid; /* blob-id of a modified file */
|
| ︙ | ︙ | |||
2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 | int bTrace = 0; /* Debug tracing */ int noPrompt = 0; /* True if skipping all prompts */ int forceFlag = 0; /* Undocumented: Disables all checks */ int forceDelta = 0; /* Force a delta-manifest */ int forceBaseline = 0; /* Force a baseline-manifest */ int allowConflict = 0; /* Allow unresolve merge conflicts */ int allowEmpty = 0; /* Allow a commit with no changes */ int allowFork = 0; /* Allow the commit to fork */ int allowOlder = 0; /* Allow a commit older than its ancestor */ char *zManifestFile; /* Name of the manifest file */ int useCksum; /* True if checksums should be computed and verified */ int outputManifest; /* True to output "manifest" and "manifest.uuid" */ int dryRunFlag; /* True for a test run. Debugging only */ CheckinInfo sCiInfo; /* Information about this check-in */ const char *zComFile; /* Read commit message from this file */ int nTag = 0; /* Number of --tag arguments */ | > > | 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 | int bTrace = 0; /* Debug tracing */ int noPrompt = 0; /* True if skipping all prompts */ int forceFlag = 0; /* Undocumented: Disables all checks */ int forceDelta = 0; /* Force a delta-manifest */ int forceBaseline = 0; /* Force a baseline-manifest */ int allowConflict = 0; /* Allow unresolve merge conflicts */ int allowEmpty = 0; /* Allow a commit with no changes */ int onlyIfChanges = 0; /* No-op if there are no changes */ int allowFork = 0; /* Allow the commit to fork */ int allowOlder = 0; /* Allow a commit older than its ancestor */ int noVerifyCom = 0; /* Allow suspicious check-in comments */ char *zManifestFile; /* Name of the manifest file */ int useCksum; /* True if checksums should be computed and verified */ int outputManifest; /* True to output "manifest" and "manifest.uuid" */ int dryRunFlag; /* True for a test run. Debugging only */ CheckinInfo sCiInfo; /* Information about this check-in */ const char *zComFile; /* Read commit message from this file */ int nTag = 0; /* Number of --tag arguments */ |
| ︙ | ︙ | |||
2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 |
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;
char *zCurBranch = 0; /* The current branch name of checkout */
char *zNewBranch = 0; /* The branch name after update */
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;
| > | 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 |
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;
char *zCurBranch = 0; /* The current branch name of checkout */
char *zNewBranch = 0; /* The branch name after update */
int ckComFlgs; /* Flags passed to verify_comment() */
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;
|
| ︙ | ︙ | |||
2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 |
if( !dryRunFlag ){
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
}
zComment = find_option("comment","m",1);
forceFlag = find_option("force", "f", 0)!=0;
allowConflict = find_option("allow-conflict",0,0)!=0;
allowEmpty = find_option("allow-empty",0,0)!=0;
allowFork = find_option("allow-fork",0,0)!=0;
if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
allowOlder = find_option("allow-older",0,0)!=0;
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);
| > > > > > > | | > > < | > > > > > > > | 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 |
if( !dryRunFlag ){
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
}
zComment = find_option("comment","m",1);
forceFlag = find_option("force", "f", 0)!=0;
allowConflict = find_option("allow-conflict",0,0)!=0;
allowEmpty = find_option("allow-empty",0,0)!=0;
noVerifyCom = find_option("no-verify-comment",0,0)!=0;
onlyIfChanges = find_option("if-changes",0,0)!=0;
allowFork = find_option("allow-fork",0,0)!=0;
if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
allowOlder = find_option("allow-older",0,0)!=0;
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);
/* NB: the --bgcolor and --branchcolor flags still work, but are
** now undocumented, to discourage their use. --mimetype has never
** been used for anything, so also leave it undocumented */
sCiInfo.zColor = find_option("bgcolor",0,1); /* Deprecated, undocumented*/
sCiInfo.zBrClr = find_option("branchcolor",0,1); /* Deprecated, undocumented*/
sCiInfo.zMimetype = find_option("mimetype",0,1); /* Deprecated, undocumented*/
sCiInfo.closeFlag = find_option("close",0,0)!=0;
sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
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,
sizeof(char*)*(nTag+2));
sCiInfo.azTag[nTag++] = zTag;
sCiInfo.azTag[nTag] = 0;
}
zComFile = find_option("message-file", "M", 1);
sCiInfo.zDateOvrd = find_option("date-override",0,1);
sCiInfo.zUserOvrd = find_option("user-override",0,1);
noSign = db_get_boolean("omitsign", 0)|noSign;
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
useCksum = db_get_boolean("repo-cksum", 1);
bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0;
outputManifest = db_get_manifest_setting(0);
mxSize = db_large_file_size();
if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0;
(void)fossil_text_editor();
verify_all_options();
/* The --no-warnings flag and the --force flag each imply
** the --no-verify-comment flag */
if( noWarningFlag || forceFlag ){
noVerifyCom = 1;
}
/* Get the ID of the parent manifest artifact */
vid = db_lget_int("checkout", 0);
if( vid==0 ){
useCksum = 1;
if( privateFlag==0 && sCiInfo.zBranch==0 ) {
sCiInfo.zBranch=db_get("main-branch", 0);
|
| ︙ | ︙ | |||
2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 |
/* Escape special characters in tags and put all tags in sorted order */
if( nTag ){
int i;
for(i=0; i<nTag; i++) sCiInfo.azTag[i] = mprintf("%F", sCiInfo.azTag[i]);
qsort((void*)sCiInfo.azTag, nTag, sizeof(sCiInfo.azTag[0]), tagCmp);
}
/*
** Autosync if autosync is enabled and this is not a private check-in.
*/
if( !g.markPrivate ){
int syncFlags = SYNC_PULL;
if( vid!=0 && !allowFork && !forceFlag ){
syncFlags |= SYNC_CKIN_LOCK;
}
if( autosync_loop(syncFlags, 1, "commit") ){
fossil_exit(1);
}
}
/* So that older versions of Fossil (that do not understand delta-
| > > > > > > | > > | > | | | | | < | | 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 |
/* Escape special characters in tags and put all tags in sorted order */
if( nTag ){
int i;
for(i=0; i<nTag; i++) sCiInfo.azTag[i] = mprintf("%F", sCiInfo.azTag[i]);
qsort((void*)sCiInfo.azTag, nTag, sizeof(sCiInfo.azTag[0]), tagCmp);
}
hasChanges = unsaved_changes(useHash ? CKSIG_HASH : 0);
if( hasChanges==0 && onlyIfChanges ){
/* "fossil commit --if-changes" is a no-op if there are no changes. */
return;
}
/*
** Autosync if autosync is enabled and this is not a private check-in.
*/
if( !g.markPrivate ){
int syncFlags = SYNC_PULL;
if( vid!=0 && !allowFork && !forceFlag ){
syncFlags |= SYNC_CKIN_LOCK;
}
if( autosync_loop(syncFlags, 1, "commit") ){
fossil_exit(1);
}
}
/* So that older versions of Fossil (that do not understand delta-
** manifest) can continue to use this repository, and because
** delta manifests are usually a bad idea unless the repository
** has a really large number of files, do not create a new
** delta-manifest unless this repository already contains one or more
** delta-manifests, or unless the delta-manifest is explicitly requested
** by the --delta option.
**
** The forbid-delta-manifests setting prevents new delta manifests,
** even if the --delta option is used.
**
** If the remote repository sent an avoid-delta-manifests pragma on
** the autosync above, then also forbid delta manifests, even if the
** --delta option is specified. The remote repo will send the
** avoid-delta-manifests pragma if its "forbid-delta-manifests"
** setting is enabled.
*/
if( !(forceDelta || db_get_boolean("seen-delta-manifest",0))
|| db_get_boolean("forbid-delta-manifests",0)
|| g.bAvoidDeltaManifests
){
forceBaseline = 1;
}
/* Require confirmation to continue with the check-in if there is
** clock skew. This helps to prevent timewarps.
*/
if( g.clockSkewSeen ){
if( bIgnoreSkew!=0 ){
cReply = 'y';
fossil_warning("Clock skew ignored due to --ignore-clock-skew.");
}else if( !noPrompt ){
prompt_user("continue in spite of time skew (y/N)? ", &ans);
|
| ︙ | ︙ | |||
2684 2685 2686 2687 2688 2689 2690 |
const char *zTo = db_column_text(&q, 1);
fossil_fatal("cannot do a partial commit of '%s' without '%s' because "
"'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo);
}
db_finalize(&q);
}
| < | 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 |
const char *zTo = db_column_text(&q, 1);
fossil_fatal("cannot do a partial commit of '%s' without '%s' because "
"'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo);
}
db_finalize(&q);
}
db_begin_transaction();
db_record_repository_filename(0);
if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
fossil_fatal("nothing has changed; use --allow-empty to override");
}
/* If none of the files that were named on the command line have
|
| ︙ | ︙ | |||
2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 |
zCurBranch = branch_of_rid(vid);
}
fossil_free(zNewBranch);
/* 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);
}else if( zComFile ){
blob_zero(&comment);
blob_read_from_file(&comment, zComFile, ExtFILE);
blob_to_utf8_no_bom(&comment, 1);
}else if( !noPrompt ){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > | | | | | | | | | > > | | 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 |
zCurBranch = branch_of_rid(vid);
}
fossil_free(zNewBranch);
/* Always exit the loop on the second pass */
if( bRecheck ) break;
/* Figure out how much comment verification is requested */
if( noVerifyCom ){
ckComFlgs = 0;
}else{
const char *zVerComs = db_get("verify-comments","on");
if( is_false(zVerComs) ){
ckComFlgs = 0;
}else if( strcmp(zVerComs,"preview")==0 ){
ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP;
}else{
ckComFlgs = COMCK_MARKUP;
}
}
/* 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);
ckComFlgs &= ~COMCK_PREVIEW;
if( verify_comment(&comment, ckComFlgs) ){
fossil_fatal("Commit aborted; "
"use --no-verify-comment to override");
}
}else if( zComFile ){
blob_zero(&comment);
blob_read_from_file(&comment, zComFile, ExtFILE);
blob_to_utf8_no_bom(&comment, 1);
ckComFlgs &= ~COMCK_PREVIEW;
if( verify_comment(&comment, ckComFlgs) ){
fossil_fatal("Commit aborted; "
"use --no-verify-comment to override");
}
}else if( !noPrompt ){
while( 1/*exit-by-break*/ ){
int rc;
char *zInit;
zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
if( (rc = verify_comment(&comment, ckComFlgs))!=0 ){
if( rc==COMCK_PREVIEW ){
prompt_user("Continue, abort, or edit? (C/a/e)? ", &ans);
}else{
prompt_user("Edit, abort, or continue (E/a/c)? ", &ans);
}
cReply = blob_str(&ans)[0];
cReply = fossil_tolower(cReply);
blob_reset(&ans);
if( cReply=='a' ){
fossil_fatal("Commit aborted.");
}
if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){
fossil_free(zInit);
continue;
}
}
if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
prompt_user("unchanged check-in comment. continue (y/N)? ", &ans);
cReply = blob_str(&ans)[0];
blob_reset(&ans);
if( cReply!='y' && cReply!='Y' ){
fossil_fatal("Commit aborted.");
}
}
fossil_free(zInit);
break;
}
db_end_transaction(0);
db_begin_transaction();
if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){
/* Do another auto-pull, renewing the check-in lock. Then set
** bRecheck so that we loop back above to verify that the check-in
** is still not against a closed branch and still won't fork. */
int syncFlags = SYNC_PULL|SYNC_CKIN_LOCK;
|
| ︙ | ︙ |
Changes to src/checkout.c.
| ︙ | ︙ | |||
169 170 171 172 173 174 175 |
** the text of the manifest and the artifact ID of the manifest.
** If the manifest setting is set, but is not a boolean value, then treat
** each character as a flag to enable writing "manifest", "manifest.uuid" or
** "manifest.tags".
*/
void manifest_to_disk(int vid){
char *zManFile;
| < < | | > | 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 |
** the text of the manifest and the artifact ID of the manifest.
** If the manifest setting is set, but is not a boolean value, then treat
** each character as a flag to enable writing "manifest", "manifest.uuid" or
** "manifest.tags".
*/
void manifest_to_disk(int vid){
char *zManFile;
int flg;
flg = db_get_manifest_setting(0);
if( flg & MFESTFLG_RAW ){
Blob manifest = BLOB_INITIALIZER;
content_get(vid, &manifest);
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
zManFile = mprintf("%smanifest", g.zLocalRoot);
blob_write_to_file(&manifest, zManFile);
free(zManFile);
blob_reset(&manifest);
}else{
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
zManFile = mprintf("%smanifest", g.zLocalRoot);
file_delete(zManFile);
free(zManFile);
}
}
|
| ︙ | ︙ | |||
205 206 207 208 209 210 211 |
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
file_delete(zManFile);
free(zManFile);
}
}
if( flg & MFESTFLG_TAGS ){
| | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
file_delete(zManFile);
free(zManFile);
}
}
if( flg & MFESTFLG_TAGS ){
Blob taglist = BLOB_INITIALIZER;
zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
get_checkin_taglist(vid, &taglist);
blob_write_to_file(&taglist, zManFile);
free(zManFile);
blob_reset(&taglist);
}else{
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.tags'") ){
|
| ︙ | ︙ | |||
275 276 277 278 279 280 281 | ** leaves files on disk unchanged, except the manifest and manifest.uuid ** files. ** ** The --latest flag can be used in place of VERSION to check-out the ** latest version in the repository. ** ** Options: | | | > < | > > > > > > | 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 |
** leaves files on disk unchanged, except the manifest and manifest.uuid
** files.
**
** The --latest flag can be used in place of VERSION to check-out the
** latest version in the repository.
**
** Options:
** -f|--force Ignore edited files in the current check-out
** -k|--keep Only update the manifest file(s)
** --force-missing Force check-out even if content is missing
** --prompt Prompt before overwriting when --force is used
** --setmtime Set timestamps of all files to match their SCM-side
** times (the timestamp of the last check-in which modified
** them)
**
** See also: [[update]]
*/
void checkout_cmd(void){
int forceFlag; /* Force check-out even if edits exist */
int forceMissingFlag; /* Force check-out even if missing content */
int keepFlag; /* Do not change any files on disk */
int latestFlag; /* Check out the latest version */
char *zVers; /* Version to check out */
int promptFlag; /* True to prompt before overwriting */
int vid, prior;
int setmtimeFlag; /* --setmtime. Set mtimes on files */
Blob cksum1, cksum1b, cksum2;
db_must_be_within_tree();
db_begin_transaction();
forceMissingFlag = find_option("force-missing",0,0)!=0;
keepFlag = find_option("keep","k",0)!=0;
forceFlag = find_option("force","f",0)!=0;
latestFlag = find_option("latest",0,0)!=0;
promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
setmtimeFlag = find_option("setmtime",0,0)!=0;
if( keepFlag != 0 ){
/* After flag collection, in order not to affect promptFlag */
forceFlag=1;
}
/* We should be done with options.. */
verify_all_options();
if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
usage("VERSION|--latest ?--force? ?--keep?");
}
|
| ︙ | ︙ |
Changes to src/clearsign.c.
| ︙ | ︙ | |||
27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
** pOut.
*/
int clearsign(Blob *pIn, Blob *pOut){
char *zRand;
char *zIn;
char *zOut;
char *zBase = db_get("pgp-command", "gpg --clearsign -o ");
char *zCmd;
int rc;
if( is_false(zBase) ){
return 0;
}
zRand = db_text(0, "SELECT hex(randomblob(10))");
zOut = mprintf("out-%s", zRand);
| > < > > > > > > | > > > > > > > > > > > > > > > > | > | 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 |
** pOut.
*/
int clearsign(Blob *pIn, Blob *pOut){
char *zRand;
char *zIn;
char *zOut;
char *zBase = db_get("pgp-command", "gpg --clearsign -o ");
int useSsh = 0;
char *zCmd;
int rc;
if( is_false(zBase) ){
return 0;
}
zRand = db_text(0, "SELECT hex(randomblob(10))");
zOut = mprintf("out-%s", zRand);
blob_write_to_file(pIn, zOut);
useSsh = (fossil_strncmp(command_basename(zBase), "ssh", 3)==0);
if( useSsh ){
zIn = mprintf("out-%s.sig", zRand);
zCmd = mprintf("%s %s", zBase, zOut);
}else{
zIn = mprintf("in-%z", zRand);
zCmd = mprintf("%s %s %s", zBase, zIn, zOut);
}
rc = fossil_system(zCmd);
free(zCmd);
if( rc==0 ){
if( pOut==pIn ){
blob_reset(pIn);
}
blob_zero(pOut);
if( useSsh ){
/* As of 2025, SSH cannot create non-detached SSH signatures */
/* We put one together */
Blob tmpBlob;
blob_zero(&tmpBlob);
blob_read_from_file(&tmpBlob, zOut, ExtFILE);
/* Add armor header line and manifest */
blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n");
blob_appendf(pOut, "%s", blob_str(&tmpBlob));
blob_zero(&tmpBlob);
blob_read_from_file(&tmpBlob, zIn, ExtFILE);
/* Add signature - already armored by SSH */
blob_appendf(pOut, "%s", blob_str(&tmpBlob));
}else{
/* Assume that the external command creates non-detached signatures */
blob_read_from_file(pOut, zIn, ExtFILE);
}
}else{
if( pOut!=pIn ){
blob_copy(pOut, pIn);
}
}
file_delete(zOut);
file_delete(zIn);
|
| ︙ | ︙ |
Changes to src/clone.c.
| ︙ | ︙ | |||
127 128 129 130 131 132 133 134 135 136 137 138 139 140 | ** -B|--httpauth USER:PASS Add HTTP Basic Authorization to requests ** --nested Allow opening a repository inside an opened ** check-out ** --nocompress Omit extra delta compression ** --no-open Clone only. Do not open a check-out. ** --once Don't remember the URI. ** --private Also clone private branches ** --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 | > | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | ** -B|--httpauth USER:PASS Add HTTP Basic Authorization to requests ** --nested Allow opening a repository inside an opened ** check-out ** --nocompress Omit extra delta compression ** --no-open Clone only. Do not open a check-out. ** --once Don't remember the URI. ** --private Also clone private branches ** --proxy PROXY Use the specified HTTP proxy ** --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 |
| ︙ | ︙ | |||
427 428 429 430 431 432 433 |
}else{
const char *zDLTag = db_get("download-tag","trunk");
const char *zNm = db_get("short-project-name","download");
char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
@ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
@ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
| > | | > | 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
}else{
const char *zDLTag = db_get("download-tag","trunk");
const char *zNm = db_get("short-project-name","download");
char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
@ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
@ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
if( g.zLogin!=0 ){
zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
@ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
}
}
if( !g.perm.Clone ){
@ <p>You are not authorized to clone this repository.
if( g.zLogin==0 || g.zLogin[0]==0 ){
@ Maybe you would be able to clone if you
@ %z(href("%R/login"))logged in</a>.
}else{
|
| ︙ | ︙ |
Changes to src/color.c.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
** This file contains code used to select colors based on branch and
** user names.
**
*/
#include "config.h"
#include <string.h>
#include "color.h"
/*
** Compute a hash on a branch or user name
*/
static unsigned int hash_of_name(const char *z){
unsigned int h = 0;
int i;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
** This file contains code used to select colors based on branch and
** user names.
**
*/
#include "config.h"
#include <string.h>
#include "color.h"
/*
** 140 standard CSS color names and their corresponding RGB values,
** in alphabetical order by name so that we can do a binary search
** for lookup.
*/
static const struct CssColors {
const char *zName; /* CSS Color name, lower case */
unsigned int iRGB; /* Corresponding RGB value */
} aCssColors[] = {
{ "aliceblue", 0xf0f8ff },
{ "antiquewhite", 0xfaebd7 },
{ "aqua", 0x00ffff },
{ "aquamarine", 0x7fffd4 },
{ "azure", 0xf0ffff },
{ "beige", 0xf5f5dc },
{ "bisque", 0xffe4c4 },
{ "black", 0x000000 },
{ "blanchedalmond", 0xffebcd },
{ "blue", 0x0000ff },
{ "blueviolet", 0x8a2be2 },
{ "brown", 0xa52a2a },
{ "burlywood", 0xdeb887 },
{ "cadetblue", 0x5f9ea0 },
{ "chartreuse", 0x7fff00 },
{ "chocolate", 0xd2691e },
{ "coral", 0xff7f50 },
{ "cornflowerblue", 0x6495ed },
{ "cornsilk", 0xfff8dc },
{ "crimson", 0xdc143c },
{ "cyan", 0x00ffff },
{ "darkblue", 0x00008b },
{ "darkcyan", 0x008b8b },
{ "darkgoldenrod", 0xb8860b },
{ "darkgray", 0xa9a9a9 },
{ "darkgreen", 0x006400 },
{ "darkkhaki", 0xbdb76b },
{ "darkmagenta", 0x8b008b },
{ "darkolivegreen", 0x556b2f },
{ "darkorange", 0xff8c00 },
{ "darkorchid", 0x9932cc },
{ "darkred", 0x8b0000 },
{ "darksalmon", 0xe9967a },
{ "darkseagreen", 0x8fbc8f },
{ "darkslateblue", 0x483d8b },
{ "darkslategray", 0x2f4f4f },
{ "darkturquoise", 0x00ced1 },
{ "darkviolet", 0x9400d3 },
{ "deeppink", 0xff1493 },
{ "deepskyblue", 0x00bfff },
{ "dimgray", 0x696969 },
{ "dodgerblue", 0x1e90ff },
{ "firebrick", 0xb22222 },
{ "floralwhite", 0xfffaf0 },
{ "forestgreen", 0x228b22 },
{ "fuchsia", 0xff00ff },
{ "gainsboro", 0xdcdcdc },
{ "ghostwhite", 0xf8f8ff },
{ "gold", 0xffd700 },
{ "goldenrod", 0xdaa520 },
{ "gray", 0x808080 },
{ "green", 0x008000 },
{ "greenyellow", 0xadff2f },
{ "honeydew", 0xf0fff0 },
{ "hotpink", 0xff69b4 },
{ "indianred", 0xcd5c5c },
{ "indigo", 0x4b0082 },
{ "ivory", 0xfffff0 },
{ "khaki", 0xf0e68c },
{ "lavender", 0xe6e6fa },
{ "lavenderblush", 0xfff0f5 },
{ "lawngreen", 0x7cfc00 },
{ "lemonchiffon", 0xfffacd },
{ "lightblue", 0xadd8e6 },
{ "lightcoral", 0xf08080 },
{ "lightcyan", 0xe0ffff },
{ "lightgoldenrodyellow", 0xfafad2 },
{ "lightgrey", 0xd3d3d3 },
{ "lightgreen", 0x90ee90 },
{ "lightpink", 0xffb6c1 },
{ "lightsalmon", 0xffa07a },
{ "lightseagreen", 0x20b2aa },
{ "lightskyblue", 0x87cefa },
{ "lightslategray", 0x778899 },
{ "lightsteelblue", 0xb0c4de },
{ "lightyellow", 0xffffe0 },
{ "lime", 0x00ff00 },
{ "limegreen", 0x32cd32 },
{ "linen", 0xfaf0e6 },
{ "magenta", 0xff00ff },
{ "maroon", 0x800000 },
{ "mediumaquamarine", 0x66cdaa },
{ "mediumblue", 0x0000cd },
{ "mediumorchid", 0xba55d3 },
{ "mediumpurple", 0x9370d8 },
{ "mediumseagreen", 0x3cb371 },
{ "mediumslateblue", 0x7b68ee },
{ "mediumspringgreen", 0x00fa9a },
{ "mediumturquoise", 0x48d1cc },
{ "mediumvioletred", 0xc71585 },
{ "midnightblue", 0x191970 },
{ "mintcream", 0xf5fffa },
{ "mistyrose", 0xffe4e1 },
{ "moccasin", 0xffe4b5 },
{ "navajowhite", 0xffdead },
{ "navy", 0x000080 },
{ "oldlace", 0xfdf5e6 },
{ "olive", 0x808000 },
{ "olivedrab", 0x6b8e23 },
{ "orange", 0xffa500 },
{ "orangered", 0xff4500 },
{ "orchid", 0xda70d6 },
{ "palegoldenrod", 0xeee8aa },
{ "palegreen", 0x98fb98 },
{ "paleturquoise", 0xafeeee },
{ "palevioletred", 0xd87093 },
{ "papayawhip", 0xffefd5 },
{ "peachpuff", 0xffdab9 },
{ "peru", 0xcd853f },
{ "pink", 0xffc0cb },
{ "plum", 0xdda0dd },
{ "powderblue", 0xb0e0e6 },
{ "purple", 0x800080 },
{ "red", 0xff0000 },
{ "rosybrown", 0xbc8f8f },
{ "royalblue", 0x4169e1 },
{ "saddlebrown", 0x8b4513 },
{ "salmon", 0xfa8072 },
{ "sandybrown", 0xf4a460 },
{ "seagreen", 0x2e8b57 },
{ "seashell", 0xfff5ee },
{ "sienna", 0xa0522d },
{ "silver", 0xc0c0c0 },
{ "skyblue", 0x87ceeb },
{ "slateblue", 0x6a5acd },
{ "slategray", 0x708090 },
{ "snow", 0xfffafa },
{ "springgreen", 0x00ff7f },
{ "steelblue", 0x4682b4 },
{ "tan", 0xd2b48c },
{ "teal", 0x008080 },
{ "thistle", 0xd8bfd8 },
{ "tomato", 0xff6347 },
{ "turquoise", 0x40e0d0 },
{ "violet", 0xee82ee },
{ "wheat", 0xf5deb3 },
{ "white", 0xffffff },
{ "whitesmoke", 0xf5f5f5 },
{ "yellow", 0xffff00 },
{ "yellowgreen", 0x9acd32 },
};
/*
** Attempt to translate a CSS color name into an integer that
** represents the equivalent RGB value. Ignore alpha if provided.
** If the name cannot be translated, return -1.
*/
int color_name_to_rgb(const char *zName){
if( zName==0 || zName[0]==0 ) return -1;
if( zName[0]=='#' ){
int i, v = 0;
for(i=1; i<=6 && fossil_isxdigit(zName[i]); i++){
v = v*16 + fossil_hexvalue(zName[i]);
}
if( i==4 ){
v = fossil_hexvalue(zName[1])*0x110000 +
fossil_hexvalue(zName[2])*0x1100 +
fossil_hexvalue(zName[3])*0x11;
return v;
}
if( i==7 ){
return v;
}
return -1;
}else{
int iMin = 0;
int iMax = count(aCssColors)-1;
while( iMin<=iMax ){
int iMid = (iMin+iMax)/2;
int c = sqlite3_stricmp(aCssColors[iMid].zName, zName);
if( c==0 ) return aCssColors[iMid].iRGB;
if( c<0 ){
iMin = iMid+1;
}else{
iMax = iMid-1;
}
}
return -1;
}
}
/*
** SETTING: raw-bgcolor boolean default=off
**
** Fossil usually tries to adjust user-specified background colors
** for checkins so that the text is readable and so that the color
** is not too garish. This setting disables that filter. When
** this setting is on, the user-selected background colors are shown
** exactly as requested.
*/
/*
** Shift a color provided by the user so that it is suitable
** for use as a background color in the current skin.
**
** The return value is a #HHHHHH color name contained in
** static space that is overwritten on the next call.
**
** If we cannot make sense of the background color recommendation
** that is the input, then return NULL.
**
** The iFgClr parameter is normally 0. But for testing purposes, set
** it to 1 for a black foregrounds and 2 for a white foreground.
*/
const char *reasonable_bg_color(const char *zRequested, int iFgClr){
int iRGB = color_name_to_rgb(zRequested);
int r, g, b; /* RGB components of requested color */
static int systemFg = 0; /* 1==black-foreground 2==white-foreground */
int fg; /* Foreground color to actually use */
static char zColor[10]; /* Return value */
if( iFgClr ){
fg = iFgClr;
}else if( systemFg==0 ){
if( db_get_boolean("raw-bgcolor",0) ){
fg = systemFg = 3;
}else{
fg = systemFg = skin_detail_boolean("white-foreground") ? 2 : 1;
}
}else{
fg = systemFg;
}
if( fg>=3 ) return zRequested;
if( iRGB<0 ) return 0;
r = (iRGB>>16) & 0xff;
g = (iRGB>>8) & 0xff;
b = iRGB & 0xff;
if( fg==1 ){
/* Dark text on a light background. Adjust so that
** no color component is less than 255-K, resulting in
** a pastel background color. Color adjustment is quadratic
** so that colors that are further out of range have a greater
** adjustment. */
const int K = 79;
int k, x, m;
m = r<g ? r : g;
if( m>b ) m = b;
k = (m*m)/255 + K;
x = 255 - k;
r = (k*r)/255 + x;
g = (k*g)/255 + x;
b = (k*b)/255 + x;
}else{
/* Light text on a dark background. Adjust so that
** no color component is greater than K, resulting in
** a low-intensity, low-saturation background color.
** The color adjustment is quadratic so that colors that
** are further out of range have a greater adjustment. */
const int K = 112;
int k, m;
m = r>g ? r : g;
if( m<b ) m = b;
k = 255 - (255-K)*(m*m)/65025;
r = (k*r)/255;
g = (k*g)/255;
b = (k*b)/255;
}
sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b);
return zColor;
}
/*
** Compute a hash on a branch or user name
*/
static unsigned int hash_of_name(const char *z){
unsigned int h = 0;
int i;
|
| ︙ | ︙ | |||
183 184 185 186 187 188 189 |
@ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br>
}
@ <input type="submit" value="Submit">
@ <input type="submit" name="rand" value="Random">
@ </form>
style_finish_page();
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
@ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br>
}
@ <input type="submit" value="Submit">
@ <input type="submit" name="rand" value="Random">
@ </form>
style_finish_page();
}
/*
** WEBPAGE: test-bgcolor
**
** Show how user-specified background colors will be rendered
** using the reasonable_bg_color() algorithm.
*/
void test_bgcolor_page(void){
const char *zReq; /* Requested color name */
const char *zBG; /* Actual color provided */
const char *zBg1;
char zNm[10];
static const char *azDflt[] = {
"red", "orange", "yellow", "green", "blue", "indigo", "violet",
"tan", "brown", "gray",
};
const int N = count(azDflt);
int i, cnt, iClr, r, g, b;
char *zFg;
login_check_credentials();
style_set_current_feature("test");
style_header("Background Color Test");
for(i=cnt=0; i<N; i++){
sqlite3_snprintf(sizeof(zNm),zNm,"b%c",'a'+i);
zReq = PD(zNm,azDflt[i]);
if( zReq==0 || zReq[0]==0 ) continue;
if( cnt==0 ){
@ <table border="1" cellspacing="0" cellpadding="10">
@ <tr>
@ <th>Requested Background
@ <th>Light mode
@ <th>Dark mode
@ </tr>
}
cnt++;
zBG = reasonable_bg_color(zReq, 0);
if( zBG==0 ){
@ <tr><td colspan="3" align="center">\
@ "%h(zReq)" is not a recognized color name</td></tr>
continue;
}
iClr = color_name_to_rgb(zReq);
r = (iClr>>16) & 0xff;
g = (iClr>>8) & 0xff;
b = iClr & 0xff;
if( 3*r + 7*g + b > 6*255 ){
zFg = "black";
}else{
zFg = "white";
}
if( zReq[0]!='#' ){
char zReqRGB[12];
sqlite3_snprintf(sizeof(zReqRGB),zReqRGB,"#%06x",color_name_to_rgb(zReq));
@ <tr><td style='color:%h(zFg);background-color:%h(zReq);'>\
@ Requested color "%h(zReq)" (%h(zReqRGB))</td>
}else{
@ <tr><td style='color:%h(zFg);background-color:%s(zReq);'>\
@ Requested color "%h(zReq)"</td>
}
zBg1 = reasonable_bg_color(zReq,1);
@ <td style='color:black;background-color:%h(zBg1);'>\
@ Background color for dark text: %h(zBg1)</td>
zBg1 = reasonable_bg_color(zReq,2);
@ <td style='color:white;background-color:%h(zBg1);'>\
@ Background color for light text: %h(zBg1)</td></tr>
}
if( cnt ){
@ </table>
@ <hr>
}
@ <form method="POST">
@ <p>Enter CSS color names below and see them shifted into corresponding
@ background colors above.</p>
for(i=0; i<N; i++){
sqlite3_snprintf(sizeof(zNm),zNm,"b%c",'a'+i);
@ <input type="text" size="30" name='%s(zNm)' \
@ value='%h(PD(zNm,azDflt[i]))'><br>
}
@ <input type="submit" value="Submit">
@ </form>
style_finish_page();
}
|
Changes to src/comformat.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | ** text on a TTY. */ #include "config.h" #include "comformat.h" #include <assert.h> #if INTERFACE | | > > > | > > > < < | 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 |
** text on a TTY.
*/
#include "config.h"
#include "comformat.h"
#include <assert.h>
#if INTERFACE
#define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags */
#define COMMENT_PRINT_CANONICAL ((u32)0x00000001) /* Use canonical algorithm */
#define COMMENT_PRINT_DEFAULT COMMENT_PRINT_CANONICAL /* Default */
#define COMMENT_PRINT_UNSET (-1) /* Not initialized */
/* The canonical comment printing algorithm is recommended. We make
** no promise of on-going support for any of the following flags:
*/
#define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */
#define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */
#define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */
#define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */
#endif
/********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/
/* Lookup table to estimate the number of columns consumed by a Unicode
** character.
*/
static const struct {
|
| ︙ | ︙ | |||
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
const char *zLine, /* [in] The comment line being printed. */
int index, /* [in] The current character index being handled. */
int maxChars, /* [in] Optimization hint to abort before space found. */
int *sumWidth /* [out] Summated width of all characters to next space. */
){
int cchUTF8, utf32, wcwidth = 0;
int nextIndex = index;
for(;;){
char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32);
nextIndex += cchUTF8;
wcwidth += cli_wcwidth(utf32);
if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) ||
wcwidth>maxChars ){
*sumWidth = wcwidth;
return nextIndex;
}
}
return 0; /* NOT REACHED */
}
/*
| > | > > | > > | | | | | | | > > > | | | > > > > > > > > > > | 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 |
const char *zLine, /* [in] The comment line being printed. */
int index, /* [in] The current character index being handled. */
int maxChars, /* [in] Optimization hint to abort before space found. */
int *sumWidth /* [out] Summated width of all characters to next space. */
){
int cchUTF8, utf32, wcwidth = 0;
int nextIndex = index;
if( zLine[index]==0 ) return index;
for(;;){
char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32);
nextIndex += cchUTF8;
wcwidth += cli_wcwidth(utf32);
if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) ||
wcwidth>maxChars ){
*sumWidth = wcwidth;
return nextIndex;
}
}
return 0; /* NOT REACHED */
}
/*
** Return information about the next (single- or multi-byte) character in
** z[0]. Two values are computed:
**
** * The number of bytes needed to represent the character.
** * The UTF code point value.
**
** Incomplete, ill-formed and overlong sequences are consumed together as
** one invalid code point. The invalid lead bytes 0xC0 to 0xC1 and 0xF5 to
** 0xF7 are allowed to initiate (ill-formed) 2- and 4-byte sequences,
** respectively, the other invalid lead bytes 0xF8 to 0xFF are treated
** as invalid 1-byte sequences (as lone trail bytes), all resulting
** in one invalid code point. Invalid UTF-8 sequences encoding a
** non-scalar code point (UTF-16 surrogates U+D800 to U+DFFF) are allowed.
**
** ANSI escape sequences of the form "\033[...X" are interpreted as a
** zero-width character.
*/
void char_info_utf8(
const char *z, /* The character to be analyzed */
int *pCchUTF8, /* OUT: The number of bytes used by this character */
int *pUtf32 /* OUT: The UTF8 code point (used to determine width) */
){
int i = 0; /* Counted bytes. */
int cchUTF8 = 1; /* Code units consumed. */
int maxUTF8 = 1; /* Expected sequence length. */
char c = z[i++];
if( c==0x1b && z[i]=='[' ){
i++;
while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
if( z[i]>=0x40 && z[i]<=0x7e ){
*pCchUTF8 = i+1;
*pUtf32 = 0x301; /* A zero-width character */
return;
}
}
if( (c&0x80)==0x00 ){ /* 7-bit ASCII character. */
*pCchUTF8 = 1;
*pUtf32 = (int)z[0];
return;
}
else if( (c&0xe0)==0xc0 ) maxUTF8 = 2; /* UTF-8 lead byte 110vvvvv */
else if( (c&0xf0)==0xe0 ) maxUTF8 = 3; /* UTF-8 lead byte 1110vvvv */
|
| ︙ | ︙ | |||
463 464 465 466 467 468 469 |
}
if( pzLine ){
*pzLine = zLine + index;
}
}
/*
| | | > | > > > > > > > | | 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 |
}
if( pzLine ){
*pzLine = zLine + index;
}
}
/*
** This is the canonical comment printing algorithm. This is the algorithm
** that is recommended and that is used unless the administrator has made
** special arrangements to use a customized algorithm.
**
** Given a comment string, format that string for printing on a TTY.
** Assume that the output cursor is indent spaces from the left margin
** and that a single line can contain no more than 'width' characters.
** Indent all subsequent lines by 'indent'.
**
** Formatting features:
**
** * Leading whitespace is removed.
** * Internal whitespace sequences are changed into a single space (0x20)
** character.
** * Lines are broken at a space, or at a hyphen ("-") whenever possible.
**
** Returns the number of new lines emitted.
*/
static int comment_print_canonical(
const char *zText, /* The comment text to be printed. */
int indent, /* Number of spaces to indent each non-initial line. */
int width /* Maximum number of characters per line. */
){
int maxChars = width - indent;
int si, sk, i, k, kc;
int doIndent = 0;
|
| ︙ | ︙ | |||
562 563 564 565 566 567 568 | /* ** This is the comment printing function. The comment printing algorithm ** contained within it attempts to preserve the formatting present within ** the comment string itself while honoring line width limitations. There ** are several flags that modify the default behavior of this function: ** | | > > > > | > | | 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 | /* ** This is the comment printing function. The comment printing algorithm ** contained within it attempts to preserve the formatting present within ** the comment string itself while honoring line width limitations. There ** are several flags that modify the default behavior of this function: ** ** COMMENT_PRINT_CANONICAL: Use the canonical printing algorithm: ** * Omit leading and trailing whitespace ** * Collapse internal whitespace into a ** single space (0x20) character. ** * Attempt to break lines at whitespace ** or hyphens. ** This is the recommended algorithm and is ** used in most cases. ** ** COMMENT_PRINT_TRIM_CRLF: Trims leading and trailing carriage-returns ** and line-feeds where they do not materially ** impact pre-existing formatting (i.e. at the ** start of the comment string -AND- right ** before line indentation). This flag does ** not apply to the legacy comment printing |
| ︙ | ︙ | |||
611 612 613 614 615 616 617 |
const char *zText, /* The comment text to be printed. */
const char *zOrigText, /* Original comment text ONLY, may be NULL. */
int indent, /* Spaces to indent each non-initial line. */
int width, /* Maximum number of characters per line. */
int flags /* Zero or more "COMMENT_PRINT_*" flags. */
){
int maxChars = width - indent;
| > | > > > > > > > | | | | | | < < < | | | | | | | | | | | | | | | | | | | | | | | | | > < | | | | 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 |
const char *zText, /* The comment text to be printed. */
const char *zOrigText, /* Original comment text ONLY, may be NULL. */
int indent, /* Spaces to indent each non-initial line. */
int width, /* Maximum number of characters per line. */
int flags /* Zero or more "COMMENT_PRINT_*" flags. */
){
int maxChars = width - indent;
if( flags & COMMENT_PRINT_CANONICAL ){
/* Use the canonical algorithm. This is what happens in almost
** all cases. */
return comment_print_canonical(zText, indent, width);
}else{
/* The remaining is a more complex formatting algorithm that is very
** seldom used and is considered deprecated.
*/
int trimCrLf = flags & COMMENT_PRINT_TRIM_CRLF;
int trimSpace = flags & COMMENT_PRINT_TRIM_SPACE;
int wordBreak = flags & COMMENT_PRINT_WORD_BREAK;
int origBreak = flags & COMMENT_PRINT_ORIG_BREAK;
int lineCnt = 0;
const char *zLine;
if( width<0 ){
comment_set_maxchars(indent, &maxChars);
}
if( zText==0 ) zText = "(NULL)";
if( maxChars<=0 ){
maxChars = strlen(zText);
}
if( trimSpace ){
while( fossil_isspace(zText[0]) ){ zText++; }
}
if( zText[0]==0 ){
fossil_print("\n");
lineCnt++;
return lineCnt;
}
zLine = zText;
for(;;){
comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0,
maxChars, trimCrLf, trimSpace, wordBreak, origBreak,
&lineCnt, &zLine);
if( zLine==0 ) break;
while( fossil_isspace(zLine[0]) ) zLine++;
if( zLine[0]==0 ) break;
}
return lineCnt;
}
}
/*
** Return the "COMMENT_PRINT_*" flags specified by the following sources,
** evaluated in the following cascading order:
**
** 1. The local (per-repository) "comment-format" setting.
** 2. The global (all-repositories) "comment-format" setting.
** 3. The default value COMMENT_PRINT_DEFAULT.
*/
int get_comment_format(){
int comFmtFlags;
/* We must cache this result, else running the timeline can end up
** querying the comment-format setting from the global db once per
** timeline entry, which brings it to a crawl if that db is
|
| ︙ | ︙ | |||
687 688 689 690 691 692 693 | return g.comFmtFlags; } /* ** ** COMMAND: test-comment-format ** | | > > > > > > > > > > < < | | < < < < | | | > > > | > > > > | | | | | > | | > | | | > > > < < < < < < < < | < | > | > | | | | > > > > > > > > > > > > > > > < < | 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 |
return g.comFmtFlags;
}
/*
**
** COMMAND: test-comment-format
**
** Usage: %fossil test-comment-format [OPTIONS] TEXT [PREFIX] [ORIGTEXT]
**
** Test comment formatting and printing. Use for testing only.
**
** The default (canonical) formatting algorithm is:
**
** * Omit leading/trailing whitespace
** * Collapse internal whitespace into a single space character.
** * Attempt to break lines at whitespace or at a hyphen.
**
** Use --whitespace, --origbreak, --trimcrlf, --trimspace,
** and/or --wordbreak to disable the canonical processing and do
** the special processing specified by those other options.
**
** Options:
** --decode Decode the text using the same method used when
** handling the value of a C-card from a manifest.
** --file FILE Omit the TEXT argument and read the comment text
** from FILE.
** --indent Number of spaces to indent (default (-1) is to
** auto-detect). Zero means no indent.
** --orig FILE Take the value for the ORIGTEXT argumetn from FILE.
** --origbreak Attempt to break when the original comment text
** is detected.
** --trimcrlf Enable trimming of leading/trailing CR/LF.
** --trimspace Enable trimming of leading/trailing spaces.
** --whitespace Keep all internal whitespace.
** --wordbreak Attempt to break lines on word boundaries.
** -W|--width NUM Width of lines (default (-1) is to auto-detect).
** Zero means no limit.
*/
void test_comment_format(void){
const char *zWidth;
const char *zIndent;
const char *zPrefix = 0;
char *zText = 0;
char *zOrigText = 0;
int indent, width;
int i;
const char *fromFile = find_option("file", 0, 1);
int decode = find_option("decode", 0, 0)!=0;
int flags = COMMENT_PRINT_CANONICAL;
const char *fromOrig = find_option("orig", 0, 1);
if( find_option("whitespace",0,0) ){
flags = 0;
}
if( find_option("trimcrlf", 0, 0) ){
flags = COMMENT_PRINT_TRIM_CRLF;
}
if( find_option("trimspace", 0, 0) ){
flags |= COMMENT_PRINT_TRIM_SPACE;
flags &= COMMENT_PRINT_CANONICAL;
}
if( find_option("wordbreak", 0, 0) ){
flags |= COMMENT_PRINT_WORD_BREAK;
flags &= COMMENT_PRINT_CANONICAL;
}
if( find_option("origbreak", 0, 0) ){
flags |= COMMENT_PRINT_ORIG_BREAK;
flags &= COMMENT_PRINT_CANONICAL;
}
zWidth = find_option("width","W",1);
if( zWidth ){
width = atoi(zWidth);
}else{
width = -1; /* automatic */
}
zIndent = find_option("indent",0,1);
if( zIndent ){
indent = atoi(zIndent);
}else{
indent = -1; /* automatic */
}
verify_all_options();
zPrefix = zText = zOrigText = 0;
if( fromFile ){
Blob fileData;
blob_read_from_file(&fileData, fromFile, ExtFILE);
zText = mprintf("%s", blob_str(&fileData));
blob_reset(&fileData);
}
if( fromOrig ){
Blob fileData;
blob_read_from_file(&fileData, fromOrig, ExtFILE);
zOrigText = mprintf("%s", blob_str(&fileData));
blob_reset(&fileData);
}
for(i=2; i<g.argc; i++){
if( zText==0 ){
zText = g.argv[i];
continue;
}
if( zPrefix==0 ){
zPrefix = g.argv[i];
continue;
}
if( zOrigText==0 ){
zOrigText = g.argv[i];
continue;
}
usage("[OPTIONS] TEXT [PREFIX] [ORIGTEXT]");
}
if( decode ){
zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText);
defossilize(zText);
if( zOrigText ){
zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText);
defossilize(zOrigText);
}
}
if( zPrefix==0 ) zPrefix = "00:00:00 ";
if( indent<0 ){
indent = strlen(zPrefix);
}
if( zPrefix && *zPrefix ){
fossil_print("%s", zPrefix);
}
fossil_print("(%d lines output)\n",
comment_print(zText, zOrigText, indent, width, flags));
}
|
Changes to src/configure.c.
| ︙ | ︙ | |||
98 99 100 101 102 103 104 |
{ "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 },
| < | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
{ "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-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 },
|
| ︙ | ︙ | |||
369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
** ------- -----------------------------------------------------------
** /config $MTIME $NAME value $VALUE
** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE
** /shun $MTIME $UUID scom $VALUE
** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE jx $JSON
** /concealed $MTIME $HASH content $VALUE
** /subscriber $SMTIME $SEMAIL suname $V ...
*/
void configure_receive(const char *zName, Blob *pContent, int groupMask){
int checkMask; /* Masks for which we must first check existance of tables */
checkMask = CONFIGSET_SCRIBER;
if( zName[0]=='/' ){
/* The new format */
| > > > > | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
** ------- -----------------------------------------------------------
** /config $MTIME $NAME value $VALUE
** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE
** /shun $MTIME $UUID scom $VALUE
** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE jx $JSON
** /concealed $MTIME $HASH content $VALUE
** /subscriber $SMTIME $SEMAIL suname $V ...
**
** NAME-specific notes:
**
** - /reportftm's $MTIME is in Julian, not the Unix epoch.
*/
void configure_receive(const char *zName, Blob *pContent, int groupMask){
int checkMask; /* Masks for which we must first check existance of tables */
checkMask = CONFIGSET_SCRIBER;
if( zName[0]=='/' ){
/* The new format */
|
| ︙ | ︙ | |||
815 816 817 818 819 820 821 822 823 824 825 826 827 828 |
**
** > fossil configuration sync AREA ?URL?
**
** Synchronize configuration changes in the local repository with
** the remote repository at URL.
**
** Options:
** -R|--repository REPO Affect repository REPO with changes
**
** See also: [[settings]], [[unset]]
*/
void configuration_cmd(void){
int n;
const char *zMethod;
| > > | 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
**
** > fossil configuration sync AREA ?URL?
**
** Synchronize configuration changes in the local repository with
** the remote repository at URL.
**
** Options:
** --proxy PROXY Use PROXY as http proxy during sync operation
** (used by pull, push and sync subcommands)
** -R|--repository REPO Affect repository REPO with changes
**
** See also: [[settings]], [[unset]]
*/
void configuration_cmd(void){
int n;
const char *zMethod;
|
| ︙ | ︙ | |||
887 888 889 890 891 892 893 894 895 896 897 898 899 900 |
if( g.argc==5 ){
zServer = g.argv[4];
}
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();
| > | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 |
if( g.argc==5 ){
zServer = g.argv[4];
}
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: ");
g.zHttpAuth = get_httpauth();
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();
|
| ︙ | ︙ |
Changes to src/content.c.
| ︙ | ︙ | |||
944 945 946 947 948 949 950 951 952 953 954 955 956 957 |
** Return true if Blob p looks like it might be a parsable control artifact.
*/
static int looks_like_control_artifact(Blob *p){
const char *z = blob_buffer(p);
int n = blob_size(p);
if( n<10 ) return 0;
if( strncmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)==0 ) return 1;
if( z[0]<'A' || z[0]>'Z' || z[1]!=' ' || z[0]=='I' ) return 0;
if( z[n-1]!='\n' ) return 0;
return 1;
}
/*
** COMMAND: test-integrity
| > | 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 |
** Return true if Blob p looks like it might be a parsable control artifact.
*/
static int looks_like_control_artifact(Blob *p){
const char *z = blob_buffer(p);
int n = blob_size(p);
if( n<10 ) return 0;
if( strncmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)==0 ) return 1;
if( strncmp(z, "-----BEGIN SSH SIGNED MESSAGE-----", 34)==0 ) return 1;
if( z[0]<'A' || z[0]>'Z' || z[1]!=' ' || z[0]=='I' ) return 0;
if( z[n-1]!='\n' ) return 0;
return 1;
}
/*
** COMMAND: test-integrity
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
int iStartLine; /* Line of zStartFile where transaction started */
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
void *pAuthArg; /* Argument to the authorizer */
const char *zAuthName; /* Name of the authorizer */
int bProtectTriggers; /* True if protection triggers already exist */
int nProtect; /* Slots of aProtect used */
unsigned aProtect[12]; /* Saved values of protectMask */
} db = {
PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}};
/*
** Arrange for the given file to be deleted on a failure.
*/
| > > | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
int iStartLine; /* Line of zStartFile where transaction started */
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
void *pAuthArg; /* Argument to the authorizer */
const char *zAuthName; /* Name of the authorizer */
int bProtectTriggers; /* True if protection triggers already exist */
int nProtect; /* Slots of aProtect used */
unsigned aProtect[12]; /* Saved values of protectMask */
int pauseDmlLog; /* Ignore pDmlLog if positive */
Blob *pDmlLog; /* Append DML statements here, of not NULL */
} db = {
PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}};
/*
** Arrange for the given file to be deleted on a failure.
*/
|
| ︙ | ︙ | |||
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 |
/*
** Possible flags to db_vprepare
*/
#define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */
#define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */
#endif
/*
** Prepare a Stmt. Assume that the Stmt is previously uninitialized.
** If the input string contains multiple SQL statements, only the first
** one is processed. All statements beyond the first are silently ignored.
*/
int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
int rc;
int prepFlags = 0;
char *zSql;
const char *zExtra = 0;
blob_zero(&pStmt->sql);
blob_vappendf(&pStmt->sql, zFormat, ap);
va_end(ap);
zSql = blob_str(&pStmt->sql);
db.nPrepare++;
if( flags & DB_PREPARE_PERSISTENT ){
prepFlags = SQLITE_PREPARE_PERSISTENT;
}
rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
}else if( zExtra && !fossil_all_whitespace(zExtra) ){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/*
** Possible flags to db_vprepare
*/
#define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */
#define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */
#endif
/*
** If zSql is a DML statement, append it db.pDmlLog.
*/
static void db_append_dml(const char *zSql){
size_t nSql;
if( db.pDmlLog==0 ) return;
if( db.pauseDmlLog ) return;
if( zSql==0 ) return;
nSql = strlen(zSql);
while( nSql>0 && fossil_isspace(zSql[0]) ){ nSql--; zSql++; }
while( nSql>0 && fossil_isspace(zSql[nSql-1]) ) nSql--;
if( nSql<6 ) return;
if( fossil_strnicmp(zSql, "SELECT", 6)==0 ) return;
if( fossil_strnicmp(zSql, "PRAGMA", 6)==0 ) return;
blob_append(db.pDmlLog, zSql, nSql);
if( zSql[nSql-1]!=';' ) blob_append_char(db.pDmlLog, ';');
blob_append_char(db.pDmlLog, '\n');
}
/*
** Set the Blob to which DML statement text should be appended. Set it
** to zero to stop appending DML statement text.
*/
void db_append_dml_to_blob(Blob *pBlob){
db.pDmlLog = pBlob;
}
/*
** Pause or unpause the DML log
*/
void db_pause_dml_log(void){ db.pauseDmlLog++; }
void db_unpause_dml_log(void){ db.pauseDmlLog--; }
/*
** Prepare a Stmt. Assume that the Stmt is previously uninitialized.
** If the input string contains multiple SQL statements, only the first
** one is processed. All statements beyond the first are silently ignored.
*/
int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
int rc;
int prepFlags = 0;
char *zSql;
const char *zExtra = 0;
blob_zero(&pStmt->sql);
blob_vappendf(&pStmt->sql, zFormat, ap);
va_end(ap);
zSql = blob_str(&pStmt->sql);
db.nPrepare++;
db_append_dml(zSql);
if( flags & DB_PREPARE_PERSISTENT ){
prepFlags = SQLITE_PREPARE_PERSISTENT;
}
rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
}else if( zExtra && !fossil_all_whitespace(zExtra) ){
|
| ︙ | ︙ | |||
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 |
while( rc==SQLITE_OK && z[0] ){
pStmt = 0;
rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
if( rc ){
db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
}else if( pStmt ){
db.nPrepare++;
while( sqlite3_step(pStmt)==SQLITE_ROW ){}
rc = sqlite3_finalize(pStmt);
if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
}
z = zEnd;
}
return rc;
| > | 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 |
while( rc==SQLITE_OK && z[0] ){
pStmt = 0;
rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
if( rc ){
db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
}else if( pStmt ){
db.nPrepare++;
db_append_dml(sqlite3_sql(pStmt));
while( sqlite3_step(pStmt)==SQLITE_ROW ){}
rc = sqlite3_finalize(pStmt);
if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
}
z = zEnd;
}
return rc;
|
| ︙ | ︙ | |||
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 |
sqlite3_result_null(context);
}else{
sqlite3_result_int64(context, rid);
}
}
}
/*
** The toLocal() SQL function returns a string that is an argument to a
** date/time function that is appropriate for modifying the time for display.
** If UTC time display is selected, no modification occurs. If local time
** display is selected, the time is adjusted appropriately.
**
** Example usage:
**
** SELECT datetime('now',toLocal());
*/
void db_tolocal_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < | 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 |
sqlite3_result_null(context);
}else{
sqlite3_result_int64(context, rid);
}
}
}
/*
** SETTING: timeline-utc boolean default=on
**
** If the timeline-utc setting is true, then Fossil tries to understand
** and display all time values using UTC. If this setting is false, Fossil
** tries to understand and display time values using the local timezone.
**
** The word "timeline" in the name of this setting is historical.
** This setting applies to all user interfaces of Fossil,
** not just the timeline.
**
** Note that when accessing Fossil using the web interface, the localtime
** used is the localtime on the server, not on the client.
*/
/*
** Return true if Fossil is set to display times as UTC. Return false
** if it wants to display times using the local timezone.
**
** False is returned if display is set to localtime even if the localtime
** happens to be the same as UTC.
*/
int fossil_ui_utctime(void){
if( g.fTimeFormat==0 ){
if( db_get_int("timeline-utc", 1) ){
g.fTimeFormat = 1; /* UTC */
}else{
g.fTimeFormat = 2; /* Localtime */
}
}
return g.fTimeFormat==1;
}
/*
** Return true if Fossil is set to display times using the local timezone.
*/
int fossil_ui_localtime(void){
return fossil_ui_utctime()==0;
}
/*
** The toLocal() SQL function returns a string that is an argument to a
** date/time function that is appropriate for modifying the time for display.
** If UTC time display is selected, no modification occurs. If local time
** display is selected, the time is adjusted appropriately.
**
** Example usage:
**
** SELECT datetime('now',toLocal());
*/
void db_tolocal_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
if( fossil_ui_utctime() ){
sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
}else{
sqlite3_result_text(context, "localtime", -1, SQLITE_STATIC);
}
}
/*
|
| ︙ | ︙ | |||
1354 1355 1356 1357 1358 1359 1360 |
** SELECT julianday(:user_input,fromLocal());
*/
void db_fromlocal_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
| | < < < < < < < | 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 |
** SELECT julianday(:user_input,fromLocal());
*/
void db_fromlocal_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
if( fossil_ui_utctime() ){
sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
}else{
sqlite3_result_text(context, "utc", -1, SQLITE_STATIC);
}
}
/*
|
| ︙ | ︙ | |||
1522 1523 1524 1525 1526 1527 1528 |
/*
** Register the SQL functions that are useful both to the internal
** representation and to the "fossil sql" command.
*/
void db_add_aux_functions(sqlite3 *db){
sqlite3_create_collation(db, "uintnocase", SQLITE_UTF8,0,uintNocaseCollFunc);
| | > | | > | | > | | > | > | | > | | > | | 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 |
/*
** Register the SQL functions that are useful both to the internal
** representation and to the "fossil sql" command.
*/
void db_add_aux_functions(sqlite3 *db){
sqlite3_create_collation(db, "uintnocase", SQLITE_UTF8,0,uintNocaseCollFunc);
sqlite3_create_function(db, "checkin_mtime", 2,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_checkin_mtime_function, 0, 0);
sqlite3_create_function(db, "symbolic_name_to_rid", 1,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_sym2rid_function, 0, 0);
sqlite3_create_function(db, "symbolic_name_to_rid", 2,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_sym2rid_function, 0, 0);
sqlite3_create_function(db, "now", 0,
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
db_now_function, 0, 0);
sqlite3_create_function(db, "toLocal", 0,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_tolocal_function, 0, 0);
sqlite3_create_function(db, "fromLocal", 0,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_fromlocal_function, 0, 0);
sqlite3_create_function(db, "hextoblob", 1,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_hextoblob, 0, 0);
sqlite3_create_function(db, "capunion", 1, SQLITE_UTF8, 0,
0, capability_union_step, capability_union_finalize);
sqlite3_create_function(db, "fullcap", 1, SQLITE_UTF8, 0,
capability_fullcap, 0, 0);
sqlite3_create_function(db, "find_emailaddr", 1, SQLITE_UTF8, 0,
alert_find_emailaddr_func, 0, 0);
sqlite3_create_function(db, "display_name", 1, SQLITE_UTF8, 0,
|
| ︙ | ︙ | |||
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 |
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);
sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
file_inode_sql_func,0,0);
}
#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/
| > > | 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 |
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);
sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
file_inode_sql_func,0,0);
sqlite3_create_function(db, "artifact_to_json", 1, SQLITE_UTF8, 0,
artifact_to_json_sql_func,0,0);
}
#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/
|
| ︙ | ︙ | |||
2103 2104 2105 2106 2107 2108 2109 |
if( strcmp(blob_str(&bNameCheck), g.nameOfExe)==0 ){
extern int sqlite3_appendvfs_init(
sqlite3 *, char **, const sqlite3_api_routines *
);
sqlite3_appendvfs_init(0,0,0);
g.zVfsName = "apndvfs";
}
| | | 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 |
if( strcmp(blob_str(&bNameCheck), g.nameOfExe)==0 ){
extern int sqlite3_appendvfs_init(
sqlite3 *, char **, const sqlite3_api_routines *
);
sqlite3_appendvfs_init(0,0,0);
g.zVfsName = "apndvfs";
}
blob_reset(&bNameCheck);
rc = sqlite3_open_v2(
zDbName, &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
g.zVfsName
);
if( rc!=SQLITE_OK ){
db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
|
| ︙ | ︙ | |||
2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 |
sqlite3_finalize(pStmt);
sqlite3_close(db);
return res;
}
/*
** COMMAND: test-is-repo
*/
void test_is_repo(void){
int i;
for(i=2; i<g.argc; i++){
fossil_print("%s: %s\n",
db_looks_like_a_repository(g.argv[i]) ? "yes" : " no",
g.argv[i]
);
}
}
| > > > > > | 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 |
sqlite3_finalize(pStmt);
sqlite3_close(db);
return res;
}
/*
** COMMAND: test-is-repo
** Usage: %fossil test-is-repo FILENAME...
**
** Test whether the specified files look like a SQLite database
** containing a Fossil repository schema.
*/
void test_is_repo(void){
int i;
verify_all_options();
for(i=2; i<g.argc; i++){
fossil_print("%s: %s\n",
db_looks_like_a_repository(g.argv[i]) ? "yes" : " no",
g.argv[i]
);
}
}
|
| ︙ | ︙ | |||
3127 3128 3129 3130 3131 3132 3133 |
Blob hash;
Blob manifest;
db_unprotect(PROTECT_ALL);
db_set("content-schema", CONTENT_SCHEMA, 0);
db_set("aux-schema", AUX_SCHEMA_MAX, 0);
db_set("rebuilt", get_version(), 0);
| < < < < < < < | 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 |
Blob hash;
Blob manifest;
db_unprotect(PROTECT_ALL);
db_set("content-schema", CONTENT_SCHEMA, 0);
db_set("aux-schema", AUX_SCHEMA_MAX, 0);
db_set("rebuilt", get_version(), 0);
db_multi_exec(
"INSERT INTO config(name,value,mtime)"
" VALUES('server-code', lower(hex(randomblob(20))),now());"
"INSERT INTO config(name,value,mtime)"
" VALUES('project-code', lower(hex(randomblob(20))),now());"
);
db_create_default_users(0, zDefaultUser);
if( zDefaultUser ) g.zLogin = zDefaultUser;
user_select();
if( zTemplate ){
/*
** Copy all settings from the supplied template repository.
|
| ︙ | ︙ | |||
3297 3298 3299 3300 3301 3302 3303 |
db_end_transaction(0);
if( zTemplate ) db_detach("settingSrc");
if( zProjectName ) fossil_print("project-name: %s\n", zProjectName);
if( zProjectDesc ) fossil_print("project-description: %s\n", zProjectDesc);
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);
| | | 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 |
db_end_transaction(0);
if( zTemplate ) db_detach("settingSrc");
if( zProjectName ) fossil_print("project-name: %s\n", zProjectName);
if( zProjectDesc ) fossil_print("project-description: %s\n", zProjectDesc);
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 remote-access password is \"%s\")\n",
g.zLogin, zPassword);
hash_user_password(g.zLogin);
}
/*
** SQL functions for debugging.
**
|
| ︙ | ︙ | |||
3570 3571 3572 3573 3574 3575 3576 3577 | ** Return the text of the string if it is found. Return NULL if not ** found. ** ** If the zNonVersionedSetting parameter is not NULL then it holds the ** non-versioned value for this setting. If both a versioned and a ** non-versioned value exist and are not equal, then a warning message ** might be generated. */ | > > > > > > > | > > > > | > > > > | | | | | | | | > > | | < < | < > > | | > | | | < < < < < | | | | | | | | | | | > > > | | | | | | > > | | > > > > | 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 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 3686 3687 3688 3689 3690 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 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 |
** Return the text of the string if it is found. Return NULL if not
** found.
**
** If the zNonVersionedSetting parameter is not NULL then it holds the
** non-versioned value for this setting. If both a versioned and a
** non-versioned value exist and are not equal, then a warning message
** might be generated.
**
** zCkin is normally NULL. In that case, the versioned setting is
** take from the local check-out, if a local checkout exists, or from
** checkin named by the g.zOpenRevision global variable. If zCkin is
** not NULL, then zCkin is the name of the specific checkin from which
** versioned setting value is taken. When zCkin is not NULL, the cache
** is bypassed.
*/
char *db_get_versioned(
const char *zName,
char *zNonVersionedSetting,
const char *zCkin
){
char *zVersionedSetting = 0;
int noWarn = 0;
int found = 0;
struct _cacheEntry {
struct _cacheEntry *next;
const char *zName, *zValue;
} *cacheEntry = 0;
static struct _cacheEntry *cache = 0;
if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){
return zNonVersionedSetting;
}
/* Look up name in cache */
if( zCkin==0 ){
cacheEntry = cache;
while( cacheEntry!=0 ){
if( fossil_strcmp(cacheEntry->zName, zName)==0 ){
zVersionedSetting = fossil_strdup(cacheEntry->zValue);
break;
}
cacheEntry = cacheEntry->next;
}
}
/* Attempt to read value from file in check-out if there wasn't a cache hit.*/
if( cacheEntry==0 ){
Blob versionedPathname;
Blob setting;
blob_init(&versionedPathname, 0, 0);
blob_init(&setting, 0, 0);
if( !g.localOpen || zCkin!=0 ){
/* Repository is in the process of being opened, but files have not been
* written to disk. Load from the database. */
blob_appendf(&versionedPathname, ".fossil-settings/%s", zName);
if( historical_blob(zCkin ? zCkin : g.zOpenRevision,
blob_str(&versionedPathname),
&setting, 0)
){
found = 1;
}
}else{
blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
g.zLocalRoot, zName);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
/* File exists, and contains the value for this setting. Load from
** the file. */
const char *zFile = blob_str(&versionedPathname);
if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){
found = 1;
}
/* See if there's a no-warn flag */
blob_append(&versionedPathname, ".no-warn", -1);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
noWarn = 1;
}
}
}
blob_reset(&versionedPathname);
if( found ){
blob_strip_comment_lines(&setting, &setting);
blob_trim(&setting); /* Avoid non-obvious problems with line endings
** on boolean properties */
zVersionedSetting = fossil_strdup(blob_str(&setting));
}
blob_reset(&setting);
/* Store result in cache, which can be the value or 0 if not found */
if( zCkin==0 ){
cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(*cacheEntry));
cacheEntry->next = cache;
cacheEntry->zName = zName;
cacheEntry->zValue = fossil_strdup(zVersionedSetting);
cache = cacheEntry;
}
}
/* Display a warning? */
if( zVersionedSetting!=0
&& zNonVersionedSetting!=0
&& zNonVersionedSetting[0]!='\0'
&& zCkin==0
&& !noWarn
){
/* There's a versioned setting, and a non-versioned setting. Tell
** the user about the conflict */
fossil_warning(
"setting %s has both versioned and non-versioned values: using "
"versioned value from file \"%/.fossil-settings/%s\" (to silence "
"this warning, either create an empty file named "
"\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete "
"the non-versioned setting with \"fossil unset %s\")", zName,
g.zLocalRoot, zName, g.zLocalRoot, zName, zName
);
}
/* Prefer the versioned setting */
return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting;
}
/*
** Get and set values from the CONFIG, GLOBAL_CONFIG and VVAR table in the
|
| ︙ | ︙ | |||
3701 3702 3703 3704 3705 3706 3707 |
}
db_reset(&q2);
}
if( pSetting!=0 && pSetting->versionable ){
/* This is a versionable setting, try and get the info from a
** checked-out file */
char * zZ = z;
| | | 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 |
}
db_reset(&q2);
}
if( pSetting!=0 && pSetting->versionable ){
/* This is a versionable setting, try and get the info from a
** checked-out file */
char * zZ = z;
z = db_get_versioned(zName, z, 0);
if(zZ != z){
fossil_free(zZ);
}
}
if( z==0 ){
if( zDefault==0 && pSetting && pSetting->def[0] ){
z = fossil_strdup(pSetting->def);
|
| ︙ | ︙ | |||
3842 3843 3844 3845 3846 3847 3848 |
}else if( is_false(zVal) ){
dflt = 0;
}
fossil_free(zVal);
return dflt;
}
int db_get_versioned_boolean(const char *zName, int dflt){
| | | 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 |
}else if( is_false(zVal) ){
dflt = 0;
}
fossil_free(zVal);
return dflt;
}
int db_get_versioned_boolean(const char *zName, int dflt){
char *zVal = db_get_versioned(zName, 0, 0);
if( zVal==0 ) return dflt;
if( is_truth(zVal) ) return 1;
if( is_false(zVal) ) return 0;
return dflt;
}
char *db_lget(const char *zName, const char *zDefault){
return db_text(zDefault,
|
| ︙ | ︙ | |||
3971 3972 3973 3974 3975 3976 3977 3978 | /* ** Get the manifest setting. For backwards compatibility first check if the ** value is a boolean. If it's not a boolean, treat each character as a flag ** to enable a manifest type. This system puts certain boundary conditions on ** which letters can be used to represent flags (any permutation of flags must ** not be able to fully form one of the boolean values). */ | > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 |
/*
** Get the manifest setting. For backwards compatibility first check if the
** value is a boolean. If it's not a boolean, treat each character as a flag
** to enable a manifest type. This system puts certain boundary conditions on
** which letters can be used to represent flags (any permutation of flags must
** not be able to fully form one of the boolean values).
**
** "manifest" is a versionable setting. But we do not issue a warning
** if there is a conflict. Instead, the value returned is the value for
** the versioned setting if the versioned setting exists, or the ordinary
** setting otherwise.
**
** The argument zCkin is the specific check-in for which we want the
** manifest setting.
*/
int db_get_manifest_setting(const char *zCkin){
int flg;
char *zVal;
/* Look for the versioned setting first */
zVal = db_get_versioned("manifest", 0, zCkin);
if( zVal==0 && g.repositoryOpen ){
/* No versioned setting, look for the repository setting second */
zVal = db_text(0, "SELECT value FROM config WHERE name='manifest'");
}
if( zVal==0 || is_false(zVal) ){
return 0;
}else if( is_truth(zVal) ){
return MFESTFLG_RAW|MFESTFLG_UUID;
}
flg = 0;
while( *zVal ){
switch( *zVal ){
case 'r': flg |= MFESTFLG_RAW; break;
case 'u': flg |= MFESTFLG_UUID; break;
case 't': flg |= MFESTFLG_TAGS; break;
}
zVal++;
}
return flg;
}
/*
** COMMAND: test-manifest-setting
**
** Usage: %fossil test-manifest-setting VERSION VERSION ...
**
** Display the value for the "manifest" setting for various versions
** of the repository.
*/
void test_manfest_setting_cmd(void){
int i;
db_find_and_open_repository(0, 0);
for(i=2; i<g.argc; i++){
int m = db_get_manifest_setting(g.argv[i]);
fossil_print("%s:\n", g.argv[i]);
fossil_print(" flags = 0x%02x\n", m);
if( m & MFESTFLG_RAW ){
fossil_print(" manifest\n");
}
if( m & MFESTFLG_UUID ){
fossil_print(" manifest.uuid\n");
}
if( m & MFESTFLG_TAGS ){
fossil_print(" manifest.tags\n");
}
}
}
/*
** Record the name of a local repository in the global_config() database.
** The repository filename %s is recorded as an entry with a "name" field
** of the following form:
**
|
| ︙ | ︙ | |||
4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 | ** -f|--force Continue with the open even if the working directory is ** not empty, or if auto-sync fails. ** --force-missing Force opening a repository with missing content ** -k|--keep Only modify the manifest file(s) ** --nested Allow opening a repository inside an opened check-out ** --nosync Do not auto-sync the repository prior to opening even ** if the autosync setting is on. ** --repodir DIR If REPOSITORY is a URI that will be cloned, store ** the clone in DIR rather than in "." ** --setmtime Set timestamps of all files to match their SCM-side ** times (the timestamp of the last check-in which modified ** them). ** --verbose If passed a URI then this flag is passed on to the clone ** operation, otherwise it has no effect | > | 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 | ** -f|--force Continue with the open even if the working directory is ** not empty, or if auto-sync fails. ** --force-missing Force opening a repository with missing content ** -k|--keep Only modify the manifest file(s) ** --nested Allow opening a repository inside an opened check-out ** --nosync Do not auto-sync the repository prior to opening even ** if the autosync setting is on. ** --proxy PROXY Use PROXY as http proxy during sync operation ** --repodir DIR If REPOSITORY is a URI that will be cloned, store ** the clone in DIR rather than in "." ** --setmtime Set timestamps of all files to match their SCM-side ** times (the timestamp of the last check-in which modified ** them). ** --verbose If passed a URI then this flag is passed on to the clone ** operation, otherwise it has no effect |
| ︙ | ︙ | |||
4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 |
if(vid!=0){
vfile_check_signature(vid, CKSIG_SETMTIME);
}
}
g.argc = 2;
info_cmd();
}
/*
** Print the current value of a setting identified by the pSetting
** pointer.
*/
| > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > | | | > > > > > > > > > > > > > > > | > > > > > > | | 4428 4429 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 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 |
if(vid!=0){
vfile_check_signature(vid, CKSIG_SETMTIME);
}
}
g.argc = 2;
info_cmd();
}
/*
** Return true if pSetting has its default value assuming its
** current value is zVal.
*/
int setting_has_default_value(const Setting *pSetting, const char *zVal){
if( zVal==0 ) return 1;
if( pSetting->def==0 ) return 0;
if( pSetting->width==0 ){
return is_false(pSetting->def)==is_false(zVal);
}
if( fossil_strcmp(pSetting->def, zVal)==0 ) return 1;
if( is_false(zVal) && is_false(pSetting->def) ) return 1;
if( is_truth(zVal) && is_truth(pSetting->def) ) return 1;
return 0;
}
/*
** Print the current value of a setting identified by the pSetting
** pointer.
**
** Only show the value, not the setting name, if valueOnly is true.
**
** Show nothing if bIfChng is true and the setting is not currently set
** or is set to its default value.
*/
void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){
Stmt q;
int versioned = 0;
if( pSetting->versionable && g.localOpen ){
/* Check to see if this is overridden by a versionable settings file */
Blob versionedPathname;
blob_zero(&versionedPathname);
blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
g.zLocalRoot, pSetting->name);
if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
versioned = 1;
}
blob_reset(&versionedPathname);
}
if( valueOnly && versioned ){
const char *zVal = db_get_versioned(pSetting->name, NULL, NULL);
if( !bIfChng || (zVal!=0 && fossil_strcmp(zVal, pSetting->def)!=0) ){
fossil_print("%s\n", db_get_versioned(pSetting->name, NULL, NULL));
}else{
versioned = 0;
}
return;
}
if( g.repositoryOpen ){
db_prepare(&q,
"SELECT '(local)', value FROM config WHERE name=%Q"
" UNION ALL "
"SELECT '(global)', value FROM global_config WHERE name=%Q",
pSetting->name, pSetting->name
);
}else{
db_prepare(&q,
"SELECT '(global)', value FROM global_config WHERE name=%Q",
pSetting->name
);
}
if( db_step(&q)==SQLITE_ROW ){
const char *zVal = db_column_text(&q,1);
if( bIfChng && setting_has_default_value(pSetting,zVal) ){
if( versioned ){
fossil_print("%-24s (versioned)\n", pSetting->name);
versioned = 0;
}
}else if( valueOnly ){
fossil_print("%s\n", db_column_text(&q, 1));
}else{
const char *zVal = (const char*)db_column_text(&q,1);
const char *zName = (const char*)db_column_text(&q,0);
if( zVal==0 ) zVal = "NULL";
if( strchr(zVal,'\n')==0 ){
fossil_print("%-24s %-11s %s\n", pSetting->name, zName, zVal);
}else{
fossil_print("%-24s %-11s\n", pSetting->name, zName);
while( zVal[0] ){
char *zNL = strchr(zVal, '\n');
if( zNL==0 ){
fossil_print(" %s\n", zVal);
break;
}else{
int n = (int)(zNL - zVal);
while( n>0 && fossil_isspace(zVal[n-1]) ){ n--; }
fossil_print(" %.*s\n", n, zVal);
zVal = zNL+1;
}
}
}
}
}else if( bIfChng ){
/* Display nothing */
versioned = 0;
}else if( valueOnly ){
fossil_print("\n");
}else{
fossil_print("%-24s\n", pSetting->name);
}
if( versioned ){
fossil_print(" (overridden by contents of file .fossil-settings/%s)\n",
pSetting->name);
}
db_finalize(&q);
}
|
| ︙ | ︙ | |||
4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 |
** negative for values which should not appear
** on the /setup_settings page. */
char versionable; /* Is this setting versionable? */
char forceTextArea; /* Force using a text area for display? */
char sensitive; /* True if this a security-sensitive setting */
const char *def; /* Default value */
};
#endif /* INTERFACE */
/*
| > | | | 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 |
** negative for values which should not appear
** on the /setup_settings page. */
char versionable; /* Is this setting versionable? */
char forceTextArea; /* Force using a text area for display? */
char sensitive; /* True if this a security-sensitive setting */
const char *def; /* Default value */
};
#endif /* INTERFACE */
/*
** SETTING: access-log boolean default=on
**
** When the access-log setting is enabled, all login attempts (successful
** and unsuccessful) on the web interface are recorded in the "access" table
** of the repository.
*/
/*
** SETTING: admin-log boolean default=on
**
** 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
**
|
| ︙ | ︙ | |||
4559 4560 4561 4562 4563 4564 4565 | ** that the "clean" command will delete without prompting or allowing ** undo. Example: *.a,*.o,*.so The parsing rules are complex; ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ /* ** SETTING: clearsign boolean default=off ** When enabled, fossil will attempt to sign all commits | | | < < < | > > > > < | | < < < | 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 | ** that the "clean" command will delete without prompting or allowing ** undo. Example: *.a,*.o,*.so The parsing rules are complex; ** see https://fossil-scm.org/home/doc/trunk/www/globs.md#syntax */ /* ** SETTING: clearsign boolean default=off ** When enabled, fossil will attempt to sign all commits ** with gpg or ssh. When disabled, commits will be unsigned. */ /* ** SETTING: comment-format width=16 default=1 ** Set the algorithm for printing timeline comments to the console. ** ** Possible values are: ** 1 Use the original comment printing algorithm: ** * Leading and trailing whitespace is removed ** * Internal whitespace is converted into a single space (0x20) ** * Line breaks occurs at whitespace or hyphens if possible ** This is the recommended value and the default. ** ** Or a bitwise combination of the following flags: ** 2 Trim leading and trailing CR and LF characters. ** 4 Trim leading and trailing white space characters. ** 8 Attempt to break lines on word boundaries. ** 16 Break lines before the original comment embedded in other text. ** ** Note: To preserve line breaks and/or other whitespace within comment text, ** make this setting some integer value that omits the "1" bit. */ /* ** SETTING: crlf-glob width=40 versionable block-text ** The VALUE of this setting is a list of GLOB patterns matching files ** in which it is allowed to have CR, CR+LF or mixed line endings, ** suppressing Fossil's normal warning about this. Set it to "*" to ** disable CR+LF checking entirely. Example: *.md,*.txt |
| ︙ | ︙ | |||
4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 | ** SETTING: dotfiles boolean versionable default=off ** If enabled, include --dotfiles option for all compatible commands. */ /* ** SETTING: editor width=32 sensitive ** The value is an external command that will launch the ** text editor command used for check-in comments. */ /* ** SETTING: empty-dirs width=40 versionable block-text ** The value is a list of pathnames parsed according to the same rules as ** the *-glob settings. On update and checkout commands, if no directory ** exists with that name, an empty directory will be be created, even if ** it must create one or more parent directories. | > > > > > > > > | 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 |
** SETTING: dotfiles boolean versionable default=off
** If enabled, include --dotfiles option for all compatible commands.
*/
/*
** SETTING: editor width=32 sensitive
** The value is an external command that will launch the
** text editor command used for check-in comments.
**
** If this value is not set, then environment variables VISUAL and
** EDITOR are consulted, in that order. If neither of those are set,
** then a search is made for common text editors, including
** "notepad", "nano", "pico", "jove", "edit", "vi", "vim", and "ed".
**
** If this setting is false ("off", "no", "false", or "0") then no
** text editor is used.
*/
/*
** SETTING: empty-dirs width=40 versionable block-text
** The value is a list of pathnames parsed according to the same rules as
** the *-glob settings. On update and checkout commands, if no directory
** exists with that name, an empty directory will be be created, even if
** it must create one or more parent directories.
|
| ︙ | ︙ | |||
4682 4683 4684 4685 4686 4687 4688 | ** 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. */ /* | | | > | 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 | ** 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: gdiff-command width=40 sensitive ** The value is an external command to run when performing a graphical ** diff. If undefined, a --tk diff is done if commands "tclsh" and "wish" ** are on PATH, or a --by diff is done if "tclsh" or "wish" are unavailable. */ /* ** SETTING: gmerge-command width=40 sensitive ** The value is a graphical merge conflict resolver command operating ** on four files. Examples: ** ** kdiff3 "%baseline" "%original" "%merge" -o "%output" |
| ︙ | ︙ | |||
4825 4826 4827 4828 4829 4830 4831 | ** the associated files within the check-out -AND- the "rm" ** and "delete" commands will also remove the associated ** files from within the check-out. */ /* ** SETTING: pgp-command width=40 sensitive ** Command used to clear-sign manifests at check-in. | | > | 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 | ** the associated files within the check-out -AND- the "rm" ** and "delete" commands will also remove the associated ** files from within the check-out. */ /* ** SETTING: pgp-command width=40 sensitive ** Command used to clear-sign manifests at check-in. ** Default value is "gpg --clearsign -o". ** For SSH, use e.g. "ssh-keygen -q -Y sign -n fossilscm -f ~/.ssh/id_ed25519" */ /* ** SETTING: proxy width=32 default=system ** URL of the HTTP proxy. If undefined or "system", the "http_proxy" ** environment variable is consulted. If "off", a direct HTTP connection is ** used. */ |
| ︙ | ︙ | |||
4871 4872 4873 4874 4875 4876 4877 | ** 5) fossil all ui ** 6) fossil all server ** ** All repositories are searched (in lexicographical order) and the first ** repository with a non-zero "repolist-skin" value is used as the skin ** for the repository list page. If none of the repositories on the list ** have a non-zero "repolist-skin" setting then the repository list is | | > | 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 |
** 5) fossil all ui
** 6) fossil all server
**
** All repositories are searched (in lexicographical order) and the first
** repository with a non-zero "repolist-skin" value is used as the skin
** for the repository list page. If none of the repositories on the list
** have a non-zero "repolist-skin" setting then the repository list is
** displayed using unadorned HTML ("skinless"), with the page title taken
** from the FOSSIL_REPOLIST_TITLE environment variable.
**
** If repolist-skin has a value of 2, then the repository is omitted from
** the list in use cases 1 through 4, but not for 5 and 6.
*/
/*
** SETTING: self-pw-reset boolean default=off sensitive
** Allow users to request that an email containing a hyperlink
|
| ︙ | ︙ | |||
5076 5077 5078 5079 5080 5081 5082 5083 5084 | ** that applies to all repositories. The local values are stored in the ** "config" table of the repository and the global values are stored in the ** configuration database. If both a local and a global value exists for a ** setting, the local value takes precedence. This command normally operates ** on the local settings. Use the --global option to change global settings. ** ** Options: ** --global Set or unset the given property globally instead of ** setting or unsetting it for the open repository only | > > < > | 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 |
** that applies to all repositories. The local values are stored in the
** "config" table of the repository and the global values are stored in the
** configuration database. If both a local and a global value exists for a
** setting, the local value takes precedence. This command normally operates
** on the local settings. Use the --global option to change global settings.
**
** Options:
** --changed Only show settings if the value differs from the default
** --exact Only consider exact name matches
** --global Set or unset the given property globally instead of
** setting or unsetting it for the open repository only
** --value Only show the value of a given property (implies --exact)
**
** See also: [[configuration]]
*/
void setting_cmd(void){
int i;
int globalFlag = find_option("global","g",0)!=0;
int bIfChng = find_option("changed",0,0)!=0;
int exactFlag = find_option("exact",0,0)!=0;
int valueFlag = find_option("value",0,0)!=0;
/* Undocumented "--test-for-subsystem SUBSYS" option used to test
** the db_get_for_subsystem() interface: */
const char *zSubsys = find_option("test-for-subsystem",0,1);
int unsetFlag = g.argv[1][0]=='u';
int nSetting;
|
| ︙ | ︙ | |||
5110 5111 5112 5113 5114 5115 5116 |
if( unsetFlag && g.argc!=3 ){
usage("PROPERTY ?-global?");
}
if( valueFlag ){
if( g.argc!=3 ){
fossil_fatal("--value is only supported when qurying a given property");
}
| < | | 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 |
if( unsetFlag && g.argc!=3 ){
usage("PROPERTY ?-global?");
}
if( valueFlag ){
if( g.argc!=3 ){
fossil_fatal("--value is only supported when qurying a given property");
}
}
if( g.argc==2 ){
for(i=0; i<nSetting; i++){
print_setting(&aSetting[i], 0, bIfChng);
}
}else if( g.argc==3 || g.argc==4 ){
const char *zName = g.argv[2];
int n = (int)strlen(zName);
const Setting *pSetting = db_find_setting(zName, !exactFlag);
if( pSetting==0 ){
fossil_fatal("no such setting: %s", zName);
|
| ︙ | ︙ | |||
5170 5171 5172 5173 5174 5175 5176 |
fossil_print("%s (subsystem %s) ->", pSetting->name, zSubsys);
if( zValue ){
fossil_print(" [%s]", zValue);
fossil_free(zValue);
}
fossil_print("\n");
}else{
| | | 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 |
fossil_print("%s (subsystem %s) ->", pSetting->name, zSubsys);
if( zValue ){
fossil_print(" [%s]", zValue);
fossil_free(zValue);
}
fossil_print("\n");
}else{
print_setting(pSetting, valueFlag, bIfChng);
}
pSetting++;
}
}
}else{
usage("?PROPERTY? ?VALUE? ?-global?");
}
|
| ︙ | ︙ |
Changes to src/default.css.
| ︙ | ︙ | |||
710 711 712 713 714 715 716 717 718 719 720 721 722 723 |
border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
border-left: 1px solid gold;
}
body.cpage-ckout .file-change-line,
body.cpage-info .file-change-line,
body.cpage-vdiff .file-change-line {
margin-top: 16px;
margin-bottom: 16px;
margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
display: flex;
flex-direction: row;
justify-content: space-between;
| > > | 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 |
border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
border-left: 1px solid gold;
}
body.cpage-ckout .file-change-line,
body.cpage-info .file-change-line,
body.cpage-vinfo .file-change-line,
body.cpage-ci .file-change-line,
body.cpage-vdiff .file-change-line {
margin-top: 16px;
margin-bottom: 16px;
margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
display: flex;
flex-direction: row;
justify-content: space-between;
|
| ︙ | ︙ |
Changes to src/descendants.c.
| ︙ | ︙ | |||
154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
" WHERE tagxref.rid=leaves.rid "
" AND tagxref.tagid=%d"
" AND tagxref.tagtype>0)",
TAG_CLOSED
);
}
}
/*
** Load the record ID rid and up to |N|-1 closest ancestors into
** the "ok" table. If N is zero, no limit. If ridBackTo is not zero
** then stop the search upon reaching the ancestor with rid==ridBackTo.
*/
void compute_ancestors(int rid, int N, int directOnly, int ridBackTo){
| > > > > > > > > > > > > > > > > > | 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 |
" WHERE tagxref.rid=leaves.rid "
" AND tagxref.tagid=%d"
" AND tagxref.tagtype>0)",
TAG_CLOSED
);
}
}
/*
** If RID refers to a check-in, return the mtime of that check-in - the
** julian day number of when the check-in occurred.
*/
double mtime_of_rid(int rid, double mtime){
static Stmt q;
db_static_prepare(&q,"SELECT mtime FROM event WHERE objid=:rid");
db_bind_int(&q, ":rid", rid);
if( db_step(&q)==SQLITE_ROW ){
mtime = db_column_double(&q,0);
}
db_reset(&q);
return mtime;
}
/*
** Load the record ID rid and up to |N|-1 closest ancestors into
** the "ok" table. If N is zero, no limit. If ridBackTo is not zero
** then stop the search upon reaching the ancestor with rid==ridBackTo.
*/
void compute_ancestors(int rid, int N, int directOnly, int ridBackTo){
|
| ︙ | ︙ | |||
195 196 197 198 199 200 201 |
** (1) Primary parents
** (2) Merge parents
** (3) Cherrypick merge parents.
** (4) All ancestores of 1 and 2 but not of 3.
*/
double rLimitMtime = 0.0;
if( ridBackTo ){
| | < < | | | | | | | | | | | | | | | | | | < | 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 |
** (1) Primary parents
** (2) Merge parents
** (3) Cherrypick merge parents.
** (4) All ancestores of 1 and 2 but not of 3.
*/
double rLimitMtime = 0.0;
if( ridBackTo ){
rLimitMtime = mtime_of_rid(ridBackTo, 0.0);
}
db_multi_exec(
"WITH RECURSIVE\n"
" parent(pid,cid,isCP) AS (\n"
" SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n"
" UNION ALL\n"
" SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude\n"
" ),\n"
" ancestor(rid, mtime, isCP) AS (\n"
" SELECT %d, mtime, 0 FROM event WHERE objid=%d\n"
" UNION\n"
" SELECT parent.pid, event.mtime, parent.isCP\n"
" FROM ancestor, parent, event\n"
" WHERE parent.cid=ancestor.rid\n"
" AND event.objid=parent.pid\n"
" AND NOT ancestor.isCP\n"
" AND (event.mtime>=%.17g OR parent.pid=%d)\n"
" ORDER BY mtime DESC LIMIT %d\n"
" )\n"
"INSERT OR IGNORE INTO ok SELECT rid FROM ancestor;",
rid, rid, rLimitMtime, ridBackTo, N
);
if( ridBackTo && db_changes()>1 ){
db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
}
}
}
|
| ︙ | ︙ | |||
320 321 322 323 324 325 326 |
void compute_descendants(int rid, int N){
if( !N ){
N = -1;
}else if( N<0 ){
N = -N;
}
db_multi_exec(
| | | | | | | | | | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
void compute_descendants(int rid, int N){
if( !N ){
N = -1;
}else if( N<0 ){
N = -N;
}
db_multi_exec(
"WITH RECURSIVE\n"
" dx(rid,mtime) AS (\n"
" SELECT %d, 0\n"
" UNION\n"
" SELECT plink.cid, plink.mtime FROM dx, plink\n"
" WHERE plink.pid=dx.rid\n"
" ORDER BY 2\n"
" )\n"
"INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
rid, N
);
}
/*
** COMMAND: descendants*
|
| ︙ | ︙ | |||
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 |
#if INTERFACE
/* Flag parameters to compute_uses_file() */
#define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
#endif
/*
** Add to table zTab the record ID (rid) of every check-in that contains
** the file fid.
*/
void compute_uses_file(const char *zTab, int fid, int usesFlags){
Bag seen;
Bag pending;
| > > > > > > > > > > > > > > > | > | | < < | < < | < < > | | 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 |
#if INTERFACE
/* Flag parameters to compute_uses_file() */
#define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
#endif
/*
** Append a new VALUES term.
*/
static void uses_file_append_term(Blob *pSql, int *pnCnt, int rid){
if( *pnCnt==0 ){
blob_append_sql(pSql, "(%d)", rid);
*pnCnt = 4;
}else if( (*pnCnt)%10==9 ){
blob_append_sql(pSql, ",\n (%d)", rid);
}else{
blob_append_sql(pSql, ",(%d)", rid);
}
++*pnCnt;
}
/*
** Add to table zTab the record ID (rid) of every check-in that contains
** the file fid.
*/
void compute_uses_file(const char *zTab, int fid, int usesFlags){
Bag seen;
Bag pending;
Blob ins = BLOB_INITIALIZER;
int nIns = 0;
Stmt q;
int rid;
bag_init(&seen);
bag_init(&pending);
blob_append_sql(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES", zTab);
db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
while( db_step(&q)==SQLITE_ROW ){
int mid = db_column_int(&q, 0);
bag_insert(&pending, mid);
bag_insert(&seen, mid);
uses_file_append_term(&ins, &nIns, mid);
}
db_finalize(&q);
db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid);
while( db_step(&q)==SQLITE_ROW ){
int mid = db_column_int(&q, 0);
bag_insert(&seen, mid);
if( usesFlags & USESFILE_DELETE ){
uses_file_append_term(&ins, &nIns, mid);
}
}
db_finalize(&q);
db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid AND isprim");
while( (rid = bag_first(&pending))!=0 ){
bag_remove(&pending, rid);
db_bind_int(&q, ":rid", rid);
while( db_step(&q)==SQLITE_ROW ){
int mid = db_column_int(&q, 0);
if( bag_find(&seen, mid) ) continue;
bag_insert(&seen, mid);
bag_insert(&pending, mid);
uses_file_append_term(&ins, &nIns, mid);
}
db_reset(&q);
}
db_finalize(&q);
db_exec_sql(blob_str(&ins));
blob_reset(&ins);
bag_clear(&seen);
bag_clear(&pending);
}
|
Changes to src/diff.c.
| ︙ | ︙ | |||
106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 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 */ const char *zBinGlob; /* GLOB pattern for binary files */ ReCompiled *pRe; /* Show only changes matching this pattern */ const char *zLeftHash; /* HASH-id of the left file */ }; #endif /* INTERFACE */ /* ** Initialize memory for a DiffConfig based on just a diffFlags integer. */ | > | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 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 */ const char *zBinGlob; /* GLOB pattern for binary files */ ReCompiled *pRe; /* Show only changes matching this pattern */ const char *zLeftHash; /* HASH-id of the left file */ const char *azLabel[2]; /* Optional labels for left and right files */ }; #endif /* INTERFACE */ /* ** Initialize memory for a DiffConfig based on just a diffFlags integer. */ |
| ︙ | ︙ | |||
3057 3058 3059 3060 3061 3062 3063 |
nDel += c.aEdit[i+1];
nIns += c.aEdit[i+2];
}
g.diffCnt[1] += nIns;
g.diffCnt[2] += nDel;
if( nIns+nDel ){
g.diffCnt[0]++;
| > | > | 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 |
nDel += c.aEdit[i+1];
nIns += c.aEdit[i+2];
}
g.diffCnt[1] += nIns;
g.diffCnt[2] += nDel;
if( nIns+nDel ){
g.diffCnt[0]++;
if( !(pCfg->diffFlags & DIFF_BRIEF) ){
blob_appendf(pOut, "%10d %10d", nIns, nDel);
}
}
}else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
const int *R = c.aEdit;
unsigned int r;
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
blob_appendf(pOut, " copy %6d delete %6d insert %6d\n",
R[r], R[r+1], R[r+2]);
|
| ︙ | ︙ | |||
3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 | ** --by Shorthand for "--browser -y" ** -c|--context N N lines of context. nContext ** --dark Use dark mode for Tcl/Tk and HTML output ** --html Format for HTML DIFF_HTML ** -i|--internal Use built-in diff, not an external tool ** --invert Invert the diff DIFF_INVERT ** --json Output formatted as JSON ** -n|--linenum Show line numbers DIFF_LINENO ** -N|--new-file Alias for --verbose ** --noopt Disable optimization DIFF_NOOPT ** --numstat Show change counts DIFF_NUMSTAT ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR ** --tcl Tcl-formatted output used internally by --tk ** --unified Unified diff. ~DIFF_SIDEBYSIDE | > | 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 | ** --by Shorthand for "--browser -y" ** -c|--context N N lines of context. nContext ** --dark Use dark mode for Tcl/Tk and HTML output ** --html Format for HTML DIFF_HTML ** -i|--internal Use built-in diff, not an external tool ** --invert Invert the diff DIFF_INVERT ** --json Output formatted as JSON ** --label NAME Column label. Can be repeated once. ** -n|--linenum Show line numbers DIFF_LINENO ** -N|--new-file Alias for --verbose ** --noopt Disable optimization DIFF_NOOPT ** --numstat Show change counts DIFF_NUMSTAT ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR ** --tcl Tcl-formatted output used internally by --tk ** --unified Unified diff. ~DIFF_SIDEBYSIDE |
| ︙ | ︙ | |||
3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 |
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;
if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS;
if( find_option("dark",0,0)!=0 ) diffFlags |= DIFF_DARKMODE;
if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
| > > > > | 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 |
pCfg->nContext = f;
diffFlags |= DIFF_CONTEXT_EX;
}
}
if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
pCfg->wColumn = f;
}
pCfg->azLabel[0] = find_option("label",0,1);
if( pCfg->azLabel[0] ){
pCfg->azLabel[1] = find_option("label",0,1);
}
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;
if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS;
if( find_option("dark",0,0)!=0 ) diffFlags |= DIFF_DARKMODE;
if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
|
| ︙ | ︙ | |||
3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 |
pCfg->zBinGlob = diff_get_binary_glob();
zDiffBinary = find_option("diff-binary", 0, 1);
if( zDiffBinary ){
if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY;
}else if( db_get_boolean("diff-binary", 1) ){
diffFlags |= DIFF_INCBINARY;
}
}
}
if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
/* Deprecated, but retained for script compatibility. */
else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE;
pCfg->diffFlags = diffFlags;
| > > > > | 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 |
pCfg->zBinGlob = diff_get_binary_glob();
zDiffBinary = find_option("diff-binary", 0, 1);
if( zDiffBinary ){
if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY;
}else if( db_get_boolean("diff-binary", 1) ){
diffFlags |= DIFF_INCBINARY;
}
}else if( isGDiff) {
/* No external gdiff command found, using --by */
diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER
|DIFF_SIDEBYSIDE;
}
}
if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
/* Deprecated, but retained for script compatibility. */
else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE;
pCfg->diffFlags = diffFlags;
|
| ︙ | ︙ | |||
3236 3237 3238 3239 3240 3241 3242 | ** In other words, this command provides a mechanism to use Fossil's file ** difference engine on arbitrary disk files. See the "diff" command for ** computing differences between files that are under management. ** ** This command prints the differences between the two files FILE1 and FILE2. ** all of the usual diff formatting options (--tk, --by, -c N, etc.) apply. ** See the "diff" command for a full list of command-line options. | < < < | 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 |
** In other words, this command provides a mechanism to use Fossil's file
** difference engine on arbitrary disk files. See the "diff" command for
** computing differences between files that are under management.
**
** This command prints the differences between the two files FILE1 and FILE2.
** all of the usual diff formatting options (--tk, --by, -c N, etc.) apply.
** See the "diff" command for a full list of command-line options.
*/
void xdiff_cmd(void){
Blob a, b, out;
const char *zRe; /* Regex filter for diff output */
DiffConfig DCfg;
if( find_option("tk",0,0)!=0 ){
|
| ︙ | ︙ | |||
3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 |
verify_all_options();
if( g.argc!=4 ) usage("FILE1 FILE2");
blob_zero(&out);
diff_begin(&DCfg);
diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
blob_read_from_file(&a, g.argv[2], ExtFILE);
blob_read_from_file(&b, g.argv[3], ExtFILE);
text_diff(&a, &b, &out, &DCfg);
blob_write_to_file(&out, "-");
diff_end(&DCfg, 0);
re_free(DCfg.pRe);
}
/**************************************************************************
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
verify_all_options();
if( g.argc!=4 ) usage("FILE1 FILE2");
blob_zero(&out);
diff_begin(&DCfg);
diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
blob_read_from_file(&a, g.argv[2], ExtFILE);
blob_read_from_file(&b, g.argv[3], ExtFILE);
text_diff(&a, &b, &out, &DCfg);
blob_write_to_file(&out, "-");
diff_end(&DCfg, 0);
re_free(DCfg.pRe);
}
/*
** COMMAND: fdiff
**
** Usage: %fossil fdiff [options] HASH1 HASH2
**
** Compute a diff between two artifacts in a repository (either a repository
** identified by the "-R FILENAME" option, or the repository that contains
** the working directory).
**
** All of the usual diff formatting options (--tk, --by, -c N, etc.) apply.
** See the "diff" command for a full list of command-line options.
*/
void fdiff_cmd(void){
Blob a, b, out;
const char *zRe; /* Regex filter for diff output */
int rid;
DiffConfig DCfg;
if( find_option("tk",0,0)!=0 ){
diff_tk("fdiff", 2);
return;
}
find_option("i",0,0);
find_option("v",0,0);
diff_options(&DCfg, 0, 0);
zRe = find_option("regexp","e",1);
if( zRe ){
const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
if( zErr ) fossil_fatal("regex error: %s", zErr);
}
db_find_and_open_repository(0, 0);
verify_all_options();
if( g.argc!=4 ) usage("HASH1 HASH2");
blob_zero(&out);
diff_begin(&DCfg);
diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
rid = name_to_typed_rid(g.argv[2], 0);
content_get(rid, &a);
rid = name_to_typed_rid(g.argv[3], 0);
content_get(rid, &b);
text_diff(&a, &b, &out, &DCfg);
blob_write_to_file(&out, "-");
diff_end(&DCfg, 0);
re_free(DCfg.pRe);
}
/**************************************************************************
|
| ︙ | ︙ | |||
3609 3610 3611 3612 3613 3614 3615 | HQuery url; 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(); | | | 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 |
HQuery url;
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 || g.zLogin==0 ){ 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");
|
| ︙ | ︙ |
Changes to src/diff.tcl.
| ︙ | ︙ | |||
88 89 90 91 92 93 94 |
upvar $iivar ii
if {$ii>=$N} {return -1}
set x [lindex $difftxt $ii]
incr ii
return $x
}
| > > > > > > > > > | | > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > > > | > > | > > | | > > > > | > > > > | > | 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 |
upvar $iivar ii
if {$ii>=$N} {return -1}
set x [lindex $difftxt $ii]
incr ii
return $x
}
proc reloadDiff {} {
global fossilcmd difftxt
unset -nocomplain difftxt
set idx [.txtA index @0,0]
readDiffs $fossilcmd 1
update
viewDiff $idx
}
proc readDiffs {fossilcmd redo} {
global difftxt debug
if {![info exists difftxt]} {
if {$debug} {
puts "# [list open $fossilcmd r]"
flush stdout
}
if {[catch {
set in [open $fossilcmd r]
fconfigure $in -encoding utf-8
set difftxt [split [read $in] \n]
close $in
} msg]} {
if {$redo} {
tk_messageBox -type ok -title Error -message "Unable to refresh:\n$msg"
return 0
} else {
puts $msg
exit 1
}
}
}
set N [llength $difftxt]
set ii 0
set nDiffs 0
set n1 0
set n2 0
array set widths {txt 3 ln 3 mkr 1}
if {$redo} {
foreach c [cols] {$c config -state normal}
.lnA delete 1.0 end
.txtA delete 1.0 end
.lnB delete 1.0 end
.txtB delete 1.0 end
.mkr delete 1.0 end
.wfiles.lb delete 0 end
}
set fromIndex [lsearch -glob $fossilcmd *-from]
set toIndex [lsearch -glob $fossilcmd *-to]
set branchIndex [lsearch -glob $fossilcmd *-branch]
set checkinIndex [lsearch -glob $fossilcmd *-checkin]
if {[lsearch -glob $fossilcmd *-label]>=0
|| [lsearch {xdiff fdiff merge-info} [lindex $fossilcmd 2]]>=0
} {
set fA {}
set fB {}
} else {
if {[string match *?--external-baseline* $fossilcmd]} {
set fA {external baseline}
} else {
set fA {base check-in}
}
set fB {current check-out}
if {$fromIndex > -1} {
set fA [lindex $fossilcmd $fromIndex+1]
}
if {$toIndex > -1} {
set fB [lindex $fossilcmd $toIndex+1]
}
if {$branchIndex > -1} {
set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"
}
if {$checkinIndex > -1} {
set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]
}
}
while {[set line [getLine $difftxt $N ii]] != -1} {
switch -- [lindex $line 0] {
FILE {
incr nDiffs
foreach wx [list [string length $n1] [string length $n2]] {
if {$wx>$widths(ln)} {set widths(ln) $wx}
}
.lnA insert end \n fn \n -
if {$fA==""} {
.txtA insert end "[lindex $line 1]\n" fn \n -
} else {
.txtA insert end "[lindex $line 1] ($fA)\n" fn \n -
}
.mkr insert end \n fn \n -
.lnB insert end \n fn \n -
if {$fB==""} {
.txtB insert end "[lindex $line 2]\n" fn \n -
} else {
.txtB insert end "[lindex $line 2] ($fB)\n" fn \n -
}
.wfiles.lb insert end [lindex $line 2]
set n1 0
set n2 0
}
SKIP {
set n [lindex $line 1]
incr n1 $n
|
| ︙ | ︙ | |||
431 432 433 434 435 436 437 |
}
::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
frame .spacer
| | > > | 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 |
}
::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
frame .spacer
if {[readDiffs $fossilcmd 0] == 0} {
tk_messageBox -type ok -title $CFG(TITLE) -message "No changes"
exit
}
update idletasks
proc saveDiff {} {
set fn [tk_getSaveFile]
if {$fn==""} return
set out [open $fn wb]
puts $out "#!/usr/bin/tclsh\n#\n# Run this script using 'tclsh' or 'wish'"
puts $out "# to see the graphical diff.\n#"
puts $out "set fossilcmd {}"
puts $out "set darkmode $::darkmode"
puts $out "set debug $::debug"
puts $out "set prog [list $::prog]"
puts $out "set difftxt \173"
foreach e $::difftxt {puts $out [list $e]}
puts $out "\175"
puts $out "eval \$prog"
close $out
}
|
| ︙ | ︙ | |||
546 547 548 549 550 551 552 553 554 555 |
$w yview -pickplace $idx
$w tag add search search "$idx +$count chars"
$w tag config search -background {#fcc000}
}
set ::search $w
}
::ttk::button .bb.quit -text {Quit} -command exit
::ttk::button .bb.invert -text {Invert} -command invertDiff
::ttk::button .bb.save -text {Save As...} -command saveDiff
::ttk::button .bb.search -text {Search} -command searchOnOff
| > > > > > | | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
$w yview -pickplace $idx
$w tag add search search "$idx +$count chars"
$w tag config search -background {#fcc000}
}
set ::search $w
}
::ttk::button .bb.quit -text {Quit} -command exit
::ttk::button .bb.reload -text {Reload} -command reloadDiff
::ttk::button .bb.invert -text {Invert} -command invertDiff
::ttk::button .bb.save -text {Save As...} -command saveDiff
::ttk::button .bb.search -text {Search} -command searchOnOff
pack .bb.quit -side left
if {$fossilcmd ne ""} {
pack .bb.reload -side left
}
pack .bb.invert -side left
if {$fossilcmd!=""} {pack .bb.save -side left}
pack .bb.files .bb.search -side left
grid rowconfigure . 1 -weight 1
grid columnconfigure . 1 -weight 1
grid columnconfigure . 4 -weight 1
grid .bb -row 0 -columnspan 6
eval grid [cols] -row 1 -sticky nsew
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
125 126 127 128 129 130 131 |
void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){
if( (pCfg->diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT|
DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
fossil_print("Fossil-Diff-From: %s\n",
zFrom[0]=='(' ? zFrom : mprintf("%S %s",
rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
db_text("","SELECT datetime(%f)||' UTC'",
| | | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){
if( (pCfg->diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT|
DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
fossil_print("Fossil-Diff-From: %s\n",
zFrom[0]=='(' ? zFrom : mprintf("%S %s",
rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
db_text("","SELECT datetime(%f)||' UTC'",
symbolic_name_to_mtime(zFrom, 0, 0))));
fossil_print("Fossil-Diff-To: %s\n",
zTo[0]=='(' ? zTo : mprintf("%S %s",
rid_to_uuid(symbolic_name_to_rid(zTo, "ci")),
db_text("","SELECT datetime(%f)||' UTC'",
symbolic_name_to_mtime(zTo, 0, 1))));
fossil_print("%.66c\n", '-');
}
}
/*
** Print the "Index:" message that patches wants to see at the top of a diff.
*/
|
| ︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
DiffConfig *pCfg, /* Diff configuration */
Blob *pOut /* Write to this blob, or stdout of this is NULL */
){
u64 diffFlags = pCfg->diffFlags;
/* Standardize on /dev/null, regardless of platform. */
if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null";
if( pCfg->diffFlags & DIFF_FILE_DELETED ) zRight = "/dev/null";
if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
/* no-op */
}else if( diffFlags & DIFF_DEBUG ){
blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight);
}else if( diffFlags & DIFF_WEBPAGE ){
if( fossil_strcmp(zLeft,zRight)==0 ){
blob_appendf(pOut,"<h1>%h</h1>\n", zLeft);
| > > | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
DiffConfig *pCfg, /* Diff configuration */
Blob *pOut /* Write to this blob, or stdout of this is NULL */
){
u64 diffFlags = pCfg->diffFlags;
/* Standardize on /dev/null, regardless of platform. */
if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null";
if( pCfg->diffFlags & DIFF_FILE_DELETED ) zRight = "/dev/null";
if( pCfg->azLabel[0] ) zLeft = pCfg->azLabel[0];
if( pCfg->azLabel[1] ) zRight = pCfg->azLabel[1];
if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
/* no-op */
}else if( diffFlags & DIFF_DEBUG ){
blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight);
}else if( diffFlags & DIFF_WEBPAGE ){
if( fossil_strcmp(zLeft,zRight)==0 ){
blob_appendf(pOut,"<h1>%h</h1>\n", zLeft);
|
| ︙ | ︙ | |||
563 564 565 566 567 568 569 |
zName2 = NULL_DEVICE;
}else{
blob_read_from_file(&file2, zFile2, ExtFILE);
zName2 = zName;
}
/* Compute and output the differences */
| | > | > | 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 |
zName2 = NULL_DEVICE;
}else{
blob_read_from_file(&file2, zFile2, ExtFILE);
zName2 = zName;
}
/* Compute and output the differences */
if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
if( blob_compare(pFile1, &file2) ){
fossil_print("CHANGED %s\n", zName);
}
}else{
blob_zero(&out);
text_diff(pFile1, &file2, &out, pCfg);
if( blob_size(&out) ){
if( pCfg->diffFlags & DIFF_NUMSTAT ){
if( !(pCfg->diffFlags & DIFF_BRIEF) ){
blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);
}
}else{
diff_print_filenames(zName, zName2, pCfg, pOut);
blob_appendf(pOut, "%s\n", blob_str(&out));
}
}
blob_reset(&out);
}
|
| ︙ | ︙ | |||
669 670 671 672 673 674 675 |
*/
void diff_file_mem(
Blob *pFile1, /* In memory content to compare from */
Blob *pFile2, /* In memory content to compare to */
const char *zName, /* Display name of the file */
DiffConfig *pCfg /* Diff flags */
){
| | > > > | > | 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 |
*/
void diff_file_mem(
Blob *pFile1, /* In memory content to compare from */
Blob *pFile2, /* In memory content to compare to */
const char *zName, /* Display name of the file */
DiffConfig *pCfg /* Diff flags */
){
if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
return;
}
if( pCfg->zDiffCmd==0 ){
Blob out; /* Diff output text */
blob_zero(&out);
text_diff(pFile1, pFile2, &out, pCfg);
if( pCfg->diffFlags & DIFF_NUMSTAT ){
if( !(pCfg->diffFlags & DIFF_BRIEF) ){
fossil_print("%s %s\n", blob_str(&out), zName);
}
}else{
diff_print_filenames(zName, zName, pCfg, 0);
fossil_print("%s\n", blob_str(&out));
}
/* Release memory resources */
blob_reset(&out);
|
| ︙ | ︙ | |||
966 967 968 969 970 971 972 |
if( pFrom ){
zName = pFrom->zName;
}else if( pTo ){
zName = pTo->zName;
}else{
zName = DIFF_NO_NAME;
}
| | > > | 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 |
if( pFrom ){
zName = pFrom->zName;
}else if( pTo ){
zName = pTo->zName;
}else{
zName = DIFF_NO_NAME;
}
if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
return;
}
diff_print_index(zName, pCfg, 0);
if( pFrom ){
rid = uuid_to_rid(pFrom->zUuid, 0);
content_get(rid, &f1);
}else{
blob_zero(&f1);
}
|
| ︙ | ︙ | |||
1054 1055 1056 1057 1058 1059 1060 |
}else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
/* No changes */
(void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
pFromFile = manifest_file_next(pFrom,0);
pToFile = manifest_file_next(pTo,0);
}else{
if( file_dir_match(pFileDir, pToFile->zName) ){
| | | 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 |
}else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
/* No changes */
(void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
pFromFile = manifest_file_next(pFrom,0);
pToFile = manifest_file_next(pTo,0);
}else{
if( file_dir_match(pFileDir, pToFile->zName) ){
if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){
fossil_print("CHANGED %s\n", pFromFile->zName);
}else{
diff_manifest_entry(pFromFile, pToFile, pCfg);
}
}
pFromFile = manifest_file_next(pFrom,0);
pToFile = manifest_file_next(pTo,0);
|
| ︙ | ︙ | |||
1130 1131 1132 1133 1134 1135 1136 |
/*
** Return the name of the external diff command, or return NULL if
** no external diff command is defined.
*/
const char *diff_command_external(int guiDiff){
| | > | | | < > > > > > | < < > > > > > | | > > > | < < | > > > > > > > > | 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 |
/*
** Return the name of the external diff command, or return NULL if
** no external diff command is defined.
*/
const char *diff_command_external(int guiDiff){
const char *zName;
zName = guiDiff ? "gdiff-command" : "diff-command";
return db_get(zName, 0);
}
/*
** Return true if it reasonable to run "diff -tk" for "gdiff".
**
** Details: Return true if all of the following are true:
**
** (1) The isGDiff flags is true
** (2) The "gdiff-command" setting is undefined
** (3) There is a "tclsh" on PATH
** (4) There is a "wish" on PATH
*/
int gdiff_using_tk(int isGdiff){
if( isGdiff
&& db_get("gdiff-command","")[0]==0
&& fossil_app_on_path("tclsh",0)
&& fossil_app_on_path("wish",0)
){
return 1;
}
return 0;
}
/*
** Show diff output in a Tcl/Tk window, in response to the --tk option
** to the diff command.
**
** If fossil has direct access to a Tcl interpreter (either loaded
** dynamically through stubs or linked in statically), we can use it
** directly. Otherwise:
** (1) Write the Tcl/Tk script used for rendering into a temp file.
** (2) Invoke "tclsh" on the temp file using fossil_system().
** (3) Delete the temp file.
*/
void diff_tk(const char *zSubCmd, int firstArg){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
int bDebug = find_option("tkdebug",0,0)!=0;
int bDarkMode = find_option("dark",0,0)!=0;
(void)find_option("debug",0,0);
blob_zero(&script);
/* Caution: When this routine is called from the merge-info command,
** the --tcl argument requires an argument. But merge-info does not
** use -i, so we can take -i as that argument. This routine needs to
** always have -i after --tcl.
** CAUTION!
** vvvvvvv */
blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
g.nameOfExe, zSubCmd);
find_option("tcl",0,0);
find_option("html",0,0);
find_option("side-by-side","y",0);
find_option("internal","i",0);
find_option("verbose","v",0);
|
| ︙ | ︙ | |||
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 |
}else{
int j;
blob_append(&script, " ", 1);
for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
}
}
blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
blob_appendf(&script, "%s", builtin_file("diff.tcl", 0));
if( zTempFile ){
blob_write_to_file(&script, zTempFile);
fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile);
}else{
#if defined(FOSSIL_ENABLE_TCL)
Th_FossilInit(TH_INIT_DEFAULT);
| > | 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 |
}else{
int j;
blob_append(&script, " ", 1);
for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
}
}
blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
blob_appendf(&script, "set debug %d\n", bDebug);
blob_appendf(&script, "%s", builtin_file("diff.tcl", 0));
if( zTempFile ){
blob_write_to_file(&script, zTempFile);
fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile);
}else{
#if defined(FOSSIL_ENABLE_TCL)
Th_FossilInit(TH_INIT_DEFAULT);
|
| ︙ | ︙ | |||
1239 1240 1241 1242 1243 1244 1245 | ** COMMAND: gdiff ** ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? ** ** Show the difference between the current version of each of the FILEs ** specified (as they exist on disk) and that same file as it was checked- ** out. Or if the FILE arguments are omitted, show all unsaved changes | | > | 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 | ** COMMAND: gdiff ** ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? ** ** Show the difference between the current version of each of the FILEs ** specified (as they exist on disk) and that same file as it was checked- ** out. Or if the FILE arguments are omitted, show all unsaved changes ** currently in the working check-out. The "gdiff" variant means to ** use a GUI diff. ** ** The default output format is a "unified patch" (the same as the ** output of "diff -u" on most unix systems). Many alternative formats ** are available. A few of the more useful alternatives: ** ** --tk Pop up a Tcl/Tk-based GUI to show the diff ** --by Show a side-by-side diff in the default web browser |
| ︙ | ︙ | |||
1305 1306 1307 1308 1309 1310 1311 | ** that directory as the baseline. ** -w|--ignore-all-space Ignore white space when comparing lines ** -i|--internal Use internal diff logic ** --invert Invert the diff ** --json Output formatted as JSON ** -n|--linenum Show line numbers ** -N|--new-file Alias for --verbose | | > > | 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 | ** that directory as the baseline. ** -w|--ignore-all-space Ignore white space when comparing lines ** -i|--internal Use internal diff logic ** --invert Invert the diff ** --json Output formatted as JSON ** -n|--linenum Show line numbers ** -N|--new-file Alias for --verbose ** --numstat Show the number of added and deleted lines per ** file, omitting the diff. When combined with ** --brief, show only the total row. ** -y|--side-by-side Side-by-side diff ** --strip-trailing-cr Strip trailing CR ** --tcl Tcl-formatted output used internally by --tk ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") ** --tk Launch a Tcl/Tk GUI for display ** --to VERSION Select VERSION as target for the diff ** --undo Use the undo buffer as the baseline |
| ︙ | ︙ | |||
1331 1332 1333 1334 1335 1336 1337 | const char *zCheckin; /* Check-in version number */ const char *zBranch; /* Branch to diff */ int againstUndo = 0; /* Diff against files in the undo buffer */ FileDirList *pFileDir = 0; /* Restrict the diff to these files */ DiffConfig DCfg; /* Diff configuration object */ int bFromIsDir = 0; /* True if zFrom is a directory name */ | > | < > > > > > > > > > > | 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 |
const char *zCheckin; /* Check-in version number */
const char *zBranch; /* Branch to diff */
int againstUndo = 0; /* Diff against files in the undo buffer */
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
DiffConfig DCfg; /* Diff configuration object */
int bFromIsDir = 0; /* True if zFrom is a directory name */
isGDiff = g.argv[1][0]=='g';
if( find_option("tk",0,0)!=0|| has_option("tclsh") ){
diff_tk("diff", 2);
return;
}
zFrom = find_option("from", "r", 1);
zTo = find_option("to", 0, 1);
zCheckin = find_option("checkin", "ci", 1);
zBranch = find_option("branch", 0, 1);
againstUndo = find_option("undo",0,0)!=0;
if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
" or --branch");
}
if( zBranch ){
if( zTo || zFrom || zCheckin ){
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
}
zTo = zBranch;
zFrom = mprintf("root:%s", zBranch);
zBranch = 0;
}
if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
fossil_fatal("cannot use --checkin together with --from or --to");
}
if( 0==zCheckin ){
if( zTo==0 || againstUndo ){
db_must_be_within_tree();
}else if( zFrom==0 ){
fossil_fatal("must use --from if --to is present");
}else{
db_find_and_open_repository(0, 0);
}
}else{
db_find_and_open_repository(0, 0);
}
if( gdiff_using_tk(isGDiff) ){
restore_option("--from", zFrom, 1);
restore_option("--to", zTo, 1);
restore_option("--checkin", zCheckin, 1);
restore_option("--branch", zBranch, 1);
if( againstUndo ) restore_option("--undo", 0, 0);
diff_tk("diff", 2);
return;
}
determine_exec_relative_option(1);
if( zFrom!=file_tail(zFrom)
&& file_isdir(zFrom, ExtFILE)==1
&& !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
){
bFromIsDir = 1;
|
| ︙ | ︙ | |||
1397 1398 1399 1400 1401 1402 1403 |
break;
}
pFileDir[i-2].nName = blob_size(&fname);
pFileDir[i-2].nUsed = 0;
blob_reset(&fname);
}
}
| > > > | | 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 |
break;
}
pFileDir[i-2].nName = blob_size(&fname);
pFileDir[i-2].nUsed = 0;
blob_reset(&fname);
}
}
if( DCfg.diffFlags & DIFF_NUMSTAT ){
fossil_print("%10s %10s\n", "INSERTED", "DELETED");
}
if( zCheckin!=0 ){
int ridTo = name_to_typed_rid(zCheckin, "ci");
zTo = zCheckin;
zFrom = db_text(0,
"SELECT uuid FROM blob, plink"
" WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
ridTo);
if( zFrom==0 ){
|
| ︙ | ︙ | |||
1437 1438 1439 1440 1441 1442 1443 |
}
fossil_free(pFileDir[i].zName);
}
fossil_free(pFileDir);
}
diff_end(&DCfg, 0);
if ( DCfg.diffFlags & DIFF_NUMSTAT ){
| | | | 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 |
}
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 file%s\n",
g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": "");
}
}
/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO
**
|
| ︙ | ︙ |
Changes to src/dispatch.c.
| ︙ | ︙ | |||
52 53 54 55 56 57 58 59 60 61 62 63 64 65 | #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret POST content */ /* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */ #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ /**************************************************************************/ /* Values for the 2nd parameter to dispatch_name_search() */ #define CMDFLAG_ANY 0x0038 /* Match anything */ #define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */ #endif /* INTERFACE */ | > | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret POST content */ /* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */ #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ #define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */ /**************************************************************************/ /* Values for the 2nd parameter to dispatch_name_search() */ #define CMDFLAG_ANY 0x0038 /* Match anything */ #define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */ #endif /* INTERFACE */ |
| ︙ | ︙ | |||
520 521 522 523 524 525 526 |
blob_appendf(pHtml, "%s\n", azEnd[iLevel--]);
}
}
/*
** Format help text for TTY display.
*/
| | > > > > > > > > | 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 |
blob_appendf(pHtml, "%s\n", azEnd[iLevel--]);
}
}
/*
** Format help text for TTY display.
*/
static void help_to_text(const char *zHelp, Blob *pText, int bUsage){
int i, x;
char c;
if( zHelp[0]=='>' ){
if( !bUsage ){
blob_appendf(pText, "Usage:");
}else{
blob_append_char(pText, ' ');
}
zHelp++;
}
for(i=0; (c = zHelp[i])!=0; i++){
if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){
if( i>0 ) blob_append(pText, zHelp, i);
blob_append(pText, "fossil", 6);
zHelp += i+7;
i = -1;
continue;
|
| ︙ | ︙ | |||
601 602 603 604 605 606 607 |
}else if( rawOut ){
for(j=0; j<occHelp[aCommand[i].iHelp]; j++)
fossil_print("# %s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName);
fossil_print("%s\n\n", aCommand[i].zHelp);
}else{
Blob txt;
blob_init(&txt, 0, 0);
| | | 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
}else if( rawOut ){
for(j=0; j<occHelp[aCommand[i].iHelp]; j++)
fossil_print("# %s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName);
fossil_print("%s\n\n", aCommand[i].zHelp);
}else{
Blob txt;
blob_init(&txt, 0, 0);
help_to_text(aCommand[i].zHelp, &txt, 0);
for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
fossil_print("# %s%s\n",
aCommand[bktHelp[aCommand[i].iHelp][j]].zName,
(aCommand[i].eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ?
" (versionable)" : "");
}
fossil_print("%s\n\n", blob_str(&txt));
|
| ︙ | ︙ | |||
800 801 802 803 804 805 806 807 808 809 810 811 812 813 |
n = dispatch_approx_match(g.argv[i], 20, az);
for(j=0; j<n; j++){
fossil_print(" %s\n", az[j]);
}
}
}
/*
** WEBPAGE: help
** URL: /help?name=CMD
**
** Show the built-in help text for CMD. CMD can be a command-line interface
** command or a page name from the web interface or a setting.
** Query parameters:
| > > > > > > > > > > > > > > > > > > > > > > | 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 |
n = dispatch_approx_match(g.argv[i], 20, az);
for(j=0; j<n; j++){
fossil_print(" %s\n", az[j]);
}
}
}
/*
** Returns 1 if the command or page name zName is known to be a
** command/page which is only available in certain builds/platforms,
** else returns 0.
*/
static int help_is_platform_command(const char *zName){
const char *aList[] = {
/* List of commands/pages which are known to only be available in
** certain builds/platforms. */
"winsrv",
"json", "/json",
NULL /* end-of-list sentinel */
};
int i = 0;
const char *z;
for( z = aList[0]; z ; z = aList[++i] ){
if( 0==fossil_strcmp(zName, z) ) return 1;
}
return 0;
}
/*
** WEBPAGE: help
** URL: /help?name=CMD
**
** Show the built-in help text for CMD. CMD can be a command-line interface
** command or a page name from the web interface or a setting.
** Query parameters:
|
| ︙ | ︙ | |||
827 828 829 830 831 832 833 |
if( zCmd==0 ) zCmd = P("name");
cgi_check_for_malice();
if( zCmd && *zCmd ){
int rc;
const CmdOrPage *pCmd = 0;
style_set_current_feature("tkt");
| | > > | < > > > > > > > | | | | > > > | > | | > | 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 |
if( zCmd==0 ) zCmd = P("name");
cgi_check_for_malice();
if( zCmd && *zCmd ){
int rc;
const CmdOrPage *pCmd = 0;
style_set_current_feature("tkt");
style_submenu_element("Topic-List", "%R/help");
if( search_restrict(SRCH_HELP)!=0 ){
style_submenu_element("Search","%R/search?y=h");
}
rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
if( pCmd ){
style_header("Help: %s", pCmd->zName);
}else{
style_header("Help");
}
if( pCmd==0 ){
/* No <h1> line in this case */
}else if( *zCmd=='/' ){
/* Some of the webpages require query parameters in order to work.
** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */
@ <h1>The "%h(pCmd->zName)" page:</h1>
}else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){
@ <h1>The "%h(pCmd->zName)" setting:</h1>
}else{
@ <h1>The "%h(pCmd->zName)" command:</h1>
}
if( rc==1 || (rc==2 && zCmd[0]=='/') ){
if( zCmd && help_is_platform_command(zCmd) ){
@ Not available in this build: "%h(zCmd)"
}else{
@ Unknown topic: "%h(zCmd)"
}
}else if( rc==2 ){
@ Ambiguous prefix: "%h(zCmd)"
}else{
if( pCmd->zHelp[0]==0 ){
@ No help available for "%h(pCmd->zName)"
}else if( P("plaintext") ){
Blob txt;
blob_init(&txt, 0, 0);
help_to_text(pCmd->zHelp, &txt, 0);
@ <pre class="helpPage">
@ %h(blob_str(&txt))
@ </pre>
blob_reset(&txt);
}else if( P("raw") ){
@ <pre class="helpPage">
@ %h(pCmd->zHelp)
@ </pre>
}else{
@ <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");
search_screen(SRCH_HELP, 0x02);
@ <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++){
|
| ︙ | ︙ | |||
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 |
occHelp[aCommand[i].iHelp] = 0;
}
}
@ </dl>
blob_reset(&buf);
style_finish_page();
}
static void multi_column_list(const char **azWord, int nWord){
int i, j, len;
int mxLen = 0;
int nCol;
int nRow;
for(i=0; i<nWord; i++){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
occHelp[aCommand[i].iHelp] = 0;
}
}
@ </dl>
blob_reset(&buf);
style_finish_page();
}
/*
** Analyze p and return one of three values:
**
** 0 p is the continuation of a prior subcommand.
**
** 1 p is text past the end of a prior subcommand.
**
** 2 p is the start of a new subcommand.
*/
static int is_subcommand(Blob *p, int bAbbrevSubcmd){
int i, sz;
const unsigned char *z = (const unsigned char*)blob_buffer(p);
sz = blob_size(p);
if( sz>6 ) sz = 6;
for(i=0; i<sz && fossil_isspace(z[i]); i++){}
if( i>=sz ) return 0;
if( bAbbrevSubcmd==0 ){
if( i>1 ) return 0;
return z[0]=='>' ? 2 : 1;
}else{
return (i==3 && fossil_isalpha(z[3])) ? 2 : 1;
}
}
/*
** Input z[] is help text for zTopic. If zTopic has sub-command zSub,
** then cut out all portions of the original help text that do not
** directly pertain to zSub and write the zSub-relevant parts into
** pOut.
**
** Return the number of lines of z[] written into pOut. A return of
** zero means no simplification occurred.
*/
static int simplify_to_subtopic(
const char *z, /* Full original help text */
Blob *pOut, /* Write simplified help text here */
const char *zTopic, /* TOPIC */
const char *zSubtopic, /* SUBTOPIC */
int bAbbrevSubcmd /* True if z[] contains abbreviated subcommands */
){
Blob in, line; //, subsection;
int n = 0;
char *zQTop = re_quote(zTopic);
char *zQSub = re_quote(zSubtopic);
char *zPattern;
ReCompiled *pRe = 0;
if( bAbbrevSubcmd ){
zPattern = mprintf(" ([a-z]+ ?\\| ?)*%s\\b", zQSub);
}else{
zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
}
fossil_free(zQTop);
fossil_free(zQSub);
re_compile(&pRe, zPattern, 0);
fossil_free(zPattern);
blob_init(&in, z, -1);
while( blob_line(&in, &line) ){
if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){
int atStart = 1;
blob_appendb(pOut, &line);
n++;
while( blob_line(&in, &line) ){
if( re_match(pRe,(unsigned char*)blob_buffer(&line),blob_size(&line)) ){
blob_appendb(pOut, &line);
n++;
atStart = 1;
}else{
int x = is_subcommand(&line,bAbbrevSubcmd);
if( x==2 ){
if( atStart ){
blob_appendb(pOut, &line);
n++;
}else{
break;
}
}else if( x==1 ){
break;
}else{
blob_appendb(pOut, &line);
n++;
atStart = 0;
}
}
}
}
}
blob_reset(&line);
re_free(pRe);
if( n ){
blob_trim(pOut);
blob_reset(&in);
}
return n;
}
/*
** Input p is a "Usage:" line or a subcommand line. Simplify this line
** for the --usage option and write it into pOut.
*/
static void simplify_usage_line(
Blob *p,
Blob *pOut,
int bAbbrevSubcmd,
const char *zCmd
){
const char *z = blob_buffer(p);
int sz = blob_size(p);
int i = 0;
if( sz>6 && z[0]=='U' ){
for(i=1; i<sz && !fossil_isspace(z[i]); i++){}
}else if( sz>0 && z[0]=='>' ){
i = 1;
}else if( sz>4 && bAbbrevSubcmd
&& memcmp(z," ",3)==0 && !fossil_isspace(z[3]) ){
int j;
for(j=3; j<sz-1 && (z[j]!=' ' || z[j+1]!=' '); j++){}
blob_appendf(pOut, "fossil %s %.*s\n", zCmd, j-3, &z[3]);
return;
}else{
while( i<sz && fossil_isspace(z[i]) ) i++;
if( i+2<sz && (z[i]=='o' || z[i]=='O') && z[i+1]=='r' ){
while( i<sz && !fossil_isspace(z[i]) ) i++;
}
}
while( i<sz && fossil_isspace(z[i]) ) i++;
blob_append(pOut, &z[i], sz-i);
}
/*
** Input z[] is help text for a command zTopic. Write into pOut all lines of
** z[] that show the command-line syntax for that command. Lines written
** to pOut are lines that begin with out of:
**
** Usage:
** or:
** > fossil TOPIC
**
** Return the number of lines written into pOut.
*/
static int simplify_to_usage(
const char *z, /* Full original help text */
Blob *pOut, /* Write simplified help text here */
const char *zTopic, /* The command for which z[] is full help text */
int bAbbrevSubcmd /* z[] uses abbreviated subcommands */
){
ReCompiled *pRe = 0;
Blob in, line;
int n = 0;
if( bAbbrevSubcmd ){
re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
}else{
re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
}
blob_init(&in, z, -1);
while( blob_line(&in, &line) ){
if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){
simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic);
n++;
}
}
re_free(pRe);
if( n ) blob_trim(pOut);
return n;
}
/*
** Input z[] is help text. Write into pOut all lines of z[] that show
** command-line options. Return the number of lines written.
*/
static int simplify_to_options(
const char *z, /* Full original help text */
Blob *pOut, /* Write simplified help text here */
int bAbbrevSubcmd, /* z[] uses abbreviated subcommands */
const char *zCmd /* Name of the command that z[] describes */
){
ReCompiled *pRe = 0;
Blob txt, line, subsection;
int n = 0;
int bSubsectionSeen = 0;
blob_init(&txt, z, -1);
blob_init(&subsection, 0, 0);
re_compile(&pRe, "^ +-.* ", 0);
while( blob_line(&txt, &line) ){
int len = blob_size(&line);
unsigned char *zLine = (unsigned char *)blob_buffer(&line);
if( re_match(pRe, zLine, len) ){
if( blob_size(&subsection) ){
simplify_usage_line(&subsection, pOut, bAbbrevSubcmd, zCmd);
blob_reset(&subsection);
}
blob_appendb(pOut, &line);
}else if( len>7 && !fossil_isspace(zLine[0]) && bSubsectionSeen
&& sqlite3_strlike("%options:%",blob_str(&line),0)==0 ){
subsection = line;
}else if( !bAbbrevSubcmd && len>9
&& (memcmp(zLine,"> fossil ",9)==0
|| memcmp(zLine,"> fossil",9)==0) ){
subsection = line;
bSubsectionSeen = 1;
}else if( bAbbrevSubcmd && len>5 && memcmp(zLine," ",3)==0
&& fossil_isalpha(zLine[3]) ){
subsection = line;
bSubsectionSeen = 1;
}else if( len>1 && !fossil_isspace(zLine[0]) && bSubsectionSeen ){
blob_reset(&subsection);
}
}
re_free(pRe);
blob_trim(pOut);
blob_reset(&subsection);
return n;
}
static void multi_column_list(const char **azWord, int nWord){
int i, j, len;
int mxLen = 0;
int nCol;
int nRow;
for(i=0; i<nWord; i++){
|
| ︙ | ︙ | |||
1123 1124 1125 1126 1127 1128 1129 | 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 | < < | | > > > > > > < < < < > | > > > > | | | | | | | | > > > | > > | > > > | | < < | | 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 |
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
@ --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
@ --sqltrace Trace all SQL commands
@ --sshtrace Trace SSH activity
@ --ssl-identity NAME Set the SSL identity to NAME
@ --systemtrace Trace calls to system()
@ -U|--user USER Make the default user be USER
@ --utc Display times using UTC
@ --vfs NAME Cause SQLite to use the NAME VFS
;
/*
** COMMAND: help
**
** Usage: %fossil help [OPTIONS] [TOPIC] [SUBCOMMAND]
**
** Display information on how to use TOPIC, which may be a command, webpage, or
** setting. Webpage names begin with "/". If TOPIC is omitted, a list of
** topics is returned. If there is an extra argument after TOPIC, it is
** the name of a subcommand, in which case only the help text for that one
** subcommand is shown.
**
** The following options can be used when TOPIC is omitted:
**
** -a|--all List both common and auxiliary commands
** -e|--everything List all help on all topics
** -f|--full List full set of commands (including auxiliary
** and unsupported "test" commands), options,
** settings, and web pages
** -o|--options List command-line options common to all commands
** -s|--setting List setting names
** -t|--test List unsupported "test" commands
** -v|--verbose List both names and help text
** -x|--aux List only auxiliary commands
** -w|--www List all web pages
**
** These options can be used when TOPIC is present:
**
** -c|--commands Restrict TOPIC search to commands
** -h|--html Format output as HTML rather than plain text
** -o|--options Show command-line options for TOPIC
** --raw Output raw, unformatted help text
** -u|--usage Show a succinct usage summary, not full help text
**
** See also: [[usage]], [[options]], [[search]] with the -h option
*/
void help_cmd(void){
int rc;
int mask = CMDFLAG_ANY; /* Mask of help topic types */
int isPage = 0; /* True if TOPIC is a page */
int verboseFlag = 0; /* -v option */
int commandsFlag = 0; /* -c option */
const char *z; /* Original, untranslated help text */
const char *zCmdOrPage; /* "command" or "page" or "setting" */
const CmdOrPage *pCmd = 0; /* ptr to aCommand[] entry for TOPIC */
int useHtml = 0; /* -h option */
int bUsage; /* --usage */
int bRaw; /* --raw option */
int bOptions; /* --options */
const char *zTopic; /* TOPIC argument */
const char *zSubtopic = 0; /* SUBTOPIC argument */
Blob subtext1, subtext2, s3; /* Subsets of z[] containing subtopic/usage */
Blob txt; /* Text after rendering */
int bAbbrevSubcmd = 0; /* Help text uses abbreviated subcommands */
verboseFlag = find_option("verbose","v",0)!=0;
commandsFlag = find_option("commands","c",0)!=0;
useHtml = find_option("html","h",0)!=0;
bRaw = find_option("raw",0,0)!=0;
bOptions = find_option("options","o",0)!=0;
bUsage = find_option("usage","u",0)!=0;
if( find_option("all","a",0) ){
command_list(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER, verboseFlag, useHtml);
return;
}
else if( find_option("www","w",0) ){
command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
return;
}
|
| ︙ | ︙ | |||
1221 1222 1223 1224 1225 1226 1227 |
fossil_print("\n%s", zOptions);
fossil_print("\nfossil settings:\n\n");
command_list(CMDFLAG_SETTING, verboseFlag, useHtml);
fossil_print("\nfossil web pages:\n\n");
command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
fossil_print("\nfossil test commands (unsupported):\n\n");
command_list(CMDFLAG_TEST, verboseFlag, useHtml);
| > | | > > > > > > | > > > | | > | > > > > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > | > > | | | > > | 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 |
fossil_print("\n%s", zOptions);
fossil_print("\nfossil settings:\n\n");
command_list(CMDFLAG_SETTING, verboseFlag, useHtml);
fossil_print("\nfossil web pages:\n\n");
command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
fossil_print("\nfossil test commands (unsupported):\n\n");
command_list(CMDFLAG_TEST, verboseFlag, useHtml);
if ( !verboseFlag ) {
fossil_print("\n");
version_cmd();
}
return;
}
else if( find_option("everything","e",0) ){
display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE |
CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0);
return;
}
verify_all_options();
if( g.argc<3 ){
if( bOptions ){
fossil_print("%s", zOptions);
return;
}
z = g.argv[0];
fossil_print(
"Usage: %s help TOPIC\n"
"Things to try:\n\n"
" %s help help\n"
" %s help -o\n"
" %s help -a\n"
" %s search -h TOPIC\n\n"
"Other common values for TOPIC:\n\n",
z, z, z, z, z);
command_list(CMDFLAG_1ST_TIER,verboseFlag,useHtml);
if( !verboseFlag ) version_cmd();
return;
}
zTopic = g.argv[2];
zSubtopic = g.argc>=4 ? g.argv[3] : 0;
isPage = ('/' == zTopic[0]) ? 1 : 0;
if( isPage ){
zCmdOrPage = "page";
}else if( commandsFlag ){
mask = CMDFLAG_COMMAND;
zCmdOrPage = "command";
}else{
zCmdOrPage = "command or setting";
}
rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd);
if( rc ){
int i, n;
const char *az[5];
if( rc==1 ){
if( help_is_platform_command(g.argv[2]) ){
fossil_print("Not available in this build: %s\n", g.argv[2]);
return;
}
fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
}else{
fossil_print("ambiguous %s prefix: %s\n",
zCmdOrPage, g.argv[2]);
}
fossil_print("Did you mean one of these TOPICs:\n");
n = dispatch_approx_match(g.argv[2], 5, az);
for(i=0; i<n; i++){
fossil_print(" * %s\n", az[i]);
}
fossil_print("Other commands to try:\n");
fossil_print(" fossil search -h PATTERN ;# search all help text\n");
fossil_print(" fossil help -a ;# show all commands\n");
fossil_print(" fossil help -w ;# show all web-pages\n");
fossil_print(" fossil help -s ;# show all settings\n");
fossil_print(" fossil help -o ;# show global options\n");
return;
}
bAbbrevSubcmd = (pCmd->eCmdFlags & CMDFLAG_ABBREVSUBCMD)!=0;
z = pCmd->zHelp;
if( z==0 ){
fossil_fatal("no help available for the %s %s",
pCmd->zName, zCmdOrPage);
}
blob_init(&subtext1, 0, 0);
blob_init(&subtext2, 0, 0);
blob_init(&s3, 0, 0);
if( zSubtopic!=0 ){
if( simplify_to_subtopic(z, &subtext1, zTopic, zSubtopic, bAbbrevSubcmd) ){
z = blob_str(&subtext1);
}else{
fossil_print("No subtopic \"%s\" for \"%s\".\n", zSubtopic, zTopic);
bUsage = 1;
zSubtopic = 0;
}
}
if( bUsage ){
if( simplify_to_usage(z, &subtext2, zTopic, bAbbrevSubcmd) ){
z = blob_str(&subtext2);
}else{
bUsage = 0;
}
}
if( bOptions ){
simplify_to_options(z, &s3, bAbbrevSubcmd, zTopic);
z = blob_str(&s3);
}
if( pCmd && pCmd->eCmdFlags & CMDFLAG_SETTING ){
const Setting *pSetting = db_find_setting(pCmd->zName, 0);
char *zDflt = 0;
if( pSetting!=0 && pSetting->def!=0 && *pSetting->def!=0 ){
zDflt = mprintf(" (default: %s)", pSetting->def);
}
fossil_print("Setting: \"%s\"%s%s\n\n",
pCmd->zName, zDflt!=0 ? zDflt : "",
(pCmd->eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? " (versionable)" : ""
);
fossil_free(zDflt);
}
blob_init(&txt, 0, 0);
if( bRaw ){
blob_append(&txt, z, -1);
}else if( useHtml ){
help_to_html(z, &txt);
}else{
help_to_text(z, &txt, bUsage || zSubtopic!=0);
}
if( blob_strlen(&txt)>0 ) fossil_print("%s\n", blob_str(&txt));
blob_reset(&txt);
blob_reset(&subtext1);
blob_reset(&subtext2);
}
/*
** Return a pointer to the setting information array.
**
** This routine provides access to the aSetting[] array which is created
** by the mkindex utility program and included with <page_index.h>.
|
| ︙ | ︙ | |||
1475 1476 1477 1478 1479 1480 1481 |
break;
case 3: /* helptext */
sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC);
break;
case 4: { /* formatted */
Blob txt;
blob_init(&txt, 0, 0);
| | | 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 |
break;
case 3: /* helptext */
sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC);
break;
case 4: { /* formatted */
Blob txt;
blob_init(&txt, 0, 0);
help_to_text(pPage->zHelp, &txt, 0);
sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free);
break;
}
case 5: { /* formatted */
Blob txt;
blob_init(&txt, 0, 0);
help_to_html(pPage->zHelp, &txt);
|
| ︙ | ︙ |
Changes to src/doc.c.
| ︙ | ︙ | |||
637 638 639 640 641 642 643 |
if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){
/* The text argument to data-title="" will have had any characters that
** are special to HTML encoded. We need to decode these before turning
** the text into a title, as the title text will be reencoded later */
char *zTitle = mprintf("%.*s", nValue, zValue);
int i;
for(i=0; fossil_isspace(zTitle[i]); i++){}
| | | 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 |
if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){
/* The text argument to data-title="" will have had any characters that
** are special to HTML encoded. We need to decode these before turning
** the text into a title, as the title text will be reencoded later */
char *zTitle = mprintf("%.*s", nValue, zValue);
int i;
for(i=0; fossil_isspace(zTitle[i]); i++){}
html_to_plaintext(zTitle+i, pTitle, 0);
fossil_free(zTitle);
seenTitle = 1;
if( seenClass ) return 1;
}
}
return seenClass;
}
|
| ︙ | ︙ | |||
786 787 788 789 790 791 792 |
const char *zDefaultTitle, /* Default title */
const char *zFilename /* Name of the file being rendered */
){
Blob title;
int isPopup = P("popup")!=0;
blob_init(&title,0,0);
if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
| | > > > | 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 |
const char *zDefaultTitle, /* Default title */
const char *zFilename /* Name of the file being rendered */
){
Blob title;
int isPopup = P("popup")!=0;
blob_init(&title,0,0);
if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
Blob tail = BLOB_INITIALIZER;
style_adunit_config(ADUNIT_RIGHT_OK);
if( wiki_find_title(pBody, &title, &tail) ){
if( !isPopup ) style_header("%s", blob_str(&title));
wiki_convert(&tail, 0, WIKI_BUTTONS);
}else{
if( !isPopup ) style_header("%s", zDefaultTitle);
wiki_convert(pBody, 0, WIKI_BUTTONS);
}
if( !isPopup ){
document_emit_js();
style_finish_page();
}
blob_reset(&tail);
}else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
Blob tail = BLOB_INITIALIZER;
markdown_to_html(pBody, &title, &tail);
if( !isPopup ){
if( blob_size(&title)>0 ){
markdown_dehtmlize_blob(&title);
style_header("%s", blob_str(&title));
}else{
style_header("%s", zDefaultTitle);
}
}
convert_href_and_output(&tail);
if( !isPopup ){
document_emit_js();
style_finish_page();
}
blob_reset(&tail);
}else if( fossil_strcmp(zMime, "text/plain")==0 ){
style_header("%s", zDefaultTitle);
@ <blockquote><pre>
@ %h(blob_str(pBody))
@ </pre></blockquote>
document_emit_js();
style_finish_page();
|
| ︙ | ︙ | |||
947 948 949 950 951 952 953 954 955 956 957 958 959 960 |
#endif
};
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_set_current_feature("doc");
blob_init(&title, 0, 0);
zDfltTitle = isUV ? "" : "Documentation";
db_begin_transaction();
while( rid==0 && (++nMiss)<=count(azSuffix) ){
zName = P("name");
if( isUV ){
if( zName==0 ) zName = "index.wiki";
i = 0;
| > | 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 |
#endif
};
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_set_current_feature("doc");
blob_init(&title, 0, 0);
blob_init(&filebody, 0, 0);
zDfltTitle = isUV ? "" : "Documentation";
db_begin_transaction();
while( rid==0 && (++nMiss)<=count(azSuffix) ){
zName = P("name");
if( isUV ){
if( zName==0 ) zName = "index.wiki";
i = 0;
|
| ︙ | ︙ | |||
1046 1047 1048 1049 1050 1051 1052 |
/* The file is now contained in the filebody blob. Deliver the
** file to the user
*/
zMime = nMiss==0 ? P("mimetype") : 0;
if( zMime==0 ){
zMime = mimetype_from_name(zName);
}
| | > > > > | 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 |
/* The file is now contained in the filebody blob. Deliver the
** file to the user
*/
zMime = nMiss==0 ? P("mimetype") : 0;
if( zMime==0 ){
zMime = mimetype_from_name(zName);
}
Th_StoreUnsafe("doc_name", zName);
if( vid ){
Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
" FROM blob WHERE rid=%d", vid));
Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
" WHERE objid=%d AND type='ci'", vid));
}
cgi_check_for_malice();
document_render(&filebody, zMime, zDfltTitle, zName);
if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found");
db_end_transaction(0);
blob_reset(&title);
blob_reset(&filebody);
return;
/* Jump here when unable to locate the document */
doc_not_found:
db_end_transaction(0);
if( isUV && P("name")==0 ){
uvlist_page();
return;
}
cgi_set_status(404, "Not Found");
style_header("Not Found");
@ <p>Document %h(zOrigName) not found
if( fossil_strcmp(zCheckin,"ckout")!=0 ){
@ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
}
style_finish_page();
blob_reset(&title);
blob_reset(&filebody);
return;
}
/*
** The default logo.
*/
static const unsigned char aLogo[] = {
|
| ︙ | ︙ |
Changes to src/encode.c.
| ︙ | ︙ | |||
140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
j = i+1;
break;
}
}
if( j<i ) blob_append(p, zIn+j, i-j);
}
/*
** Encode a string for HTTP. This means converting lots of
** characters into the "%HH" where H is a hex digit. It also
** means converting spaces to "+".
**
** This is the opposite of DeHttpizeString below.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
j = i+1;
break;
}
}
if( j<i ) blob_append(p, zIn+j, i-j);
}
/*
** Make the given string safe for HTML by converting syntax characters
** into alternatives that do not have the special syntactic meaning.
**
** < --> U+227a
** > --> U+227b
** & --> +
** " --> U+201d
** ' --> U+2019
**
** Return a pointer to a new string obtained from fossil_malloc().
*/
char *html_lookalike(const char *z, int n){
unsigned char c;
int i = 0;
int count = 0;
unsigned char *zOut;
const unsigned char *zIn = (const unsigned char*)z;
if( n<0 ) n = strlen(z);
while( i<n ){
switch( zIn[i] ){
case '<': count += 3; break;
case '>': count += 3; break;
case '"': count += 3; break;
case '\'': count += 3; break;
case 0: n = i; break;
}
i++;
}
i = 0;
zOut = fossil_malloc( count+n+1 );
if( count==0 ){
memcpy(zOut, zIn, n);
zOut[n] = 0;
return (char*)zOut;
}
while( n-->0 ){
c = *(zIn++);
switch( c ){
case '<':
zOut[i++] = 0xe2;
zOut[i++] = 0x89;
zOut[i++] = 0xba;
break;
case '>':
zOut[i++] = 0xe2;
zOut[i++] = 0x89;
zOut[i++] = 0xbb;
break;
case '&':
zOut[i++] = '+';
break;
case '"':
zOut[i++] = 0xe2;
zOut[i++] = 0x80;
zOut[i++] = 0x9d;
break;
case '\'':
zOut[i++] = 0xe2;
zOut[i++] = 0x80;
zOut[i++] = 0x99;
break;
default:
zOut[i++] = c;
break;
}
}
zOut[i] = 0;
return (char*)zOut;
}
/*
** Encode a string for HTTP. This means converting lots of
** characters into the "%HH" where H is a hex digit. It also
** means converting spaces to "+".
**
** This is the opposite of DeHttpizeString below.
|
| ︙ | ︙ | |||
242 243 244 245 246 247 248 | *zOut = 0; return zRet; } /* ** Convert a single HEX digit to an integer */ | | | 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
*zOut = 0;
return zRet;
}
/*
** Convert a single HEX digit to an integer
*/
int fossil_hexvalue(int c){
if( c>='a' && c<='f' ){
c += 10 - 'a';
}else if( c>='A' && c<='F' ){
c += 10 - 'A';
}else if( c>='0' && c<='9' ){
c -= '0';
}else{
|
| ︙ | ︙ | |||
270 271 272 273 274 275 276 |
if( !z ) return 0;
i = j = 0;
while( z[i] ){
switch( z[i] ){
case '%':
if( z[i+1] && z[i+2] ){
| | | | 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
if( !z ) return 0;
i = j = 0;
while( z[i] ){
switch( z[i] ){
case '%':
if( z[i+1] && z[i+2] ){
z[j] = fossil_hexvalue(z[i+1]) << 4;
z[j] |= fossil_hexvalue(z[i+2]);
i += 2;
}
break;
case '+':
z[j] = ' ';
break;
default:
|
| ︙ | ︙ | |||
727 728 729 730 731 732 733 734 735 736 737 738 739 740 |
*/
void canonical16(char *z, int n){
while( *z && n-- ){
*z = zEncode[zDecode[(*z)&0x7f]&0x1f];
z++;
}
}
/*
** Decode a string encoded using "quoted-printable".
**
** (1) "=" followed by two hex digits becomes a single
** byte specified by the two digits
**
| > > > > > > > > > > > > > > > > > > | 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 |
*/
void canonical16(char *z, int n){
while( *z && n-- ){
*z = zEncode[zDecode[(*z)&0x7f]&0x1f];
z++;
}
}
/*
** Decode hexadecimal into a string and return the new string. Space to
** hold the string is obtained from fossil_malloc() and should be released
** by the caller.
**
** If the input is not hex, return NULL.
*/
char *decode16_dup(const char *zIn){
int nIn = (int)strlen(zIn);
char *zOut;
if( !validate16(zIn, nIn) ) return 0;
zOut = fossil_malloc(nIn/2+1);
decode16((const u8*)zIn, (u8*)zOut, nIn);
zOut[nIn/2] = 0;
return zOut;
}
/*
** Decode a string encoded using "quoted-printable".
**
** (1) "=" followed by two hex digits becomes a single
** byte specified by the two digits
**
|
| ︙ | ︙ |
Changes to src/etag.c.
| ︙ | ︙ | |||
92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
/*
** Generate an ETag
*/
void etag_check(unsigned eFlags, const char *zHash){
const char *zIfNoneMatch;
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;
| > > | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
/*
** Generate an ETag
*/
void etag_check(unsigned eFlags, const char *zHash){
const char *zIfNoneMatch;
char zBuf[50];
const int cchETag = 32; /* Not including NULL terminator. */
int cch; /* Length of zIfNoneMatch header. */
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;
|
| ︙ | ︙ | |||
155 156 157 158 159 160 161 |
md5sum_step_text("login: ", -1);
md5sum_step_text(g.zLogin, -1);
md5sum_step_text("\n", 1);
}
}
/* Generate the ETag */
| | | > > > > > | > | 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 |
md5sum_step_text("login: ", -1);
md5sum_step_text(g.zLogin, -1);
md5sum_step_text("\n", 1);
}
}
/* Generate the ETag */
memcpy(zETag, md5sum_finish(0), cchETag+1);
/* Check to see if the generated ETag matches If-None-Match and
** generate a 304 reply if it does. Test both with and without
** double quotes. */
zIfNoneMatch = P("HTTP_IF_NONE_MATCH");
if( zIfNoneMatch==0 ) return;
cch = strlen(zIfNoneMatch);
if( cch==cchETag+2 && zIfNoneMatch[0]=='"' && zIfNoneMatch[cch-1]=='"' ){
if( memcmp(&zIfNoneMatch[1],zETag,cchETag)!=0 ) return;
}else{
if( strcmp(zIfNoneMatch,zETag)!=0 ) return;
}
/* If we get this far, it means that the content has
** not changed and we can do a 304 reply */
cgi_reset_content();
cgi_set_status(304, "Not Modified");
cgi_reply();
db_close(0);
|
| ︙ | ︙ |
Changes to src/export.c.
| ︙ | ︙ | |||
478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
** --rename-trunk NAME Use NAME as name of exported trunk branch
** -R|--repository REPO Export the given REPOSITORY
**
** See also: import
*/
/*
** COMMAND: export*
**
** This command is deprecated. Use "fossil git export" instead.
*/
void export_cmd(void){
Stmt q, q2, q3;
Bag blobs, vers;
unsigned int unused_mark = 1;
| > > | 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
** --rename-trunk NAME Use NAME as name of exported trunk branch
** -R|--repository REPO Export the given REPOSITORY
**
** See also: import
*/
/*
** COMMAND: export*
**
** Usage: %fossil export --git [REPOSITORY]
**
** This command is deprecated. Use "fossil git export" instead.
*/
void export_cmd(void){
Stmt q, q2, q3;
Bag blobs, vers;
unsigned int unused_mark = 1;
|
| ︙ | ︙ | |||
1070 1071 1072 1073 1074 1075 1076 | ** ** Return zero on success and non-zero if the export should be stopped. */ static int gitmirror_send_checkin( FILE *xCmd, /* Write fast-import text on this pipe */ int rid, /* BLOB.RID for the check-in to export */ const char *zUuid, /* BLOB.UUID for the check-in to export */ | | < > > | | 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 |
**
** Return zero on success and non-zero if the export should be stopped.
*/
static int gitmirror_send_checkin(
FILE *xCmd, /* Write fast-import text on this pipe */
int rid, /* BLOB.RID for the check-in to export */
const char *zUuid, /* BLOB.UUID for the check-in to export */
int *pnLimit /* Stop when the counter reaches zero */
){
Manifest *pMan; /* The check-in to be output */
int i; /* Loop counter */
int iParent; /* Which immediate ancestor is primary. -1 for none */
Stmt q; /* An SQL query */
char *zBranch; /* The branch of the check-in */
char *zMark; /* The Git-name of the check-in */
Blob sql; /* String of SQL for part of the query */
Blob comment; /* The comment text for the check-in */
int nErr = 0; /* Number of errors */
int bPhantomOk; /* True if phantom files should be ignored */
char buf[24];
char *zEmail; /* Contact info for Git committer field */
int fManifest; /* Should the manifest files be included? */
int fPManifest = 0; /* OR of the manifest files for all parents */
pMan = manifest_get(rid, CFTYPE_MANIFEST, 0);
if( pMan==0 ){
/* Must be a phantom. Return without doing anything, and in particular
** without creating a mark for this check-in. */
gitmirror_message(VERB_NORMAL, "missing check-in: %s\n", zUuid);
return 0;
}
/* Check to see if any parent logins have not yet been processed, and
** if so, create them */
for(i=0; i<pMan->nParent; i++){
char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0);
if( zPMark==0 ){
int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q",
pMan->azParent[i]);
int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i],
pnLimit);
if( rc || *pnLimit<=0 ){
manifest_destroy(pMan);
return 1;
}
}
fossil_free(zPMark);
}
|
| ︙ | ︙ | |||
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 |
blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid);
fprintf(xCmd, "data %d\n%s\n", blob_strlen(&comment), blob_str(&comment));
blob_reset(&comment);
iParent = -1; /* Which ancestor is the primary parent */
for(i=0; i<pMan->nParent; i++){
char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
if( zOther==0 ) continue;
if( iParent<0 ){
iParent = i;
fprintf(xCmd, "from %s\n", zOther);
}else{
fprintf(xCmd, "merge %s\n", zOther);
}
fossil_free(zOther);
| > | 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 |
blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid);
fprintf(xCmd, "data %d\n%s\n", blob_strlen(&comment), blob_str(&comment));
blob_reset(&comment);
iParent = -1; /* Which ancestor is the primary parent */
for(i=0; i<pMan->nParent; i++){
char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
if( zOther==0 ) continue;
fPManifest |= db_get_manifest_setting(pMan->azParent[i]);
if( iParent<0 ){
iParent = i;
fprintf(xCmd, "from %s\n", zOther);
}else{
fprintf(xCmd, "merge %s\n", zOther);
}
fossil_free(zOther);
|
| ︙ | ︙ | |||
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 |
fossil_free(zFNQuoted);
}
db_finalize(&q);
manifest_destroy(pMan);
pMan = 0;
/* Include Fossil-generated auxiliary files in the check-in */
if( fManifest & MFESTFLG_RAW ){
Blob manifest;
content_get(rid, &manifest);
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));
blob_reset(&tagslist);
}
/* The check-in is finished, so decrement the counter */
(*pnLimit)--;
return 0;
}
| > > > > > > > | 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 |
fossil_free(zFNQuoted);
}
db_finalize(&q);
manifest_destroy(pMan);
pMan = 0;
/* Include Fossil-generated auxiliary files in the check-in */
fManifest = db_get_manifest_setting(zUuid);
if( fManifest & MFESTFLG_RAW ){
Blob manifest;
content_get(rid, &manifest);
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);
}else if( fPManifest & MFESTFLG_RAW ){
fprintf(xCmd, "D manifest\n");
}
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);
}else if( fPManifest & MFESTFLG_UUID ){
fprintf(xCmd, "D manifest.uuid\n");
}
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));
blob_reset(&tagslist);
}else if( fPManifest & MFESTFLG_TAGS ){
fprintf(xCmd, "D manifest.tags\n");
}
/* The check-in is finished, so decrement the counter */
(*pnLimit)--;
return 0;
}
|
| ︙ | ︙ | |||
1379 1380 1381 1382 1383 1384 1385 | const char *zAutoPush = 0; /* Value of the --autopush flag */ char *zMainBr = 0; /* Value of the --mainbranch flag */ char *zPushUrl; /* URL to sync the mirror to */ double rEnd; /* time of most recent export */ int rc; /* Result code */ int bForce; /* Do the export and sync even if no changes*/ int bNeedRepack = 0; /* True if we should run repack at the end */ | < | 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 |
const char *zAutoPush = 0; /* Value of the --autopush flag */
char *zMainBr = 0; /* Value of the --mainbranch flag */
char *zPushUrl; /* URL to sync the mirror to */
double rEnd; /* time of most recent export */
int rc; /* Result code */
int bForce; /* Do the export and sync even if no changes*/
int bNeedRepack = 0; /* True if we should run repack at the end */
int bIfExists; /* The --if-mirrored flag */
FILE *xCmd; /* Pipe to the "git fast-import" command */
FILE *pMarks; /* Git mark files */
Stmt q; /* Queries */
char zLine[200]; /* One line of a mark file */
zDebug = find_option("debug",0,1);
|
| ︙ | ︙ | |||
1517 1518 1519 1520 1521 1522 1523 |
" WHERE key='start'),0.0)")
){
gitmirror_message(VERB_NORMAL, "no changes\n");
db_commit_transaction();
return;
}
| < < < | 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 |
" WHERE key='start'),0.0)")
){
gitmirror_message(VERB_NORMAL, "no changes\n");
db_commit_transaction();
return;
}
/* Change to the MIRROR directory so that the Git commands will work */
rc = file_chdir(zMirror, 0);
if( rc ) fossil_fatal("cannot change the working directory to \"%s\"",
zMirror);
/* Start up the git fast-import command */
if( zDebug ){
|
| ︙ | ︙ | |||
1575 1576 1577 1578 1579 1580 1581 |
"SELECT objid, mtime, uuid FROM tomirror ORDER BY mtime"
);
while( nLimit && db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
double rMTime = db_column_double(&q, 1);
const char *zUuid = db_column_text(&q, 2);
if( rMTime>rEnd ) rEnd = rMTime;
| | | 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 |
"SELECT objid, mtime, uuid FROM tomirror ORDER BY mtime"
);
while( nLimit && db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
double rMTime = db_column_double(&q, 1);
const char *zUuid = db_column_text(&q, 2);
if( rMTime>rEnd ) rEnd = rMTime;
rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit);
if( rc ) break;
gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal);
fflush(stdout);
}
db_finalize(&q);
fprintf(xCmd, "done\n");
if( zDebug ){
|
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
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 |
if( !zTail ) return 0;
while( z[0] ){
if( fossil_isdirsep(z[0]) ) zTail = &z[1];
z++;
}
return zTail;
}
/*
** Return the directory of a file path name. The directory is all components
** except the last one. For example, the directory of "/a/b/c.d" is "/a/b".
** If there is no directory, NULL is returned; otherwise, the returned memory
** should be freed via fossil_free().
*/
char *file_dirname(const char *z){
const char *zTail = file_tail(z);
if( zTail && zTail!=z ){
return mprintf("%.*s", (int)(zTail-z-1), z);
}else{
return 0;
}
}
/* SQL Function: file_dirname(NAME)
**
** Return the directory for NAME
*/
void file_dirname_sql_function(
sqlite3_context *context,
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
if( !zTail ) return 0;
while( z[0] ){
if( fossil_isdirsep(z[0]) ) zTail = &z[1];
z++;
}
return zTail;
}
/*
** Return the tail of a command: the basename of the putative executable (which
** could be quoted when containing spaces) and the following arguments.
*/
const char *command_tail(const char *z){
const char *zTail = z;
char chQuote = 0;
if( !zTail ) return 0;
while( z[0] && (!fossil_isspace(z[0]) || chQuote) ){
if( z[0]=='"' || z[0]=='\'' ){
chQuote = (chQuote==z[0]) ? 0 : z[0];
}
if( fossil_isdirsep(z[0]) ) zTail = &z[1];
z++;
}
return zTail;
}
/*
** Return the directory of a file path name. The directory is all components
** except the last one. For example, the directory of "/a/b/c.d" is "/a/b".
** If there is no directory, NULL is returned; otherwise, the returned memory
** should be freed via fossil_free().
*/
char *file_dirname(const char *z){
const char *zTail = file_tail(z);
if( zTail && zTail!=z ){
return mprintf("%.*s", (int)(zTail-z-1), z);
}else{
return 0;
}
}
/*
** Return the basename of the putative executable in a command (w/o arguments).
** The returned memory should be freed via fossil_free().
*/
char *command_basename(const char *z){
const char *zTail = command_tail(z);
const char *zEnd = zTail;
while( zEnd[0] && !fossil_isspace(zEnd[0]) && zEnd[0]!='"' && zEnd[0]!='\'' ){
zEnd++;
}
if( zEnd ){
return mprintf("%.*s", (int)(zEnd-zTail), zTail);
}else{
return 0;
}
}
/* SQL Function: file_dirname(NAME)
**
** Return the directory for NAME
*/
void file_dirname_sql_function(
sqlite3_context *context,
|
| ︙ | ︙ | |||
1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 |
/*
** COMMAND: test-which
**
** Usage: %fossil test-which ARGS...
**
** For each argument, search the PATH for the executable with the name
** and print its full pathname.
*/
void test_which_cmd(void){
int i;
for(i=2; i<g.argc; i++){
char *z = file_fullexename(g.argv[i]);
fossil_print("%z\n", z);
}
| > > > > > > > | 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 |
/*
** COMMAND: test-which
**
** Usage: %fossil test-which ARGS...
**
** For each argument, search the PATH for the executable with the name
** and print its full pathname.
**
** See also the "which" command (without the "test-" prefix). The plain
** "which" command is more convenient to use since it provides the -a/-all
** option, and because it is shorter. The "fossil which" command without
** the "test-" prefix is recommended for day-to-day use. This command is
** retained because it tests the internal file_fullexename() function
** whereas plain "which" does not.
*/
void test_which_cmd(void){
int i;
for(i=2; i<g.argc; i++){
char *z = file_fullexename(g.argv[i]);
fossil_print("%z\n", z);
}
|
| ︙ | ︙ |
Changes to src/finfo.c.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 | ** ** The -i mode will print various facts about FILENAME, including its ** hash and the check-in and time when the current version of the file ** was created. Use -v for additional information. Add the -r VERSION ** option to see similar information about the same file for the check-in ** specified by VERSION. ** | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | ** ** The -i mode will print various facts about FILENAME, including its ** hash and the check-in and time when the current version of the file ** was created. Use -v for additional information. Add the -r VERSION ** option to see similar information about the same file for the check-in ** specified by VERSION. ** ** The -s mode prints the status as <status> <revision>. This is ** a quick status and does not check for up-to-date-ness of the file. ** ** In the -p mode, there's an optional flag "-r|--revision REVISION". ** The specified version (or the latest checked-out version) is printed ** to stdout. The -p mode is another form of the "cat" command. ** ** Options: |
| ︙ | ︙ | |||
496 497 498 499 500 501 502 |
" LEFT JOIN filename ON filename.fnid=clade.fnid\n"
"WHERE mlink.fnid=clade.fnid AND mlink.fid=clade.fid\n"
" AND event.objid=mlink.mid\n",
TAG_BRANCH
);
if( (zA = P("a"))!=0 ){
blob_append_sql(&sql, " AND event.mtime>=%.16g\n",
| | | | 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 |
" LEFT JOIN filename ON filename.fnid=clade.fnid\n"
"WHERE mlink.fnid=clade.fnid AND mlink.fid=clade.fid\n"
" AND event.objid=mlink.mid\n",
TAG_BRANCH
);
if( (zA = P("a"))!=0 ){
blob_append_sql(&sql, " AND event.mtime>=%.16g\n",
symbolic_name_to_mtime(zA,0,0));
url_add_parameter(&url, "a", zA);
}
if( (zB = P("b"))!=0 ){
blob_append_sql(&sql, " AND event.mtime<=%.16g\n",
symbolic_name_to_mtime(zB,0,1));
url_add_parameter(&url, "b", zB);
}
if( ridFrom ){
blob_append_sql(&sql,
" AND mlink.mid IN (SELECT rid FROM ancestor)\n"
"GROUP BY mlink.fid\n"
);
|
| ︙ | ︙ | |||
634 635 636 637 638 639 640 641 642 643 644 645 646 647 |
}
db_reset(&qparent);
if( zBr==0 ) zBr = "trunk";
if( uBg ){
zBgClr = user_color(zUser);
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
}
gidx = graph_add_row(pGraph,
frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000,
nParent, 0, aParent, zBr, zBgClr,
zUuid, 0);
if( strncmp(zDate, zPrevDate, 10) ){
sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
| > > | 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 |
}
db_reset(&qparent);
if( zBr==0 ) zBr = "trunk";
if( uBg ){
zBgClr = user_color(zUser);
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
}else if( zBgClr ){
zBgClr = reasonable_bg_color(zBgClr,0);
}
gidx = graph_add_row(pGraph,
frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000,
nParent, 0, aParent, zBr, zBgClr,
zUuid, 0);
if( strncmp(zDate, zPrevDate, 10) ){
sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
|
| ︙ | ︙ |
Changes to src/forum.c.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
*/
struct ForumThread {
ForumPost *pFirst; /* First post in chronological order */
ForumPost *pLast; /* Last post in chronological order */
ForumPost *pDisplay; /* Entries in display order */
ForumPost *pTail; /* Last on the display list */
int mxIndent; /* Maximum indentation level */
};
#endif /* INTERFACE */
/*
** Return true if the forum post with the given rid has been
** subsequently edited.
*/
| > | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
*/
struct ForumThread {
ForumPost *pFirst; /* First post in chronological order */
ForumPost *pLast; /* Last post in chronological order */
ForumPost *pDisplay; /* Entries in display order */
ForumPost *pTail; /* Last on the display list */
int mxIndent; /* Maximum indentation level */
int nArtifact; /* Number of forum artifacts in this thread */
};
#endif /* INTERFACE */
/*
** Return true if the forum post with the given rid has been
** subsequently edited.
*/
|
| ︙ | ︙ | |||
107 108 109 110 111 112 113 | ** value. Returns 0 if !p. For an edited chain of post, the tag is ** checked on the pEditHead entry, to simplify subsequent unlocking of ** the post. ** ** If bCheckIrt is true then p's thread in-response-to parents are ** checked (recursively) for closure, else only p is checked. */ | | > > > > > | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
** value. Returns 0 if !p. For an edited chain of post, the tag is
** checked on the pEditHead entry, to simplify subsequent unlocking of
** the post.
**
** If bCheckIrt is true then p's thread in-response-to parents are
** checked (recursively) for closure, else only p is checked.
*/
static int forumpost_is_closed(
ForumThread *pThread, /* Thread that the post is a member of */
ForumPost *p, /* the forum post */
int bCheckIrt /* True to check In-Reply-To posts */
){
int mx = pThread->nArtifact+1;
while( p && (mx--)>0 ){
if( p->pEditHead ) p = p->pEditHead;
if( p->iClosed || !bCheckIrt ) return p->iClosed;
p = p->pIrt;
}
return 0;
}
|
| ︙ | ︙ | |||
407 408 409 410 411 412 413 414 415 416 417 418 419 420 |
pPost->pNext = 0;
if( pThread->pLast==0 ){
pThread->pFirst = pPost;
}else{
pThread->pLast->pNext = pPost;
}
pThread->pLast = pPost;
/* Find the in-reply-to post. Default to the topic post if the replied-to
** post cannot be found. */
if( firt ){
pPost->pIrt = pThread->pFirst;
for(p=pThread->pFirst; p; p=p->pNext){
if( p->fpid==firt ){
| > | 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 |
pPost->pNext = 0;
if( pThread->pLast==0 ){
pThread->pFirst = pPost;
}else{
pThread->pLast->pNext = pPost;
}
pThread->pLast = pPost;
pThread->nArtifact++;
/* Find the in-reply-to post. Default to the topic post if the replied-to
** post cannot be found. */
if( firt ){
pPost->pIrt = pThread->pFirst;
for(p=pThread->pFirst; p; p=p->pNext){
if( p->fpid==firt ){
|
| ︙ | ︙ | |||
518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
if( froot==0 ){
fossil_fatal("Not a forum post: \"%s\"", zName);
}
fossil_print("fpid = %d\n", fpid);
fossil_print("froot = %d\n", froot);
pThread = forumthread_create(froot, 1);
fossil_print("Chronological:\n");
fossil_print(
/* 0 1 2 3 4 5 6 7 */
/* 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123 */
" sid rev closed fpid pIrt pEditPrev pEditTail hash\n");
for(p=pThread->pFirst; p; p=p->pNext){
fossil_print("%4d %4d %7d %9d %9d %9d %9d %8.8s\n",
| > | 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
froot = db_int(0, "SELECT froot FROM forumpost WHERE fpid=%d", fpid);
if( froot==0 ){
fossil_fatal("Not a forum post: \"%s\"", zName);
}
fossil_print("fpid = %d\n", fpid);
fossil_print("froot = %d\n", froot);
pThread = forumthread_create(froot, 1);
fossil_print("count = %d\n", pThread->nArtifact);
fossil_print("Chronological:\n");
fossil_print(
/* 0 1 2 3 4 5 6 7 */
/* 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123 */
" sid rev closed fpid pIrt pEditPrev pEditTail hash\n");
for(p=pThread->pFirst; p; p=p->pNext){
fossil_print("%4d %4d %7d %9d %9d %9d %9d %8.8s\n",
|
| ︙ | ︙ | |||
563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
void forumthreadhashlist(void){
int fpid;
int froot;
const char *zName = P("name");
ForumThread *pThread;
ForumPost *p;
char *fuuid;
login_check_credentials();
if( !g.perm.Admin ){
return;
}
if( zName==0 ){
webpage_error("Missing \"name=\" query parameter");
| > | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 |
void forumthreadhashlist(void){
int fpid;
int froot;
const char *zName = P("name");
ForumThread *pThread;
ForumPost *p;
char *fuuid;
Stmt q;
login_check_credentials();
if( !g.perm.Admin ){
return;
}
if( zName==0 ){
webpage_error("Missing \"name=\" query parameter");
|
| ︙ | ︙ | |||
597 598 599 600 601 602 603 604 605 606 607 608 609 610 |
@ <pre>
pThread = forumthread_create(froot, 1);
for(p=pThread->pFirst; p; p=p->pNext){
@ %h(p->zUuid)
}
forumthread_delete(pThread);
@ </pre>
style_finish_page();
}
/*
** Render a forum post for display
*/
void forum_render(
| > > > > > > > > > > > > > > > > > | 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 |
@ <pre>
pThread = forumthread_create(froot, 1);
for(p=pThread->pFirst; p; p=p->pNext){
@ %h(p->zUuid)
}
forumthread_delete(pThread);
@ </pre>
@ <hr>
@ <h2>Related FORUMPOST Table Content</h2>
@ <table border="1" cellpadding="4" cellspacing="0">
@ <tr><th>fpid<th>froot<th>fprev<th>firt<th>fmtime
db_prepare(&q, "SELECT fpid, froot, fprev, firt, datetime(fmtime)"
" FROM forumpost"
" WHERE froot=%d"
" ORDER BY fmtime", froot);
while( db_step(&q)==SQLITE_ROW ){
@ <tr><td>%d(db_column_int(&q,0))\
@ <td>%d(db_column_int(&q,1))\
@ <td>%d(db_column_int(&q,2))\
@ <td>%d(db_column_int(&q,3))\
@ <td>%h(db_column_text(&q,4))</tr>
}
@ </table>
db_finalize(&q);
style_finish_page();
}
/*
** Render a forum post for display
*/
void forum_render(
|
| ︙ | ︙ | |||
723 724 725 726 727 728 729 730 731 732 733 734 735 736 | } /* ** Display a single post in a forum thread. */ static void forum_display_post( ForumPost *p, /* Forum post to display */ int iIndentScale, /* Indent scale factor */ int bRaw, /* True to omit the border */ int bUnf, /* True to leave the post unformatted */ int bHist, /* True if showing edit history */ int bSelect, /* True if this is the selected post */ char *zQuery /* Common query string */ | > | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | } /* ** Display a single post in a forum thread. */ static void forum_display_post( ForumThread *pThread, /* The thread that this post is a member of */ ForumPost *p, /* Forum post to display */ int iIndentScale, /* Indent scale factor */ int bRaw, /* True to omit the border */ int bUnf, /* True to leave the post unformatted */ int bHist, /* True if showing edit history */ int bSelect, /* True if this is the selected post */ char *zQuery /* Common query string */ |
| ︙ | ︙ | |||
745 746 747 748 749 750 751 | int iIndent; /* Indent level */ int iClosed; /* True if (sub)thread is closed */ const char *zMimetype;/* Formatting MIME type */ /* Get the manifest for the post. Abort if not found (e.g. shunned). */ pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0); if( !pManifest ) return; | | | | 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 |
int iIndent; /* Indent level */
int iClosed; /* True if (sub)thread is closed */
const char *zMimetype;/* Formatting MIME type */
/* Get the manifest for the post. Abort if not found (e.g. shunned). */
pManifest = manifest_get(p->fpid, CFTYPE_FORUM, 0);
if( !pManifest ) return;
iClosed = forumpost_is_closed(pThread, p, 1);
/* When not in raw mode, create the border around the post. */
if( !bRaw ){
/* Open the <div> enclosing the post. Set the class string to mark the post
** as selected and/or obsolete. */
iIndent = (p->pEditHead ? p->pEditHead->nIndent : p->nIndent)-1;
@ <div id='forum%d(p->fpid)' class='forumTime\
@ %s(bSelect ? " forumSel" : "")\
@ %s(iClosed ? " forumClosed" : "")\
@ %s(p->pEditTail ? " forumObs" : "")' \
if( iIndent && iIndentScale ){
|
| ︙ | ︙ | |||
904 905 906 907 908 909 910 |
if( bSelect && forumpost_may_close() && iClosed>=0 ){
int iHead = forumpost_head_rid(p->fpid);
@ <form method="post" \
@ action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'>
login_insert_csrf_secret();
@ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" />
if( moderation_pending(p->fpid)==0 ){
| | > | 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 |
if( bSelect && forumpost_may_close() && iClosed>=0 ){
int iHead = forumpost_head_rid(p->fpid);
@ <form method="post" \
@ action='%R/forumpost_%s(iClosed > 0 ? "reopen" : "close")'>
login_insert_csrf_secret();
@ <input type="hidden" name="fpid" value="%z(rid_to_uuid(iHead))" />
if( moderation_pending(p->fpid)==0 ){
@ <input type="button" value='%s(iClosed ? "Re-open" : "Close")' \
@ class='%s(iClosed ? "action-reopen" : "action-close")'/>
}
@ </form>
}
@ </div>
}
@ </div>
}
|
| ︙ | ︙ | |||
1025 1026 1027 1028 1029 1030 1031 |
p = mode==FD_CHRONO ? pThread->pFirst : pThread->pDisplay;
if( !bHist && p->pEditTail ) p = p->pEditTail;
}
/* Display the appropriate subset of posts in sequence. */
while( p ){
/* Display the post. */
| | | 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 |
p = mode==FD_CHRONO ? pThread->pFirst : pThread->pDisplay;
if( !bHist && p->pEditTail ) p = p->pEditTail;
}
/* Display the appropriate subset of posts in sequence. */
while( p ){
/* Display the post. */
forum_display_post(pThread, p, iIndentScale, mode==FD_RAW,
bUnf, bHist, p==pSelect, zQuery);
/* Advance to the next post in the thread. */
if( mode==FD_CHRONO ){
/* Chronological mode: display posts (optionally including edits) in their
** original commit order. */
if( bHist ){
|
| ︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 |
/*
** Emit Forum Javascript which applies (or optionally can apply)
** to all forum-related pages. It does not include page-specific
** code (e.g. "forum.js").
*/
static void forum_emit_js(void){
| | > | 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 |
/*
** Emit Forum Javascript which applies (or optionally can apply)
** to all forum-related pages. It does not include page-specific
** code (e.g. "forum.js").
*/
static void forum_emit_js(void){
builtin_fossil_js_bundle_or("copybutton", "pikchr", "confirmer",
NULL);
builtin_request_js("fossil.page.forumpost.js");
}
/*
** WEBPAGE: forumpost
**
** Show a single forum posting. The posting is shown in context with
|
| ︙ | ︙ | |||
1828 1829 1830 1831 1832 1833 1834 |
@ <h2>Moderators</h2>
if( db_int(0, "SELECT count(*) FROM user "
" WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){
@ <p>No non-supervisor moderators
}else{
Stmt q = empty_Stmt;
| < < | 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 |
@ <h2>Moderators</h2>
if( db_int(0, "SELECT count(*) FROM user "
" WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){
@ <p>No non-supervisor moderators
}else{
Stmt q = empty_Stmt;
db_prepare(&q, "SELECT uid, login, cap FROM user "
"WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'"
" ORDER BY login");
@ <table class='bordered'>
@ <thead><tr><th>User</th><th>Capabilities</th></tr></thead>
@ <tbody>
while( SQLITE_ROW==db_step(&q) ){
const int iUid = db_column_int(&q, 0);
const char *zUser = db_column_text(&q, 1);
const char *zCap = db_column_text(&q, 2);
@ <tr>
@ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
@ <td>(%h(zCap))</td>
@ </tr>
}
db_finalize(&q);
@ </tbody></table>
|
| ︙ | ︙ |
Changes to src/fossil.diff.js.
| ︙ | ︙ | |||
43 44 45 46 47 48 49 |
/* Install a diff-toggle button for the given diff table element. */
const addToggle = function(diffElem){
const sib = diffElem.previousElementSibling,
ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
if(!sib) return;
const lblToggle = D.label();
D.append(lblToggle, ckbox, D.text(" show/hide "));
| < > > > | > > > > > > > > | 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 |
/* Install a diff-toggle button for the given diff table element. */
const addToggle = function(diffElem){
const sib = diffElem.previousElementSibling,
ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
if(!sib) return;
const lblToggle = D.label();
D.append(lblToggle, ckbox, D.text(" show/hide "));
allToggles.push(ckbox);
++checkedCount;
/* Make all of the available empty space a click zone for the checkbox */
lblToggle.style.flexGrow = 1;
lblToggle.style.textAlign = 'right';
D.append(sib, lblToggle);
ckbox.addEventListener('change', function(){
diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
if(btnAll){
checkedCount += (this.checked ? 1 : -1);
btnAll.innerText = (checkedCount < allToggles.length)
? "Show diffs" : "Hide diffs";
}
}, false);
/* Extend the toggle click zone to all of the non-hyperlink
elements in the left of this area (filenames and hashes). */
sib.firstElementChild.addEventListener('click', (event)=>{
if( event.target===sib.firstElementChild ){
/* Don't respond to clicks bubbling via hyperlink children */
ckbox.click();;
}
}, false);
};
if( !document.querySelector('body.fdiff') ){
/* Don't show the diff toggle button for /fdiff because it only
has a single file to show (and also a different DOM layout). */
document.querySelectorAll('table.diff').forEach(addToggle);
}
|
| ︙ | ︙ |
Changes to src/fossil.dom.js.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
},
createElemFactory: function(eType){
return function(){
return document.createElement(eType);
};
},
remove: function(e){
| | | | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
},
createElemFactory: function(eType){
return function(){
return document.createElement(eType);
};
},
remove: function(e){
if(e?.forEach){
e.forEach(
(x)=>x?.parentNode?.removeChild(x)
);
}else{
e?.parentNode?.removeChild(e);
}
return e;
},
/**
Removes all child DOM elements from the given element
and returns that element.
|
| ︙ | ︙ |
Changes to src/fossil.fetch.js.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | - onload: callback(responseData) (default = output response to the console). In the context of the callback, the options object is "this", noting that this call may have amended the options object with state other than what the caller provided. - onerror: callback(Error object) (default = output error message to console.error() and fossil.error()). Triggered if the request | | < | | | > | > > > > > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > | > > > > | | 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 |
- onload: callback(responseData) (default = output response to the
console). In the context of the callback, the options object is
"this", noting that this call may have amended the options object
with state other than what the caller provided.
- onerror: callback(Error object) (default = output error message
to console.error() and fossil.error()). Triggered if the request
generates any response other than HTTP 200, or if the beforesend()
or onload() handler throws an exception. In the context of the
callback, the options object is "this". This function is intended
to be used solely for error reporting, not error recovery. Special
cases for the Error object:
1. Timeouts unfortunately show up as a series of 2 events: an
HTTP 0 followed immediately by an XHR.ontimeout(). The former
cannot(?) be unambiguously identified as the trigger for the
pending timeout, so we have no option but to pass it on as-is
instead of flagging it as a timeout response. The latter will
trigger the client-provided ontimeout() if it's available (see
below), else it calls the onerror() callback. An error object
passed to ontimeout() by fetch() will have (.name='timeout',
.status=XHR.status).
2. Else if the response contains a JSON-format exception on the
server, it will have (.name='json-error',
status=XHR.status). Any JSON-format result object which has a
property named "error" is considered to be a server-generated
error.
3. Else if it gets a non 2xx HTTP code then it will have
(.name='http',.status=XHR.status).
4. If onerror() throws, the exception is suppressed but may
generate a console error message.
- ontimeout: callback(Error object). If set, timeout errors are
reported here, else they are reported through onerror().
Unfortunately, XHR fires two events for a timeout: an
onreadystatechange() and an ontimeout(), in that order. From the
former, however, we cannot unambiguously identify the error as
having been caused by a timeout, so clients which set ontimeout()
will get _two_ callback calls: one with with an HTTP error response
followed immediately by an ontimeout() response. Error objects
passed to this will have (.name='timeout', .status=xhr.HttpStatus).
In the context of the callback, the options object is "this", Like
onerror(), any exceptions thrown by the ontimeout() handler are
suppressed, but may generate a console error message. The onerror()
handler is _not_ called in this case.
- method: 'POST' | 'GET' (default = 'GET'). CASE SENSITIVE!
- payload: anything acceptable by XHR2.send(ARG) (DOMString,
Document, FormData, Blob, File, ArrayBuffer), or a plain object or
array, either of which gets JSON.stringify()'d. If payload is set
then the method is automatically set to 'POST'. By default XHR2
will set the content type based on the payload type. If an
object/array is converted to JSON, the contentType option is
automatically set to 'application/json', and if JSON.stringify() of
that value fails then the exception is propagated to this
function's caller. (beforesend(), aftersend(), and onerror() are
NOT triggered in that case.)
- contentType: Optional request content type when POSTing. Ignored
if the method is not 'POST'.
- responseType: optional string. One of ("text", "arraybuffer",
"blob", or "document") (as specified by XHR2). Default = "text".
As an extension, it supports "json", which tells it that the
response is expected to be text and that it should be JSON.parse()d
before passing it on to the onload() callback. If parsing of such
an object fails, the onload callback is not called, and the
onerror() callback is passed the exception from the parsing error.
If the parsed JSON object has an "error" property, it is assumed to
be an error string, which is used to populate a new Error object,
which will gets (.name="json") set on it.
- urlParams: string|object. If a string, it is assumed to be a
URI-encoded list of params in the form "key1=val1&key2=val2...",
with NO leading '?'. If it is an object, all of its properties get
converted to that form. Either way, the parameters get appended to
the URL before submitting the request.
- responseHeaders: If true, the onload() callback is passed an
additional argument: a map of all of the response headers. If it's
a string value, the 2nd argument passed to onload() is instead the
value of that single header. If it's an array, it's treated as a
list of headers to return, and the 2nd argument is a map of those
header values. When a map is passed on, all of its keys are
lower-cased. When a given header is requested and that header is
set multiple times, their values are (per the XHR docs)
concatenated together with "," between them.
- beforesend/aftersend: optional callbacks which are called
without arguments immediately before the request is submitted
and immediately after it is received, regardless of success or
error. In the context of the callback, the options object is
the "this". These can be used to, e.g., keep track of in-flight
requests and update the UI accordingly, e.g. disabling/enabling
|
| ︙ | ︙ | |||
131 132 133 134 135 136 137 |
const value = parts.join(': ');
rc[header.toLowerCase()] = value;
});
return rc;
};
}
if('/'===uri[0]) uri = uri.substr(1);
| | | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
const value = parts.join(': ');
rc[header.toLowerCase()] = value;
});
return rc;
};
}
if('/'===uri[0]) uri = uri.substr(1);
if(!opt) opt = {}/* should arguably be Object.create(null) */;
else if('function'===typeof opt) opt={onload:opt};
if(!opt.onload) opt.onload = f.onload;
if(!opt.onerror) opt.onerror = f.onerror;
if(!opt.beforesend) opt.beforesend = f.beforesend;
if(!opt.aftersend) opt.aftersend = f.aftersend;
let payload = opt.payload, jsonResponse = false;
if(undefined!==payload){
|
| ︙ | ︙ | |||
162 163 164 165 166 167 168 |
list. We use it as a flag to tell us to JSON.parse()
the response. */
jsonResponse = true;
x.responseType = 'text';
}else{
x.responseType = opt.responseType||'text';
}
| | | > > > > > > > > > > > > > > > > > > | > | > > > > > > | > > > > > > > | > > > > | | | | | | 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 |
list. We use it as a flag to tell us to JSON.parse()
the response. */
jsonResponse = true;
x.responseType = 'text';
}else{
x.responseType = opt.responseType||'text';
}
x.ontimeout = function(ev){
try{opt.aftersend()}catch(e){/*ignore*/}
const err = new Error("XHR timeout of "+x.timeout+"ms expired.");
err.status = x.status;
err.name = 'timeout';
//console.warn("fetch.ontimeout",ev);
try{
(opt.ontimeout || opt.onerror)(err);
}catch(e){
/*ignore*/
console.error("fossil.fetch()'s ontimeout() handler threw",e);
}
};
/* Ensure that if onerror() throws, it's ignored. */
const origOnError = opt.onerror;
opt.onerror = (arg)=>{
try{ origOnError.call(this, arg) }
catch(e){
/*ignored*/
console.error("fossil.fetch()'s onerror() threw",e);
}
};
x.onreadystatechange = function(ev){
//console.warn("onreadystatechange", x.readyState, ev.target.responseText);
if(XMLHttpRequest.DONE !== x.readyState) return;
try{opt.aftersend()}catch(e){/*ignore*/}
if(false && 0===x.status){
/* For reasons unknown, we _sometimes_ trigger x.status==0 in FF
when the /chat page starts up, but not in Chrome nor in other
apps. Insofar as has been determined, this happens before a
request is actually sent and it appears to have no
side-effects on the app other than to generate an error
(i.e. no requests/responses are missing). This is a silly
workaround which may or may not bite us later. If so, it can
be removed at the cost of an unsightly console error message
in FF.
2025-04-10: that behavior is now also in Chrome and enabling
this workaround causes our timeout errors to never arrive.
*/
return;
}
if(200!==x.status){
//console.warn("Error response",ev.target);
let err;
try{
const j = JSON.parse(x.response);
if(j.error){
err = new Error(j.error);
err.name = 'json.error';
}
}catch(ex){/*ignore*/}
if( !err ){
/* We can't tell from here whether this was a timeout-capable
request which timed out on our end or was one which is a
genuine error. We also don't know whether the server timed
out the connection before we did. */
err = new Error("HTTP response status "+x.status+".")
err.name = 'http';
}
err.status = x.status;
opt.onerror(err);
return;
}
const orh = opt.responseHeaders;
let head;
if(true===orh){
head = f.parseResponseHeaders(x.getAllResponseHeaders());
}else if('string'===typeof orh){
head = x.getResponseHeader(orh);
}else if(orh instanceof Array){
head = {};
orh.forEach((s)=>{
if('string' === typeof s) head[s.toLowerCase()] = x.getResponseHeader(s);
});
}
try{
const args = [(jsonResponse && x.response)
? JSON.parse(x.response) : x.response];
if(head) args.push(head);
opt.onload.apply(opt, args);
}catch(err){
opt.onerror(err);
}
}/*onreadystatechange()*/;
try{opt.beforesend()}
catch(err){
opt.onerror(err);
return;
}
x.open(opt.method||'GET', url.join(''), true);
if('POST'===opt.method && 'string'===typeof opt.contentType){
x.setRequestHeader('Content-Type',opt.contentType);
}
x.timeout = +opt.timeout || f.timeout;
|
| ︙ | ︙ |
Changes to src/fossil.numbered-lines.js.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 |
if(!tbl) return /* no matching elements */;
const F = window.fossil, D = F.dom;
const tdLn = tbl.querySelector('td.line-numbers');
const urlArgsRaw = (window.location.search||'?')
.replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
.replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
.replace('?&','?');
| < < < | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
if(!tbl) return /* no matching elements */;
const F = window.fossil, D = F.dom;
const tdLn = tbl.querySelector('td.line-numbers');
const urlArgsRaw = (window.location.search||'?')
.replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
.replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
.replace('?&','?');
const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
const lineTip = new F.PopupWidget({
style: {
cursor: 'pointer'
},
refresh: function(){
const link = this.state.link;
D.clearElement(link);
|
| ︙ | ︙ |
Changes to src/fossil.page.chat.js.
|
| | | 1 2 3 4 5 6 7 8 |
-/**
This file contains the client-side implementation of fossil's /chat
application.
*/
window.fossil.onPageLoad(function(){
const F = window.fossil, D = F.dom;
const E1 = function(selector){
const e = document.querySelector(selector);
|
| ︙ | ︙ | |||
127 128 129 130 131 132 133 |
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)
| > > | | | | | > > | 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 |
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)
beVerbose: false
//!!window.location.hostname.match("localhost")
/* if true then certain, mostly extraneous, error messages and
log 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) */,
inputArea: E1("#chat-input-area"),
inputLineWrapper: 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'),
eMsgPollError: undefined /* current connection error MessageMidget */,
pollErrorMarker: document.body /* element to toggle 'connection-error' CSS class on */
},
me: F.user.name,
mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
pageIsActive: 'visible'===document.visibilityState,
changesSincePageHidden: 0,
notificationBubbleColor: 'white',
|
| ︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
new Date((J - 2440587.5) * 86400000) */
},
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
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
new Date((J - 2440587.5) * 86400000) */
},
filterState:{
activeUser: undefined,
match: function(uname){
return this.activeUser===uname || !this.activeUser;
}
},
/**
The timer object is used to control connection throttling
when connection errors arrise. It starts off with a polling
delay of $initialDelay ms. If there's a connection error,
that gets bumped by some value for each subsequent error, up
to some max value.
The timing of resetting the delay when service returns is,
because of the long-poll connection and our lack of low-level
insight into the connection at this level, a bit wonky.
*/
timer:{
/* setTimeout() ID for (delayed) starting a Chat.poll(), so
that it runs at controlled intervals (which change when a
connection drops and recovers). */
tidPendingPoll: undefined,
tidClearPollErr: undefined /*setTimeout() timer id for
reconnection determination. See
clearPollErrOnWait(). */,
$initialDelay: 1000 /* initial polling interval (ms) */,
currentDelay: 1000 /* current polling interval */,
maxDelay: 60000 * 5 /* max interval when backing off for
connection errors */,
minDelay: 5000 /* minimum delay time for a back-off/retry
attempt. */,
errCount: 0 /* Current poller connection error count */,
minErrForNotify: 4 /* Don't warn for connection errors until this
many have occurred */,
pollTimeout: (1 && window.location.hostname.match(
"localhost" /*presumably local dev mode*/
)) ? 15000
: (+F.config.chat.pollTimeout>0
? (1000 * (F.config.chat.pollTimeout - Math.floor(F.config.chat.pollTimeout * 0.1)))
/* ^^^^^^^^^^^^ we want our timeouts to be slightly shorter
than the server's so that we can distingished timed-out
polls on our end from HTTP errors (if the server times
out). */
: 30000),
/** Returns a random fudge value for reconnect attempt times,
intended to keep the /chat server from getting hammered if
all clients which were just disconnected all reconnect at
the same instant. */
randomInterval: function(factor){
return Math.floor(Math.random() * factor);
},
/** Increments the reconnection delay, within some min/max range. */
incrDelay: function(){
if( this.maxDelay > this.currentDelay ){
if(this.currentDelay < this.minDelay){
this.currentDelay = this.minDelay + this.randomInterval(this.minDelay);
}else{
this.currentDelay = this.currentDelay*2 + this.randomInterval(this.currentDelay);
}
}
return this.currentDelay;
},
/** Resets the delay counter to v || its initial value. */
resetDelay: function(ms=0){
return this.currentDelay = ms || this.$initialDelay;
},
/** Returns true if the timer is set to delayed mode. */
isDelayed: function(){
return (this.currentDelay > this.$initialDelay) ? this.currentDelay : 0;
},
/**
Cancels any in-progress pending-poll timer and starts a new
one with the given delay, defaulting to this.resetDelay().
*/
startPendingPollTimer: function(delay){
this.cancelPendingPollTimer().tidPendingPoll
= setTimeout( Chat.poll, delay || Chat.timer.resetDelay() );
return this;
},
/**
Cancels any still-active timer set to trigger the next
Chat.poll().
*/
cancelPendingPollTimer: function(){
if( this.tidPendingPoll ){
clearTimeout(this.tidPendingPoll);
this.tidPendingPoll = 0;
}
return this;
},
/**
Cancels any pending reconnection attempt back-off timer..
*/
cancelReconnectCheckTimer: function(){
if( this.tidClearPollErr ){
clearTimeout(this.tidClearPollErr);
this.tidClearPollErr = 0;
}
return this;
}
},
/**
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
|
| ︙ | ︙ | |||
604 605 606 607 608 609 610 |
return this;
},
/**
If animations are enabled, passes its arguments
to D.addClassBriefly(), else this is a no-op.
If cb is a function, it is called after the
| | | | 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 |
return this;
},
/**
If animations are enabled, passes its arguments
to D.addClassBriefly(), else this is a no-op.
If cb is a function, it is called after the
CSS class is removed. Returns this object;
*/
animate: function f(e,a,cb){
if(!f.$disabled){
D.addClassBriefly(e, a, 0, cb);
}
return this;
}
}/*Chat object*/;
cs.e.inputFields = [ cs.e.input1, cs.e.inputM, cs.e.inputX ];
cs.e.inputFields.$currentIndex = 0;
cs.e.inputFields.forEach(function(e,ndx){
if(ndx===cs.e.inputFields.$currentIndex) D.removeClass(e,'hidden');
else D.addClass(e,'hidden');
});
if(D.attr(cs.e.inputX,'contenteditable','plaintext-only').isContentEditable){
|
| ︙ | ︙ | |||
643 644 645 646 647 648 649 650 651 652 653 654 655 656 |
Accepts any argument types valid for fossil.toast.error().
*/
cs.reportError = function(/*msg args*/){
const args = argsToArray(arguments);
console.error("chat error:",args);
F.toast.error.apply(F.toast, args);
};
/**
Reports an error in the form of a new message in the chat
feed. All arguments are appended to the message's content area
using fossil.dom.append(), so may be of any type supported by
that function.
*/
cs.reportErrorAsMessage = function f(/*msg args*/){
| > > < > | > | | > > > > > > > > > > > > > > > > > > > > > > > | 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 |
Accepts any argument types valid for fossil.toast.error().
*/
cs.reportError = function(/*msg args*/){
const args = argsToArray(arguments);
console.error("chat error:",args);
F.toast.error.apply(F.toast, args);
};
let InternalMsgId = 0;
/**
Reports an error in the form of a new message in the chat
feed. All arguments are appended to the message's content area
using fossil.dom.append(), so may be of any type supported by
that function.
*/
cs.reportErrorAsMessage = function f(/*msg args*/){
const args = argsToArray(arguments).map(function(v){
return (v instanceof Error) ? v.message : v;
});
if(Chat.beVerbose){
console.error("chat error:",args);
}
const d = new Date().toISOString(),
mw = new this.MessageWidget({
isError: true,
xfrom: undefined,
msgid: "error-"+(++InternalMsgId),
mtime: d,
lmtime: d,
xmsg: args
});
this.injectMessageElem(mw.e.body);
mw.scrollIntoView();
return mw;
};
/**
For use by the connection poller to send a "connection
restored" message.
*/
cs.reportReconnection = function f(/*msg args*/){
const args = argsToArray(arguments).map(function(v){
return (v instanceof Error) ? v.message : v;
});
const d = new Date().toISOString(),
mw = new this.MessageWidget({
isError: false,
xfrom: undefined,
msgid: "reconnect-"+(++InternalMsgId),
mtime: d,
lmtime: d,
xmsg: args
});
this.injectMessageElem(mw.e.body);
mw.scrollIntoView();
return mw;
};
cs.getMessageElemById = function(id){
return qs('[data-msgid="'+id+'"]');
};
/** Finds the last .message-widget element and returns it or
|
| ︙ | ︙ | |||
688 689 690 691 692 693 694 |
};
/**
LOCALLY deletes a message element by the message ID or passing
the .message-row element. Returns true if it removes an element,
else false.
*/
| | > > > > > > > > > > > > > > | > | > | 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 |
};
/**
LOCALLY deletes a message element by the message ID or passing
the .message-row element. Returns true if it removes an element,
else false.
*/
cs.deleteMessageElem = function(id, silent){
var e;
if(id instanceof HTMLElement){
e = id;
id = e.dataset.msgid;
delete e.dataset.msgid;
if( e?.dataset?.alsoRemove ){
const xId = e.dataset.alsoRemove;
delete e.dataset.alsoRemove;
this.deleteMessageElem( xId );
}
}else if(id instanceof Chat.MessageWidget) {
if( this.e.eMsgPollError === e ){
this.e.eMsgPollError = undefined;
}
if(id.e?.body){
this.deleteMessageElem(id.e.body);
}
return;
} else{
e = this.getMessageElemById(id);
}
if(e && id){
D.remove(e);
if(e===this.e.newestMessage){
this.fetchLastMessageElem();
}
if( !silent ){
F.toast.message("Deleted message "+id+".");
}
}
return !!e;
};
/**
Toggles the given message between its parsed and plain-text
representations. It requires a server round-trip to collect the
|
| ︙ | ︙ | |||
774 775 776 777 778 779 780 781 782 783 784 785 786 787 |
}
// We need to fetch the plain-text version...
const self = this;
F.fetch('chat-fetch-one',{
urlParams:{ name: id, raw: true},
responseType: 'json',
onload: function(msg){
content.$elems[1] = D.append(D.pre(),msg.xmsg);
content.$elems[1]._xmsgRaw = msg.xmsg/*used for copy-to-clipboard feature*/;
self.toggleTextMode(e);
},
aftersend:function(){
delete e.$isToggling;
Chat.ajaxEnd();
| > | 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 |
}
// We need to fetch the plain-text version...
const self = this;
F.fetch('chat-fetch-one',{
urlParams:{ name: id, raw: true},
responseType: 'json',
onload: function(msg){
reportConnectionOkay('chat-fetch-one');
content.$elems[1] = D.append(D.pre(),msg.xmsg);
content.$elems[1]._xmsgRaw = msg.xmsg/*used for copy-to-clipboard feature*/;
self.toggleTextMode(e);
},
aftersend:function(){
delete e.$isToggling;
Chat.ajaxEnd();
|
| ︙ | ︙ | |||
832 833 834 835 836 837 838 |
e = id;
id = e.dataset.msgid;
}else{
e = this.getMessageElemById(id);
}
if(!(e instanceof HTMLElement)) return;
if(this.userMayDelete(e)){
| < > > | > | 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 |
e = id;
id = e.dataset.msgid;
}else{
e = this.getMessageElemById(id);
}
if(!(e instanceof HTMLElement)) return;
if(this.userMayDelete(e)){
F.fetch("chat-delete/" + id, {
responseType: 'json',
onload:(r)=>{
reportConnectionOkay('chat-delete');
this.deleteMessageElem(r);
},
onerror:(err)=>this.reportErrorAsMessage(err)
});
}else{
this.deleteMessageElem(id);
}
};
document.addEventListener('visibilitychange', function(ev){
|
| ︙ | ︙ | |||
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 |
}
};
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;
ds.msgid = m.msgid;
ds.xfrom = m.xfrom || '';
if(m.xfrom === Chat.me){
| > | 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 |
}
};
ctor.prototype = {
scrollIntoView: function(){
this.e.content.scrollIntoView();
},
//remove: function(silent){Chat.deleteMessageElem(this, silent);},
setMessage: function(m){
const ds = this.e.body.dataset;
ds.timestamp = m.mtime;
ds.lmtime = m.lmtime;
ds.msgid = m.msgid;
ds.xfrom = m.xfrom || '';
if(m.xfrom === Chat.me){
|
| ︙ | ︙ | |||
1210 1211 1212 1213 1214 1215 1216 |
const toolbar = D.addClass(D.div(), 'toolbar');
D.append(this.e, toolbar);
const btnDeleteLocal = D.button("Delete locally");
D.append(toolbar, btnDeleteLocal);
const self = this;
btnDeleteLocal.addEventListener('click', function(){
self.hide();
| | > > > > > > > > > > | 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 |
const toolbar = D.addClass(D.div(), 'toolbar');
D.append(this.e, toolbar);
const btnDeleteLocal = D.button("Delete locally");
D.append(toolbar, btnDeleteLocal);
const self = this;
btnDeleteLocal.addEventListener('click', function(){
self.hide();
Chat.deleteMessageElem(eMsg)
});
if( eMsg.classList.contains('notification') ){
const btnDeletePoll = D.button("Delete /chat notifications?");
D.append(toolbar, btnDeletePoll);
btnDeletePoll.addEventListener('click', function(){
self.hide();
Chat.e.viewMessages.querySelectorAll(
'.message-widget.notification:not(.resend-message)'
).forEach(e=>Chat.deleteMessageElem(e, true));
});
}
if(Chat.userMayDelete(eMsg)){
const btnDeleteGlobal = D.button("Delete globally");
D.append(toolbar, btnDeleteGlobal);
F.confirmer(btnDeleteGlobal,{
pinSize: true,
ticks: F.config.confirmerButtonTicks,
confirmText: "Confirm delete?",
|
| ︙ | ︙ | |||
1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 |
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
| > | 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 |
urlParams:{
q: '',
n: nFetch,
i: iFirst
},
responseType: "json",
onload:function(jx){
reportConnectionOkay('chat-query');
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
|
| ︙ | ︙ | |||
1522 1523 1524 1525 1526 1527 1528 |
D.append(dd, D.br(), img);
const reader = new FileReader();
reader.onload = (e)=>img.setAttribute('src', e.target.result);
reader.readAsDataURL(blob);
}
};
Chat.e.inputFile.addEventListener('change', function(ev){
| | | 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 |
D.append(dd, D.br(), img);
const reader = new FileReader();
reader.onload = (e)=>img.setAttribute('src', e.target.result);
reader.readAsDataURL(blob);
}
};
Chat.e.inputFile.addEventListener('change', function(ev){
updateDropZoneContent(this?.files[0])
});
/* Handle image paste from clipboard. TODO: figure out how we can
paste non-image binary data as if it had been selected via the
file selection element. */
const pasteListener = function(event){
const items = event.clipboardData.items,
item = items[0];
|
| ︙ | ︙ | |||
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 |
const w = D.addClass(D.div(), 'failed-message');
D.append(w, D.append(
D.span(),"This message was not successfully sent to the server:"
));
if(state.msg){
const ta = D.textarea();
ta.value = state.msg;
D.append(w,ta);
}
if(state.blob){
D.append(w,D.append(D.span(),"Attachment: ",(state.blob.name||"unnamed")));
//console.debug("blob = ",state.blob);
}
const buttons = D.addClass(D.div(), 'buttons');
D.append(w, buttons);
D.append(buttons, D.button("Discard message?", function(){
const theMsg = findMessageWidgetParent(w);
if(theMsg) Chat.deleteMessageElem(theMsg);
}));
D.append(buttons, D.button("Edit message and try again?", function(){
if(state.msg) Chat.inputValue(state.msg);
if(state.blob) BlobXferState.updateDropZoneContent(state.blob);
const theMsg = findMessageWidgetParent(w);
if(theMsg) Chat.deleteMessageElem(theMsg);
}));
| > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
const w = D.addClass(D.div(), 'failed-message');
D.append(w, D.append(
D.span(),"This message was not successfully sent to the server:"
));
if(state.msg){
const ta = D.textarea();
ta.value = state.msg;
ta.setAttribute('readonly','true');
D.append(w,ta);
}
if(state.blob){
D.append(w,D.append(D.span(),"Attachment: ",(state.blob.name||"unnamed")));
//console.debug("blob = ",state.blob);
}
const buttons = D.addClass(D.div(), 'buttons');
D.append(w, buttons);
D.append(buttons, D.button("Discard message?", function(){
const theMsg = findMessageWidgetParent(w);
if(theMsg) Chat.deleteMessageElem(theMsg);
}));
D.append(buttons, D.button("Edit message and try again?", function(){
if(state.msg) Chat.inputValue(state.msg);
if(state.blob) BlobXferState.updateDropZoneContent(state.blob);
const theMsg = findMessageWidgetParent(w);
if(theMsg) Chat.deleteMessageElem(theMsg);
}));
D.addClass(Chat.reportErrorAsMessage(w).e.body, "resend-message");
};
/* Assume the connection has been established, reset the
Chat.timer.tidClearPollErr, and (if showMsg and
!!Chat.e.eMsgPollError) alert the user that the outage appears to
be over. Also schedule Chat.poll() to run in the very near
future. */
const reportConnectionOkay = function(dbgContext, showMsg = true){
if(Chat.beVerbose){
console.warn('reportConnectionOkay', dbgContext,
'Chat.e.pollErrorMarker classes =',
Chat.e.pollErrorMarker.classList,
'Chat.timer.tidClearPollErr =',Chat.timer.tidClearPollErr,
'Chat.timer =',Chat.timer);
}
if( Chat.timer.errCount ){
D.removeClass(Chat.e.pollErrorMarker, 'connection-error');
Chat.timer.errCount = 0;
}
Chat.timer.cancelReconnectCheckTimer().startPendingPollTimer();
if( Chat.e.eMsgPollError ) {
const oldErrMsg = Chat.e.eMsgPollError;
Chat.e.eMsgPollError = undefined;
if( showMsg ){
if(Chat.beVerbose){
console.log("Poller Connection restored.");
}
const m = Chat.reportReconnection("Poller connection restored.");
if( oldErrMsg ){
D.remove(oldErrMsg.e?.body.querySelector('button.retry-now'));
}
m.e.body.dataset.alsoRemove = oldErrMsg?.e?.body?.dataset?.msgid;
D.addClass(m.e.body,'poller-connection');
}
}
};
/**
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.
|
| ︙ | ︙ | |||
1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 |
payload: fd,
responseType: 'text',
onerror:function(err){
self.reportErrorAsMessage(err);
recoverFailedMessage(fallback);
},
onload:function(txt){
if(!txt) return/*success response*/;
try{
const json = JSON.parse(txt);
self.newContent({msgs:[json]});
}catch(e){
self.reportError(e);
}
| > | 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 |
payload: fd,
responseType: 'text',
onerror:function(err){
self.reportErrorAsMessage(err);
recoverFailedMessage(fallback);
},
onload:function(txt){
reportConnectionOkay('chat-send');
if(!txt) return/*success response*/;
try{
const json = JSON.parse(txt);
self.newContent({msgs:[json]});
}catch(e){
self.reportError(e);
}
|
| ︙ | ︙ | |||
2124 2125 2126 2127 2128 2129 2130 |
const v = Chat.inputValue();
Chat.inputValue('');
Chat.e.inputFields.$currentIndex = a[2];
Chat.inputValue(v);
D.removeClass(a[0], 'hidden');
D.addClass(a[1], 'hidden');
}
| | | 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 |
const v = Chat.inputValue();
Chat.inputValue('');
Chat.e.inputFields.$currentIndex = a[2];
Chat.inputValue(v);
D.removeClass(a[0], 'hidden');
D.addClass(a[1], 'hidden');
}
Chat.e.inputLineWrapper.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)=>{
|
| ︙ | ︙ | |||
2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 |
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!'
| > | 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 |
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){
reportConnectionOkay('ajax/preview-text');
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!'
|
| ︙ | ︙ | |||
2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 |
},
responseType: 'json',
onerror:function(err){
Chat.reportErrorAsMessage(err);
Chat._isBatchLoading = false;
},
onload:function(x){
let gotMessages = x.msgs.length;
newcontent(x,true);
Chat._isBatchLoading = false;
Chat.updateActiveUserList();
if(Chat._gotServerError){
Chat._gotServerError = false;
return;
| > | 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 |
},
responseType: 'json',
onerror:function(err){
Chat.reportErrorAsMessage(err);
Chat._isBatchLoading = false;
},
onload:function(x){
reportConnectionOkay('loadOldMessages()');
let gotMessages = x.msgs.length;
newcontent(x,true);
Chat._isBatchLoading = false;
Chat.updateActiveUserList();
if(Chat._gotServerError){
Chat._gotServerError = false;
return;
|
| ︙ | ︙ | |||
2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 |
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,
| > | 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 |
payload: fd,
responseType: 'json',
onerror:function(err){
Chat.setCurrentView(Chat.e.viewMessages);
Chat.reportErrorAsMessage(err);
},
onload:function(jx){
reportConnectionOkay('submitSearch()');
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,
|
| ︙ | ︙ | |||
2442 2443 2444 2445 2446 2447 2448 |
term );
}
}
}
);
}/*Chat.submitSearch()*/;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | < | > > | > | > | > | > > > > | < > > > > > > > | < | < < < > > > > > > > > > > > > | < > | < > > > > > > > > > | > > > > > > > > > > > | > > | > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > | > > | < | < > | | | | 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 |
term );
}
}
}
);
}/*Chat.submitSearch()*/;
/*
To be called from F.fetch('chat-poll') beforesend() handler. If
we're currently in delayed-retry mode and a connection is
started, try to reset the delay after N time waiting on that
connection. The fact that the connection is waiting to respond,
rather than outright failing, is a good hint that the outage is
over and we can reset the back-off timer.
Without this, recovery of a connection error won't be reported
until after the long-poll completes by either receiving new
messages or timing out. Once a long-poll is in progress, though,
we "know" that it's up and running again, so can update the UI and
connection timer to reflect that. That's the job this function
does.
Only one of these asynchronous checks will ever be active
concurrently and only if Chat.timer.isDelayed() is true. i.e. if
this timer is active or Chat.timer.isDelayed() is false, this is a
no-op.
*/
const chatPollBeforeSend = function(){
//console.warn('chatPollBeforeSend outer', Chat.timer.tidClearPollErr, Chat.timer.currentDelay);
if( !Chat.timer.tidClearPollErr && Chat.timer.isDelayed() ){
Chat.timer.tidClearPollErr = setTimeout(()=>{
//console.warn('chatPollBeforeSend inner');
Chat.timer.tidClearPollErr = 0;
if( poll.running ){
/* This chat-poll F.fetch() is still underway, so let's
assume the connection is back up until/unless it times
out or breaks again. */
reportConnectionOkay('chatPollBeforeSend', true);
}
}, Chat.timer.$initialDelay * 4/*kinda arbitrary: not too long for UI wait and
not too short as to make connection unlikely. */ );
}
};
/**
Deal with the last poll() response and maybe re-start poll().
*/
const afterPollFetch = function f(err){
if(true===f.isFirstCall){
f.isFirstCall = false;
Chat.ajaxEnd();
Chat.e.viewMessages.classList.remove('loading');
setTimeout(function(){
Chat.scrollMessagesTo(1);
}, 250);
}
Chat.timer.cancelPendingPollTimer();
if(Chat._gotServerError){
Chat.reportErrorAsMessage(
"Shutting down chat poller due to server-side error. ",
"Reload this page to reactivate it."
);
} else {
if( err && Chat.beVerbose ){
console.error("afterPollFetch:",err.name,err.status,err.message);
}
if( !err || 'timeout'===err.name/*(probably) long-poll expired*/ ){
/* Restart the poller immediately. */
reportConnectionOkay('afterPollFetch '+err, false);
}else{
/* Delay a while before trying again, noting that other Chat
APIs may try and succeed at connections before this timer
resolves, in which case they'll clear this timeout and the
UI message about the outage. */
let delay;
D.addClass(Chat.e.pollErrorMarker, 'connection-error');
if( ++Chat.timer.errCount < Chat.timer.minErrForNotify ){
delay = Chat.timer.resetDelay(
(Chat.timer.minDelay * Chat.timer.errCount)
+ Chat.timer.randomInterval(Chat.timer.minDelay)
);
if(Chat.beVerbose){
console.warn("Ignoring polling error #",Chat.timer.errCount,
"for another",delay,"ms" );
}
} else {
delay = Chat.timer.incrDelay();
//console.warn("afterPollFetch Chat.e.eMsgPollError",Chat.e.eMsgPollError);
const msg = "Poller connection error. Retrying in "+delay+ " ms.";
/* Replace the current/newest connection error widget. We could also
just update its body with the new message, but then its timestamp
never updates. OTOH, if we replace the message, we lose the
start time of the outage in the log. It seems more useful to
update the timestamp so that it doesn't look like it's hung. */
if( Chat.e.eMsgPollError ){
Chat.deleteMessageElem(Chat.e.eMsgPollError, false);
}
const theMsg = Chat.e.eMsgPollError = Chat.reportErrorAsMessage(msg);
D.addClass(Chat.e.eMsgPollError.e.body,'poller-connection');
/* Add a "retry now" button */
const btnDel = D.addClass(D.button("Retry now"), 'retry-now');
const eParent = Chat.e.eMsgPollError.e.content;
D.append(eParent, " ", btnDel);
btnDel.addEventListener('click', function(){
D.remove(btnDel);
D.append(eParent, D.text("retrying..."));
Chat.timer.cancelPendingPollTimer().currentDelay =
Chat.timer.resetDelay() +
1 /*workaround for showing the "connection restored"
message, as the +1 will cause
Chat.timer.isDelayed() to be true.*/;
poll();
});
//Chat.playNewMessageSound();// browser complains b/c this wasn't via human interaction
}
Chat.timer.startPendingPollTimer(delay);
}
}
};
afterPollFetch.isFirstCall = true;
/**
Initiates, if it's not already running, a single long-poll
request to the /chat-poll endpoint. In the handling of that
response, it end up will psuedo-recursively calling itself via
the response-handling process. Despite being async, the implied
returned Promise is meaningless.
*/
const poll = Chat.poll = async function f(){
if(f.running) return;
f.running = true;
Chat._isBatchLoading = f.isFirstCall;
if(true===f.isFirstCall){
f.isFirstCall = false;
f.pendingOnError = undefined;
Chat.ajaxStart();
Chat.e.viewMessages.classList.add('loading');
/*
We manager onerror() results in poll() in a roundabout
manner: when an onerror() arrives, we stash it aside
for a moment before processing it.
This level of indirection is necessary to be able to
unambiguously identify client-timeout-specific polling errors
from other errors. Timeouts are always announced in pairs of
an HTTP 0 and something we can unambiguously identify as a
timeout (in that order). When we receive an HTTP error we put
it into this queue. If an ontimeout() call arrives before
this error is handled, this error is ignored. If, however, an
HTTP error is seen without an accompanying timeout, we handle
it from here.
It's kinda like in the curses C API, where you to match
ALT-X by first getting an ESC event, then an X event, but
this one is a lot less explicable. (It's almost certainly a
mis-handling bug in F.fetch(), but it has so far eluded my
eyes.)
*/
f.delayPendingOnError = function(err){
if( f.pendingOnError ){
const x = f.pendingOnError;
f.pendingOnError = undefined;
afterPollFetch(x);
}
};
}
F.fetch("chat-poll",{
timeout: Chat.timer.pollTimeout,
urlParams:{
name: Chat.mxMsg
},
responseType: "json",
// Disable the ajax start/end handling for this long-polling op:
beforesend: chatPollBeforeSend,
aftersend: function(){
poll.running = false;
},
ontimeout: function(err){
f.pendingOnError = undefined /*strip preceeding non-timeout error, if any*/;
afterPollFetch(err);
},
onerror:function(err){
Chat._isBatchLoading = false;
if(Chat.beVerbose){
console.error("poll.onerror:",err.name,err.status,JSON.stringify(err));
}
f.pendingOnError = err;
setTimeout(f.delayPendingOnError, 100);
},
onload:function(y){
reportConnectionOkay('poll.onload', true);
newcontent(y);
if(Chat._isBatchLoading){
Chat._isBatchLoading = false;
Chat.updateActiveUserList();
}
afterPollFetch();
}
});
}/*poll()*/;
poll.isFirstCall = true;
Chat._gotServerError = poll.running = false;
if( window.fossil.config.chat.fromcli ){
Chat.chatOnlyMode(true);
}
Chat.timer.startPendingPollTimer();
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.forumpost.js.
| ︙ | ︙ | |||
115 116 117 118 119 120 121 |
browser's cancel button while waiting, they'll be stuck with
an unsubmittable form. */
setTimeout(()=>{delete form.dataset.submitted}, 7000);
return;
};
document.querySelectorAll("form").forEach(function(form){
form.addEventListener('submit',formSubmitted);
| > > > > > > > > | > > > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
browser's cancel button while waiting, they'll be stuck with
an unsubmittable form. */
setTimeout(()=>{delete form.dataset.submitted}, 7000);
return;
};
document.querySelectorAll("form").forEach(function(form){
form.addEventListener('submit',formSubmitted);
form
.querySelectorAll("input.action-close, input.action-reopen")
.forEach(function(e){
F.confirmer(e, {
confirmText: (e.classList.contains('action-reopen')
? "Confirm re-open"
: "Confirm close"),
onconfirm: ()=>form.submit()
});
});
});
})/*F.onPageLoad callback*/;
})(window.fossil);
|
Changes to src/fossil.page.pikchrshowasm.js.
| ︙ | ︙ | |||
118 119 120 121 122 123 124 |
if(PS._config.hasOwnProperty(k)){
PS.config[k] = PS._config[k];
}
});
delete PS._config;
}
| > > > | > > | 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
if(PS._config.hasOwnProperty(k)){
PS.config[k] = PS._config[k];
}
});
delete PS._config;
}
/* Randomize the name of the worker script so that it is never cached.
** The Fossil /builtin method will automatically remove the "-v000000000"
** part of the filename, resolving it to just "pikchr-worker.js". */
PS.worker = new Worker('builtin/extsrc/pikchr-worker-v'+
(Math.floor(Math.random()*10000000000) + 1000000000)+
'.js');
PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data);
PS.addMsgHandler('stdout', console.log.bind(console));
PS.addMsgHandler('stderr', console.error.bind(console));
/** Handles status updates from the Module object. */
PS.addMsgHandler('module', function f(ev){
ev = ev.data;
|
| ︙ | ︙ | |||
170 171 172 173 174 175 176 |
PS.e.previewModeLabel.innerText =
PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]];
/**
The 'pikchr-ready' event is fired (with no payload) when the
wasm module has finished loading. */
| | | | | 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 |
PS.e.previewModeLabel.innerText =
PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]];
/**
The 'pikchr-ready' event is fired (with no payload) when the
wasm module has finished loading. */
PS.addMsgHandler('pikchr-ready', function(event){
PS.clearMsgHandlers('pikchr-ready');
F.page.onPikchrshowLoaded(event.data);
});
/**
Performs all app initialization which must wait until after the
worker module is loaded. This function removes itself when it's
called.
*/
F.page.onPikchrshowLoaded = function(pikchrVersion){
delete this.onPikchrshowLoaded;
// Unhide all elements which start out hidden
EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden'));
const taInput = E('#input');
const btnClearIn = E('#btn-clear');
btnClearIn.addEventListener('click',function(){
taInput.value = '';
|
| ︙ | ︙ | |||
437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
link in the forum. */
const src = window.sessionStorage.getItem('pikchr-xfer');
if( src && (new URL(self.location.href).searchParams).has('fromSession') ){
taInput.value = src;
window.sessionStorage.removeItem('pikchr-xfer');
}
}
PS.e.btnRender.click();
/** Debounce handler for auto-rendering while typing. */
const debounceAutoRender = F.debounce(function f(){
if(!PS._isDirty) return;
const text = getCurrentText();
| > > > | 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 |
link in the forum. */
const src = window.sessionStorage.getItem('pikchr-xfer');
if( src && (new URL(self.location.href).searchParams).has('fromSession') ){
taInput.value = src;
window.sessionStorage.removeItem('pikchr-xfer');
}
}
D.append(E('fieldset.options > div'),
D.append(D.addClass(D.span(), 'labeled-input'),
'pikchr v. '+pikchrVersion));
PS.e.btnRender.click();
/** Debounce handler for auto-rendering while typing. */
const debounceAutoRender = F.debounce(function f(){
if(!PS._isDirty) return;
const text = getCurrentText();
|
| ︙ | ︙ | |||
522 523 524 525 526 527 528 |
delete ForceResizeKludge.$disabled;
ForceResizeKludge();
}/*onPikchrshowLoaded()*/;
/**
| | | 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 |
delete ForceResizeKludge.$disabled;
ForceResizeKludge();
}/*onPikchrshowLoaded()*/;
/**
Predefined example pikchr scripts. Each entry is an object:
{
name: required string,
code: optional code string. An entry with a falsy code is treated
like a separator in the resulting SELECT element (a
disabled OPTION).
}
|
| ︙ | ︙ |
Changes to src/fossil.page.wikiedit.js.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
- Event 'wiki-page-loaded': passes on information when it
loads a wiki (whether from the network or its internal local-edit
cache), in the form of an "winfo" object:
{
name: string,
mimetype: mimetype string,
| | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
- Event 'wiki-page-loaded': passes on information when it
loads a wiki (whether from the network or its internal local-edit
cache), in the form of an "winfo" object:
{
name: string,
mimetype: mimetype string,
type: "normal" | "tag" | "checkin" | "branch" | "ticket" | "sandbox",
version: UUID string or null for a sandbox page or new page,
parent: parent UUID string or null if no parent,
isEmpty: true if page has no content (is "deleted").
content: string, optional in most contexts
}
The internal docs and code frequently use the term "winfo", and such
|
| ︙ | ︙ | |||
190 191 192 193 194 195 196 |
old = ndx[key];
const record = old || (ndx[key]={
name: winfo.name
});
record.mimetype = winfo.mimetype;
record.type = winfo.type;
record.parent = winfo.parent;
| | | | 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 |
old = ndx[key];
const record = old || (ndx[key]={
name: winfo.name
});
record.mimetype = winfo.mimetype;
record.type = winfo.type;
record.parent = winfo.parent;
record.version = winfo.version;
record.stashTime = new Date().getTime();
record.isEmpty = !!winfo.isEmpty;
record.attachments = winfo.attachments;
this.storeIndex();
if(arguments.length>1){
if(content) delete record.isEmpty;
F.storage.set(this.contentKey(key), content);
}
this._fireStashEvent();
return this;
},
/**
Returns the stashed content, if any, for the given winfo
object.
*/
stashedContent: function(winfo){
return F.storage.get(this.contentKey(this.indexKey(winfo)));
},
/** Returns true if we have stashed content for the given winfo
record or page name. */
hasStashedContent: function(winfo){
if('string'===typeof winfo) winfo = {name: winfo};
|
| ︙ | ︙ | |||
268 269 270 271 272 273 274 |
console.warn("Pruned oldest local file edit entry:",e);
}
if(n) this._fireStashEvent();
}
};
$stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10;
P.$stash = $stash /* we have to expose this for the new-page case :/ */;
| | | 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
console.warn("Pruned oldest local file edit entry:",e);
}
if(n) this._fireStashEvent();
}
};
$stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10;
P.$stash = $stash /* we have to expose this for the new-page case :/ */;
/**
Internal workaround to select the current preview mode
and fire a change event if the value actually changes
or if forceEvent is truthy.
*/
P.selectMimetype = function(modeValue, forceEvent){
const s = this.e.selectMimetype;
|
| ︙ | ︙ | |||
534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
*/
addNewPage: function(name){
name = name.trim();
if(!this.validatePageName(name)) return false;
var wtype = 'normal';
if(0===name.indexOf('checkin/')) wtype = 'checkin';
else if(0===name.indexOf('branch/')) wtype = 'branch';
else if(0===name.indexOf('tag/')) wtype = 'tag';
/* ^^^ note that we're not validating that, e.g., checkin/XYZ
has a full artifact ID after "checkin/". */
const winfo = {
name: name, type: wtype, mimetype: 'text/x-markdown',
version: null, parent: null
};
| > | 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
*/
addNewPage: function(name){
name = name.trim();
if(!this.validatePageName(name)) return false;
var wtype = 'normal';
if(0===name.indexOf('checkin/')) wtype = 'checkin';
else if(0===name.indexOf('branch/')) wtype = 'branch';
else if(0===name.indexOf('ticket/')) wtype = 'ticket';
else if(0===name.indexOf('tag/')) wtype = 'tag';
/* ^^^ note that we're not validating that, e.g., checkin/XYZ
has a full artifact ID after "checkin/". */
const winfo = {
name: name, type: wtype, mimetype: 'text/x-markdown',
version: null, parent: null
};
|
| ︙ | ︙ | |||
571 572 573 574 575 576 577 |
D.attr(sel, 'size', 12);
D.option(D.disable(D.clearElement(sel)), undefined, "Loading...");
/** Set up filter checkboxes for the various types
of wiki pages... */
const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"),
fsFilterBody = D.div(),
| | | 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 |
D.attr(sel, 'size', 12);
D.option(D.disable(D.clearElement(sel)), undefined, "Loading...");
/** Set up filter checkboxes for the various types
of wiki pages... */
const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"),
fsFilterBody = D.div(),
filters = ['normal', 'branch/...', 'tag/...', 'checkin/...', 'ticket/...']
;
D.append(fsFilter, fsFilterBody);
D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch');
// Add filters by page type...
const self = this;
const filterByType = function(wtype, show){
|
| ︙ | ︙ | |||
1043 1044 1045 1046 1047 1048 1049 |
});
}else{
P.e.btnSave.addEventListener('click', ()=>doSave(), false);
P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false);
}
P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false);
| | | | 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 |
});
}else{
P.e.btnSave.addEventListener('click', ()=>doSave(), false);
P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false);
}
P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false);
P.selectMimetype(false, true);
P.e.selectMimetype.addEventListener(
'change',
function(e){
if(P.winfo && P.winfo.mimetype !== e.target.value){
P.winfo.mimetype = e.target.value;
P._isDirty = true;
P.stashContentChange(true);
}
},
false
);
const selectFontSize = E('select[name=editor_font_size]');
if(selectFontSize){
selectFontSize.addEventListener(
"change",function(e){
const ed = P.e.taEditor;
ed.className = ed.className.replace(
/\bfont-size-\d+/g, '' );
|
| ︙ | ︙ | |||
1588 1589 1590 1591 1592 1593 1594 |
).fetch('wikiajax/save',{
payload: fd,
responseType: 'json',
onload: callee.onload
});
return this;
};
| | | 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 |
).fetch('wikiajax/save',{
payload: fd,
responseType: 'json',
onload: callee.onload
});
return this;
};
/**
Updates P.winfo for certain state and stashes P.winfo, with the
current content fetched via P.wikiContent().
If passed truthy AND the stash already has stashed content for
the current page, only the stashed winfo record is updated, else
both the winfo and content are updated.
|
| ︙ | ︙ |
Changes to src/fossil.popupwidget.js.
| ︙ | ︙ | |||
284 285 286 287 288 289 290 |
);
return F.toast;
};
F.toast = {
config: {
position: { x: 5, y: 5 /*viewport-relative, pixels*/ },
| | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
);
return F.toast;
};
F.toast = {
config: {
position: { x: 5, y: 5 /*viewport-relative, pixels*/ },
displayTimeMs: 5000
},
/**
Convenience wrapper around a PopupWidget which pops up a shared
PopupWidget instance to show toast-style messages (commonly
seen on Android). Its arguments may be anything suitable for
passing to fossil.dom.append(), and each argument is first
append()ed to the toast widget, then the widget is shown for
|
| ︙ | ︙ |
Changes to src/fuzz.c.
| ︙ | ︙ | |||
58 59 60 61 62 63 64 65 66 67 68 69 70 71 | /* ** Type of fuzzing: */ #define FUZZ_WIKI 0 /* The Fossil-Wiki formatter */ #define FUZZ_MARKDOWN 1 /* The Markdown formatter */ #define FUZZ_ARTIFACT 2 /* Fuzz the artifact parser */ #define FUZZ_WIKI2 3 /* FOSSIL_WIKI and FOSSIL_MARKDOWN */ #endif /* The type of fuzzing to do */ static int eFuzzType = FUZZ_WIKI; /* The fuzzer invokes this routine once for each fuzzer input */ | > | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | /* ** Type of fuzzing: */ #define FUZZ_WIKI 0 /* The Fossil-Wiki formatter */ #define FUZZ_MARKDOWN 1 /* The Markdown formatter */ #define FUZZ_ARTIFACT 2 /* Fuzz the artifact parser */ #define FUZZ_WIKI2 3 /* FOSSIL_WIKI and FOSSIL_MARKDOWN */ #define FUZZ_COMFORMAT 4 /* comment_print() */ #endif /* The type of fuzzing to do */ static int eFuzzType = FUZZ_WIKI; /* The fuzzer invokes this routine once for each fuzzer input */ |
| ︙ | ︙ | |||
91 92 93 94 95 96 97 |
Blob title = BLOB_INITIALIZER;
wiki_convert(&in, &out, 0);
blob_reset(&out);
markdown_to_html(&in, &title, &out);
blob_reset(&title);
break;
}
| | > > > > > > > > > | 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 |
Blob title = BLOB_INITIALIZER;
wiki_convert(&in, &out, 0);
blob_reset(&out);
markdown_to_html(&in, &title, &out);
blob_reset(&title);
break;
}
case FUZZ_ARTIFACT: {
fossil_fatal("FUZZ_ARTIFACT is not implemented.");
break;
}
case FUZZ_COMFORMAT: {
if( nByte>=3 && aData[1]!=0 && memchr(&aData[1], 0, nByte-1)!=0 ){
int flags = (int)aData[0];
comment_print((const char*)&aData[1],0,15,80,flags);
}
}
}
blob_reset(&in);
blob_reset(&out);
return 0;
}
/*
** Check fuzzer command-line options.
*/
static void fuzzer_options(void){
const char *zType;
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
db_multi_exec("PRAGMA query_only=1;");
zType = find_option("fuzztype",0,1);
if( zType==0 || fossil_strcmp(zType,"wiki")==0 ){
eFuzzType = FUZZ_WIKI;
}else if( fossil_strcmp(zType,"markdown")==0 ){
eFuzzType = FUZZ_MARKDOWN;
}else if( fossil_strcmp(zType,"wiki2")==0 ){
eFuzzType = FUZZ_WIKI2;
}else if( fossil_strcmp(zType,"comformat")==0 ){
eFuzzType = FUZZ_COMFORMAT;
}else{
fossil_fatal("unknown fuzz type: \"%s\"", zType);
}
}
/* Libfuzzer invokes this routine once prior to start-up to
** process command-line options.
|
| ︙ | ︙ | |||
137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
/*
** COMMAND: test-fuzz
**
** Usage: %fossil test-fuzz [-fuzztype TYPE] INPUTFILE...
**
** Run a fuzz test using INPUTFILE as the test data. TYPE can be one of:
**
** wiki Fuzz the Fossil-wiki translator
** markdown Fuzz the markdown translator
** artifact Fuzz the artifact parser
** wiki2 Fuzz the Fossil-wiki and markdown translator
*/
void fuzz_command(void){
Blob in;
| > | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
/*
** COMMAND: test-fuzz
**
** Usage: %fossil test-fuzz [-fuzztype TYPE] INPUTFILE...
**
** Run a fuzz test using INPUTFILE as the test data. TYPE can be one of:
**
** comformat Fuzz the comment_print() routine
** wiki Fuzz the Fossil-wiki translator
** markdown Fuzz the markdown translator
** artifact Fuzz the artifact parser
** wiki2 Fuzz the Fossil-wiki and markdown translator
*/
void fuzz_command(void){
Blob in;
|
| ︙ | ︙ |
Changes to src/graph.c.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 | ** the identifier is a combination of the BLOB.RID and the FILENAME.FNID ** values, and so it can become quite large for repos that have both many ** check-ins and many files. For this reason, we make the identifier ** a 64-bit integer, to dramatically reduce the risk of an overflow. */ typedef sqlite3_int64 GraphRowId; | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | ** the identifier is a combination of the BLOB.RID and the FILENAME.FNID ** values, and so it can become quite large for repos that have both many ** check-ins and many files. For this reason, we make the identifier ** a 64-bit integer, to dramatically reduce the risk of an overflow. */ typedef sqlite3_int64 GraphRowId; #define GR_MAX_RAIL 64 /* Max number of "rails" to display */ /* The graph appears vertically beside a timeline. Each row in the ** timeline corresponds to a row in the graph. GraphRow.idx is 0 for ** the top-most row and increases moving down. Hence (in the absence of ** time skew) parents have a larger index than their children. ** ** The nParent field is -1 for entires that do not participate in the graph |
| ︙ | ︙ | |||
82 83 84 85 86 87 88 | char *zBgClr; /* Background Color */ char zUuid[HNAME_MAX+1]; /* Check-in for file ID */ GraphRow *pNext; /* Next row down in the list of all rows */ GraphRow *pPrev; /* Previous row */ int idx; /* Row index. Top row is smallest. */ | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
char *zBgClr; /* Background Color */
char zUuid[HNAME_MAX+1]; /* Check-in for file ID */
GraphRow *pNext; /* Next row down in the list of all rows */
GraphRow *pPrev; /* Previous row */
int idx; /* Row index. Top row is smallest. */
int idxTop; /* Direct descendant highest up on the graph */
GraphRow *pChild; /* Child immediately above this node */
u8 isDup; /* True if this is duplicate of a prior entry */
u8 isLeaf; /* True if this is a leaf node */
u8 isStepParent; /* pChild is actually a step-child. The thick
** arrow up to the child is dashed, not solid */
u8 hasNormalOutMerge; /* Is parent of at laest 1 non-cherrypick merge */
u8 timeWarp; /* Child is earlier in time */
|
| ︙ | ︙ | |||
116 117 118 119 120 121 122 123 124 |
GraphRow *pLast; /* Last row in the list. Bottom row of graph. */
int nBranch; /* Number of distinct branches */
char **azBranch; /* Names of the branches */
int nRow; /* Number of rows */
int nHash; /* Number of slots in apHash[] */
u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different
** rail that the node */
u64 mergeRail; /* Rails used for merge lines */
GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */
| > | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
GraphRow *pLast; /* Last row in the list. Bottom row of graph. */
int nBranch; /* Number of distinct branches */
char **azBranch; /* Names of the branches */
int nRow; /* Number of rows */
int nHash; /* Number of slots in apHash[] */
u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different
** rail that the node */
u8 bOverfull; /* Unable to allocate sufficient rails */
u64 mergeRail; /* Rails used for merge lines */
GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */
u8 aiRailMap[GR_MAX_RAIL+1]; /* Mapping of rails to actually columns */
};
#endif
/* The N-th bit */
#define BIT(N) (((u64)1)<<(N))
|
| ︙ | ︙ | |||
334 335 336 337 338 339 340 |
if( dist<0 ) dist = -dist;
if( dist<iBestDist ){
iBestDist = dist;
iBest = i;
}
}
}
| | > > > > > > > | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
if( dist<0 ) dist = -dist;
if( dist<iBestDist ){
iBestDist = dist;
iBest = i;
}
}
}
if( iBestDist>1000 ){
p->bOverfull = 1;
iBest = GR_MAX_RAIL;
}
if( iBest>GR_MAX_RAIL ){
p->bOverfull = 1;
iBest = GR_MAX_RAIL;
}
if( iBest>p->mxRail ) p->mxRail = iBest;
if( bMergeRail ) p->mergeRail |= BIT(iBest);
return iBest;
}
/*
** Assign all children of node pBottom to the same rail as pBottom.
|
| ︙ | ︙ | |||
493 494 495 496 497 498 499 | ** The tmFlags parameter is zero or more of the TIMELINE_* constants. ** Only the following are honored: ** ** TIMELINE_DISJOINT: Omit descenders ** TIMELINE_FILLGAPS: Use step-children ** TIMELINE_XMERGE: Omit off-graph merge lines */ | | > > > > | 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 |
** The tmFlags parameter is zero or more of the TIMELINE_* constants.
** Only the following are honored:
**
** TIMELINE_DISJOINT: Omit descenders
** TIMELINE_FILLGAPS: Use step-children
** TIMELINE_XMERGE: Omit off-graph merge lines
*/
void graph_finish(
GraphContext *p, /* The graph to be laid out */
Matcher *pLeftBranch, /* Compares true for left-most branch */
u32 tmFlags /* TIMELINE flags */
){
GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent;
int i, j;
u64 mask;
int hasDup = 0; /* True if one or more isDup entries */
const char *zTrunk;
u8 *aMap; /* Copy of p->aiRailMap */
int omitDescenders = (tmFlags & TIMELINE_DISJOINT)!=0;
|
| ︙ | ︙ | |||
703 704 705 706 707 708 709 |
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
if( i==0 && pRow->zBranch!=zTrunk ) continue;
if( pRow->iRail>=0 ) continue;
if( pRow->isDup ) continue;
if( pRow->nParent<0 ) continue;
if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0);
| | | 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 |
for(pRow=p->pLast; pRow; pRow=pRow->pPrev){
if( i==0 && pRow->zBranch!=zTrunk ) continue;
if( pRow->iRail>=0 ) continue;
if( pRow->isDup ) continue;
if( pRow->nParent<0 ) continue;
if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0);
/* if( p->mxRail>=GR_MAX_RAIL ) return; */
mask = BIT(pRow->iRail);
if( !omitDescenders ){
int n = RISER_MARGIN;
pRow->bDescender = pRow->nParent>0;
for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
pLoop->railInUse |= mask;
}
|
| ︙ | ︙ | |||
738 739 740 741 742 743 744 |
continue;
}else{
assert( pRow->nParent>0 );
parentRid = pRow->aParent[0];
pParent = hashFind(p, parentRid);
if( pParent==0 ){
pRow->iRail = ++p->mxRail;
| | > > > | > > > > | > > > | 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 |
continue;
}else{
assert( pRow->nParent>0 );
parentRid = pRow->aParent[0];
pParent = hashFind(p, parentRid);
if( pParent==0 ){
pRow->iRail = ++p->mxRail;
if( p->mxRail>=GR_MAX_RAIL ){
pRow->iRail = p->mxRail = GR_MAX_RAIL;
p->bOverfull = 1;
}
pRow->railInUse = BIT(pRow->iRail);
continue;
}
if( pParent->idx>pRow->idx ){
/* Common case: Child occurs after parent and is above the
** parent in the timeline */
pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx,
pParent->iRail, 0);
/* if( p->mxRail>=GR_MAX_RAIL ) return; */
pParent->aiRiser[pRow->iRail] = pRow->idx;
}else{
/* Timewarp case: Child occurs earlier in time than parent and
** appears below the parent in the timeline. */
int iDownRail = ++p->mxRail;
if( iDownRail<1 ) iDownRail = ++p->mxRail;
if( p->mxRail>GR_MAX_RAIL ){
iDownRail = p->mxRail = GR_MAX_RAIL;
p->bOverfull = 1;
}
pRow->iRail = ++p->mxRail;
if( p->mxRail>=GR_MAX_RAIL ){
pRow->iRail = p->mxRail = GR_MAX_RAIL;
p->bOverfull = 1;
}
pRow->railInUse = BIT(pRow->iRail);
pParent->aiRiser[iDownRail] = pRow->idx;
mask = BIT(iDownRail);
for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
pLoop->railInUse |= mask;
}
}
|
| ︙ | ︙ | |||
818 819 820 821 822 823 824 |
if( mergeRiserFrom[j]==parentRid ){
iMrail = j;
break;
}
}
if( iMrail==-1 ){
iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1);
| | | 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 |
if( mergeRiserFrom[j]==parentRid ){
iMrail = j;
break;
}
}
if( iMrail==-1 ){
iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1);
/*if( p->mxRail>=GR_MAX_RAIL ) return;*/
mergeRiserFrom[iMrail] = parentRid;
}
iReuseIdx = p->nRow+1;
iReuseRail = iMrail;
mask = BIT(iMrail);
if( i>=pRow->nNonCherrypick ){
pRow->mergeIn[iMrail] = 2;
|
| ︙ | ︙ | |||
850 851 852 853 854 855 856 |
}else{
pDesc->hasNormalOutMerge = 1;
pDesc->mergeUpto = pDesc->idx;
}
}else{
/* Create a new merge for an on-screen node */
createMergeRiser(p, pDesc, pRow, isCherrypick);
| | | | | | 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 |
}else{
pDesc->hasNormalOutMerge = 1;
pDesc->mergeUpto = pDesc->idx;
}
}else{
/* Create a new merge for an on-screen node */
createMergeRiser(p, pDesc, pRow, isCherrypick);
/* if( p->mxRail>=GR_MAX_RAIL ) return; */
if( iReuseIdx<0
&& pDesc->nMergeChild==1
&& (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf)
){
iReuseIdx = pDesc->idx;
iReuseRail = pDesc->mergeOut;
}
}
}
}
}
/*
** Insert merge rails from primaries to duplicates.
*/
if( hasDup && p->mxRail<GR_MAX_RAIL ){
int dupRail;
int mxRail;
find_max_rail(p);
mxRail = p->mxRail;
dupRail = mxRail+1;
/* if( p->mxRail>=GR_MAX_RAIL ) return; */
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( !pRow->isDup ) continue;
pRow->iRail = dupRail;
pDesc = hashFind(p, pRow->rid);
assert( pDesc!=0 && pDesc!=pRow );
createMergeRiser(p, pDesc, pRow, 0);
if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut;
}
if( dupRail<=mxRail ){
dupRail = mxRail+1;
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->isDup ) pRow->iRail = dupRail;
}
}
/* if( mxRail>=GR_MAX_RAIL ) return; */
}
/*
** Find the maximum rail number.
*/
find_max_rail(p);
|
| ︙ | ︙ | |||
961 962 963 964 965 966 967 |
pRoot->aiRiser[iFrom] = -1;
}
}
}
/*
** Compute the rail mapping that tries to put the branch named
| | | > | > | | < | > > | > > | > > > > | > | > > | 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 |
pRoot->aiRiser[iFrom] = -1;
}
}
}
/*
** Compute the rail mapping that tries to put the branch named
** pLeftBranch at the left margin. Other branches that merge
** with pLeftBranch are to the right with merge rails in between.
**
** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail.
**
** Do not move rails around if there are timewarps, because that can
** seriously mess up the display of timewarps. Timewarps should be
** rare so this should not be a serious limitation to the algorithm.
*/
aMap = p->aiRailMap;
for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
if( nTimewarp==0 ){
int kk;
/* 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
*/
u16 aPriority[GR_MAX_RAIL];
int mxMatch = 0;
memset(aPriority, 0, (p->mxRail+1)*sizeof(aPriority[0]));
if( pLeftBranch ){
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
int iMatch = match_text(pLeftBranch, pRow->zBranch);
if( iMatch>0 ){
if( iMatch>10 ) iMatch = 10;
aPriority[pRow->iRail] |= 1<<(iMatch+1);
if( mxMatch<iMatch ) mxMatch = iMatch;
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;
mxMatch = 1;
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," %x.%x",
aPriority[i]/4, aPriority[i]&3);
}
fprintf(stderr,"\n");
#endif
j = 0;
for(kk=4; kk<=1<<(mxMatch+1); kk*=2){
for(i=0; i<=p->mxRail; i++){
if( aPriority[i]>=kk && aPriority[i]<kk*2 ){
aMap[i] = j++;
}
}
}
for(i=p->mxRail; i>=0; i--){
if( aPriority[i]==3 ) aMap[i] = j++;
}
for(i=0; i<=p->mxRail; i++){
if( aPriority[i]==1 || aPriority[i]==2 ) aMap[i] = j++;
}
|
| ︙ | ︙ |
Changes to src/graph.js.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* This module contains javascript needed to render timeline graphs in Fossil.
**
** There can be multiple graphs on a single webpage, but this script is only
** loaded once.
**
** Prior to sourcing this script, there should be a separate
** <script type='application/json' id='timeline-data-NN'> for each graph,
** each containing JSON like this:
**
** { "iTableId": INTEGER, // Table sequence number (NN)
** "circleNodes": BOOLEAN, // True for circle nodes. False for squares
** "showArrowheads": BOOLEAN, // True for arrowheads. False to omit
** "iRailPitch": INTEGER, // Spacing between vertical lines (px)
| < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* This module contains javascript needed to render timeline graphs in Fossil.
**
** There can be multiple graphs on a single webpage, but this script is only
** loaded once.
**
** Prior to sourcing this script, there should be a separate
** <script type='application/json' id='timeline-data-NN'> for each graph,
** each containing JSON like this:
**
** { "iTableId": INTEGER, // Table sequence number (NN)
** "circleNodes": BOOLEAN, // True for circle nodes. False for squares
** "showArrowheads": BOOLEAN, // True for arrowheads. False to omit
** "iRailPitch": INTEGER, // Spacing between vertical lines (px)
** "nomo": BOOLEAN, // True to join merge lines with rails
** "iTopRow": INTEGER, // Index of top-most row in the graph
** "omitDescenders": BOOLEAN, // Omit ancestor lines off bottom of screen
** "fileDiff": BOOLEAN, // True for file diff. False for check-in
** "scrollToSelect": BOOLEAN, // Scroll to selection on first render
** "nrail": INTEGER, // Number of vertical "rails"
** "baseUrl": TEXT, // Top-level URL
|
| ︙ | ︙ |
Changes to src/hook.c.
| ︙ | ︙ | |||
196 197 198 199 200 201 202 | /* ** COMMAND: hook* ** ** Usage: %fossil hook COMMAND ... ** ** Commands include: ** | | | | | | | | | 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 |
/*
** COMMAND: hook*
**
** Usage: %fossil hook COMMAND ...
**
** Commands include:
**
** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER
**
** Create a new hook. The --command and --type arguments are
** required. --sequence is optional.
**
** > fossil hook delete ID ...
**
** Delete one or more hooks by their IDs. ID can be "all"
** to delete all hooks. Caution: There is no "undo" for
** this operation. Deleted hooks are permanently lost.
**
** > fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ...
**
** Make changes to one or more existing hooks. The ID argument
** is either a hook-id, or a list of hook-ids, or the keyword
** "all". For example, to disable hook number 2, use:
**
** fossil hook edit --type disabled 2
**
** > fossil hook list
**
** Show all current hooks
**
** > fossil hook status
**
** Print the values of CONFIG table entries that are relevant to
** hook processing. Used for debugging.
**
** > fossil hook test [OPTIONS] ID
**
** Run the hook script given by ID for testing purposes.
** Options:
**
** --dry-run Print the script on stdout rather than run it
** --base-rcvid N Pretend that the hook-last-rcvid value is N
** --new-rcvid M Pretend that the last rcvid value is M
** --aux-file NAME NAME is substituted for %A in the script
**
** The --base-rcvid and --new-rcvid options are silently ignored if
** the hook type is not "after-receive". The default values for
** --base-rcvid and --new-rcvid cause the last receive to be processed.
*/
void hook_cmd(void){
|
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
50 51 52 53 54 55 56 | /* ** 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 | | > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
/*
** 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
** of all payload that follows the login card. Randomness for the NONCE
** must be provided in the payload (in xfer.c). SIGNATURE is the sha1
** checksum of the nonce followed by the user password.
**
** Write the constructed login card into pLogin. pLogin is initialized
** by this routine.
*/
static void http_build_login_card(Blob *pPayload, Blob *pLogin){
Blob nonce; /* The nonce */
|
| ︙ | ︙ | |||
510 511 512 513 514 515 516 517 518 519 520 521 522 523 |
blob_size(&hdr), blob_size(&payload));
}
transport_send(&g.url, &hdr);
transport_send(&g.url, &payload);
blob_reset(&hdr);
blob_reset(&payload);
transport_flip(&g.url);
/*
** Read and interpret the server reply
*/
closeConnection = 1;
iLength = -1;
iHttpVersion = -1;
| > > > | 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
blob_size(&hdr), blob_size(&payload));
}
transport_send(&g.url, &hdr);
transport_send(&g.url, &payload);
blob_reset(&hdr);
blob_reset(&payload);
transport_flip(&g.url);
if( mHttpFlags & HTTP_VERBOSE ){
fossil_print("IP-Address: %s\n", g.zIpAddr);
}
/*
** Read and interpret the server reply
*/
closeConnection = 1;
iLength = -1;
iHttpVersion = -1;
|
| ︙ | ︙ | |||
570 571 572 573 574 575 576 577 578 579 580 581 582 583 |
}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;
if ( --maxRedirect == 0){
fossil_warning("redirect limit exceeded");
goto write_err;
}
for(i=9; zLine[i] && zLine[i]==' '; i++){}
if( zLine[i]==0 ){
| > | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
}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;
int priorUrlFlags;
if ( --maxRedirect == 0){
fossil_warning("redirect limit exceeded");
goto write_err;
}
for(i=9; zLine[i] && zLine[i]==' '; i++){}
if( zLine[i]==0 ){
|
| ︙ | ︙ | |||
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 |
}
if( g.url.isFile || g.url.isSsh ){
fossil_warning("cannot redirect from %s to %s", g.url.canonical,
&zLine[i]);
goto write_err;
}
wasHttps = g.url.isHttps;
url_parse(&zLine[i], 0);
if( wasHttps && !g.url.isHttps ){
fossil_warning("cannot redirect from HTTPS to HTTP");
goto write_err;
}
if( g.url.isSsh || g.url.isFile ){
fossil_warning("cannot redirect to %s", &zLine[i]);
goto write_err;
}
transport_close(&g.url);
transport_global_shutdown(&g.url);
fSeenHttpAuth = 0;
if( g.zHttpAuth ) free(g.zHttpAuth);
g.zHttpAuth = get_httpauth();
| > > > | > | 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 |
}
if( g.url.isFile || g.url.isSsh ){
fossil_warning("cannot redirect from %s to %s", g.url.canonical,
&zLine[i]);
goto write_err;
}
wasHttps = g.url.isHttps;
priorUrlFlags = g.url.flags;
url_parse(&zLine[i], 0);
if( wasHttps && !g.url.isHttps ){
fossil_warning("cannot redirect from HTTPS to HTTP");
goto write_err;
}
if( g.url.isSsh || g.url.isFile ){
fossil_warning("cannot redirect to %s", &zLine[i]);
goto write_err;
}
transport_close(&g.url);
transport_global_shutdown(&g.url);
fSeenHttpAuth = 0;
if( g.zHttpAuth ) free(g.zHttpAuth);
g.zHttpAuth = get_httpauth();
if( (rc==301 || rc==308) && (priorUrlFlags & URL_REMEMBER)!=0 ){
g.url.flags |= URL_REMEMBER;
url_remember();
}
return http_exchange(pSend, pReply, mHttpFlags,
maxRedirect, zAltMimetype);
}else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
isCompressed = 0;
}else if( fossil_strnicmp(&zLine[14],
"application/x-fossil-uncompressed", -1)==0 ){
|
| ︙ | ︙ | |||
754 755 756 757 758 759 760 | /* ** COMMAND: test-httpmsg ** ** Usage: %fossil test-httpmsg ?OPTIONS? URL ?PAYLOAD? ?OUTPUT? ** ** Send an HTTP message to URL and get the reply. PAYLOAD is a file containing | | | 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 | /* ** COMMAND: test-httpmsg ** ** Usage: %fossil test-httpmsg ?OPTIONS? URL ?PAYLOAD? ?OUTPUT? ** ** Send an HTTP message to URL and get the reply. PAYLOAD is a file containing ** the payload, or "-" to read payload from standard input. A POST message ** is sent if PAYLOAD is specified and is non-empty. If PAYLOAD is omitted ** or is an empty file, then a GET message is sent. ** ** If a second filename (OUTPUT) is given after PAYLOAD, then the reply ** is written into that second file instead of being written on standard ** output. Use the "--out OUTPUT" option to specify an output file for ** a GET request where there is no PAYLOAD. |
| ︙ | ︙ | |||
791 792 793 794 795 796 797 798 799 800 801 802 803 804 |
ssl_disable_cert_verification();
#endif
}
if( find_option("xfer",0,0)!=0 ){
mHttpFlags |= HTTP_USE_LOGIN;
mHttpFlags &= ~HTTP_GENERIC;
}
verify_all_options();
if( g.argc<3 || g.argc>5 ){
usage("URL ?PAYLOAD? ?OUTPUT?");
}
zInFile = g.argc>=4 ? g.argv[3] : 0;
if( g.argc==5 ){
if( zOutFile ){
| > | 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 |
ssl_disable_cert_verification();
#endif
}
if( find_option("xfer",0,0)!=0 ){
mHttpFlags |= HTTP_USE_LOGIN;
mHttpFlags &= ~HTTP_GENERIC;
}
if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
verify_all_options();
if( g.argc<3 || g.argc>5 ){
usage("URL ?PAYLOAD? ?OUTPUT?");
}
zInFile = g.argc>=4 ? g.argv[3] : 0;
if( g.argc==5 ){
if( zOutFile ){
|
| ︙ | ︙ |
Changes to src/http_ssl.c.
| ︙ | ︙ | |||
317 318 319 320 321 322 323 | ** 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 | > | > | 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
** 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( SSLeay()!=0x30500000 /* Don't use for 3.5.0 due to a bug */
&& 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 */
|
| ︙ | ︙ | |||
449 450 451 452 453 454 455 |
const char *zRemoteHost;
ssl_global_init_client();
if( pUrlData->useProxy ){
int rc;
char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
BIO *sBio = BIO_new_connect(connStr);
| > > > > > > > | | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 |
const char *zRemoteHost;
ssl_global_init_client();
if( pUrlData->useProxy ){
int rc;
char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
BIO *sBio = BIO_new_connect(connStr);
if( g.fIPv4 ){
#ifdef BIO_FAMILY_IPV4
BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV4);
#else
fossil_warning("The --ipv4 option is not supported in this build\n");
#endif
}
fossil_free(connStr);
if( BIO_do_connect(sBio)<=0 ){
ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
pUrlData->name, pUrlData->port,
ERR_reason_error_string(ERR_get_error()));
ssl_close_client();
return 1;
}
|
| ︙ | ︙ | |||
501 502 503 504 505 506 507 |
/* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */
}
#endif
if( !pUrlData->useProxy ){
char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
BIO_set_conn_hostname(iBio, connStr);
| | > > > > > > > | 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
/* SSL_set_verify(ssl, SSL_VERIFY_PEER, 0); */
}
#endif
if( !pUrlData->useProxy ){
char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
BIO_set_conn_hostname(iBio, connStr);
fossil_free(connStr);
if( g.fIPv4 ){
#ifdef BIO_FAMILY_IPV4
BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV4);
#else
fossil_warning("The --ipv4 option is not supported in this build\n");
#endif
}
if( BIO_do_connect(iBio)<=0 ){
ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
pUrlData->name, pUrlData->port,
ERR_reason_error_string(ERR_get_error()));
ssl_close_client();
return 1;
}
|
| ︙ | ︙ | |||
908 909 910 911 912 913 914 |
static void trust_location_usable(const char *zPath, const char **pzStore){
if( *pzStore!=0 ) return;
if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath;
}
#endif /* FOSSIL_ENABLE_SSL */
/*
| | | | | | | | | | | | 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 |
static void trust_location_usable(const char *zPath, const char **pzStore){
if( *pzStore!=0 ) return;
if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath;
}
#endif /* FOSSIL_ENABLE_SSL */
/*
** COMMAND: tls-config* abbrv-subcom
** COMMAND: ssl-config abbrv-subcom
**
** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
**
** This command is used to view or modify the TLS (Transport Layer
** Security) configuration for Fossil. TLS (formerly SSL) is the
** encryption technology used for secure HTTPS transport.
**
** Sub-commands:
**
** remove-exception DOMAINS Remove TLS cert exceptions for the domains
** listed. Or remove them all if the --all
** option is specified.
**
** scrub ?--force? Remove all SSL configuration data from the
** repository. Use --force to omit the
** confirmation.
**
** show ?-v? Show the TLS configuration. Add -v to see
** additional explanation
*/
void test_tlsconfig_info(void){
const char *zCmd;
size_t nCmd;
int nHit = 0;
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
|
| ︙ | ︙ | |||
983 984 985 986 987 988 989 |
fossil_print("OpenSSL-version: (none)\n");
if( verbose ){
fossil_print("\n"
" The OpenSSL library is not used by this build of Fossil\n\n"
);
}
#else
| | | | 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 |
fossil_print("OpenSSL-version: (none)\n");
if( verbose ){
fossil_print("\n"
" The OpenSSL library is not used by this build of Fossil\n\n"
);
}
#else
fossil_print("OpenSSL-version: %s (0x%09llx)\n",
SSLeay_version(SSLEAY_VERSION), (unsigned long long)SSLeay());
if( verbose ){
fossil_print("\n"
" The version of the OpenSSL library being used\n"
" by this instance of Fossil. Version 3.0.0 or\n"
" later is recommended.\n\n"
);
}
|
| ︙ | ︙ | |||
1045 1046 1047 1048 1049 1050 1051 |
" 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)
| < | | < < > | | | | 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 |
" 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)
fossil_print(" OpenSSL-winstore: %s\n",
(SSLeay()>=0x30200000 && SSLeay()!=0x30500000) ? "Yes" : "No");
if( verbose ){
fossil_print("\n"
" OpenSSL 3.2.0, or newer, but not version 3.5.0 due to a bug,\n"
" are able to use the root certificates managed by the Windows\n"
" operating system. The installed root certificates are listed\n"
" by the command:\n\n"
" certutil -store \"ROOT\"\n\n"
);
}
#endif /* _WIN32 */
if( zUsed==0 ) zUsed = "";
fossil_print(" Trust store used: %s\n", zUsed);
|
| ︙ | ︙ | |||
1216 1217 1218 1219 1220 1221 1222 |
** 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",
| | | 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 |
** 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), (sqlite3_uint64)SSLeay());
#else
return mprintf("none");
#endif
}
|
Changes to src/info.c.
| ︙ | ︙ | |||
255 256 257 258 259 260 261 |
}else{
z = blob_str(&vx);
}
fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe));
fossil_print("version: %s", z);
blob_reset(&vx);
}
| | > > | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
}else{
z = blob_str(&vx);
}
fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe));
fossil_print("version: %s", z);
blob_reset(&vx);
}
}else if( g.repositoryOpen ){
int rid;
rid = name_to_rid(g.argv[2]);
if( rid==0 ){
fossil_fatal("no such object: %s", g.argv[2]);
}
show_common_info(rid, "hash:", 1, 1);
}else{
fossil_fatal("Could not find or open a Fossil repository");
}
}
/*
** Show the context graph (immediate parents and children) for
** check-in rid and rid2
*/
|
| ︙ | ︙ | |||
318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
|TIMELINE_GRAPH
|TIMELINE_FILLGAPS
|TIMELINE_NOSCROLL
|TIMELINE_XMERGE
|TIMELINE_CHPICK,
0, 0, 0, rid, rid2, 0);
db_finalize(&q);
}
/*
** Append the difference between artifacts to the output
*/
static void append_diff(
| > | 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
|TIMELINE_GRAPH
|TIMELINE_FILLGAPS
|TIMELINE_NOSCROLL
|TIMELINE_XMERGE
|TIMELINE_CHPICK,
0, 0, 0, rid, rid2, 0);
db_finalize(&q);
blob_reset(&sql);
}
/*
** Append the difference between artifacts to the output
*/
static void append_diff(
|
| ︙ | ︙ | |||
782 783 784 785 786 787 788 |
){
@ <div class='file-change-line'><span>
@ Changes to %h(zFile)
@ </span></div>
if( pCfg ){
char *zFullFN;
char *zHexFN;
| < < | < < < | 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 |
){
@ <div class='file-change-line'><span>
@ Changes to %h(zFile)
@ </span></div>
if( pCfg ){
char *zFullFN;
char *zHexFN;
zFullFN = file_canonical_name_dup(zLhs);
zHexFN = mprintf("x%H", zFullFN);
fossil_free(zFullFN);
pCfg->zLeftHash = zHexFN;
text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
pCfg->zLeftHash = 0;
fossil_free(zHexFN);
}
}
|
| ︙ | ︙ | |||
817 818 819 820 821 822 823 | ** if the web server is run on a loopback interface (in other words, was ** started using "fossil ui" or similar) from within an open check-out. ** ** If the "exbase=PATH" query parameter is provided, then the diff shown ** uses the files in PATH as the baseline. This is the same as using ** the "--from PATH" argument to the "fossil diff" command-line. In fact, ** when using "fossil ui --from PATH", the --from argument becomes the value | | > > > | | 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 |
** if the web server is run on a loopback interface (in other words, was
** started using "fossil ui" or similar) from within an open check-out.
**
** If the "exbase=PATH" query parameter is provided, then the diff shown
** uses the files in PATH as the baseline. This is the same as using
** the "--from PATH" argument to the "fossil diff" command-line. In fact,
** when using "fossil ui --from PATH", the --from argument becomes the value
** of the exbase query parameter for the start page. Note that if PATH
** is a pure hexadecimal string, it is decoded first before being used as
** the pathname. Real pathnames should contain at least one directory
** separator character.
**
** Other query parameters related to diffs are also accepted.
*/
void ckout_page(void){
int vid;
const char *zHome; /* Home directory */
int nHome;
const char *zExBase;
char *zHostname;
char *zCwd;
if( !cgi_is_loopback(g.zIpAddr) || !db_open_local(0) ){
cgi_redirectf("%R/home");
return;
}
file_chdir(g.zLocalRoot, 0);
vid = db_lget_int("checkout", 0);
db_unprotect(PROTECT_ALL);
vfile_check_signature(vid, CKSIG_ENOTFILE);
|
| ︙ | ︙ | |||
859 860 861 862 863 864 865 |
}else{
style_header("Checkout Status: %h", zCwd);
}
render_checkin_context(vid, 0, 0, 0);
@ <hr>
zExBase = P("exbase");
if( zExBase && zExBase[0] ){
| > | > | 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 |
}else{
style_header("Checkout Status: %h", zCwd);
}
render_checkin_context(vid, 0, 0, 0);
@ <hr>
zExBase = P("exbase");
if( zExBase && zExBase[0] ){
char *zPath = decode16_dup(zExBase);
char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase);
if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
@ <p>Using external baseline: ~%h(zCBase+nHome)</p>
}else{
@ <p>Using external baseline: %h(zCBase)</p>
}
ckout_external_base_diff(vid, zCBase);
fossil_free(zCBase);
fossil_free(zPath);
}else{
ckout_normal_diff(vid);
}
style_finish_page();
}
/*
|
| ︙ | ︙ | |||
922 923 924 925 926 927 928 |
zParent = db_text(0,
"SELECT uuid FROM plink, blob"
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
rid
);
isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
db_prepare(&q1,
| | | | 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 |
zParent = db_text(0,
"SELECT uuid FROM plink, blob"
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
rid
);
isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
db_prepare(&q1,
"SELECT uuid, datetime(mtime,toLocal(),'subsec'), user, comment,"
" datetime(omtime,toLocal(),'subsec'), mtime"
" FROM blob, event"
" WHERE blob.rid=%d"
" AND event.objid=%d",
rid, rid
);
zBrName = branch_of_rid(rid);
|
| ︙ | ︙ | |||
946 947 948 949 950 951 952 |
const char *zComment;
const char *zDate;
const char *zOrigDate;
int okWiki = 0;
Blob wiki_read_links = BLOB_INITIALIZER;
Blob wiki_add_links = BLOB_INITIALIZER;
| | | 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 |
const char *zComment;
const char *zDate;
const char *zOrigDate;
int okWiki = 0;
Blob wiki_read_links = BLOB_INITIALIZER;
Blob wiki_add_links = BLOB_INITIALIZER;
Th_StoreUnsafe("current_checkin", zName);
style_header("Check-in [%S]", zUuid);
login_anonymous_available();
zEUser = db_text(0,
"SELECT value FROM tagxref"
" WHERE tagid=%d AND rid=%d AND tagtype>0",
TAG_USER, rid);
zEComment = db_text(0,
|
| ︙ | ︙ | |||
988 989 990 991 992 993 994 |
zPJ[jj] = '_';
}
}
zUrl = mprintf("%R/tarball/%S/%t-%S.tar.gz", zUuid, zPJ, zUuid);
@ <tr><th>Downloads:</th><td>
@ %z(href("%s",zUrl))Tarball</a>
@ | %z(href("%R/zip/%S/%t-%S.zip",zUuid, zPJ,zUuid))ZIP archive</a>
| > | | > | 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 |
zPJ[jj] = '_';
}
}
zUrl = mprintf("%R/tarball/%S/%t-%S.tar.gz", zUuid, zPJ, zUuid);
@ <tr><th>Downloads:</th><td>
@ %z(href("%s",zUrl))Tarball</a>
@ | %z(href("%R/zip/%S/%t-%S.zip",zUuid, zPJ,zUuid))ZIP archive</a>
if( g.zLogin!=0 ){
@ | %z(href("%R/sqlar/%S/%t-%S.sqlar",zUuid,zPJ,zUuid))\
@ SQL archive</a></td></tr>
}
fossil_free(zUrl);
blob_reset(&projName);
}
@ <tr><th>Timelines:</th><td>
@ %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a>
if( zParent ){
|
| ︙ | ︙ | |||
1177 1178 1179 1180 1181 1182 1183 |
}
if( diffType!=2 ){
@ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
@ Side-by-Side Diff</a>
}
if( diffType!=0 ){
if( *zW ){
| | | | 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 |
}
if( diffType!=2 ){
@ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
@ Side-by-Side Diff</a>
}
if( diffType!=0 ){
if( *zW ){
@ %z(chref("button","%R/%s/%T?diff=%d",zPage,zName,diffType))
@ Show Whitespace Changes</a>
}else{
@ %z(chref("button","%R/%s/%T?diff=%d&w",zPage,zName,diffType))
@ Ignore Whitespace</a>
}
}
if( zParent ){
@ %z(chref("button","%R/vpatch?from=%!S&to=%!S",zParent,zUuid))
@ Patch</a>
}
|
| ︙ | ︙ | |||
1363 1364 1365 1366 1367 1368 1369 |
}
if( !is_a_version(rid) ){
webpage_error("Artifact %s is not a check-in.", P(zParam));
return 0;
}
return manifest_get(rid, CFTYPE_MANIFEST, 0);
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 |
}
if( !is_a_version(rid) ){
webpage_error("Artifact %s is not a check-in.", P(zParam));
return 0;
}
return manifest_get(rid, CFTYPE_MANIFEST, 0);
}
/*
** WEBPAGE: vdiff
** URL: /vdiff?from=TAG&to=TAG
**
** Show the difference between two check-ins identified by the from= and
** to= query parameters.
|
| ︙ | ︙ | |||
1930 1931 1932 1933 1934 1935 1936 |
blob_append(pDownloadName, zFilename, -1);
}
tag_private_status(rid);
}
db_finalize(&q);
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
rid, TAG_CLUSTER) ){
| | | 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 |
blob_append(pDownloadName, zFilename, -1);
}
tag_private_status(rid);
}
db_finalize(&q);
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
rid, TAG_CLUSTER) ){
@ Cluster %z(href("%R/info/%S",zUuid))%S(zUuid)</a>.
cnt++;
}
if( cnt==0 ){
@ Unrecognized artifact
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_appendf(pDownloadName, "%S.txt", zUuid);
}
|
| ︙ | ︙ | |||
2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 |
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
diff_config_init(&DCfg, 0);
diffType = preferred_diff_type();
if( P("from") && P("to") ){
v1 = artifact_from_ci_and_filename("from");
v2 = artifact_from_ci_and_filename("to");
}else{
Stmt q;
v1 = name_to_rid_www("v1");
v2 = name_to_rid_www("v2");
/* If the two file versions being compared both have the same
** filename, then offer an "Annotate" link that constructs an
** annotation between those version. */
db_prepare(&q,
"SELECT (SELECT substr(uuid,1,20) FROM blob WHERE rid=a.mid),"
" (SELECT substr(uuid,1,20) FROM blob WHERE rid=b.mid),"
| > > | 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 |
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
diff_config_init(&DCfg, 0);
diffType = preferred_diff_type();
if( P("from") && P("to") ){
v1 = artifact_from_ci_and_filename("from");
v2 = artifact_from_ci_and_filename("to");
if( v1==0 || v2==0 ) fossil_redirect_home();
}else{
Stmt q;
v1 = name_to_rid_www("v1");
v2 = name_to_rid_www("v2");
if( v1==0 || v2==0 ) fossil_redirect_home();
/* If the two file versions being compared both have the same
** filename, then offer an "Annotate" link that constructs an
** annotation between those version. */
db_prepare(&q,
"SELECT (SELECT substr(uuid,1,20) FROM blob WHERE rid=a.mid),"
" (SELECT substr(uuid,1,20) FROM blob WHERE rid=b.mid),"
|
| ︙ | ︙ | |||
2060 2061 2062 2063 2064 2065 2066 |
const char *zFN = db_column_text(&q, 2);
style_submenu_element("Annotate",
"%R/annotate?origin=%s&checkin=%s&filename=%T",
zOrig, zCkin, zFN);
}
db_finalize(&q);
}
| < | 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 |
const char *zFN = db_column_text(&q, 2);
style_submenu_element("Annotate",
"%R/annotate?origin=%s&checkin=%s&filename=%T",
zOrig, zCkin, zFN);
}
db_finalize(&q);
}
zRe = P("regex");
cgi_check_for_malice();
if( zRe ) re_compile(&pRe, zRe, 0);
if( verbose ) objdescFlags |= OBJDESC_DETAIL;
if( isPatch ){
Blob c1, c2, *pOut;
DiffConfig DCfg;
|
| ︙ | ︙ | |||
2246 2247 2248 2249 2250 2251 2252 |
iFrom, iTo);
return;
}
if( zName[0]=='x'
&& ((nName-1)&1)==0
&& validate16(&zName[1],nName-1)
&& g.perm.Admin
| < > | 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 |
iFrom, iTo);
return;
}
if( zName[0]=='x'
&& ((nName-1)&1)==0
&& validate16(&zName[1],nName-1)
&& g.perm.Admin
&& cgi_is_loopback(g.zIpAddr)
&& db_open_local(0)
){
/* Treat the HASH as a hex-encoded filename */
int n = (nName-1)/2;
char *zFN = fossil_malloc(n+1);
decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
zFN[n] = 0;
if( file_size(zFN, ExtFILE)<0 ){
|
| ︙ | ︙ | |||
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 |
fossil_print("%b\n", cgi_output_blob());
}
/*
** WEBPAGE: artifact
** WEBPAGE: file
** WEBPAGE: whatis
**
** Typical usage:
**
** /artifact/HASH
** /whatis/HASH
** /file/NAME
**
** Additional query parameters:
**
** ln - show line numbers
** ln=N - highlight line number N
** ln=M-N - highlight lines M through N inclusive
** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
** verbose - show more detail in the description
** download - redirect to the download (artifact page only)
** name=NAME - filename or hash as a query parameter
** filename=NAME - alternative spelling for "name="
** fn=NAME - alternative spelling for "name="
** ci=VERSION - The specific check-in to use with "name=" to
** identify the file.
** txt - Force display of unformatted source text
**
** The /artifact page show the complete content of a file
** identified by HASH. The /whatis page shows only a description
** of how the artifact is used. The /file page shows the most recent
** version of the file or directory called NAME, or a list of the
** top-level directory if NAME is omitted.
**
| > > > > > | 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 |
fossil_print("%b\n", cgi_output_blob());
}
/*
** WEBPAGE: artifact
** WEBPAGE: file
** WEBPAGE: whatis
** WEBPAGE: docfile
**
** Typical usage:
**
** /artifact/HASH
** /whatis/HASH
** /file/NAME
** /docfile/NAME
**
** Additional query parameters:
**
** ln - show line numbers
** ln=N - highlight line number N
** ln=M-N - highlight lines M through N inclusive
** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
** verbose - show more detail in the description
** brief - show just the document, not the metadata. The
** /docfile page is an alias for /file?brief
** download - redirect to the download (artifact page only)
** name=NAME - filename or hash as a query parameter
** filename=NAME - alternative spelling for "name="
** fn=NAME - alternative spelling for "name="
** ci=VERSION - The specific check-in to use with "name=" to
** identify the file.
** txt - Force display of unformatted source text
** hash - Output only the hash of the artifact
**
** The /artifact page show the complete content of a file
** identified by HASH. The /whatis page shows only a description
** of how the artifact is used. The /file page shows the most recent
** version of the file or directory called NAME, or a list of the
** top-level directory if NAME is omitted.
**
|
| ︙ | ︙ | |||
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 |
** a default value of "tip" is used for ci= if ci= is omitted.
*/
void artifact_page(void){
int rid = 0;
Blob content;
const char *zMime;
Blob downloadName;
int renderAsWiki = 0;
int renderAsHtml = 0;
int renderAsSvg = 0;
int objType;
int asText;
const char *zUuid = 0;
u32 objdescFlags = OBJDESC_BASE;
int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
int isFile = fossil_strcmp(g.zPath,"file")==0;
const char *zLn = P("ln");
const char *zName = P("name");
const char *zCI = P("ci");
HQuery url;
char *zCIUuid = 0;
int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
int isBranchCI = 0; /* ci= refers to a branch name */
char *zHeader = 0;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
cgi_check_for_malice();
style_set_current_feature("artifact");
/* Capture and normalize the name= and ci= query parameters */
if( zName==0 ){
zName = P("filename");
if( zName==0 ){
zName = P("fn");
}
| > > > > > > > | 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 |
** a default value of "tip" is used for ci= if ci= is omitted.
*/
void artifact_page(void){
int rid = 0;
Blob content;
const char *zMime;
Blob downloadName;
Blob uuid;
int renderAsWiki = 0;
int renderAsHtml = 0;
int renderAsSvg = 0;
int objType;
int asText;
const char *zUuid = 0;
u32 objdescFlags = OBJDESC_BASE;
int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
int hashOnly = P("hash")!=0;
int docOnly = P("brief")!=0;
int isFile = fossil_strcmp(g.zPath,"file")==0;
const char *zLn = P("ln");
const char *zName = P("name");
const char *zCI = P("ci");
HQuery url;
char *zCIUuid = 0;
int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
int isBranchCI = 0; /* ci= refers to a branch name */
char *zHeader = 0;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
cgi_check_for_malice();
style_set_current_feature("artifact");
if( fossil_strcmp(g.zPath, "docfile")==0 ){
isFile = 1;
docOnly = 1;
}
/* Capture and normalize the name= and ci= query parameters */
if( zName==0 ){
zName = P("filename");
if( zName==0 ){
zName = P("fn");
}
|
| ︙ | ︙ | |||
2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 |
if( descOnly || P("verbose")!=0 ){
url_add_parameter(&url, "verbose", "1");
objdescFlags |= OBJDESC_DETAIL;
}
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
etag_check(ETAG_HASH, zUuid);
asText = P("txt")!=0;
if( isFile ){
| > > > > > > > > > | | 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 |
if( descOnly || P("verbose")!=0 ){
url_add_parameter(&url, "verbose", "1");
objdescFlags |= OBJDESC_DETAIL;
}
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
etag_check(ETAG_HASH, zUuid);
if( descOnly && hashOnly ){
blob_set(&uuid, zUuid);
cgi_set_content_type("text/plain");
cgi_set_content(&uuid);
return;
}
asText = P("txt")!=0;
if( isFile ){
if( docOnly ){
/* No header */
}else if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
zCI = "tip";
@ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a>
@ from the %z(href("%R/info/tip"))latest check-in</a></h2>
}else{
const char *zPath;
Blob path;
blob_zero(&path);
|
| ︙ | ︙ | |||
2871 2872 2873 2874 2875 2876 2877 |
}else if( isSymbolicCI ){
@ part of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
}else{
@ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
}
blob_reset(&path);
}
| < > > | | | | | > | 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 |
}else if( isSymbolicCI ){
@ part of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
}else{
@ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
}
blob_reset(&path);
}
zMime = mimetype_from_name(zName);
if( !docOnly ){
style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
zName, zCI);
style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
zName, zCI);
style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName);
}
blob_init(&downloadName, zName, -1);
objType = OBJTYPE_CONTENT;
}else{
@ <h2>Artifact
style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
if( g.perm.Setup ){
@ (%d(rid)):</h2>
|
| ︙ | ︙ | |||
2900 2901 2902 2903 2904 2905 2906 |
}
if( !descOnly && P("download")!=0 ){
cgi_redirectf("%R/raw/%s?at=%T",
db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
file_tail(blob_str(&downloadName)));
/*NOTREACHED*/
}
| | | 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 |
}
if( !descOnly && P("download")!=0 ){
cgi_redirectf("%R/raw/%s?at=%T",
db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
file_tail(blob_str(&downloadName)));
/*NOTREACHED*/
}
if( g.perm.Admin && !docOnly ){
const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid);
}else{
style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid);
}
}
|
| ︙ | ︙ | |||
2945 2946 2947 2948 2949 2950 2951 |
const char *zUser = db_column_text(&q,0);
const char *zDate = db_column_text(&q,1);
const char *zIp = db_column_text(&q,2);
@ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
}
db_finalize(&q);
}
| > | | | > > | > > | > > | | | > | > | > | 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 |
const char *zUser = db_column_text(&q,0);
const char *zDate = db_column_text(&q,1);
const char *zIp = db_column_text(&q,2);
@ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
}
db_finalize(&q);
}
if( !docOnly ){
style_submenu_element("Download", "%R/raw/%s?at=%T",zUuid,file_tail(zName));
if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
}
}
if( zMime ){
if( fossil_strcmp(zMime, "text/html")==0 ){
if( asText ){
style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
}else{
renderAsHtml = 1;
if( !docOnly ){
style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
}
}
}else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0
|| fossil_strcmp(zMime, "text/x-markdown")==0
|| fossil_strcmp(zMime, "text/x-pikchr")==0 ){
if( asText ){
style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki",
"%s", url_render(&url, "txt", 0, 0, 0));
}else{
renderAsWiki = 1;
if( !docOnly ){
style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
}
}
}else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){
if( asText ){
style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0));
}else{
renderAsSvg = 1;
if( !docOnly ){
style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
}
}
}
if( !docOnly && fileedit_is_editable(zName) ){
style_submenu_element("Edit",
"%R/fileedit?filename=%T&checkin=%!S",
zName, zCI);
}
}
if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
style_submenu_element("Parsed", "%R/info/%s", zUuid);
}
if( descOnly ){
style_submenu_element("Content", "%R/artifact/%s", zUuid);
}else{
if( !docOnly || !isFile ){
@ <hr>
}
content_get(rid, &content);
if( renderAsWiki ){
safe_html_context(DOCSRC_FILE);
wiki_render_by_mimetype(&content, zMime);
document_emit_js();
}else if( renderAsHtml ){
@ <iframe src="%R/raw/%s(zUuid)"
|
| ︙ | ︙ | |||
3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 | @ <div class="section">Changes</div> @ <p> ticket_output_change_artifact(pTktChng, 0, 1, 0); manifest_destroy(pTktChng); style_finish_page(); } /* ** WEBPAGE: info ** URL: info/NAME ** ** The NAME argument is any valid artifact name: an artifact hash, ** a timestamp, a tag name, etc. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
@ <div class="section">Changes</div>
@ <p>
ticket_output_change_artifact(pTktChng, 0, 1, 0);
manifest_destroy(pTktChng);
style_finish_page();
}
/*
** rid is a cluster. Paint a page that contains detailed information
** about that cluster.
*/
static void cluster_info(int rid, const char *zName){
Manifest *pCluster;
int i;
Blob where = BLOB_INITIALIZER;
Blob unks = BLOB_INITIALIZER;
Stmt q;
char *zSha1Bg;
char *zSha3Bg;
int badRid = 0;
int rcvid;
int hashClr = PB("hclr");
const char *zDate;
pCluster = manifest_get(rid, CFTYPE_CLUSTER, 0);
if( pCluster==0 ){
artifact_page();
return;
}
style_header("Cluster %S", zName);
rcvid = db_int(0, "SELECT rcvid FROM blob WHERE rid=%d", rid);
if( rcvid==0 ){
zDate = 0;
}else{
zDate = db_text(0, "SELECT datetime(mtime) FROM rcvfrom WHERE rcvid=%d",
rcvid);
}
@ <p>Artifact %z(href("%R/artifact/%h",zName))%S(zName)</a> is a cluster
@ with %d(pCluster->nCChild) entries
if( g.perm.Admin ){
@ received <a href="%R/rcvfrom?rcvid=%d(rcvid)">%h(zDate)</a>:
}else{
@ received %h(zDate):
}
blob_appendf(&where,"IN(0");
for(i=0; i<pCluster->nCChild; i++){
int rid = fast_uuid_to_rid(pCluster->azCChild[i]);
if( rid ){
blob_appendf(&where,",%d", rid);
}else{
if( blob_size(&unks)>0 ) blob_append_char(&unks, ',');
badRid++;
blob_append_sql(&unks,"(%d,%Q)",-badRid,pCluster->azCChild[i]);
}
}
blob_append_char(&where,')');
describe_artifacts(blob_str(&where));
blob_reset(&where);
if( badRid>0 ){
db_multi_exec(
"WITH unks(rx,hx) AS (VALUES %s)\n"
"INSERT INTO description(rid,uuid,type,summary) "
" SELECT rx, hx, 'phantom', '' FROM unks;",
blob_sql_text(&unks)
);
}
blob_reset(&unks);
db_prepare(&q,
"SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref"
" FROM description ORDER BY uuid"
);
if( skin_detail_boolean("white-foreground") ){
zSha1Bg = "#714417";
zSha3Bg = "#177117";
}else{
zSha1Bg = "#ebffb0";
zSha3Bg = "#b0ffb0";
}
@ <table cellpadding="2" cellspacing="0" border="1">
if( g.perm.Admin ){
@ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks
}else{
@ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks
}
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q,0);
const char *zUuid = db_column_text(&q, 1);
const char *zDesc = db_column_text(&q, 2);
int isPriv = db_column_int(&q,3);
int isPhantom = db_column_int(&q,4);
const char *zRef = db_column_text(&q,6);
if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
/* Don't show private artifacts to users without Private (x) permission */
continue;
}
if( rid<=0 ){
@ <tr><td> </td>
}else if( hashClr ){
const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg;
@ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td>
}else{
@ <tr><td align="right">%d(rid)</td>
}
if( rid<=0 ){
@ <td> %S(zUuid) </td>
}else{
@ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td>
}
if( g.perm.Admin ){
int rcvid = db_column_int(&q,5);
if( rcvid<=0 ){
@ <td>
}else{
@ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a>
}
}
@ <td align="left">%h(zDesc)</td>
if( zRef && zRef[0] ){
@ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
}else{
@ <td>
}
if( isPriv || isPhantom ){
if( isPriv==0 ){
@ <td>phantom</td>
}else if( isPhantom==0 ){
@ <td>private</td>
}else{
@ <td>private,phantom</td>
}
}else{
@ <td>
}
@ </tr>
}
@ </table>
db_finalize(&q);
style_finish_page();
}
/*
** WEBPAGE: info
** URL: info/NAME
**
** The NAME argument is any valid artifact name: an artifact hash,
** a timestamp, a tag name, etc.
|
| ︙ | ︙ | |||
3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 |
ci_page();
}else
if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
ci_page();
}else
if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
ainfo_page();
}else
{
artifact_page();
}
}
/*
| > > > > | 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 |
ci_page();
}else
if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
ci_page();
}else
if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
ainfo_page();
}else
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
rid, TAG_CLUSTER) ){
cluster_info(rid, zName);
}else
{
artifact_page();
}
}
/*
|
| ︙ | ︙ | |||
3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 |
rid, TAG_BGCOLOR)==2;
fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor;
zNewColorFlag = P("newclr") ? " checked" : "";
zNewTagFlag = P("newtag") ? " checked" : "";
zNewTag = PDT("tagname","");
zNewBrFlag = P("newbr") ? " checked" : "";
zNewBranch = PDT("brname","");
zCloseFlag = P("close") ? " checked" : "";
zHideFlag = P("hide") ? " checked" : "";
if( P("apply") && cgi_csrf_safe(2) ){
Blob ctrl;
char *zNow;
blob_zero(&ctrl);
| > | 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 |
rid, TAG_BGCOLOR)==2;
fNewPropagateColor = P("clr")!=0 ? P("pclr")!=0 : fPropagateColor;
zNewColorFlag = P("newclr") ? " checked" : "";
zNewTagFlag = P("newtag") ? " checked" : "";
zNewTag = PDT("tagname","");
zNewBrFlag = P("newbr") ? " checked" : "";
zNewBranch = PDT("brname","");
zBranchName = branch_of_rid(rid);
zCloseFlag = P("close") ? " checked" : "";
zHideFlag = P("hide") ? " checked" : "";
if( P("apply") && cgi_csrf_safe(2) ){
Blob ctrl;
char *zNow;
blob_zero(&ctrl);
|
| ︙ | ︙ | |||
3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 |
blob_zero(&comment);
blob_append(&comment, zNewComment, -1);
zUuid[10] = 0;
style_header("Edit Check-in [%s]", zUuid);
if( P("preview") ){
Blob suffix;
int nTag = 0;
@ <b>Preview:</b>
@ <blockquote>
@ <table border=0>
if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
| > > > > > > | > > | | 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 |
blob_zero(&comment);
blob_append(&comment, zNewComment, -1);
zUuid[10] = 0;
style_header("Edit Check-in [%s]", zUuid);
if( P("preview") ){
Blob suffix;
int nTag = 0;
const char *zDplyBr; /* Branch name used to determine BG color */
if( zNewBrFlag[0] && zNewBranch[0] ){
zDplyBr = zNewBranch;
}else{
zDplyBr = zBranchName;
}
@ <b>Preview:</b>
@ <blockquote>
@ <table border=0>
if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
@ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));">
}else if( zColor[0] ){
@ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));">
}else if( zDplyBr && fossil_strcmp(zDplyBr,"trunk")!=0 ){
@ <tr><td style="background-color:%h(hash_color(zDplyBr));">
}else{
@ <tr><td>
}
@ %!W(blob_str(&comment))
blob_zero(&suffix);
blob_appendf(&suffix, "(user: %h", zNewUser);
db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
|
| ︙ | ︙ | |||
3658 3659 3660 3661 3662 3663 3664 | @ </td></tr> @ <tr><th align="right" valign="top">Tags:</th> @ <td valign="top"> @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)> @ Add the following new tag name to this check-in:</label> @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)"> | < < < | 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 |
@ </td></tr>
@ <tr><th align="right" valign="top">Tags:</th>
@ <td valign="top">
@ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)>
@ Add the following new tag name to this check-in:</label>
@ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)">
db_prepare(&q,
"SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
" WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
" ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
" ELSE tagname END /*sort*/",
rid
);
|
| ︙ | ︙ | |||
3801 3802 3803 3804 3805 3806 3807 | ** COMMAND: amend ** ** Usage: %fossil amend HASH OPTION ?OPTION ...? ** ** Amend the tags on check-in HASH to change how it displays in the timeline. ** ** Options: | | < < < < | > | < | > > > > | | | > | > | | | 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 | ** COMMAND: amend ** ** Usage: %fossil amend HASH OPTION ?OPTION ...? ** ** Amend the tags on check-in HASH to change how it displays in the timeline. ** ** Options: ** --author USER Make USER the author for check-in ** --bgcolor COLOR Apply COLOR to this check-in ** --branch NAME Rename branch of check-in to NAME ** --branchcolor COLOR Apply and propagate COLOR to the branch ** --cancel TAG Cancel TAG from this check-in ** --close Mark this "leaf" as closed ** --date DATETIME Make DATETIME the check-in time ** --date-override DATETIME Set the change time on the control artifact ** -e|--edit-comment Launch editor to revise comment ** --editor NAME Text editor to use for check-in comment ** --hide Hide branch starting from this check-in ** -m|--comment COMMENT Make COMMENT the check-in comment ** -M|--message-file FILE Read the amended comment from FILE ** -n|--dry-run Print control artifact, but make no changes ** --no-verify-comment Do not validate the check-in comment ** --tag TAG Add new TAG to this check-in ** --user-override USER Set the user name on the control artifact ** ** DATETIME 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). Either no timezone suffix or "Z" ** means UTC. */ |
| ︙ | ︙ | |||
3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 |
int fHide; /* True if branch should be hidden */
int fPropagateColor; /* True if color propagates before amend */
int fNewPropagateColor = 0; /* True if color propagates after amend */
int fHasHidden = 0; /* True if hidden tag already set */
int fHasClosed = 0; /* True if closed tag already set */
int fEditComment; /* True if editor to be used for comment */
int fDryRun; /* Print control artifact, make no changes */
const char *zChngTime; /* The change time on the control artifact */
const char *zUserOvrd; /* The user name on the control artifact */
const char *zUuid;
Blob ctrl;
Blob comment;
char *zNow;
int nTags, nCancels;
int i;
Stmt q;
fEditComment = find_option("edit-comment","e",0)!=0;
zNewComment = find_option("comment","m",1);
zComFile = find_option("message-file","M",1);
zNewBranch = find_option("branch",0,1);
zNewColor = find_option("bgcolor",0,1);
zNewBrColor = find_option("branchcolor",0,1);
| > > > | 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 |
int fHide; /* True if branch should be hidden */
int fPropagateColor; /* True if color propagates before amend */
int fNewPropagateColor = 0; /* True if color propagates after amend */
int fHasHidden = 0; /* True if hidden tag already set */
int fHasClosed = 0; /* True if closed tag already set */
int fEditComment; /* True if editor to be used for comment */
int fDryRun; /* Print control artifact, make no changes */
int noVerifyCom = 0; /* Allow suspicious check-in comments */
const char *zChngTime; /* The change time on the control artifact */
const char *zUserOvrd; /* The user name on the control artifact */
const char *zUuid;
Blob ctrl;
Blob comment;
char *zNow;
int nTags, nCancels;
int i;
Stmt q;
int ckComFlgs; /* Flags passed to verify_comment() */
fEditComment = find_option("edit-comment","e",0)!=0;
zNewComment = find_option("comment","m",1);
zComFile = find_option("message-file","M",1);
zNewBranch = find_option("branch",0,1);
zNewColor = find_option("bgcolor",0,1);
zNewBrColor = find_option("branchcolor",0,1);
|
| ︙ | ︙ | |||
3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 |
pzCancelTags = find_repeatable_option("cancel",0,&nCancels);
fClose = find_option("close",0,0)!=0;
fHide = find_option("hide",0,0)!=0;
fDryRun = find_option("dry-run","n",0)!=0;
zChngTime = find_option("date-override",0,1);
if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
zUserOvrd = find_option("user-override",0,1);
db_find_and_open_repository(0,0);
user_select();
verify_all_options();
if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
rid = name_to_typed_rid(g.argv[2], "ci");
if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
if( zUuid==0 ) fossil_fatal("Unable to find artifact hash");
zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
| > > | 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 |
pzCancelTags = find_repeatable_option("cancel",0,&nCancels);
fClose = find_option("close",0,0)!=0;
fHide = find_option("hide",0,0)!=0;
fDryRun = find_option("dry-run","n",0)!=0;
zChngTime = find_option("date-override",0,1);
if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
zUserOvrd = find_option("user-override",0,1);
noVerifyCom = find_option("no-verify-comment",0,0)!=0;
db_find_and_open_repository(0,0);
user_select();
(void)fossil_text_editor();
verify_all_options();
if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
rid = name_to_typed_rid(g.argv[2], "ci");
if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
if( zUuid==0 ) fossil_fatal("Unable to find artifact hash");
zComment = db_text(0, "SELECT coalesce(ecomment,comment)"
|
| ︙ | ︙ | |||
3934 3935 3936 3937 3938 3939 3940 |
),
fNewPropagateColor
);
}
if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
cancel_color();
}
| > > > > > > > > > > > > > > > > | | < | < | | | > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 |
),
fNewPropagateColor
);
}
if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
cancel_color();
}
if( fEditComment || zNewComment || zComFile ){
blob_init(&comment, 0, 0);
/* Figure out how much comment verification is requested */
if( noVerifyCom ){
ckComFlgs = 0;
}else{
const char *zVerComs = db_get("verify-comments","on");
if( is_false(zVerComs) ){
ckComFlgs = 0;
}else if( strcmp(zVerComs,"preview")==0 ){
ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP;
}else{
ckComFlgs = COMCK_MARKUP;
}
}
if( fEditComment ){
prepare_amend_comment(&comment, zComment, zUuid);
}else if( zComFile ){
blob_read_from_file(&comment, zComFile, ExtFILE);
blob_to_utf8_no_bom(&comment, 1);
}else if( zNewComment ){
blob_init(&comment, zNewComment, -1);
}
if( blob_size(&comment)>0
&& comment_compare(zComment, blob_str(&comment))==0
){
int rc;
while( (rc = verify_comment(&comment, ckComFlgs))!=0 ){
char cReply;
Blob ans;
if( !fEditComment ){
fossil_fatal("Amend aborted; "
"use --no-verify-comment to override");
}
if( rc==COMCK_PREVIEW ){
prompt_user("Continue, abort, or edit (C/a/e)? ", &ans);
}else{
prompt_user("Edit, abort, or continue (E/a/c)? ", &ans);
}
cReply = blob_str(&ans)[0];
cReply = fossil_tolower(cReply);
blob_reset(&ans);
if( cReply=='a' ){
fossil_fatal("Amend aborted.");
}
if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){
char *zPrior = blob_materialize(&comment);
blob_init(&comment, 0, 0);
prepare_amend_comment(&comment, zPrior, zUuid);
fossil_free(zPrior);
continue;
}else{
break;
}
}
}
add_comment(blob_str(&comment));
}
if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
if( is_datetime(zNewDate) ){
add_date(zNewDate);
}else{
fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS");
}
}
|
| ︙ | ︙ |
Changes to src/interwiki.c.
| ︙ | ︙ | |||
159 160 161 162 163 164 165 | ** COMMAND: interwiki* ** ** Usage: %fossil interwiki COMMAND ... ** ** Manage the "intermap" that defines the mapping from interwiki tags ** to complete URLs for interwiki links. ** | | | | | 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 |
** COMMAND: interwiki*
**
** Usage: %fossil interwiki COMMAND ...
**
** Manage the "intermap" that defines the mapping from interwiki tags
** to complete URLs for interwiki links.
**
** > fossil interwiki delete TAG ...
**
** Delete one or more interwiki maps.
**
** > fossil interwiki edit TAG --base URL --hash PATH --wiki PATH
**
** Create an interwiki referenced call TAG. The base URL is
** the --base option, which is required. The --hash and --wiki
** paths are optional. The TAG must be lower-case alphanumeric
** and must be unique. A new entry is created if it does not
** already exit.
**
** > fossil interwiki list
**
** Show all interwiki mappings.
*/
void interwiki_cmd(void){
const char *zCmd;
int nCmd;
db_find_and_open_repository(0, 0);
|
| ︙ | ︙ |
Changes to src/json_config.c.
| ︙ | ︙ | |||
84 85 86 87 88 89 90 |
{ "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 },
| < | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
{ "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-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 },
|
| ︙ | ︙ |
Changes to src/loadctrl.c.
| ︙ | ︙ | |||
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
** %fossil test-loadavg
**
** Print the load average on the host machine.
*/
void loadavg_test_cmd(void){
fossil_print("load-average: %f\n", load_average());
}
/*
** 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;
}
#endif
| > > > > > > > > > > > > > > > > > > > > < < < < < < < | | 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 |
** %fossil test-loadavg
**
** Print the load average on the host machine.
*/
void loadavg_test_cmd(void){
fossil_print("load-average: %f\n", load_average());
}
/*
** WEBPAGE: test-overload
**
** Generate the response that would normally be shown only when
** service is denied due to an overload condition. This is for
** testing of the overload warning page.
*/
void overload_page(void){
double mxLoad = atof(db_get("max-loadavg", "0.0"));
style_set_current_feature("test");
style_header("Server Overload");
@ <h2>The server load is currently too high.
@ Please try again later.</h2>
@ <p>Current load average: %f(load_average())<br>
@ Load average limit: %f(mxLoad)<br>
@ URL: %h(g.zBaseURL)%h(P("PATH_INFO"))<br>
@ Timestamp: %h(db_text("","SELECT datetime()"))Z</p>
style_finish_page();
}
/*
** 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;
}
#endif
overload_page();
cgi_set_status(503,"Server Overload");
cgi_reply();
exit(0);
}
|
Changes to src/login.c.
| ︙ | ︙ | |||
1297 1298 1299 1300 1301 1302 1303 |
**
** 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){
| < > > > > > > > > > > > > > > > > > | 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 |
**
** 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 *zGlob;
int isMatch = 1;
int nQP; /* Number of query parameters other than name= */
if( g.zLogin!=0 ) return;
zGlob = db_get("robot-restrict",0);
if( zGlob==0 || zGlob[0]==0 ) return;
if( g.isHuman ){
const char *zReferer;
const char *zAccept;
const char *zBr;
zReferer = P("HTTP_REFERER");
if( zReferer && zReferer[0]!=0 ) return;
/* Robots typically do not accept the brotli encoding, at least not
** at the time of this writing (2025-04-01), but standard web-browser
** all generally do accept brotli. So if brotli is accepted,
** assume we are not talking to a robot. We might want to revisit this
** heuristic in the future...
*/
if( (zAccept = P("HTTP_ACCEPT_ENCODING"))!=0
&& (zBr = strstr(zAccept,"br"))!=0
&& !fossil_isalnum(zBr[2])
&& (zBr==zAccept || !fossil_isalnum(zBr[-1]))
){
return;
}
}
nQP = cgi_qp_count();
if( nQP<1 ) return;
isMatch = glob_multi_match(zGlob, g.zPath);
if( !isMatch ) return;
/* Check for exceptions to the restriction on the number of query
|
| ︙ | ︙ | |||
1398 1399 1400 1401 1402 1403 1404 |
** This feature allows the "fossil ui" command to give the user
** full access rights without having to log in.
*/
zIpAddr = PD("REMOTE_ADDR","nil");
if( ( cgi_is_loopback(zIpAddr)
|| (g.fSshClient & CGI_SSH_CLIENT)!=0 )
&& g.useLocalauth
| | | 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 |
** This feature allows the "fossil ui" command to give the user
** full access rights without having to log in.
*/
zIpAddr = PD("REMOTE_ADDR","nil");
if( ( cgi_is_loopback(zIpAddr)
|| (g.fSshClient & CGI_SSH_CLIENT)!=0 )
&& g.useLocalauth
&& db_get_boolean("localauth",0)==0
&& P("HTTPS")==0
){
char *zSeed;
if( g.localOpen ) zLogin = db_lget("default-user",0);
if( zLogin!=0 ){
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
}else{
|
| ︙ | ︙ |
Changes to src/lookslike.c.
| ︙ | ︙ | |||
476 477 478 479 480 481 482 |
if( strchr("(_", z[i+n])!=0 ) return 0;
return 1;
}
/*
** Returns true if the given text contains certain keywords or
** punctuation which indicate that it might be an SQL injection attempt
| | | | | > | > > | | | | | | | | > > > > > > > > > | | | | | | | | | 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 |
if( strchr("(_", z[i+n])!=0 ) return 0;
return 1;
}
/*
** Returns true if the given text contains certain keywords or
** punctuation which indicate that it might be an SQL injection attempt
** or Cross-site scripting attempt or some other kind of mischief.
**
** This is not a primary defense against vulnerabilities in the Fossil
** code. Rather, this is part of an effort to do early detection of malicious
** spiders to avoid them using up too many CPU cycles. Or, this routine
** can also be thought of as a secondary layer of defense against attacks.
*/
int looks_like_attack(const char *zTxt){
unsigned int i;
int rc = 0;
if( zTxt==0 ) return 0;
for(i=0; zTxt[i]; i++){
switch( zTxt[i] ){
case '<':
case ';':
case '\'':
return 1;
case '/': /* 0123456789 123456789 */
if( strncmp(zTxt+i+1, "/wp-content/plugins/", 20)==0 ) rc = 1;
if( strncmp(zTxt+i+1, "/wp-admin/admin-ajax", 20)==0 ) rc = 1;
break;
case 'a':
case 'A':
if( isWholeWord(zTxt, i, "and", 3) ) rc = 1;
break;
case 'n':
case 'N':
if( isWholeWord(zTxt, i, "null", 4) ) rc = 1;
break;
case 'o':
case 'O':
if( isWholeWord(zTxt, i, "order", 5) && fossil_isspace(zTxt[i+5]) ){
rc = 1;
}
if( isWholeWord(zTxt, i, "or", 2) ) rc = 1;
break;
case 's':
case 'S':
if( isWholeWord(zTxt, i, "select", 6) ) rc = 1;
break;
case 'w':
case 'W':
if( isWholeWord(zTxt, i, "waitfor", 7) ) rc = 1;
break;
}
}
if( rc ){
/* The test/markdown-test3.md document which is part of the Fossil source
** tree intentionally tries to fake an attack. Do not report such
** errors. */
const char *zPathInfo = P("PATH_INFO");
if( sqlite3_strglob("/doc/*/test/markdown-test3.md", zPathInfo)==0 ){
rc = 0;
}
}
return rc;
}
/*
** This is a utility routine associated with the test-looks-like-sql-injection
** command.
**
** Read input from zInFile and print only those lines that look like they
** might be SQL injection.
**
** Or if bInvert is true, then show the opposite - those lines that do NOT
** look like SQL injection.
*/
static void show_attack_lines(
const char *zInFile, /* Name of input file */
int bInvert, /* Invert the sense of the output (-v) */
int bDeHttpize /* De-httpize the inputs. (-d) */
){
FILE *in;
char zLine[10000];
if( zInFile==0 || strcmp(zInFile,"-")==0 ){
in = stdin;
}else{
in = fopen(zInFile, "rb");
if( in==0 ){
fossil_fatal("cannot open \"%s\" for reading\n", zInFile);
}
}
while( fgets(zLine, sizeof(zLine), in) ){
dehttpize(zLine);
if( (looks_like_attack(zLine)!=0) ^ bInvert ){
fossil_print("%s", zLine);
}
}
if( in!=stdin ) fclose(in);
}
/*
** COMMAND: test-looks-like-attack
**
** Read lines of input from files named as arguments (or from standard
** input if no arguments are provided) and print those that look like they
** might be part of an SQL injection attack.
**
** Used to test the looks_lile_attack() utility subroutine, possibly
** by piping in actual server log data.
*/
void test_looks_like_attack(void){
int i;
int bInvert = find_option("invert","v",0)!=0;
int bDeHttpize = find_option("dehttpize","d",0)!=0;
verify_all_options();
if( g.argc==2 ){
show_attack_lines(0, bInvert, bDeHttpize);
}
for(i=2; i<g.argc; i++){
show_attack_lines(g.argv[i], bInvert, bDeHttpize);
}
}
|
Changes to src/main.c.
| ︙ | ︙ | |||
637 638 639 640 641 642 643 |
}
}
fossil_warning("%s", blob_str(&msg));
blob_reset(&msg);
}
/*
| < | > | | > | 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 |
}
}
fossil_warning("%s", blob_str(&msg));
blob_reset(&msg);
}
/*
** Initialize the g.comFmtFlags global variable.
**
** Global command-line options --comfmtflags or --comment-format can be
** used for this. However, those command-line options are undocumented
** and deprecated. They are here for backwards compatibility only.
*/
static void fossil_init_flags_from_options(void){
const char *zValue = find_option("comfmtflags", 0, 1);
if( zValue==0 ){
zValue = find_option("comment-format", 0, 1);
}
if( zValue ){
|
| ︙ | ︙ | |||
723 724 725 726 727 728 729 | 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! */ | | | | | 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 |
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()<3049000
|| strncmp(sqlite3_sourceid(),"2025-02-06",10)<0
){
fossil_panic("Unsuitable SQLite version %s, must be at least 3.49.0",
sqlite3_libversion());
}
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
memset(&g, 0, sizeof(g));
g.now = time(0);
|
| ︙ | ︙ | |||
995 996 997 998 999 1000 1001 |
fossil_fatal("Usage: %s %s %s", g.argv[0], g.argv[1], zFormat);
}
/*
** Remove n elements from g.argv beginning with the i-th element.
*/
static void remove_from_argv(int i, int n){
| < < | < | | 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 |
fossil_fatal("Usage: %s %s %s", g.argv[0], g.argv[1], zFormat);
}
/*
** Remove n elements from g.argv beginning with the i-th element.
*/
static void remove_from_argv(int i, int n){
memmove(&g.argv[i], &g.argv[i+n], sizeof(g.argv[i])*(g.argc-i-n));
g.argc -= n;
}
/*
** Look for a command-line option. If present, remove it from the
** argument list and return a pointer to either the flag's name (if
** hasArg==0), sans leading - or --, or its value (if hasArg==1).
|
| ︙ | ︙ | |||
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 |
zReturn = g.argv[i+hasArg];
remove_from_argv(i, 1+hasArg);
break;
}
}
return zReturn;
}
/* Return true if zOption exists in the command-line arguments,
** but do not remove it from the list or otherwise process it.
*/
int has_option(const char *zOption){
int i;
int n = (int)strlen(zOption);
| > > > > > > > > > | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 |
zReturn = g.argv[i+hasArg];
remove_from_argv(i, 1+hasArg);
break;
}
}
return zReturn;
}
/*
** Restore an option previously removed by find_option().
*/
void restore_option(const char *zName, const char *zValue, int hasOpt){
if( zValue==0 && hasOpt ) return;
g.argv[g.argc++] = (char*)zName;
if( hasOpt ) g.argv[g.argc++] = (char*)zValue;
}
/* Return true if zOption exists in the command-line arguments,
** but do not remove it from the list or otherwise process it.
*/
int has_option(const char *zOption){
int i;
int n = (int)strlen(zOption);
|
| ︙ | ︙ | |||
1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 |
while( find_option("verbose","v",0)!=0 ) verboseFlag++;
while( find_option("vv",0,0)!=0 ) verboseFlag += 2;
/* We should be done with options.. */
verify_all_options();
fossil_version_blob(&versionInfo, verboseFlag);
fossil_print("%s", blob_str(&versionInfo));
}
/*
** WEBPAGE: version
**
** Show the version information for Fossil.
| > | 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 |
while( find_option("verbose","v",0)!=0 ) verboseFlag++;
while( find_option("vv",0,0)!=0 ) verboseFlag += 2;
/* We should be done with options.. */
verify_all_options();
fossil_version_blob(&versionInfo, verboseFlag);
fossil_print("%s", blob_str(&versionInfo));
blob_reset(&versionInfo);
}
/*
** WEBPAGE: version
**
** Show the version information for Fossil.
|
| ︙ | ︙ | |||
1632 1633 1634 1635 1636 1637 1638 | exit(1); } /* ** Return true if it is appropriate to redirect requests to HTTPS. ** ** Redirect to https is appropriate if all of the above are true: | | | 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 |
exit(1);
}
/*
** Return true if it is appropriate to redirect requests to HTTPS.
**
** Redirect to https is appropriate if all of the above are true:
** (1) The redirect-to-https flag has a value of iLevel or greater.
** (2) The current connection is http, not https or ssh
** (3) The sslNotAvailable flag is clear
*/
int fossil_wants_https(int iLevel){
if( g.sslNotAvailable ) return 0;
if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
if( P("HTTPS")!=0 ) return 0;
|
| ︙ | ︙ | |||
1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 |
** then szFile will become zero (for an empty file) or positive.
** Special case: Assume any file with a basename of ".fossil" does
** not exist.
*/
zCleanRepo = file_cleanup_fullpath(zRepo);
if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
szFile = file_size(zCleanRepo, ExtFILE);
if( g.fHttpTrace ){
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
@ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
}
}
| > > > > > | 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 |
** then szFile will become zero (for an empty file) or positive.
** Special case: Assume any file with a basename of ".fossil" does
** not exist.
*/
zCleanRepo = file_cleanup_fullpath(zRepo);
if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
szFile = file_size(zCleanRepo, ExtFILE);
if( szFile>0 && !file_isfile(zCleanRepo, ExtFILE) ){
/* Only let szFile be non-negative if zCleanRepo really is a file
** and not a directory or some other filesystem object. */
szFile = -1;
}
if( g.fHttpTrace ){
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
@ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
}
}
|
| ︙ | ︙ | |||
2038 2039 2040 2041 2042 2043 2044 |
/* Use the first element of PATH_INFO as the page name
** and deliver the appropriate page back to the user.
*/
set_base_url(0);
if( fossil_redirect_to_https_if_needed(2) ) return;
if( zPathInfo==0 || zPathInfo[0]==0
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
| | > | 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 |
/* Use the first element of PATH_INFO as the page name
** and deliver the appropriate page back to the user.
*/
set_base_url(0);
if( fossil_redirect_to_https_if_needed(2) ) return;
if( zPathInfo==0 || zPathInfo[0]==0
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
/* Second special case: If the PATH_INFO is blank, issue a
** temporary 302 redirect:
** (1) to "/ckout" if g.useLocalauth and g.localOpen are both set.
** (2) to the home page identified by the "index-page" setting
** in the repository CONFIG table
** (3) to "/index" if there no "index-page" setting in CONFIG
*/
#ifdef FOSSIL_ENABLE_JSON
if(g.json.isJsonMode){
|
| ︙ | ︙ | |||
2152 2153 2154 2155 2156 2157 2158 |
json_bootstrap_late();
jsonOnce = 1;
}
}
#endif
if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
cgi_decode_post_parameters();
| | | 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 |
json_bootstrap_late();
jsonOnce = 1;
}
}
#endif
if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
cgi_decode_post_parameters();
if( !cgi_same_origin(0) ){
isReadonly = 1;
db_protect(PROTECT_READONLY);
}
}
if( g.fCgiTrace ){
fossil_trace("######## Calling %s #########\n", pCmd->zName);
cgi_print_all(1, 1, 0);
|
| ︙ | ︙ | |||
2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 |
** website we have an CGI at http://fossil.com/index.html (note
** ".com" instead of ".org") that looks like this:
**
** #!/usr/bin/fossil
** redirect: * https://fossil-scm.org/home
**
** Thus requests to the .com website redirect to the .org website.
*/
static void redirect_web_page(int nRedirect, char **azRedirect){
int i; /* Loop counter */
const char *zNotFound = 0; /* Not found URL */
const char *zName = P("name");
set_base_url(0);
if( zName==0 ){
| > > > > > > > > > > > | 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 |
** website we have an CGI at http://fossil.com/index.html (note
** ".com" instead of ".org") that looks like this:
**
** #!/usr/bin/fossil
** redirect: * https://fossil-scm.org/home
**
** Thus requests to the .com website redirect to the .org website.
** This form uses a 301 Permanent redirect.
**
** On a "*" redirect, the PATH_INFO and QUERY_STRING of the query
** that provoked the redirect are appended to the target. So, for
** example, if the input URL for the redirect above were
** "http://www.fossil.com/index.html/timeline?c=20250404", then
** the redirect would be to:
**
** https://fossil-scm.org/home/timeline?c=20250404
** ^^^^^^^^^^^^^^^^^^^^
** Copied from input URL
*/
static void redirect_web_page(int nRedirect, char **azRedirect){
int i; /* Loop counter */
const char *zNotFound = 0; /* Not found URL */
const char *zName = P("name");
set_base_url(0);
if( zName==0 ){
|
| ︙ | ︙ | |||
2282 2283 2284 2285 2286 2287 2288 |
}
}
}
if( zNotFound ){
Blob to;
const char *z;
if( strstr(zNotFound, "%s") ){
| | > | | | 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 |
}
}
}
if( zNotFound ){
Blob to;
const char *z;
if( strstr(zNotFound, "%s") ){
char *zTarget = mprintf(zNotFound /*works-like:"%s"*/, zName);
cgi_redirect_perm(zTarget);
}
if( strchr(zNotFound, '?') ){
cgi_redirect_perm(zNotFound);
}
blob_init(&to, zNotFound, -1);
z = P("PATH_INFO");
if( z && z[0]=='/' ) blob_append(&to, z, -1);
z = P("QUERY_STRING");
if( z && z[0]!=0 ) blob_appendf(&to, "?%s", z);
cgi_redirect_perm(blob_str(&to));
}else{
@ <html>
@ <head><title>No Such Object</title></head>
@ <body>
@ <p>No such object: <b>%h(zName)</b></p>
@ </body>
cgi_reply();
|
| ︙ | ︙ | |||
2337 2338 2339 2340 2341 2342 2343 | ** or "directory:" ** ** notfound: URL When in "directory:" mode, redirect to ** URL if no suitable repository is found. ** ** repolist When in "directory:" mode, display a page ** showing a list of available repositories if | | > > > > > > > | 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 | ** or "directory:" ** ** notfound: URL When in "directory:" mode, redirect to ** URL if no suitable repository is found. ** ** repolist When in "directory:" mode, display a page ** showing a list of available repositories if ** the URL is "/". Some control over the display ** is accomplished using environment variables. ** FOSSIL_REPOLIST_TITLE is the tital of the page. ** FOSSIL_REPOLIST_SHOW cause the "Description" ** column to display if it contains "description" as ** as a substring, and causes the Login-Group column ** to display if it contains the "login-group" ** substring. ** ** localauth Grant administrator privileges to connections ** from 127.0.0.1 or ::1. ** ** nossl Signal that no SSL connections are available. ** ** nocompress Do not compress HTTP replies. |
| ︙ | ︙ | |||
2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 | ** ** redirect: REPO URL Extract the "name" query parameter and search ** REPO for a check-in or ticket that matches the ** value of "name", then redirect to URL. There ** can be multiple "redirect:" lines that are ** processed in order. If the REPO is "*", then ** an unconditional redirect to URL is taken. ** ** jsmode: VALUE Specifies the delivery mode for JavaScript ** files. See the help text for the --jsmode ** flag of the http command. ** ** mainmenu: FILE Override the mainmenu config setting with the ** contents of the given file. ** ** Most CGI files contain only a "repository:" line. It is uncommon to ** use any other option. ** ** The lines are processed in the order they are read, which is most ** 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. ** | > > > | | 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 |
**
** redirect: REPO URL Extract the "name" query parameter and search
** REPO for a check-in or ticket that matches the
** value of "name", then redirect to URL. There
** can be multiple "redirect:" lines that are
** processed in order. If the REPO is "*", then
** an unconditional redirect to URL is taken.
** When "*" is used a 301 permanent redirect is
** issued and the tail and query string from the
** original query are appeneded onto URL.
**
** jsmode: VALUE Specifies the delivery mode for JavaScript
** files. See the help text for the --jsmode
** flag of the http command.
**
** mainmenu: FILE Override the mainmenu config setting with the
** contents of the given file.
**
** Most CGI files contain only a "repository:" line. It is uncommon to
** use any other option.
**
** The lines are processed in the order they are read, which is most
** 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]] [Windows only]
*/
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 */
|
| ︙ | ︙ | |||
2527 2528 2529 2530 2531 2532 2533 |
if( blob_eq(&key, "setenv:") && blob_token(&line, &value) ){
/* setenv: NAME VALUE
** setenv: NAME
**
** Sets environment variable NAME to VALUE. If VALUE is omitted, then
** the environment variable is unset.
*/
| > | > > > | | 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 |
if( blob_eq(&key, "setenv:") && blob_token(&line, &value) ){
/* setenv: NAME VALUE
** setenv: NAME
**
** Sets environment variable NAME to VALUE. If VALUE is omitted, then
** the environment variable is unset.
*/
char *zValue;
blob_tail(&line,&value2);
blob_trim(&value2);
zValue = blob_str(&value2);
while( fossil_isspace(zValue[0]) ){ zValue++; }
fossil_setenv(blob_str(&value), zValue);
blob_reset(&value);
blob_reset(&value2);
continue;
}
if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){
/* errorlog: FILENAME
**
|
| ︙ | ︙ | |||
2854 2855 2856 2857 2858 2859 2860 |
** --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)
** --usepidkey Use saved encryption key from parent process. This is
** only necessary when using SEE on Windows or Linux.
**
| | | 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 |
** --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)
** --usepidkey Use saved encryption key from parent process. This is
** only necessary when using SEE on Windows or Linux.
**
** See also: [[cgi]], [[server]], [[winsrv]] [Windows only]
*/
void cmd_http(void){
const char *zIpAddr = 0;
const char *zNotFound;
const char *zHost;
const char *zAltBase;
const char *zFileGlob;
|
| ︙ | ︙ | |||
2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 |
}else{
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 */
| > > > > > > > > > > > > > | 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{
g.httpOut = stdout;
#if defined(_WIN32)
_setmode(_fileno(stdout), _O_BINARY);
#endif
}
zIpAddr = find_option("ipaddr",0,1);
#if defined(_WIN32)
/* The undocumented option "--as NAME" causes NAME to become
** the fake command name. This only happens on Windows and only
** if preceded by --in, --out, and --ipaddr. It is a work-around
** to get the original command-name down into the "http" command that
** is run in a subprocess to manage HTTP requests on Windows for
** commands like "fossil ui" and "fossil server".
*/
if( zInFile && zOutFile && zIpAddr ){
const char *z = find_option("as",0,1);
if( z ) g.zCmdName = z;
}
#endif
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 */
|
| ︙ | ︙ | |||
3009 3010 3011 3012 3013 3014 3015 | ** This command can used for interactive debugging of web pages. For ** example, one can put a simple HTTP request in a file like this: ** ** echo 'GET /timeline' >request.txt ** ** Then run (in a debugger) a command like this: ** | | > < > > | 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 |
** This command can used for interactive debugging of web pages. For
** example, one can put a simple HTTP request in a file like this:
**
** echo 'GET /timeline' >request.txt
**
** Then run (in a debugger) a command like this:
**
** fossil test-http <request.txt
**
** This command is also used internally by the "ssh" sync protocol. Some
** special processing to support sync happens when this command is run
** 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:
** --csrf-safe N Set cgi_csrf_safe() to to return N
** --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;
const char *zCsrfSafe = find_option("csrf-safe",0,1);
Th_InitTraceLog();
if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
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);
|
| ︙ | ︙ | |||
3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 | ** --cert FILE Use TLS (HTTPS) encryption with the certificate (the ** fullchain.pem) taken from FILE. ** --chroot DIR Use directory for chroot instead of repository path ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were ** /doc/ckout/... ** --create Create a new REPOSITORY if it does not already exist ** --errorlog FILE Append HTTP error messages to FILE ** --extroot DIR Document root for the /ext extension mechanism ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --fossilcmd PATH The pathname of the "fossil" executable on the remote ** system when REPOSITORY is remote. ** --from PATH Use PATH as the diff baseline for the /ckout page ** --localauth Enable automatic login for requests from localhost ** --localhost Listen on 127.0.0.1 only (always true for "ui") | > > > | 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 | ** --cert FILE Use TLS (HTTPS) encryption with the certificate (the ** fullchain.pem) taken from FILE. ** --chroot DIR Use directory for chroot instead of repository path ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were ** /doc/ckout/... ** --create Create a new REPOSITORY if it does not already exist ** --errorlog FILE Append HTTP error messages to FILE ** --extpage FILE Shortcut for "--extroot DIR --page ext/TAIL" where ** DIR is the directory holding FILE and TAIL is the ** filename at the end of FILE. Only works for "ui". ** --extroot DIR Document root for the /ext extension mechanism ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --fossilcmd PATH The pathname of the "fossil" executable on the remote ** system when REPOSITORY is remote. ** --from PATH Use PATH as the diff baseline for the /ckout page ** --localauth Enable automatic login for requests from localhost ** --localhost Listen on 127.0.0.1 only (always true for "ui") |
| ︙ | ︙ | |||
3223 3224 3225 3226 3227 3228 3229 | ** --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. ** | | | 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 |
** --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]] [Windows only]
*/
void cmd_webserver(void){
int iPort, mxPort; /* Range of TCP ports allowed */
const char *zPort; /* Value of the --port option */
const char *zBrowser; /* Name of web browser program */
char *zBrowserCmd = 0; /* Command to launch the web browser */
int isUiCmd; /* True if command is "ui", not "server' */
|
| ︙ | ︙ | |||
3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 | 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 */ const char *zFrom; /* Value for --from */ #if USE_SEE db_setup_for_saved_encryption_key(); #endif #if defined(_WIN32) | > | 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 | 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 */ const char *zFrom; /* Value for --from */ const char *zExtPage = 0; /* Argument to --extpage */ #if USE_SEE db_setup_for_saved_encryption_key(); #endif #if defined(_WIN32) |
| ︙ | ︙ | |||
3291 3292 3293 3294 3295 3296 3297 |
isUiCmd = g.argv[1][0]=='u';
if( isUiCmd ){
zFrom = find_option("from", 0, 1);
if( zFrom && zFrom==file_tail(zFrom) ){
fossil_fatal("the argument to --from must be a pathname for"
" the \"ui\" command");
}
| > > > > > > > | | > | | 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 |
isUiCmd = g.argv[1][0]=='u';
if( isUiCmd ){
zFrom = find_option("from", 0, 1);
if( zFrom && zFrom==file_tail(zFrom) ){
fossil_fatal("the argument to --from must be a pathname for"
" the \"ui\" command");
}
zExtPage = find_option("extpage",0,1);
if( zExtPage ){
char *zFullPath = file_canonical_name_dup(zExtPage);
g.zExtRoot = file_dirname(zFullPath);
zInitPage = mprintf("ext/%s",file_tail(zFullPath));
fossil_free(zFullPath);
}else{
zInitPage = find_option("page", "p", 1);
if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
}
zFossilCmd = find_option("fossilcmd", 0, 1);
if( zFrom && zInitPage==0 ){
zInitPage = mprintf("ckout?exbase=%H", zFrom);
}
}
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;
|
| ︙ | ︙ | |||
3461 3462 3463 3464 3465 3466 3467 |
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);
}
| | > > > > > > > | > | 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 |
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 127.0.0.1:%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( zExtPage ){
if( !file_is_absolute_path(zExtPage) ){
zExtPage = mprintf("%s/%s", g.argv[2], zExtPage);
}
blob_appendf(&ssh, " --extpage %$", zExtPage);
}else 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);
|
| ︙ | ︙ | |||
3583 3584 3585 3586 3587 3588 3589 |
if( g.httpUseSSL && g.httpSSLConn ){
ssl_close_server(g.httpSSLConn);
g.httpSSLConn = 0;
}
#endif /* FOSSIL_ENABLE_SSL */
#else /* WIN32 */
| | < | 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 |
if( g.httpUseSSL && g.httpSSLConn ){
ssl_close_server(g.httpSSLConn);
g.httpSSLConn = 0;
}
#endif /* FOSSIL_ENABLE_SSL */
#else /* WIN32 */
/* Win32 implementation */
if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
allowRepoList = 1;
}
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);
}
|
| ︙ | ︙ | |||
3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 |
** case=1 Issue a fossil_warning() while generating the page.
** case=2 Extra db_begin_transaction()
** case=3 Extra db_end_transaction()
** case=4 Error during SQL processing
** case=5 Call the segfault handler
** case=6 Call webpage_assert()
** case=7 Call webpage_error()
*/
void test_warning_page(void){
int iCase = atoi(PD("case","0"));
int i;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_set_current_feature("test");
style_header("Warning Test Page");
style_submenu_element("Error Log","%R/errorlog");
| > > > | > | | < < < | | 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 |
** case=1 Issue a fossil_warning() while generating the page.
** case=2 Extra db_begin_transaction()
** case=3 Extra db_end_transaction()
** case=4 Error during SQL processing
** case=5 Call the segfault handler
** case=6 Call webpage_assert()
** case=7 Call webpage_error()
** case=8 Simulate a timeout
** case=9 Simulate a TH1 XSS vulnerability
** case=10 Simulate a TH1 SQL-injection vulnerability
*/
void test_warning_page(void){
int iCase = atoi(PD("case","0"));
int i;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_set_current_feature("test");
style_header("Warning Test Page");
style_submenu_element("Error Log","%R/errorlog");
@ <p>This page will generate various kinds of errors to test Fossil's
@ reaction. Depending on settings, a message might be written
@ into the <a href="%R/errorlog">error log</a>. Click on
@ one of the following hyperlinks to generate a simulated error:
for(i=1; i<=10; i++){
@ <a href='./test-warning?case=%d(i)'>[%d(i)]</a>
}
@ </p>
@ <p><ol>
@ <li value='1'> Call fossil_warning()
if( iCase==1 ){
fossil_warning("Test warning message from /test-warning");
|
| ︙ | ︙ | |||
3693 3694 3695 3696 3697 3698 3699 |
if( iCase==5 ){
sigsegv_handler(0);
}
@ <li value='6'> call webpage_assert(0)
if( iCase==6 ){
webpage_assert( 5==7 );
}
| | | > > > > > > > > > > > > > > > > > > > | 3768 3769 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 3808 |
if( iCase==5 ){
sigsegv_handler(0);
}
@ <li value='6'> call webpage_assert(0)
if( iCase==6 ){
webpage_assert( 5==7 );
}
@ <li value='7'> call webpage_error()
if( iCase==7 ){
cgi_reset_content();
webpage_error("Case 7 from /test-warning");
}
@ <li value='8'> simulated timeout
if( iCase==8 ){
fossil_set_timeout(1);
cgi_reset_content();
sqlite3_sleep(1100);
}
@ <li value='9'> simulated TH1 XSS vulnerability
@ <li value='10'> simulated TH1 SQL-injection vulnerability
if( iCase==9 || iCase==10 ){
const char *zR;
int n, rc;
static const char *zTH1[] = {
/* case 9 */ "html [taint {<b>XSS</b>}]",
/* case 10 */ "query [taint {SELECT 'SQL-injection' AS msg}] {\n"
" html \"<b>[htmlize $msg]</b>\"\n"
"}"
};
rc = Th_Eval(g.interp, 0, zTH1[iCase==10], -1);
zR = Th_GetResult(g.interp, &n);
if( rc==TH_OK ){
@ <pre class="th1result">%h(zR)</pre>
}else{
@ <pre class="th1error">%h(zR)</pre>
}
}
@ </ol>
@ <p>End of test</p>
style_finish_page();
}
|
Changes to src/main.mk.
| ︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 107 108 109 110 | $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ | > | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/match.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ |
| ︙ | ︙ | |||
362 363 364 365 366 367 368 369 370 371 372 373 374 375 | $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ | > | 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/match_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ |
| ︙ | ︙ | |||
511 512 513 514 515 516 517 518 519 520 521 522 523 524 | $(OBJDIR)/loadctrl.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/patch.o \ $(OBJDIR)/path.o \ | > | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 | $(OBJDIR)/loadctrl.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/match.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/patch.o \ $(OBJDIR)/path.o \ |
| ︙ | ︙ | |||
573 574 575 576 577 578 579 | $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o | | < < < > > > > > > | | 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 | $(OBJDIR)/wiki.o \ $(OBJDIR)/wikiformat.o \ $(OBJDIR)/winfile.o \ $(OBJDIR)/winhttp.o \ $(OBJDIR)/xfer.o \ $(OBJDIR)/xfersetup.o \ $(OBJDIR)/zip.o all: $(APPNAME) install: all mkdir -p $(INSTALLDIR) cp $(APPNAME) $(INSTALLDIR) codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 $(OBJDIR)/codecheck1 $(TRANS_SRC) $(OBJDIR)/translate: $(SRCDIR_tools)/translate.c -mkdir -p $(OBJDIR) $(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c $(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c -mkdir -p $(OBJDIR) $(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c $(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c -mkdir -p $(OBJDIR) $(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c $(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c -mkdir -p $(OBJDIR) $(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c $(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c -mkdir -p $(OBJDIR) $(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c $(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c -mkdir -p $(OBJDIR) $(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c # Run the test suite. # Other flags that can be included in TESTFLAGS are: # # -halt Stop testing after the first failed test # -keep Keep the temporary workspace for debugging # -prot Write a detailed log of the tests to the file ./prot # -verbose Include even more details in the output # -quiet Hide most output from the terminal # -strict Treat known bugs as failures # # TESTFLAGS can also include names of specific test files to limit # the run to just those test cases. # test: $(APPNAME) $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ $(SRCDIR)/../manifest \ $(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h |
| ︙ | ︙ | |||
642 643 644 645 646 647 648 649 650 |
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_MAX_EXPR_DEPTH=0 \
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_FTS4 \
| > | > | | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 |
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_MAX_EXPR_DEPTH=0 \
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_FTS4 \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_ENABLE_MATH_FUNCTIONS \
-DSQLITE_ENABLE_SETLK_TIMEOUT \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_HAVE_ZLIB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_TRUSTED_SCHEMA=0 \
-DHAVE_USLEEP
# Setup the options used to compile the included SQLite shell.
|
| ︙ | ︙ | |||
667 668 669 670 671 672 673 674 675 |
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_MAX_EXPR_DEPTH=0 \
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_FTS4 \
| > | > | | 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 |
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_MAX_EXPR_DEPTH=0 \
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_FTS4 \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_ENABLE_MATH_FUNCTIONS \
-DSQLITE_ENABLE_SETLK_TIMEOUT \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_HAVE_ZLIB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_TRUSTED_SCHEMA=0 \
-DHAVE_USLEEP \
-Dmain=sqlite3_shell \
-DSQLITE_SHELL_IS_UTF8=1 \
|
| ︙ | ︙ | |||
846 847 848 849 850 851 852 853 854 855 856 857 858 859 | $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ | > | 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/match_.c:$(OBJDIR)/match.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ |
| ︙ | ︙ | |||
1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 | $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c | > > > > > > > > | 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 | $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/match_.c: $(SRCDIR)/match.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/match.c >$@ $(OBJDIR)/match.o: $(OBJDIR)/match_.c $(OBJDIR)/match.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/match.o -c $(OBJDIR)/match_.c $(OBJDIR)/match.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c |
| ︙ | ︙ | |||
2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 | $(OBJDIR)/shell.o: $(SQLITE3_SHELL_SRC) $(SRCDIR_extsrc)/sqlite3.h $(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) $(SEE_FLAGS) $(LINENOISE_DEF.$(USE_LINENOISE)) -c $(SQLITE3_SHELL_SRC) -o $@ $(OBJDIR)/linenoise.o: $(SRCDIR_extsrc)/linenoise.c $(SRCDIR_extsrc)/linenoise.h $(XTCC) -c $(SRCDIR_extsrc)/linenoise.c -o $@ $(OBJDIR)/th.o: $(SRCDIR)/th.c $(XTCC) -c $(SRCDIR)/th.c -o $@ $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c $(XTCC) -c $(SRCDIR)/th_lang.c -o $@ $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c $(XTCC) -c $(SRCDIR)/th_tcl.c -o $@ | > > > > > > | | | | | > | 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 |
$(OBJDIR)/shell.o: $(SQLITE3_SHELL_SRC) $(SRCDIR_extsrc)/sqlite3.h
$(XTCC) $(SHELL_OPTIONS) $(SHELL_CFLAGS) $(SEE_FLAGS) $(LINENOISE_DEF.$(USE_LINENOISE)) -c $(SQLITE3_SHELL_SRC) -o $@
$(OBJDIR)/linenoise.o: $(SRCDIR_extsrc)/linenoise.c $(SRCDIR_extsrc)/linenoise.h
$(XTCC) -c $(SRCDIR_extsrc)/linenoise.c -o $@
$(OBJDIR)/th.o: $(SRCDIR)/th.c
-mkdir -p $(OBJDIR)
$(XTCC) -c $(SRCDIR)/th.c -o $@
$(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
-mkdir -p $(OBJDIR)
$(XTCC) -c $(SRCDIR)/th_lang.c -o $@
$(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c
-mkdir -p $(OBJDIR)
$(XTCC) -c $(SRCDIR)/th_tcl.c -o $@
$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(OBJDIR)/mkversion
$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(OBJDIR)/mkversion
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \
-sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore \
-sEXPORTED_FUNCTIONS=_pikchr,_pikchr_version $(SRCDIR_extsrc)/pikchr.c \
-sENVIRONMENT=web \
-sMODULARIZE \
-sEXPORT_NAME=initPikchrModule \
--minify 0
$(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc)
@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
wasm: $(SRCDIR_extsrc)/pikchr.js
#
# compile_commands.json support...
#
# We have to avoid applying compile_commands support to the in-tree
|
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
316 317 318 319 320 321 322 |
/*
** Remove the PGP signature from the artifact, if there is one.
*/
static void remove_pgp_signature(const char **pz, int *pn){
const char *z = *pz;
int n = *pn;
int i;
| | > > | > | > | 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 |
/*
** Remove the PGP signature from the artifact, if there is one.
*/
static void remove_pgp_signature(const char **pz, int *pn){
const char *z = *pz;
int n = *pn;
int i;
if( strncmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)==0 ) i = 34;
else if( strncmp(z, "-----BEGIN SSH SIGNED MESSAGE-----", 34)==0 ) i = 34;
else return;
for(; i<n && !after_blank_line(z+i); i++){}
if( i>=n ) return;
z += i;
n -= i;
*pz = z;
for(i=n-1; i>=0; i--){
if( z[i]=='\n' &&
(strncmp(&z[i],"\n-----BEGIN PGP SIGNATURE-----", 29)==0
|| strncmp(&z[i],"\n-----BEGIN SSH SIGNATURE-----", 29)==0 )){
n = i+1;
break;
}
}
*pn = n;
return;
}
|
| ︙ | ︙ | |||
2905 2906 2907 2908 2909 2910 2911 |
Blob content;
db_find_and_open_repository(0, 0);
if( g.argc!=3 ) usage("RECORDID");
rid = name_to_rid(g.argv[2]);
content_get(rid, &content);
manifest_crosslink(rid, &content, MC_NONE);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 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 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 |
Blob content;
db_find_and_open_repository(0, 0);
if( g.argc!=3 ) usage("RECORDID");
rid = name_to_rid(g.argv[2]);
content_get(rid, &content);
manifest_crosslink(rid, &content, MC_NONE);
}
/*
** For a given CATYPE_... value, returns a human-friendly name, or
** NULL if typeId is unknown or is CFTYPE_ANY. The names returned by
** this function are geared towards use with artifact_to_json(), and
** may differ from some historical uses. e.g. CFTYPE_CONTROL artifacts
** are called "tag" artifacts by this function.
*/
const char * artifact_type_to_name(int typeId){
switch(typeId){
case CFTYPE_MANIFEST: return "checkin";
case CFTYPE_CLUSTER: return "cluster";
case CFTYPE_CONTROL: return "tag";
case CFTYPE_WIKI: return "wiki";
case CFTYPE_TICKET: return "ticket";
case CFTYPE_ATTACHMENT: return "attachment";
case CFTYPE_EVENT: return "technote";
case CFTYPE_FORUM: return "forumpost";
}
return NULL;
}
/*
** Creates a JSON representation of p, appending it to b.
**
** b is not cleared before rendering, so the caller needs to do that
** if it's important for their use case.
**
** Pedantic note: this routine traverses p->aFile directly, rather
** than using manifest_file_next(), so that delta manifests are
** rendered as-is instead of containing their derived F-cards. If that
** policy is ever changed, p will need to be non-const.
*/
void artifact_to_json(Manifest const *p, Blob *b){
int i;
blob_append_literal(b, "{");
blob_appendf(b, "\"uuid\":\"%z\"", rid_to_uuid(p->rid));
/*blob_appendf(b, ", \"rid\": %d", p->rid); not portable across repos*/
blob_appendf(b, ",\"type\":%!j", artifact_type_to_name(p->type));
#define ISA(TYPE) if( p->type==TYPE )
#define CARD_LETTER(LETTER) \
blob_append_literal(b, ",\"" #LETTER "\":")
#define CARD_STR(LETTER, VAL) \
assert( VAL ); CARD_LETTER(LETTER); blob_appendf(b, "%!j", VAL)
#define CARD_STR2(LETTER, VAL) \
if( VAL ) { CARD_STR(LETTER, VAL); } (void)0
#define STR_OR_NULL(VAL) \
if( VAL ) blob_appendf(b, "%!j", VAL); \
else blob_append(b, "null", 4)
#define KVP_STR(ADDCOMMA, KEY,VAL) \
if(ADDCOMMA) blob_append_char(b, ','); \
blob_appendf(b, "%!j:", #KEY); \
STR_OR_NULL(VAL)
ISA( CFTYPE_ATTACHMENT ){
CARD_LETTER(A);
blob_append_char(b, '{');
KVP_STR(0, filename, p->zAttachName);
KVP_STR(1, target, p->zAttachTarget);
KVP_STR(1, source, p->zAttachSrc);
blob_append_char(b, '}');
}
CARD_STR2(B, p->zBaseline);
CARD_STR2(C, p->zComment);
CARD_LETTER(D); blob_appendf(b, "%f", p->rDate);
ISA( CFTYPE_EVENT ){
blob_appendf(b, ", \"E\":{\"time\":%f,\"id\":%!j}",
p->rEventDate, p->zEventId);
}
ISA( CFTYPE_MANIFEST ){
CARD_LETTER(F);
blob_append_char(b, '[');
for( i = 0; i < p->nFile; ++i ){
ManifestFile const * const pF = &p->aFile[i];
if( i>0 ) blob_append_char(b, ',');
blob_append_char(b, '{');
KVP_STR(0, name, pF->zName);
KVP_STR(1, uuid, pF->zUuid);
KVP_STR(1, perm, pF->zPerm);
KVP_STR(1, rename, pF->zPrior);
blob_append_char(b, '}');
}
/* Special case: model checkins with no F-card as having an empty
** array, rather than no F-cards, to hypothetically simplify
** handling in JSON queries. */
blob_append_char(b, ']');
}
CARD_STR2(G, p->zThreadRoot);
ISA( CFTYPE_FORUM ){
CARD_LETTER(H);
STR_OR_NULL( (p->zThreadTitle && *p->zThreadTitle) ? p->zThreadTitle : NULL);
CARD_STR2(I, p->zInReplyTo);
}
if( p->nField ){
CARD_LETTER(J);
blob_append_char(b, '[');
for( i = 0; i < p->nField; ++i ){
const char * zName = p->aField[i].zName;
if( i>0 ) blob_append_char(b, ',');
blob_append_char(b, '{');
KVP_STR(0, name, '+'==*zName ? &zName[1] : zName);
KVP_STR(1, value, p->aField[i].zValue);
blob_appendf(b, ",\"append\":%s", '+'==*zName ? "true" : "false");
blob_append_char(b, '}');
}
blob_append_char(b, ']');
}
CARD_STR2(K, p->zTicketUuid);
CARD_STR2(L, p->zWikiTitle);
ISA( CFTYPE_CLUSTER ){
CARD_LETTER(M);
blob_append_char(b, '[');
for( i = 0; i < p->nCChild; ++i ){
if( i>0 ) blob_append_char(b, ',');
blob_appendf(b, "%!j", p->azCChild[i]);
}
blob_append_char(b, ']');
}
CARD_STR2(N, p->zMimetype);
ISA( CFTYPE_MANIFEST || p->nParent>0 ){
CARD_LETTER(P);
blob_append_char(b, '[');
for( i = 0; i < p->nParent; ++i ){
if( i>0 ) blob_append_char(b, ',');
blob_appendf(b, "%!j", p->azParent[i]);
}
/* Special case: model checkins with no P-card as having an empty
** array, as per F-cards. */
blob_append_char(b, ']');
}
if( p->nCherrypick ){
CARD_LETTER(Q);
blob_append_char(b, '[');
for( i = 0; i < p->nCherrypick; ++i ){
if( i>0 ) blob_append_char(b, ',');
blob_append_char(b, '{');
blob_appendf(b, "\"type\":\"%c\"", p->aCherrypick[i].zCPTarget[0]);
KVP_STR(1, target, &p->aCherrypick[i].zCPTarget[1]);
KVP_STR(1, base, p->aCherrypick[i].zCPBase);
blob_append_char(b, '}');
}
blob_append_char(b, ']');
}
CARD_STR2(R, p->zRepoCksum);
if( p->nTag ){
CARD_LETTER(T);
blob_append_char(b, '[');
for( i = 0; i < p->nTag; ++i ){
const char *zName = p->aTag[i].zName;
if( i>0 ) blob_append_char(b, ',');
blob_append_char(b, '{');
blob_appendf(b, "\"type\":\"%c\"", *zName);
KVP_STR(1, name, &zName[1]);
KVP_STR(1, target, p->aTag[i].zUuid ? p->aTag[i].zUuid : "*")
/* We could arguably resolve the "*" as null or p's uuid. */;
KVP_STR(1, value, p->aTag[i].zValue);
blob_append_char(b, '}');
}
blob_append_char(b, ']');
}
CARD_STR2(U, p->zUser);
if( p->zWiki || CFTYPE_WIKI==p->type || CFTYPE_FORUM==p->type
|| CFTYPE_EVENT==p->type ){
CARD_LETTER(W);
STR_OR_NULL((p->zWiki && *p->zWiki) ? p->zWiki : NULL);
}
blob_append_literal(b, "}");
#undef CARD_FMT
#undef CARD_LETTER
#undef CARD_STR
#undef CARD_STR2
#undef ISA
#undef KVP_STR
#undef STR_OR_NULL
}
/*
** Convenience wrapper around artifact_to_json() which expects rid to
** be the blob.rid of any artifact type. If it can load a Manifest
** with that rid, it returns rid, else it returns 0.
*/
int artifact_to_json_by_rid(int rid, Blob *pOut){
Manifest * const p = manifest_get(rid, CFTYPE_ANY, 0);
if( p ){
artifact_to_json(p, pOut);
manifest_destroy(p);
}else{
rid = 0;
}
return rid;
}
/*
** Convenience wrapper around artifact_to_json() which accepts any
** artifact name which is legal for symbolic_name_to_rid(). On success
** it returns the rid of the artifact. Returns 0 if no such artifact
** exists and a negative value if the name is ambiguous.
**
** pOut is not cleared before rendering, so the caller needs to do
** that if it's important for their use case.
*/
int artifact_to_json_by_name(const char *zName, Blob *pOut){
const int rid = symbolic_name_to_rid(zName, 0);
return rid>0
? artifact_to_json_by_rid(rid, pOut)
: rid;
}
/*
** SQLite UDF for artifact_to_json(). Its single argument should be
** either an INTEGER (blob.rid value) or a TEXT symbolic artifact
** name, as per symbolic_name_to_rid(). If an artifact is found then
** the result of the UDF is that JSON as a string, else it evaluates
** to NULL.
*/
void artifact_to_json_sql_func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
int rid = 0;
Blob b = empty_blob;
if(1 != argc){
goto error_usage;
}
switch( sqlite3_value_type(argv[0]) ){
case SQLITE_INTEGER:
rid = artifact_to_json_by_rid(sqlite3_value_int(argv[0]), &b);
break;
case SQLITE_TEXT:{
const char * z = (const char *)sqlite3_value_text(argv[0]);
if( z ){
rid = artifact_to_json_by_name(z, &b);
}
break;
}
default:
goto error_usage;
}
if( rid>0 ){
sqlite3_result_text(context, blob_str(&b), blob_size(&b),
SQLITE_TRANSIENT);
blob_reset(&b);
}else{
/* We should arguably error out if rid<0 (ambiguous name) */
sqlite3_result_null(context);
}
return;
error_usage:
sqlite3_result_error(context, "Expecting one argument: blob.rid or "
"artifact symbolic name", -1);
}
/*
** COMMAND: test-artifact-to-json
**
** Usage: %fossil test-artifact-to-json ?-pretty|-p? symbolic-name [...names]
**
** Tests the artifact_to_json() and artifact_to_json_by_name() APIs.
*/
void test_manifest_to_json(void){
int i;
Blob b = empty_blob;
Stmt q;
const int bPretty = find_option("pretty","p",0)!=0;
int nErr = 0;
db_find_and_open_repository(0,0);
db_prepare(&q, "select json_pretty(:json)");
for( i=2; i<g.argc; ++i ){
char const *zName = g.argv[i];
const int rc = artifact_to_json_by_name(zName, &b);
if( rc<=0 ){
++nErr;
fossil_warning("Error reading artifact %Q", zName);
continue;
}else if( bPretty ){
db_bind_blob(&q, ":json", &b);
b.nUsed = 0;
db_step(&q);
db_column_blob(&q, 0, &b);
db_reset(&q);
}
fossil_print("%b\n", &b);
blob_reset(&b);
}
db_finalize(&q);
if( nErr ){
fossil_warning("Error count: %d", nErr);
}
}
|
Changes to src/markdown_html.c.
| ︙ | ︙ | |||
276 277 278 279 280 281 282 |
static void html_table(
struct Blob *ob,
struct Blob *head_row,
struct Blob *rows,
void *opaque
){
INTER_BLOCK(ob);
| | | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
static void html_table(
struct Blob *ob,
struct Blob *head_row,
struct Blob *rows,
void *opaque
){
INTER_BLOCK(ob);
blob_append_literal(ob, "<table class='md-table'>\n");
if( head_row && blob_size(head_row)>0 ){
blob_append_literal(ob, "<thead>\n");
blob_appendb(ob, head_row);
blob_append_literal(ob, "</thead>\n<tbody>\n");
}
if( rows ){
blob_appendb(ob, rows);
|
| ︙ | ︙ | |||
694 695 696 697 698 699 700 |
&& (rPikVar = atof(zPikVar))>=0.1
&& rPikVar<10.0
){
blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar);
}
blob_append(&bSrc, zSrc, nSrc)
/*have to dup input to ensure a NUL-terminated source string */;
| | | 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 |
&& (rPikVar = atof(zPikVar))>=0.1
&& rPikVar<10.0
){
blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar);
}
blob_append(&bSrc, zSrc, nSrc)
/*have to dup input to ensure a NUL-terminated source string */;
pikchr_process(blob_str(&bSrc), pikFlags, ob);
blob_reset(&bSrc);
}
/* Invoked for `...` blocks where there are nSep grave accents in a
** row that serve as the delimiter. According to CommonMark:
**
** * https://spec.commonmark.org/0.29/#fenced-code-blocks
|
| ︙ | ︙ | |||
909 910 911 912 913 914 915 |
blob_set( &context.reqURI, zRU );
#endif
html_renderer.opaque = &context;
if( output_title ) blob_reset(output_title);
blob_reset(output_body);
markdown(output_body, input_markdown, &html_renderer);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
blob_set( &context.reqURI, zRU );
#endif
html_renderer.opaque = &context;
if( output_title ) blob_reset(output_title);
blob_reset(output_body);
markdown(output_body, input_markdown, &html_renderer);
}
/*
** Undo HTML escapes in Blob p. In other words convert:
**
** & -> &
** < -> <
** > -> >
** " -> "
** &#NNN; -> ascii character NNN
*/
void markdown_dehtmlize_blob(Blob *p){
char *z;
unsigned int j, k;
z = p->aData;
for(j=k=0; j<p->nUsed; j++){
char c = z[j];
if( c=='&' ){
if( z[j+1]=='#' && fossil_isdigit(z[j+2]) ){
int n = 3;
int x = z[j+2] - '0';
if( fossil_isdigit(z[j+3]) ){
x = x*10 + z[j+3] - '0';
n++;
if( fossil_isdigit(z[j+4]) ){
x = x*10 + z[j+4] - '0';
n++;
}
}
if( z[j+n]==';' ){
z[k++] = (char)x;
j += n;
}else{
z[k++] = c;
}
}else if( memcmp(&z[j],"<",4)==0 ){
z[k++] = '<';
j += 3;
}else if( memcmp(&z[j],">",4)==0 ){
z[k++] = '>';
j += 3;
}else if( memcmp(&z[j],""",6)==0 ){
z[k++] = '"';
j += 5;
}else if( memcmp(&z[j],"&",5)==0 ){
z[k++] = '&';
j += 4;
}else{
z[k++] = c;
}
}else{
z[k++] = c;
}
}
z[k] = 0;
p->nUsed = k;
}
|
Added src/match.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/*
** Copyright (c) 2007 D. Richard Hipp
**
** 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".)
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code to implement string comparisons using a
** variety of algorithm. The comparison algorithm can be any of:
**
** MS_EXACT The string must exactly match the pattern.
**
** MS_BRLIST The pattern is a space- and/or comma-separated
** list of strings, any one of which may match
** the input string.
**
** MS_GLOB Like BRLIST, except each component of the pattern
** is a GLOB expression.
**
** MS_LIKE Like BRLIST, except each component of the pattern
** is an SQL LIKE expression.
**
** MS_REGEXP Like BRLIST, except each component of the pattern
** is a regular expression.
**
*/
#include "config.h"
#include <string.h>
#include "match.h"
#if INTERFACE
/*
** Types of comparisons that we are able to perform:
*/
typedef enum {
MS_EXACT=1, /* Exact string comparison */
MS_GLOB=2, /* Matches against a list of GLOB patterns. */
MS_LIKE=3, /* Matches against a list of LIKE patterns. */
MS_REGEXP=4, /* Matches against a list of regular expressions. */
MS_BRLIST=5, /* Matches any element of a list */
} MatchStyle;
/*
** The following object represents a precompiled pattern to use for
** string matching.
**
** * Create an instance of this object using match_create().
** * Do comparisons using match_text().
** * Destroy using match_free() when you are done.
**
*/
struct Matcher {
MatchStyle style; /* Which algorithm to use */
int nPattern; /* How many patterns are their */
char **azPattern; /* List of patterns */
ReCompiled **aRe; /* List of compiled regular expressions */
};
#endif /*INTERFACE*/
/*
** Translate a "match style" text name into the MS_* enum value.
** Return eDflt if no match is found.
*/
MatchStyle match_style(const char *zStyle, MatchStyle eDflt){
if( zStyle==0 ) return eDflt;
if( fossil_stricmp(zStyle, "brlist")==0 ) return MS_BRLIST;
if( fossil_stricmp(zStyle, "list")==0 ) return MS_BRLIST;
if( fossil_stricmp(zStyle, "regexp")==0 ) return MS_REGEXP;
if( fossil_stricmp(zStyle, "re")==0 ) return MS_REGEXP;
if( fossil_stricmp(zStyle, "glob")==0 ) return MS_GLOB;
if( fossil_stricmp(zStyle, "like")==0 ) return MS_LIKE;
if( fossil_stricmp(zStyle, "exact")==0 ) return MS_EXACT;
return eDflt;
}
/*
** Create a new Matcher object using the pattern provided.
*/
Matcher *match_create(MatchStyle style, const char *zPat){
char cDel; /* Delimiter character */
int i; /* Loop counter */
Matcher *p; /* The new Matcher to be constructed */
char *zOne; /* One element of the pattern */
if( zPat==0 ) return 0;
p = fossil_malloc( sizeof(*p) );
memset(p, 0, sizeof(*p));
p->style = style;
if( style==MS_EXACT ){
p->nPattern = 1;
p->azPattern = fossil_malloc( sizeof(p->azPattern[0]) );
p->azPattern[0] = fossil_strdup(zPat);
return p;
}
while( 1 ){
/* Skip leading delimiters. */
for( ; fossil_isspace(*zPat) || *zPat==','; ++zPat );
/* Next non-delimiter character determines quoting. */
if( zPat[0]==0 ){
/* Terminate loop at end of string. */
break;
}else if( zPat[0]=='\'' || zPat[0]=='"' ){
/* If word is quoted, prepare to stop at end quote. */
cDel = zPat[0];
++zPat;
}else{
/* If word is not quoted, prepare to stop at delimiter. */
cDel = ',';
}
/* Find the next delimiter character or end of string. */
for( i=0; zPat[i] && zPat[i]!=cDel; ++i ){
/* If delimiter is comma, also recognize spaces as delimiters. */
if( cDel==',' && fossil_isspace(zPat[i]) ){
break;
}
/* In regexp mode, ignore delimiters following backslashes. */
if( style==MS_REGEXP && zPat[i]=='\\' && zPat[i+1] ){
++i;
}
}
/* zOne is a zero-terminated copy of the pattern, without delimiters */
zOne = fossil_strndup(zPat, i);
zPat += i;
if( zPat[0] ) zPat++;
/* Check for regular expression syntax errors. */
if( style==MS_REGEXP ){
ReCompiled *regexp;
const char *zFail = re_compile(®exp, zOne, 0);
if( zFail ){
re_free(regexp);
continue;
}
p->nPattern++;
p->aRe = fossil_realloc(p->aRe, sizeof(p->aRe)*p->nPattern);
p->aRe[p->nPattern-1] = regexp;
fossil_free(zOne);
}else{
p->nPattern++;
p->azPattern = fossil_realloc(p->azPattern, sizeof(char*)*p->nPattern);
p->azPattern[p->nPattern-1] = zOne;
}
}
return p;
}
/*
** Return non-zero (true) if the input string matches the pattern
** described by the matcher.
**
** The return value is really the 1-based index of the particular
** pattern that matched.
*/
int match_text(Matcher *p, const char *zText){
int i;
if( p==0 ){
return zText==0;
}
switch( p->style ){
case MS_BRLIST:
case MS_EXACT: {
for(i=0; i<p->nPattern; i++){
if( strcmp(p->azPattern[i], zText)==0 ) return i+1;
}
break;
}
case MS_GLOB: {
for(i=0; i<p->nPattern; i++){
if( sqlite3_strglob(p->azPattern[i], zText)==0 ) return i+1;
}
break;
}
case MS_LIKE: {
for(i=0; i<p->nPattern; i++){
if( sqlite3_strlike(p->azPattern[i], zText, 0)==0 ) return i+1;
}
break;
}
case MS_REGEXP: {
int nText = (int)strlen(zText);
for(i=0; i<p->nPattern; i++){
if( re_match(p->aRe[i], (const u8*)zText, nText) ) return i+1;
}
break;
}
}
return 0;
}
/*
** Destroy a previously allocated Matcher object.
*/
void match_free(Matcher *p){
int i;
if( p==0 ) return;
if( p->style==MS_REGEXP ){
for(i=0; i<p->nPattern; i++) re_free(p->aRe[i]);
fossil_free(p->aRe);
}else{
for(i=0; i<p->nPattern; i++) fossil_free(p->azPattern[i]);
fossil_free(p->azPattern);
}
memset(p, 0, sizeof(*p));
fossil_free(p);
}
/*
** Quote a tag string by surrounding it with double quotes and preceding
** internal double quotes and backslashes with backslashes.
*/
static const char *tagQuote(
int len, /* Maximum length of zTag, or negative for unlimited */
const char *zTag /* Tag string */
){
Blob blob = BLOB_INITIALIZER;
int i, j;
blob_zero(&blob);
blob_append(&blob, "\"", 1);
for( i=j=0; zTag[j] && (len<0 || j<len); ++j ){
if( zTag[j]=='\"' || zTag[j]=='\\' ){
if( j>i ){
blob_append(&blob, zTag+i, j-i);
}
blob_append(&blob, "\\", 1);
i = j;
}
}
if( j>i ){
blob_append(&blob, zTag+i, j-i);
}
blob_append(&blob, "\"", 1);
return blob_str(&blob);
}
/*
** Construct the SQL expression that goes into the WHERE clause of a join
** that involves the TAG table and that selects a particular tag out of
** that table.
**
** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB,
** MS_LIKE, MS_REGEXP, and MS_BRLIST match styles.
**
** For MS_EXACT, the returned expression
** checks for integer match against the tag ID which is looked up directly by
** this function. For the other modes, the returned SQL expression performs
** string comparisons against the tag names, so it is necessary to join against
** the tag table to access the "tagname" column.
**
** Each pattern is adjusted to to start with "sym-" and be anchored at end.
**
** In MS_REGEXP mode, backslash can be used to protect delimiter characters.
** The backslashes are not removed from the regular expression.
**
** In addition to assembling and returning an SQL expression, this function
** makes an English-language description of the patterns being matched, suitable
** for display in the web interface.
**
** If any errors arise during processing, *zError is set to an error message.
** Otherwise it is set to NULL.
*/
const char *match_tag_sqlexpr(
MatchStyle matchStyle, /* Match style code */
const char *zTag, /* Tag name, match pattern, or pattern list */
const char **zDesc, /* Output expression description string */
const char **zError /* Output error string */
){
Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
Blob desc = BLOB_INITIALIZER; /* English description of match patterns */
Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */
const char *zStart; /* Text at start of expression */
const char *zDelimiter; /* Text between expression terms */
const char *zEnd; /* Text at end of expression */
const char *zPrefix; /* Text before each match pattern */
const char *zSuffix; /* Text after each match pattern */
const char *zIntro; /* Text introducing pattern description */
const char *zPattern = 0; /* Previous quoted pattern */
const char *zFail = 0; /* Current failure message or NULL if okay */
const char *zOr = " or "; /* Text before final quoted pattern */
char cDel; /* Input delimiter character */
int i; /* Input match pattern length counter */
/* Optimize exact matches by looking up the ID in advance to create a simple
* numeric comparison. Bypass the remainder of this function. */
if( matchStyle==MS_EXACT ){
*zDesc = tagQuote(-1, zTag);
return mprintf("(tagid=%d)", db_int(-1,
"SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag));
}
/* Decide pattern prefix and suffix strings according to match style. */
if( matchStyle==MS_GLOB ){
zStart = "(";
zDelimiter = " OR ";
zEnd = ")";
zPrefix = "tagname GLOB 'sym-";
zSuffix = "'";
zIntro = "glob pattern ";
}else if( matchStyle==MS_LIKE ){
zStart = "(";
zDelimiter = " OR ";
zEnd = ")";
zPrefix = "tagname LIKE 'sym-";
zSuffix = "'";
zIntro = "SQL LIKE pattern ";
}else if( matchStyle==MS_REGEXP ){
zStart = "(tagname REGEXP '^sym-(";
zDelimiter = "|";
zEnd = ")$')";
zPrefix = "";
zSuffix = "";
zIntro = "regular expression ";
}else/* if( matchStyle==MS_BRLIST )*/{
zStart = "tagname IN ('sym-";
zDelimiter = "','sym-";
zEnd = "')";
zPrefix = "";
zSuffix = "";
zIntro = "";
}
/* Convert the list of matches into an SQL expression and text description. */
blob_zero(&expr);
blob_zero(&desc);
blob_zero(&err);
while( 1 ){
/* Skip leading delimiters. */
for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
/* Next non-delimiter character determines quoting. */
if( !*zTag ){
/* Terminate loop at end of string. */
break;
}else if( *zTag=='\'' || *zTag=='"' ){
/* If word is quoted, prepare to stop at end quote. */
cDel = *zTag;
++zTag;
}else{
/* If word is not quoted, prepare to stop at delimiter. */
cDel = ',';
}
/* Find the next delimiter character or end of string. */
for( i=0; zTag[i] && zTag[i]!=cDel; ++i ){
/* If delimiter is comma, also recognize spaces as delimiters. */
if( cDel==',' && fossil_isspace(zTag[i]) ){
break;
}
/* In regexp mode, ignore delimiters following backslashes. */
if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){
++i;
}
}
/* Check for regular expression syntax errors. */
if( matchStyle==MS_REGEXP ){
ReCompiled *regexp;
char *zTagDup = fossil_strndup(zTag, i);
zFail = re_compile(®exp, zTagDup, 0);
re_free(regexp);
fossil_free(zTagDup);
}
/* Process success and error results. */
if( !zFail ){
/* Incorporate the match word into the output expression. %q is used to
* protect against SQL injection attacks by replacing ' with ''. */
blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart,
zPrefix, i, zTag, zSuffix);
/* Build up the description string. */
if( !blob_size(&desc) ){
/* First tag: start with intro followed by first quoted tag. */
blob_append(&desc, zIntro, -1);
blob_append(&desc, tagQuote(i, zTag), -1);
}else{
if( zPattern ){
/* Third and subsequent tags: append comma then previous tag. */
blob_append(&desc, ", ", 2);
blob_append(&desc, zPattern, -1);
zOr = ", or ";
}
/* Second and subsequent tags: store quoted tag for next iteration. */
zPattern = tagQuote(i, zTag);
}
}else{
/* On error, skip the match word and build up the error message buffer. */
if( !blob_size(&err) ){
blob_append(&err, "Error: ", 7);
}else{
blob_append(&err, ", ", 2);
}
blob_appendf(&err, "(%s%s: %s)", zIntro, tagQuote(i, zTag), zFail);
}
/* Advance past all consumed input characters. */
zTag += i;
if( cDel!=',' && *zTag==cDel ){
++zTag;
}
}
/* Finalize and extract the pattern description. */
if( zPattern ){
blob_append(&desc, zOr, -1);
blob_append(&desc, zPattern, -1);
}
*zDesc = blob_str(&desc);
/* Finalize and extract the error text. */
*zError = blob_size(&err) ? blob_str(&err) : 0;
/* Finalize and extract the SQL expression. */
if( blob_size(&expr) ){
blob_append(&expr, zEnd, -1);
return blob_str(&expr);
}
/* If execution reaches this point, the pattern was empty. Return NULL. */
return 0;
}
|
Changes to src/merge.c.
| ︙ | ︙ | |||
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 |
/*
** Bring up a Tcl/Tk GUI to show details of the most recent merge.
*/
static void merge_info_tk(int bDark, int bAll, int nContext){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
zTclsh = find_option("tclsh",0,1);
if( zTclsh==0 ){
zTclsh = db_get("tclsh",0);
}
/* The undocumented --script FILENAME option causes the Tk script to
** be written into the FILENAME instead of being run. This is used
** for testing and debugging. */
zTempFile = find_option("script",0,1);
verify_all_options();
blob_zero(&script);
blob_appendf(&script, "set ncontext %d\n", nContext);
blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n",
g.nameOfExe);
blob_appendf(&script, "set filelist [list");
if( g.argc==2 ){
/* No files named on the command-line. Use every file mentioned
** in the MERGESTAT table to generate the file list. */
Stmt q;
| > > > | 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 |
/*
** Bring up a Tcl/Tk GUI to show details of the most recent merge.
*/
static void merge_info_tk(int bDark, int bAll, int nContext){
int i;
Blob script;
const char *zTempFile = 0;
int bDebug;
char *zCmd;
const char *zTclsh;
zTclsh = find_option("tclsh",0,1);
if( zTclsh==0 ){
zTclsh = db_get("tclsh",0);
}
/* The undocumented --script FILENAME option causes the Tk script to
** be written into the FILENAME instead of being run. This is used
** for testing and debugging. */
zTempFile = find_option("script",0,1);
bDebug = find_option("tkdebug",0,0)!=0;
verify_all_options();
blob_zero(&script);
blob_appendf(&script, "set ncontext %d\n", nContext);
blob_appendf(&script, "set fossilexe {\"%/\"}\n", g.nameOfExe);
blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n",
g.nameOfExe);
blob_appendf(&script, "set filelist [list");
if( g.argc==2 ){
/* No files named on the command-line. Use every file mentioned
** in the MERGESTAT table to generate the file list. */
Stmt q;
|
| ︙ | ︙ | |||
91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
fossil_free(zOp);
blob_append_tcl_literal(&script, zTreename, (int)strlen(zTreename));
blob_reset(&fname);
}
}
blob_appendf(&script, "]\n");
blob_appendf(&script, "set darkmode %d\n", bDark!=0);
blob_appendf(&script, "%s", builtin_file("merge.tcl", 0));
if( zTempFile ){
blob_write_to_file(&script, zTempFile);
fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile);
}else{
#if defined(FOSSIL_ENABLE_TCL)
Th_FossilInit(TH_INIT_DEFAULT);
| > | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
fossil_free(zOp);
blob_append_tcl_literal(&script, zTreename, (int)strlen(zTreename));
blob_reset(&fname);
}
}
blob_appendf(&script, "]\n");
blob_appendf(&script, "set darkmode %d\n", bDark!=0);
blob_appendf(&script, "set debug %d\n", bDebug!=0);
blob_appendf(&script, "%s", builtin_file("merge.tcl", 0));
if( zTempFile ){
blob_write_to_file(&script, zTempFile);
fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile);
}else{
#if defined(FOSSIL_ENABLE_TCL)
Th_FossilInit(TH_INIT_DEFAULT);
|
| ︙ | ︙ | |||
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
* could not be found by the loaded Tcl, or that Tcl cannot be loaded
* dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
* to using the external "tclsh", if available.
*/
#endif
zTempFile = write_blob_to_temp_file(&script);
zCmd = mprintf("%$ %$", zTclsh, zTempFile);
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
blob_reset(&script);
}
/*
** Generate a TCL list on standard output that can be fed into the
** merge.tcl script to show the details of the most recent merge
** command associated with file "zFName". zFName must be the filename
** relative to the root of the check-in - in other words a "tree name".
**
** When this routine is called, we know that the mergestat table
** exists, but we do not know if zFName is mentioned in that table.
*/
| > > > > > > > > > > > | | 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 |
* could not be found by the loaded Tcl, or that Tcl cannot be loaded
* dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
* to using the external "tclsh", if available.
*/
#endif
zTempFile = write_blob_to_temp_file(&script);
zCmd = mprintf("%$ %$", zTclsh, zTempFile);
if( bDebug ){
fossil_print("%s\n", zCmd);
fflush(stdout);
}
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
blob_reset(&script);
}
/*
** Generate a TCL list on standard output that can be fed into the
** merge.tcl script to show the details of the most recent merge
** command associated with file "zFName". zFName must be the filename
** relative to the root of the check-in - in other words a "tree name".
**
** When this routine is called, we know that the mergestat table
** exists, but we do not know if zFName is mentioned in that table.
**
** The diffMode variable has these values:
**
** 0 Standard 3-way diff
** 12 2-way diff between baseline and local
** 13 2-way diff between baseline and merge-in
** 23 2-way diff between local and merge-in
*/
static void merge_info_tcl(const char *zFName, int nContext, int diffMode){
const char *zTreename;/* Name of the file in the tree */
Stmt q; /* To query the MERGESTAT table */
MergeBuilder mb; /* The merge builder object */
Blob pivot,v1,v2,out; /* Blobs for holding content */
const char *zFN; /* A filename */
int rid; /* RID value */
int sz; /* File size value */
|
| ︙ | ︙ | |||
153 154 155 156 157 158 159 |
db_finalize(&q);
fossil_print("ERROR {don't know anything about file: %s}\n", zTreename);
return;
}
mergebuilder_init_tcl(&mb);
mb.nContext = nContext;
| > > | | | | | < | | | | | | | > > > | | | | | < | | | | | | | > > > | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > | | | | | | | < | > > > > > > > > > > > > > > > | > > > > > | > > > > | > > | > > | 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 |
db_finalize(&q);
fossil_print("ERROR {don't know anything about file: %s}\n", zTreename);
return;
}
mergebuilder_init_tcl(&mb);
mb.nContext = nContext;
blob_zero(&pivot);
if( diffMode!=23 ){
/* Set up the pivot or baseline */
zFN = db_column_text(&q, 0);
if( zFN==0 ){
/* No pivot because the file was added */
mb.zPivot = "(no baseline)";
}else{
mb.zPivot = mprintf("%s (baseline)", file_tail(zFN));
rid = db_column_int(&q, 1);
content_get(rid, &pivot);
}
mb.pPivot = &pivot;
}
blob_zero(&v2);
if( diffMode!=12 ){
/* Set up the merge-in as V2 */
zFN = db_column_text(&q, 5);
if( zFN==0 ){
/* File deleted in the merged-in branch */
mb.zV2 = "(deleted file)";
}else{
mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
rid = db_column_int(&q, 6);
content_get(rid, &v2);
}
mb.pV2 = &v2;
}
blob_zero(&v1);
if( diffMode!=13 ){
/* Set up the local content as V1 */
zFN = db_column_text(&q, 2);
if( zFN==0 ){
/* File added by merge */
mb.zV1 = "(no original)";
}else{
mb.zV1 = mprintf("%s (local)", file_tail(zFN));
rid = db_column_int(&q, 3);
sz = db_column_int(&q, 4);
if( rid==0 && sz>0 ){
/* The origin file had been edited so we'll have to pull its
** original content out of the undo buffer */
Stmt q2;
db_prepare(&q2,
"SELECT content FROM undo"
" WHERE pathname=%Q AND octet_length(content)=%d",
zFN, sz
);
blob_zero(&v1);
if( db_step(&q2)==SQLITE_ROW ){
db_column_blob(&q2, 0, &v1);
}else{
mb.zV1 = "(local content missing)";
}
db_finalize(&q2);
}else{
/* The origin file was unchanged when the merge first occurred */
content_get(rid, &v1);
}
}
mb.pV1 = &v1;
}
blob_zero(&out);
if( diffMode==0 ){
/* Set up the output and do a 3-way diff */
zFN = db_column_text(&q, 7);
if( zFN==0 ){
mb.zOut = "(Merge Result)";
}else{
mb.zOut = mprintf("%s (after merge)", file_tail(zFN));
}
mb.pOut = &out;
merge_three_blobs(&mb);
}else{
/* Set up to do a two-way diff */
Blob *pLeft, *pRight;
const char *zTagLeft, *zTagRight;
DiffConfig cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.diffFlags = DIFF_TCL;
cfg.nContext = mb.nContext;
if( diffMode==12 || diffMode==13 ){
pLeft = &pivot;
zTagLeft = "baseline";
}else{
pLeft = &v1;
zTagLeft = "local";
}
if( diffMode==12 ){
pRight = &v1;
zTagRight = "local";
}else{
pRight = &v2;
zTagRight = "merge-in";
}
cfg.azLabel[0] = mprintf("%s (%s)", zFName, zTagLeft);
cfg.azLabel[1] = mprintf("%s (%s)", zFName, zTagRight);
diff_print_filenames("", "", &cfg, &out);
text_diff(pLeft, pRight, &out, &cfg);
fossil_free((char*)cfg.azLabel[0]);
fossil_free((char*)cfg.azLabel[1]);
}
blob_write_to_file(&out, "-");
mb.xDestroy(&mb);
blob_reset(&pivot);
blob_reset(&v1);
blob_reset(&v2);
blob_reset(&out);
db_finalize(&q);
}
|
| ︙ | ︙ | |||
248 249 250 251 252 253 254 255 256 257 258 | ** -a|--all Show all file changes that happened because of ** the merge. Normally only MERGE, CONFLICT, and ERROR ** lines are shown ** -c|--context N Show N lines of context around each change, ** with negative N meaning show all content. Only ** meaningful in combination with --tcl or --tk. ** --dark Use dark mode for the Tcl/Tk-based GUI ** --tcl FILE Generate (to stdout) a TCL list containing ** information needed to display the changes to ** FILE caused by the most recent merge. FILE must ** be a pathname relative to the root of the check-out. | > > > > > > > > < < > > > > > | | > | > | | < > > > | | > | > > > > > > > > > > > > > > | 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 |
** -a|--all Show all file changes that happened because of
** the merge. Normally only MERGE, CONFLICT, and ERROR
** lines are shown
** -c|--context N Show N lines of context around each change,
** with negative N meaning show all content. Only
** meaningful in combination with --tcl or --tk.
** --dark Use dark mode for the Tcl/Tk-based GUI
** --tk Bring up a Tcl/Tk GUI that shows the changes
** associated with the most recent merge.
**
** Options used internally by --tk:
** --diff12 FILE Bring up a separate --tk diff for just the baseline
** and local variants of FILE.
** --diff13 FILE Like --diff12 but for baseline versus merge-in
** --diff23 FILE Like --diff12 but for local versus merge-in
** --tcl FILE Generate (to stdout) a TCL list containing
** information needed to display the changes to
** FILE caused by the most recent merge. FILE must
** be a pathname relative to the root of the check-out.
**
** Debugging options available only when --tk is used:
** --tkdebug Show sub-commands run to implement --tk
** --script FILE Write script used to implement --tk into FILE
*/
void merge_info_cmd(void){
const char *zCnt;
const char *zTcl;
int bTk;
int bDark;
int bAll;
int nContext;
Stmt q;
const char *zWhere;
int cnt = 0;
const char *zDiff2 = 0;
int diffMode = 0;
db_must_be_within_tree();
bTk = find_option("tk", 0, 0)!=0;
zTcl = find_option("tcl", 0, 1);
zCnt = find_option("context", "c", 1);
bDark = find_option("dark", 0, 0)!=0;
bAll = find_option("all", "a", 0)!=0;
if( (zDiff2 = find_option("diff12", 0, 1))!=0 ){
diffMode = 12;
}else
if( (zDiff2 = find_option("diff13", 0, 1))!=0 ){
diffMode = 13;
}else
if( (zDiff2 = find_option("diff23", 0, 1))!=0 ){
diffMode = 23;
}
if( zCnt ){
nContext = atoi(zCnt);
if( nContext<0 ) nContext = 0xfffffff;
}else{
nContext = 6;
}
if( !db_table_exists("localdb","mergestat") ){
if( zTcl ){
fossil_print("ERROR {no merge data available}\n");
}else{
fossil_print("No merge data is available\n");
}
return;
}
if( bTk ){
merge_info_tk(bDark, bAll, nContext);
return;
}
if( zTcl ){
if( diffMode ) zTcl = zDiff2;
merge_info_tcl(zTcl, nContext, diffMode);
return;
}
if( diffMode ){
char *zCmd;
zCmd = mprintf("merge-info --diff%d %!$ -c %d%s",
diffMode, zDiff2, nContext, bDark ? " --dark" : "");
diff_tk(zCmd, g.argc);
fossil_free(zCmd);
return;
}
verify_all_options();
if( g.argc>2 ){
usage("[OPTIONS]");
}
if( bAll ){
zWhere = "";
}else{
zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
}
db_prepare(&q,
"WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
|
| ︙ | ︙ |
Changes to src/merge.tcl.
1 2 3 4 | # Show details of a 3-way merge operation. The left-most column is the # common ancestor. The next two columns are edits of that common ancestor. # The right-most column is the result of the merge. # | | < > | > > > > > > > > < < < < | 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 |
# Show details of a 3-way merge operation. The left-most column is the
# common ancestor. The next two columns are edits of that common ancestor.
# The right-most column is the result of the merge.
#
# Several variables will have been initialized:
#
# ncontext The number of lines of context to show on each change
#
# fossilexe Pathname of the fossil program
#
# filelist A list of "merge-type filename" pairs.
#
# darkmode Boolean. True for dark mode
#
# debug Boolean. True for debugging output
#
# If the "filelist" global variable is defined, then it is a list of
# alternating "merge-type names" (ex: UPDATE, MERGE, CONFLICT, ERROR) and
# filenames. In that case, the initial display shows the changes for
# the first pair on the list and there is a optionmenu that allows the
# user to select other fiels on the list.
#
# This header comment is stripped off by the "mkbuiltin.c" program.
#
package require Tk
array set CFG_light {
TITLE {Fossil Merge}
LN_COL_BG #dddddd
|
| ︙ | ︙ | |||
91 92 93 94 95 96 97 |
proc colType {c} {
regexp {[a-z]+} $c type
return $type
}
proc readMerge {args} {
| | | | | > > > > > | 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 |
proc colType {c} {
regexp {[a-z]+} $c type
return $type
}
proc readMerge {args} {
global fossilexe ncontext current_file debug
if {$ncontext=="All"} {
set cmd "| $fossilexe merge-info -c -1"
} else {
set cmd "| $fossilexe merge-info -c $ncontext"
}
if {[info exists current_file]} {
regsub {^[A-Z]+ } $current_file {} fn
lappend cmd -tcl $fn
}
if {$debug} {
regsub {^\| +} $cmd {} cmd2
puts $cmd2
flush stdout
}
if {[catch {
set in [open $cmd r]
fconfigure $in -encoding utf-8
set mergetxt [read $in]
close $in
} msg]} {
|
| ︙ | ︙ | |||
335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
End y {moveto 1}
} {
bind . <$key> "scroll-$axis $args; break"
bind . <Shift-$key> continue
}
frame .bb
set useOptionMenu 1
if {[info exists filelist]} {
set current_file "[lindex $filelist 0] [lindex $filelist 1]"
if {[llength $filelist]>2} {
trace add variable current_file write readMerge
if {$tcl_platform(os)=="Darwin" || [llength $filelist]<30} {
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
End y {moveto 1}
} {
bind . <$key> "scroll-$axis $args; break"
bind . <Shift-$key> continue
}
frame .bb
::ttk::menubutton .bb.diff2 -text {2-way diff} -menu .bb.diff2.m
menu .bb.diff2.m -tearoff 0
.bb.diff2.m add command -label {baseline vs. local} -command {two-way 12}
.bb.diff2.m add command -label {baseline vs. merge-in} -command {two-way 13}
.bb.diff2.m add command -label {local vs. merge-in} -command {two-way 23}
# Bring up a separate two-way diff between a pair of columns
# the argument is one of:
# 12 Baseline versus Local
# 13 Baseline versus Merge-in
# 23 Local versus Merge-in
#
proc two-way {mode} {
global current_file fossilexe debug darkmode ncontext
regsub {^[A-Z]+ } $current_file {} fn
set cmd $fossilexe
lappend cmd merge-info --diff$mode $fn -c $ncontext
if {$darkmode} {
lappend cmd --dark
}
if {$debug} {
lappend cmd --tkdebug
puts $cmd
flush stdout
}
exec {*}$cmd &
}
set useOptionMenu 1
if {[info exists filelist]} {
set current_file "[lindex $filelist 0] [lindex $filelist 1]"
if {[llength $filelist]>2} {
trace add variable current_file write readMerge
if {$tcl_platform(os)=="Darwin" || [llength $filelist]<30} {
|
| ︙ | ︙ | |||
572 573 574 575 576 577 578 |
$w tag add search search "$idx +$count chars"
$w tag config search -background {#fcc000}
}
set ::search $w
}
::ttk::button .bb.quit -text {Quit} -command exit
::ttk::button .bb.search -text {Search} -command searchOnOff
| | > | | | | 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 |
$w tag add search search "$idx +$count chars"
$w tag config search -background {#fcc000}
}
set ::search $w
}
::ttk::button .bb.quit -text {Quit} -command exit
::ttk::button .bb.search -text {Search} -command searchOnOff
pack .bb.quit -side left -fill y
pack .bb.diff2 -side left -fill y
if {[winfo exists .bb.files]} {
pack .bb.files -side left -fill y
}
pack .bb.ctxtag .bb.ctx -side left -fill y
pack .bb.search -side left -fill y
grid rowconfigure . 1 -weight 1 -minsize [winfo reqheight .nameA]
grid rowconfigure . 2 -weight 100
readMerge
grid .bb -row 0 -columnspan 8
grid .nameA -row 1 -column 1 -sticky ew
grid .nameB -row 1 -column 3 -sticky ew
grid .nameC -row 1 -column 5 -sticky ew
|
| ︙ | ︙ |
Changes to src/name.c.
| ︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 | /* ** Check to see if the string might be a compact date/time that omits ** the punctuation. Example: "20190327084549" instead of ** "2019-03-27 08:45:49". If the string is of the appropriate form, ** then return an alternative string (in static space) that is the same ** string with punctuation inserted. ** ** If the bVerifyNotAHash flag is true, then a check is made to see if | > > > > > > > > > > > > > | | | > | > > | | | > > > > > > | > > | | > > > > > > > > > > > > > > > | 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 |
/*
** Check to see if the string might be a compact date/time that omits
** the punctuation. Example: "20190327084549" instead of
** "2019-03-27 08:45:49". If the string is of the appropriate form,
** then return an alternative string (in static space) that is the same
** string with punctuation inserted.
**
** If the bRoundUp parameter is true, then round the resulting date-time
** up to the largest date/time that is consistent with the input value.
** This is because the result will be used for an mtime<=julianday($DATE)
** comparison. In other words:
**
** 20250317123421 -> 2025-03-17 12:34:21.999
** ^^^^--- Added
**
** 202503171234 -> 2025-03-17 12:34:59.999
** ^^^^^^^--- Added
** 20250317 -> 2025-03-17 23:59:59.999
** ^^^^^^^^^^^^^--- Added
**
** If the bVerifyNotAHash flag is true, then a check is made to see if
** the input string is a hash prefix and NULL is returned if it is. If the
** bVerifyNotAHash flag is false, then the result is determined by syntax
** of the input string only, without reference to the artifact table.
*/
char *fossil_expand_datetime(const char *zIn,int bVerifyNotAHash,int bRoundUp){
static char zEDate[24];
static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
int n = (int)strlen(zIn);
int i, j;
int addZulu = 0;
/* These forms are allowed:
**
** 123456789 1234 123456789 123456789 1234
** (1) YYYYMMDD => YYYY-MM-DD 23:59:59.999
** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM:59.999
** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS.999
**
** An optional "Z" zulu timezone designator is allowed at the end.
*/
if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
n--;
addZulu = 1;
}
if( n!=8 && n!=12 && n!=14 ){
return 0;
}
/* Every character must be a digit */
for(i=0; fossil_isdigit(zIn[i]); i++){}
if( i!=n && (!addZulu || i!=n+1) ) return 0;
/* Expand the date */
for(i=j=0; i<n; i++){
if( i>=4 && (i%2)==0 ){
zEDate[j++] = aPunct[i/2];
}
zEDate[j++] = zIn[i];
}
if( bRoundUp ){
if( j==10 ){
memcpy(&zEDate[10], " 23:59:59.999", 13);
j += 13;
}else if( j==16 ){
memcpy(&zEDate[16], ":59.999",7);
j += 7;
}else if( j==19 ){
memcpy(&zEDate[19], ".999", 4);
j += 4;
}
}
if( addZulu ){
zEDate[j++] = 'Z';
}
zEDate[j] = 0;
/* Check for reasonable date values.
** Offset references:
** YYYY-MM-DD HH:MM:SS
** 0123456789 12345678
*/
|
| ︙ | ︙ | |||
109 110 111 112 113 114 115 |
if( i>24 ) return 0;
i = atoi(zEDate+14);
if( i>60 ) return 0;
if( n==14 && atoi(zEDate+17)>60 ) return 0;
}
/* The string is not also a hash prefix */
| | > > > > > | > > > > > | > | > | > | 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 |
if( i>24 ) return 0;
i = atoi(zEDate+14);
if( i>60 ) return 0;
if( n==14 && atoi(zEDate+17)>60 ) return 0;
}
/* The string is not also a hash prefix */
if( bVerifyNotAHash && !addZulu ){
if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'",zIn) ) return 0;
}
/* It looks like this may be a date. Return it with punctuation added. */
return zEDate;
}
/*
** The data-time string in the argument is going to be used as an
** upper bound like this: mtime<=julianday(zDate,'localtime').
** But if the zDate parameter omits the fractional seconds or the
** seconds, or the time, that might mess up the == part of the
** comparison. So add in missing factional seconds or seconds or time.
**
** The returned string is held in a static buffer that is overwritten
** with each call, or else is just a copy of its input if there are
** no changes.
**
** For reference:
**
** 0123456789 123456789 1234
** YYYY-MM-DD HH:MM:SS.SSSz
*/
const char *fossil_roundup_date(const char *zDate){
static char zUp[28];
int n = (int)strlen(zDate);
int addZ = 0;
if( n>10 && (zDate[n-1]=='z' || zDate[n-1]=='Z') ){
n--;
addZ = 1;
}
if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */
memcpy(zUp, zDate, 19);
memcpy(zUp+19, ".999z", 6);
if( !addZ ) zUp[23] = 0;
return zUp;
}
if( n==16 ){ /* YYYY-MM-DD HH:MM */
memcpy(zUp, zDate, 16);
memcpy(zUp+16, ":59.999z", 8);
if( !addZ ) zUp[23] = 0;
return zUp;
}
if( n==10 ){ /* YYYY-MM-DD */
memcpy(zUp, zDate, 10);
memcpy(zUp+10, " 23:59:59.999z", 14);
if( !addZ ) zUp[23] = 0;
return zUp;
}
return zDate;
}
/*
|
| ︙ | ︙ | |||
211 212 213 214 215 216 217 | ** 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) | | | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
** 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 shown below as Q2.
*/
static int most_recent_event_with_tag(const char *zTag, const char *zType){
return db_int(0,
"SELECT objid FROM ("
/* Q1: Begin by looking for the tag in the 30 most recent events */
"SELECT objid"
" FROM (SELECT * FROM event ORDER BY mtime DESC LIMIT 30) AS ex"
|
| ︙ | ︙ | |||
451 452 453 454 455 456 457 458 459 460 461 462 463 |
rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim",
ridCkout);
}else if( fossil_strcmp(zTag, "next")==0 ){
rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
" ORDER BY isprim DESC, mtime DESC", ridCkout);
}else if( isCheckin>1 && fossil_strcmp(zTag, "ckout")==0 ){
rid = RID_CKOUT;
}
if( rid ) return rid;
}
/* Date and times */
if( memcmp(zTag, "date:", 5)==0 ){
| > > | | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim",
ridCkout);
}else if( fossil_strcmp(zTag, "next")==0 ){
rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
" ORDER BY isprim DESC, mtime DESC", ridCkout);
}else if( isCheckin>1 && fossil_strcmp(zTag, "ckout")==0 ){
rid = RID_CKOUT;
assert(ridCkout>0);
g.localOpen = ridCkout;
}
if( rid ) return rid;
}
/* Date and times */
if( memcmp(zTag, "date:", 5)==0 ){
zDate = fossil_expand_datetime(&zTag[5],0,1);
if( zDate==0 ) zDate = &zTag[5];
rid = db_int(0,
"SELECT objid FROM event"
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
" ORDER BY mtime DESC LIMIT 1",
fossil_roundup_date(zDate), zType);
return rid;
|
| ︙ | ︙ | |||
523 524 525 526 527 528 529 |
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]==':'
| | | | 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 |
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)!=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 ){
zDate[nDate-3] = 'z';
zDate[nDate-2] = 0;
}
zXDate = fossil_expand_datetime(zDate,0,1);
if( zXDate==0 ) zXDate = zDate;
rid = db_int(0,
"SELECT event.objid, max(event.mtime)"
" FROM tag, tagxref, event"
" WHERE tag.tagname='sym-%q' "
" AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
" AND event.objid=tagxref.rid "
|
| ︙ | ︙ | |||
609 610 611 612 613 614 615 |
if( rid>0 ){
if( startOfBranch ) rid = start_of_branch(rid,1);
return rid;
}
/* Pure numeric date/time */
| | | 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 |
if( rid>0 ){
if( startOfBranch ) rid = start_of_branch(rid,1);
return rid;
}
/* Pure numeric date/time */
zDate = fossil_expand_datetime(zTag, 0,1);
if( zDate ){
rid = db_int(0,
"SELECT objid FROM event"
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
" ORDER BY mtime DESC LIMIT 1",
fossil_roundup_date(zDate), zType);
if( rid) return rid;
|
| ︙ | ︙ | |||
662 663 664 665 666 667 668 669 670 671 672 673 674 675 |
){
char *zNew = fossil_strndup(zTag, nTag-2);
rid = symbolic_name_to_rid(zNew,zType);
fossil_free(zNew);
}
return rid;
}
/*
** This routine takes a user-entered string and tries to convert it to
** an artifact hash.
**
** We first try to treat the string as an artifact hash, or at least a
** unique prefix of an artifact hash. The input may be in mixed case.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
){
char *zNew = fossil_strndup(zTag, nTag-2);
rid = symbolic_name_to_rid(zNew,zType);
fossil_free(zNew);
}
return rid;
}
/*
** Convert a symbolic name used as an argument to the a=, b=, or c=
** query parameters of timeline into a julianday mtime value.
**
** If pzDisplay is not null, then display text for the symbolic name might
** be written into *pzDisplay. But that is not guaranteed.
**
** If bRoundUp is true and the symbolic name is a timestamp with less
** than millisecond resolution, then the timestamp is rounding up to the
** largest millisecond consistent with that timestamp. If bRoundUp is
** false, then the resulting time is obtained by extending the timestamp
** with zeros (hence rounding down). Use bRoundUp==1 if the result
** will be used in mtime<=$RESULT and use bRoundUp==0 if the result
** will be used in mtime>=$RESULT.
*/
double symbolic_name_to_mtime(
const char *z, /* Input symbolic name */
const char **pzDisplay, /* Perhaps write display text here, if not NULL */
int bRoundUp /* Round up if true */
){
double mtime;
int rid;
const char *zDate;
if( z==0 ) return -1.0;
if( fossil_isdate(z) ){
mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
if( mtime>0.0 ) return mtime;
}
zDate = fossil_expand_datetime(z, 1, bRoundUp);
if( zDate!=0 ){
mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
bRoundUp ? fossil_roundup_date(zDate) : zDate);
if( mtime>0.0 ){
if( pzDisplay ){
zDate = fossil_expand_datetime(z,0,0);
*pzDisplay = fossil_strdup(zDate);
}
return mtime;
}
}
rid = symbolic_name_to_rid(z, "*");
if( rid ){
mtime = mtime_of_rid(rid, 0.0);
}else{
mtime = db_double(-1.0,
"SELECT max(event.mtime) FROM event, tag, tagxref"
" WHERE tag.tagname GLOB 'event-%q*'"
" AND tagxref.tagid=tag.tagid AND tagxref.tagtype"
" AND event.objid=tagxref.rid",
z
);
}
return mtime;
}
/*
** This routine takes a user-entered string and tries to convert it to
** an artifact hash.
**
** We first try to treat the string as an artifact hash, or at least a
** unique prefix of an artifact hash. The input may be in mixed case.
|
| ︙ | ︙ | |||
697 698 699 700 701 702 703 704 705 706 707 708 709 710 |
fossil_error(iErrPriority, "ambiguous name: %s", zName);
return 2;
}else if( rid==0 ){
fossil_error(iErrPriority, "cannot resolve name: %s", zName);
return 1;
}else{
blob_reset(pName);
db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
return 0;
}
}
/*
** This routine is similar to name_to_uuid() except in the form it
| > > > | 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 |
fossil_error(iErrPriority, "ambiguous name: %s", zName);
return 2;
}else if( rid==0 ){
fossil_error(iErrPriority, "cannot resolve name: %s", zName);
return 1;
}else{
blob_reset(pName);
if( RID_CKOUT==rid ) {
rid = g.localOpen;
}
db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
return 0;
}
}
/*
** This routine is similar to name_to_uuid() except in the form it
|
| ︙ | ︙ | |||
1109 1110 1111 1112 1113 1114 1115 |
cnt++;
}
db_finalize(&q);
/* Check to see if this object is used as a file in a check-in */
db_prepare(&q,
"SELECT filename.name, blob.uuid, datetime(event.mtime,toLocal()),"
| | > > | | > | 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 |
cnt++;
}
db_finalize(&q);
/* Check to see if this object is used as a file in a check-in */
db_prepare(&q,
"SELECT filename.name, blob.uuid, datetime(event.mtime,toLocal()),"
" coalesce(euser,user), coalesce(ecomment,comment),"
" coalesce((SELECT value FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=mlink.mid),'trunk')"
" 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*/",
TAG_BRANCH, 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] on branch %s by %s on %s\n",
db_column_text(&q, 1),
db_column_text(&q, 5),
db_column_text(&q, 3),
db_column_text(&q, 2));
fossil_print(" ");
comment_print(db_column_text(&q,4), 0, 12, -1, get_comment_format());
cnt++;
}
db_finalize(&q);
|
| ︙ | ︙ | |||
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 |
** Return a page showing all artifacts in the repository. Query parameters:
**
** n=N Show N artifacts
** s=S Start with artifact number S
** priv Show only unpublished or private artifacts
** phan Show only phantom artifacts
** hclr Color code hash types (SHA1 vs SHA3)
*/
void bloblist_page(void){
Stmt q;
int s = atoi(PD("s","0"));
int n = atoi(PD("n","5000"));
int mx = db_int(0, "SELECT max(rid) FROM blob");
int privOnly = PB("priv");
int phantomOnly = PB("phan");
int hashClr = PB("hclr");
char *zRange;
char *zSha1Bg;
char *zSha3Bg;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
cgi_check_for_malice();
style_header("List Of Artifacts");
style_submenu_element("250 Largest", "bigbloblist");
if( g.perm.Admin ){
| > > > > > > > > > | > | > > > > > > > > > > > > | > | > > < | < < < | > | < | | | < | 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 |
** Return a page showing all artifacts in the repository. Query parameters:
**
** n=N Show N artifacts
** s=S Start with artifact number S
** priv Show only unpublished or private artifacts
** phan Show only phantom artifacts
** hclr Color code hash types (SHA1 vs SHA3)
** recent Show the most recent N artifacts
*/
void bloblist_page(void){
Stmt q;
int s = atoi(PD("s","0"));
int n = atoi(PD("n","5000"));
int mx = db_int(0, "SELECT max(rid) FROM blob");
int privOnly = PB("priv");
int phantomOnly = PB("phan");
int hashClr = PB("hclr");
int bRecent = PB("recent");
int bUnclst = PB("unclustered");
char *zRange;
char *zSha1Bg;
char *zSha3Bg;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
cgi_check_for_malice();
style_header("List Of Artifacts");
style_submenu_element("250 Largest", "bigbloblist");
if( bRecent==0 || n!=250 ){
style_submenu_element("Recent","bloblist?n=250&recent");
}
if( bUnclst==0 ){
style_submenu_element("Unclustered","bloblist?unclustered");
}
if( g.perm.Admin ){
style_submenu_element("Xfer Log", "rcvfromlist");
}
if( !phantomOnly ){
style_submenu_element("Phantoms", "bloblist?phan");
}
style_submenu_element("Clusters","clusterlist");
if( g.perm.Private || g.perm.Admin ){
if( !privOnly ){
style_submenu_element("Private", "bloblist?priv");
}
}else{
privOnly = 0;
}
if( g.perm.Write ){
style_submenu_element("Artifact Stats", "artifact_stats");
}
if( !privOnly && !phantomOnly && mx>n && P("s")==0 && !bRecent && !bUnclst ){
int i;
@ <p>Select a range of artifacts to view:</p>
@ <ul>
for(i=1; i<=mx; i+=n){
@ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
@ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a>
}
@ <li> %z(href("%R/bloblist?n=250&recent"))250 most recent</a>
@ <li> %z(href("%R/bloblist?unclustered"))All unclustered</a>
@ </ul>
style_finish_page();
return;
}
if( phantomOnly || privOnly || mx>n ){
style_submenu_element("Index", "bloblist");
}
if( privOnly ){
@ <h2>Private Artifacts</h2>
zRange = mprintf("IN private");
}else if( phantomOnly ){
@ <h2>Phantom Artifacts</h2>
zRange = mprintf("IN phantom");
}else if( bUnclst ){
@ <h2>Unclustered Artifacts</h2>
zRange = mprintf("IN unclustered");
}else if( bRecent ){
@ <h2>%d(n) Most Recent Artifacts</h2>
zRange = mprintf(">=(SELECT rid FROM blob"
" ORDER BY rid DESC LIMIT 1 OFFSET %d)",n);
}else{
zRange = mprintf("BETWEEN %d AND %d", s, s+n-1);
}
describe_artifacts(zRange);
fossil_free(zRange);
db_prepare(&q,
/* 0 1 2 3 4 5 6 */
"SELECT rid, uuid, summary, isPrivate, type='phantom', ref, rcvid, "
" datetime(rcvfrom.mtime)"
" FROM description LEFT JOIN rcvfrom USING(rcvid)"
" ORDER BY rid %s",
((bRecent||bUnclst)?"DESC":"ASC")/*safe-for-%s*/
);
if( skin_detail_boolean("white-foreground") ){
zSha1Bg = "#714417";
zSha3Bg = "#177117";
}else{
zSha1Bg = "#ebffb0";
zSha3Bg = "#b0ffb0";
}
@ <table cellpadding="2" cellspacing="0" border="1">
@ <tr><th>RID<th>Hash<th>Received<th>Description<th>Ref<th>Remarks
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q,0);
const char *zUuid = db_column_text(&q, 1);
const char *zDesc = db_column_text(&q, 2);
int isPriv = db_column_int(&q,3);
int isPhantom = db_column_int(&q,4);
const char *zRef = db_column_text(&q,5);
const char *zDate = db_column_text(&q,7);
if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
/* Don't show private artifacts to users without Private (x) permission */
continue;
}
if( hashClr ){
const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg;
@ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td>
}else{
@ <tr><td align="right">%d(rid)</td>
}
@ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td>
if( g.perm.Admin ){
int rcvid = db_column_int(&q, 6);
@ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%h(zDate)</a>
}else{
@ <td>%h(zDate)
}
@ <td align="left">%h(zDesc)</td>
if( zRef && zRef[0] ){
@ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
}else{
@ <td>
}
|
| ︙ | ︙ | |||
1871 1872 1873 1874 1875 1876 1877 |
** generate extra network traffic on every sync request.
*/
void phantom_list_page(void){
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_header("Public Phantom Artifacts");
if( g.perm.Admin ){
| | | 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 |
** generate extra network traffic on every sync request.
*/
void phantom_list_page(void){
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_header("Public Phantom Artifacts");
if( g.perm.Admin ){
style_submenu_element("Xfer Log", "rcvfromlist");
style_submenu_element("Artifact List", "bloblist");
}
if( g.perm.Write ){
style_submenu_element("Artifact Stats", "artifact_stats");
}
table_of_public_phantoms();
style_finish_page();
|
| ︙ | ︙ | |||
1896 1897 1898 1899 1900 1901 1902 |
void bigbloblist_page(void){
Stmt q;
int n = atoi(PD("n","250"));
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( g.perm.Admin ){
| | | 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 |
void bigbloblist_page(void){
Stmt q;
int n = atoi(PD("n","250"));
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( g.perm.Admin ){
style_submenu_element("Xfer Log", "rcvfromlist");
}
if( g.perm.Write ){
style_submenu_element("Artifact Stats", "artifact_stats");
}
style_submenu_element("All Artifacts", "bloblist");
style_header("%d Largest Artifacts", n);
db_multi_exec(
|
| ︙ | ︙ | |||
2069 2070 2071 2072 2073 2074 2075 | } /* ** COMMAND: test-phantoms ** ** Usage: %fossil test-phantoms ** | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 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 |
}
/*
** COMMAND: test-phantoms
**
** Usage: %fossil test-phantoms
**
** Show all phantom artifacts. A phantom artifact is one for which there
** is no content. Options:
**
** --count Show only a count of the number of phantoms.
** --delta Show all delta-phantoms. A delta-phantom is a
** artifact for which there is a delta but the delta
** source is a phantom.
** --list Just list the phantoms. Do not try to describe them.
*/
void test_phatoms_cmd(void){
int bDelta;
int bList;
int bCount;
unsigned nPhantom = 0;
unsigned nDeltaPhantom = 0;
db_find_and_open_repository(0,0);
bDelta = find_option("delta", 0, 0)!=0;
bList = find_option("list", 0, 0)!=0;
bCount = find_option("count", 0, 0)!=0;
verify_all_options();
if( bList || bCount ){
Stmt q1, q2;
db_prepare(&q1, "SELECT rid, uuid FROM blob WHERE size<0");
while( db_step(&q1)==SQLITE_ROW ){
int rid = db_column_int(&q1, 0);
nPhantom++;
if( !bCount ){
fossil_print("%S (%d)\n", db_column_text(&q1,1), rid);
}
db_prepare(&q2,
"WITH RECURSIVE deltasof(rid) AS ("
" SELECT rid FROM delta WHERE srcid=%d"
" UNION"
" SELECT delta.rid FROM deltasof, delta"
" WHERE delta.srcid=deltasof.rid)"
"SELECT deltasof.rid, blob.uuid FROM deltasof LEFT JOIN blob"
" ON blob.rid=deltasof.rid", rid
);
while( db_step(&q2)==SQLITE_ROW ){
nDeltaPhantom++;
if( !bCount ){
fossil_print(" %S (%d)\n", db_column_text(&q2,1),
db_column_int(&q2,0));
}
}
db_finalize(&q2);
}
db_finalize(&q1);
if( nPhantom ){
fossil_print("Phantoms: %u Delta-phantoms: %u\n",
nPhantom, nDeltaPhantom);
}
}else if( bDelta ){
describe_artifacts_to_stdout(
"IN (WITH RECURSIVE delta_phantom(rid) AS (\n"
" SELECT delta.rid FROM blob, delta\n"
" WHERE blob.size<0 AND delta.srcid=blob.rid\n"
" UNION\n"
" SELECT delta.rid FROM delta_phantom, delta\n"
" WHERE delta.srcid=delta_phantom.rid)\n"
" SELECT rid FROM delta_phantom)", 0);
}else{
describe_artifacts_to_stdout("IN (SELECT rid FROM blob WHERE size<0)", 0);
}
}
/* Maximum number of collision examples to remember */
#define MAX_COLLIDE 25
/*
** Generate a report on the number of collisions in artifact hashes
|
| ︙ | ︙ | |||
2161 2162 2163 2164 2165 2166 2167 |
collision_report("SELECT (SELECT uuid FROM blob WHERE rid=objid)"
" FROM event WHERE event.type='ci'"
" ORDER BY 1");
@ <h1>Hash Prefix Collisions on All Artifacts</h1>
collision_report("SELECT uuid FROM blob ORDER BY 1");
style_finish_page();
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
collision_report("SELECT (SELECT uuid FROM blob WHERE rid=objid)"
" FROM event WHERE event.type='ci'"
" ORDER BY 1");
@ <h1>Hash Prefix Collisions on All Artifacts</h1>
collision_report("SELECT uuid FROM blob ORDER BY 1");
style_finish_page();
}
/*
** WEBPAGE: clusterlist
**
** Show information about all cluster artifacts in the database.
*/
void clusterlist_page(void){
Stmt q;
int cnt = 1;
sqlite3_int64 szTotal = 0;
sqlite3_int64 szCTotal = 0;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_header("All Cluster Artifacts");
style_submenu_element("All Artifactst", "bloblist");
if( g.perm.Admin ){
style_submenu_element("Xfer Log", "rcvfromlist");
}
style_submenu_element("Phantoms", "bloblist?phan");
if( g.perm.Write ){
style_submenu_element("Artifact Stats", "artifact_stats");
}
db_prepare(&q,
"SELECT blob.uuid, "
" blob.size, "
" octet_length(blob.content), "
" datetime(rcvfrom.mtime),"
" user.login,"
" rcvfrom.ipaddr"
" FROM tagxref JOIN blob ON tagxref.rid=blob.rid"
" LEFT JOIN rcvfrom ON blob.rcvid=rcvfrom.rcvid"
" LEFT JOIN user ON user.uid=rcvfrom.uid"
" WHERE tagxref.tagid=%d"
" ORDER BY rcvfrom.mtime, blob.uuid",
TAG_CLUSTER
);
@ <table cellpadding="2" cellspacing="0" border="1">
@ <tr><th>
@ <th>Hash
@ <th>Date Received
@ <th>Size
@ <th>Compressed Size
if( g.perm.Admin ){
@ <th>User<th>IP-Address
}
while( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
sqlite3_int64 sz = db_column_int64(&q, 1);
sqlite3_int64 szC = db_column_int64(&q, 2);
const char *zDate = db_column_text(&q, 3);
const char *zUser = db_column_text(&q, 4);
const char *zIp = db_column_text(&q, 5);
szTotal += sz;
szCTotal += szC;
@ <tr><td align="right">%d(cnt++)
@ <td><a href="%R/info/%S(zUuid)">%S(zUuid)</a>
if( zDate ){
@ <td>%h(zDate)
}else{
@ <td>
}
@ <td align="right">%,lld(sz)
@ <td align="right">%,lld(szC)
if( g.perm.Admin ){
if( zUser ){
@ <td>%h(zUser)
}else{
@ <td>
}
if( zIp ){
@ <td>%h(zIp)
}else{
@ <td>
}
}
@ </tr>
}
@ </table>
db_finalize(&q);
@ <p>Total size of all clusters: %,lld(szTotal) bytes,
@ %,lld(szCTotal) bytes compressed</p>
style_finish_page();
}
|
Changes to src/patch.c.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 |
*/
static void mkdeltaFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zFile;
| | < < < < | | < < | > < | | | | 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 |
*/
static void mkdeltaFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zFile;
Blob x, y, out;
int rid;
int nOut;
sqlite3_int64 sz;
rid = sqlite3_value_int(argv[0]);
if( !content_get(rid, &x) ){
sqlite3_result_error(context, "mkdelta(X,Y): no content for X", -1);
return;
}
zFile = (const char*)sqlite3_value_text(argv[1]);
if( zFile==0 ){
sqlite3_result_error(context, "mkdelta(X,Y): NULL Y argument", -1);
blob_reset(&x);
return;
}
sz = blob_read_from_file(&y, zFile, RepoFILE);
if( sz<0 ){
sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1);
blob_reset(&x);
return;
}
blob_init(&out, 0, 0);
blob_resize(&out, sz+70);
if( blob_size(&x)==blob_size(&y)
&& memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0
){
blob_reset(&y);
blob_reset(&x);
sqlite3_result_blob64(context, "", 0, SQLITE_STATIC);
return;
}
nOut = delta_create(blob_buffer(&x),blob_size(&x),
blob_buffer(&y),blob_size(&y), blob_buffer(&out));
blob_resize(&out, nOut);
blob_reset(&x);
blob_reset(&y);
blob_compress(&out, &out);
sqlite3_result_blob64(context, blob_buffer(&out), blob_size(&out),
SQLITE_TRANSIENT);
blob_reset(&out);
}
/*
** Generate a binary patch file and store it into the file
** named zOut. Or if zOut is NULL, write it into out.
**
|
| ︙ | ︙ | |||
702 703 704 705 706 707 708 709 710 711 712 713 714 715 |
}
if( zDir && file_chdir(zDir,0) ){
fossil_fatal("cannot change to directory \"%s\"", zDir);
}
fossil_free(zToFree);
return zPatchFile;
}
/*
** Create a FILE* that will execute the remote side of a push or pull
** using ssh (probably) or fossil for local pushes and pulls. Return
** a FILE* obtained from popen() into which we write the patch, or from
** which we read the patch, depending on whether this is a push or pull.
*/
| > > > > > > > > > > > > > > > > | 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 |
}
if( zDir && file_chdir(zDir,0) ){
fossil_fatal("cannot change to directory \"%s\"", zDir);
}
fossil_free(zToFree);
return zPatchFile;
}
/*
** Resolves a patch-command remote system name, accounting for patch
** aliases.
**
** If a CONFIG table entry matching name='patch-alias:$zKey' is found,
** the corresponding value is returned, else a fossil_strdup() of zKey
** is returned. The caller is responsible for passing the resulting
** string to fossil_free().
*/
static char *patch_resolve_remote(const char *zKey){
char *zAlias = db_text(0, "SELECT value FROM config "
"WHERE name = 'patch-alias:%q'",
zKey);
return zAlias ? zAlias : fossil_strdup(zKey);
}
/*
** Create a FILE* that will execute the remote side of a push or pull
** using ssh (probably) or fossil for local pushes and pulls. Return
** a FILE* obtained from popen() into which we write the patch, or from
** which we read the patch, depending on whether this is a push or pull.
*/
|
| ︙ | ︙ | |||
733 734 735 736 737 738 739 |
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));
}
| | | 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 |
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 = patch_resolve_remote(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{
|
| ︙ | ︙ | |||
782 783 784 785 786 787 788 |
}
/*
** Toggle the use-path-for-ssh setting for the remote host defined
** by g.argv[3].
*/
static void patch_toggle_ssh_needs_path(void){
| | | 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 |
}
/*
** 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 = patch_resolve_remote(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);
}
|
| ︙ | ︙ | |||
909 910 911 912 913 914 915 |
}
}
db_finalize(&q);
diff_end(pCfg, nErr);
if( nErr ) fossil_fatal("abort due to prior errors");
}
| < > > > > > > > > > > > > | 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 |
}
}
db_finalize(&q);
diff_end(pCfg, nErr);
if( nErr ) fossil_fatal("abort due to prior errors");
}
/*
** COMMAND: patch
**
** Usage: %fossil patch SUBCOMMAND ?ARGS ..?
**
** This command is used to create, view, and apply Fossil binary patches.
** A Fossil binary patch is a single (binary) file that captures all of the
** uncommitted changes of a check-out. Use Fossil binary patches to transfer
** proposed or incomplete changes between machines for testing or analysis.
**
** > fossil patch alias add|rm|ls|list ?ARGS?
**
** Manage remote-name aliases, which act as short-form
** equivalents to REMOTE-CHECKOUT strings. Aliases are local to
** a given repository and do not sync. Subcommands:
**
** ... add ALIAS REMOTE-CHECKOUT Add ALIAS as an alias
** for REMOTE-CHECKOUT.
** ... ls|list List all local aliases.
** ... rm ALIAS [ALIAS...] Remove named aliases
** ... rm --all Remove all aliases
**
** > fossil patch create [DIRECTORY] PATCHFILE
**
** Create a new binary patch in PATCHFILE that captures all uncommitted
** changes in the check-out at DIRECTORY, or the current directory if
** DIRECTORY is omitted. If PATCHFILE is "-" then the binary patch
** is written to standard output.
**
|
| ︙ | ︙ | |||
1000 1001 1002 1003 1004 1005 1006 |
**
*/
void patch_cmd(void){
const char *zCmd;
size_t n;
if( g.argc<3 ){
patch_usage:
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
**
*/
void patch_cmd(void){
const char *zCmd;
size_t n;
if( g.argc<3 ){
patch_usage:
usage("alias|apply|create|diff|gdiff|pull|push|view");
}
zCmd = g.argv[2];
n = strlen(zCmd);
if( strncmp(zCmd, "alias", n)==0 ){
const char * zArg = g.argc>3 ? g.argv[3] : 0;
db_must_be_within_tree();
if( 0==zArg ){
goto usage_patch_alias;
}else if( 0==strcmp("ls",zArg) || 0==strcmp("list",zArg) ){
/* alias ls|list */
Stmt q;
int nAlias = 0;
verify_all_options();
db_prepare(&q, "SELECT substr(name,13), value FROM config "
"WHERE name GLOB 'patch-alias:*' ORDER BY name");
while( SQLITE_ROW==db_step(&q) ){
const char *zName = db_column_text(&q, 0);
const char *zVal = db_column_text(&q, 1);
++nAlias;
fossil_print("%s = %s\n", zName, zVal);
}
db_finalize(&q);
if( 0==nAlias ){
fossil_print("No patch aliases defined\n");
}
}else if( 0==strcmp("add", zArg) ){
/* alias add localName remote */
verify_all_options();
if( 6!=g.argc ){
usage("alias add localName remote");
}
db_unprotect(PROTECT_CONFIG);
db_multi_exec("REPLACE INTO config (name, value, mtime) "
"VALUES ('patch-alias:%q', %Q, unixepoch())",
g.argv[4], g.argv[5]);
db_protect_pop();
}else if( 0==strcmp("rm", zArg) ){
/* alias rm */
const int fAll = 0!=find_option("all", 0, 0);
if( fAll ? g.argc<4 : g.argc<5 ){
usage("alias rm [-all] [aliasGlob [...aliasGlobN]]");
}
verify_all_options();
db_unprotect(PROTECT_CONFIG);
if( 0!=fAll ){
db_multi_exec("DELETE FROM config WHERE name GLOB 'patch-alias:*'");
}else{
Stmt q;
int i;
db_prepare(&q, "DELETE FROM config WHERE name "
"GLOB 'patch-alias:' || :pattern");
for(i = 4; i < g.argc; ++i){
db_bind_text(&q, ":pattern", g.argv[i]);
db_step(&q);
db_reset(&q);
}
db_finalize(&q);
}
db_protect_pop();
}else{
usage_patch_alias:
usage("alias ls|list|add|rm ...");
}
}else
if( strncmp(zCmd, "apply", n)==0 ){
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");
|
| ︙ | ︙ | |||
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 |
if( find_option("tk",0,0)!=0 ){
db_close(0);
diff_tk("patch diff", 3);
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();
| > > > > | | 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 |
if( find_option("tk",0,0)!=0 ){
db_close(0);
diff_tk("patch diff", 3);
return;
}
db_find_and_open_repository(0, 0);
if( gdiff_using_tk(zCmd[0]=='g') ){
diff_tk("patch diff", 3);
return;
}
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
diff_options(&DCfg, zCmd[0]=='g', 0);
verify_all_options();
zIn = patch_find_patch_filename("diff");
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;
|
| ︙ | ︙ |
Changes to src/path.c.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
**
** This file contains code used to trace paths of through the
** directed acyclic graph (DAG) of check-ins.
*/
#include "config.h"
#include "path.h"
#include <assert.h>
#if INTERFACE
/* Nodes for the paths through the DAG.
*/
struct PathNode {
int rid; /* ID for this node */
u8 fromIsParent; /* True if pFrom is the parent of rid */
u8 isPrim; /* True if primary side of common ancestor */
u8 isHidden; /* Abbreviate output in "fossil bisect ls" */
PathNode *pFrom; /* Node we came from */
union {
| > > > | | | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > > > > | > > > > > > > > > | > > | > | | 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 |
**
** This file contains code used to trace paths of through the
** directed acyclic graph (DAG) of check-ins.
*/
#include "config.h"
#include "path.h"
#include <assert.h>
#include <math.h>
#if INTERFACE
/* Nodes for the paths through the DAG.
*/
struct PathNode {
int rid; /* ID for this node */
u8 fromIsParent; /* True if pFrom is the parent of rid */
u8 isPrim; /* True if primary side of common ancestor */
u8 isHidden; /* Abbreviate output in "fossil bisect ls" */
char *zBranch; /* Branch name for this node. Might be NULL */
double mtime; /* Date/time of this check-in */
PathNode *pFrom; /* Node we came from */
union {
double rCost; /* Cost of getting to this node from pStart */
PathNode *pTo; /* Next on path from beginning to end */
} u;
PathNode *pAll; /* List of all nodes */
};
#endif
/*
** Local variables for this module
*/
static struct {
PQueue pending; /* Nodes pending review for inclusion in the graph */
PathNode *pAll; /* All nodes */
int nStep; /* Number of steps from first to last */
int nNotHidden; /* Number of steps not counting hidden nodes */
int brCost; /* Extra cost for moving to a different branch */
int revCost; /* Extra cost for changing directions */
PathNode *pStart; /* Earliest node */
PathNode *pEnd; /* Most recent */
} path;
static int path_debug = 0; /* Flag to enable debugging */
/*
** Return the first (last) element of the computed path.
*/
PathNode *path_first(void){ return path.pStart; }
PathNode *path_last(void){ return path.pEnd; }
/*
** Return the number of steps in the computed path.
*/
int path_length(void){ return path.nStep; }
/*
** Return the number of non-hidden steps in the computed path.
*/
int path_length_not_hidden(void){ return path.nNotHidden; }
/*
** Used for debugging only.
**
** Given a RID, return the ISO date/time string and branch for the
** corresponding check-in. Memory is held locally and is overwritten
** with each call.
*/
char *path_rid_desc(int rid){
static Stmt q;
static char *zDesc = 0;
db_static_prepare(&q,
"SELECT concat(strftime('%%Y%%m%%d%%H%%M',event.mtime),'/',value)"
" FROM event, tagxref"
" WHERE event.objid=:rid"
" AND tagxref.rid=:rid"
" AND tagxref.tagid=%d"
" AND tagxref.tagtype>0",
TAG_BRANCH
);
fossil_free(zDesc);
db_bind_int(&q, ":rid", rid);
if( db_step(&q)==SQLITE_ROW ){
zDesc = fossil_strdup(db_column_text(&q,0));
}
db_reset(&q);
return zDesc ? zDesc : "???";
}
/*
** Create a new node and insert it into the path.pending queue.
*/
static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){
PathNode *p;
p = fossil_malloc( sizeof(*p) );
memset(p, 0, sizeof(*p));
p->pAll = path.pAll;
path.pAll = p;
p->rid = rid;
p->fromIsParent = isParent;
p->pFrom = pFrom;
p->u.rCost = pFrom ? pFrom->u.rCost : 0.0;
if( path.brCost ){
p->zBranch = branch_of_rid(rid);
p->mtime = mtime_of_rid(rid, 0.0);
if( pFrom ){
p->u.rCost += fabs(pFrom->mtime - p->mtime);
if( fossil_strcmp(p->zBranch, pFrom->zBranch)!=0 ){
p->u.rCost += path.brCost;
}
}
}else{
/* When brCost==0, we try to minimize the number of nodes
** along the path. The cost is just the number of nodes back
** to the start. We do not need to know the branch name nor
** the mtime */
p->u.rCost += 1.0;
}
if( path_debug ){
fossil_print("PUSH %-50s cost = %g\n", path_rid_desc(p->rid), p->u.rCost);
}
pqueuex_insert_ptr(&path.pending, (void*)p, p->u.rCost);
return p;
}
/*
** Reset memory used by the shortest path algorithm.
*/
void path_reset(void){
PathNode *p;
while( path.pAll ){
p = path.pAll;
path.pAll = p->pAll;
fossil_free(p->zBranch);
fossil_free(p);
}
pqueuex_clear(&path.pending);
memset(&path, 0, sizeof(path));
}
/*
** Construct the path from path.pStart to path.pEnd in the u.pTo fields.
*/
static void path_reverse_path(void){
|
| ︙ | ︙ | |||
126 127 128 129 130 131 132 | ** Return NULL if no path is found. */ PathNode *path_shortest( int iFrom, /* Path starts here */ int iTo, /* Path ends here */ int directOnly, /* No merge links if true */ int oneWayOnly, /* Parent->child only if true */ | | > < > > | | | | | < < | < < < < < < > | | | | | | | | | > > > > > > > > > > > | | < < | 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 |
** Return NULL if no path is found.
*/
PathNode *path_shortest(
int iFrom, /* Path starts here */
int iTo, /* Path ends here */
int directOnly, /* No merge links if true */
int oneWayOnly, /* Parent->child only if true */
Bag *pHidden, /* Hidden nodes */
int branchCost /* Add extra cost to changing branches */
){
Stmt s;
Bag seen;
PathNode *p;
path_reset();
path.brCost = branchCost;
path.pStart = path_new_node(iFrom, 0, 0);
if( iTo==iFrom ){
path.pEnd = path.pStart;
return path.pStart;
}
if( oneWayOnly && directOnly ){
db_prepare(&s,
"SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim"
);
}else if( oneWayOnly ){
db_prepare(&s,
"SELECT cid, 1 FROM plink WHERE pid=:pid "
);
}else if( directOnly ){
db_prepare(&s,
"SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim "
"UNION ALL "
"SELECT pid, 0 FROM plink WHERE :back AND cid=:pid AND isprim"
);
}else{
db_prepare(&s,
"SELECT cid, 1 FROM plink WHERE pid=:pid "
"UNION ALL "
"SELECT pid, 0 FROM plink WHERE :back AND cid=:pid"
);
}
bag_init(&seen);
while( (p = pqueuex_extract_ptr(&path.pending))!=0 ){
if( path_debug ){
printf("PULL %s %g\n", path_rid_desc(p->rid), p->u.rCost);
}
if( p->rid==iTo ){
db_finalize(&s);
path.pEnd = p;
path_reverse_path();
for(p=path.pStart->u.pTo; p; p=p->u.pTo ){
if( !p->isHidden ) path.nNotHidden++;
}
return path.pStart;
}
if( bag_find(&seen, p->rid) ) continue;
bag_insert(&seen, p->rid);
db_bind_int(&s, ":pid", p->rid);
if( !oneWayOnly ) db_bind_int(&s, ":back", !p->fromIsParent);
while( db_step(&s)==SQLITE_ROW ){
int cid = db_column_int(&s, 0);
int isParent = db_column_int(&s, 1);
PathNode *pNew;
if( bag_find(&seen, cid) ) continue;
pNew = path_new_node(cid, p, isParent);
if( pHidden && bag_find(pHidden,cid) ) pNew->isHidden = 1;
}
db_reset(&s);
}
db_finalize(&s);
path_reset();
return 0;
}
/*
|
| ︙ | ︙ | |||
213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
*/
PathNode *path_next(void){
PathNode *p;
p = path.pStart;
if( p ) p = p->u.pTo;
return p;
}
/*
** Return an estimate of the number of comparisons remaining in order
** to bisect path. This is based on the log2() of path.nStep.
*/
int path_search_depth(void){
int i, j;
| > > > > > > > > > > > > | 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 |
*/
PathNode *path_next(void){
PathNode *p;
p = path.pStart;
if( p ) p = p->u.pTo;
return p;
}
/*
** Return the branch for a path node.
**
** Storage space is managed by the path subsystem. The returned value
** is valid until the path is reset.
*/
const char *path_branch(PathNode *p){
if( p==0 ) return 0;
if( p->zBranch==0 ) p->zBranch = branch_of_rid(p->rid);
return p->zBranch;
}
/*
** Return an estimate of the number of comparisons remaining in order
** to bisect path. This is based on the log2() of path.nStep.
*/
int path_search_depth(void){
int i, j;
|
| ︙ | ︙ | |||
236 237 238 239 240 241 242 |
void path_shortest_stored_in_ancestor_table(
int origid, /* RID for check-in at start of the path */
int cid /* RID for check-in at the end of the path */
){
PathNode *pPath;
int gen = 0;
Stmt ins;
| | | 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
void path_shortest_stored_in_ancestor_table(
int origid, /* RID for check-in at start of the path */
int cid /* RID for check-in at the end of the path */
){
PathNode *pPath;
int gen = 0;
Stmt ins;
pPath = path_shortest(cid, origid, 1, 0, 0, 0);
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ancestor("
" rid INT UNIQUE,"
" generation INTEGER PRIMARY KEY"
");"
"DELETE FROM ancestor;"
);
|
| ︙ | ︙ | |||
259 260 261 262 263 264 265 | db_finalize(&ins); path_reset(); } /* ** COMMAND: test-shortest-path ** | | | > > > > | > > > > | > < < < < < < < < < < < < | | < > | < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < | 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 |
db_finalize(&ins);
path_reset();
}
/*
** COMMAND: test-shortest-path
**
** Usage: %fossil test-shortest-path [OPTIONS] VERSION1 VERSION2
**
** Report the shortest path between two check-ins. Options:
**
** --branch-cost N Additional cost N for changing branches
** --debug Show debugging output
** --one-way One-way forwards in time, parent->child only
** --no-merge Follow only direct parent-child paths and omit
** merge links.
*/
void shortest_path_test_cmd(void){
int iFrom;
int iTo;
PathNode *p;
int n;
int directOnly;
int oneWay;
const char *zBrCost;
db_find_and_open_repository(0,0);
directOnly = find_option("no-merge",0,0)!=0;
oneWay = find_option("one-way",0,0)!=0;
zBrCost = find_option("branch-cost",0,1);
if( find_option("debug",0,0)!=0 ) path_debug = 1;
if( g.argc!=4 ) usage("VERSION1 VERSION2");
iFrom = name_to_rid(g.argv[2]);
iTo = name_to_rid(g.argv[3]);
p = path_shortest(iFrom, iTo, directOnly, oneWay, 0,
zBrCost ? atoi(zBrCost) : 0);
if( p==0 ){
fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]);
}
for(n=1, p=path.pStart; p; p=p->u.pTo, n++){
fossil_print("%4d: %s\n", n, path_rid_desc(p->rid));
}
path_debug = 0;
}
/*
** Find the closest common ancestor of two nodes. "Closest" means the
** fewest number of arcs.
*/
int path_common_ancestor(int iMe, int iYou){
Stmt s;
PathNode *pThis;
PathNode *p;
Bag me, you;
if( iMe==iYou ) return iMe;
if( iMe==0 || iYou==0 ) return 0;
path_reset();
path.pStart = path_new_node(iMe, 0, 0);
path.pStart->isPrim = 1;
path.pEnd = path_new_node(iYou, 0, 0);
db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid");
bag_init(&me);
bag_insert(&me, iMe);
bag_init(&you);
bag_insert(&you, iYou);
while( (pThis = pqueuex_extract_ptr(&path.pending))!=0 ){
db_bind_int(&s, ":cid", pThis->rid);
while( db_step(&s)==SQLITE_ROW ){
int pid = db_column_int(&s, 0);
if( bag_find(pThis->isPrim ? &you : &me, pid) ){
/* pid is the common ancestor */
PathNode *pNext;
for(p=path.pAll; p && p->rid!=pid; p=p->pAll){}
assert( p!=0 );
pNext = p;
while( pNext ){
pNext = p->pFrom;
p->pFrom = pThis;
pThis = p;
p = pNext;
}
if( pThis==path.pStart ) path.pStart = path.pEnd;
path.pEnd = pThis;
path_reverse_path();
db_finalize(&s);
return pid;
}else if( bag_find(pThis->isPrim ? &me : &you, pid) ){
/* pid is just an alternative path to a node we've already visited */
continue;
}
p = path_new_node(pid, pThis, 0);
p->isPrim = pThis->isPrim;
bag_insert(pThis->isPrim ? &me : &you, pid);
}
db_reset(&s);
}
db_finalize(&s);
path_reset();
return 0;
}
/*
|
| ︙ | ︙ | |||
451 452 453 454 455 456 457 |
if(0==iFrom){
fossil_fatal("Invalid 'from' RID: 0");
}else if(0==iTo){
fossil_fatal("Invalid 'to' RID: 0");
}
if( iFrom==iTo ) return;
path_reset();
| | | 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
if(0==iFrom){
fossil_fatal("Invalid 'from' RID: 0");
}else if(0==iTo){
fossil_fatal("Invalid 'to' RID: 0");
}
if( iFrom==iTo ) return;
path_reset();
p = path_shortest(iFrom, iTo, 1, revOK==0, 0, 0);
if( p==0 ) return;
path_reverse_path();
db_prepare(&q1,
"SELECT pfnid, fnid FROM mlink"
" WHERE mid=:mid AND (pfnid>0 OR fid==0)"
" ORDER BY pfnid"
);
|
| ︙ | ︙ |
Changes to src/pikchrshow.c.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | #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 */ | < < < | | < < | | | < < < < < < < < < < < < < | 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 | #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_NONCE 0x0010 #define PIKCHR_PROCESS_ERR_PRE 0x0020 #define PIKCHR_PROCESS_SRC 0x0040 #define PIKCHR_PROCESS_DIV 0x0080 #define PIKCHR_PROCESS_DIV_INDENT 0x0100 #define PIKCHR_PROCESS_DIV_CENTER 0x0200 #define PIKCHR_PROCESS_DIV_FLOAT_LEFT 0x0400 #define PIKCHR_PROCESS_DIV_FLOAT_RIGHT 0x0800 #define PIKCHR_PROCESS_DIV_TOGGLE 0x1000 #define PIKCHR_PROCESS_DIV_SOURCE 0x2000 #define PIKCHR_PROCESS_DIV_SOURCE_INLINE 0x4000 #endif /* ** Processes a pikchr script. zIn is the NUL-terminated input ** script. pikFlags may be a bitmask of any of the PIKCHR_PROCESS_xxx ** flags documented below. Output is sent to pOut, ** ** Returns 0 on success, or non-zero if pikchr processing failed. ** In either case, the error message (if any) from pikchr will be ** appended to pOut. ** ** pikFlags flag descriptions: ** ** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV ** element which specifies a max-width style value based on the SVG's ** calculated size. This flag has multiple mutually exclusive forms: ** ** - PIKCHR_PROCESS_DIV uses default element alignment. ** - PIKCHR_PROCESS_DIV_INDENT indents the div. ** - PIKCHR_PROCESS_DIV_CENTER centers the div. |
| ︙ | ︙ | |||
114 115 116 117 118 119 120 | ** PIKCHR_PROCESS_DIV_SOURCE or PIKCHR_PROCESS_DIV_SOURCE_INLINE is ** set, this flag is automatically implied. ** ** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting ** error report is wrapped in a PRE element, else it is retained ** as-is (intended only for console output). */ | | < < > > < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < < | 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 |
** PIKCHR_PROCESS_DIV_SOURCE or PIKCHR_PROCESS_DIV_SOURCE_INLINE is
** set, this flag is automatically implied.
**
** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
** error report is wrapped in a PRE element, else it is retained
** as-is (intended only for console output).
*/
int pikchr_process(const char *zIn, int pikFlags, Blob * pOut){
int isErr = 0;
int w = 0, h = 0;
char *zOut;
const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
? safe_html_nonce(1) : 0;
if(!(PIKCHR_PROCESS_DIV & pikFlags)
/* If any DIV_xxx flags are set, set DIV */
&& (PIKCHR_PROCESS_DIV_INDENT
| PIKCHR_PROCESS_DIV_CENTER
| PIKCHR_PROCESS_DIV_FLOAT_RIGHT
| PIKCHR_PROCESS_DIV_FLOAT_LEFT
| PIKCHR_PROCESS_DIV_SOURCE
| PIKCHR_PROCESS_DIV_SOURCE_INLINE
| PIKCHR_PROCESS_DIV_TOGGLE
) & pikFlags){
pikFlags |= PIKCHR_PROCESS_DIV;
}
if(zNonce){
blob_appendf(pOut, "%s\n", zNonce);
}
zOut = pikchr(zIn, "pikchr",
0x01 | (pikFlags&PIKCHR_PROCESS_PASSTHROUGH),
&w, &h);
if( w>0 && h>0 ){
const char * zClassToggle = "";
const char * zClassSource = "";
const char * zWrapperClass = "";
if(PIKCHR_PROCESS_DIV & pikFlags){
if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
zWrapperClass = " center";
}else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
zWrapperClass = " indent";
}else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
zWrapperClass = " float-left";
}else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
zWrapperClass = " float-right";
}
if(PIKCHR_PROCESS_DIV_TOGGLE & pikFlags){
zClassToggle = " toggle";
}
if(PIKCHR_PROCESS_DIV_SOURCE_INLINE & pikFlags){
if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
zClassSource = " source source-inline";
}else{
zClassSource = " source-inline";
}
pikFlags |= PIKCHR_PROCESS_SRC;
}else if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
zClassSource = " source";
pikFlags |= PIKCHR_PROCESS_SRC;
}
blob_appendf(pOut,"<div class='pikchr-wrapper"
"%s%s%s'>"
"<div class=\"pikchr-svg\" "
"style=\"max-width:%dpx\">\n",
zWrapperClass/*safe-for-%s*/,
zClassToggle/*safe-for-%s*/,
zClassSource/*safe-for-%s*/, w);
}
blob_append(pOut, zOut, -1);
if(PIKCHR_PROCESS_DIV & pikFlags){
blob_append(pOut, "</div>\n", 7);
}
if(PIKCHR_PROCESS_SRC & pikFlags){
static int counter = 0;
++counter;
blob_appendf(pOut, "<div class='pikchr-src'>"
"<pre id='pikchr-src-%d'>%h</pre>"
"<span class='hidden'>"
"<a href='%R/pikchrshow?fromSession' "
"class='pikchr-src-pikchrshow' target='_new-%d' "
"data-pikchrid='pikchr-src-%d' "
"title='Open this pikchr in /pikchrshow'"
">→ /pikchrshow</a></span>"
"</div>\n",
counter, zIn, counter, counter);
}
if(PIKCHR_PROCESS_DIV & pikFlags){
blob_append(pOut, "</div>\n", 7);
}
}else{
isErr = 2;
if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
blob_append(pOut, "<pre class='error'>\n", 20);
}
blob_appendf(pOut, "%h", zOut);
if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
blob_append(pOut, "\n</pre>\n", 8);
}
}
fossil_free(zOut);
if(zNonce){
blob_appendf(pOut, "%s\n", zNonce);
}
return isErr;
}
/*
** Legacy impl of /pikchrshow. pikchrshow_page() will delegate to
** this one if the "legacy" or "ajax" request arguments are set.
**
|
| ︙ | ︙ | |||
277 278 279 280 281 282 283 |
if(P("ajax")!=0){
/* Called from the JS-side preview updater.
TODO: respond with JSON instead.*/
cgi_set_content_type("text/html");
if(zContent && *zContent){
Blob out = empty_blob;
const int isErr =
| | | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
if(P("ajax")!=0){
/* Called from the JS-side preview updater.
TODO: respond with JSON instead.*/
cgi_set_content_type("text/html");
if(zContent && *zContent){
Blob out = empty_blob;
const int isErr =
pikchr_process(zContent, pikFlags, &out);
if(isErr){
cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr);
}
CX("%b", &out);
blob_reset(&out);
}else{
CX("<pre>No content! Nothing to render</pre>");
|
| ︙ | ︙ | |||
382 383 384 385 386 387 388 |
CX("<fieldset id='pikchrshow-output-wrapper'>"); {
CX("<legend></legend>"
/* Reminder: Firefox does not properly flexbox a LEGEND
element, always flowing it in column mode. */);
CX("<div id='pikchrshow-output'>");
if(*zContent){
Blob out = empty_blob;
| | | 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
CX("<fieldset id='pikchrshow-output-wrapper'>"); {
CX("<legend></legend>"
/* Reminder: Firefox does not properly flexbox a LEGEND
element, always flowing it in column mode. */);
CX("<div id='pikchrshow-output'>");
if(*zContent){
Blob out = empty_blob;
pikchr_process(zContent, pikFlags, &out);
CX("%b", &out);
blob_reset(&out);
} CX("</div>"/*#pikchrshow-output*/);
} CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
} CX("</div>"/*sbs-wrapper*/);
builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget",
"storage", "pikchr", NULL);
|
| ︙ | ︙ | |||
559 560 561 562 563 564 565 | ** -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. ** | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 |
** -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.
**
** -dark Change pikchr colors to assume a dark-mode theme.
**
**
** The -div-indent/center/left/right flags may not be combined.
*/
void pikchr_cmd(void){
Blob bIn = empty_blob;
Blob bOut = empty_blob;
const char * zInfile = "-";
const char * zOutfile = "-";
int isErr = 0;
int pikFlags = find_option("src",0,0)!=0
? PIKCHR_PROCESS_SRC : 0;
if(find_option("div",0,0)!=0){
pikFlags |= PIKCHR_PROCESS_DIV;
}else if(find_option("div-indent",0,0)!=0){
pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
}else if(find_option("div-center",0,0)!=0){
pikFlags |= PIKCHR_PROCESS_DIV_CENTER;
|
| ︙ | ︙ | |||
642 643 644 645 646 647 648 |
if(g.argc>2){
zInfile = g.argv[2];
}
if(g.argc>3){
zOutfile = g.argv[3];
}
blob_read_from_file(&bIn, zInfile, ExtFILE);
| < < < < < < | < < < | < | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
if(g.argc>2){
zInfile = g.argv[2];
}
if(g.argc>3){
zOutfile = g.argv[3];
}
blob_read_from_file(&bIn, zInfile, ExtFILE);
isErr = pikchr_process(blob_str(&bIn), pikFlags, &bOut);
if(isErr){
fossil_fatal("pikchr ERROR: %b", &bOut);
}else{
blob_write_to_file(&bOut, zOutfile);
}
blob_reset(&bIn);
blob_reset(&bOut);
}
|
Changes to src/pqueue.c.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to implement a priority queue. ** A priority queue is a list of items order by a floating point | | | > < < | > > > > > > > > | > | > > | 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 |
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to implement a priority queue.
** A priority queue is a list of items order by a floating point
** value. Each value can be associated with either a pointer or
** an integer. Items are inserted into the queue in an arbitrary
** order, but are returned in order of the floating point value.
**
** This implementation uses a heap of QueueElement objects. The
** root of the heap is PQueue.a[0]. Each node a[x] has two daughter
** nodes a[x*2+1] and a[x*2+2]. The mother node of a[y] is a[(y-1)/2]
** (assuming integer division rounded down). The following is always true:
**
** The value of any node is less than or equal two the values
** of both daughter nodes. (The Heap Property).
**
** A consequence of the heap property is that a[0] always contains
** the node with the smallest value.
**
** Compatibility note: Some versions of OpenSSL export a symbols
** like "pqueue_insert". This is, technically, a bug in OpenSSL.
** We work around it here by using "pqueuex_" instead of "pqueue_".
*/
#include "config.h"
#include "pqueue.h"
#include <assert.h>
#if INTERFACE
/*
** An integer can appear in the bag at most once.
** Integers must be positive.
*/
struct PQueue {
int cnt; /* Number of entries in the queue */
int sz; /* Number of slots in a[] */
struct QueueElement {
union {
int id; /* ID of the element */
void *p; /* Pointer to an object */
} u;
double value; /* Value of element. Kept in ascending order */
} *a;
};
#endif
/*
** Initialize a PQueue structure
|
| ︙ | ︙ | |||
67 68 69 70 71 72 73 74 75 76 77 78 79 |
/*
** Change the size of the queue so that it contains N slots
*/
static void pqueuex_resize(PQueue *p, int N){
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;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < | > | < > > | > | < < < < < < | | | | | > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/*
** Change the size of the queue so that it contains N slots
*/
static void pqueuex_resize(PQueue *p, int N){
p->a = fossil_realloc(p->a, sizeof(p->a[0])*N);
p->sz = N;
}
/*
** Allocate a new queue entry and return a pointer to it.
*/
static struct QueueElement *pqueuex_new_entry(PQueue *p){
if( p->cnt+1>p->sz ){
pqueuex_resize(p, p->cnt+7);
}
return &p->a[p->cnt++];
}
/*
** Element p->a[p->cnt-1] has just been inserted. Shift entries
** around so as to preserve the heap property.
*/
static void pqueuex_rebalance(PQueue *p){
int i, j;
struct QueueElement *a = p->a;
i = p->cnt-1;
while( (j = (i-1)/2)>=0 && a[j].value>a[i].value ){
struct QueueElement t = a[j];
a[j] = a[i];
a[i] = t;
i = j;
}
}
/*
** Insert element e into the queue.
*/
void pqueuex_insert(PQueue *p, int e, double v){
struct QueueElement *pE = pqueuex_new_entry(p);
pE->value = v;
pE->u.id = e;
pqueuex_rebalance(p);
}
void pqueuex_insert_ptr(PQueue *p, void *pPtr, double v){
struct QueueElement *pE = pqueuex_new_entry(p);
pE->value = v;
pE->u.p = pPtr;
pqueuex_rebalance(p);
}
/*
** Remove and discard p->a[0] element from the queue. Rearrange
** nodes to preserve the heap property.
*/
static void pqueuex_pop(PQueue *p){
int i, j;
struct QueueElement *a = p->a;
struct QueueElement tmp;
i = 0;
a[0] = a[p->cnt-1];
p->cnt--;
while( (j = i*2+1)<p->cnt ){
if( j+1<p->cnt && a[j].value > a[j+1].value ) j++;
if( a[i].value < a[j].value ) break;
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
i = j;
}
}
/*
** 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;
if( p->cnt==0 ){
return 0;
}
e = p->a[0].u.id;
pqueuex_pop(p);
return e;
}
void *pqueuex_extract_ptr(PQueue *p){
void *pPtr;
if( p->cnt==0 ){
return 0;
}
pPtr = p->a[0].u.p;
pqueuex_pop(p);
return pPtr;
}
/*
** Print the entire heap associated with the test-pqueue command.
*/
static void pqueuex_test_print(PQueue *p){
int j;
for(j=0; j<p->cnt; j++){
fossil_print("(%d) %g/%s ",j,p->a[j].value,p->a[j].u.p);
}
fossil_print("\n");
}
/*
** COMMAND: test-pqueue
**
** This command is used for testing the PQueue object. There are one
** or more arguments, each of the form:
**
** (1) NUMBER/TEXT
** (2) ^
** (3) -v
**
** Form (1) arguments add an entry to the queue with value NUMBER and
** content TEXT. Form (2) pops off the queue entry with the smallest
** value. Form (3) (the -v option) causes the heap to be displayed after
** each subsequent operation.
*/
void pqueuex_test_cmd(void){
int i;
PQueue x;
const char *zId;
int bDebug = 0;
pqueuex_init(&x);
for(i=2; i<g.argc; i++){
const char *zArg = g.argv[i];
if( strcmp(zArg,"-v")==0 ){
bDebug = 1;
}else if( strcmp(zArg, "^")==0 ){
zId = pqueuex_extract_ptr(&x);
if( zId==0 ){
fossil_print("%2d: POP NULL\n", i);
}else{
fossil_print("%2d: POP \"%s\"\n", i, zId);
}
if( bDebug) pqueuex_test_print(&x);
}else{
double r = atof(zArg);
zId = strchr(zArg,'/');
if( zId==0 ) zId = zArg;
if( zId[0]=='/' ) zId++;
pqueuex_insert_ptr(&x, (void*)zId, r);
fossil_print("%2d: INSERT \"%s\"\n", i, zId);
if( bDebug) pqueuex_test_print(&x);
}
}
while( (zId = pqueuex_extract_ptr(&x))!=0 ){
fossil_print("... POP \"%s\"\n", zId);
if( bDebug) pqueuex_test_print(&x);
}
pqueuex_clear(&x);
}
|
Changes to src/printf.c.
| ︙ | ︙ | |||
103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
#define etROOT 24 /* String value of g.zTop: %R */
#define etJSONSTR 25 /* String encoded as a JSON string literal: %j
Use %!j to include double-quotes around it. */
#define etSHELLESC 26 /* Escape a filename for use in a shell command: %$
See blob_append_escaped_arg() for details
"%$" -> adds "./" prefix if necessary.
"%!$" -> omits the "./" prefix. */
/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;
| > | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
#define etROOT 24 /* String value of g.zTop: %R */
#define etJSONSTR 25 /* String encoded as a JSON string literal: %j
Use %!j to include double-quotes around it. */
#define etSHELLESC 26 /* Escape a filename for use in a shell command: %$
See blob_append_escaped_arg() for details
"%$" -> adds "./" prefix if necessary.
"%!$" -> omits the "./" prefix. */
#define etHEX 27 /* Encode a string as hexadecimal */
/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;
|
| ︙ | ︙ | |||
140 141 142 143 144 145 146 | ** most frequently used conversion types first. ** ** NB: When modifying this table is it vital that you also update the fmtchr[] ** variable to match!!! */ static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; static const char aPrefix[] = "-x0\000X0"; | | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
** most frequently used conversion types first.
**
** NB: When modifying this table is it vital that you also update the fmtchr[]
** variable to match!!!
*/
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
static const char aPrefix[] = "-x0\000X0";
static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$H";
static const et_info fmtinfo[] = {
{ 'd', 10, 1, etRADIX, 0, 0 },
{ 's', 0, 4, etSTRING, 0, 0 },
{ 'g', 0, 1, etGENERIC, 30, 0 },
{ 'z', 0, 6, etDYNSTRING, 0, 0 },
{ 'q', 0, 4, etSQLESCAPE, 0, 0 },
{ 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
|
| ︙ | ︙ | |||
174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
{ 'G', 0, 1, etGENERIC, 14, 0 },
{ 'i', 10, 1, etRADIX, 0, 0 },
{ 'n', 0, 0, etSIZE, 0, 0 },
{ '%', 0, 0, etPERCENT, 0, 0 },
{ 'p', 16, 0, etPOINTER, 0, 1 },
{ '/', 0, 0, etPATH, 0, 0 },
{ '$', 0, 0, etSHELLESC, 0, 0 },
{ etERROR, 0,0,0,0,0} /* Must be last */
};
#define etNINFO count(fmtinfo)
/*
** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement.
**
| > | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
{ 'G', 0, 1, etGENERIC, 14, 0 },
{ 'i', 10, 1, etRADIX, 0, 0 },
{ 'n', 0, 0, etSIZE, 0, 0 },
{ '%', 0, 0, etPERCENT, 0, 0 },
{ 'p', 16, 0, etPOINTER, 0, 1 },
{ '/', 0, 0, etPATH, 0, 0 },
{ '$', 0, 0, etSHELLESC, 0, 0 },
{ 'H', 0, 0, etHEX, 0, 0 },
{ etERROR, 0,0,0,0,0} /* Must be last */
};
#define etNINFO count(fmtinfo)
/*
** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement.
**
|
| ︙ | ︙ | |||
236 237 238 239 240 241 242 243 244 245 246 247 248 |
static int StrNLen32(const char *z, int N){
int n = 0;
while( (N-- != 0) && *(z++)!=0 ){ n++; }
return n;
}
#endif
/*
** Return an appropriate set of flags for wiki_convert() for displaying
** comments on a timeline. These flag settings are determined by
** configuration parameters.
**
** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2
| > > > > > > > > > > > > > > > > > | > | < > > | > < | < < < | 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 |
static int StrNLen32(const char *z, int N){
int n = 0;
while( (N-- != 0) && *(z++)!=0 ){ n++; }
return n;
}
#endif
/*
** SETTING: timeline-plaintext boolean default=off
**
** If enabled, no wiki-formatting is done for timeline comment messages.
** Hyperlinks are activated, but they show up on screen using the
** complete input text, not just the display text. No other formatting
** is done.
*/
/*
** SETTING: timeline-hard-newlines boolean default=off
**
** If enabled, the timeline honors newline characters in check-in comments.
** In other words, newlines are coverted into <br> for HTML display.
** The default behavior, when this setting is off, is that newlines are
** treated like any other whitespace character.
*/
/*
** Return an appropriate set of flags for wiki_convert() for displaying
** comments on a timeline. These flag settings are determined by
** configuration parameters.
**
** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2
** flags) and is false for plain "%W". The ! flag indicates that the
** formatting is for display of a check-in comment on the timeline. Such
** comments used to be renderedd differently, but ever since 2020, they
** have been rendered identially, so the ! flag does not make any different
** in the output any more.
*/
int wiki_convert_flags(int altForm2){
static int wikiFlags = 0;
(void)altForm2;
if( wikiFlags==0 ){
wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
if( db_get_boolean("timeline-plaintext", 0) ){
wikiFlags |= WIKI_LINKSONLY;
}
if( db_get_boolean("timeline-hard-newlines", 0) ){
wikiFlags |= WIKI_NEWLINE;
}
}
|
| ︙ | ︙ | |||
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 |
int needQuote;
int limit = flag_alternateform ? va_arg(ap,int) : -1;
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote characters */
char *escarg = va_arg(ap,char*);
isnull = escarg==0;
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
if( limit<0 ) limit = strlen(escarg);
for(i=n=0; i<limit; i++){
if( escarg[i]==q ) n++;
}
needQuote = !isnull && xtype==etSQLESCAPE2;
n += i + 1 + needQuote*2;
if( n>etBUFSIZE ){
bufpt = zExtra = fossil_malloc( n );
}else{
bufpt = buf;
}
j = 0;
if( needQuote ) bufpt[j++] = q;
for(i=0; i<limit; i++){
bufpt[j++] = ch = escarg[i];
if( ch==q ) bufpt[j++] = ch;
}
if( needQuote ) bufpt[j++] = q;
bufpt[j] = 0;
length = j;
| > < | 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 |
int needQuote;
int limit = flag_alternateform ? va_arg(ap,int) : -1;
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote characters */
char *escarg = va_arg(ap,char*);
isnull = escarg==0;
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
if( limit<0 ) limit = strlen(escarg);
if( precision>=0 && precision<limit ) limit = precision;
for(i=n=0; i<limit; i++){
if( escarg[i]==q ) n++;
}
needQuote = !isnull && xtype==etSQLESCAPE2;
n += i + 1 + needQuote*2;
if( n>etBUFSIZE ){
bufpt = zExtra = fossil_malloc( n );
}else{
bufpt = buf;
}
j = 0;
if( needQuote ) bufpt[j++] = q;
for(i=0; i<limit; i++){
bufpt[j++] = ch = escarg[i];
if( ch==q ) bufpt[j++] = ch;
}
if( needQuote ) bufpt[j++] = q;
bufpt[j] = 0;
length = j;
break;
}
case etHTMLIZE: {
int limit = flag_alternateform ? va_arg(ap,int) : -1;
char *zMem = va_arg(ap,char*);
if( zMem==0 ) zMem = "";
zExtra = bufpt = htmlize(zMem, limit);
|
| ︙ | ︙ | |||
841 842 843 844 845 846 847 848 849 850 851 852 853 854 |
break;
}
case etSHELLESC: {
char *zArg = va_arg(ap, char*);
blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
length = width = 0;
break;
}
case etERROR:
buf[0] = '%';
buf[1] = c;
errorflag = 0;
idx = 1+(c!=0);
blob_append(pBlob,"%",idx);
| > > > > > > > > > > > | 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 |
break;
}
case etSHELLESC: {
char *zArg = va_arg(ap, char*);
blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
length = width = 0;
break;
}
case etHEX: {
char *zArg = va_arg(ap, char*);
int szArg = (int)strlen(zArg);
int szBlob = blob_size(pBlob);
u8 *aBuf;
blob_resize(pBlob, szBlob+szArg*2+1);
aBuf = (u8*)&blob_buffer(pBlob)[szBlob];
encode16((const u8*)zArg, aBuf, szArg);
length = width = 0;
break;
}
case etERROR:
buf[0] = '%';
buf[1] = c;
errorflag = 0;
idx = 1+(c!=0);
blob_append(pBlob,"%",idx);
|
| ︙ | ︙ | |||
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 |
void fossil_errorlog(const char *zFormat, ...){
struct tm *pNow;
time_t now;
FILE *out;
const char *z;
int i;
int bDetail = 0;
va_list ap;
static const char *const azEnv[] = { "HTTP_HOST", "HTTP_REFERER",
"HTTP_USER_AGENT",
"PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD",
"REQUEST_URI", "SCRIPT_NAME" };
if( g.zErrlog==0 ) return;
if( g.zErrlog[0]=='-' && g.zErrlog[1]==0 ){
| > | 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 |
void fossil_errorlog(const char *zFormat, ...){
struct tm *pNow;
time_t now;
FILE *out;
const char *z;
int i;
int bDetail = 0;
int bBrief = 0;
va_list ap;
static const char *const azEnv[] = { "HTTP_HOST", "HTTP_REFERER",
"HTTP_USER_AGENT",
"PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD",
"REQUEST_URI", "SCRIPT_NAME" };
if( g.zErrlog==0 ) return;
if( g.zErrlog[0]=='-' && g.zErrlog[1]==0 ){
|
| ︙ | ︙ | |||
1067 1068 1069 1070 1071 1072 1073 1074 1075 |
fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n",
pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday,
pNow->tm_hour, pNow->tm_min, pNow->tm_sec);
va_start(ap, zFormat);
if( zFormat[0]=='X' ){
bDetail = 1;
zFormat++;
}
vfprintf(out, zFormat, ap);
| > > | > > | | | 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 |
fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n",
pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday,
pNow->tm_hour, pNow->tm_min, pNow->tm_sec);
va_start(ap, zFormat);
if( zFormat[0]=='X' ){
bDetail = 1;
zFormat++;
}else if( strncmp(zFormat,"SMTP:",5)==0 ){
bBrief = 1;
}
vfprintf(out, zFormat, ap);
fprintf(out, " (pid %d)\n", (int)getpid());
va_end(ap);
if( g.zPhase!=0 ) fprintf(out, "while in %s\n", g.zPhase);
if( bBrief ){
/* Say nothing more */
}else 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);
fossil_path_free(p);
}else if( (z = P(azEnv[i]))!=0 && z[0]!=0 ){
fprintf(out, "%s=%s\n", azEnv[i], z);
}
}
}
if( out!=stderr ) fclose(out);
}
/*
** The following variable becomes true while processing a fatal error
** or a panic. If additional "recursive-fatal" errors occur while
** shutting down, the recursive errors are silently ignored.
*/
|
| ︙ | ︙ | |||
1122 1123 1124 1125 1126 1127 1128 |
}
else
#endif
if( g.cgiOutput==1 && g.db ){
g.cgiOutput = 2;
cgi_reset_content();
cgi_set_content_type("text/html");
| > | > > > > > > | | > | 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 |
}
else
#endif
if( g.cgiOutput==1 && g.db ){
g.cgiOutput = 2;
cgi_reset_content();
cgi_set_content_type("text/html");
if( g.zLogin!=0 ){
style_set_current_feature("error");
}
style_header("Bad Request");
etag_cancel();
if( g.zLogin==0 ){
/* Do not give unnecessary clues about a malfunction to robots */
@ <p>Something did not work right.</p>
@ <p>%h(z)</p>
}else{
@ <p class="generalError">%h(z)</p>
cgi_set_status(400, "Bad Request");
}
style_finish_page();
cgi_reply();
}else if( !g.fQuiet ){
fossil_force_newline();
fossil_trace("%s\n", z);
}
return rc;
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
1412 1413 1414 1415 1416 1417 1418 |
zPassword);
hash_user_password(g.zLogin);
}
/*
** COMMAND: deconstruct*
**
| | | 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 |
zPassword);
hash_user_password(g.zLogin);
}
/*
** COMMAND: deconstruct*
**
** Usage: %fossil deconstruct ?OPTIONS? DESTINATION
**
** This command exports all artifacts of a given repository and writes all
** artifacts to the file system. The DESTINATION directory will be populated
** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the
** 40+ character artifact ID, AA the first 2 characters.
** If -L|--prefixlength is given, the length (default 2) of the directory prefix
** can be set to 0,1,..,9 characters.
|
| ︙ | ︙ |
Changes to src/regexp.c.
| ︙ | ︙ | |||
680 681 682 683 684 685 686 687 688 689 690 691 692 693 |
}
}
if( j>0 && pRe->zInit[j-1]==0 ) j--;
pRe->nInit = j;
}
return pRe->zErr;
}
/*
** Implementation of the regexp() SQL function. This function implements
** the build-in REGEXP operator. The first argument to the function is the
** pattern and the second argument is the string. So, the SQL statements:
**
** A REGEXP B
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
}
}
if( j>0 && pRe->zInit[j-1]==0 ) j--;
pRe->nInit = j;
}
return pRe->zErr;
}
/*
** The input zIn is a string that we want to match exactly as part of
** a regular expression. Return a new string (in space obtained from
** fossil_malloc() or the equivalent) that escapes all regexp syntax
** characters in zIn.
*/
char *re_quote(const char *zIn){
Blob out;
blob_init(&out, 0, 0);
while( zIn[0] ){
switch( zIn[0] ){
case '.':
case '?':
case '*':
case '+':
case '\\':
case '(':
case ')':
case '[':
case ']':
case '|':
case '^':
case '$':
case '{':
case '}': {
blob_appendf(&out,"\\x%02x", (unsigned char)zIn[0]);
break;
}
default: {
blob_append_char(&out, zIn[0]);
break;
}
}
zIn++;
}
blob_materialize(&out);
return out.aData;
}
/*
** Implementation of the regexp() SQL function. This function implements
** the build-in REGEXP operator. The first argument to the function is the
** pattern and the second argument is the string. So, the SQL statements:
**
** A REGEXP B
|
| ︙ | ︙ |
Changes to src/repolist.c.
| ︙ | ︙ | |||
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 |
struct RepoInfo {
char *zRepoName; /* Name of the repository file */
int isValid; /* True if zRepoName is a valid Fossil repository */
int isRepolistSkin; /* 1 or 2 if this repository wants to be the skin
** for the repository list. 2 means do use this
** repository but do not display it in the list. */
char *zProjName; /* Project Name. Memory from fossil_malloc() */
char *zLoginGroup; /* Name of login group, or NULL. Malloced() */
double rMTime; /* Last update. Julian day number */
};
#endif
/*
** Discover information about the repository given by
** pRepo->zRepoName. The discovered information is stored in other
** fields of the RepoInfo object.
*/
static void remote_repo_info(RepoInfo *pRepo){
sqlite3 *db;
sqlite3_stmt *pStmt;
int rc;
pRepo->isRepolistSkin = 0;
pRepo->isValid = 0;
pRepo->zProjName = 0;
pRepo->zLoginGroup = 0;
pRepo->rMTime = 0.0;
g.dbIgnoreErrors++;
rc = sqlite3_open_v2(pRepo->zRepoName, &db, SQLITE_OPEN_READWRITE, 0);
if( rc ) goto finish_repo_list;
rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
| > > | 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 |
struct RepoInfo {
char *zRepoName; /* Name of the repository file */
int isValid; /* True if zRepoName is a valid Fossil repository */
int isRepolistSkin; /* 1 or 2 if this repository wants to be the skin
** for the repository list. 2 means do use this
** repository but do not display it in the list. */
char *zProjName; /* Project Name. Memory from fossil_malloc() */
char *zProjDesc; /* Project Description. Memory from fossil_malloc() */
char *zLoginGroup; /* Name of login group, or NULL. Malloced() */
double rMTime; /* Last update. Julian day number */
};
#endif
/*
** Discover information about the repository given by
** pRepo->zRepoName. The discovered information is stored in other
** fields of the RepoInfo object.
*/
static void remote_repo_info(RepoInfo *pRepo){
sqlite3 *db;
sqlite3_stmt *pStmt;
int rc;
pRepo->isRepolistSkin = 0;
pRepo->isValid = 0;
pRepo->zProjName = 0;
pRepo->zProjDesc = 0;
pRepo->zLoginGroup = 0;
pRepo->rMTime = 0.0;
g.dbIgnoreErrors++;
rc = sqlite3_open_v2(pRepo->zRepoName, &db, SQLITE_OPEN_READWRITE, 0);
if( rc ) goto finish_repo_list;
rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
|
| ︙ | ︙ | |||
69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
" WHERE name='project-name'",
-1, &pStmt, 0);
if( rc ) goto finish_repo_list;
if( sqlite3_step(pStmt)==SQLITE_ROW ){
pRepo->zProjName = fossil_strdup((char*)sqlite3_column_text(pStmt,0));
}
sqlite3_finalize(pStmt);
rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
" WHERE name='login-group-name'",
-1, &pStmt, 0);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
pRepo->zLoginGroup = fossil_strdup((char*)sqlite3_column_text(pStmt,0));
}
| > > > > > > > > > | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
" WHERE name='project-name'",
-1, &pStmt, 0);
if( rc ) goto finish_repo_list;
if( sqlite3_step(pStmt)==SQLITE_ROW ){
pRepo->zProjName = fossil_strdup((char*)sqlite3_column_text(pStmt,0));
}
sqlite3_finalize(pStmt);
if( rc ) goto finish_repo_list;
rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
" WHERE name='project-description'",
-1, &pStmt, 0);
if( rc ) goto finish_repo_list;
if( sqlite3_step(pStmt)==SQLITE_ROW ){
pRepo->zProjDesc = fossil_strdup((char*)sqlite3_column_text(pStmt,0));
}
sqlite3_finalize(pStmt);
rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
" WHERE name='login-group-name'",
-1, &pStmt, 0);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
pRepo->zLoginGroup = fossil_strdup((char*)sqlite3_column_text(pStmt,0));
}
|
| ︙ | ︙ | |||
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
Blob base; /* document root for all repositories */
int n = 0; /* Number of repositories found */
int allRepo; /* True if running "fossil ui all".
** False if a directory scan of base for repos */
Blob html; /* Html for the body of the repository list */
char *zSkinRepo = 0; /* Name of the repository database used for skins */
char *zSkinUrl = 0; /* URL for the skin database */
assert( g.db==0 );
blob_init(&html, 0, 0);
if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){
/* For the special case of the "repository directory" being "/",
** show all of the repositories named in the ~/.fossil database.
**
** On unix systems, then entries are of the form "repo:/home/..."
** and on Windows systems they are like on unix, starting with a "/"
| > > > > > > > > | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
Blob base; /* document root for all repositories */
int n = 0; /* Number of repositories found */
int allRepo; /* True if running "fossil ui all".
** False if a directory scan of base for repos */
Blob html; /* Html for the body of the repository list */
char *zSkinRepo = 0; /* Name of the repository database used for skins */
char *zSkinUrl = 0; /* URL for the skin database */
const char *zShow; /* Value of FOSSIL_REPOLIST_SHOW environment variable */
int bShowDesc = 0; /* True to show the description column */
int bShowLg = 0; /* True to show the login-group column */
assert( g.db==0 );
zShow = P("FOSSIL_REPOLIST_SHOW");
if( zShow ){
bShowDesc = strstr(zShow,"description")!=0;
bShowLg = strstr(zShow,"login-group")!=0;
}
blob_init(&html, 0, 0);
if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){
/* For the special case of the "repository directory" being "/",
** show all of the repositories named in the ~/.fossil database.
**
** On unix systems, then entries are of the form "repo:/home/..."
** and on Windows systems they are like on unix, starting with a "/"
|
| ︙ | ︙ | |||
136 137 138 139 140 141 142 143 144 145 146 |
" WHERE name GLOB 'repo:*'"
);
allRepo = 1;
}else{
/* The default case: All repositories under the g.zRepositoryName
** directory.
*/
blob_init(&base, g.zRepositoryName, -1);
sqlite3_open(":memory:", &g.db);
db_multi_exec("CREATE TABLE sfile(pathname TEXT);");
db_multi_exec("CREATE TABLE vfile(pathname);");
| > > > > | > > > > > > > > > > > > > > > > > > | | | | > > > > > > > > > | > > > > | > > | | 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 |
" WHERE name GLOB 'repo:*'"
);
allRepo = 1;
}else{
/* The default case: All repositories under the g.zRepositoryName
** directory.
*/
Glob *pExclude;
blob_init(&base, g.zRepositoryName, -1);
db_close(0);
assert( g.db==0 );
sqlite3_open(":memory:", &g.db);
db_multi_exec("CREATE TABLE sfile(pathname TEXT);");
db_multi_exec("CREATE TABLE vfile(pathname);");
pExclude = glob_create("*/proc,proc");
vfile_scan(&base, blob_size(&base), 0, pExclude, 0, ExtFILE);
glob_free(pExclude);
db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"
#if USE_SEE
" AND pathname NOT GLOB '*[^/].efossil'"
#endif
);
allRepo = 0;
}
n = db_int(0, "SELECT count(*) FROM sfile");
if( n==0 ){
sqlite3_close(g.db);
g.db = 0;
g.repositoryOpen = 0;
g.localOpen = 0;
return 0;
}else{
Stmt q;
double rNow;
char zType[16]; /* Column type letters for class "sortable" */
int nType;
zType[0] = 't'; /* Repo name */
zType[1] = 'x'; /* Space between repo-name and project-name */
zType[2] = 't'; /* Project name */
nType = 3;
if( bShowDesc ){
zType[nType++] = 'x'; /* Space between name and description */
zType[nType++] = 't'; /* Project description */
}
zType[nType++] = 'x'; /* space before age */
zType[nType++] = 'k'; /* Project age */
if( bShowLg ){
zType[nType++] = 'x'; /* space before login-group */
zType[nType++] = 't'; /* Login Group */
}
zType[nType] = 0;
blob_appendf(&html,
"<table border='0' class='sortable' data-init-sort='1'"
" data-column-types='%s' cellspacing='0' cellpadding='0'><thead>\n"
"<tr><th>Filename</th><th> </th>\n"
"<th%s><nobr>Project Name</nobr></th>\n",
zType, (bShowDesc ? " width='25%'" : ""));
if( bShowDesc ){
blob_appendf(&html,
"<th> </th>\n"
"<th width='25%%'><nobr>Project Description</nobr></th>\n"
);
}
blob_appendf(&html,
"<th> </th>"
"<th><nobr>Last Modified</nobr></th>\n"
);
if( bShowLg ){
blob_appendf(&html,
"<th> </th>"
"<th><nobr>Login Group</nobr></th></tr>\n"
);
}
blob_appendf(&html,"</thead><tbody>\n");
db_prepare(&q, "SELECT pathname"
" FROM sfile ORDER BY pathname COLLATE nocase;");
rNow = db_double(0, "SELECT julianday('now')");
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
int nName = (int)strlen(zName);
int nSuffix = 7; /* ".fossil" */
|
| ︙ | ︙ | |||
228 229 230 231 232 233 234 |
iAge = (sqlite3_int64)((rNow - x.rMTime)*86400);
zAge = human_readable_age(rNow - x.rMTime);
if( x.rMTime==0.0 ){
/* This repository has no entry in the "event" table.
** Its age will still be maximum, so data-sortkey will work. */
zAge = mprintf("unknown");
}
| | | | | | | | > > | | > > > > > > > > > | | > > > | > > | | | | 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 |
iAge = (sqlite3_int64)((rNow - x.rMTime)*86400);
zAge = human_readable_age(rNow - x.rMTime);
if( x.rMTime==0.0 ){
/* This repository has no entry in the "event" table.
** Its age will still be maximum, so data-sortkey will work. */
zAge = mprintf("unknown");
}
blob_appendf(&html, "<tr><td valign='top'><nobr>");
if( !file_ends_with_repository_extension(zName,0) ){
/* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands
** do not work for repositories whose names do not end in ".fossil".
** So do not hyperlink those cases. */
blob_appendf(&html,"%h",zName);
} else if( sqlite3_strglob("*/.*", zName)==0 ){
/* Do not show hyperlinks for hidden repos */
blob_appendf(&html, "%h (hidden)", zName);
} else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
blob_appendf(&html,
"<a href='%R/%T/home' target='_blank'>/%h</a>\n",
zUrl, zName);
}else if( file_ends_with_repository_extension(zName,1) ){
/* As described in
** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if
** foo.fossil and foo/bar.fossil both exist and we create a
** link to foo/bar/... then the URI dispatcher will instead
** see that as a link to foo.fossil. In such cases, do not
** emit a link to foo/bar.fossil. */
char * zDirPart = file_dirname(zName);
if( db_exists("SELECT 1 FROM sfile "
"WHERE pathname=(%Q || '.fossil') COLLATE nocase"
#if USE_SEE
" OR pathname=(%Q || '.efossil') COLLATE nocase"
#endif
, zDirPart
#if USE_SEE
, zDirPart
#endif
) ){
blob_appendf(&html,
"<s>%h</s> (directory/repo name collision)\n",
zName);
}else{
blob_appendf(&html,
"<a href='%R/%T/home' target='_blank'>%h</a>\n",
zUrl, zName);
}
fossil_free(zDirPart);
}else{
blob_appendf(&html,
"<a href='%R/%T/home' target='_blank'>%h</a>\n",
zUrl, zName);
}
blob_appendf(&html,"</nobr></td>\n");
if( x.zProjName ){
blob_appendf(&html, "<td> </td><td valign='top'>%h</td>\n",
x.zProjName);
fossil_free(x.zProjName);
}else{
blob_appendf(&html, "<td> </td><td></td>\n");
}
if( !bShowDesc ){
/* Do nothing */
}else if( x.zProjDesc ){
blob_appendf(&html, "<td> </td><td valign='top'>%h</td>\n",
x.zProjDesc);
fossil_free(x.zProjDesc);
}else{
blob_appendf(&html, "<td> </td><td></td>\n");
}
blob_appendf(&html,
"<td> </td><td data-sortkey='%08x' align='center' valign='top'>"
"<nobr>%h</nobr></td>\n",
(int)iAge, zAge);
fossil_free(zAge);
if( !bShowLg ){
blob_appendf(&html, "</tr>\n");
}else if( x.zLoginGroup ){
blob_appendf(&html, "<td> </td><td valign='top'>"
"<nobr>%h</nobr></td></tr>\n",
x.zLoginGroup);
fossil_free(x.zLoginGroup);
}else{
blob_appendf(&html, "<td> </td><td></td></tr>\n");
}
sqlite3_free(zUrl);
}
db_finalize(&q);
blob_appendf(&html,"</tbody></table>\n");
}
if( zSkinRepo ){
char *zNewBase = mprintf("%s/%s", g.zBaseURL, zSkinUrl);
g.zBaseURL = 0;
set_base_url(zNewBase);
db_open_repository(zSkinRepo);
fossil_free(zSkinRepo);
|
| ︙ | ︙ | |||
314 315 316 317 318 319 320 321 322 323 324 325 326 |
login_check_credentials();
style_set_current_feature("repolist");
style_header("Repository List");
@ %s(blob_str(&html))
style_table_sorter();
style_finish_page();
}else{
/* If no repositories were found that had the "repolist_skin"
** property set, then use a default skin */
@ <html>
@ <head>
@ <base href="%s(g.zBaseURL)/">
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
| > | | | 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 |
login_check_credentials();
style_set_current_feature("repolist");
style_header("Repository List");
@ %s(blob_str(&html))
style_table_sorter();
style_finish_page();
}else{
const char *zTitle = PD("FOSSIL_REPOLIST_TITLE","Repository List");
/* If no repositories were found that had the "repolist_skin"
** property set, then use a default skin */
@ <html>
@ <head>
@ <base href="%s(g.zBaseURL)/">
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>%h(zTitle)</title>
@ </head>
@ <body>
@ <h1 align="center">%h(zTitle)</h1>
@ %s(blob_str(&html))
@ <script>%s(builtin_text("sorttable.js"))</script>
@ </body>
@ </html>
}
blob_reset(&html);
cgi_reply();
|
| ︙ | ︙ |
Changes to src/report.c.
| ︙ | ︙ | |||
581 582 583 584 585 586 587 |
rn = 0;
zTitle = mprintf("Copy Of %s", zTitle);
zOwner = g.zLogin;
}
}
if( zOwner==0 ) zOwner = g.zLogin;
style_submenu_element("Cancel", "%R/reportlist");
| < < < | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
rn = 0;
zTitle = mprintf("Copy Of %s", zTitle);
zOwner = g.zLogin;
}
}
if( zOwner==0 ) zOwner = g.zLogin;
style_submenu_element("Cancel", "%R/reportlist");
style_header("%s", rn>0 ? "Edit Report Format":"Create New Report Format");
if( zErr ){
@ <blockquote class="reportError">%h(zErr)</blockquote>
}
@ <form action="rptedit" method="post"><div>
@ <input type="hidden" name="rn" value="%d(rn)">
@ <p>Report Title:<br>
|
| ︙ | ︙ | |||
895 896 897 898 899 900 901 |
}
++pState->nCount;
/* Output the separator above each entry in a table which has multiple lines
** per database entry.
*/
if( pState->iNewRow>=0 ){
| | > | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 |
}
++pState->nCount;
/* Output the separator above each entry in a table which has multiple lines
** per database entry.
*/
if( pState->iNewRow>=0 ){
@ <tr><td colspan="%d(pState->nCol)" style="padding:0px">
@ <hr style="margin:0px"></td></tr>
}
/* Output the data for this entry from the database
*/
zBg = pState->iBg>=0 ? azArg[pState->iBg] : 0;
if( zBg==0 ) zBg = "white";
@ <tr style="background-color:%h(zBg)">
|
| ︙ | ︙ |
Changes to src/schema.c.
| ︙ | ︙ | |||
509 510 511 512 513 514 515 | /* ** Predefined tagid values */ #if INTERFACE # define TAG_BGCOLOR 1 /* Set the background color for display */ # define TAG_COMMENT 2 /* The check-in comment */ | | | 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | /* ** Predefined tagid values */ #if INTERFACE # define TAG_BGCOLOR 1 /* Set the background color for display */ # define TAG_COMMENT 2 /* The check-in comment */ # define TAG_USER 3 /* User who made a check-in */ # define TAG_DATE 4 /* The date of a check-in */ # define TAG_HIDDEN 5 /* Do not display in timeline */ # define TAG_PRIVATE 6 /* Do not sync */ # define TAG_CLUSTER 7 /* A cluster */ # define TAG_BRANCH 8 /* Value is name of the current branch */ # define TAG_CLOSED 9 /* Do not display this check-in as a leaf */ # define TAG_PARENT 10 /* Change to parentage on a check-in */ |
| ︙ | ︙ |
Changes to src/search.c.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 | ******************************************************************************* ** ** This file contains code to implement a search functions ** against timeline comments, check-in content, wiki pages, tickets, ** and/or forum posts. ** ** The search can be either a per-query "grep"-like search that scans | | | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ******************************************************************************* ** ** This file contains code to implement a search functions ** against timeline comments, check-in content, wiki pages, tickets, ** and/or forum posts. ** ** The search can be either a per-query "grep"-like search that scans ** the entire corpus. Or it can use the FTS5 search engine of SQLite. ** The choice is an administrator configuration option. ** ** The first option is referred to as "full-scan search". The second ** option is called "indexed search". ** ** The code in this file is ordered approximately as follows: ** |
| ︙ | ︙ | |||
562 563 564 565 566 567 568 | } /* ** Testing the search function. ** ** COMMAND: search* ** | | | | | < | | > > > > > > | > > | < < < > > | | | | > > > | | | > > > > < | > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < > > > | > | | < | < < > | 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 |
}
/*
** Testing the search function.
**
** COMMAND: search*
**
** Usage: %fossil search [OPTIONS] PATTERN...
**
** Search the repository for PATTERN and show matches. Depending on
** options and how the administrator has search configured for the
** repository, the search can cover:
**
** * check-in comments (-c)
** * embedded documentation (--docs)
** * forum posts (--forum)
** * tickets (--tickets)
** * tech notes (--technotes)
** * wiki pages (--wiki)
** * built-in fossil help text (-h)
** * all of the above (-a)
**
** Use options below to select the scope of the search. The
** default is check-in comments only (-c).
**
** Output is colorized if writing to a TTY and if the NO_COLOR environment
** variable is not set. Use the "--highlight 0" option to disable colorization
** or use "--highlight 91" to force it on. Change the argument to --highlight
** to change the color.
**
** Options:
** -a|--all Search everything
** -c|--checkins Search checkin comments
** --docs Search embedded documentation
** --forum Search forum posts
** -h|--bi-help Search built-in help
** --highlight N Used VT100 color N for matching text. 0 means "off".
** -n|--limit N Limit output to N matches
** --technotes Search tech notes
** --tickets Search tickets
** -W|--width WIDTH Set display width to WIDTH columns, 0 for
** unlimited. Defaults to the terminal's width.
** --wiki Search wiki
*/
void search_cmd(void){
Blob pattern;
int i;
Blob sql = empty_blob;
Stmt q;
int iBest;
int srchFlags = 0;
int bFts = 1; /* Use FTS search by default now */
char fAll = NULL != find_option("all", "a", 0);
const char *zLimit = find_option("limit","n",1);
const char *zScope = 0;
const char *zWidth = find_option("width","W",1);
int bDebug = find_option("debug",0,0)!=0; /* Undocumented */
int nLimit = zLimit ? atoi(zLimit) : -1000;
int width;
int nTty = 0; /* VT100 highlight color for matching text */
const char *zHighlight = 0;
int bFlags = 0; /* DB open flags */
nTty = terminal_is_vt100();
/* Undocumented option to change highlight color */
zHighlight = find_option("highlight",0,1);
if( zHighlight ) nTty = atoi(zHighlight);
/* Undocumented option (legacy) */
zScope = find_option("scope",0,1);
if( find_option("fts",0,0)!=0 ) bFts = 1; /* Undocumented legacy */
if( find_option("legacy",0,0)!=0 ) bFts = 0; /* Undocumented */
if( zWidth ){
width = atoi(zWidth);
if( (width!=0) && (width<=20) ){
fossil_fatal("-W|--width value must be >20 or 0");
}
}else{
width = -1;
}
if( zScope ){
for(i=0; zScope[i]; i++){
switch( zScope[i] ){
case 'a': srchFlags = SRCH_ALL; break;
case 'c': srchFlags |= SRCH_CKIN; break;
case 'd': srchFlags |= SRCH_DOC; break;
case 'e': srchFlags |= SRCH_TECHNOTE; break;
case 'f': srchFlags |= SRCH_FORUM; break;
case 'h': srchFlags |= SRCH_HELP; break;
case 't': srchFlags |= SRCH_TKT; break;
case 'w': srchFlags |= SRCH_WIKI; break;
}
}
bFts = 1;
}
if( find_option("all","a",0) ){ srchFlags |= SRCH_ALL; bFts = 1; }
if( find_option("bi-help","h",0) ){ srchFlags |= SRCH_HELP; bFts = 1; }
if( find_option("checkins","c",0) ){ srchFlags |= SRCH_CKIN; bFts = 1; }
if( find_option("docs",0,0) ){ srchFlags |= SRCH_DOC; bFts = 1; }
if( find_option("forum",0,0) ){ srchFlags |= SRCH_FORUM; bFts = 1; }
if( find_option("technotes",0,0) ){ srchFlags |= SRCH_TECHNOTE; bFts = 1; }
if( find_option("tickets",0,0) ){ srchFlags |= SRCH_TKT; bFts = 1; }
if( find_option("wiki",0,0) ){ srchFlags |= SRCH_WIKI; bFts = 1; }
/* If no search objects are specified, default to "check-in comments" */
if( srchFlags==0 ) srchFlags = SRCH_CKIN;
if( srchFlags==SRCH_HELP ) bFlags = OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE;
db_find_and_open_repository(bFlags, 0);
verify_all_options();
if( g.argc<3 ) return;
login_set_capabilities("s", 0);
if( search_restrict(srchFlags)==0 && (srchFlags & SRCH_HELP)==0 ){
const char *zC1 = 0, *zPlural = "s";
if( srchFlags & SRCH_TECHNOTE ){ zC1 = "technote"; }
if( srchFlags & SRCH_TKT ){ zC1 = "ticket"; }
if( srchFlags & SRCH_FORUM ){ zC1 = "forum"; zPlural = ""; }
if( srchFlags & SRCH_DOC ){ zC1 = "document"; }
if( srchFlags & SRCH_WIKI ){ zC1 = "wiki"; zPlural = ""; }
if( srchFlags & SRCH_CKIN ){ zC1 = "check-in"; }
fossil_print(
"Search of %s%s is disabled on this repository.\n"
"Enable using \"fossil fts-config enable %s\".\n",
zC1, zPlural, zC1
);
return;
}
blob_init(&pattern, g.argv[2], -1);
for(i=3; i<g.argc; i++){
blob_appendf(&pattern, " %s", g.argv[i]);
}
if( bFts ){
/* Search using FTS */
Blob com;
Blob snip;
const char *zPattern = blob_str(&pattern);
search_sql_setup(g.db);
add_content_sql_commands(g.db);
db_multi_exec(
"CREATE TEMP TABLE x(label,url,score,id,date,snip);"
);
if( !search_index_exists() ){
search_fullscan(zPattern, srchFlags); /* Full-scan search */
}else{
search_update_index(srchFlags); /* Update the index */
search_indexed(zPattern, srchFlags); /* Indexed search */
if( srchFlags & SRCH_HELP ){
search_fullscan(zPattern, SRCH_HELP);
}
}
db_prepare(&q, "SELECT snip, label, score, id, date"
" FROM x"
" ORDER BY score DESC, date DESC;");
blob_init(&com, 0, 0);
blob_init(&snip, 0, 0);
if( width<0 ) width = terminal_get_width(80);
while( db_step(&q)==SQLITE_ROW ){
const char *zSnippet = db_column_text(&q, 0);
const char *zLabel = db_column_text(&q, 1);
const char *zDate = db_column_text(&q, 4);
const char *zScore = db_column_text(&q, 2);
const char *zId = db_column_text(&q, 3);
char *zOrig;
blob_appendf(&snip, "%s", zSnippet);
zOrig = blob_materialize(&snip);
blob_init(&snip, 0, 0);
html_to_plaintext(zOrig, &snip, (nTty?HTOT_VT100:0)|HTOT_FLOW|HTOT_TRIM);
fossil_free(zOrig);
blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate);
if( bDebug ){
blob_appendf(&com," score: %s id: %s", zScore, zId);
}
comment_print(blob_str(&com), 0, 5, width,
COMMENT_PRINT_TRIM_CRLF |
COMMENT_PRINT_WORD_BREAK |
|
| ︙ | ︙ | |||
729 730 731 732 733 734 735 | /* What to search for */ #define SRCH_CKIN 0x0001 /* Search over check-in comments */ #define SRCH_DOC 0x0002 /* Search over embedded documents */ #define SRCH_TKT 0x0004 /* Search over tickets */ #define SRCH_WIKI 0x0008 /* Search over wiki */ #define SRCH_TECHNOTE 0x0010 /* Search over tech notes */ #define SRCH_FORUM 0x0020 /* Search over forum messages */ | > | > > > | | | | | > | 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 |
/* What to search for */
#define SRCH_CKIN 0x0001 /* Search over check-in comments */
#define SRCH_DOC 0x0002 /* Search over embedded documents */
#define SRCH_TKT 0x0004 /* Search over tickets */
#define SRCH_WIKI 0x0008 /* Search over wiki */
#define SRCH_TECHNOTE 0x0010 /* Search over tech notes */
#define SRCH_FORUM 0x0020 /* Search over forum messages */
#define SRCH_HELP 0x0040 /* Search built-in help (full-scan only) */
#define SRCH_ALL 0x007f /* Search over everything */
#endif
/*
** Remove bits from srchFlags which are disallowed by either the
** current server configuration or by user permissions. Return
** the revised search flags mask.
**
** If bFlex is true, that means allow through the SRCH_HELP option
** even if it is not explicitly enabled.
*/
unsigned int search_restrict(unsigned int srchFlags){
static unsigned int knownGood = 0;
static unsigned int knownBad = 0;
static const struct { unsigned m; const char *zKey; } aSetng[] = {
{ SRCH_CKIN, "search-ci" },
{ SRCH_DOC, "search-doc" },
{ SRCH_TKT, "search-tkt" },
{ SRCH_WIKI, "search-wiki" },
{ SRCH_TECHNOTE, "search-technote" },
{ SRCH_FORUM, "search-forum" },
{ SRCH_HELP, "search-help" },
};
int i;
if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE);
if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT);
if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI);
if( g.perm.RdForum==0) srchFlags &= ~(SRCH_FORUM);
for(i=0; i<count(aSetng); i++){
|
| ︙ | ︙ | |||
910 911 912 913 914 915 916 917 918 919 920 921 922 923 |
" 'f'||rid,"
" datetime(event.mtime),"
" search_snippet()"
" FROM event JOIN blob on event.objid=blob.rid"
" WHERE search_match('',body('f',rid,NULL));"
);
}
}
/*
** Number of significant bits in a u32
*/
static int nbits(u32 x){
int n = 0;
| > > > > > > > > > > > > > > > > > > > > > | 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 |
" 'f'||rid,"
" datetime(event.mtime),"
" search_snippet()"
" FROM event JOIN blob on event.objid=blob.rid"
" WHERE search_match('',body('f',rid,NULL));"
);
}
if( (srchFlags & SRCH_HELP)!=0 ){
const char *zPrefix;
helptext_vtab_register(g.db);
if( srchFlags==SRCH_HELP ){
zPrefix = "The";
}else{
zPrefix = "Built-in help for the";
}
db_multi_exec(
"INSERT INTO x(label,url,score,id,snip)"
" SELECT format('%q \"%%s\" %%s',name,type),"
" '/help?cmd='||name,"
" search_score(),"
" 'h'||rowid,"
" search_snippet()"
" FROM helptext"
" WHERE search_match(format('the \"%%s\" %%s',name,type),"
" helptext.helptext);",
zPrefix
);
}
}
/*
** Number of significant bits in a u32
*/
static int nbits(u32 x){
int n = 0;
|
| ︙ | ︙ | |||
1036 1037 1038 1039 1040 1041 1042 |
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 ){
| | | 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 |
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 FTS5 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 */
zSnippetCall = "snippet(ftsidx,-1,'<mark>','</mark>',' ... ',35)";
}
blob_appendf(&sql,
|
| ︙ | ︙ | |||
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 |
static const struct { unsigned m; char c; } aMask[] = {
{ SRCH_CKIN, 'c' },
{ SRCH_DOC, 'd' },
{ SRCH_TKT, 't' },
{ SRCH_WIKI, 'w' },
{ SRCH_TECHNOTE, 'e' },
{ SRCH_FORUM, 'f' },
};
int i;
for(i=0; i<count(aMask); i++){
if( srchFlags & aMask[i].m ){
blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c);
zSep = " OR ";
}
| > | 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 |
static const struct { unsigned m; char c; } aMask[] = {
{ SRCH_CKIN, 'c' },
{ SRCH_DOC, 'd' },
{ SRCH_TKT, 't' },
{ SRCH_WIKI, 'w' },
{ SRCH_TECHNOTE, 'e' },
{ SRCH_FORUM, 'f' },
{ SRCH_HELP, 'h' },
};
int i;
for(i=0; i<count(aMask); i++){
if( srchFlags & aMask[i].m ){
blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c);
zSep = " OR ";
}
|
| ︙ | ︙ | |||
1155 1156 1157 1158 1159 1160 1161 |
Stmt q;
int nRow = 0;
int nLimit = db_get_int("search-limit", 100);
if( P("searchlimit")!=0 ){
nLimit = atoi(P("searchlimit"));
}
| | > > > | 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 |
Stmt q;
int nRow = 0;
int nLimit = db_get_int("search-limit", 100);
if( P("searchlimit")!=0 ){
nLimit = atoi(P("searchlimit"));
}
srchFlags = search_restrict(srchFlags) | (srchFlags & SRCH_HELP);
if( srchFlags==0 ) return 0;
search_sql_setup(g.db);
add_content_sql_commands(g.db);
db_multi_exec(
"CREATE TEMP TABLE x(label,url,score,id,date,snip);"
);
if( !search_index_exists() ){
search_fullscan(zPattern, srchFlags); /* Full-scan search */
}else{
search_update_index(srchFlags); /* Update the index, if necessary */
search_indexed(zPattern, srchFlags); /* Indexed search */
if( srchFlags & SRCH_HELP ){
search_fullscan(zPattern, SRCH_HELP);
}
}
db_prepare(&q, "SELECT url, snip, label, score, id, substr(date,1,10)"
" FROM x"
" ORDER BY score DESC, date DESC;");
while( db_step(&q)==SQLITE_ROW ){
const char *zUrl = db_column_text(&q, 0);
const char *zSnippet = db_column_text(&q, 1);
|
| ︙ | ︙ | |||
1221 1222 1223 1224 1225 1226 1227 | ** 0x01 If the y= query parameter is present, use it as an addition ** restriction what to search. ** ** 0x02 Show nothing if search is disabled. ** ** Return true if there are search results. */ | | > > > > > | > | > | > | | > > > > > < < | > > | < | | | | | | | | > | | 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 |
** 0x01 If the y= query parameter is present, use it as an addition
** restriction what to search.
**
** 0x02 Show nothing if search is disabled.
**
** Return true if there are search results.
*/
int search_screen(unsigned srchAllowed, int mFlags){
const char *zType = 0;
const char *zClass = 0;
const char *zDisable1;
const char *zDisable2;
const char *zPattern;
int fDebug = PB("debug");
int haveResult = 0;
int srchThisTime;
const char *zY = PD("y","all");
if( zY[0]=='h' && zY[1]==0 ){
srchAllowed = search_restrict(srchAllowed) | (srchAllowed & SRCH_HELP);
}else{
srchAllowed = search_restrict(srchAllowed);
}
switch( srchAllowed ){
case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break;
case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break;
case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break;
case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break;
case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break;
case SRCH_FORUM: zType = " Forum"; zClass = "Frm"; break;
case SRCH_HELP: zType = " Help"; zClass = "Hlp"; break;
}
if( srchAllowed==0 ){
if( mFlags & 0x02 ) return 0;
zDisable1 = " disabled";
zDisable2 = " disabled";
zPattern = "";
}else{
zDisable1 = ""; /* Was: " autofocus" */
zDisable2 = "";
zPattern = PD("s","");
}
@ <form method='GET' action='%R/%T(g.zPath)'>
if( zClass ){
@ <div class='searchForm searchForm%s(zClass)'>
}else{
@ <div class='searchForm'>
}
@ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)>
srchThisTime = srchAllowed;
if( (mFlags & 0x01)!=0 && (srchAllowed & (srchAllowed-1))!=0 ){
static const struct {
const char *z;
const char *zNm;
unsigned m;
} aY[] = {
{ "all", "All", SRCH_ALL },
{ "c", "Check-ins", SRCH_CKIN },
{ "d", "Docs", SRCH_DOC },
{ "t", "Tickets", SRCH_TKT },
{ "w", "Wiki", SRCH_WIKI },
{ "e", "Tech Notes", SRCH_TECHNOTE },
{ "f", "Forum", SRCH_FORUM },
{ "h", "Help", SRCH_HELP },
};
int i;
@ <select size='1' name='y'>
for(i=0; i<count(aY); i++){
if( (aY[i].m & srchAllowed)==0 ) continue;
if( aY[i].m==SRCH_HELP && fossil_strcmp(zY,"h")!=0
&& search_restrict(SRCH_HELP)==0 ) continue;
cgi_printf("<option value='%s'", aY[i].z);
if( fossil_strcmp(zY,aY[i].z)==0 ){
srchThisTime &= aY[i].m;
cgi_printf(" selected");
}
cgi_printf(">%s</option>\n", aY[i].zNm);
}
@ </select>
}
if( fDebug ){
@ <input type="hidden" name="debug" value="1">
}
@ <input type="submit" value="Search%s(zType)"%s(zDisable2)>
if( srchAllowed==0 && srchThisTime==0 ){
@ <p class="generalError">Search is disabled</p>
}
@ </div></form>
while( fossil_isspace(zPattern[0]) ) zPattern++;
if( zPattern[0] ){
if( zClass ){
@ <div class='searchResult searchResult%s(zClass)'>
}else{
@ <div class='searchResult'>
}
if( search_run_and_output(zPattern, srchThisTime, fDebug)==0 ){
@ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p>
}
@ </div>
haveResult = 1;
}
return haveResult;
}
/*
** WEBPAGE: search
**
** Search for check-in comments, documents, tickets, or wiki that
** match a user-supplied pattern.
**
** s=PATTERN Specify the full-text pattern to search for
** y=TYPE What to search.
** c -> check-ins,
** d -> documentation,
** t -> tickets,
** w -> wiki,
** e -> tech notes,
** f -> forum,
** h -> built-in help,
** all -> everything.
*/
void search_page(void){
const int isSearch = P("s")!=0;
login_check_credentials();
style_header("Search%s", isSearch ? " Results" : "");
cgi_check_for_malice();
search_screen(SRCH_ALL, 1);
|
| ︙ | ︙ | |||
1372 1373 1374 1375 1376 1377 1378 |
wiki_convert(&tail, &html, 0);
blob_reset(&tail);
}else{
blob_append(pOut, "\n", 1);
wiki_convert(pIn, &html, 0);
}
}
| | | | 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 |
wiki_convert(&tail, &html, 0);
blob_reset(&tail);
}else{
blob_append(pOut, "\n", 1);
wiki_convert(pIn, &html, 0);
}
}
html_to_plaintext(blob_str(&html), pOut, 0);
}else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
}else if( fossil_strcmp(zMimetype,"text/html")==0 ){
if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
pHtml = pIn;
}
blob_appendf(pOut, "%s\n", blob_str(&title));
if( blob_size(pHtml) ){
html_to_plaintext(blob_str(pHtml), pOut, 0);
}else{
blob_append(pOut, blob_buffer(pIn), blob_size(pIn));
}
blob_reset(&html);
blob_reset(&title);
}
|
| ︙ | ︙ | |||
2106 2107 2108 2109 2110 2111 2112 |
if( db_table_exists("repository","chat") ){
chat_rebuild_index(1);
}
fossil_print(" done\n");
}
/*
| | | | | | | | | | | | | | | | | | | > | 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 |
if( db_table_exists("repository","chat") ){
chat_rebuild_index(1);
}
fossil_print(" done\n");
}
/*
** COMMAND: fts-config* abbrv-subcom
**
** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
**
** The "fossil fts-config" command configures the full-text search capabilities
** of the repository. Subcommands:
**
** reindex Rebuild the search index. This is a no-op if
** index search is disabled
**
** index (on|off) Turn the search index on or off
**
** enable TYPE .. Enable search for TYPE. TYPE is one of:
** check-in, document, ticket, wiki, technote,
** forum, help, or all
**
** disable TYPE ... Disable search for TYPE
**
** tokenizer VALUE Select a tokenizer for indexed search. VALUE
** may be one of (porter, on, off, trigram, unicode61),
** and "on" is equivalent to "porter". Unindexed
** search never uses tokenization or stemming.
**
** The current search settings are displayed after any changes are applied.
** Run this command with no arguments to simply see the settings.
*/
void fts_config_cmd(void){
static const struct {
int iCmd;
const char *z;
} aCmd[] = {
{ 1, "reindex" },
{ 2, "index" },
{ 3, "disable" },
{ 4, "enable" },
{ 5, "tokenizer"},
};
static const struct {
const char *zSetting;
const char *zName;
const char *zSw;
} aSetng[] = {
{ "search-ci", "check-in search:", "c" },
{ "search-doc", "document search:", "d" },
{ "search-tkt", "ticket search:", "t" },
{ "search-wiki", "wiki search:", "w" },
{ "search-technote", "technote search:", "e" },
{ "search-forum", "forum search:", "f" },
{ "search-help", "built-in help search:", "h" },
};
char *zSubCmd = 0;
int i, j, n;
int iCmd = 0;
int iAction = 0;
db_find_and_open_repository(0, 0);
if( g.argc>2 ){
|
| ︙ | ︙ | |||
2190 2191 2192 2193 2194 2195 2196 2197 |
if( g.argc<3 ) usage("index (on|off)");
iAction = 1 + is_truth(g.argv[3]);
}
db_begin_transaction();
/* Adjust search settings */
if( iCmd==3 || iCmd==4 ){
const char *zCtrl;
| > > > | > > | > > > > > > > > > > > > > | > > > > > > > > > > | | > > | 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 |
if( g.argc<3 ) usage("index (on|off)");
iAction = 1 + is_truth(g.argv[3]);
}
db_begin_transaction();
/* Adjust search settings */
if( iCmd==3 || iCmd==4 ){
int k;
const char *zCtrl;
for(k=2; k<g.argc; k++){
if( k==2 ){
if( g.argc<4 ){
zCtrl = "all";
}else{
zCtrl = g.argv[3];
k++;
}
}else{
zCtrl = g.argv[k];
}
if( fossil_strcmp(zCtrl,"all")==0 ){
zCtrl = "cdtwefh";
}
if( strlen(zCtrl)>=4 ){
/* If the argument to "enable" or "disable" is a string of at least
** 4 characters which matches part of any aSetng.zName, then use that
** one aSetng value only. */
char *zGlob = mprintf("*%s*", zCtrl);
for(j=0; j<count(aSetng); j++){
if( sqlite3_strglob(zGlob, aSetng[j].zName)==0 ){
db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0);
zCtrl = 0;
break;
}
}
fossil_free(zGlob);
}
if( zCtrl ){
for(j=0; j<count(aSetng); j++){
if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){
db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0);
}
}
}
}
}else if( iCmd==5 ){
int iOldTokenizer, iNewTokenizer;
if( g.argc<4 ) usage("tokenizer porter|on|off|trigram|unicode61");
iOldTokenizer = search_tokenizer_type(0);
db_set("search-tokenizer",
|
| ︙ | ︙ | |||
2222 2223 2224 2225 2226 2227 2228 |
}
if( iAction>=2 ){
search_rebuild_index();
}
/* Always show the status before ending */
for(i=0; i<count(aSetng); i++){
| | | | | | | | 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 |
}
if( iAction>=2 ){
search_rebuild_index();
}
/* Always show the status before ending */
for(i=0; i<count(aSetng); i++){
fossil_print("%-21s %s\n", aSetng[i].zName,
db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off");
}
fossil_print("%-21s %s\n", "tokenizer:",
search_tokenizer_for_string(0));
if( search_index_exists() ){
int pgsz = db_int64(0, "PRAGMA repository.page_size;");
i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz;
i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat"
" WHERE schema='repository'"
" AND name LIKE 'fts%%'")*pgsz;
char zSize[50];
fossil_print("%-21s FTS%d\n", "full-text index:", search_index_type(1));
fossil_print("%-21s %d\n", "documents:",
db_int(0, "SELECT count(*) FROM ftsdocs"));
approxSizeName(sizeof(zSize), zSize, nFts);
fossil_print("%-21s %s (%.1f%% of repository)\n", "space used",
zSize, 100.0*((double)nFts/(double)nTotal));
}else{
fossil_print("%-21s disabled\n", "full-text index:");
}
db_end_transaction(0);
}
/*
** WEBPAGE: test-ftsdocs
**
|
| ︙ | ︙ |
Changes to src/security_audit.c.
| ︙ | ︙ | |||
98 99 100 101 102 103 104 105 106 107 108 109 110 111 | const char *zAnonCap; /* Capabilities of user "anonymous" and "nobody" */ const char *zDevCap; /* Capabilities of user group "developer" */ const char *zReadCap; /* Capabilities of user group "reader" */ const char *zPubPages; /* GLOB pattern for public pages */ const char *zSelfCap; /* Capabilities of self-registered users */ int hasSelfReg = 0; /* True if able to self-register */ const char *zPublicUrl; /* Canonical access URL */ Blob cmd; char *z; int n, i; CapabilityString *pCap; char **azCSP; /* Parsed content security policy */ login_check_credentials(); | > | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | const char *zAnonCap; /* Capabilities of user "anonymous" and "nobody" */ const char *zDevCap; /* Capabilities of user group "developer" */ const char *zReadCap; /* Capabilities of user group "reader" */ const char *zPubPages; /* GLOB pattern for public pages */ const char *zSelfCap; /* Capabilities of self-registered users */ int hasSelfReg = 0; /* True if able to self-register */ const char *zPublicUrl; /* Canonical access URL */ const char *zVulnReport; /* The vuln-report setting */ Blob cmd; char *z; int n, i; CapabilityString *pCap; char **azCSP; /* Parsed content security policy */ login_check_credentials(); |
| ︙ | ︙ | |||
360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
/* 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.
}
/* Obsolete: */
if( hasAnyCap(zAnonCap, "d") ||
hasAnyCap(zDevCap, "d") ||
hasAnyCap(zReadCap, "d") ){
@ <li><p><b>WARNING:</b>
@ One or more users has the <a
| > > > > > > > > > > > > | 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 |
/* 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.
}
zVulnReport = db_get("vuln-report","log");
if( fossil_strcmp(zVulnReport,"block")!=0
&& fossil_strcmp(zVulnReport,"fatal")!=0
){
@ <li><p><b>WARNING:</b>
@ The <a href="%R/help?cmd=vuln-report">vuln-report setting</a>
@ has a value of "%h(zVulnReport)". This disables defenses against
@ XSS or SQL-injection vulnerabilities caused by coding errors in
@ custom TH1 scripts. For the best security, change
@ the value of the vuln-report setting to "block" or "fatal".
}
/* Obsolete: */
if( hasAnyCap(zAnonCap, "d") ||
hasAnyCap(zDevCap, "d") ||
hasAnyCap(zReadCap, "d") ){
@ <li><p><b>WARNING:</b>
@ One or more users has the <a
|
| ︙ | ︙ | |||
541 542 543 544 545 546 547 |
@ up by the webserver contains the name of an authenticated user.
@ Fossil's built-in authentication mechanism is bypassed.
@ Fix this by deactivating the "Allow REMOTE_USER authentication"
@ checkbox on the <a href="setup_access">Access Control</a> page.
}
if( db_get_boolean("http_authentication_ok", 0) ){
@ <li><p><b>Caution:</b>
| | | | | 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 |
@ up by the webserver contains the name of an authenticated user.
@ Fossil's built-in authentication mechanism is bypassed.
@ Fix this by deactivating the "Allow REMOTE_USER authentication"
@ checkbox on the <a href="setup_access">Access Control</a> page.
}
if( db_get_boolean("http_authentication_ok", 0) ){
@ <li><p><b>Caution:</b>
@ This repository trusts that the HTTP_AUTHENTICATION environment
@ variable set up by the webserver contains the name of an
@ authenticated user.
@ Fossil's built-in authentication mechanism is bypassed.
@ Fix this by deactivating the "Allow HTTP_AUTHENTICATION authentication"
@ checkbox on the <a href="setup_access">Access Control</a> page.
}
/* Logging should be turned on
*/
if( db_get_boolean("access-log",1)==0 ){
@ <li><p>
@ The <a href="access_log">User Log</a> is disabled. The user log
@ keeps a record of successful and unsuccessful login attempts and is
@ useful for security monitoring.
}
if( db_get_boolean("admin-log",1)==0 ){
@ <li><p>
@ The <a href="admin_log">Administrative Log</a> is disabled.
@ The administrative log provides a record of configuration changes
@ and is useful for security monitoring.
}
#if !defined(_WIN32) && !defined(FOSSIL_OMIT_LOAD_AVERAGE)
|
| ︙ | ︙ | |||
802 803 804 805 806 807 808 |
@ <blockquote><pre>
@ errorlog: <i>FILENAME</i>
@ </pre></blockquote>
blob_reset(&fullname);
}
}
| < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | 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 |
@ <blockquote><pre>
@ errorlog: <i>FILENAME</i>
@ </pre></blockquote>
blob_reset(&fullname);
}
}
/*
** WEBPAGE: errorlog
**
** Show the content of the error log. Only the administrator can view
** this page.
**
** y=0x001 Show only hack attempts
** y=0x002 Show only panics and assertion faults
** y=0x004 Show hung backoffice processes
** y=0x008 Show POST requests from a different origin
** y=0x010 Show SQLITE_AUTH and similar
** y=0x020 Show SMTP error reports
** y=0x040 Show TH1 vulnerability reports
** y=0x080 Show SQL errors
** y=0x800 Show other uncategorized messages
**
** If y is omitted or is zero, a count of the various message types is
** shown.
*/
void errorlog_page(void){
i64 szFile;
FILE *in;
char *zLog;
const char *zType = P("y");
static const int eAllTypes = 0x8ff;
long eType = 0;
int bOutput = 0;
int prevWasTime = 0;
int nHack = 0;
int nPanic = 0;
int nOther = 0;
int nHang = 0;
int nXPost = 0;
int nAuth = 0;
int nSmtp = 0;
int nVuln = 0;
int nSqlErr = 0;
char z[10000];
char zTime[10000];
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
if( zType ){
eType = strtol(zType,0,0) & eAllTypes;
}
style_header("Server Error Log");
style_submenu_element("Test", "%R/test-warning");
style_submenu_element("Refresh", "%R/errorlog");
style_submenu_element("Download", "%R/errorlog?download");
style_submenu_element("Truncate", "%R/errorlog?truncate");
style_submenu_element("Log-Menu", "%R/setup-logmenu");
if( eType ){
style_submenu_element("Summary", "%R/errorlog");
}
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
no_error_log_available();
style_finish_page();
return;
}
if( P("truncate1") && cgi_csrf_safe(2) ){
|
| ︙ | ︙ | |||
859 860 861 862 863 864 865 |
@ </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);
| < < | | > | < | | > | > | > > | < < | < > > > > | < | > > | < < < > > > > | > > | > | < > | > | > | < | < < | > > | > > > > | > > | > > | | > > | < > | | | | | | > | > > | > | < | > > > | < < < < < < < < < | < < | < > | > | < | > > > > > > > | < < < < < < < | | | < < < < < < | < | < < > > | < < > > > | < < < | > > | < | < | | | | | < | < < > | | < > > | > | < < < > | < < < < > < > < | | < < < | 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 |
@ </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);
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( eType==0 ){
/* will do a summary */
}else if( (eType&eAllTypes)!=eAllTypes ){
@ Only the following types of messages displayed:
@ <ul>
if( eType & 0x01 ){
@ <li>Hack attempts
}
if( eType & 0x02 ){
@ <li>Panics and assertion faults
}
if( eType & 0x04 ){
@ <li>Hung backoffice processes
}
if( eType & 0x08 ){
@ <li>POST requests from different origin
}
if( eType & 0x10 ){
@ <li>SQLITE_AUTH and similar errors
}
if( eType & 0x20 ){
@ <li>SMTP malfunctions
}
if( eType & 0x40 ){
@ <li>TH1 vulnerabilities
}
if( eType & 0x80 ){
@ <li>SQL errors
}
if( eType & 0x800 ){
@ <li>Other uncategorized messages
}
@ </ul>
}
@ <hr>
if( eType ){
@ <pre>
}
while( fgets(z, sizeof(z), in) ){
if( prevWasTime ){
if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
bOutput = (eType & 0x01)!=0;
nHack++;
}else
if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){
bOutput = (eType & 0x02)!=0;
nPanic++;
}else
if( strncmp(z,"SMTP:", 5)==0 ){
bOutput = (eType & 0x20)!=0;
nSmtp++;
}else
if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){
bOutput = (eType & 0x04)!=0;
nHang++;
}else
if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){
bOutput = (eType & 0x08)!=0;
nXPost++;
}else
if( sqlite3_strglob("SECURITY: authorizer blocks*",z)==0
|| sqlite3_strglob("warning: SQLITE_AUTH*",z)==0
){
bOutput = (eType & 0x10)!=0;
nAuth++;
}else
if( strncmp(z,"possible", 8)==0 && strstr(z,"tainted")!=0 ){
bOutput = (eType & 0x40)!=0;
nVuln++;
}else
if( strstr(z,"statement aborts at ") ){
bOutput = (eType & 0x80)!=0;
nSqlErr++;
}else
{
bOutput = (eType & 0x800)!=0;
nOther++;
}
if( bOutput ){
@ %h(zTime)\
}
}
if( strncmp(z, "--------", 8)==0 ){
size_t n = strlen(z);
memcpy(zTime, z, n+1);
prevWasTime = 1;
bOutput = 0;
}else{
prevWasTime = 0;
}
if( bOutput && eType ){
@ %h(z)\
}
}
fclose(in);
if( eType ){
@ </pre>
}
if( eType==0 ){
int nNonHack = nPanic + nHang + nAuth + nSmtp + nVuln + nOther + nSqlErr;
int nTotal = nNonHack + nHack + nXPost;
@ <p><table border="a" cellspacing="0" cellpadding="5">
if( nPanic>0 ){
@ <tr><td align="right">%d(nPanic)</td>
@ <td><a href="./errorlog?y=2">Panics</a></td>
}
if( nVuln>0 ){
@ <tr><td align="right">%d(nVuln)</td>
@ <td><a href="./errorlog?y=64">TH1 Vulnerabilities</a></td>
}
if( nHack>0 ){
@ <tr><td align="right">%d(nHack)</td>
@ <td><a href="./errorlog?y=1">Hack Attempts</a></td>
}
if( nSqlErr>0 ){
@ <tr><td align="right">%d(nSqlErr)</td>
@ <td><a href="./errorlog?y=128">SQL Errors</a></td>
}
if( nHang>0 ){
@ <tr><td align="right">%d(nHang)</td>
@ <td><a href="./errorlog?y=4">Hung Backoffice</a></td>
}
if( nXPost>0 ){
@ <tr><td align="right">%d(nXPost)</td>
@ <td><a href="./errorlog?y=8">POSTs from different origin</a></td>
}
if( nAuth>0 ){
@ <tr><td align="right">%d(nAuth)</td>
@ <td><a href="./errorlog?y=16">SQLITE_AUTH and similar</a></td>
}
if( nSmtp>0 ){
@ <tr><td align="right">%d(nSmtp)</td>
@ <td><a href="./errorlog?y=32">SMTP faults</a></td>
}
if( nOther>0 ){
@ <tr><td align="right">%d(nOther)</td>
@ <td><a href="./errorlog?y=2048">Other</a></td>
}
@ <tr><td align="right">%d(nTotal)</td>
if( nTotal>0 ){
@ <td><a href="./errorlog?y=4095">All Messages</a></td>
}else{
@ <td>All Messages</td>
}
@ </table>
}
style_finish_page();
}
|
Changes to src/setup.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 |
}
db_protect_pop();
}
}
/*
** Output a single entry for a menu generated using an HTML table.
| | | | | | 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 |
}
db_protect_pop();
}
}
/*
** Output a single entry for a menu generated using an HTML table.
** If zLink is neither NULL nor an empty string, then it is the page that
** the menu entry will hyperlink to. If zLink is NULL or "", then
** the menu entry has no hyperlink - it is disabled.
*/
void setup_menu_entry(
const char *zTitle,
const char *zLink,
const char *zDesc /* Caution! Rendered using %s. May contain raw HTML. */
){
@ <tr><td valign="top" align="right">
if( zLink && zLink[0] ){
@ <a href="%s(zLink)"><nobr>%h(zTitle)</nobr></a>
}else{
@ <nobr>%h(zTitle)</nobr>
}
@ </td><td width="5"></td><td valign="top">%s(zDesc)</td></tr>
}
/*
** WEBPAGE: setup
**
|
| ︙ | ︙ | |||
181 182 183 184 185 186 187 | style_finish_page(); } /* ** WEBPAGE: setup-logmenu ** | | > > > > > > > > > > > | | | | > | | < | > > > > > > > > | > | > > | > | | | > | < < > | < | < < < < | | < < > | > > | | | < > > | > | | < < | | 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 |
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;
int bErrLog; /* True if Error Log enabled */
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">
if( db_get_boolean("admin-log",1)==0 ){
blob_appendf(&desc,
"The admin log records configuration changes to the repository.\n"
"<b>Disabled</b>: Turn on the "
" <a href='%R/setup_settings'>admin-log setting</a> to enable."
);
setup_menu_entry("Admin Log", 0, blob_str(&desc));
blob_reset(&desc);
}else{
setup_menu_entry("Admin Log", "admin_log",
"The admin log records configuration changes to the repository\n"
"in the \"admin_log\" table.\n"
);
}
setup_menu_entry("Xfer Log", "rcvfromlist",
"The artifact log records when new content is added in the\n"
"\"rcvfrom\" table.\n"
);
if( db_get_boolean("access-log",1) ){
setup_menu_entry("User Log", "user_log",
"Login attempts recorded in the \"accesslog\" table."
);
}else{
blob_appendf(&desc,
"Login attempts recorded in the \"accesslog\" table.\n"
"<b>Disabled</b>: Turn on the "
"<a href='%R/setup_settings'>access-log setting</a> to enable."
);
setup_menu_entry("User Log", 0, blob_str(&desc));
blob_reset(&desc);
}
blob_appendf(&desc,
"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,"<b>Disabled</b>: "
"To enable the error log ");
if( fossil_strcmp(g.zCmdName, "cgi")==0 ){
blob_appendf(&desc,
"make an entry like \"errorlog: <i>FILENAME</i>\""
" in the CGI script at %h",
P("SCRIPT_FILENAME")
);
}else{
blob_appendf(&desc,
" add the \"--errorlog <i>FILENAME</i>\" option to the\n"
"\"%h %h\" command that launched the server.",
g.argv[0], g.zCmdName
);
}
bErrLog = 0;
}else{
blob_appendf(&desc,"In this repository, the error log is the file "
"named \"%s\".", g.zErrlog);
bErrLog = 1;
}
setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc));
blob_reset(&desc);
@ </table>
style_finish_page();
}
/*
** Generate a checkbox for an attribute.
|
| ︙ | ︙ | |||
441 442 443 444 445 446 447 |
@ </blockquote>
@ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
@ and "require a mouse event" should be turned on. These values only come
@ into play when the main auto-hyperlink settings is 2 ("UserAgent and
@ Javascript").</p>
@
@ <p>To see if Javascript-base hyperlink enabling mechanism is working,
| | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 |
@ </blockquote>
@ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
@ and "require a mouse event" should be turned on. These values only come
@ into play when the main auto-hyperlink settings is 2 ("UserAgent and
@ Javascript").</p>
@
@ <p>To see if Javascript-base hyperlink enabling mechanism is working,
@ visit the <a href="%R/test-env">/test-env</a> page (from a separate
@ web browser that is not logged in, even as "anonymous") and verify
@ that the "g.jsHref" value is "1".</p>
@ <p>(Properties: "auto-hyperlink", "auto-hyperlink-delay", and
@ "auto-hyperlink-mouseover"")</p>
}
/*
|
| ︙ | ︙ | |||
583 584 585 586 587 588 589 | @ without the "--localauth" option. @ <li> The server is started from CGI without the "localauth" keyword @ in the CGI script. @ </ol> @ (Property: "localauth") @ @ <hr> | | | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
@ without the "--localauth" option.
@ <li> The server is started from CGI without the "localauth" keyword
@ in the CGI script.
@ </ol>
@ (Property: "localauth")
@
@ <hr>
onoff_attribute("Enable /test-env",
"test_env_enable", "test_env_enable", 0, 0);
@ <p>When enabled, the %h(g.zBaseURL)/test_env URL is available to all
@ users. When disabled (the default) only users Admin and Setup can visit
@ the /test_env page.
@ (Property: "test_env_enable")
@ </p>
@
|
| ︙ | ︙ | |||
914 915 916 917 918 919 920 | @ <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 | | | 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 | @ <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. @ |
| ︙ | ︙ | |||
960 961 962 963 964 965 966 |
style_set_current_feature("setup");
style_header("Timeline Display Preferences");
db_begin_transaction();
@ <form action="%R/setup_timeline" method="post"><div>
login_insert_csrf_secret();
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
| < < < < < < < | 979 980 981 982 983 984 985 986 987 988 989 990 991 992 |
style_set_current_feature("setup");
style_header("Timeline Display Preferences");
db_begin_transaction();
@ <form action="%R/setup_timeline" method="post"><div>
login_insert_csrf_secret();
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
onoff_attribute("Plaintext comments on timelines",
"timeline-plaintext", "tpt", 0, 0);
@ <p>In timeline displays, check-in comments are displayed literally,
@ without any wiki or HTML interpretation. Use CSS to change
@ display formatting features such as fonts and line-wrapping behavior.
@ (Property: "timeline-plaintext")</p>
|
| ︙ | ︙ | |||
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 |
@ <hr>
onoff_attribute("Break comments at newline characters",
"timeline-hard-newlines", "thnl", 0, 0);
@ <p>In timeline displays, newline characters in check-in comments force
@ a line break on the display.
@ (Property: "timeline-hard-newlines")</p>
@ <hr>
onoff_attribute("Use Universal Coordinated Time (UTC)",
"timeline-utc", "utc", 1, 0);
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
@ Zulu) instead of in local time. On this server, local time is currently
tmDiff = db_double(0.0, "SELECT julianday('now')");
tmDiff = db_double(0.0,
| > > > > > > > > > > | 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 |
@ <hr>
onoff_attribute("Break comments at newline characters",
"timeline-hard-newlines", "thnl", 0, 0);
@ <p>In timeline displays, newline characters in check-in comments force
@ a line break on the display.
@ (Property: "timeline-hard-newlines")</p>
@ <hr>
onoff_attribute("Do not adjust user-selected background colors",
"raw-bgcolor", "rbgc", 0, 0);
@ <p>Fossil normally attempts to adjust the saturation and intensity of
@ user-specified background colors on check-ins and branches so that the
@ foreground text is easily readable on all skins. Enable this setting
@ to omit that adjustment and use exactly the background color specified
@ by users.
@ (Property: "raw-bgcolor")</p>
@ <hr>
onoff_attribute("Use Universal Coordinated Time (UTC)",
"timeline-utc", "utc", 1, 0);
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
@ Zulu) instead of in local time. On this server, local time is currently
tmDiff = db_double(0.0, "SELECT julianday('now')");
tmDiff = db_double(0.0,
|
| ︙ | ︙ | |||
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 |
** Change or view miscellaneous settings. Part of the
** /setup pages requiring Setup privileges.
*/
void setup_settings(void){
int nSetting;
int i;
Setting const *pSet;
const Setting *aSetting = setting_info(&nSetting);
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
style_set_current_feature("setup");
style_header("Settings");
if(!g.repositoryOpen){
/* Provide read-only access to versioned settings,
but only if no repo file was explicitly provided. */
db_open_local(0);
}
db_begin_transaction();
@ <p>Settings marked with (v) are "versionable" and will be overridden
@ by the contents of managed files named
@ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
@ If the file for a versionable setting exists, the value cannot be
@ changed on this screen.</p><hr><p>
@
@ <form action="%R/setup_settings" method="post"><div>
@ <table border="0"><tr><td valign="top">
login_insert_csrf_secret();
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width==0 ){
int hasVersionableValue = pSet->versionable &&
| > > > > > > > > > > > | > > > | > > > | > > > | 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 |
** Change or view miscellaneous settings. Part of the
** /setup pages requiring Setup privileges.
*/
void setup_settings(void){
int nSetting;
int i;
Setting const *pSet;
int bIfChng = P("all")==0;
const Setting *aSetting = setting_info(&nSetting);
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
style_set_current_feature("setup");
style_header("Settings");
if(!g.repositoryOpen){
/* Provide read-only access to versioned settings,
but only if no repo file was explicitly provided. */
db_open_local(0);
}
db_begin_transaction();
if( bIfChng ){
@ <p>Only settings whose value is different from the default are shown.
@ Click the "All" button above to set all settings.
}
@ <p>Settings marked with (v) are "versionable" and will be overridden
@ by the contents of managed files named
@ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
@ If the file for a versionable setting exists, the value cannot be
@ changed on this screen.</p><hr><p>
@
@ <form action="%R/setup_settings" method="post"><div>
if( bIfChng ){
style_submenu_element("All", "%R/setup_settings?all");
}else{
@ <input type="hidden" name="all" value="1">
style_submenu_element("Changes-Only", "%R/setup_settings");
}
@ <table border="0"><tr><td valign="top">
login_insert_csrf_secret();
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width==0 ){
int hasVersionableValue = pSet->versionable &&
(db_get_versioned(pSet->name, NULL, NULL)!=0);
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
continue;
}
onoff_attribute("", pSet->name,
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
is_truth(pSet->def), hasVersionableValue);
@ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
if( pSet->versionable ){
@ (v)<br>
} else {
@ <br>
}
}
}
@ <br><input type="submit" name="submit" value="Apply Changes">
@ </td><td style="width:50px;"></td><td valign="top">
@ <table>
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width>0 && !pSet->forceTextArea ){
int hasVersionableValue = pSet->versionable &&
(db_get_versioned(pSet->name, NULL, NULL)!=0);
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
continue;
}
@ <tr><td>
@ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
if( pSet->versionable ){
@ (v)
} else {
@
}
@</td><td>
entry_attribute("", /*pSet->width*/ 25, pSet->name,
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
(char*)pSet->def, hasVersionableValue);
@</td></tr>
}
}
@</table>
@ </td><td style="width:50px;"></td><td valign="top">
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width>0 && pSet->forceTextArea ){
int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0;
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
continue;
}
@ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
if( pSet->versionable ){
@ (v)<br>
} else {
@ <br>
}
textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name,
|
| ︙ | ︙ | |||
1252 1253 1254 1255 1256 1257 1258 |
@ The project name will also be used as the RSS feed title.
@ (Property: "project-name")
@ </p>
@ <hr>
textarea_attribute("Project Description", 3, 80,
"project-description", "pd", "", 0);
@ <p>Describe your project. This will be used in page headers for search
| | | 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 |
@ The project name will also be used as the RSS feed title.
@ (Property: "project-name")
@ </p>
@ <hr>
textarea_attribute("Project Description", 3, 80,
"project-description", "pd", "", 0);
@ <p>Describe your project. This will be used in page headers for search
@ engines, the repository listing and a short RSS description.
@ (Property: "project-description")</p>
@ <hr>
entry_attribute("Canonical Server URL", 40, "email-url",
"eurl", "", 0);
@ <p>This is the URL used to access this repository as a server.
@ Other repositories use this URL to clone or sync against this repository.
@ This is also the basename for hyperlinks included in email alert text.
|
| ︙ | ︙ | |||
1390 1391 1392 1393 1394 1395 1396 |
style_set_current_feature("setup");
style_header("Wiki Configuration");
db_begin_transaction();
@ <form action="%R/setup_wiki" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
| | | | | | > | 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 |
style_set_current_feature("setup");
style_header("Wiki Configuration");
db_begin_transaction();
@ <form action="%R/setup_wiki" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
onoff_attribute("Associate Wiki Pages With Branches, Tags, Tickets, or Checkins",
"wiki-about", "wiki-about", 1, 0);
@ <p>
@ Associate wiki pages with branches, tags, tickets, or checkins, based on
@ the wiki page name. Wiki pages that begin with "branch/", "checkin/",
@ "tag/" or "ticket" and which continue with the name of an existing branch,
@ check-in, tag or ticket are treated specially when this feature is enabled.
@ <ul>
@ <li> <b>branch/</b><i>branch-name</i>
@ <li> <b>checkin/</b><i>full-check-in-hash</i>
@ <li> <b>tag/</b><i>tag-name</i>
@ <li> <b>ticket/</b><i>full-ticket-hash</i>
@ </ul>
@ (Property: "wiki-about")</p>
@ <hr>
entry_attribute("Allow Unsafe HTML In Markdown", 6,
"safe-html", "safe-html", "", 0);
@ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be
@ generated by <a href="%R/md_rules">Markdown-formatted</a> documents.
|
| ︙ | ︙ | |||
2099 2100 2101 2102 2103 2104 2105 |
}
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"));
| | | 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 |
}
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", 1);
@ <div>Admin logging is %s(fLogEnabled?"on":"off").
@ (Change this on the <a href="setup_settings">settings</a> page.)</div>
if( ofst>0 ){
int prevx = ofst - limit;
if( prevx<0 ) prevx = 0;
@ <p><a href="admin_log?n=%d(limit)&x=%d(prevx)">[Newer]</a></p>
|
| ︙ | ︙ | |||
2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 |
onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
@ <br>
onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
@ <br>
onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
@ <br>
onoff_attribute("Search Forum", "search-forum", "sf", 0, 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
if( P("fts0") ){
search_drop_index();
}else if( P("fts1") ){
const char *zTokenizer = PD("ftstok","off");
| > > | 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 |
onoff_attribute("Search Tickets", "search-tkt", "st", 0, 0);
@ <br>
onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
@ <br>
onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
@ <br>
onoff_attribute("Search Forum", "search-forum", "sf", 0, 0);
@ <br>
onoff_attribute("Search Built-in Help Text", "search-help", "sh", 0, 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
if( P("fts0") ){
search_drop_index();
}else if( P("fts1") ){
const char *zTokenizer = PD("ftstok","off");
|
| ︙ | ︙ |
Changes to src/setupuser.c.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 51 |
*/
void setup_ulist(void){
Stmt s;
double rNow;
const char *zWith = P("with");
int bUnusedOnly = P("unused")!=0;
int bUbg = P("ubg")!=0;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
| > | | | 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 |
*/
void setup_ulist(void){
Stmt s;
double rNow;
const char *zWith = P("with");
int bUnusedOnly = P("unused")!=0;
int bUbg = P("ubg")!=0;
int bHaveAlerts;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
bHaveAlerts = alert_tables_exist();
style_submenu_element("Add", "setup_uedit");
style_submenu_element("Log", "access_log");
style_submenu_element("Help", "setup_ulist_notes");
if( bHaveAlerts ){
style_submenu_element("Subscribers", "subscribers");
}
style_set_current_feature("setup");
style_header("User List");
if( (zWith==0 || zWith[0]==0) && !bUnusedOnly ){
@ <table border=1 cellpadding=2 cellspacing=0 class='userTable'>
@ <thead><tr>
|
| ︙ | ︙ | |||
145 146 147 148 149 150 151 |
}
if( bUnusedOnly ){
zWith = mprintf(
" AND login NOT IN ("
"SELECT user FROM event WHERE user NOT NULL "
"UNION ALL SELECT euser FROM event WHERE euser NOT NULL%s)"
" AND uid NOT IN (SELECT uid FROM rcvfrom)",
| | | | | | < | > | | | | > > > > | | | | > > | | 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 |
}
if( bUnusedOnly ){
zWith = mprintf(
" AND login NOT IN ("
"SELECT user FROM event WHERE user NOT NULL "
"UNION ALL SELECT euser FROM event WHERE euser NOT NULL%s)"
" AND uid NOT IN (SELECT uid FROM rcvfrom)",
bHaveAlerts ?
" UNION ALL SELECT suname FROM subscriber WHERE suname NOT NULL":"");
}else if( zWith && zWith[0] ){
zWith = mprintf(" AND fullcap(cap) GLOB '*[%q]*'", zWith);
}else{
zWith = "";
}
db_prepare(&s,
/*0-4*/"SELECT uid, login, cap, info, date(user.mtime,'unixepoch'),"
/* 5 */"lower(login) AS sortkey, "
/* 6 */"CASE WHEN info LIKE '%%expires 20%%'"
" THEN substr(info,instr(lower(info),'expires')+8,10)"
" END AS exp,"
/* 7 */"atime,"
/* 8 */"user.mtime AS sorttime,"
/*9-11*/"%s"
" FROM user LEFT JOIN lastAccess ON login=uname"
" LEFT JOIN subscriber ON login=suname"
" WHERE login NOT IN ('anonymous','nobody','developer','reader') %s"
" ORDER BY sorttime DESC",
bHaveAlerts
? "subscriber.ssub, subscriber.subscriberId, subscriber.semail"
: "null, null, null",
zWith/*safe-for-%s*/
);
rNow = db_double(0.0, "SELECT julianday('now');");
while( db_step(&s)==SQLITE_ROW ){
int uid = db_column_int(&s, 0);
const char *zLogin = db_column_text(&s, 1);
const char *zCap = db_column_text(&s, 2);
const char *zInfo = db_column_text(&s, 3);
const char *zDate = db_column_text(&s, 4);
const char *zSortKey = db_column_text(&s,5);
const char *zExp = db_column_text(&s,6);
double rATime = db_column_double(&s,7);
char *zAge = 0;
const char *zSub;
int sid = db_column_int(&s,10);
sqlite3_int64 sorttime = db_column_int64(&s, 8);
if( rATime>0.0 ){
zAge = human_readable_age(rNow - rATime);
}
if( bUbg ){
@ <tr style='background-color: %h(user_color(zLogin));'>
}else{
@ <tr>
}
@ <td data-sortkey='%h(zSortKey)'>\
@ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a>
@ <td>%h(zCap)
@ <td>%h(zInfo)
@ <td data-sortkey='%09llx(sorttime)'>%h(zDate?zDate:"")
@ <td>%h(zExp?zExp:"")
@ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"")
if( db_column_type(&s,9)==SQLITE_NULL ){
@ <td>
}else if( (zSub = db_column_text(&s,9))==0 || zSub[0]==0 ){
@ <td><a href="%R/alerts?sid=%d(sid)"><i>off</i></a>
}else{
const char *zEmail = db_column_text(&s, 11);
char * zAt = zEmail ? mprintf(" → %h", zEmail) : mprintf("");
@ <td><a href="%R/alerts?sid=%d(sid)">%h(zSub)</a> %z(zAt)
}
@ </tr>
fossil_free(zAge);
}
@ </tbody></table>
db_finalize(&s);
|
| ︙ | ︙ | |||
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 |
*/
static int isValidPwString(const char *zPw){
if( zPw==0 ) return 0;
if( zPw[0]==0 ) return 1;
while( zPw[0]=='*' ){ zPw++; }
return zPw[0]!=0;
}
/*
** WEBPAGE: setup_uedit
**
** Edit information about a user or create a new user.
** Requires Admin privileges.
*/
void user_edit(void){
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
const char *zGroup;
const char *zOldLogin;
int uid, i;
char *zDeleteVerify = 0; /* Delete user verification text */
int higherUser = 0; /* True if user being edited is SETUP and the */
/* user doing the editing is ADMIN. Disallow editing */
const char *inherit[128];
int a[128];
const char *oa[128];
/* Must have ADMIN privileges to access this page
*/
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
/* Check to see if an ADMIN user is trying to edit a SETUP account.
** Don't allow that.
*/
zId = PD("id", "0");
uid = atoi(zId);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < | > | > | 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 |
*/
static int isValidPwString(const char *zPw){
if( zPw==0 ) return 0;
if( zPw[0]==0 ) return 1;
while( zPw[0]=='*' ){ zPw++; }
return zPw[0]!=0;
}
/*
** Return true if user capability strings zOrig and zNew materially
** differ, taking into account that they may be sorted in an arbitary
** order. This does not take inherited permissions into
** account. Either argument may be NULL. A NULL and an empty string
** are considered equivalent here. e.g. "abc" and "cab" are equivalent
** for this purpose, but "aCb" and "acb" are not.
*/
static int userCapsChanged(const char *zOrig, const char *zNew){
if( !zOrig ){
return zNew ? (0!=*zNew) : 0;
}else if( !zNew ){
return 0!=*zOrig;
}else if( 0==fossil_strcmp(zOrig, zNew) ){
return 0;
}else{
/* We don't know that zOrig and zNew are sorted equivalently. The
** following steps will compare strings which contain all the same
** capabilities letters as equivalent, regardless of the letters'
** order in their strings. */
char aOrig[128]; /* table of zOrig bytes */
int nOrig = 0, nNew = 0;
memset( &aOrig[0], 0, sizeof(aOrig) );
for( ; *zOrig; ++zOrig, ++nOrig ){
if( 0==(*zOrig & 0x80) ){
aOrig[(int)*zOrig] = 1;
}
}
for( ; *zNew; ++zNew, ++nNew ){
if( 0==(*zNew & 0x80) && !aOrig[(int)*zNew] ){
return 1;
}
}
return nOrig!=nNew;
}
}
/*
** COMMAND: test-user-caps-changed
**
** Usage: %fossil test-user-caps-changed caps1 caps2
**
*/
void test_user_caps_changed(void){
char const * zOld = g.argc>2 ? g.argv[2] : NULL;
char const * zNew = g.argc>3 ? g.argv[3] : NULL;
fossil_print("Has changes? = %d\n",
userCapsChanged( zOld, zNew ));
}
/*
** Sends notification of user permission elevation changes to all
** subscribers with a "u" subscription. This is a no-op if alerts are
** not enabled.
**
** These subscriptions differ from most, in that:
**
** - They currently lack an "unsubscribe" link.
**
** - Only an admin can assign this subscription, but if a non-admin
** edits their subscriptions after an admin assigns them this one,
** this particular one will be lost. "Feature or bug?" is unclear,
** but it would be odd for a non-admin to be assigned this
** capability.
*/
static void alert_user_cap_change(const char *zLogin, /*Affected user*/
int uid, /*[user].uid*/
int bIsNew, /*true if new user*/
const char *zOrigCaps,/*Old caps*/
const char *zNewCaps /*New caps*/){
Blob hdr, body;
Stmt q;
int nBody;
AlertSender *pSender;
char *zSubname;
char *zURL;
char * zSubject;
if( !alert_enabled() ) return;
zSubject = bIsNew
? mprintf("New user created: [%q]", zLogin)
: mprintf("User [%q] capabilities changed", zLogin);
zURL = db_get("email-url",0);
zSubname = db_get("email-subname", "[Fossil Repo]");
blob_init(&body, 0, 0);
blob_init(&hdr, 0, 0);
if( bIsNew ){
blob_appendf(&body, "User [%q] was created with "
"permissions [%q] by user [%q].\n",
zLogin, zNewCaps, g.zLogin);
} else {
blob_appendf(&body, "Permissions for user [%q] where changed "
"from [%q] to [%q] by user [%q].\n",
zLogin, zOrigCaps, zNewCaps, g.zLogin);
}
if( zURL ){
blob_appendf(&body, "\nUser editor: %s/setup_uedit?id=%d\n", zURL, uid);
}
nBody = blob_size(&body);
pSender = alert_sender_new(0, 0);
db_prepare(&q,
"SELECT semail, hex(subscriberCode)"
" FROM subscriber, user "
" WHERE sverified AND NOT sdonotcall"
" AND suname=login"
" AND ssub GLOB '*u*'");
while( !pSender->zErr && db_step(&q)==SQLITE_ROW ){
const char *zTo = db_column_text(&q, 0);
blob_truncate(&hdr, 0);
blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n",
zTo, zSubname, zSubject);
if( zURL ){
const char *zCode = db_column_text(&q, 1);
blob_truncate(&body, nBody);
blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
zURL, zCode);
}
alert_send(pSender, &hdr, &body, 0);
}
db_finalize(&q);
alert_sender_free(pSender);
fossil_free(zURL);
fossil_free(zSubname);
fossil_free(zSubject);
}
/*
** WEBPAGE: setup_uedit
**
** Edit information about a user or create a new user.
** Requires Admin privileges.
*/
void user_edit(void){
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
const char *zGroup;
const char *zOldLogin;
int uid, i;
char *zOldCaps = 0; /* Capabilities before edit */
char *zDeleteVerify = 0; /* Delete user verification text */
int higherUser = 0; /* True if user being edited is SETUP and the */
/* user doing the editing is ADMIN. Disallow editing */
const char *inherit[128];
int a[128];
const char *oa[128];
/* Must have ADMIN privileges to access this page
*/
login_check_credentials();
if( !g.perm.Admin ){ login_needed(0); return; }
/* Check to see if an ADMIN user is trying to edit a SETUP account.
** Don't allow that.
*/
zId = PD("id", "0");
uid = atoi(zId);
if( uid>0 ){
zOldCaps = db_text("", "SELECT cap FROM user WHERE uid=%d",uid);
if( zId && !g.perm.Setup ){
higherUser = zOldCaps && strchr(zOldCaps,'s');
}
}
if( P("can") ){
/* User pressed the cancel button */
cgi_redirect(cgi_referer("setup_ulist"));
return;
}
|
| ︙ | ︙ | |||
391 392 393 394 395 396 397 |
}else if( zDeleteVerify!=0 ){
/* Need to verify a delete request */
}else if( !cgi_csrf_safe(2) ){
/* This might be a cross-site request forgery, so ignore it */
}else{
/* We have all the information we need to make the change to the user */
char c;
| > > | | | | | > | 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 |
}else if( zDeleteVerify!=0 ){
/* Need to verify a delete request */
}else if( !cgi_csrf_safe(2) ){
/* This might be a cross-site request forgery, so ignore it */
}else{
/* We have all the information we need to make the change to the user */
char c;
int bCapsChanged = 0 /* 1 if user's permissions changed */;
const int bIsNew = uid<=0;
char aCap[70], zNm[4];
zNm[0] = 'a';
zNm[2] = 0;
for(i=0, c='a'; c<='z'; c++){
zNm[1] = c;
a[c&0x7f] = ((c!='s' && c!='y') || g.perm.Setup) && P(zNm)!=0;
if( a[c&0x7f] ) aCap[i++] = c;
}
for(c='0'; c<='9'; c++){
zNm[1] = c;
a[c&0x7f] = P(zNm)!=0;
if( a[c&0x7f] ) aCap[i++] = c;
}
for(c='A'; c<='Z'; c++){
zNm[1] = c;
a[c&0x7f] = P(zNm)!=0;
if( a[c&0x7f] ) aCap[i++] = c;
}
aCap[i] = 0;
bCapsChanged = bIsNew || userCapsChanged(zOldCaps, &aCap[0]);
zPw = P("pw");
zLogin = P("login");
if( strlen(zLogin)==0 ){
const char *zRef = cgi_referer("setup_ulist");
style_header("User Creation Error");
@ <span class="loginError">Empty login not allowed.</span>
@
|
| ︙ | ︙ | |||
442 443 444 445 446 447 448 |
@ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
@ [Bummer]</a></p>
style_finish_page();
return;
}
cgi_csrf_verify();
db_unprotect(PROTECT_USER);
| | | | > | | | > | | 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 |
@ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
@ [Bummer]</a></p>
style_finish_page();
return;
}
cgi_csrf_verify();
db_unprotect(PROTECT_USER);
uid = db_int(0,
"REPLACE INTO user(uid,login,info,pw,cap,mtime) "
"VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now()) "
"RETURNING uid",
uid, zLogin, P("info"), zPw, &aCap[0]);
assert( uid>0 );
if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){
if( alert_tables_exist() ){
/* Rename matching subscriber entry, else the user cannot
re-subscribe with their same email address. */
db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q",
zLogin, zOldLogin);
}
admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin );
}
db_protect_pop();
setup_incr_cfgcnt();
admin_log( "%s user [%q] with capabilities [%q].",
bIsNew ? "Added" : "Updated",
zLogin, &aCap[0] );
if( atoi(PD("all","0"))>0 ){
Blob sql;
char *zErr = 0;
blob_zero(&sql);
if( zOldLogin==0 ){
blob_appendf(&sql,
"INSERT INTO user(login)"
|
| ︙ | ︙ | |||
494 495 496 497 498 499 500 |
"UPDATE user SET login=%Q,"
" pw=coalesce(shared_secret(%Q,%Q,"
"(SELECT value FROM config WHERE name='project-code')),pw),"
" info=%Q,"
" cap=%Q,"
" mtime=now()"
" WHERE login=%Q;",
| | | > > > > > > > > | > < | 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 |
"UPDATE user SET login=%Q,"
" pw=coalesce(shared_secret(%Q,%Q,"
"(SELECT value FROM config WHERE name='project-code')),pw),"
" info=%Q,"
" cap=%Q,"
" mtime=now()"
" WHERE login=%Q;",
zLogin, P("pw"), zLogin, P("info"), &aCap[0],
zOldLogin
);
db_unprotect(PROTECT_USER);
login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr);
db_protect_pop();
blob_reset(&sql);
admin_log( "Updated user [%q] in all login groups "
"with capabilities [%q].",
zLogin, &aCap[0] );
if( zErr ){
const char *zRef = cgi_referer("setup_ulist");
style_header("User Change Error");
admin_log( "Error updating user '%q': %s'.", zLogin, zErr );
@ <span class="loginError">%h(zErr)</span>
@
@ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
@ [Bummer]</a></p>
style_finish_page();
if( bCapsChanged ){
/* It's possible that caps were updated locally even if
** login group updates failed. */
alert_user_cap_change(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
}
return;
}
}
if( bCapsChanged ){
alert_user_cap_change(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
}
cgi_redirect(cgi_referer("setup_ulist"));
return;
}
/* Load the existing information about the user, if any
*/
zLogin = "";
zInfo = "";
zCap = zOldCaps;
zPw = "";
for(i='a'; i<='z'; i++) oa[i] = "";
for(i='0'; i<='9'; i++) oa[i] = "";
for(i='A'; i<='Z'; i++) oa[i] = "";
if( uid ){
assert( zCap );
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
for(i=0; zCap[i]; i++){
char c = zCap[i];
if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){
oa[c&0x7f] = " checked=\"checked\"";
}
}
|
| ︙ | ︙ |
Changes to src/shun.c.
| ︙ | ︙ | |||
371 372 373 374 375 376 377 |
const int perScreen = 500; /* RCVIDs per page */
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
| | | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
const int perScreen = 500; /* RCVIDs per page */
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Xfer Log");
style_submenu_element("Log-Menu", "setup-logmenu");
if( showAll ){
ofst = 0;
}else{
style_submenu_element("All", "rcvfromlist?all=1");
}
if( ofst>0 ){
|
| ︙ | ︙ | |||
413 414 415 416 417 418 419 |
" EXISTS(SELECT 1 FROM rcvidSha1 WHERE x=rcvfrom.rcvid),"
" EXISTS(SELECT 1 FROM rcvidSha3 WHERE x=rcvfrom.rcvid)"
" FROM rcvfrom LEFT JOIN user USING(uid)"
" ORDER BY rcvid DESC LIMIT %d OFFSET %d",
showAll ? -1 : perScreen+1, ofst
);
@ <p>Whenever new artifacts are added to the repository, either by
| > | | | 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
" EXISTS(SELECT 1 FROM rcvidSha1 WHERE x=rcvfrom.rcvid),"
" EXISTS(SELECT 1 FROM rcvidSha3 WHERE x=rcvfrom.rcvid)"
" FROM rcvfrom LEFT JOIN user USING(uid)"
" ORDER BY rcvid DESC LIMIT %d OFFSET %d",
showAll ? -1 : perScreen+1, ofst
);
@ <p>Whenever new artifacts are added to the repository, either by
@ push or using the web interface or by "fossil commit" or similar,
@ an entry is made in the RCVFROM table
@ to record the source of those artifacts. This log facilitates
@ finding and fixing attempts to inject illicit content into the
@ repository.</p>
@
@ <p>Click on the "rcvid" to show a list of specific artifacts received
@ by a transaction. After identifying illicit artifacts, remove them
@ using the "Shun" button. If an "rcvid" is not hyperlinked, that means
@ all artifacts associated with that rcvid have already been shunned
|
| ︙ | ︙ |
Changes to src/sitemap.c.
| ︙ | ︙ | |||
114 115 116 117 118 119 120 |
if( (e&1)!=0 ) goto end_of_sitemap;
if( inSublist ){
@ </ul>
inSublist = 0;
}
@ </li>
| | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
if( (e&1)!=0 ) goto end_of_sitemap;
if( inSublist ){
@ </ul>
inSublist = 0;
}
@ </li>
if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
@ <li>%z(href("%R/ckout"))Checkout Status</a></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,
|
| ︙ | ︙ | |||
283 284 285 286 287 288 289 |
}
if( !isPopup ){
style_header("Test Page Map");
style_adunit_config(ADUNIT_RIGHT_OK);
}
@ <ul id="sitemap" class="columns" style="column-width:20em">
if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){
| | > | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
}
if( !isPopup ){
style_header("Test Page Map");
style_adunit_config(ADUNIT_RIGHT_OK);
}
@ <ul id="sitemap" class="columns" style="column-width:20em">
if( g.perm.Admin || db_get_boolean("test_env_enable",0) ){
@ <li>%z(href("%R/test-env"))CGI Environment Test</a></li>
}
if( g.perm.Read ){
@ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
}
@ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li>
@ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li>
@ <li>%z(href("%R/hash-color-test"))Hash color test</a>
@ <li>%z(href("%R/test-bgcolor"))Background color test</a>
if( g.perm.Admin ){
@ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li>
@ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li>
@ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li>
@ <li>%z(href("%R/test-warning"))Error Log test page</a></li>
@ <li>%z(href("%R/repo_stat1"))Repository <tt>sqlite_stat1</tt> table</a>
@ <li>%z(href("%R/repo_schema"))Repository schema</a></li>
|
| ︙ | ︙ |
Changes to src/smtp.c.
| ︙ | ︙ | |||
154 155 156 157 158 159 160 |
struct SmtpSession {
const char *zFrom; /* Domain from which we are sending */
const char *zDest; /* Domain that will receive the email */
char *zHostname; /* Hostname of SMTP server for zDest */
u32 smtpFlags; /* Flags changing the operation */
FILE *logFile; /* Write session transcript to this log file */
Blob *pTranscript; /* Record session transcript here */
| | > > | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
struct SmtpSession {
const char *zFrom; /* Domain from which we are sending */
const char *zDest; /* Domain that will receive the email */
char *zHostname; /* Hostname of SMTP server for zDest */
u32 smtpFlags; /* Flags changing the operation */
FILE *logFile; /* Write session transcript to this log file */
Blob *pTranscript; /* Record session transcript here */
int bOpen; /* True if connection is Open */
int bFatal; /* Error is fatal. Do not retry */
char *zErr; /* Error message */
Blob inbuf; /* Input buffer */
UrlData url; /* Address of the server */
};
/* Allowed values for SmtpSession.smtpFlags */
#define SMTP_TRACE_STDOUT 0x00001 /* Debugging info to console */
#define SMTP_TRACE_FILE 0x00002 /* Debugging info to logFile */
#define SMTP_TRACE_BLOB 0x00004 /* Record transcript */
#define SMTP_DIRECT 0x00008 /* Skip the MX lookup */
|
| ︙ | ︙ | |||
178 179 180 181 182 183 184 185 186 187 188 |
void smtp_session_free(SmtpSession *pSession){
socket_close();
blob_reset(&pSession->inbuf);
fossil_free(pSession->zHostname);
fossil_free(pSession->zErr);
fossil_free(pSession);
}
/*
** Allocate a new SmtpSession object.
**
| > > > > > > > > > > > > > > > > > > > > > > | | | | < < | < < < | < | < < < < < < < | < | | | > > | > > > > > > > > > | > | > > < | | 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 |
void smtp_session_free(SmtpSession *pSession){
socket_close();
blob_reset(&pSession->inbuf);
fossil_free(pSession->zHostname);
fossil_free(pSession->zErr);
fossil_free(pSession);
}
/*
** Set an error message on the SmtpSession
*/
static void smtp_set_error(
SmtpSession *p, /* The SMTP context */
int bFatal, /* Fatal error. Reset and retry is pointless */
const char *zFormat, /* Error message. */
...
){
if( bFatal ) p->bFatal = 1;
if( p->zErr==0 ){
va_list ap;
va_start(ap, zFormat);
p->zErr = vmprintf(zFormat, ap);
va_end(ap);
}
if( p->bOpen ){
socket_close();
p->bOpen = 0;
}
}
/*
** Allocate a new SmtpSession object.
**
** Both zFrom and zDest must be specified. smtpFlags may not contain
** either SMTP_TRACE_FILE or SMTP_TRACE_BLOB as those settings must be
** added by a subsequent call to smtp_session_config().
**
** The iPort option is ignored unless SMTP_PORT is set in smtpFlags
*/
SmtpSession *smtp_session_new(
const char *zFrom, /* Domain for the client */
const char *zDest, /* Domain of the server */
u32 smtpFlags, /* Flags */
int iPort /* TCP port if the SMTP_PORT flags is present */
){
SmtpSession *p;
p = fossil_malloc( sizeof(*p) );
memset(p, 0, sizeof(*p));
p->zFrom = zFrom;
p->zDest = zDest;
p->smtpFlags = smtpFlags;
p->url.port = 25;
blob_init(&p->inbuf, 0, 0);
if( smtpFlags & SMTP_PORT ){
p->url.port = iPort;
}
if( (smtpFlags & SMTP_DIRECT)!=0 ){
int i;
p->zHostname = fossil_strdup(zDest);
for(i=0; p->zHostname[i] && p->zHostname[i]!=':'; i++){}
if( p->zHostname[i]==':' ){
p->zHostname[i] = 0;
p->url.port = atoi(&p->zHostname[i+1]);
}
}else{
p->zHostname = smtp_mx_host(zDest);
}
if( p->zHostname==0 ){
smtp_set_error(p, 1, "cannot locate SMTP server for \"%s\"", zDest);
return p;
}
p->url.name = p->zHostname;
socket_global_init();
p->bOpen = 0;
return p;
}
/*
** Configure debugging options on SmtpSession. Add all bits in
** smtpFlags to the settings. The following bits can be added:
**
** SMTP_FLAG_FILE: In which case pArg is the FILE* pointer to use
**
** SMTP_FLAG_BLOB: In which case pArg is the Blob* poitner to use.
*/
void smtp_session_config(SmtpSession *p, u32 smtpFlags, void *pArg){
p->smtpFlags = smtpFlags;
if( smtpFlags & SMTP_TRACE_FILE ){
p->logFile = (FILE*)pArg;
}else if( smtpFlags & SMTP_TRACE_BLOB ){
p->pTranscript = (Blob*)pArg;
}
}
/*
** Send a single line of output the SMTP client to the server.
*/
static void smtp_send_line(SmtpSession *p, const char *zFormat, ...){
Blob b = empty_blob;
va_list ap;
char *z;
int n;
if( !p->bOpen ) return;
va_start(ap, zFormat);
blob_vappendf(&b, zFormat, ap);
va_end(ap);
z = blob_buffer(&b);
n = blob_size(&b);
assert( n>=2 );
assert( z[n-1]=='\n' );
|
| ︙ | ︙ | |||
289 290 291 292 293 294 295 |
static void smtp_recv_line(SmtpSession *p, Blob *in){
int n = blob_size(&p->inbuf);
char *z = blob_buffer(&p->inbuf);
int i = blob_tell(&p->inbuf);
int nDelay = 0;
if( i<n && z[n-1]=='\n' ){
blob_line(&p->inbuf, in);
| | | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
static void smtp_recv_line(SmtpSession *p, Blob *in){
int n = blob_size(&p->inbuf);
char *z = blob_buffer(&p->inbuf);
int i = blob_tell(&p->inbuf);
int nDelay = 0;
if( i<n && z[n-1]=='\n' ){
blob_line(&p->inbuf, in);
}else if( !p->bOpen ){
blob_init(in, 0, 0);
}else{
if( n>0 && i>=n ){
blob_truncate(&p->inbuf, 0);
blob_rewind(&p->inbuf);
n = 0;
}
|
| ︙ | ︙ | |||
312 313 314 315 316 317 318 |
z[n] = 0;
if( n>0 && z[n-1]=='\n' ) break;
if( got==1000 ) continue;
}
nDelay++;
if( nDelay>100 ){
blob_init(in, 0, 0);
| | < < | 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
z[n] = 0;
if( n>0 && z[n-1]=='\n' ) break;
if( got==1000 ) continue;
}
nDelay++;
if( nDelay>100 ){
blob_init(in, 0, 0);
smtp_set_error(p, 1, "client times out waiting on server response");
return;
}else{
sqlite3_sleep(100);
}
}while( n<1 || z[n-1]!='\n' );
blob_truncate(&p->inbuf, n);
blob_line(&p->inbuf, in);
|
| ︙ | ︙ | |||
352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
int *pbMore, /* True if the reply is not complete */
char **pzArg /* Argument */
){
int n;
char *z;
blob_truncate(in, 0);
smtp_recv_line(p, in);
z = blob_str(in);
n = blob_size(in);
if( z[0]=='#' ){
*piCode = 0;
*pbMore = 1;
*pzArg = z;
}else{
| > | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
int *pbMore, /* True if the reply is not complete */
char **pzArg /* Argument */
){
int n;
char *z;
blob_truncate(in, 0);
smtp_recv_line(p, in);
blob_trim(in);
z = blob_str(in);
n = blob_size(in);
if( z[0]=='#' ){
*piCode = 0;
*pbMore = 1;
*pzArg = z;
}else{
|
| ︙ | ︙ | |||
373 374 375 376 377 378 379 |
** Have the client send a QUIT message.
*/
int smtp_client_quit(SmtpSession *p){
Blob in = BLOB_INITIALIZER;
int iCode = 0;
int bMore = 0;
char *zArg = 0;
| > | | | | | | > | > > > > > > > > > > | 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 |
** Have the client send a QUIT message.
*/
int smtp_client_quit(SmtpSession *p){
Blob in = BLOB_INITIALIZER;
int iCode = 0;
int bMore = 0;
char *zArg = 0;
if( p->bOpen ){
smtp_send_line(p, "QUIT\r\n");
do{
smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
}while( bMore );
p->bOpen = 0;
socket_close();
}
return 0;
}
/*
** Begin a client SMTP session. Wait for the initial 220 then send
** the EHLO and wait for a 250.
**
** Return 0 on success and non-zero for a failure.
*/
static int smtp_client_startup(SmtpSession *p){
Blob in = BLOB_INITIALIZER;
int iCode = 0;
int bMore = 0;
char *zArg = 0;
if( p==0 || p->bFatal ) return 1;
if( socket_open(&p->url) ){
smtp_set_error(p, 1, "can't open socket: %z", socket_errmsg());
return 1;
}
p->bOpen = 1;
do{
smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
}while( bMore );
if( iCode!=220 ){
smtp_set_error(p, 1, "conversation begins with: \"%d %s\"",iCode,zArg);
smtp_client_quit(p);
return 1;
}
smtp_send_line(p, "EHLO %s\r\n", p->zFrom);
do{
smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
}while( bMore );
if( iCode!=250 ){
smtp_set_error(p, 1, "reply to EHLO with: \"%d %s\"",iCode, zArg);
smtp_client_quit(p);
return 1;
}
fossil_free(p->zErr);
p->zErr = 0;
return 0;
}
/*
** COMMAND: test-smtp-probe
**
** Usage: %fossil test-smtp-probe DOMAIN [ME]
|
| ︙ | ︙ | |||
539 540 541 542 543 544 545 546 547 548 549 |
){
int i;
int iCode = 0;
int bMore = 0;
char *zArg = 0;
Blob in;
blob_init(&in, 0, 0);
smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom);
do{
smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
}while( bMore );
| > > > > | > > > | > > > | > > > | > > > > | 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 |
){
int i;
int iCode = 0;
int bMore = 0;
char *zArg = 0;
Blob in;
blob_init(&in, 0, 0);
if( !p->bOpen ){
if( !p->bFatal ) smtp_client_startup(p);
if( !p->bOpen ) return 1;
}
smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom);
do{
smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
}while( bMore );
if( iCode!=250 ){
smtp_set_error(p, 0,"reply to MAIL FROM: \"%d %s\"",iCode,zArg);
return 1;
}
for(i=0; i<nTo; i++){
smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]);
do{
smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
}while( bMore );
if( iCode!=250 ){
smtp_set_error(p, 0,"reply to RCPT TO: \"%d %s\"",iCode,zArg);
return 1;
}
}
smtp_send_line(p, "DATA\r\n");
do{
smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
}while( bMore );
if( iCode!=354 ){
smtp_set_error(p, 0, "reply to DATA with: \"%d %s\"",iCode,zArg);
return 1;
}
smtp_send_email_body(zMsg, socket_send, 0);
if( p->smtpFlags & SMTP_TRACE_STDOUT ){
fossil_print("C: # message content\nC: .\n");
}
if( p->smtpFlags & SMTP_TRACE_FILE ){
fprintf(p->logFile, "C: # message content\nC: .\n");
}
if( p->smtpFlags & SMTP_TRACE_BLOB ){
blob_appendf(p->pTranscript, "C: # message content\nC: .\n");
}
do{
smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
}while( bMore );
if( iCode!=250 ){
smtp_set_error(p, 0, "reply to end-of-DATA with: \"%d %s\"",
iCode, zArg);
return 1;
}
return 0;
}
/*
** The input is a base email address of the form "local@domain".
** Return a pointer to just the "domain" part, or 0 if the string
** contains no "@".
|
| ︙ | ︙ | |||
634 635 636 637 638 639 640 |
zToDomain = domain_of_addr(azTo[0]);
}
p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort);
if( p->zErr ){
fossil_fatal("%s", p->zErr);
}
fossil_print("Connection to \"%s\"\n", p->zHostname);
| < | 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 |
zToDomain = domain_of_addr(azTo[0]);
}
p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort);
if( p->zErr ){
fossil_fatal("%s", p->zErr);
}
fossil_print("Connection to \"%s\"\n", p->zHostname);
smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body));
smtp_client_quit(p);
if( p->zErr ){
fossil_fatal("ERROR: %s\n", p->zErr);
}
smtp_session_free(p);
blob_reset(&body);
}
|
Changes to src/sorttable.js.
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* Javascript code that will enables sorting of the table. This code is ** derived from ** ** http://www.webtoolkit.info/sortable-html-table.html ** ** but with extensive modifications. ** ** All tables with class "sortable" are registered with the SortableTable() ** function. Example: ** ** <table class='sortable' data-column-types='tnkx' data-init-sort='2'> ** ** Column data types are determined by the data-column-types attribute of | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* Javascript code that will enables sorting of the table. This code is ** derived from ** ** http://www.webtoolkit.info/sortable-html-table.html ** ** but with extensive modifications. ** ** All tables with class "sortable" are registered with the SortableTable() ** function. Example: ** ** <table class='sortable' data-column-types='tnkx' data-init-sort='2'> ** ** Column data types are determined by the data-column-types attribute of ** the table. The value of data-column-types is a string where each ** character of the string represents a datatype for one column in the ** table. ** ** t Sort by text ** n Sort numerically ** k Sort by the data-sortkey property ** x This column is not sortable |
| ︙ | ︙ | |||
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
var clsName = hdrCell.className.replace(/\s*\bsort\s*\w+/, '');
clsName += ' sort ' + sortType;
hdrCell.className = clsName;
}
}
this.sortText = function(a,b) {
var i = thisObject.sortIndex;
aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
if(aa<bb) return -1;
if(aa==bb) return a.rowIndex-b.rowIndex;
return 1;
}
this.sortReverseText = function(a,b) {
var i = thisObject.sortIndex;
aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
if(aa<bb) return +1;
if(aa==bb) return a.rowIndex-b.rowIndex;
return -1;
}
this.sortNumeric = function(a,b) {
| > > > > | 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 |
var clsName = hdrCell.className.replace(/\s*\bsort\s*\w+/, '');
clsName += ' sort ' + sortType;
hdrCell.className = clsName;
}
}
this.sortText = function(a,b) {
var i = thisObject.sortIndex;
if (a.cells.length<=i) return -1; /* see ticket 59d699710b1ab5d4 */
if (b.cells.length<=i) return 1;
aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
if(aa<bb) return -1;
if(aa==bb) return a.rowIndex-b.rowIndex;
return 1;
}
this.sortReverseText = function(a,b) {
var i = thisObject.sortIndex;
if (a.cells.length<=i) return 1; /* see ticket 59d699710b1ab5d4 */
if (b.cells.length<=i) return -1;
aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase();
if(aa<bb) return +1;
if(aa==bb) return a.rowIndex-b.rowIndex;
return -1;
}
this.sortNumeric = function(a,b) {
|
| ︙ | ︙ |
Changes to src/sqlcmd.c.
| ︙ | ︙ | |||
233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
}
if( g.zConfigDbName ){
char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''",
g.zConfigDbName);
sqlite3_exec(db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}
/* Arrange to trace close operations so that static prepared statements
** will get cleaned up when the shell closes the database connection */
if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
db_protect_only(PROTECT_NONE);
sqlite3_set_authorizer(db, db_top_authorizer, db);
if( local_bSqlCmdTest ){
| > | 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
}
if( g.zConfigDbName ){
char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''",
g.zConfigDbName);
sqlite3_exec(db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}
(void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */
/* Arrange to trace close operations so that static prepared statements
** will get cleaned up when the shell closes the database connection */
if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
db_protect_only(PROTECT_NONE);
sqlite3_set_authorizer(db, db_top_authorizer, db);
if( local_bSqlCmdTest ){
|
| ︙ | ︙ |
Changes to src/stash.c.
| ︙ | ︙ | |||
257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
*/
static int stash_create(void){
const char *zComment; /* Comment to add to the stash */
int stashid; /* ID of the new stash */
int vid; /* Current check-out */
zComment = find_option("comment", "m", 1);
verify_all_options();
if( zComment==0 ){
Blob prompt; /* Prompt for stash comment */
Blob comment; /* User comment reply */
#if defined(_WIN32) || defined(__CYGWIN__)
int bomSize;
const unsigned char *bom = get_utf8_bom(&bomSize);
| > | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
*/
static int stash_create(void){
const char *zComment; /* Comment to add to the stash */
int stashid; /* ID of the new stash */
int vid; /* Current check-out */
zComment = find_option("comment", "m", 1);
(void)fossil_text_editor();
verify_all_options();
if( zComment==0 ){
Blob prompt; /* Prompt for stash comment */
Blob comment; /* User comment reply */
#if defined(_WIN32) || defined(__CYGWIN__)
int bomSize;
const unsigned char *bom = get_utf8_bom(&bomSize);
|
| ︙ | ︙ | |||
506 507 508 509 510 511 512 | /* ** COMMAND: stash ** ** Usage: %fossil stash SUBCOMMAND ARGS... ** ** > fossil stash | | | > > > > > | 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 | /* ** COMMAND: stash ** ** Usage: %fossil stash SUBCOMMAND ARGS... ** ** > fossil stash ** > fossil stash save ?FILES...? ** > fossil stash snapshot ?FILES...? ** ** Save the current changes in the working tree as a new stash. ** Then revert the changes back to the last check-in. If FILES ** are listed, then only stash and revert the named files. The ** "save" verb can be omitted if and only if there are no other ** arguments. The "snapshot" verb works the same as "save" but ** omits the revert, keeping the check-out unchanged. ** ** Options: ** --editor NAME Use the NAME editor to enter comment ** -m|--comment COMMENT Comment text for the new stash ** ** ** > fossil stash list|ls ?-v|--verbose? ?-W|--width NUM? ** ** List all changes sets currently stashed. Show information about ** individual files in each changeset if -v or --verbose is used. ** ** > fossil stash show|cat ?STASHID? ?DIFF-OPTIONS? |
| ︙ | ︙ | |||
754 755 756 757 758 759 760 |
){
int fBaseline = 0;
DiffConfig DCfg;
if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
fBaseline = 1;
}
| | | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 |
){
int fBaseline = 0;
DiffConfig DCfg;
if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
fBaseline = 1;
}
if( find_option("tk",0,0)!=0 || gdiff_using_tk(zCmd[0]=='g') ){
db_close(0);
diff_tk(fBaseline ? "stash show" : "stash diff", 3);
return;
}
diff_options(&DCfg, zCmd[0]=='g', 0);
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
stash_diff(stashid, fBaseline, &DCfg);
|
| ︙ | ︙ |
Changes to src/stat.c.
| ︙ | ︙ | |||
164 165 166 167 168 169 170 |
style_submenu_element("Activity Reports", "reports");
style_submenu_element("Hash Collisions", "hash-collisions");
style_submenu_element("Artifacts", "bloblist");
if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){
style_submenu_element("Table Sizes", "repo-tabsize");
}
if( g.perm.Admin || g.perm.Setup || db_get_boolean("test_env_enable",0) ){
| | | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
style_submenu_element("Activity Reports", "reports");
style_submenu_element("Hash Collisions", "hash-collisions");
style_submenu_element("Artifacts", "bloblist");
if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){
style_submenu_element("Table Sizes", "repo-tabsize");
}
if( g.perm.Admin || g.perm.Setup || db_get_boolean("test_env_enable",0) ){
style_submenu_element("Environment", "test-env");
}
@ <table class="label-value">
fsize = file_size(g.zRepositoryName, ExtFILE);
@ <tr><th>Repository Size:</th><td>%,lld(fsize) bytes</td>
@ </td></tr>
if( !brief ){
@ <tr><th>Number Of Artifacts:</th><td>
|
| ︙ | ︙ | |||
266 267 268 269 270 271 272 |
}
}
}
@ <tr><th>Project Age:</th><td>
z = db_text(0, "SELECT timediff('now',(SELECT min(mtime) FROM event));");
sscanf(z, "+%d-%d-%d", &Y, &M, &D);
if( Y>0 ){
| | | > | > | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
}
}
}
@ <tr><th>Project Age:</th><td>
z = db_text(0, "SELECT timediff('now',(SELECT min(mtime) FROM event));");
sscanf(z, "+%d-%d-%d", &Y, &M, &D);
if( Y>0 ){
@ %d(Y) year%s(Y==1?"":"s") \
}
if( M>0 ){
@ %d(M) month%s(M==1?"":"s") \
}
if( D>0 || (Y==0 && M==0) ){
@ %d(D) day%s(D==1?"":"s")
}
@ </td></tr>
p = db_get("project-code", 0);
if( p ){
@ <tr><th>Project ID:</th>
@ <td>%h(p) %h(db_get("project-name",""))</td></tr>
}
p = db_get("parent-project-code", 0);
|
| ︙ | ︙ |
Changes to src/statrep.c.
| ︙ | ︙ | |||
288 289 290 291 292 293 294 |
rowClass = ++nRowNumber % 2;
nEventTotal += nCount;
nEventsPerYear += nCount;
@<tr class='row%d(rowClass)'>
@ <td>
if(includeMonth){
cgi_printf("<a href='%R/timeline?"
| | | | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
rowClass = ++nRowNumber % 2;
nEventTotal += nCount;
nEventsPerYear += nCount;
@<tr class='row%d(rowClass)'>
@ <td>
if(includeMonth){
cgi_printf("<a href='%R/timeline?"
"ym=%t&y=%s",
zTimeframe,
statsReportTimelineYFlag );
/* Reminder: n=nCount is not actually correct for bymonth unless
that was the only user who caused events.
*/
if( zUserName ){
cgi_printf("&u=%t", zUserName);
}
|
| ︙ | ︙ | |||
316 317 318 319 320 321 322 323 324 325 326 |
&& rNowFraction>0.05
&& nCount>0
&& nMaxEvents>0
){
/* If the timespan covered by this row contains "now", then project
** the number of changes until the completion of the timespan and
** show a dashed box of that projection. */
int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
int nXSize = (100 * nExtra)/nMaxEvents;
@ <span class='statistics-report-graph-line' \
@ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\
| > | | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
&& rNowFraction>0.05
&& nCount>0
&& nMaxEvents>0
){
/* If the timespan covered by this row contains "now", then project
** the number of changes until the completion of the timespan and
** show a dashed box of that projection. */
int nProj = (int)(((double)nCount)/rNowFraction);
int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
int nXSize = (100 * nExtra)/nMaxEvents;
@ <span class='statistics-report-graph-line' \
@ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\
@ <span class='statistics-report-graph-extra' title='%d(nProj)' \
@ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\
}else{
@ <div class='statistics-report-graph-line' \
@ style='width:%d(nSize)%%;'> </div> \
}
@ </td>
@ </tr>
|
| ︙ | ︙ | |||
729 730 731 732 733 734 735 |
const int nCount = db_column_int(&q,1);
int nSize = (nCount>0 && nMaxEvents>0)
? (int)(100 * nCount / nMaxEvents)
: 0;
if(!nSize) nSize = 1;
total += nCount;
cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
| | | | > | | 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 |
const int nCount = db_column_int(&q,1);
int nSize = (nCount>0 && nMaxEvents>0)
? (int)(100 * nCount / nMaxEvents)
: 0;
if(!nSize) nSize = 1;
total += nCount;
cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
cgi_printf("<td><a href='%R/timeline?yw=%t%s&y=%s",
zYear, zWeek,
statsReportTimelineYFlag);
if( zUserName ){
cgi_printf("&u=%t",zUserName);
}
cgi_printf("'>%s</a></td>",zWeek);
cgi_printf("<td>%d</td>",nCount);
cgi_printf("<td style='white-space: nowrap;'>");
if( nCount ){
if( zCurrentWeek!=0
&& strcmp(zWeek, zCurrentWeek)==0
&& rNowFraction>0.05
&& nMaxEvents>0
){
/* If the timespan covered by this row contains "now", then project
** the number of changes until the completion of the week and
** show a dashed box of that projection. */
int nProj = (int)(((double)nCount)/rNowFraction);
int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
int nXSize = (100 * nExtra)/nMaxEvents;
@ <span class='statistics-report-graph-line' \
@ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\
@ <span class='statistics-report-graph-extra' title='%d(nProj)' \
@ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\
}else{
@ <div class='statistics-report-graph-line' \
@ style='width:%d(nSize)%%;'> </div> \
}
}
cgi_printf("</td></tr>\n");
|
| ︙ | ︙ | |||
855 856 857 858 859 860 861 862 863 864 865 866 867 868 | ** * m (merge check-in), ** * n (non-merge check-in) ** * f (forum post) ** * w (wiki page change) ** * t (ticket change) ** * g (tag added or removed) ** Defaulting to all event types. ** ** The view-specific query parameters include: ** ** view=byweek: ** ** y=YYYY The year to report (default is the server's ** current year). | > > > | 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 | ** * m (merge check-in), ** * n (non-merge check-in) ** * f (forum post) ** * w (wiki page change) ** * t (ticket change) ** * g (tag added or removed) ** Defaulting to all event types. ** from=DATETIME Consider only events after this timestamp (requires to) ** to=DATETIME Consider only events before this timestamp (requires from) ** ** ** The view-specific query parameters include: ** ** view=byweek: ** ** y=YYYY The year to report (default is the server's ** current year). |
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
742 743 744 745 746 747 748 |
** Do not overwrite the TH1 variable "default_csp" if it exists, as this
** allows it to be properly overridden via the TH1 setup script (i.e. it
** is evaluated before the header is rendered).
*/
Th_MaybeStore("default_csp", zDfltCsp);
fossil_free(zDfltCsp);
Th_Store("nonce", zNonce);
| > | | | | 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 |
** Do not overwrite the TH1 variable "default_csp" if it exists, as this
** allows it to be properly overridden via the TH1 setup script (i.e. it
** is evaluated before the header is rendered).
*/
Th_MaybeStore("default_csp", zDfltCsp);
fossil_free(zDfltCsp);
Th_Store("nonce", zNonce);
Th_StoreUnsafe("project_name",
db_get("project-name","Unnamed Fossil Project"));
Th_StoreUnsafe("project_description", db_get("project-description",""));
if( zTitle ) Th_Store("title", html_lookalike(zTitle,-1));
Th_Store("baseurl", g.zBaseURL);
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
Th_Store("home", g.zTop);
Th_Store("index_page", db_get("index-page","/home"));
if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath);
Th_Store("current_page", local_zCurrentPage);
if( g.zPath ){ /* store the first segment of a path; */
|
| ︙ | ︙ | |||
770 771 772 773 774 775 776 |
Th_Store("manifest_date", MANIFEST_DATE);
Th_Store("compiler_name", COMPILER_NAME);
Th_Store("mainmenu", style_get_mainmenu());
stylesheet_url_var();
image_url_var("logo");
image_url_var("background");
if( !login_is_nobody() ){
| | | 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
Th_Store("manifest_date", MANIFEST_DATE);
Th_Store("compiler_name", COMPILER_NAME);
Th_Store("mainmenu", style_get_mainmenu());
stylesheet_url_var();
image_url_var("logo");
image_url_var("background");
if( !login_is_nobody() ){
Th_Store("login", html_lookalike(g.zLogin,-1));
}
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]);
|
| ︙ | ︙ | |||
819 820 821 822 823 824 825 826 827 828 829 830 831 832 |
headerHasBeenGenerated = 1;
sideboxUsed = 0;
if( g.perm.Debug && P("showqp") ){
@ <div class="debug">
cgi_print_all(0, 0, 0);
@ </div>
}
}
#if INTERFACE
/* Allowed parameters for style_adunit() */
#define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */
#define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */
#endif
| > | 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 |
headerHasBeenGenerated = 1;
sideboxUsed = 0;
if( g.perm.Debug && P("showqp") ){
@ <div class="debug">
cgi_print_all(0, 0, 0);
@ </div>
}
fossil_free(zTitle);
}
#if INTERFACE
/* Allowed parameters for style_adunit() */
#define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */
#define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */
#endif
|
| ︙ | ︙ | |||
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 |
*/
Th_Store("baseurl", g.zBaseURL);
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
Th_Store("home", g.zTop);
image_url_var("logo");
image_url_var("background");
Th_Render(blob_str(&css));
/* Tell CGI that the content returned by this page is considered cacheable */
g.isConst = 1;
}
/*
** All possible capabilities
| > | 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 |
*/
Th_Store("baseurl", g.zBaseURL);
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
Th_Store("home", g.zTop);
image_url_var("logo");
image_url_var("background");
Th_Render(blob_str(&css));
blob_reset(&css);
/* Tell CGI that the content returned by this page is considered cacheable */
g.isConst = 1;
}
/*
** All possible capabilities
|
| ︙ | ︙ | |||
1338 1339 1340 1341 1342 1343 1344 |
&& !login_has_capability(&c, 1, 0) ) zCap[i++] = c;
}
zCap[i] = 0;
return zCap;
}
/*
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 |
&& !login_has_capability(&c, 1, 0) ) zCap[i++] = c;
}
zCap[i] = 0;
return zCap;
}
/*
** WEBPAGE: test-title
**
** Render a test page in which the page title is set by the "title"
** query parameter. This can be used to show that HTML or Javascript
** content in the title does not leak through into generated page, resulting
** in an XSS issue.
**
** Due to the potential for abuse, this webpage is only available to
** administrators.
*/
void page_test_title(void){
const char *zTitle;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
}
zTitle = P("title");
if( zTitle==0 ){
zTitle = "(No Title)";
}
style_header("%s", zTitle);
@ <p>
@ This page sets its title to the value of the "title" query parameter.
@ The form below is a convenient way to set the title query parameter:
@
@ <form method="GET">
@ Title: <input type="text" size="50" name="title" value="%h(zTitle)">
@ <input type="submit" value="Submit">
@ </form>
style_finish_page();
}
/*
** WEBPAGE: test-env
** WEBPAGE: test_env alias
**
** Display CGI-variables and other aspects of the run-time
** environment, for debugging and trouble-shooting purposes.
*/
void page_test_env(void){
webpage_error("");
}
|
| ︙ | ︙ | |||
1401 1402 1403 1404 1405 1406 1407 | ** 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. ** | | | 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 |
** 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];
|
| ︙ | ︙ | |||
1501 1502 1503 1504 1505 1506 1507 |
blob_zero(&t);
}
}
@ <hr>
P("HTTP_USER_AGENT");
P("SERVER_SOFTWARE");
cgi_print_all(showAll, 0, 0);
| | | 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 |
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))
|
| ︙ | ︙ |
Changes to src/style.chat.css.
| ︙ | ︙ | |||
211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
overflow: auto;
padding: 0 0.25em;
}
body.chat #chat-messages-wrapper.loading > * {
/* An attempt at reducing flicker when loading lots of messages. */
visibility: hidden;
}
body.chat div.content {
margin: 0;
padding: 0;
display: flex;
flex-direction: column-reverse;
/* ^^^^ In order to get good automatic scrolling of new messages on
the BOTTOM in bottom-up chat mode, such that they scroll up
| > > > > > > > > > | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
overflow: auto;
padding: 0 0.25em;
}
body.chat #chat-messages-wrapper.loading > * {
/* An attempt at reducing flicker when loading lots of messages. */
visibility: hidden;
}
/* Provide a visual cue when polling is offline. */
body.chat.connection-error #chat-input-line-wrapper {
border-top: medium dotted red;
}
body.chat.fossil-dark-style.connection-error #chat-input-line-wrapper {
border-color: yellow;
}
body.chat div.content {
margin: 0;
padding: 0;
display: flex;
flex-direction: column-reverse;
/* ^^^^ In order to get good automatic scrolling of new messages on
the BOTTOM in bottom-up chat mode, such that they scroll up
|
| ︙ | ︙ | |||
239 240 241 242 243 244 245 |
}
body.chat:not(.chat-only-mode) #chat-input-area{
/* Safari user reports that 2em is necessary to keep the file selection
widget from overlapping the page footer, whereas a margin of 0 is fine
for FF/Chrome (and 2em is a *huge* waste of space for those). */
margin-bottom: 0;
}
| > | | | | | | | | 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 |
}
body.chat:not(.chat-only-mode) #chat-input-area{
/* Safari user reports that 2em is necessary to keep the file selection
widget from overlapping the page footer, whereas a margin of 0 is fine
for FF/Chrome (and 2em is a *huge* waste of space for those). */
margin-bottom: 0;
}
body.chat .chat-input-field {
flex: 10 1 auto;
margin: 0;
}
body.chat #chat-input-field-x,
body.chat #chat-input-field-multi {
overflow: auto;
resize: vertical;
}
body.chat #chat-input-field-x {
display: inline-block/*supposed workaround for Chrome weirdness*/;
padding: 0.2em;
background-color: rgba(156,156,156,0.3);
white-space: pre-wrap;
/* ^^^ Firefox, when pasting plain text into a contenteditable field,
loses all newlines unless we explicitly set this. Chrome does not. */
cursor: text;
/* ^^^ In some browsers the cursor may not change for a contenteditable
element until it has focus, causing potential confusion. */
}
body.chat #chat-input-field-x:empty::before {
content: attr(data-placeholder);
opacity: 0.6;
}
body.chat .chat-input-field:not(:focus){
border-width: 1px;
border-style: solid;
border-radius: 0.25em;
}
body.chat .chat-input-field:focus{
/* This transparent border helps avoid the text shifting around
when the contenteditable attribute causes a border (which we
apparently cannot style) to be added. */
border-width: 1px;
border-style: solid;
border-color: transparent;
border-radius: 0.25em;
|
| ︙ | ︙ |
Changes to src/tag.c.
| ︙ | ︙ | |||
410 411 412 413 414 415 416 | ** --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 | | | 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | ** --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-MM-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 ** |
| ︙ | ︙ |
Changes to src/tar.c.
| ︙ | ︙ | |||
497 498 499 500 501 502 503 |
nPrefix = blob_size(&filename);
pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
if( pManifest ){
int flg, eflg = 0;
mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0);
if( pTar ) tar_begin(mTime);
| | | 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
nPrefix = blob_size(&filename);
pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
if( pManifest ){
int flg, eflg = 0;
mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0);
if( pTar ) tar_begin(mTime);
flg = db_get_manifest_setting(blob_str(&hash));
if( flg ){
/* eflg is the effective flags, taking include/exclude into account */
if( (pInclude==0 || glob_match(pInclude, "manifest"))
&& !glob_match(pExclude, "manifest")
&& (flg & MFESTFLG_RAW) ){
eflg |= MFESTFLG_RAW;
}
|
| ︙ | ︙ |
Changes to src/terminal.c.
| ︙ | ︙ | |||
58 59 60 61 62 63 64 |
*/
int terminal_get_size(TerminalSize *t){
memset(t, 0, sizeof(*t));
#if defined(TIOCGSIZE)
{
struct ttysize ts;
| | > > > | > > > | > > > | 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 |
*/
int terminal_get_size(TerminalSize *t){
memset(t, 0, sizeof(*t));
#if defined(TIOCGSIZE)
{
struct ttysize ts;
if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0
|| ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0
|| ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0
){
t->nColumns = ts.ts_cols;
t->nLines = ts.ts_lines;
return 1;
}
return 0;
}
#elif defined(TIOCGWINSZ)
{
struct winsize ws;
if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0
|| ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0
|| ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0
){
t->nColumns = ws.ws_col;
t->nLines = ws.ws_row;
return 1;
}
return 0;
}
#elif defined(_WIN32)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi)
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi)
){
t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
return 1;
}
return 0;
}
#else
|
| ︙ | ︙ | |||
127 128 129 130 131 132 133 |
** If the size cannot be determined, two zeros are shown.
*/
void test_terminal_size_cmd(void){
TerminalSize ts;
terminal_get_size(&ts);
fossil_print("%d %d\n", ts.nColumns, ts.nLines);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
** If the size cannot be determined, two zeros are shown.
*/
void test_terminal_size_cmd(void){
TerminalSize ts;
terminal_get_size(&ts);
fossil_print("%d %d\n", ts.nColumns, ts.nLines);
}
/*
** Return true if it is reasonable is emit VT100 escape codes.
*/
int terminal_is_vt100(void){
char *zNoColor;
#ifdef _WIN32
if( !win32_terminal_is_vt100(1) ) return 0;
#endif /* _WIN32 */
if( !fossil_isatty(1) ) return 0;
zNoColor =fossil_getenv("NO_COLOR");
if( zNoColor==0 ) return 1;
if( zNoColor[0]==0 ) return 1;
if( is_false(zNoColor) ) return 1;
return 0;
}
#ifdef _WIN32
/*
** Return true if the Windows console supports VT100 escape codes.
**
** Support for VT100 escape codes is enabled by default in Windows Terminal
** on Windows 10 and Windows 11, and disabled by default in Legacy Consoles
** and on older versions of Windows. Programs can turn on VT100 support for
** Legacy Consoles using the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag.
**
** NOTE: If this function needs to be called in more complex scenarios with
** reassigned stdout and stderr streams, the following CRT calls are useful
** to translate from CRT streams to file descriptors and to Win32 handles:
**
** HANDLE hOutputHandle = (HANDLE)_get_osfhandle(_fileno(<FILE*>));
*/
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
int win32_terminal_is_vt100(int fd){
HANDLE hConsole = NULL;
DWORD dwConsoleMode;
switch( fd ){
case 1:
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
break;
case 2:
hConsole = GetStdHandle(STD_ERROR_HANDLE);
break;
}
if( GetConsoleMode(hConsole,&dwConsoleMode) ){
return (dwConsoleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)!=0;
}
return 0;
}
#endif /* _WIN32 */
|
Changes to src/th.c.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* ** The implementation of the TH core. This file contains the parser, and ** the implementation of the interface in th.h. */ #include "config.h" #include "th.h" #include <string.h> #include <assert.h> /* ** Values used for element values in the tcl_platform array. */ #if !defined(TH_ENGINE) # define TH_ENGINE "TH1" #endif | > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /* ** The implementation of the TH core. This file contains the parser, and ** the implementation of the interface in th.h. */ #include "config.h" #include "th.h" #include <string.h> #include <assert.h> /* ** External routines */ void fossil_panic(const char*,...); void fossil_errorlog(const char*,...); /* ** Values used for element values in the tcl_platform array. */ #if !defined(TH_ENGINE) # define TH_ENGINE "TH1" #endif |
| ︙ | ︙ | |||
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 |
** The Buffer structure and the thBufferXXX() functions are used to make
** memory allocation easier when building up a result.
*/
struct Buffer {
char *zBuf;
int nBuf;
int nBufAlloc;
};
typedef struct Buffer Buffer;
static void thBufferInit(Buffer *);
static void thBufferFree(Th_Interp *interp, Buffer *);
/*
** This version of memcpy() allows the first and second argument to
** be NULL as long as the number of bytes to copy is zero.
*/
static void th_memcpy(void *dest, const void *src, size_t n){
if( n>0 ) memcpy(dest,src,n);
}
/*
** Append nAdd bytes of content copied from zAdd to the end of buffer
** pBuffer. If there is not enough space currently allocated, resize
** the allocation to make space.
*/
static void thBufferWriteResize(
Th_Interp *interp,
Buffer *pBuffer,
const char *zAdd,
| > > > > > > > > > | > > > > | > | > | 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 |
** The Buffer structure and the thBufferXXX() functions are used to make
** memory allocation easier when building up a result.
*/
struct Buffer {
char *zBuf;
int nBuf;
int nBufAlloc;
int bTaint;
};
typedef struct Buffer Buffer;
static void thBufferInit(Buffer *);
static void thBufferFree(Th_Interp *interp, Buffer *);
/*
** This version of memcpy() allows the first and second argument to
** be NULL as long as the number of bytes to copy is zero.
*/
static void th_memcpy(void *dest, const void *src, size_t n){
if( n>0 ) memcpy(dest,src,n);
}
/*
** An oversized string has been encountered. Do not try to recover.
** Panic the process.
*/
void Th_OversizeString(void){
fossil_panic("string too large. maximum size 286MB.");
}
/*
** Append nAdd bytes of content copied from zAdd to the end of buffer
** pBuffer. If there is not enough space currently allocated, resize
** the allocation to make space.
*/
static void thBufferWriteResize(
Th_Interp *interp,
Buffer *pBuffer,
const char *zAdd,
int nAddX
){
int nAdd = TH1_LEN(nAddX);
int nNew = (pBuffer->nBuf+nAdd)*2+32;
#if defined(TH_MEMDEBUG)
char *zNew = (char *)Th_Malloc(interp, nNew);
TH1_SIZECHECK(nNew);
th_memcpy(zNew, pBuffer->zBuf, pBuffer->nBuf);
Th_Free(interp, pBuffer->zBuf);
pBuffer->zBuf = zNew;
#else
int nOld = pBuffer->nBufAlloc;
TH1_SIZECHECK(nNew);
pBuffer->zBuf = Th_Realloc(interp, pBuffer->zBuf, nNew);
memset(pBuffer->zBuf+nOld, 0, nNew-nOld);
#endif
pBuffer->nBufAlloc = nNew;
th_memcpy(&pBuffer->zBuf[pBuffer->nBuf], zAdd, nAdd);
pBuffer->nBuf += nAdd;
TH1_XFER_TAINT(pBuffer->bTaint, nAddX);
}
static void thBufferWriteFast(
Th_Interp *interp,
Buffer *pBuffer,
const char *zAdd,
int nAddX
){
int nAdd = TH1_LEN(nAddX);
if( pBuffer->nBuf+nAdd > pBuffer->nBufAlloc ){
thBufferWriteResize(interp, pBuffer, zAdd, nAddX);
}else{
if( pBuffer->zBuf ){
memcpy(pBuffer->zBuf + pBuffer->nBuf, zAdd, nAdd);
}
pBuffer->nBuf += nAdd;
TH1_XFER_TAINT(pBuffer->bTaint, nAddX);
}
}
#define thBufferWrite(a,b,c,d) thBufferWriteFast(a,b,(const char *)c,d)
/*
** Add a single character to a buffer
*/
|
| ︙ | ︙ | |||
702 703 704 705 706 707 708 709 710 711 |
Th_Interp *interp,
const char *zWord,
int nWord
){
int rc = TH_OK;
Buffer output;
int i;
thBufferInit(&output);
| > | | | | | | 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 |
Th_Interp *interp,
const char *zWord,
int nWord
){
int rc = TH_OK;
Buffer output;
int i;
int nn = TH1_LEN(nWord);
thBufferInit(&output);
if( nn>1 && (zWord[0]=='{' && zWord[nn-1]=='}') ){
thBufferWrite(interp, &output, &zWord[1], nn-2);
}else{
/* If the word is surrounded by double-quotes strip these away. */
if( nn>1 && (zWord[0]=='"' && zWord[nn-1]=='"') ){
zWord++;
nn -= 2;
}
for(i=0; rc==TH_OK && i<nn; i++){
int nGet;
int (*xGet)(Th_Interp *, const char*, int, int *) = 0;
int (*xSubst)(Th_Interp *, const char*, int) = 0;
switch( zWord[i] ){
case '\\':
|
| ︙ | ︙ | |||
741 742 743 744 745 746 747 |
}
default: {
thBufferAddChar(interp, &output, zWord[i]);
continue; /* Go to the next iteration of the for(...) loop */
}
}
| | | | 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 |
}
default: {
thBufferAddChar(interp, &output, zWord[i]);
continue; /* Go to the next iteration of the for(...) loop */
}
}
rc = xGet(interp, &zWord[i], nn-i, &nGet);
if( rc==TH_OK ){
rc = xSubst(interp, &zWord[i], nGet);
}
if( rc==TH_OK ){
const char *zRes;
int nRes;
zRes = Th_GetResult(interp, &nRes);
thBufferWrite(interp, &output, zRes, nRes);
i += (nGet-1);
}
}
}
if( rc==TH_OK ){
Th_SetResult(interp, output.zBuf, output.nBuf|output.bTaint);
}
thBufferFree(interp, &output);
return rc;
}
/*
** Return true if one of the following is true of the buffer pointed
|
| ︙ | ︙ | |||
824 825 826 827 828 829 830 | int rc = TH_OK; Buffer strbuf; Buffer lenbuf; int nCount = 0; const char *zInput = zList; | | | | | | 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 |
int rc = TH_OK;
Buffer strbuf;
Buffer lenbuf;
int nCount = 0;
const char *zInput = zList;
int nInput = TH1_LEN(nList);
thBufferInit(&strbuf);
thBufferInit(&lenbuf);
while( nInput>0 ){
const char *zWord;
int nWord;
thNextSpace(interp, zInput, nInput, &nWord);
zInput += nWord;
nInput = TH1_LEN(nList)-(zInput-zList);
if( TH_OK!=(rc = thNextWord(interp, zInput, nInput, &nWord, 0))
|| TH_OK!=(rc = thSubstWord(interp, zInput, nWord))
){
goto finish;
}
zInput = &zInput[TH1_LEN(nWord)];
nInput = TH1_LEN(nList)-(zInput-zList);
if( nWord>0 ){
zWord = Th_GetResult(interp, &nWord);
thBufferWrite(interp, &strbuf, zWord, nWord);
thBufferAddChar(interp, &strbuf, 0);
thBufferWrite(interp, &lenbuf, &nWord, sizeof(int));
nCount++;
}
|
| ︙ | ︙ | |||
870 871 872 873 874 875 876 |
);
anElem = (int *)&azElem[nCount];
zElem = (char *)&anElem[nCount];
th_memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf);
th_memcpy(zElem, strbuf.zBuf, strbuf.nBuf);
for(i=0; i<nCount;i++){
azElem[i] = zElem;
| | | 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 |
);
anElem = (int *)&azElem[nCount];
zElem = (char *)&anElem[nCount];
th_memcpy(anElem, lenbuf.zBuf, lenbuf.nBuf);
th_memcpy(zElem, strbuf.zBuf, strbuf.nBuf);
for(i=0; i<nCount;i++){
azElem[i] = zElem;
zElem += (TH1_LEN(anElem[i]) + 1);
}
*pazElem = azElem;
*panElem = anElem;
}
if( pnCount ){
*pnCount = nCount;
}
|
| ︙ | ︙ | |||
892 893 894 895 896 897 898 |
/*
** Evaluate the th1 script contained in the string (zProgram, nProgram)
** in the current stack frame.
*/
static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){
int rc = TH_OK;
const char *zInput = zProgram;
| | > > > > > | 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 |
/*
** Evaluate the th1 script contained in the string (zProgram, nProgram)
** in the current stack frame.
*/
static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){
int rc = TH_OK;
const char *zInput = zProgram;
int nInput = TH1_LEN(nProgram);
if( TH1_TAINTED(nProgram)
&& Th_ReportTaint(interp, "script", zProgram, nProgram)
){
return TH_ERROR;
}
while( rc==TH_OK && nInput ){
Th_HashEntry *pEntry;
int nSpace;
const char *zFirst;
char **argv;
int *argl;
|
| ︙ | ︙ | |||
947 948 949 950 951 952 953 |
*/
rc = thSplitList(interp, zFirst, zInput-zFirst, &argv, &argl, &argc);
if( rc!=TH_OK ) continue;
if( argc>0 ){
/* Look up the command name in the command hash-table. */
| | | | 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 |
*/
rc = thSplitList(interp, zFirst, zInput-zFirst, &argv, &argl, &argc);
if( rc!=TH_OK ) continue;
if( argc>0 ){
/* Look up the command name in the command hash-table. */
pEntry = Th_HashFind(interp, interp->paCmd, argv[0], TH1_LEN(argl[0]),0);
if( !pEntry ){
Th_ErrorMessage(interp, "no such command: ", argv[0], TH1_LEN(argl[0]));
rc = TH_ERROR;
}
/* Call the command procedure. */
if( rc==TH_OK ){
Th_Command *p = (Th_Command *)(pEntry->pData);
const char **azArg = (const char **)argv;
|
| ︙ | ︙ | |||
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 |
if( !interp->pFrame ){
rc = TH_ERROR;
}else{
int nInput = nProgram;
if( nInput<0 ){
nInput = th_strlen(zProgram);
}
rc = thEvalLocal(interp, zProgram, nInput);
}
interp->pFrame = pSavedFrame;
return rc;
}
| > > | 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 |
if( !interp->pFrame ){
rc = TH_ERROR;
}else{
int nInput = nProgram;
if( nInput<0 ){
nInput = th_strlen(zProgram);
}else{
nInput = TH1_LEN(nInput);
}
rc = thEvalLocal(interp, zProgram, nInput);
}
interp->pFrame = pSavedFrame;
return rc;
}
|
| ︙ | ︙ | |||
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 |
const char *zInner = 0;
int nInner = 0;
int isGlobal = 0;
int i;
if( nVarname<0 ){
nVarname = th_strlen(zVarname);
}
nOuter = nVarname;
/* If the variable name starts with "::", then do the lookup is in the
** uppermost (global) frame.
*/
if( nVarname>2 && zVarname[0]==':' && zVarname[1]==':' ){
| > > | 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 |
const char *zInner = 0;
int nInner = 0;
int isGlobal = 0;
int i;
if( nVarname<0 ){
nVarname = th_strlen(zVarname);
}else{
nVarname = TH1_LEN(nVarname);
}
nOuter = nVarname;
/* If the variable name starts with "::", then do the lookup is in the
** uppermost (global) frame.
*/
if( nVarname>2 && zVarname[0]==':' && zVarname[1]==':' ){
|
| ︙ | ︙ | |||
1269 1270 1271 1272 1273 1274 1275 |
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
return TH_ERROR;
}
return Th_SetResult(interp, pValue->zData, pValue->nData);
}
| < < < < < < < < < < < < < < < < < < < < < | 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
Th_ErrorMessage(interp, "no such variable:", zVar, nVar);
return TH_ERROR;
}
return Th_SetResult(interp, pValue->zData, pValue->nData);
}
/*
** Return true if variable (zVar, nVar) exists.
*/
int Th_ExistsVar(Th_Interp *interp, const char *zVar, int nVar){
Th_Variable *pValue = thFindValue(interp, zVar, nVar, 0, 1, 1, 0);
return pValue && (pValue->zData || pValue->pHash);
}
|
| ︙ | ︙ | |||
1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 |
Th_Interp *interp,
const char *zVar,
int nVar,
const char *zValue,
int nValue
){
Th_Variable *pValue;
pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
if( !pValue ){
return TH_ERROR;
}
if( nValue<0 ){
| > > | > > | | | | | 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 |
Th_Interp *interp,
const char *zVar,
int nVar,
const char *zValue,
int nValue
){
Th_Variable *pValue;
int nn;
nVar = TH1_LEN(nVar);
pValue = thFindValue(interp, zVar, nVar, 1, 0, 0, 0);
if( !pValue ){
return TH_ERROR;
}
if( nValue<0 ){
nn = th_strlen(zValue);
}else{
nn = TH1_LEN(nValue);
}
if( pValue->zData ){
Th_Free(interp, pValue->zData);
pValue->zData = 0;
}
assert(zValue || nn==0);
pValue->zData = Th_Malloc(interp, nn+1);
pValue->zData[nn] = '\0';
th_memcpy(pValue->zData, zValue, nn);
pValue->nData = nValue;
return TH_OK;
}
/*
** Create a variable link so that accessing variable (zLocal, nLocal) is
|
| ︙ | ︙ | |||
1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 |
** caller is responsible for eventually calling Th_Free() to free
** the returned buffer.
*/
char *th_strdup(Th_Interp *interp, const char *z, int n){
char *zRes;
if( n<0 ){
n = th_strlen(z);
}
zRes = Th_Malloc(interp, n+1);
th_memcpy(zRes, z, n);
zRes[n] = '\0';
return zRes;
}
| > > | 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 |
** caller is responsible for eventually calling Th_Free() to free
** the returned buffer.
*/
char *th_strdup(Th_Interp *interp, const char *z, int n){
char *zRes;
if( n<0 ){
n = th_strlen(z);
}else{
n = TH1_LEN(n);
}
zRes = Th_Malloc(interp, n+1);
th_memcpy(zRes, z, n);
zRes[n] = '\0';
return zRes;
}
|
| ︙ | ︙ | |||
1517 1518 1519 1520 1521 1522 1523 |
if( n<0 ){
n = th_strlen(z);
}
if( z && n>0 ){
char *zResult;
| > | | | | 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 |
if( n<0 ){
n = th_strlen(z);
}
if( z && n>0 ){
char *zResult;
int nn = TH1_LEN(n);
zResult = Th_Malloc(pInterp, nn+1);
th_memcpy(zResult, z, nn);
zResult[nn] = '\0';
pInterp->zResult = zResult;
pInterp->nResult = n;
}
return TH_OK;
}
|
| ︙ | ︙ | |||
1775 1776 1777 1778 1779 1780 1781 |
int i;
int hasSpecialChar = 0; /* Whitespace or {}[]'" */
int hasEscapeChar = 0; /* '}' without matching '{' to the left or a '\\' */
int nBrace = 0;
output.zBuf = *pzList;
| | > > > > | 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 |
int i;
int hasSpecialChar = 0; /* Whitespace or {}[]'" */
int hasEscapeChar = 0; /* '}' without matching '{' to the left or a '\\' */
int nBrace = 0;
output.zBuf = *pzList;
output.nBuf = TH1_LEN(*pnList);
output.nBufAlloc = output.nBuf;
output.bTaint = 0;
TH1_XFER_TAINT(output.bTaint, *pnList);
if( nElem<0 ){
nElem = th_strlen(zElem);
}else{
nElem = TH1_LEN(nElem);
}
if( output.nBuf>0 ){
thBufferAddChar(interp, &output, ' ');
}
for(i=0; i<nElem; i++){
char c = zElem[i];
|
| ︙ | ︙ | |||
1832 1833 1834 1835 1836 1837 1838 |
Th_Interp *interp, /* Interpreter context */
char **pzStr, /* IN/OUT: Ptr to ptr to list */
int *pnStr, /* IN/OUT: Current length of *pzStr */
const char *zElem, /* Data to append */
int nElem /* Length of nElem */
){
char *zNew;
| | > | > > | > | | | 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 |
Th_Interp *interp, /* Interpreter context */
char **pzStr, /* IN/OUT: Ptr to ptr to list */
int *pnStr, /* IN/OUT: Current length of *pzStr */
const char *zElem, /* Data to append */
int nElem /* Length of nElem */
){
char *zNew;
long long int nNew;
int nn;
if( nElem<0 ){
nn = th_strlen(zElem);
}else{
nn = TH1_LEN(nElem);
}
nNew = TH1_LEN(*pnStr) + nn;
TH1_SIZECHECK(nNew);
zNew = Th_Malloc(interp, nNew);
th_memcpy(zNew, *pzStr, *pnStr);
th_memcpy(&zNew[TH1_LEN(*pnStr)], zElem, nn);
Th_Free(interp, *pzStr);
*pzStr = zNew;
*pnStr = (int)nNew;
return TH_OK;
}
/*
** Initialize an interpreter.
*/
|
| ︙ | ︙ | |||
2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 |
char *zRight = 0; int nRight = 0;
/* Evaluate left and right arguments, if they exist. */
if( pExpr->pLeft ){
rc = exprEval(interp, pExpr->pLeft);
if( rc==TH_OK ){
zLeft = Th_TakeResult(interp, &nLeft);
}
}
if( rc==TH_OK && pExpr->pRight ){
rc = exprEval(interp, pExpr->pRight);
if( rc==TH_OK ){
zRight = Th_TakeResult(interp, &nRight);
}
}
/* Convert arguments to their required forms. */
if( rc==TH_OK ){
eArgType = pExpr->pOp->eArgType;
if( eArgType==ARG_NUMBER ){
| > > | 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 |
char *zRight = 0; int nRight = 0;
/* Evaluate left and right arguments, if they exist. */
if( pExpr->pLeft ){
rc = exprEval(interp, pExpr->pLeft);
if( rc==TH_OK ){
zLeft = Th_TakeResult(interp, &nLeft);
nLeft = TH1_LEN(nLeft);
}
}
if( rc==TH_OK && pExpr->pRight ){
rc = exprEval(interp, pExpr->pRight);
if( rc==TH_OK ){
zRight = Th_TakeResult(interp, &nRight);
nRight = TH1_LEN(nRight);
}
}
/* Convert arguments to their required forms. */
if( rc==TH_OK ){
eArgType = pExpr->pOp->eArgType;
if( eArgType==ARG_NUMBER ){
|
| ︙ | ︙ | |||
2158 2159 2160 2161 2162 2163 2164 |
rc = TH_ERROR;
goto finish;
}
iRes = iLeft%iRight;
break;
case OP_ADD: iRes = iLeft+iRight; break;
case OP_SUBTRACT: iRes = iLeft-iRight; break;
| | > > > | | 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 |
rc = TH_ERROR;
goto finish;
}
iRes = iLeft%iRight;
break;
case OP_ADD: iRes = iLeft+iRight; break;
case OP_SUBTRACT: iRes = iLeft-iRight; break;
case OP_LEFTSHIFT: {
iRes = (int)(((unsigned int)iLeft)<<(iRight&0x1f));
break;
}
case OP_RIGHTSHIFT: iRes = iLeft>>(iRight&0x1f); break;
case OP_LT: iRes = iLeft<iRight; break;
case OP_GT: iRes = iLeft>iRight; break;
case OP_LE: iRes = iLeft<=iRight; break;
case OP_GE: iRes = iLeft>=iRight; break;
case OP_EQ: iRes = iLeft==iRight; break;
case OP_NE: iRes = iLeft!=iRight; break;
case OP_BITWISE_AND: iRes = iLeft&iRight; break;
|
| ︙ | ︙ | |||
2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 |
int i; /* Loop counter */
int nToken = 0;
Expr **apToken = 0;
if( nExpr<0 ){
nExpr = th_strlen(zExpr);
}
/* Parse the expression to a list of tokens. */
rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken);
/* If the parsing was successful, create an expression tree from
** the parsed list of tokens. If successful, apToken[0] is set
| > > | 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 |
int i; /* Loop counter */
int nToken = 0;
Expr **apToken = 0;
if( nExpr<0 ){
nExpr = th_strlen(zExpr);
}else{
nExpr = TH1_LEN(nExpr);
}
/* Parse the expression to a list of tokens. */
rc = exprParse(interp, zExpr, nExpr, &apToken, &nToken);
/* If the parsing was successful, create an expression tree from
** the parsed list of tokens. If successful, apToken[0] is set
|
| ︙ | ︙ | |||
2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 |
unsigned int iKey = 0;
int i;
Th_HashEntry *pRet;
Th_HashEntry **ppRet;
if( nKey<0 ){
nKey = th_strlen(zKey);
}
for(i=0; i<nKey; i++){
iKey = (iKey<<3) ^ iKey ^ zKey[i];
}
iKey = iKey % TH_HASHSIZE;
| > > | 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 |
unsigned int iKey = 0;
int i;
Th_HashEntry *pRet;
Th_HashEntry **ppRet;
if( nKey<0 ){
nKey = th_strlen(zKey);
}else{
nKey = TH1_LEN(nKey);
}
for(i=0; i<nKey; i++){
iKey = (iKey<<3) ^ iKey ^ zKey[i];
}
iKey = iKey % TH_HASHSIZE;
|
| ︙ | ︙ | |||
2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 |
int i = 0;
int iOut = 0;
int base = 10;
int (*isdigit)(char) = th_isdigit;
if( n<0 ){
n = th_strlen(z);
}
if( n>1 && (z[0]=='-' || z[0]=='+') ){
i = 1;
}
if( (n-i)>2 && z[i]=='0' ){
if( z[i+1]=='x' || z[i+1]=='X' ){
| > > | 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 |
int i = 0;
int iOut = 0;
int base = 10;
int (*isdigit)(char) = th_isdigit;
if( n<0 ){
n = th_strlen(z);
}else{
n = TH1_LEN(n);
}
if( n>1 && (z[0]=='-' || z[0]=='+') ){
i = 1;
}
if( (n-i)>2 && z[i]=='0' ){
if( z[i+1]=='x' || z[i+1]=='X' ){
|
| ︙ | ︙ | |||
2854 2855 2856 2857 2858 2859 2860 |
int Th_ToDouble(
Th_Interp *interp,
const char *z,
int n,
double *pfOut
){
if( !sqlite3IsNumber((const char *)z, 0) ){
| | > > > | 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 |
int Th_ToDouble(
Th_Interp *interp,
const char *z,
int n,
double *pfOut
){
if( !sqlite3IsNumber((const char *)z, 0) ){
Th_ErrorMessage(interp, "expected number, got: \"", z, TH1_LEN(n));
return TH_ERROR;
}
sqlite3AtoF((const char *)z, pfOut);
return TH_OK;
}
/*
** Set the result of the interpreter to the th1 representation of
** the integer iVal and return TH_OK.
*/
int Th_SetResultInt(Th_Interp *interp, int iVal){
int isNegative = 0;
unsigned int uVal = iVal;
char zBuf[32];
char *z = &zBuf[32];
if( iVal<0 ){
if( iVal==0x80000000 ){
return Th_SetResult(interp, "-2147483648", -1);
}
isNegative = 1;
uVal = iVal * -1;
}
*(--z) = '\0';
*(--z) = (char)(48+(uVal%10));
while( (uVal = (uVal/10))>0 ){
*(--z) = (char)(48+(uVal%10));
|
| ︙ | ︙ |
Changes to src/th.h.
|
| < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/* This header file defines the external interface to the custom Scripting
** Language (TH) interpreter. TH is very similar to Tcl but is not an
** exact clone.
**
** TH1 was original developed to run SQLite tests on SymbianOS. This version
** of TH1 was repurposed as a scripted language for Fossil, and was heavily
** modified for that purpose, beginning in early 2008.
**
** More recently, TH1 has been enhanced to distinguish between regular text
** and "tainted" text. "Tainted" text is text that might have originated
** from an outside source and hence might not be trustworthy. To prevent
** cross-site scripting (XSS) and SQL-injections and similar attacks,
** tainted text should not be used for the following purposes:
**
** * executed as TH1 script or expression.
** * output as HTML or Javascript
** * used as part of an SQL query
**
** Tainted text can be converted into a safe form using commands like
** "htmlize". And some commands ("query" and "expr") know how to use
** potentially tainted variable values directly, and thus can bypass
** the restrictions above.
**
** Whether a string is clean or tainted is determined by its length integer.
** TH1 limits strings to be no more than 0x0fffffff bytes bytes in length
** (about 268MB - more than sufficient for the purposes of Fossil). The top
** bit of the length integer is the sign bit, of course. The next three bits
** are reserved. One of those, the 0x10000000 bit, marks tainted strings.
*/
#define TH1_MX_STRLEN 0x0fffffff /* Maximum length of a TH1-C string */
#define TH1_TAINT_BIT 0x10000000 /* The taint bit */
#define TH1_SIGN 0x80000000
/* Convert an integer into a string length. Negative values remain negative */
#define TH1_LEN(X) ((TH1_SIGN|TH1_MX_STRLEN)&(X))
/* Return true if the string is tainted */
#define TH1_TAINTED(X) (((X)&TH1_TAINT_BIT)!=0)
/* Remove taint from a string */
#define TH1_RM_TAINT(X) ((X)&~TH1_TAINT_BIT)
/* Add taint to a string */
#define TH1_ADD_TAINT(X) ((X)|TH1_TAINT_BIT)
/* If B is tainted, make A tainted too */
#define TH1_XFER_TAINT(A,B) (A)|=(TH1_TAINT_BIT&(B))
/* Check to see if a string is too big for TH1 */
#define TH1_SIZECHECK(N) if((N)>TH1_MX_STRLEN){Th_OversizeString();}
void Th_OversizeString(void);
/*
** Before creating an interpreter, the application must allocate and
** populate an instance of the following structure. It must remain valid
** for the lifetime of the interpreter.
*/
typedef struct Th_Vtab Th_Vtab;
|
| ︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /* ** Create and delete interpreters. */ Th_Interp * Th_CreateInterp(Th_Vtab *); void Th_DeleteInterp(Th_Interp *); /* ** Evaluate an TH program in the stack frame identified by parameter ** iFrame, according to the following rules: ** ** * If iFrame is 0, this means the current frame. ** ** * If iFrame is negative, then the nth frame up the stack, where n is | > > > > > > | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | /* ** Create and delete interpreters. */ Th_Interp * Th_CreateInterp(Th_Vtab *); void Th_DeleteInterp(Th_Interp *); /* ** Report taint in the string zStr,nStr. That string represents "zTitle" ** If non-zero is returned error out of the caller. */ int Th_ReportTaint(Th_Interp*,const char*,const char*zStr,int nStr); /* ** Evaluate an TH program in the stack frame identified by parameter ** iFrame, according to the following rules: ** ** * If iFrame is 0, this means the current frame. ** ** * If iFrame is negative, then the nth frame up the stack, where n is |
| ︙ | ︙ | |||
54 55 56 57 58 59 60 | int Th_ExistsVar(Th_Interp *, const char *, int); int Th_ExistsArrayVar(Th_Interp *, const char *, int); int Th_GetVar(Th_Interp *, const char *, int); int Th_SetVar(Th_Interp *, const char *, int, const char *, int); int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); int Th_UnsetVar(Th_Interp *, const char *, int); | < < < < < < < < < < < < < | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | int Th_ExistsVar(Th_Interp *, const char *, int); int Th_ExistsArrayVar(Th_Interp *, const char *, int); int Th_GetVar(Th_Interp *, const char *, int); int Th_SetVar(Th_Interp *, const char *, int, const char *, int); int Th_LinkVar(Th_Interp *, const char *, int, int, const char *, int); int Th_UnsetVar(Th_Interp *, const char *, int); typedef int (*Th_CommandProc)(Th_Interp *, void *, int, const char **, int *); /* ** Register new commands. */ int Th_CreateCommand( Th_Interp *interp, |
| ︙ | ︙ |
Changes to src/th_lang.c.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
return Th_WrongNumArgs(interp, "catch script ?varname?");
}
rc = Th_Eval(interp, 0, argv[1], -1);
if( argc==3 ){
int nResult;
const char *zResult = Th_GetResult(interp, &nResult);
| | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
return Th_WrongNumArgs(interp, "catch script ?varname?");
}
rc = Th_Eval(interp, 0, argv[1], -1);
if( argc==3 ){
int nResult;
const char *zResult = Th_GetResult(interp, &nResult);
Th_SetVar(interp, argv[2], TH1_LEN(argl[2]), zResult, nResult);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
|
| ︙ | ︙ | |||
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
char **azVar = 0;
int *anVar;
int nVar;
char **azValue = 0;
int *anValue;
int nValue;
int ii, jj;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "foreach varlist list script");
}
rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar);
if( rc ) return rc;
rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue);
for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){
for(jj=0; jj<nVar; jj++){
| > > > > | | 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 |
char **azVar = 0;
int *anVar;
int nVar;
char **azValue = 0;
int *anValue;
int nValue;
int ii, jj;
int bTaint = 0;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "foreach varlist list script");
}
rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar);
if( rc ) return rc;
TH1_XFER_TAINT(bTaint, argl[2]);
rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue);
for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){
for(jj=0; jj<nVar; jj++){
int x = anValue[ii+jj];
TH1_XFER_TAINT(x, bTaint);
Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], x);
}
rc = eval_loopbody(interp, argv[3], argl[3]);
}
if( rc==TH_BREAK ) rc = TH_OK;
Th_Free(interp, azVar);
Th_Free(interp, azValue);
return rc;
|
| ︙ | ︙ | |||
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
int argc,
const char **argv,
int *argl
){
char *zList = 0;
int nList = 0;
int i;
for(i=1; i<argc; i++){
Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
}
Th_SetResult(interp, zList, nList);
Th_Free(interp, zList);
return TH_OK;
}
/*
| > > > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
int argc,
const char **argv,
int *argl
){
char *zList = 0;
int nList = 0;
int i;
int bTaint = 0;
for(i=1; i<argc; i++){
TH1_XFER_TAINT(bTaint,argl[i]);
Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
}
TH1_XFER_TAINT(nList, bTaint);
Th_SetResult(interp, zList, nList);
Th_Free(interp, zList);
return TH_OK;
}
/*
|
| ︙ | ︙ | |||
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 |
int argc,
const char **argv,
int *argl
){
char *zList = 0;
int nList = 0;
int i, rc;
if( argc<2 ){
return Th_WrongNumArgs(interp, "lappend var ...");
}
rc = Th_GetVar(interp, argv[1], argl[1]);
if( rc==TH_OK ){
zList = Th_TakeResult(interp, &nList);
}
for(i=2; i<argc; i++){
Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
}
Th_SetVar(interp, argv[1], argl[1], zList, nList);
Th_SetResult(interp, zList, nList);
Th_Free(interp, zList);
return TH_OK;
}
| > > > > | 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 |
int argc,
const char **argv,
int *argl
){
char *zList = 0;
int nList = 0;
int i, rc;
int bTaint = 0;
if( argc<2 ){
return Th_WrongNumArgs(interp, "lappend var ...");
}
rc = Th_GetVar(interp, argv[1], argl[1]);
if( rc==TH_OK ){
zList = Th_TakeResult(interp, &nList);
}
TH1_XFER_TAINT(bTaint, nList);
for(i=2; i<argc; i++){
TH1_XFER_TAINT(bTaint, argl[i]);
Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
}
TH1_XFER_TAINT(nList, bTaint);
Th_SetVar(interp, argv[1], argl[1], zList, nList);
Th_SetResult(interp, zList, nList);
Th_Free(interp, zList);
return TH_OK;
}
|
| ︙ | ︙ | |||
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
){
int iElem;
int rc;
char **azElem;
int *anElem;
int nCount;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "lindex list index");
}
if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){
return TH_ERROR;
}
rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
if( rc==TH_OK ){
if( iElem<nCount && iElem>=0 ){
| > > > > | | 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 |
){
int iElem;
int rc;
char **azElem;
int *anElem;
int nCount;
int bTaint = 0;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "lindex list index");
}
if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){
return TH_ERROR;
}
TH1_XFER_TAINT(bTaint, argl[1]);
rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
if( rc==TH_OK ){
if( iElem<nCount && iElem>=0 ){
int sz = anElem[iElem];
TH1_XFER_TAINT(sz, bTaint);
Th_SetResult(interp, azElem[iElem], sz);
}else{
Th_SetResult(interp, 0, 0);
}
Th_Free(interp, azElem);
}
return rc;
|
| ︙ | ︙ | |||
354 355 356 357 358 359 360 361 362 |
if( argc!=3 ){
return Th_WrongNumArgs(interp, "lsearch list string");
}
rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
if( rc==TH_OK ){
Th_SetResultInt(interp, -1);
for(i=0; i<nCount; i++){
| > | | 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
if( argc!=3 ){
return Th_WrongNumArgs(interp, "lsearch list string");
}
rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
if( rc==TH_OK ){
int nn = TH1_LEN(argl[2]);
Th_SetResultInt(interp, -1);
for(i=0; i<nCount; i++){
if( TH1_LEN(anElem[i])==nn && 0==memcmp(azElem[i], argv[2], nn) ){
Th_SetResultInt(interp, i);
break;
}
}
Th_Free(interp, azElem);
}
|
| ︙ | ︙ | |||
559 560 561 562 563 564 565 |
char *zUsage = 0; /* Build up a usage message here */
int nUsage = 0; /* Number of bytes at zUsage */
if( argc!=4 ){
return Th_WrongNumArgs(interp, "proc name arglist code");
}
| | > | | > | > | | | 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 |
char *zUsage = 0; /* Build up a usage message here */
int nUsage = 0; /* Number of bytes at zUsage */
if( argc!=4 ){
return Th_WrongNumArgs(interp, "proc name arglist code");
}
if( Th_SplitList(interp, argv[2], TH1_LEN(argl[2]),
&azParam, &anParam, &nParam) ){
return TH_ERROR;
}
/* Allocate the new ProcDefn structure. */
nByte = sizeof(ProcDefn) + /* ProcDefn structure */
(sizeof(char *) + sizeof(int)) * nParam + /* azParam, anParam */
(sizeof(char *) + sizeof(int)) * nParam + /* azDefault, anDefault */
TH1_LEN(argl[3]) + /* zProgram */
TH1_LEN(argl[2]); /* Space for copies of param names and dflt values */
p = (ProcDefn *)Th_Malloc(interp, nByte);
/* If the last parameter in the parameter list is "args", then set the
** ProcDefn.hasArgs flag. The "args" parameter does not require an
** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays.
*/
if( nParam>0 ){
if( TH1_LEN(anParam[nParam-1])==4
&& 0==memcmp(azParam[nParam-1], "args", 4)
){
p->hasArgs = 1;
nParam--;
}
}
p->nParam = nParam;
p->azParam = (char **)&p[1];
p->anParam = (int *)&p->azParam[nParam];
p->azDefault = (char **)&p->anParam[nParam];
p->anDefault = (int *)&p->azDefault[nParam];
p->zProgram = (char *)&p->anDefault[nParam];
memcpy(p->zProgram, argv[3], TH1_LEN(argl[3]));
p->nProgram = TH1_LEN(argl[3]);
zSpace = &p->zProgram[p->nProgram];
for(i=0; i<nParam; i++){
char **az;
int *an;
int n;
if( Th_SplitList(interp, azParam[i], anParam[i], &az, &an, &n) ){
|
| ︙ | ︙ | |||
670 671 672 673 674 675 676 |
int argc,
const char **argv,
int *argl
){
if( argc!=3 ){
return Th_WrongNumArgs(interp, "rename oldcmd newcmd");
}
| | > | 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 |
int argc,
const char **argv,
int *argl
){
if( argc!=3 ){
return Th_WrongNumArgs(interp, "rename oldcmd newcmd");
}
return Th_RenameCommand(interp, argv[1], TH1_LEN(argl[1]),
argv[2], TH1_LEN(argl[2]));
}
/*
** TH Syntax:
**
** break ?value...?
** continue ?value...?
|
| ︙ | ︙ | |||
744 745 746 747 748 749 750 |
int iRes = 0;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string compare str1 str2");
}
zLeft = argv[2];
| | | | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 |
int iRes = 0;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string compare str1 str2");
}
zLeft = argv[2];
nLeft = TH1_LEN(argl[2]);
zRight = argv[3];
nRight = TH1_LEN(argl[3]);
for(i=0; iRes==0 && i<nLeft && i<nRight; i++){
iRes = zLeft[i]-zRight[i];
}
if( iRes==0 ){
iRes = nLeft-nRight;
}
|
| ︙ | ︙ | |||
777 778 779 780 781 782 783 |
int nHaystack;
int iRes = -1;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string first needle haystack");
}
| | | | 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 |
int nHaystack;
int iRes = -1;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string first needle haystack");
}
nNeedle = TH1_LEN(argl[2]);
nHaystack = TH1_LEN(argl[3]);
if( nNeedle && nHaystack && nNeedle<=nHaystack ){
const char *zNeedle = argv[2];
const char *zHaystack = argv[3];
int i;
for(i=0; i<=(nHaystack-nNeedle); i++){
|
| ︙ | ︙ | |||
810 811 812 813 814 815 816 |
){
int iIndex;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string index string index");
}
| | | | > > | | | | | | > > | > | | | 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 |
){
int iIndex;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string index string index");
}
if( TH1_LEN(argl[3])==3 && 0==memcmp("end", argv[3], 3) ){
iIndex = TH1_LEN(argl[2])-1;
}else if( Th_ToInt(interp, argv[3], argl[3], &iIndex) ){
Th_ErrorMessage(
interp, "Expected \"end\" or integer, got:", argv[3], argl[3]);
return TH_ERROR;
}
if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){
int sz = 1;
TH1_XFER_TAINT(sz, argl[2]);
return Th_SetResult(interp, &argv[2][iIndex], sz);
}else{
return Th_SetResult(interp, 0, 0);
}
}
/*
** TH Syntax:
**
** string is CLASS STRING
*/
static int string_is_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string is class string");
}
if( TH1_LEN(argl[2])==5 && 0==memcmp(argv[2], "alnum", 5) ){
int i;
int iRes = 1;
for(i=0; i<TH1_LEN(argl[3]); i++){
if( !th_isalnum(argv[3][i]) ){
iRes = 0;
}
}
return Th_SetResultInt(interp, iRes);
}else if( TH1_LEN(argl[2])==6 && 0==memcmp(argv[2], "double", 6) ){
double fVal;
if( Th_ToDouble(interp, argv[3], argl[3], &fVal)==TH_OK ){
return Th_SetResultInt(interp, 1);
}
return Th_SetResultInt(interp, 0);
}else if( TH1_LEN(argl[2])==7 && 0==memcmp(argv[2], "integer", 7) ){
int iVal;
if( Th_ToInt(interp, argv[3], argl[3], &iVal)==TH_OK ){
return Th_SetResultInt(interp, 1);
}
return Th_SetResultInt(interp, 0);
}else if( TH1_LEN(argl[2])==4 && 0==memcmp(argv[2], "list", 4) ){
if( Th_SplitList(interp, argv[3], argl[3], 0, 0, 0)==TH_OK ){
return Th_SetResultInt(interp, 1);
}
return Th_SetResultInt(interp, 0);
}else if( TH1_LEN(argl[2])==7 && 0==memcmp(argv[2], "tainted", 7) ){
return Th_SetResultInt(interp, TH1_TAINTED(argl[3]));
}else{
Th_ErrorMessage(interp,
"Expected alnum, double, integer, list, or tainted, got:",
argv[2], TH1_LEN(argl[2]));
return TH_ERROR;
}
}
/*
** TH Syntax:
**
** string last NEEDLE HAYSTACK
*/
static int string_last_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
int nNeedle;
int nHaystack;
int iRes = -1;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string last needle haystack");
}
nNeedle = TH1_LEN(argl[2]);
nHaystack = TH1_LEN(argl[3]);
if( nNeedle && nHaystack && nNeedle<=nHaystack ){
const char *zNeedle = argv[2];
const char *zHaystack = argv[3];
int i;
for(i=nHaystack-nNeedle; i>=0; i--){
|
| ︙ | ︙ | |||
917 918 919 920 921 922 923 |
*/
static int string_length_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
if( argc!=3 ){
return Th_WrongNumArgs(interp, "string length string");
}
| | | | > | | | | > > | > | | > > > | | > > | | 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 |
*/
static int string_length_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
if( argc!=3 ){
return Th_WrongNumArgs(interp, "string length string");
}
return Th_SetResultInt(interp, TH1_LEN(argl[2]));
}
/*
** TH Syntax:
**
** string match PATTERN STRING
**
*/
static int string_match_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
extern char *fossil_strndup(const char*,int);
extern void fossil_free(void*);
char *zPat, *zStr;
int rc;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string match pattern string");
}
zPat = fossil_strndup(argv[2],TH1_LEN(argl[2]));
zStr = fossil_strndup(argv[3],TH1_LEN(argl[3]));
rc = sqlite3_strglob(zPat,zStr);
fossil_free(zPat);
fossil_free(zStr);
return Th_SetResultInt(interp, !rc);
}
/*
** TH Syntax:
**
** string range STRING FIRST LAST
*/
static int string_range_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
int iStart;
int iEnd;
int sz;
if( argc!=5 ){
return Th_WrongNumArgs(interp, "string range string first last");
}
if( TH1_LEN(argl[4])==3 && 0==memcmp("end", argv[4], 3) ){
iEnd = TH1_LEN(argl[2]);
}else if( Th_ToInt(interp, argv[4], argl[4], &iEnd) ){
Th_ErrorMessage(
interp, "Expected \"end\" or integer, got:", argv[4], TH1_LEN(argl[4]));
return TH_ERROR;
}
if( Th_ToInt(interp, argv[3], argl[3], &iStart) ){
return TH_ERROR;
}
if( iStart<0 ) iStart = 0;
if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1;
if( iStart>iEnd ) iEnd = iStart-1;
sz = iEnd - iStart + 1;
TH1_XFER_TAINT(sz, argl[2]);
return Th_SetResult(interp, &argv[2][iStart], sz);
}
/*
** TH Syntax:
**
** string repeat STRING COUNT
*/
static int string_repeat_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
int n;
int i;
int sz;
long long int nByte;
char *zByte;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string repeat string n");
}
if( Th_ToInt(interp, argv[3], argl[3], &n) ){
return TH_ERROR;
}
nByte = n;
sz = TH1_LEN(argl[2]);
nByte *= sz;
TH1_SIZECHECK(nByte+1);
zByte = Th_Malloc(interp, nByte+1);
for(i=0; i<nByte; i+=sz){
memcpy(&zByte[i], argv[2], sz);
}
n = nByte;
TH1_XFER_TAINT(n, argl[2]);
Th_SetResult(interp, zByte, n);
Th_Free(interp, zByte);
return TH_OK;
}
/*
** TH Syntax:
**
|
| ︙ | ︙ | |||
1025 1026 1027 1028 1029 1030 1031 |
int n;
const char *z;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "string trim string");
}
z = argv[2];
| | | | > | | 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 |
int n;
const char *z;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "string trim string");
}
z = argv[2];
n = TH1_LEN(argl[2]);
if( TH1_LEN(argl[1])<5 || argv[1][4]=='l' ){
while( n && th_isspace(z[0]) ){ z++; n--; }
}
if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){
while( n && th_isspace(z[n-1]) ){ n--; }
}
TH1_XFER_TAINT(n, argl[2]);
Th_SetResult(interp, z, n);
return TH_OK;
}
/*
** TH Syntax:
**
** info exists VARNAME
*/
static int info_exists_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
int rc;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "info exists var");
}
rc = Th_ExistsVar(interp, argv[2], TH1_LEN(argl[2]));
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
** TH Syntax:
**
|
| ︙ | ︙ | |||
1115 1116 1117 1118 1119 1120 1121 |
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
int rc;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "array exists var");
}
| | | | 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 |
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
int rc;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "array exists var");
}
rc = Th_ExistsArrayVar(interp, argv[2], TH1_LEN(argl[2]));
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
** TH Syntax:
**
** array names VARNAME
*/
static int array_names_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
int rc;
char *zElem = 0;
int nElem = 0;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "array names varname");
}
rc = Th_ListAppendArray(interp, argv[2], TH1_LEN(argl[2]), &zElem, &nElem);
if( rc!=TH_OK ){
return rc;
}
Th_SetResult(interp, zElem, nElem);
if( zElem ) Th_Free(interp, zElem);
return TH_OK;
}
|
| ︙ | ︙ | |||
1159 1160 1161 1162 1163 1164 1165 |
int argc,
const char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "unset var");
}
| | | > | > | > | 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 |
int argc,
const char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "unset var");
}
return Th_UnsetVar(interp, argv[1], TH1_LEN(argl[1]));
}
int Th_CallSubCommand(
Th_Interp *interp,
void *ctx,
int argc,
const char **argv,
int *argl,
const Th_SubCommand *aSub
){
if( argc>1 ){
int i;
for(i=0; aSub[i].zName; i++){
const char *zName = aSub[i].zName;
if( th_strlen(zName)==TH1_LEN(argl[1])
&& 0==memcmp(zName, argv[1], TH1_LEN(argl[1])) ){
return aSub[i].xProc(interp, ctx, argc, argv, argl);
}
}
}
if(argc<2){
Th_ErrorMessage(interp, "Expected sub-command for",
argv[0], TH1_LEN(argl[0]));
}else{
Th_ErrorMessage(interp, "Expected sub-command, got:",
argv[1], TH1_LEN(argl[1]));
}
return TH_ERROR;
}
/*
** TH Syntax:
**
|
| ︙ | ︙ | |||
1317 1318 1319 1320 1321 1322 1323 |
int *argl
){
int iFrame = -1;
if( argc!=2 && argc!=3 ){
return Th_WrongNumArgs(interp, "uplevel ?level? script...");
}
| | | 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 |
int *argl
){
int iFrame = -1;
if( argc!=2 && argc!=3 ){
return Th_WrongNumArgs(interp, "uplevel ?level? script...");
}
if( argc==3 && TH_OK!=thToFrame(interp, argv[1], TH1_LEN(argl[1]), &iFrame) ){
return TH_ERROR;
}
return Th_Eval(interp, iFrame, argv[argc-1], -1);
}
/*
** TH Syntax:
|
| ︙ | ︙ | |||
1340 1341 1342 1343 1344 1345 1346 |
int *argl
){
int iVar = 1;
int iFrame = -1;
int rc = TH_OK;
int i;
| | | > | 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 |
int *argl
){
int iVar = 1;
int iFrame = -1;
int rc = TH_OK;
int i;
if( TH_OK==thToFrame(0, argv[1], TH1_LEN(argl[1]), &iFrame) ){
iVar++;
}
if( argc==iVar || (argc-iVar)%2 ){
return Th_WrongNumArgs(interp,
"upvar frame othervar myvar ?othervar myvar...?");
}
for(i=iVar; rc==TH_OK && i<argc; i=i+2){
rc = Th_LinkVar(interp, argv[i+1], TH1_LEN(argl[i+1]),
iFrame, argv[i], TH1_LEN(argl[i]));
}
return rc;
}
/*
** TH Syntax:
**
|
| ︙ | ︙ |
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 | */ #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_MASK ((u32)0x0000001F) /* 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) #define TH_INIT_FORBID_MASK (TH_INIT_FORCE_TCL) /* Illegal from a script. */ |
| ︙ | ︙ | |||
260 261 262 263 264 265 266 |
const char **argv,
int *argl
){
char *zOut;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "httpize STRING");
}
| | | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
const char **argv,
int *argl
){
char *zOut;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "httpize STRING");
}
zOut = httpize((char*)argv[1], TH1_LEN(argl[1]));
Th_SetResult(interp, zOut, -1);
free(zOut);
return TH_OK;
}
/*
** True if output is enabled. False if disabled.
|
| ︙ | ︙ | |||
289 290 291 292 293 294 295 |
){
int rc;
if( argc<2 || argc>3 ){
return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN");
}
rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput);
if( g.thTrace ){
| | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
){
int rc;
if( argc<2 || argc>3 ){
return Th_WrongNumArgs(interp, "enable_output [LABEL] BOOLEAN");
}
rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &enableOutput);
if( g.thTrace ){
Th_Trace("enable_output {%.*s} -> %d<br>\n",
TH1_LEN(argl[1]),argv[1],enableOutput);
}
return rc;
}
/*
** Returns a name for a TH1 return code.
*/
|
| ︙ | ︙ | |||
373 374 375 376 377 378 379 | return tmp; } /* ** Send text to the appropriate output: If pOut is not NULL, it is ** appended there, else to the console or to the CGI reply buffer. ** Escape all characters with special meaning to HTML if the encode | | < | < < < | > > > > | 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 |
return tmp;
}
/*
** Send text to the appropriate output: If pOut is not NULL, it is
** appended there, else to the console or to the CGI reply buffer.
** Escape all characters with special meaning to HTML if the encode
** parameter is true.
**
** If pOut is NULL and the global pThOut is not then that blob
** is used for output.
*/
static void sendText(Blob *pOut, const char *z, int n, int encode){
if(0==pOut && pThOut!=0){
pOut = pThOut;
}
if( enableOutput && n ){
if( n<0 ){
n = strlen(z);
}else{
n = TH1_LEN(n);
}
if( encode ){
z = htmlize(z, n);
n = strlen(z);
}
if(pOut!=0){
blob_append(pOut, z, n);
}else if( g.cgiOutput ){
|
| ︙ | ︙ | |||
523 524 525 526 527 528 529 530 531 532 |
static int putsCmd(
Th_Interp *interp,
void *pConvert,
int argc,
const char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "puts STRING");
}
| > > > > > > > > | | 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 |
static int putsCmd(
Th_Interp *interp,
void *pConvert,
int argc,
const char **argv,
int *argl
){
int encode = *(unsigned int*)pConvert;
int n;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "puts STRING");
}
n = argl[1];
if( encode==0 && n>0 && TH1_TAINTED(n) ){
if( Th_ReportTaint(interp, "output string", argv[1], n) ){
return TH_ERROR;
}
}
sendText(0,(char*)argv[1], TH1_LEN(n), encode);
return TH_OK;
}
/*
** TH1 command: redirect URL ?withMethod?
**
** Issues an HTTP redirect to the specified URL and then exits the process.
|
| ︙ | ︙ | |||
555 556 557 558 559 560 561 562 563 564 565 566 567 568 |
if( argc!=2 && argc!=3 ){
return Th_WrongNumArgs(interp, "redirect URL ?withMethod?");
}
if( argc==3 ){
if( Th_ToInt(interp, argv[2], argl[2], &withMethod) ){
return TH_ERROR;
}
}
if( withMethod ){
cgi_redirect_with_method(argv[1]);
}else{
cgi_redirect(argv[1]);
}
Th_SetResult(interp, argv[1], argl[1]); /* NOT REACHED */
| > > > > > | 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
if( argc!=2 && argc!=3 ){
return Th_WrongNumArgs(interp, "redirect URL ?withMethod?");
}
if( argc==3 ){
if( Th_ToInt(interp, argv[2], argl[2], &withMethod) ){
return TH_ERROR;
}
}
if( TH1_TAINTED(argl[1])
&& Th_ReportTaint(interp,"redirect URL",argv[1],argl[1])
){
return TH_ERROR;
}
if( withMethod ){
cgi_redirect_with_method(argv[1]);
}else{
cgi_redirect(argv[1]);
}
Th_SetResult(interp, argv[1], argl[1]); /* NOT REACHED */
|
| ︙ | ︙ | |||
658 659 660 661 662 663 664 |
Blob src, title, body;
char *zValue = 0;
int nValue = 0;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "markdown STRING");
}
blob_zero(&src);
| | | 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 |
Blob src, title, body;
char *zValue = 0;
int nValue = 0;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "markdown STRING");
}
blob_zero(&src);
blob_init(&src, (char*)argv[1], TH1_LEN(argl[1]));
blob_zero(&title); blob_zero(&body);
markdown_to_html(&src, &title, &body);
Th_ListAppend(interp, &zValue, &nValue, blob_str(&title), blob_size(&title));
Th_ListAppend(interp, &zValue, &nValue, blob_str(&body), blob_size(&body));
Th_SetResult(interp, zValue, nValue);
Th_Free(interp, zValue);
return TH_OK;
|
| ︙ | ︙ | |||
688 689 690 691 692 693 694 |
){
int flags = WIKI_INLINE | WIKI_NOBADLINKS | *(unsigned int*)p;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "wiki STRING");
}
if( enableOutput ){
Blob src;
| | > > > > > > > > > > > > > > > > > > > > > | | 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 |
){
int flags = WIKI_INLINE | WIKI_NOBADLINKS | *(unsigned int*)p;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "wiki STRING");
}
if( enableOutput ){
Blob src;
blob_init(&src, (char*)argv[1], TH1_LEN(argl[1]));
wiki_convert(&src, 0, flags);
blob_reset(&src);
}
return TH_OK;
}
/*
** TH1 command: wiki_assoc STRING STRING
**
** Render an associated wiki page. The first string is the namespace
** (e.g. "checkin", "branch", "ticket"). The second is the ID of the
** associated object. See wiki_render_associated().
*/
static int wikiAssocCmd(
Th_Interp *interp,
void *p,
int argc,
const char **argv,
int *argl
){
if( argc!=3 ){
return Th_WrongNumArgs(interp, "wiki_assoc STRING STRING");
}
wiki_render_associated((char*)argv[1], (char*)argv[2], WIKIASSOC_FULL_TITLE);
return TH_OK;
}
/*
** TH1 command: htmlize STRING
**
** Escape all characters of STRING which have special meaning in HTML.
** Return a new string result.
*/
static int htmlizeCmd(
Th_Interp *interp,
void *p,
int argc,
const char **argv,
int *argl
){
char *zOut;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "htmlize STRING");
}
zOut = htmlize((char*)argv[1], TH1_LEN(argl[1]));
Th_SetResult(interp, zOut, -1);
free(zOut);
return TH_OK;
}
/*
** TH1 command: encode64 STRING
|
| ︙ | ︙ | |||
734 735 736 737 738 739 740 |
const char **argv,
int *argl
){
char *zOut;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "encode64 STRING");
}
| | | | 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 |
const char **argv,
int *argl
){
char *zOut;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "encode64 STRING");
}
zOut = encode64((char*)argv[1], TH1_LEN(argl[1]));
Th_SetResult(interp, zOut, -1);
free(zOut);
return TH_OK;
}
/*
** TH1 command: date
**
** Return a string which is the current time and date. If the
** -local option is used, the date appears using localtime instead
** of UTC.
*/
static int dateCmd(
Th_Interp *interp,
void *p,
int argc,
const char **argv,
int *argl
){
char *zOut;
if( argc>=2 && TH1_LEN(argl[1])==6 && memcmp(argv[1],"-local",6)==0 ){
zOut = db_text("??", "SELECT datetime('now',toLocal())");
}else{
zOut = db_text("??", "SELECT datetime('now')");
}
Th_SetResult(interp, zOut, -1);
free(zOut);
return TH_OK;
|
| ︙ | ︙ | |||
787 788 789 790 791 792 793 |
char *zCapList = 0;
int nCapList = 0;
if( argc<2 ){
return Th_WrongNumArgs(interp, "hascap STRING ...");
}
for(i=1; rc==1 && i<argc; i++){
if( g.thTrace ){
| | | | 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 |
char *zCapList = 0;
int nCapList = 0;
if( argc<2 ){
return Th_WrongNumArgs(interp, "hascap STRING ...");
}
for(i=1; rc==1 && i<argc; i++){
if( g.thTrace ){
Th_ListAppend(interp, &zCapList, &nCapList, argv[i], TH1_LEN(argl[i]));
}
rc = login_has_capability((char*)argv[i],TH1_LEN(argl[i]),*(int*)p);
}
if( g.thTrace ){
Th_Trace("[%s %#h] => %d<br>\n", argv[0], nCapList, zCapList, rc);
Th_Free(interp, zCapList);
}
Th_SetResultInt(interp, rc);
return TH_OK;
|
| ︙ | ︙ | |||
835 836 837 838 839 840 841 |
int nCap;
int rc;
int i;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "capexpr EXPR");
}
| | | 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 |
int nCap;
int rc;
int i;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "capexpr EXPR");
}
rc = Th_SplitList(interp, argv[1], TH1_LEN(argl[1]), &azCap, &anCap, &nCap);
if( rc ) return rc;
rc = 0;
for(i=0; i<nCap; i++){
if( azCap[i][0]=='!' ){
rc = !login_has_capability(azCap[i]+1, anCap[i]-1, 0);
}else if( azCap[i][0]=='@' ){
rc = login_has_capability(azCap[i]+1, anCap[i]-1, LOGIN_ANON);
|
| ︙ | ︙ | |||
898 899 900 901 902 903 904 |
int rc = 1, i, j;
unsigned int searchCap = search_restrict(SRCH_ALL);
if( argc<2 ){
return Th_WrongNumArgs(interp, "hascap STRING ...");
}
for(i=1; i<argc && rc; i++){
int match = 0;
| > | | | 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 |
int rc = 1, i, j;
unsigned int searchCap = search_restrict(SRCH_ALL);
if( argc<2 ){
return Th_WrongNumArgs(interp, "hascap STRING ...");
}
for(i=1; i<argc && rc; i++){
int match = 0;
int nn = TH1_LEN(argl[i]);
for(j=0; j<nn; j++){
switch( argv[i][j] ){
case 'c': match |= searchCap & SRCH_CKIN; break;
case 'd': match |= searchCap & SRCH_DOC; break;
case 't': match |= searchCap & SRCH_TKT; break;
case 'w': match |= searchCap & SRCH_WIKI; break;
}
}
if( !match ) rc = 0;
}
if( g.thTrace ){
Th_Trace("[searchable %#h] => %d<br>\n", TH1_LEN(argl[1]), argv[1], rc);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
** TH1 command: hasfeature STRING
|
| ︙ | ︙ | |||
1028 1029 1030 1031 1032 1033 1034 |
rc = 1;
}
#endif
else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){
rc = 1;
}
if( g.thTrace ){
| | | 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 |
rc = 1;
}
#endif
else if( 0 == fossil_strnicmp( zArg, "markdown\0", 9 ) ){
rc = 1;
}
if( g.thTrace ){
Th_Trace("[hasfeature %#h] => %d<br>\n", TH1_LEN(argl[1]), zArg, rc);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
|
| ︙ | ︙ | |||
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 |
void *p,
int argc,
const char **argv,
int *argl
){
int rc = 0;
int i;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "anycap STRING");
}
| > > | | | 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 |
void *p,
int argc,
const char **argv,
int *argl
){
int rc = 0;
int i;
int nn;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "anycap STRING");
}
nn = TH1_LEN(argl[1]);
for(i=0; rc==0 && i<nn; i++){
rc = login_has_capability((char*)&argv[1][i],1,0);
}
if( g.thTrace ){
Th_Trace("[anycap %#h] => %d<br>\n", TH1_LEN(argl[1]), argv[1], rc);
}
Th_SetResultInt(interp, rc);
return TH_OK;
}
/*
** TH1 command: combobox NAME TEXT-LIST NUMLINES
|
| ︙ | ︙ | |||
1117 1118 1119 1120 1121 1122 1123 |
){
if( argc!=4 ){
return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES");
}
if( enableOutput ){
int height;
Blob name;
| | | | > | 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 |
){
if( argc!=4 ){
return Th_WrongNumArgs(interp, "combobox NAME TEXT-LIST NUMLINES");
}
if( enableOutput ){
int height;
Blob name;
int nValue = 0;
const char *zValue;
char *z, *zH;
int nElem;
int *aszElem;
char **azElem;
int i;
if( Th_ToInt(interp, argv[3], argl[3], &height) ) return TH_ERROR;
Th_SplitList(interp, argv[2], TH1_LEN(argl[2]), &azElem, &aszElem, &nElem);
blob_init(&name, (char*)argv[1], TH1_LEN(argl[1]));
zValue = Th_Fetch(blob_str(&name), &nValue);
nValue = TH1_LEN(nValue);
zH = htmlize(blob_buffer(&name), blob_size(&name));
z = mprintf("<select id=\"%s\" name=\"%s\" size=\"%d\">", zH, zH, height);
free(zH);
sendText(0,z, -1, 0);
free(z);
blob_reset(&name);
for(i=0; i<nElem; i++){
|
| ︙ | ︙ | |||
1224 1225 1226 1227 1228 1229 1230 |
int iMin, iMax;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "linecount STRING MAX MIN");
}
if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR;
if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR;
z = argv[1];
| | | 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 |
int iMin, iMax;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "linecount STRING MAX MIN");
}
if( Th_ToInt(interp, argv[2], argl[2], &iMax) ) return TH_ERROR;
if( Th_ToInt(interp, argv[3], argl[3], &iMin) ) return TH_ERROR;
z = argv[1];
size = TH1_LEN(argl[1]);
for(n=1, i=0; i<size; i++){
if( z[i]=='\n' ){
n++;
if( n>=iMax ) break;
}
}
if( n<iMin ) n = iMin;
|
| ︙ | ︙ | |||
1384 1385 1386 1387 1388 1389 1390 |
}else if( fossil_strnicmp(argv[1], "user\0", 5)==0 ){
Th_SetResult(interp, g.zLogin ? g.zLogin : zDefault, -1);
return TH_OK;
}else if( fossil_strnicmp(argv[1], "vfs\0", 4)==0 ){
Th_SetResult(interp, g.zVfsName ? g.zVfsName : zDefault, -1);
return TH_OK;
}else{
| | > > > | > > | 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 |
}else if( fossil_strnicmp(argv[1], "user\0", 5)==0 ){
Th_SetResult(interp, g.zLogin ? g.zLogin : zDefault, -1);
return TH_OK;
}else if( fossil_strnicmp(argv[1], "vfs\0", 4)==0 ){
Th_SetResult(interp, g.zVfsName ? g.zVfsName : zDefault, -1);
return TH_OK;
}else{
Th_ErrorMessage(interp, "unsupported global state:",
argv[1], TH1_LEN(argl[1]));
return TH_ERROR;
}
}
/*
** TH1 command: getParameter NAME ?DEFAULT?
**
** Return the value of the specified query parameter or the specified default
** value when there is no matching query parameter.
*/
static int getParameterCmd(
Th_Interp *interp,
void *p,
int argc,
const char **argv,
int *argl
){
const char *zDefault = 0;
const char *zVal;
int sz;
if( argc!=2 && argc!=3 ){
return Th_WrongNumArgs(interp, "getParameter NAME ?DEFAULT?");
}
if( argc==3 ){
zDefault = argv[2];
}
zVal = cgi_parameter(argv[1], zDefault);
sz = th_strlen(zVal);
Th_SetResult(interp, zVal, TH1_ADD_TAINT(sz));
return TH_OK;
}
/*
** TH1 command: setParameter NAME VALUE
**
** Sets the value of the specified query parameter.
|
| ︙ | ︙ | |||
1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 | char zUTime[50]; fossil_cpu_times(0, &x); sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x); Th_SetResult(interp, zUTime, -1); return TH_OK; } /* ** TH1 command: randhex N ** ** Return N*2 random hexadecimal digits with N<50. If N is omitted, ** use a value of 10. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
char zUTime[50];
fossil_cpu_times(0, &x);
sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
Th_SetResult(interp, zUTime, -1);
return TH_OK;
}
/*
** TH1 command: taint STRING
**
** Return a copy of STRING that is marked as tainted.
*/
static int taintCmd(
Th_Interp *interp,
void *p,
int argc,
const char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "STRING");
}
Th_SetResult(interp, argv[1], TH1_ADD_TAINT(argl[1]));
return TH_OK;
}
/*
** TH1 command: untaint STRING
**
** Return a copy of STRING that is marked as untainted.
*/
static int untaintCmd(
Th_Interp *interp,
void *p,
int argc,
const char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "STRING");
}
Th_SetResult(interp, argv[1], TH1_LEN(argl[1]));
return TH_OK;
}
/*
** TH1 command: randhex N
**
** Return N*2 random hexadecimal digits with N<50. If N is omitted,
** use a value of 10.
*/
|
| ︙ | ︙ | |||
1900 1901 1902 1903 1904 1905 1906 | const char *zTail; int n, i; int res = TH_OK; int nVar; char *zErr = 0; int noComplain = 0; | > | > > > > > > > > | | | | | | | 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 |
const char *zTail;
int n, i;
int res = TH_OK;
int nVar;
char *zErr = 0;
int noComplain = 0;
if( argc>3 && TH1_LEN(argl[1])==11
&& strncmp(argv[1], "-nocomplain", 11)==0
){
argc--;
argv++;
argl++;
noComplain = 1;
}
if( argc!=3 ){
return Th_WrongNumArgs(interp, "query SQL CODE");
}
if( g.db==0 ){
if( noComplain ) return TH_OK;
Th_ErrorMessage(interp, "database is not open", 0, 0);
return TH_ERROR;
}
zSql = argv[1];
nSql = argl[1];
if( TH1_TAINTED(nSql) ){
if( Th_ReportTaint(interp,"query SQL",zSql,nSql) ){
return TH_ERROR;
}
nSql = TH1_LEN(nSql);
}
while( res==TH_OK && nSql>0 ){
zErr = 0;
report_restrict_sql(&zErr);
g.dbIgnoreErrors++;
rc = sqlite3_prepare_v2(g.db, argv[1], TH1_LEN(argl[1]), &pStmt, &zTail);
g.dbIgnoreErrors--;
report_unrestrict_sql();
if( rc!=0 || zErr!=0 ){
if( noComplain ) return TH_OK;
Th_ErrorMessage(interp, "SQL error: ",
zErr ? zErr : sqlite3_errmsg(g.db), -1);
return TH_ERROR;
}
n = (int)(zTail - zSql);
zSql += n;
nSql -= n;
if( pStmt==0 ) continue;
nVar = sqlite3_bind_parameter_count(pStmt);
for(i=1; i<=nVar; i++){
const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
int szVar = zVar ? th_strlen(zVar) : 0;
if( szVar>1 && zVar[0]=='$'
&& Th_GetVar(interp, zVar+1, szVar-1)==TH_OK ){
int nVal;
const char *zVal = Th_GetResult(interp, &nVal);
sqlite3_bind_text(pStmt, i, zVal, TH1_LEN(nVal), SQLITE_TRANSIENT);
}
}
while( res==TH_OK && ignore_errors_step(pStmt)==SQLITE_ROW ){
int nCol = sqlite3_column_count(pStmt);
for(i=0; i<nCol; i++){
const char *zCol = sqlite3_column_name(pStmt, i);
int szCol = th_strlen(zCol);
const char *zVal = (const char*)sqlite3_column_text(pStmt, i);
int szVal = sqlite3_column_bytes(pStmt, i);
Th_SetVar(interp, zCol, szCol, zVal, TH1_ADD_TAINT(szVal));
}
if( g.thTrace ){
Th_Trace("query_eval {<pre>%#h</pre>}<br>\n",TH1_LEN(argl[2]),argv[2]);
}
res = Th_Eval(interp, 0, argv[2], TH1_LEN(argl[2]));
if( g.thTrace ){
int nTrRes;
char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes);
Th_Trace("[query_eval] => %h {%#h}<br>\n",
Th_ReturnCodeName(res, 0), TH1_LEN(nTrRes), zTrRes);
}
if( res==TH_BREAK || res==TH_CONTINUE ) res = TH_OK;
}
rc = sqlite3_finalize(pStmt);
if( rc!=SQLITE_OK ){
if( noComplain ) return TH_OK;
Th_ErrorMessage(interp, "SQL error: ", sqlite3_errmsg(g.db), -1);
|
| ︙ | ︙ | |||
2015 2016 2017 2018 2019 2020 2021 |
rc = TH_ERROR;
}else{
Th_SetResult(interp, 0, 0);
rc = TH_OK;
}
if( g.thTrace ){
Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "",
| | | 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 |
rc = TH_ERROR;
}else{
Th_SetResult(interp, 0, 0);
rc = TH_OK;
}
if( g.thTrace ){
Th_Trace("[setting %s%#h] => %d<br>\n", strict ? "strict " : "",
TH1_LEN(argl[nArg]), argv[nArg], rc);
}
return rc;
}
/*
** TH1 command: glob_match ?-one? ?--? patternList string
**
|
| ︙ | ︙ | |||
2098 2099 2100 2101 2102 2103 2104 |
if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
if( nArg+2!=argc ){
return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
}
zErr = re_compile(&pRe, argv[nArg], noCase);
if( !zErr ){
Th_SetResultInt(interp, re_match(pRe,
| | | 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 |
if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
if( nArg+2!=argc ){
return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
}
zErr = re_compile(&pRe, argv[nArg], noCase);
if( !zErr ){
Th_SetResultInt(interp, re_match(pRe,
(const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1])));
rc = TH_OK;
}else{
Th_SetResult(interp, zErr, -1);
rc = TH_ERROR;
}
re_free(pRe);
return rc;
|
| ︙ | ︙ | |||
2137 2138 2139 2140 2141 2142 2143 |
Blob payload;
ReCompiled *pRe = 0;
UrlData urlData;
if( argc<2 || argc>5 ){
return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS);
}
| | | 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 |
Blob payload;
ReCompiled *pRe = 0;
UrlData urlData;
if( argc<2 || argc>5 ){
return Th_WrongNumArgs(interp, HTTP_WRONGNUMARGS);
}
if( fossil_strnicmp(argv[nArg], "-asynchronous", TH1_LEN(argl[nArg]))==0 ){
fAsynchronous = 1; nArg++;
}
if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
if( nArg+1!=argc && nArg+2!=argc ){
return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
}
memset(&urlData, '\0', sizeof(urlData));
|
| ︙ | ︙ | |||
2166 2167 2168 2169 2170 2171 2172 |
Th_SetResult(interp, "url not allowed", -1);
re_free(pRe);
return TH_ERROR;
}
re_free(pRe);
blob_zero(&payload);
if( nArg+2==argc ){
| | | 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 |
Th_SetResult(interp, "url not allowed", -1);
re_free(pRe);
return TH_ERROR;
}
re_free(pRe);
blob_zero(&payload);
if( nArg+2==argc ){
blob_append(&payload, argv[nArg+1], TH1_LEN(argl[nArg+1]));
zType = "POST";
}else{
zType = "GET";
}
if( fAsynchronous ){
const char *zSep, *zParams;
Blob hdr;
|
| ︙ | ︙ | |||
2245 2246 2247 2248 2249 2250 2251 |
const char * zStr;
int nStr, rc;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "captureTh1 STRING");
}
pOrig = Th_SetOutputBlob(&out);
zStr = argv[1];
| | | 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 |
const char * zStr;
int nStr, rc;
if( argc!=2 ){
return Th_WrongNumArgs(interp, "captureTh1 STRING");
}
pOrig = Th_SetOutputBlob(&out);
zStr = argv[1];
nStr = TH1_LEN(argl[1]);
rc = Th_Eval(g.interp, 0, zStr, nStr);
Th_SetOutputBlob(pOrig);
if(0==rc){
Th_SetResult(g.interp, blob_str(&out), blob_size(&out));
}
blob_reset(&out);
return rc;
|
| ︙ | ︙ | |||
2333 2334 2335 2336 2337 2338 2339 |
{"checkout", checkoutCmd, 0},
{"combobox", comboboxCmd, 0},
{"copybtn", copybtnCmd, 0},
{"date", dateCmd, 0},
{"decorate", wikiCmd, (void*)&aFlags[2]},
{"defHeader", defHeaderCmd, 0},
{"dir", dirCmd, 0},
| < | 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 |
{"checkout", checkoutCmd, 0},
{"combobox", comboboxCmd, 0},
{"copybtn", copybtnCmd, 0},
{"date", dateCmd, 0},
{"decorate", wikiCmd, (void*)&aFlags[2]},
{"defHeader", defHeaderCmd, 0},
{"dir", dirCmd, 0},
{"enable_output", enableOutputCmd, 0},
{"encode64", encode64Cmd, 0},
{"getParameter", getParameterCmd, 0},
{"glob_match", globMatchCmd, 0},
{"globalState", globalStateCmd, 0},
{"httpize", httpizeCmd, 0},
{"hascap", hascapCmd, (void*)&zeroInt},
|
| ︙ | ︙ | |||
2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 |
{"searchable", searchableCmd, 0},
{"setParameter", setParameterCmd, 0},
{"setting", settingCmd, 0},
{"styleFooter", styleFooterCmd, 0},
{"styleHeader", styleHeaderCmd, 0},
{"styleScript", styleScriptCmd, 0},
{"submenu", submenuCmd, 0},
{"tclReady", tclReadyCmd, 0},
{"trace", traceCmd, 0},
{"stime", stimeCmd, 0},
{"unversioned", unversionedCmd, 0},
{"utime", utimeCmd, 0},
{"verifyCsrf", verifyCsrfCmd, 0},
{"verifyLogin", verifyLoginCmd, 0},
{"wiki", wikiCmd, (void*)&aFlags[0]},
{0, 0, 0}
};
if( g.thTrace ){
Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags);
}
if( needConfig ){
/*
| > > > | 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 |
{"searchable", searchableCmd, 0},
{"setParameter", setParameterCmd, 0},
{"setting", settingCmd, 0},
{"styleFooter", styleFooterCmd, 0},
{"styleHeader", styleHeaderCmd, 0},
{"styleScript", styleScriptCmd, 0},
{"submenu", submenuCmd, 0},
{"taint", taintCmd, 0},
{"tclReady", tclReadyCmd, 0},
{"trace", traceCmd, 0},
{"stime", stimeCmd, 0},
{"untaint", untaintCmd, 0},
{"unversioned", unversionedCmd, 0},
{"utime", utimeCmd, 0},
{"verifyCsrf", verifyCsrfCmd, 0},
{"verifyLogin", verifyLoginCmd, 0},
{"wiki", wikiCmd, (void*)&aFlags[0]},
{"wiki_assoc", wikiAssocCmd, 0},
{0, 0, 0}
};
if( g.thTrace ){
Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags);
}
if( needConfig ){
/*
|
| ︙ | ︙ | |||
2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 |
if( zValue ){
if( g.thTrace ){
Th_Trace("set %h {%h}<br>\n", zName, zValue);
}
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
}
}
/*
** Appends an element to a TH1 list value. This function is called by the
** transfer subsystem; therefore, it must be very careful to avoid doing
** any unnecessary work. To that end, the TH1 subsystem will not be called
** or initialized if the list pointer is zero (i.e. which will be the case
** when TH1 transfer hooks are disabled).
| > > > > > > > > > > > > > > > > | 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 |
if( zValue ){
if( g.thTrace ){
Th_Trace("set %h {%h}<br>\n", zName, zValue);
}
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
}
}
/*
** Store a string value in a variable in the interpreter
** with the "taint" marking, so that TH1 knows that this
** variable contains content under the control of the remote
** user and presents a risk of XSS or SQL-injection attacks.
*/
void Th_StoreUnsafe(const char *zName, const char *zValue){
Th_FossilInit(TH_INIT_DEFAULT);
if( zValue ){
if( g.thTrace ){
Th_Trace("set %h [taint {%h}]<br>\n", zName, zValue);
}
Th_SetVar(g.interp, zName, -1, zValue, TH1_ADD_TAINT(strlen(zValue)));
}
}
/*
** Appends an element to a TH1 list value. This function is called by the
** transfer subsystem; therefore, it must be very careful to avoid doing
** any unnecessary work. To that end, the TH1 subsystem will not be called
** or initialized if the list pointer is zero (i.e. which will be the case
** when TH1 transfer hooks are disabled).
|
| ︙ | ︙ | |||
2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 |
if( rc==TH_ERROR ){
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
/*
** Make sure that the TH1 script error was not caused by a "missing"
** command hook handler as that is not actually an error condition.
*/
if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){
sendError(0,zResult, nResult, 0);
}else{
/*
** There is no command hook handler "installed". This situation
** is NOT actually an error.
*/
| > | 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 |
if( rc==TH_ERROR ){
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
/*
** Make sure that the TH1 script error was not caused by a "missing"
** command hook handler as that is not actually an error condition.
*/
nResult = TH1_LEN(nResult);
if( memcmp(zResult, NO_COMMAND_HOOK_ERROR, nResult)!=0 ){
sendError(0,zResult, nResult, 0);
}else{
/*
** There is no command hook handler "installed". This situation
** is NOT actually an error.
*/
|
| ︙ | ︙ | |||
2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 |
if( rc==TH_ERROR ){
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
/*
** Make sure that the TH1 script error was not caused by a "missing"
** webpage hook handler as that is not actually an error condition.
*/
if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){
sendError(0,zResult, nResult, 1);
}else{
/*
** There is no webpage hook handler "installed". This situation
** is NOT actually an error.
*/
| > | 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 |
if( rc==TH_ERROR ){
int nResult = 0;
char *zResult = (char*)Th_GetResult(g.interp, &nResult);
/*
** Make sure that the TH1 script error was not caused by a "missing"
** webpage hook handler as that is not actually an error condition.
*/
nResult = TH1_LEN(nResult);
if( memcmp(zResult, NO_WEBPAGE_HOOK_ERROR, nResult)!=0 ){
sendError(0,zResult, nResult, 1);
}else{
/*
** There is no webpage hook handler "installed". This situation
** is NOT actually an error.
*/
|
| ︙ | ︙ | |||
2870 2871 2872 2873 2874 2875 2876 |
nVar = n;
encode = 0;
}
rc = Th_GetVar(g.interp, (char*)zVar, nVar);
z += i+1+n;
i = 0;
zResult = (char*)Th_GetResult(g.interp, &n);
| > > > > | > | | 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 |
nVar = n;
encode = 0;
}
rc = Th_GetVar(g.interp, (char*)zVar, nVar);
z += i+1+n;
i = 0;
zResult = (char*)Th_GetResult(g.interp, &n);
if( !TH1_TAINTED(n)
|| encode
|| Th_ReportTaint(g.interp, "inline variable", zVar, nVar)==TH_OK
){
sendText(pOut,(char*)zResult, n, encode);
}
}else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
sendText(pOut,z, i, 0);
z += i+5;
for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
if( g.thTrace ){
Th_Trace("render_eval {<pre>%#h</pre>}<br>\n", i, z);
}
rc = Th_Eval(g.interp, 0, (const char*)z, i);
if( g.thTrace ){
int nTrRes;
char *zTrRes = (char*)Th_GetResult(g.interp, &nTrRes);
Th_Trace("[render_eval] => %h {%#h}<br>\n",
Th_ReturnCodeName(rc, 0), TH1_LEN(nTrRes), zTrRes);
}
if( rc!=TH_OK ) break;
z += i;
if( z[0] ){ z += 6; }
i = 0;
}else{
i++;
|
| ︙ | ︙ | |||
2925 2926 2927 2928 2929 2930 2931 |
** Th_RenderToBlob() will output directly to the CGI buffer (in
** CGI mode) or stdout (in CLI mode). Recursive calls, however,
** e.g. via the "render" script function binding, need to use the
** pThOut blob in order to avoid out-of-order output if
** Th_SetOutputBlob() has been called. If it has not been called,
** pThOut will be 0, which will redirect the output to CGI/stdout,
** as appropriate. We need to pass on g.th1Flags for the case of
| | > > | > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 |
** Th_RenderToBlob() will output directly to the CGI buffer (in
** CGI mode) or stdout (in CLI mode). Recursive calls, however,
** e.g. via the "render" script function binding, need to use the
** pThOut blob in order to avoid out-of-order output if
** Th_SetOutputBlob() has been called. If it has not been called,
** pThOut will be 0, which will redirect the output to CGI/stdout,
** as appropriate. We need to pass on g.th1Flags for the case of
** recursive calls.
*/;
}
/*
** SETTING: vuln-report width=8 default=log
**
** This setting controls Fossil's behavior when it encounters a potential
** XSS or SQL-injection vulnerability due to misuse of TH1 configuration
** scripts. Choices are:
**
** off Do nothing. Ignore the vulnerability.
**
** log Write a report of the problem into the error log.
**
** block Like "log" but also prevent the offending TH1 command
** from running.
**
** fatal Render an error message page instead of the requested
** page.
*/
/*
** Report misuse of a tainted string in TH1.
**
** The behavior depends on the vuln-report setting. If "off", this routine
** is a no-op. Otherwise, right a message into the error log. If
** vuln-report is "log", that is all that happens. But for any other
** value of vuln-report, a fatal error is raised.
*/
int Th_ReportTaint(
Th_Interp *interp, /* Report error here, if an error is reported */
const char *zWhere, /* Where the tainted string appears */
const char *zStr, /* The tainted string */
int nStr /* Length of the tainted string */
){
static const char *zDisp = 0; /* Dispensation; what to do with the error */
const char *zVulnType; /* Type of vulnerability */
if( zDisp==0 ) zDisp = db_get("vuln-report","log");
if( is_false(zDisp) ) return 0;
if( strstr(zWhere,"SQL")!=0 ){
zVulnType = "SQL-injection";
}else{
zVulnType = "XSS";
}
nStr = TH1_LEN(nStr);
fossil_errorlog("possible TH1 %s vulnerability due to tainted %s: \"%.*s\"",
zVulnType, zWhere, nStr, zStr);
if( strcmp(zDisp,"log")==0 ){
return 0;
}
if( strcmp(zDisp,"block")==0 ){
char *z = mprintf("tainted %s: \"", zWhere);
Th_ErrorMessage(interp, z, zStr, nStr);
fossil_free(z);
}else{
char *z = mprintf("%#h", nStr, zStr);
zDisp = "off";
cgi_reset_content();
style_submenu_enable(0);
style_set_current_feature("error");
style_header("Configuration Error");
@ <p>Error in a TH1 configuration script:
@ tainted %h(zWhere): "%z(z)"
style_finish_page();
cgi_reply();
fossil_exit(1);
}
return 1;
}
/*
** COMMAND: test-th-render
**
** Usage: %fossil test-th-render FILE
**
** Read the content of the file named "FILE" as if it were a header or
|
| ︙ | ︙ | |||
2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 |
g.useLocalauth = 1;
}
if( find_option("set-user-caps", 0, 0)!=0 ){
const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS");
login_set_capabilities(zCap ? zCap : "sx", 0);
g.useLocalauth = 1;
}
verify_all_options();
if( g.argc<3 ){
usage("FILE");
}
blob_zero(&in);
blob_read_from_file(&in, g.argv[2], ExtFILE);
Th_Render(blob_str(&in));
| > | 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 |
g.useLocalauth = 1;
}
if( find_option("set-user-caps", 0, 0)!=0 ){
const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS");
login_set_capabilities(zCap ? zCap : "sx", 0);
g.useLocalauth = 1;
}
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
verify_all_options();
if( g.argc<3 ){
usage("FILE");
}
blob_zero(&in);
blob_read_from_file(&in, g.argv[2], ExtFILE);
Th_Render(blob_str(&in));
|
| ︙ | ︙ | |||
3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 |
g.useLocalauth = 1;
}
if( find_option("set-user-caps", 0, 0)!=0 ){
const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS");
login_set_capabilities(zCap ? zCap : "sx", 0);
g.useLocalauth = 1;
}
verify_all_options();
if( g.argc!=3 ){
usage("script");
}
if(file_isfile(g.argv[2], ExtFILE)){
blob_read_from_file(&code, g.argv[2], ExtFILE);
zCode = blob_str(&code);
| > | 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 |
g.useLocalauth = 1;
}
if( find_option("set-user-caps", 0, 0)!=0 ){
const char *zCap = fossil_getenv("TH1_TEST_USER_CAPS");
login_set_capabilities(zCap ? zCap : "sx", 0);
g.useLocalauth = 1;
}
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
verify_all_options();
if( g.argc!=3 ){
usage("script");
}
if(file_isfile(g.argv[2], ExtFILE)){
blob_read_from_file(&code, g.argv[2], ExtFILE);
zCode = blob_str(&code);
|
| ︙ | ︙ |
Changes to src/th_tcl.c.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #ifdef FOSSIL_ENABLE_TCL #include "sqlite3.h" #include "th.h" #include "tcl.h" /* ** This macro is used to verify that the header version of Tcl meets some ** minimum requirement. */ #define MINIMUM_TCL_VERSION(major, minor) \ ((TCL_MAJOR_VERSION > (major)) || \ ((TCL_MAJOR_VERSION == (major)) && (TCL_MINOR_VERSION >= (minor)))) /* ** These macros are designed to reduce the redundant code required to marshal ** arguments from TH1 to Tcl. */ #define USE_ARGV_TO_OBJV() \ int objc; \ Tcl_Obj **objv; \ int obji; | > > > > | | | | | | | 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 |
#ifdef FOSSIL_ENABLE_TCL
#include "sqlite3.h"
#include "th.h"
#include "tcl.h"
#if TCL_MAJOR_VERSION<9 && !defined(Tcl_Size)
# define Tcl_Size int
#endif
/*
** This macro is used to verify that the header version of Tcl meets some
** minimum requirement.
*/
#define MINIMUM_TCL_VERSION(major, minor) \
((TCL_MAJOR_VERSION > (major)) || \
((TCL_MAJOR_VERSION == (major)) && (TCL_MINOR_VERSION >= (minor))))
/*
** These macros are designed to reduce the redundant code required to marshal
** arguments from TH1 to Tcl.
*/
#define USE_ARGV_TO_OBJV() \
int objc; \
Tcl_Obj **objv; \
int obji;
#define COPY_ARGV_TO_OBJV() \
objc = argc-1; \
objv = (Tcl_Obj **)ckalloc((unsigned)(objc * sizeof(Tcl_Obj *))); \
for(obji=1; obji<argc; obji++){ \
objv[obji-1] = Tcl_NewStringObj(argv[obji], TH1_LEN(argl[obji])); \
Tcl_IncrRefCount(objv[obji-1]); \
}
#define FREE_ARGV_TO_OBJV() \
for(obji=1; obji<argc; obji++){ \
Tcl_DecrRefCount(objv[obji-1]); \
objv[obji-1] = 0; \
} \
|
| ︙ | ︙ | |||
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | ** when the Tcl library is being loaded dynamically by a stubs-enabled ** application (i.e. the inverse of using a stubs-enabled package). These are ** the only Tcl API functions that MUST be called prior to being able to call ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp ** and Tcl_Finalize function types are also required. */ typedef void (tcl_FindExecutableProc) (const char *); typedef Tcl_Interp *(tcl_CreateInterpProc) (void); typedef void (tcl_DeleteInterpProc) (Tcl_Interp *); typedef void (tcl_FinalizeProc) (void); /* ** The function types for the "hook" functions to be called before and after a ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function | > > > > | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | ** when the Tcl library is being loaded dynamically by a stubs-enabled ** application (i.e. the inverse of using a stubs-enabled package). These are ** the only Tcl API functions that MUST be called prior to being able to call ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp ** and Tcl_Finalize function types are also required. */ #if TCL_MAJOR_VERSION>=9 typedef const char *(tcl_FindExecutableProc) (const char *); #else typedef void (tcl_FindExecutableProc) (const char *); #endif typedef Tcl_Interp *(tcl_CreateInterpProc) (void); typedef void (tcl_DeleteInterpProc) (Tcl_Interp *); typedef void (tcl_FinalizeProc) (void); /* ** The function types for the "hook" functions to be called before and after a ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function |
| ︙ | ︙ | |||
319 320 321 322 323 324 325 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied ** by the caller. This must be declared here because quite a few functions in ** this file need to use it before it can be defined. */ static int createTclInterp(Th_Interp *interp, void *pContext); | < < < < < < < < < < < < < < < < < | 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
** Creates and initializes a Tcl interpreter for use with the specified TH1
** interpreter. Stores the created Tcl interpreter in the Tcl context supplied
** by the caller. This must be declared here because quite a few functions in
** this file need to use it before it can be defined.
*/
static int createTclInterp(Th_Interp *interp, void *pContext);
/*
** Returns the Tcl return code corresponding to the specified TH1
** return code.
*/
static int getTclReturnCode(
int rc /* The TH1 return code value to convert. */
){
|
| ︙ | ︙ | |||
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
** If the length pointer is NULL, the length will not be stored.
*/
static char *getTclResult(
Tcl_Interp *pInterp,
int *pN
){
Tcl_Obj *resultPtr;
if( !pInterp ){ /* This should not happen. */
if( pN ) *pN = 0;
return 0;
}
resultPtr = Tcl_GetObjResult(pInterp);
if( !resultPtr ){ /* This should not happen either? */
if( pN ) *pN = 0;
return 0;
}
| > > | > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 |
** If the length pointer is NULL, the length will not be stored.
*/
static char *getTclResult(
Tcl_Interp *pInterp,
int *pN
){
Tcl_Obj *resultPtr;
Tcl_Size n;
char *zRes;
if( !pInterp ){ /* This should not happen. */
if( pN ) *pN = 0;
return 0;
}
resultPtr = Tcl_GetObjResult(pInterp);
if( !resultPtr ){ /* This should not happen either? */
if( pN ) *pN = 0;
return 0;
}
zRes = Tcl_GetStringFromObj(resultPtr, &n);
*pN = (int)n;
return zRes;
}
/*
** Tcl context information used by TH1. This structure definition has been
** copied from and should be kept in sync with the one in "main.c".
*/
struct TclContext {
int argc; /* Number of original arguments. */
char **argv; /* Full copy of the original arguments. */
void *hLibrary; /* The Tcl library module handle. */
tcl_FindExecutableProc *xFindExecutable; /* Tcl_FindExecutable() pointer. */
tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */
tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */
tcl_FinalizeProc *xFinalize; /* Tcl_Finalize() pointer. */
Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */
int useObjProc; /* Non-zero if an objProc can be called directly. */
int useTip285; /* Non-zero if TIP #285 is available. */
const char *setup; /* The optional Tcl setup script. */
};
/*
** TH1 command: tclEval arg ?arg ...?
**
** Evaluates the Tcl script and returns its result verbatim. If a Tcl script
** error is generated, it will be transformed into a TH1 script error. The
** Tcl interpreter will be created automatically if it has not been already.
*/
|
| ︙ | ︙ | |||
483 484 485 486 487 488 489 |
return Th_WrongNumArgs(interp, "tclEval arg ?arg ...?");
}
tclInterp = GET_CTX_TCL_INTERP(ctx);
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
| < < < < | < < | 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 |
return Th_WrongNumArgs(interp, "tclEval arg ?arg ...?");
}
tclInterp = GET_CTX_TCL_INTERP(ctx);
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
Tcl_Preserve((ClientData)tclInterp);
if( argc==2 ){
objPtr = Tcl_NewStringObj(argv[1], TH1_LEN(argl[1]));
Tcl_IncrRefCount(objPtr);
rc = Tcl_EvalObjEx(tclInterp, objPtr, 0);
Tcl_DecrRefCount(objPtr); objPtr = 0;
}else{
USE_ARGV_TO_OBJV();
COPY_ARGV_TO_OBJV();
objPtr = Tcl_ConcatObj(objc, objv);
Tcl_IncrRefCount(objPtr);
rc = Tcl_EvalObjEx(tclInterp, objPtr, 0);
Tcl_DecrRefCount(objPtr); objPtr = 0;
FREE_ARGV_TO_OBJV();
}
zResult = getTclResult(tclInterp, &nResult);
Th_SetResult(interp, zResult, nResult);
Tcl_Release((ClientData)tclInterp);
return rc;
}
/*
** TH1 command: tclExpr arg ?arg ...?
**
** Evaluates the Tcl expression and returns its result verbatim. If a Tcl
|
| ︙ | ︙ | |||
543 544 545 546 547 548 549 |
return Th_WrongNumArgs(interp, "tclExpr arg ?arg ...?");
}
tclInterp = GET_CTX_TCL_INTERP(ctx);
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
| < < < < | > | > | < < | 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 |
return Th_WrongNumArgs(interp, "tclExpr arg ?arg ...?");
}
tclInterp = GET_CTX_TCL_INTERP(ctx);
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
Tcl_Preserve((ClientData)tclInterp);
if( argc==2 ){
objPtr = Tcl_NewStringObj(argv[1], TH1_LEN(argl[1]));
Tcl_IncrRefCount(objPtr);
rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr);
Tcl_DecrRefCount(objPtr); objPtr = 0;
}else{
USE_ARGV_TO_OBJV();
COPY_ARGV_TO_OBJV();
objPtr = Tcl_ConcatObj(objc, objv);
Tcl_IncrRefCount(objPtr);
rc = Tcl_ExprObj(tclInterp, objPtr, &resultObjPtr);
Tcl_DecrRefCount(objPtr); objPtr = 0;
FREE_ARGV_TO_OBJV();
}
if( rc==TCL_OK ){
Tcl_Size szResult = 0;
zResult = Tcl_GetStringFromObj(resultObjPtr, &szResult);
nResult = (int)szResult;
}else{
zResult = getTclResult(tclInterp, &nResult);
}
Th_SetResult(interp, zResult, (int)nResult);
if( rc==TCL_OK ){
Tcl_DecrRefCount(resultObjPtr); resultObjPtr = 0;
}
Tcl_Release((ClientData)tclInterp);
return rc;
}
/*
** TH1 command: tclInvoke command ?arg ...?
**
** Invokes the Tcl command using the supplied arguments. No additional
|
| ︙ | ︙ | |||
608 609 610 611 612 613 614 |
return Th_WrongNumArgs(interp, "tclInvoke command ?arg ...?");
}
tclInterp = GET_CTX_TCL_INTERP(ctx);
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
| < < < < | | 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
return Th_WrongNumArgs(interp, "tclInvoke command ?arg ...?");
}
tclInterp = GET_CTX_TCL_INTERP(ctx);
if( !tclInterp || Tcl_InterpDeleted(tclInterp) ){
Th_ErrorMessage(interp, "invalid Tcl interpreter", (const char *)"", 0);
return TH_ERROR;
}
Tcl_Preserve((ClientData)tclInterp);
#if !defined(USE_TCL_EVALOBJV) || !USE_TCL_EVALOBJV
if( GET_CTX_TCL_USEOBJPROC(ctx) ){
Tcl_Command command;
Tcl_CmdInfo cmdInfo;
Tcl_Obj *objPtr = Tcl_NewStringObj(argv[1], TH1_LEN(argl[1]));
Tcl_IncrRefCount(objPtr);
command = Tcl_GetCommandFromObj(tclInterp, objPtr);
if( !command || Tcl_GetCommandInfoFromToken(command, &cmdInfo)==0 ){
Th_ErrorMessage(interp, "Tcl command not found:", argv[1], argl[1]);
Tcl_DecrRefCount(objPtr); objPtr = 0;
Tcl_Release((ClientData)tclInterp);
return TH_ERROR;
|
| ︙ | ︙ | |||
647 648 649 650 651 652 653 |
COPY_ARGV_TO_OBJV();
rc = Tcl_EvalObjv(tclInterp, objc, objv, 0);
FREE_ARGV_TO_OBJV();
}
zResult = getTclResult(tclInterp, &nResult);
Th_SetResult(interp, zResult, nResult);
Tcl_Release((ClientData)tclInterp);
| < < | 592 593 594 595 596 597 598 599 600 601 602 603 604 605 |
COPY_ARGV_TO_OBJV();
rc = Tcl_EvalObjv(tclInterp, objc, objv, 0);
FREE_ARGV_TO_OBJV();
}
zResult = getTclResult(tclInterp, &nResult);
Th_SetResult(interp, zResult, nResult);
Tcl_Release((ClientData)tclInterp);
return rc;
}
/*
** TH1 command: tclIsSafe
**
** Returns non-zero if the Tcl interpreter is "safe". The Tcl interpreter
|
| ︙ | ︙ | |||
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 |
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[]
){
Th_Interp *th1Interp;
int nArg;
const char *arg;
int rc;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "arg");
return TCL_ERROR;
}
th1Interp = (Th_Interp *)clientData;
if( !th1Interp ){
Tcl_AppendResult(interp, "invalid TH1 interpreter", NULL);
return TCL_ERROR;
}
| > | > | > | | | | 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 |
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[]
){
Th_Interp *th1Interp;
int nArg;
Tcl_Size szArg;
const char *arg;
int rc;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "arg");
return TCL_ERROR;
}
th1Interp = (Th_Interp *)clientData;
if( !th1Interp ){
Tcl_AppendResult(interp, "invalid TH1 interpreter", NULL);
return TCL_ERROR;
}
arg = Tcl_GetStringFromObj(objv[1], &szArg);
nArg = (int)szArg;
rc = Th_Eval(th1Interp, 0, arg, nArg);
arg = Th_GetResult(th1Interp, &nArg);
Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, TH1_LEN(nArg)));
return getTclReturnCode(rc);
}
/*
** Tcl command: th1Expr arg
**
** Evaluates the TH1 expression and returns its result verbatim. If a TH1
** script error is generated, it will be transformed into a Tcl script error.
*/
static int Th1ExprObjCmd(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[]
){
Th_Interp *th1Interp;
int nArg;
Tcl_Size szArg;
const char *arg;
int rc;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "arg");
return TCL_ERROR;
}
th1Interp = (Th_Interp *)clientData;
if( !th1Interp ){
Tcl_AppendResult(interp, "invalid TH1 interpreter", NULL);
return TCL_ERROR;
}
arg = Tcl_GetStringFromObj(objv[1], &szArg);
rc = Th_Expr(th1Interp, arg, (int)szArg);
arg = Th_GetResult(th1Interp, &nArg);
Tcl_SetObjResult(interp, Tcl_NewStringObj(arg, TH1_LEN(nArg)));
return getTclReturnCode(rc);
}
/*
** Array of Tcl integration commands. Used when adding or removing the Tcl
** integration commands from TH1.
*/
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
189 190 191 192 193 194 195 | ** 10. Short comment to user for repeated tickets and wiki */ void www_print_timeline( Stmt *pQuery, /* Query to implement the timeline */ int tmFlags, /* Flags controlling display behavior */ const char *zThisUser, /* Suppress links to this user */ const char *zThisTag, /* Suppress links to this tag */ | | < > > > > > > > > > > > > > > > > > < < < < | | 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 |
** 10. Short comment to user for repeated tickets and wiki
*/
void www_print_timeline(
Stmt *pQuery, /* Query to implement the timeline */
int tmFlags, /* Flags controlling display behavior */
const char *zThisUser, /* Suppress links to this user */
const char *zThisTag, /* Suppress links to this tag */
Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
int selectedRid, /* Highlight the line with this RID value or zero */
int secondRid, /* Secondary highlight (or zero) */
void (*xExtra)(int) /* Routine to call on each line of display */
){
int mxWikiLen;
Blob comment;
int prevTagid = 0;
int suppressCnt = 0;
char zPrevDate[20];
GraphContext *pGraph = 0;
int prevWasDivider = 0; /* True if previous output row was <hr> */
int fchngQueryInit = 0; /* True if fchngQuery is initialized */
Stmt fchngQuery; /* Query for file changes on check-ins */
int pendingEndTr = 0; /* True if a </td></tr> is needed */
int vid = 0; /* Current check-out version */
int dateFormat = 0; /* 0: HH:MM (default) */
int bCommentGitStyle = 0; /* Only show comments through first blank line */
const char *zStyle; /* Sub-name for classes for the style */
const char *zDateFmt;
int iTableId = timeline_tableid();
int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
** page rather than the /timeline page */
if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
vid = db_lget_int("checkout", 0);
}
zPrevDate[0] = 0;
mxWikiLen = db_get_int("timeline-max-comment", 0);
dateFormat = db_get_int("timeline-date-format", 0);
/*
** SETTING: timeline-truncate-at-blank boolean default=off
**
** If enabled, check-in comments displayed on the timeline are truncated
** at the first blank line of the comment text. The comment text after
** the first blank line is only seen in the /info or similar pages that
** show details about the check-in.
*/
bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
/*
** SETTING: timeline-tslink-info boolean default=off
**
** The hyperlink on the timestamp associated with each timeline entry,
** on the far left-hand side of the screen, normally targets another
** /timeline page that shows the entry in context. However, if this
** option is turned on, that hyperlink targets the /info page showing
** the details of the entry.
*/
bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
if( (tmFlags & TIMELINE_VIEWS)==0 ){
tmFlags |= timeline_ss_cookie();
}
if( tmFlags & TIMELINE_COLUMNAR ){
zStyle = "Columnar";
}else if( tmFlags & TIMELINE_COMPACT ){
zStyle = "Compact";
}else if( tmFlags & TIMELINE_VERBOSE ){
zStyle = "Verbose";
}else if( tmFlags & TIMELINE_CLASSIC ){
zStyle = "Classic";
}else{
zStyle = "Modern";
}
zDateFmt = P("datefmt");
if( zDateFmt ) dateFormat = atoi(zDateFmt);
if( tmFlags & TIMELINE_GRAPH ){
pGraph = graph_init();
}
if( (tmFlags & TIMELINE_CHPICK)!=0
&& !db_table_exists("repository","cherrypick")
){
tmFlags &= ~TIMELINE_CHPICK;
}
@ <table id="timelineTable%d(iTableId)" class="timelineTable"> \
@ <!-- tmFlags: 0x%x(tmFlags) -->
blob_zero(&comment);
while( db_step(pQuery)==SQLITE_ROW ){
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
int isLeaf = db_column_int(pQuery, 5);
const char *zBgClr = db_column_text(pQuery, 6);
const char *zDate = db_column_text(pQuery, 2);
const char *zType = db_column_text(pQuery, 7);
const char *zUser = db_column_text(pQuery, 4);
const char *zTagList = db_column_text(pQuery, 8);
int tagid = db_column_int(pQuery, 9);
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
char *zBr = 0; /* Branch */
int commentColumn = 3; /* Column containing comment text */
int modPending; /* Pending moderation */
char *zDateLink; /* URL for the link on the timestamp */
int drawDetailEllipsis; /* True to show ellipsis in place of detail */
int gidx = 0; /* Graph row identifier */
int isSelectedOrCurrent = 0; /* True if current row is selected */
const char *zExtraClass = "";
|
| ︙ | ︙ | |||
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 |
}
}else{
zDateLink = mprintf("<a>");
}
@ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
@ <td class="timelineGraph">
if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
if( tmFlags & TIMELINE_UCOLOR ){
zBgClr = zUser ? user_color(zUser) : 0;
}else if( tmFlags & TIMELINE_NOCOLOR ){
zBgClr = 0;
}else if( zType[0]=='c' ){
static Stmt qdelta;
db_static_prepare(&qdelta, "SELECT baseid IS NULL FROM plink"
" WHERE cid=:rid");
db_bind_int(&qdelta, ":rid", rid);
if( db_step(&qdelta)!=SQLITE_ROW ){
zBgClr = 0; /* Not a check-in */
}else if( db_column_int(&qdelta, 0) ){
zBgClr = hash_color("b"); /* baseline manifest */
}else{
zBgClr = hash_color("f"); /* delta manifest */
}
db_reset(&qdelta);
}
}
if( zType[0]=='c'
&& (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
){
| > > > > > < < < < < | < > > | 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 |
}
}else{
zDateLink = mprintf("<a>");
}
@ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
@ <td class="timelineGraph">
if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
/* Don't use the requested background color. Use the background color
** override from query parameters instead. */
if( tmFlags & TIMELINE_UCOLOR ){
zBgClr = zUser ? user_color(zUser) : 0;
}else if( tmFlags & TIMELINE_NOCOLOR ){
zBgClr = 0;
}else if( zType[0]=='c' ){
static Stmt qdelta;
db_static_prepare(&qdelta, "SELECT baseid IS NULL FROM plink"
" WHERE cid=:rid");
db_bind_int(&qdelta, ":rid", rid);
if( db_step(&qdelta)!=SQLITE_ROW ){
zBgClr = 0; /* Not a check-in */
}else if( db_column_int(&qdelta, 0) ){
zBgClr = hash_color("b"); /* baseline manifest */
}else{
zBgClr = hash_color("f"); /* delta manifest */
}
db_reset(&qdelta);
}
}else{
/* Make sure the user-specified background color is reasonable */
zBgClr = reasonable_bg_color(zBgClr, 0);
}
if( zType[0]=='c'
&& (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
){
zBr = branch_of_rid(rid);
if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
/* If no background color is specified, use a color based on the
** branch name */
if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
}else if( zBr==0 || strcmp(zBr,"trunk")==0 ){
zBgClr = 0;
}else{
zBgClr = hash_color(zBr);
}
}
|
| ︙ | ︙ | |||
457 458 459 460 461 462 463 |
nCherrypick++;
}
db_reset(&qcherrypick);
}
gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
zBr, zBgClr, zUuid,
isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0);
| < > | 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
nCherrypick++;
}
db_reset(&qcherrypick);
}
gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
zBr, zBgClr, zUuid,
isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0);
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
}else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){
/* For technotes, make a graph node with nParent==(-1). This will
** not actually draw anything on the graph, but it will set the
** background color of the timeline entry */
gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
}
fossil_free(zBr);
@</td>
if( !isSelectedOrCurrent ){
@ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
}else{
@ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
}
if( pGraph ){
|
| ︙ | ︙ | |||
583 584 585 586 587 588 589 590 591 592 593 594 595 596 |
@ %W(blob_str(&truncated))
blob_reset(&truncated);
drawDetailEllipsis = 0;
}else{
cgi_printf("%W",blob_str(&comment));
}
}
@ </span>
blob_reset(&comment);
/* Generate extra information and hyperlinks to follow the comment.
** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
*/
if( drawDetailEllipsis ){
| > > > > > > > > > | 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 |
@ %W(blob_str(&truncated))
blob_reset(&truncated);
drawDetailEllipsis = 0;
}else{
cgi_printf("%W",blob_str(&comment));
}
}
if( zType[0]=='c' && strcmp(zUuid, MANIFEST_UUID)==0 ){
/* This will only ever happen when Fossil is drawing a timeline for
** its own self-host repository. If the timeline shows the specific
** check-in corresponding to the current executable, then tag that
** check-in with "This is me!". */
@ <b>← This is me!</b>
}
@ </span>
blob_reset(&comment);
/* Generate extra information and hyperlinks to follow the comment.
** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
*/
if( drawDetailEllipsis ){
|
| ︙ | ︙ | |||
811 812 813 814 815 816 817 |
@ event%s(suppressCnt>1?"s":"") omitted.</span>
suppressCnt = 0;
}
if( pendingEndTr ){
@ </td></tr>
}
if( pGraph ){
| | | 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 |
@ event%s(suppressCnt>1?"s":"") omitted.</span>
suppressCnt = 0;
}
if( pendingEndTr ){
@ </td></tr>
}
if( pGraph ){
graph_finish(pGraph, pLeftBranch, tmFlags);
if( pGraph->nErr ){
graph_free(pGraph);
pGraph = 0;
}else{
@ <tr class="timelineBottom" id="btm-%d(iTableId)">\
@ <td></td><td></td><td></td></tr>
}
|
| ︙ | ︙ | |||
1098 1099 1100 1101 1102 1103 1104 |
@ event.mtime AS mtime
@ FROM event CROSS JOIN blob
@ WHERE blob.rid=event.objid
;
return zBase;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 |
@ event.mtime AS mtime
@ FROM event CROSS JOIN blob
@ WHERE blob.rid=event.objid
;
return zBase;
}
/*
** zDate is a localtime date. Insert records into the
** "timeline" table to cause <hr> to be inserted on zDate.
*/
static int timeline_add_divider(double rDate){
int rid = db_int(-1,
"SELECT rid FROM timeline ORDER BY abs(sortby-%.16g) LIMIT 1", rDate
|
| ︙ | ︙ | |||
1286 1287 1288 1289 1290 1291 1292 |
** for the SQL statement under construction that excludes any check-in that
** does not modify one or more files matching the globs.
*/
static void addFileGlobExclusion(
const char *zChng, /* The filename GLOB list */
Blob *pSql /* The SELECT statement under construction */
){
| | | | > | < < < < | < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < < < < < < < < < < < < < < < < < < < < | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < | < < < < < < < < < < < < | < < < < < < | < < < < < | < < < < < < < < | < < < < < < | < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < | > > > > > > > | | < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | 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 |
** for the SQL statement under construction that excludes any check-in that
** does not modify one or more files matching the globs.
*/
static void addFileGlobExclusion(
const char *zChng, /* The filename GLOB list */
Blob *pSql /* The SELECT statement under construction */
){
if( zChng==0 ) return;
blob_append_sql(pSql," AND event.objid IN ("
"SELECT mlink.mid FROM mlink, filename\n"
" WHERE mlink.fnid=filename.fnid\n"
" AND %s)",
glob_expr("filename.name", mprintf("\"%s\"", zChng)));
}
static void addFileGlobDescription(
const char *zChng, /* The filename GLOB list */
Blob *pDescription /* Result description */
){
if( zChng==0 ) return;
blob_appendf(pDescription, " that include changes to files matching '%h'",
zChng);
}
/*
** If zChng is not NULL, then use it as a comma-separated list of
** glob patterns for filenames, and remove from the "ok" table any
** check-ins that do not modify one or more of the files identified
** by zChng.
*/
static void removeFileGlobFromOk(
const char *zChng /* The filename GLOB list */
){
if( zChng==0 ) return;
db_multi_exec(
"DELETE FROM ok WHERE rid NOT IN (\n"
" SELECT mlink.mid FROM mlink, filename\n"
" WHERE mlink.fnid=filename.fnid\n"
" AND %z);\n",
glob_expr("filename.name", zChng)
);
}
/*
** Similar to fossil_expand_datetime()
**
** Add missing "-" characters into a date/time. Examples:
**
** 20190419 => 2019-04-19
** 201904 => 2019-04
*/
const char *timeline_expand_datetime(const char *zIn, int *pbZulu){
static char zEDate[16];
int n = (int)strlen(zIn);
int i, j;
/* These forms are recognized:
**
** (1) YYYYMMDD
** (2) YYYYMM
** (3) YYYYWW
*/
if( n && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
n--;
if( pbZulu ) *pbZulu = 1;
}else{
if( pbZulu ) *pbZulu = 0;
}
if( n!=8 && n!=6 ) return zIn;
/* Every character must be a digit */
for(i=0; i<n && fossil_isdigit(zIn[i]); i++){}
if( i!=n ) return zIn;
/* Expand the date */
for(i=j=0; i<n; i++){
if( j==4 || j==7 ) zEDate[j++] = '-';
zEDate[j++] = zIn[i];
}
zEDate[j] = 0;
/* It looks like this may be a date. Return it with punctuation added. */
return zEDate;
}
/*
** Check to see if the argument is a date-span for the ymd= query
** parameter. A valid date-span is of the form:
**
** 0123456789 123456 <-- index
** YYYYMMDD-YYYYMMDD
**
** with an optional "Z" timeline modifier at the end. Return true if
** the input is a valid date space and false if not.
*/
static int timeline_is_datespan(const char *zDay){
size_t n = strlen(zDay);
int i, d, m;
if( n<17 || n>18 ) return 0;
if( n==18 ){
if( zDay[17]!='Z' && zDay[17]!='z' ) return 0;
n--;
}
if( zDay[8]!='-' ) return 0;
for(i=0; i<17 && (fossil_isdigit(zDay[i]) || i==8); i++){}
if( i!=17 ) return 0;
i = atoi(zDay);
d = i%100;
if( d<1 || d>31 ) return 0;
m = (i/100)%100;
if( m<1 || m>12 ) return 0;
i = atoi(zDay+9);
d = i%100;
if( d<1 || d>31 ) return 0;
m = (i/100)%100;
if( m<1 || m>12 ) return 0;
return 1;
}
/*
** 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 (descendants) 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;
}
db_pause_dml_log();
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"
" ORDER BY dx.mtime 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"
|
| ︙ | ︙ | |||
1634 1635 1636 1637 1638 1639 1640 |
" 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"
| | > | 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 |
" 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"
" ORDER BY dx.mtime DESC 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"
|
| ︙ | ︙ | |||
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 |
);
}
}
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?
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | 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 |
);
}
}
if( db_step(&q)==SQLITE_ROW ){
ans = db_column_int(&q, 0);
}
db_finalize(&q);
db_unpause_dml_log();
return ans;
}
/*
** Add to the (temp) table zTab, RID values for every check-in
** identifier found on the zExtra string. Check-in names can be separated
** by commas or by whitespace.
*/
static void add_extra_rids(const char *zTab, const char *zExtra){
int ii;
int rid;
int cnt;
Blob sql;
char *zX;
char *zToDel;
if( zExtra==0 ) return;
cnt = 0;
blob_init(&sql, 0, 0);
zX = zToDel = fossil_strdup(zExtra);
blob_append_sql(&sql, "INSERT OR IGNORE INTO \"%w\" VALUES", zTab);
while( zX[0] ){
char c;
if( zX[0]==',' || zX[0]==' ' ){ zX++; continue; }
for(ii=1; zX[ii] && zX[ii]!=',' && zX[ii]!=' '; ii++){}
c = zX[ii];
zX[ii] = 0;
rid = name_to_rid(zX);
if( rid>0 ){
if( (cnt%10)==4 ){
blob_append_sql(&sql,",\n ");
}else if( cnt>0 ){
blob_append_sql(&sql,",");
}
blob_append_sql(&sql, "(%d)", rid);
cnt++;
}
zX[ii] = c;
zX += ii;
}
if( cnt ) db_exec_sql(blob_sql_text(&sql));
blob_reset(&sql);
fossil_free(zToDel);
}
/*
** COMMAND: test-endpoint
**
** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
**
** Show the first check-in with TAG that is a descendant or ancestor
** of BASE. The first descendant checkin is shown by default. Use
** the --backto to see the first ancestor checkin.
**
** Options:
**
** --backto Show ancestor. Others defaults to descendants.
*/
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();
|
| ︙ | ︙ | |||
1696 1697 1698 1699 1700 1701 1702 | /* ** WEBPAGE: timeline ** ** Query parameters: ** | | | | | | | | | | | > > | | > | | | | | | | > | > > | > | | | | | | | | > > | | 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 | /* ** WEBPAGE: timeline ** ** Query parameters: ** ** a=TIMEORTAG Show events after TIMEORTAG. ** b=TIMEORTAG Show events before TIMEORTAG. ** c=TIMEORTAG Show events that happen "circa" TIMEORTAG ** cf=FILEHASH Show events around the time of the first use of ** the file with FILEHASH. ** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available ** event if TIMEORTAG is not part of the timeline. If ** the t= or r= is used, the m event is added to the timeline ** if it isn't there already. ** x=LIST Show check-ins in the comma- or space-separated LIST ** in addition to check-ins specified by other parameters. ** sel1=TIMEORTAG Highlight the check-in at TIMEORTAG if it is part of ** the timeline. Similar to m= except TIMEORTAG must ** 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 ... 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 ... path from CX up to CHECKIN ** t=TAG Show only check-ins with the given TAG ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related" ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List" ** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List" ** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List" ** sl=TAGLIST "Sort List". Draw TAGLIST branches ordered left to right. ** rel Show related check-ins as well as those matching t=TAG ** mionly Show related parents but not related children. ** nowiki Do not show wiki associated with branch or tag ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob", ** "like", or "regexp". ** u=USER Only show items associated with USER ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'. ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar", * x: "Classic". ** advm Use the "Advanced" or "Busy" menu design. ** ng No Graph. ** ncp Omit cherrypick merges ** nd Do not highlight the focus check-in ** 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 Family (immediate parents and children) of CHECKIN ** from=CHECKIN Path through common ancestor from CHECKIN... ** to=CHECKIN ... to this ** to2=CHECKIN ... backup name if to= doesn't resolve ** shortest ... pick path with least number of nodes ** rel ... also show related checkins ** min ... hide long sequences along same branch ** bt=PRIOR ... path from CHECKIN back to PRIOR ** ft=LATER ... path from CHECKIN forward to LATER ** me=CHECKIN Most direct path from CHECKIN... ** you=CHECKIN ... to this ** 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 ** deltabg Background color red for delta manifests or green ** for baseline manifests ** namechng Show only check-ins that have filename changes ** forks Show only forks and their children ** cherrypicks Show all cherrypicks ** ym=YYYYMM Show only events for the given year/month ** yw=YYYYWW Show only events for the given week of the given year ** yw=YYYYMMDD Show events for the week that includes the given day ** ymd=YYYYMMDD Show only events on the given day. The use "ymd=now" ** to see all changes for the current week. Add "z" at end ** to divide days at UTC instead of localtime days. ** Use ymd=YYYYMMDD-YYYYMMDD (with optional "z") for a range. ** year=YYYY Show only events on the given year. The use "year=0" ** to see all changes for the current year. ** days=N Show events over the previous N days ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS, ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off". ** bisect Show the check-ins that are in the current bisect ** oldestfirst Show events oldest first. ** showid Show RIDs ** showsql Show the SQL used to generate the report ** ** p= and d= can appear individually or together. If either p= or d= ** appear, then u=, y=, a=, and b= are ignored. ** ** If both a= and b= appear then both upper and lower bounds are honored. ** ** When multiple time-related filters are used, e.g. ym, yw, and ymd, |
| ︙ | ︙ | |||
1841 1842 1843 1844 1845 1846 1847 |
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 */
| | | > > > > > > > | 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 |
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 bShort = P("shortest")!=0; /* shortest possible path */
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 *zDPNameP, *zDPNameD; /* 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 */
int showSql = PB("showsql"); /* True to show the SQL */
Blob allSql; /* Copy of all SQL text */
int bMin = P("min")!=0; /* True if "min" query parameter used */
login_check_credentials();
url_initialize(&url, "timeline");
cgi_query_parameters_to_url(&url);
blob_init(&allSql, 0, 0);
/* The "mionly" query parameter is like "rel", but shows merge-ins only */
if( P("mionly")!=0 ) related = 2;
(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. */;
/* Set number of rows to display */
z = P("n");
|
| ︙ | ︙ | |||
1905 1906 1907 1908 1909 1910 1911 |
}
}
}else{
nEntry = 50;
}
/* Query parameters d=, p=, and f= and variants */
| | | | > | 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 |
}
}
}else{
nEntry = 50;
}
/* Query parameters d=, p=, and f= and variants */
p_rid = name_choice("p","p2", &zDPNameP);
d_rid = name_choice("d","d2", &zDPNameD);
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));
zDPNameD = zDPNameP = z;
}
if( zChng && zChng[0]==0 ) zChng = 0;
/* 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 ){
|
| ︙ | ︙ | |||
1938 1939 1940 1941 1942 1943 1944 |
** 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.
*/
| | > | 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 |
** 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",&zDPNameP);
if( pd_rid ){
p_rid = d_rid = pd_rid;
zDPNameD = zDPNameP;
}
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
|| (bisectLocal && !g.perm.Setup)
){
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
return;
}
|
| ︙ | ︙ | |||
1982 1983 1984 1985 1986 1987 1988 1989 |
);
}
/* Check for tl=TAGLIST and rl=TAGLIST which are abbreviations for
** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
if( zBrName==0 && zTagName==0 ){
const char *z;
if( (z = P("tl"))!=0 ){
| > | < < | | | > > > > > > > > > > > | > | | > < < | < < < < < < < | | | 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 |
);
}
/* Check for tl=TAGLIST and rl=TAGLIST which are abbreviations for
** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
if( zBrName==0 && zTagName==0 ){
const char *z;
const char *zPattern = 0;
if( (z = P("tl"))!=0 ){
zPattern = zTagName = z;
}else if( (z = P("rl"))!=0 ){
zPattern = zBrName = z;
if( related==0 ) related = 1;
}else if( (z = P("ml"))!=0 ){
zPattern = zBrName = z;
if( related==0 ) related = 2;
}
if( zPattern!=0 && zMatchStyle==0 ){
/* If there was no ms= query parameter, set the match style to
** "glob" if the pattern appears to contain GLOB character, or
** "brlist" if it does not. */
if( strpbrk(zPattern,"*[?") ){
zMatchStyle = "glob";
}else{
zMatchStyle = "brlist";
}
}
}
/* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
if( zBrName ){
cgi_delete_query_parameter("r");
cgi_set_query_parameter("t", zBrName); (void)P("t");
cgi_set_query_parameter("rel", "1");
zTagName = zBrName;
if( related==0 ) related = 1;
zType = "ci";
}
/* Ignore empty tag query strings. */
if( zTagName && !*zTagName ){
zTagName = 0;
}
/* Finish preliminary processing of tag match queries. */
matchStyle = match_style(zMatchStyle, MS_EXACT);
if( zTagName ){
zType = "ci";
if( matchStyle==MS_EXACT ){
/* For exact maching, inhibit links to the selected tag. */
zThisTag = zTagName;
Th_StoreUnsafe("current_checkin", zTagName);
}
/* Display a checkbox to enable/disable display of related check-ins. */
if( advancedMenu ){
style_submenu_checkbox("rel", "Related", 0, 0);
}
/* Construct the tag match expression. */
zTagSql = match_tag_sqlexpr(matchStyle, zTagName, &zMatchDesc, &zError);
}
if( zMark && zMark[0]==0 ){
if( zAfter ) zMark = zAfter;
if( zBefore ) zMark = zBefore;
if( zCirca ) zMark = zCirca;
}
|
| ︙ | ︙ | |||
2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 |
if( PB("deltabg") ){
tmFlags |= TIMELINE_DELTA;
}
if( PB("nc") ){
tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR);
tmFlags |= TIMELINE_NOCOLOR;
}
if( zUses!=0 ){
int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
if( ufid ){
zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
compute_uses_file("usesfile", ufid, 0);
zType = "ci";
disableY = 1;
if( !haveParameterN ) nEntry = 0;
}else{
zUses = 0;
}
}
if( renameOnly ){
db_multi_exec(
"CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO rnfile"
| > | | | | | | | | | | | | | | | | 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 |
if( PB("deltabg") ){
tmFlags |= TIMELINE_DELTA;
}
if( PB("nc") ){
tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR);
tmFlags |= TIMELINE_NOCOLOR;
}
if( showSql ) db_append_dml_to_blob(&allSql);
if( zUses!=0 ){
int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
if( ufid ){
zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
compute_uses_file("usesfile", ufid, 0);
zType = "ci";
disableY = 1;
if( !haveParameterN ) nEntry = 0;
}else{
zUses = 0;
}
}
if( renameOnly ){
db_multi_exec(
"CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO rnfile"
" SELECT mid FROM mlink WHERE pfnid>0 AND pfnid!=fnid;"
);
disableY = 1;
}
if( forkOnly ){
db_multi_exec(
"CREATE TEMP TABLE rnfork(rid INTEGER PRIMARY KEY);\n"
"INSERT OR IGNORE INTO rnfork(rid)\n"
" SELECT pid FROM plink\n"
" WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
" GROUP BY pid\n"
" HAVING count(*)>1;\n"
"INSERT OR IGNORE INTO rnfork(rid)\n"
" SELECT cid FROM plink\n"
" WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
" GROUP BY cid\n"
" HAVING count(*)>1;\n",
TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
);
db_multi_exec(
"INSERT OR IGNORE INTO rnfork(rid)\n"
" SELECT cid FROM plink\n"
" WHERE pid IN rnfork\n"
" AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
" UNION\n"
" SELECT pid FROM plink\n"
" WHERE cid IN rnfork\n"
" AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n",
TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
);
tmFlags |= TIMELINE_UNHIDE;
zType = "ci";
disableY = 1;
}
if( bisectLocal && cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
|
| ︙ | ︙ | |||
2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 |
/* 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 ){
| > > > > | | > > | > > | > > > > > > > | | > > > | > > > > | > > | > > | | > > > > | | | | | | | | > > > > > > > > > > > > > > > | | | | | > | | > | 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 |
/* 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;
int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */
int earlierRid = 0, laterRid = 0;
int cost = bShort ? 0 : 1;
int nSkip = 0;
if( from_rid && to_rid ){
if( from_to_mode==0 ){
p = path_shortest(from_rid, to_rid, 0, 0, 0, cost);
}else if( from_to_mode==1 ){
p = path_shortest(from_rid, to_rid, 0, 1, 0, cost);
earlierRid = commonAncs = from_rid;
laterRid = to_rid;
}else{
p = path_shortest(to_rid, from_rid, 0, 1, 0, cost);
earlierRid = commonAncs = to_rid;
laterRid = from_rid;
}
zFrom = P("from");
zTo = zTo2 ? zTo2 : P("to");
}else{
commonAncs = path_common_ancestor(me_rid, you_rid);
if( commonAncs!=0 ){
p = path_first();
}
if( commonAncs==you_rid ){
zFrom = P("you");
zTo = P("me");
earlierRid = you_rid;
laterRid = me_rid;
}else{
zFrom = P("me");
zTo = P("you");
earlierRid = me_rid;
laterRid = you_rid;
}
}
blob_init(&ins, 0, 0);
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS pathnode(x INTEGER PRIMARY KEY);"
);
if( p ){
int cnt = 4;
blob_init(&ins, 0, 0);
blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid);
if( p->u.pTo==0 ) bMin = 0;
for(p=p->u.pTo; p; p=p->u.pTo){
if( bMin
&& p->u.pTo!=0
&& fossil_strcmp(path_branch(p->pFrom),path_branch(p))==0
&& fossil_strcmp(path_branch(p),path_branch(p->u.pTo))==0
){
nSkip++;
}else if( cnt==8 ){
blob_append_sql(&ins, ",\n (%d)", p->rid);
cnt = 0;
}else{
cnt++;
blob_append_sql(&ins, ",(%d)", p->rid);
}
}
}
path_reset();
db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
blob_reset(&ins);
if( related ){
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS related(x INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO related(x)"
" SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
);
if( related==1 ){
db_multi_exec(
"INSERT OR IGNORE INTO related(x)"
" SELECT cid FROM plink WHERE pid IN pathnode;"
);
}
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO related(x)"
" SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
);
if( related==1 ){
db_multi_exec(
"INSERT OR IGNORE INTO related(x)"
" SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
);
}
}
if( earlierRid && laterRid && commonAncs==earlierRid ){
/* On a query with me=XXX, you=YYY, and rel, omit all nodes that
** are not ancestors of either XXX or YYY, as those nodes tend to
** be extraneous */
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
compute_ancestors(laterRid, 0, 0, earlierRid);
db_multi_exec(
"DELETE FROM related WHERE x NOT IN ok;"
);
}
db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related");
}
add_extra_rids("pathnode",P("x"));
add_extra_rids("pathnode",P("sel1"));
add_extra_rids("pathnode",P("sel2"));
blob_append_sql(&sql, " AND event.objid IN pathnode");
if( zChng ){
db_multi_exec(
"DELETE FROM pathnode\n"
" WHERE NOT EXISTS(SELECT 1 FROM mlink, filename\n"
" WHERE mlink.mid=x\n"
" AND mlink.fnid=filename.fnid\n"
" AND %s)",
glob_expr("filename.name", zChng)
);
}
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( bMin ){
blob_appendf(&desc, "%d of %d check-ins along the path from ",
nNodeOnPath, nNodeOnPath+nSkip);
}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);
|
| ︙ | ︙ | |||
2312 2313 2314 2315 2316 2317 2318 |
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 ){
| | | | > > > | > > > > > | < | < | < < > > < > > > > > > > | < | > > | > | > > | | < | | | | | | | | < > > > > > | > > | < > > | | | | > | | | > > > > > > > > | < > > > > > > > > > > > > > | | | | > | | | | | > > > > > > > > > > > > > > | | > | | | | > | | > | | | | > | | < | < | 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 |
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 either p= or d= or both are present, ignore all other parameters
** other than n=, ft=, and bt= */
const char *zBaseName = 0;
int np = 0, nd;
const char *zBackTo = 0;
const char *zFwdTo = 0;
int ridBackTo = 0;
int ridFwdTo = 0;
int bBackAdded = 0; /* True if the zBackTo node was added */
int bFwdAdded = 0; /* True if the zBackTo node was added */
int bSeparateDandP = 0; /* p_rid & d_rid both exist and are distinct */
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
if( p_rid && d_rid && p_rid!=d_rid ){
bSeparateDandP = 1;
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok_d(rid INTEGER PRIMARY KEY)"
);
}else{
zBaseName = p_rid ? zDPNameP : zDPNameD;
}
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
add_extra_rids("ok", P("x"));
add_extra_rids("ok", P("sel1"));
add_extra_rids("ok", P("sel2"));
blob_append_sql(&sql, " AND event.objid IN ok");
nd = 0;
if( d_rid ){
double rStopTime = 9e99;
zFwdTo = P("ft");
if( zFwdTo && bSeparateDandP ){
if( zError==0 ){
zError = "Cannot use the ft= query parameter when both p= and d= "
"are used and have distinct values.";
}
zFwdTo = 0;
}
if( zFwdTo ){
double rStartDate = mtime_of_rid(d_rid, 0.0);
ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
if( ridFwdTo==0 ){
ridFwdTo = name_to_typed_rid(zBackTo,"ci");
}
if( ridFwdTo ){
if( !haveParameterN ) nEntry = 0;
rStopTime = mtime_of_rid(ridFwdTo, 9e99);
}
}else if( bSeparateDandP ){
rStopTime = mtime_of_rid(p_rid, 9e99);
nEntry = 0;
}
if( rStopTime<9e99 ){
rStopTime += 5.8e-6; /* Round up by 1/2 second */
}
db_multi_exec(
"WITH RECURSIVE dx(rid,mtime) AS (\n"
" SELECT %d, 0\n"
" UNION\n"
" SELECT plink.cid, plink.mtime FROM dx, plink\n"
" WHERE plink.pid=dx.rid\n"
" AND plink.mtime<=%.*g\n"
" ORDER BY 2\n"
")\n"
"INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
);
if( ridFwdTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridFwdTo) ){
db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridFwdTo);
bFwdAdded = 1;
}
if( bSeparateDandP ){
db_multi_exec(
"INSERT INTO ok_d SELECT rid FROM ok;"
"DELETE FROM ok;"
);
}else{
removeFileGlobFromOk(zChng);
nd = db_int(0, "SELECT count(*)-1 FROM ok");
if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
if( nd>0 || p_rid==0 ){
blob_appendf(&desc, "%d descendant%s",
nd>=0 ? nd : 0,(1==nd)?"":"s");
}
if( useDividers && !selectedRid ) selectedRid = d_rid;
db_multi_exec("DELETE FROM ok");
}
}
if( p_rid ){
zBackTo = P("bt");
if( zBackTo && bSeparateDandP ){
if( zError==0 ){
zError = "Cannot use the bt= query parameter when both p= and d= "
"are used and have distinct values.";
}
zBackTo = 0;
}
if( zBackTo ){
double rDateLimit = mtime_of_rid(p_rid, 0.0);
ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
if( ridBackTo==0 ){
ridBackTo = name_to_typed_rid(zBackTo,"ci");
}
if( ridBackTo && !haveParameterN ) nEntry = 0;
}else if( bSeparateDandP ){
ridBackTo = d_rid;
nEntry = 0;
}
compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
if( ridBackTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridBackTo) ){
db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
bBackAdded = 1;
}
if( bSeparateDandP ){
db_multi_exec("DELETE FROM ok WHERE rid NOT IN ok_d;");
removeFileGlobFromOk(zChng);
db_multi_exec("%s", blob_sql_text(&sql));
}else{
removeFileGlobFromOk(zChng);
np = db_int(0, "SELECT count(*)-1 FROM ok");
if( np>0 || nd==0 ){
if( nd>0 ) blob_appendf(&desc, " and ");
blob_appendf(&desc, "%d ancestor%s",
np>=0 ? np : 0, (1==np)?"":"s");
db_multi_exec("%s", blob_sql_text(&sql));
}
if( useDividers && !selectedRid ) selectedRid = p_rid;
}
}
if( bSeparateDandP ){
int n = db_int(0, "SELECT count(*) FROM ok");
blob_reset(&desc);
blob_appendf(&desc,
"%d check-ins that are derived from %z%h</a>"
" and contribute to %z%h</a>",
n,
href("%R/info?name=%h",zDPNameD),zDPNameD,
href("%R/info?name=%h",zDPNameP),zDPNameP
);
ridBackTo = 0;
ridFwdTo = 0;
}else{
blob_appendf(&desc, " of %z%h</a>",
href("%R/info?name=%h", zBaseName), zBaseName);
}
if( ridBackTo ){
if( np==0 ){
blob_reset(&desc);
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> does not precede it)",
href("%R/info?name=%h",zBaseName), zBaseName,
href("%R/info?name=%h",zBackTo), zBackTo);
}else{
blob_appendf(&desc, " back to %z%h</a>%s",
href("%R/info?name=%h",zBackTo), zBackTo,
bBackAdded ? " (not a direct anscestor)" : "");
if( ridFwdTo && zFwdTo ){
blob_appendf(&desc, " and up to %z%h</a>%s",
href("%R/info?name=%h",zFwdTo), zFwdTo,
bFwdAdded ? " (not a direct descendant)" : "");
}
}
}else if( ridFwdTo ){
if( nd==0 ){
blob_reset(&desc);
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> does not follow it)",
href("%R/info?name=%h",zBaseName), zBaseName,
href("%R/info?name=%h",zFwdTo), zFwdTo);
}else{
blob_appendf(&desc, " up to %z%h</a>%s",
href("%R/info?name=%h",zFwdTo), zFwdTo,
bFwdAdded ? " (not a direct descendant)":"");
}
}
if( zChng ){
if( strstr(blob_str(&desc)," that ") ) blob_appendf(&desc, " and");
blob_appendf(&desc, " that make changes to files matching \"%h\"", zChng);
}
if( advancedMenu ){
style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
}
style_submenu_entry("n","Max:",4,0);
timeline_y_submenu(1);
}else if( f_rid && g.perm.Read ){
|
| ︙ | ︙ | |||
2481 2482 2483 2484 2485 2486 2487 |
blob_zero(&cond);
tmFlags |= TIMELINE_FILLGAPS;
if( zChng && *zChng ){
addFileGlobExclusion(zChng, &cond);
tmFlags |= TIMELINE_XMERGE;
}
if( zUses ){
| | | | | | > > | < | < | < | > | > > < | < < > | > | > > < | > | > > | > > > > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | | > | > > > > | | < | > | > > | > > > > > > > > > > | < > | > | > > < | < > | > | > > < | > | > > | > > > | 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 |
blob_zero(&cond);
tmFlags |= TIMELINE_FILLGAPS;
if( zChng && *zChng ){
addFileGlobExclusion(zChng, &cond);
tmFlags |= TIMELINE_XMERGE;
}
if( zUses ){
blob_append_sql(&cond, " AND event.objid IN usesfile\n");
}
if( renameOnly ){
blob_append_sql(&cond, " AND event.objid IN rnfile\n");
}
if( forkOnly ){
blob_append_sql(&cond, " AND event.objid IN rnfork\n");
}
if( cpOnly && showCherrypicks ){
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS cpnodes(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO cpnodes SELECT childid FROM cherrypick;"
"INSERT OR IGNORE INTO cpnodes SELECT parentid FROM cherrypick;"
);
blob_append_sql(&cond, " AND event.objid IN cpnodes\n");
}
if( bisectLocal || zBisect!=0 ){
blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog)\n");
}
if( zYearMonth ){
char *zNext;
int bZulu = 0;
const char *zTZMod;
zYearMonth = timeline_expand_datetime(zYearMonth, &bZulu);
zYearMonth = mprintf("%.7s", zYearMonth);
if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){
zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');");
}
zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime>=julianday('%q-01',%Q)%s)",
zYearMonth, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0, "SELECT strftime('%%Y%%m%q','%q-01','+1 month');",
&"Z"[!bZulu], zYearMonth);
zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
zNewerButtonLabel = "Following month";
fossil_free(zNext);
}
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime<julianday('%q-01',%Q)%s)",
zYearMonth, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0, "SELECT strftime('%%Y%%m%q','%q-01','-1 month');",
&"Z"[!bZulu], zYearMonth);
zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
zOlderButtonLabel = "Previous month";
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday('%q-01',%Q)"
" AND event.mtime<julianday('%q-01',%Q,'+1 month')\n",
zYearMonth, zTZMod, zYearMonth, zTZMod);
nEntry = -1;
/* Adjust the zYearMonth for the title */
zYearMonth = mprintf("%z-01%s", zYearMonth, &"Z"[!bZulu]);
}
else if( zYearWeek ){
char *z, *zNext;
int bZulu = 0;
const char *zTZMod;
zYearWeek = timeline_expand_datetime(zYearWeek, &bZulu);
z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek);
if( z && z[0] ){
zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')",
zYearWeek);
zYearWeek = z;
}else{
if( strlen(zYearWeek)==7 ){
zYearWeekStart = db_text(0,
"SELECT date('%.4q-01-01','%+d days','weekday 1')",
zYearWeek, atoi(zYearWeek+5)*7-6);
}else{
zYearWeekStart = 0;
}
if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){
zYearWeekStart = db_text(0,
"SELECT date('now','-6 days','weekday 1');");
zYearWeek = db_text(0,
"SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')");
}
}
zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime>=julianday(%Q,%Q)%s)",
zYearWeekStart, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0, "SELECT strftime('%%Y%%W%q',%Q,'+7 day');",
&"Z"[!bZulu], zYearWeekStart);
zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
zNewerButtonLabel = "Following week";
fossil_free(zNext);
}
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime<julianday(%Q,%Q)%s)",
zYearWeekStart, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0, "SELECT strftime('%%Y%%W%q',%Q,'-7 days');",
&"Z"[!bZulu], zYearWeekStart);
zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
zOlderButtonLabel = "Previous week";
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday(%Q,%Q)"
" AND event.mtime<julianday(%Q,%Q,'+7 days')\n",
zYearWeekStart, zTZMod, zYearWeekStart, zTZMod);
nEntry = -1;
if( fossil_ui_localtime() && bZulu ){
zYearWeekStart = mprintf("%zZ", zYearWeekStart);
}
}
else if( zDay && timeline_is_datespan(zDay) ){
char *zNext;
char *zStart, *zEnd;
int nDay;
int bZulu = 0;
const char *zTZMod;
zEnd = db_text(0, "SELECT date(%Q)",
timeline_expand_datetime(zDay+9, &bZulu));
zStart = db_text(0, "SELECT date('%.4q-%.2q-%.2q')",
zDay, zDay+4, zDay+6);
nDay = db_int(0, "SELECT julianday(%Q)-julianday(%Q)", zEnd, zStart);
if( nDay==0 ){
zDay = &zDay[9];
goto single_ymd;
}
if( nDay<0 ){
char *zTemp = zEnd;
zEnd = zStart;
zStart = zTemp;
nDay = 1 - nDay;
}else{
nDay += 1;
}
zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
if( nDay>0 && db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime>=julianday(%Q,'1 day',%Q)%s)",
zEnd, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0,
"SELECT strftime('%%Y%%m%%d-',%Q,'%d days')||"
"strftime('%%Y%%m%%d%q',%Q,'%d day');",
zStart, nDay, &"Z"[!bZulu], zEnd, nDay);
zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
zNewerButtonLabel = mprintf("Following %d days", nDay);
fossil_free(zNext);
}
if( nDay>1 && db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime<julianday(%Q,'-1 day',%Q)%s)",
zStart, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0,
"SELECT strftime('%%Y%%m%%d-',%Q,'%d days')||"
"strftime('%%Y%%m%%d%q',%Q,'%d day');",
zStart, -nDay, &"Z"[!bZulu], zEnd, -nDay);
zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
zOlderButtonLabel = mprintf("Previous %d days", nDay);
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday(%Q,%Q)"
" AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
zStart, zTZMod, zEnd, zTZMod);
nEntry = -1;
if( fossil_ui_localtime() && bZulu ){
zDay = mprintf("%d days between %zZ and %zZ", nDay, zStart, zEnd);
}else{
zDay = mprintf("%d days between %z and %z", nDay, zStart, zEnd);
}
}
else if( zDay ){
char *zNext;
int bZulu = 0;
const char *zTZMod;
single_ymd:
bZulu = 0;
zDay = timeline_expand_datetime(zDay, &bZulu);
zDay = db_text(0, "SELECT date(%Q)", zDay);
if( zDay==0 || zDay[0]==0 ){
zDay = db_text(0, "SELECT date('now')");
}
zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime>=julianday(%Q,'+1 day',%Q)%s)",
zDay, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0,"SELECT strftime('%%Y%%m%%d%q',%Q,'+1 day');",
&"Z"[!bZulu], zDay);
zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
zNewerButtonLabel = "Following day";
fossil_free(zNext);
}
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime<julianday(%Q,'-1 day',%Q)%s)",
zDay, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0,"SELECT strftime('%%Y%%m%%d%q',%Q,'-1 day');",
&"Z"[!bZulu], zDay);
zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
zOlderButtonLabel = "Previous day";
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday(%Q,%Q)"
" AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
zDay, zTZMod, zDay, zTZMod);
nEntry = -1;
if( fossil_ui_localtime() && bZulu ){
zDay = mprintf("%zZ", zDay); /* Add Z suffix to day for the title */
}
}
else if( zNDays ){
nDays = atoi(zNDays);
if( nDays<1 ) nDays = 1;
blob_append_sql(&cond, " AND event.mtime>=julianday('now','-%d days') ",
nDays);
nEntry = -1;
|
| ︙ | ︙ | |||
2661 2662 2663 2664 2665 2666 2667 |
blob_append_sql(&cond, " AND %Q=strftime('%%Y',event.mtime) ",
zYear);
nEntry = -1;
}
if( zTagSql ){
db_multi_exec(
"CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
| | | > | < | < | < < < < < < < < | < < < < | | | | | | | | | | | | | | | > | > | | 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 |
blob_append_sql(&cond, " AND %Q=strftime('%%Y',event.mtime) ",
zYear);
nEntry = -1;
}
if( zTagSql ){
db_multi_exec(
"CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO selected_nodes\n"
" SELECT tagxref.rid FROM tagxref NATURAL JOIN tag\n"
" WHERE tagtype>0\n"
" AND %s", zTagSql/*safe-for-%s*/
);
if( zMark ){
/* If the t=release option is used with m=UUID, then also
** include the UUID check-in in the display list */
int ridMark = name_to_rid(zMark);
db_multi_exec(
"INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
}
add_extra_rids("selected_nodes",P("x"));
add_extra_rids("selected_nodes",P("sel1"));
add_extra_rids("selected_nodes",P("sel2"));
if( related==0 ){
blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
}else{
db_multi_exec(
"CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
"INSERT INTO related_nodes SELECT rid FROM selected_nodes;"
);
blob_append_sql(&cond, " AND blob.rid IN related_nodes");
/* The next two blob_appendf() calls add SQL that causes check-ins that
** are not part of the branch which are parents or children of the
** branch to be included in the report. These related check-ins are
** useful in helping to visualize what has happened on a quiescent
** branch that is infrequently merged with a much more activate branch.
*/
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes\n"
" SELECT pid FROM selected_nodes CROSS JOIN plink\n"
" WHERE selected_nodes.rid=plink.cid;"
);
if( related==1 ){
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes\n"
" SELECT cid FROM selected_nodes CROSS JOIN plink\n"
" WHERE selected_nodes.rid=plink.pid;"
);
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes\n"
" SELECT childid FROM selected_nodes CROSS JOIN cherrypick\n"
" WHERE selected_nodes.rid=cherrypick.parentid;"
);
}
}
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes\n"
" SELECT parentid FROM selected_nodes CROSS JOIN cherrypick\n"
" WHERE selected_nodes.rid=cherrypick.childid;"
);
}
if( (tmFlags & TIMELINE_UNHIDE)==0 ){
db_multi_exec(
"DELETE FROM related_nodes\n"
" WHERE rid IN (SELECT related_nodes.rid\n"
" FROM related_nodes, tagxref\n"
" WHERE tagid=%d AND tagtype>0\n"
" AND tagxref.rid=related_nodes.rid)",
TAG_HIDDEN
);
}
}
}
if( (zType[0]=='w' && !g.perm.RdWiki)
|| (zType[0]=='t' && !g.perm.RdTkt)
|
| ︙ | ︙ | |||
2820 2821 2822 2823 2824 2825 2826 |
zSearch, zSearch, zSearch);
}else{
blob_append_sql(&cond,
" AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
zSearch, zSearch);
}
}
| | | | | | | | < < < | | | 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 |
zSearch, zSearch, zSearch);
}else{
blob_append_sql(&cond,
" AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
zSearch, zSearch);
}
}
rBefore = symbolic_name_to_mtime(zBefore, &zBefore, 1);
rAfter = symbolic_name_to_mtime(zAfter, &zAfter, 0);
rCirca = symbolic_name_to_mtime(zCirca, &zCirca, 0);
blob_append_sql(&sql, "%s", blob_sql_text(&cond));
if( rAfter>0.0 ){
if( rBefore>0.0 ){
blob_append_sql(&sql,
" AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
" ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
nEntry = -1;
}else{
blob_append_sql(&sql,
" AND event.mtime>=%.17g\n ORDER BY event.mtime ASC",
rAfter-ONE_SECOND);
}
zCirca = 0;
url_add_parameter(&url, "c", 0);
}else if( rBefore>0.0 ){
blob_append_sql(&sql,
" AND event.mtime<=%.17g\n ORDER BY event.mtime DESC",
rBefore+ONE_SECOND);
zCirca = 0;
url_add_parameter(&url, "c", 0);
}else if( rCirca>0.0 ){
Blob sql2;
blob_init(&sql2, blob_sql_text(&sql), -1);
blob_append_sql(&sql2,
" AND event.mtime>=%f\n ORDER BY event.mtime ASC", rCirca);
if( nEntry>0 ){
blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2);
}
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\n ORDER BY event.mtime DESC",
rCirca
);
if( zMark==0 ) zMark = zCirca;
}else{
blob_append_sql(&sql, " ORDER BY event.mtime DESC");
}
if( nEntry>0 ) blob_append_sql(&sql, " LIMIT %d", nEntry);
db_multi_exec("%s", blob_sql_text(&sql));
n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
zPlural = n==1 ? "" : "s";
if( zYearMonth ){
blob_appendf(&desc, "%d %s%s for the month beginning %h",
n, zEType, zPlural, zYearMonth);
}else if( zYearWeek ){
blob_appendf(&desc, "%d %s%s for week %h beginning on %h",
n, zEType, zPlural, zYearWeek, zYearWeekStart);
}else if( zDay ){
blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay);
}else if( zNDays ){
|
| ︙ | ︙ | |||
2966 2967 2968 2969 2970 2971 2972 |
};
double rDate;
zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
}
if( zDate ){
| | | | 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 |
};
double rDate;
zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
}
if( zDate ){
rDate = symbolic_name_to_mtime(zDate, 0, 0);
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
rDate-ONE_SECOND, blob_sql_text(&cond))
){
zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0));
zOlderButtonLabel = "More";
}
free(zDate);
}
zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
}
if( zDate ){
rDate = symbolic_name_to_mtime(zDate, 0, 0);
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
rDate+ONE_SECOND, blob_sql_text(&cond))
){
zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0));
zNewerButtonLabel = "More";
|
| ︙ | ︙ | |||
3008 3009 3010 3011 3012 3013 3014 |
if( advancedMenu ){
style_submenu_entry("t", "Tag Filter:", -8, 0);
style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0);
}
}
blob_zero(&cond);
}
| | > | > | | 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 |
if( advancedMenu ){
style_submenu_entry("t", "Tag Filter:", -8, 0);
style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0);
}
}
blob_zero(&cond);
}
if( showSql ){
db_append_dml_to_blob(0);
@ <pre>%h(blob_str(&allSql))</pre>
blob_reset(&allSql);
}
if( search_restrict(SRCH_CKIN)!=0 ){
style_submenu_element("Search", "%R/search?y=c");
}
if( advancedMenu ){
style_submenu_element("Basic", "%s",
url_render(&url, "advm", "0", "udc", "1"));
}else{
style_submenu_element("Advanced", "%s",
url_render(&url, "advm", "1", "udc", "1"));
}
if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
if( useDividers && zMark && zMark[0] ){
double r = symbolic_name_to_mtime(zMark, 0, 0);
if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
}
blob_zero(&sql);
if( PB("oldestfirst") ){
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
}else{
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
|
| ︙ | ︙ | |||
3067 3068 3069 3070 3071 3072 3073 |
}
if( zNewerButton ){
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
@ ↑</a>
}
cgi_check_for_malice();
| > > > > > > > > > > > > > > | | > > > > | 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 |
}
if( zNewerButton ){
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
@ ↑</a>
}
cgi_check_for_malice();
{
Matcher *pLeftBranch;
const char *zPattern = P("sl");
if( zPattern!=0 ){
MatchStyle ms;
if( zMatchStyle!=0 ){
ms = matchStyle;
}else{
ms = strpbrk(zPattern,"*[?")!=0 ? MS_GLOB : MS_BRLIST;
}
pLeftBranch = match_create(ms,zPattern);
}else{
pLeftBranch = match_create(matchStyle, zBrName?zBrName:zTagName);
}
www_print_timeline(&q, tmFlags, zThisUser, zThisTag, pLeftBranch,
selectedRid, secondaryRid, 0);
match_free(pLeftBranch);
}
db_finalize(&q);
if( zOlderButton ){
@ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
@ ↓</a>
}
document_emit_js(/*handles pikchrs rendered above*/);
blob_reset(&sql);
blob_reset(&desc);
style_finish_page();
}
/*
** Translate a timeline entry into the printable format by
** converting every %-substitutions as follows:
**
|
| ︙ | ︙ | |||
3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 |
fossil_print("+++ end of timeline (%d) +++\n", nEntry);
}else{
fossil_print("+++ no more data (%d) +++\n", nEntry);
}
}
if( fchngQueryInit ) db_finalize(&fchngQuery);
}
/*
** Return a pointer to a static string that forms the basis for
** a timeline query for display on a TTY.
*/
const char *timeline_query_for_tty(void){
static const char zBaseSql[] =
@ SELECT
@ blob.rid AS rid,
@ uuid,
@ datetime(event.mtime,toLocal()) AS mDateTime,
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 |
fossil_print("+++ end of timeline (%d) +++\n", nEntry);
}else{
fossil_print("+++ no more data (%d) +++\n", nEntry);
}
}
if( fchngQueryInit ) db_finalize(&fchngQuery);
}
/*
** wiki_to_text(TEXT)
**
** Return a text rendering of Fossil-Wiki TEXT, intended for display
** on a timeline. The timeline-plaintext and timeline-hard-newlines
** settings are considered when doing this rendering.
*/
static void wiki_to_text_sqlfunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zIn, *zOut;
int nIn, nOut;
Blob in, html, txt;
zIn = (const char*)sqlite3_value_text(argv[0]);
if( zIn==0 ) return;
nIn = sqlite3_value_bytes(argv[0]);
blob_init(&in, zIn, nIn);
blob_init(&html, 0, 0);
wiki_convert(&in, &html, wiki_convert_flags(0));
blob_reset(&in);
blob_init(&txt, 0, 0);
html_to_plaintext(blob_str(&html), &txt, 0);
blob_reset(&html);
nOut = blob_size(&txt);
zOut = blob_str(&txt);
while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; }
while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; }
sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT);
blob_reset(&txt);
}
/*
** Return a pointer to a static string that forms the basis for
** a timeline query for display on a TTY.
*/
const char *timeline_query_for_tty(void){
static int once = 0;
static const char zBaseSql[] =
@ SELECT
@ blob.rid AS rid,
@ uuid,
@ datetime(event.mtime,toLocal()) AS mDateTime,
@ wiki_to_text(coalesce(ecomment,comment))
@ || ' (user: ' || coalesce(euser,user,'?')
@ || (SELECT case when length(x)>0 then ' tags: ' || 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 comment,
|
| ︙ | ︙ | |||
3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 |
@ 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'
;
return zBaseSql;
}
/*
** Return true if the input string is a date in the ISO 8601 format:
** YYYY-MM-DD.
*/
| > > > > > | 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 |
@ 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'
;
if( !once && g.db ){
once = 1;
sqlite3_create_function(g.db, "wiki_to_text", 1, SQLITE_UTF8, 0,
wiki_to_text_sqlfunc, 0, 0);
}
return zBaseSql;
}
/*
** Return true if the input string is a date in the ISO 8601 format:
** YYYY-MM-DD.
*/
|
| ︙ | ︙ | |||
3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 |
* zFilePattern. */
zFilePattern = 0;
}
}
if( mode==TIMELINE_MODE_NONE ) mode = TIMELINE_MODE_BEFORE;
blob_zero(&sql);
blob_append(&sql, timeline_query_for_tty(), -1);
blob_append_sql(&sql, "\n AND event.mtime %s %s",
( mode==TIMELINE_MODE_BEFORE ||
mode==TIMELINE_MODE_PARENTS ) ? "<=" : ">=", zDate /*safe-for-%s*/
);
/* When zFilePattern is specified, compute complete ancestry;
* limit later at print_timeline() */
if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){
db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
if( mode==TIMELINE_MODE_CHILDREN ){
compute_descendants(objid, (zFilePattern ? 0 : n));
}else{
compute_ancestors(objid, (zFilePattern ? 0 : n), 0, 0);
}
blob_append_sql(&sql, "\n AND blob.rid IN ok");
}
| > > > > > > > < < < | 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 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 |
* zFilePattern. */
zFilePattern = 0;
}
}
if( mode==TIMELINE_MODE_NONE ) mode = TIMELINE_MODE_BEFORE;
blob_zero(&sql);
if( mode==TIMELINE_MODE_AFTER ){
/* Extra outer select to get older rows in reverse order */
blob_append(&sql, "SELECT *\nFROM (", -1);
}
blob_append(&sql, timeline_query_for_tty(), -1);
blob_append_sql(&sql, "\n AND event.mtime %s %s",
( mode==TIMELINE_MODE_BEFORE ||
mode==TIMELINE_MODE_PARENTS ) ? "<=" : ">=", zDate /*safe-for-%s*/
);
if( zType && (zType[0]!='a') ){
blob_append_sql(&sql, "\n AND event.type=%Q ", zType);
}
/* When zFilePattern is specified, compute complete ancestry;
* limit later at print_timeline() */
if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){
db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
if( mode==TIMELINE_MODE_CHILDREN ){
compute_descendants(objid, (zFilePattern ? 0 : n));
}else{
compute_ancestors(objid, (zFilePattern ? 0 : n), 0, 0);
}
blob_append_sql(&sql, "\n AND blob.rid IN ok");
}
if( zFilePattern ){
blob_append(&sql,
"\n AND EXISTS(SELECT 1 FROM mlink\n"
" WHERE mlink.mid=event.objid\n"
" AND mlink.fnid IN ", -1);
if( filenames_are_case_sensitive() ){
blob_append_sql(&sql,
|
| ︙ | ︙ | |||
3711 3712 3713 3714 3715 3716 3717 |
" AND e.comment LIKE '_checkin/%%'\n"
" LEFT JOIN tagxref tx ON tx.rid=b.rid AND tx.tagid=%d\n"
" WHERE tx.value='%q'\n"
")\n" /* No merge closures */
" AND (tagxref.value IS NULL OR tagxref.value='%q')",
zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
}
| > > > > > > > > > > > > | > | 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 |
" AND e.comment LIKE '_checkin/%%'\n"
" LEFT JOIN tagxref tx ON tx.rid=b.rid AND tx.tagid=%d\n"
" WHERE tx.value='%q'\n"
")\n" /* No merge closures */
" AND (tagxref.value IS NULL OR tagxref.value='%q')",
zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
}
if( mode==TIMELINE_MODE_AFTER ){
int lim = n;
if( n == 0 ){
lim = -1; /* 0 means no limit */
}else if( n < 0 ){
lim = -n;
}
/* Complete the above outer select. */
blob_append_sql(&sql,
"\nORDER BY event.mtime LIMIT %d) t ORDER BY t.mDateTime DESC;", lim);
}else{
blob_append_sql(&sql, "\nORDER BY event.mtime DESC");
}
if( iOffset>0 ){
/* Don't handle LIMIT here, otherwise print_timeline()
* will not determine the end-marker correctly! */
blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
}
if( showSql ){
fossil_print("%s\n", blob_str(&sql));
|
| ︙ | ︙ | |||
3737 3738 3739 3740 3741 3742 3743 |
** day of the year for various years in the history of the project.
**
** Query parameters:
**
** today=DATE Use DATE as today's date
*/
void thisdayinhistory_page(void){
| | > | | 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 |
** day of the year for various years in the history of the project.
**
** Query parameters:
**
** today=DATE Use DATE as today's date
*/
void thisdayinhistory_page(void){
static int aYearsAgo[] = { 1,2,3,4,5,10,15,20,25,30,40,50,75,100 };
const char *zToday;
char *zStartOfProject;
int i;
Stmt q;
char *z;
int bZulu = 0;
login_check_credentials();
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) ){
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
return;
}
style_set_current_feature("timeline");
style_header("Today In History");
zToday = (char*)P("today");
if( zToday ){
zToday = timeline_expand_datetime(zToday, &bZulu);
if( !fossil_isdate(zToday) ) zToday = 0;
}
if( zToday==0 ){
zToday = db_text(0, "SELECT date('now',toLocal())");
}
@ <h1>This Day In History For %h(zToday)</h1>
z = db_text(0, "SELECT date(%Q,'-1 day')", zToday);
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
186 187 188 189 190 191 192 193 194 |
** Otherwise, db_reveal() is a no-op and the content remains
** obscured.
*/
static void initializeVariablesFromDb(void){
const char *zName;
Stmt q;
int i, n, size, j;
zName = PD("name","-none-");
| > | > > > > > > > | | | 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 |
** Otherwise, db_reveal() is a no-op and the content remains
** obscured.
*/
static void initializeVariablesFromDb(void){
const char *zName;
Stmt q;
int i, n, size, j;
const char *zCTimeColumn = haveTicketCTime ? "tkt_ctime" : "tkt_mtime";
zName = PD("name","-none-");
db_prepare(&q, "SELECT datetime(tkt_mtime,toLocal()) AS tkt_datetime, "
"datetime(%s,toLocal()) AS tkt_datetime_creation, "
"julianday('now') - tkt_mtime, "
"julianday('now') - %s, *"
" FROM ticket WHERE tkt_uuid GLOB '%q*'",
zCTimeColumn/*safe-for-%s*/, zCTimeColumn/*safe-for-%s*/,
zName);
if( db_step(&q)==SQLITE_ROW ){
n = db_column_count(&q);
for(i=0; i<n; i++){
const char *zVal = db_column_text(&q, i);
const char *zName = db_column_name(&q, i);
char *zRevealed = 0;
if( zVal==0 ){
zVal = "";
}else if( strncmp(zName, "private_", 8)==0 ){
zVal = zRevealed = db_reveal(zVal);
}
if( (j = fieldId(zName))>=0 ){
aField[j].zValue = mprintf("%s", zVal);
}else if( memcmp(zName, "tkt_", 4)==0 && Th_Fetch(zName, &size)==0 ){
/* TICKET table columns that begin with "tkt_" are always safe */
Th_Store(zName, zVal);
}
free(zRevealed);
}
Th_Store("tkt_mage", human_readable_age(db_column_double(&q, 2)));
Th_Store("tkt_cage", human_readable_age(db_column_double(&q, 3)));
}
db_finalize(&q);
for(i=0; i<nField; i++){
if( Th_Fetch(aField[i].zName, &size)==0 ){
Th_StoreUnsafe(aField[i].zName, aField[i].zValue);
}
}
}
/*
** Transfer all CGI parameters to variables in the interpreter.
*/
static void initializeVariablesFromCGI(void){
int i;
const char *z;
for(i=0; (z = cgi_parameter_name(i))!=0; i++){
Th_StoreUnsafe(z, P(z));
}
}
/*
** Information about a single J-card
*/
struct jCardInfo {
|
| ︙ | ︙ | |||
763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 |
}else{
showTimeline = 0;
}
}
if( !showTimeline && g.perm.Hyperlink ){
style_submenu_element("Timeline", "%R/info/%T", zUuid);
}
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
ticket_init();
initializeVariablesFromCGI();
getAllTicketFields();
initializeVariablesFromDb();
zScript = ticket_viewpage_code();
if( P("showfields")!=0 ) showAllFields();
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1);
safe_html_context(DOCSRC_TICKET);
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
| > > > < < < | 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 |
}else{
showTimeline = 0;
}
}
if( !showTimeline && g.perm.Hyperlink ){
style_submenu_element("Timeline", "%R/info/%T", zUuid);
}
zFullName = db_text(0,
"SELECT tkt_uuid FROM ticket"
" WHERE tkt_uuid GLOB '%q*'", zUuid);
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
ticket_init();
initializeVariablesFromCGI();
getAllTicketFields();
initializeVariablesFromDb();
zScript = ticket_viewpage_code();
if( P("showfields")!=0 ) showAllFields();
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1);
safe_html_context(DOCSRC_TICKET);
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
if( zFullName ){
attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
}
style_finish_page();
}
|
| ︙ | ︙ | |||
807 808 809 810 811 812 813 |
int idx;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "append_field FIELD STRING");
}
if( g.thTrace ){
Th_Trace("append_field %#h {%#h}<br>\n",
| | | | | 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
int idx;
if( argc!=3 ){
return Th_WrongNumArgs(interp, "append_field FIELD STRING");
}
if( g.thTrace ){
Th_Trace("append_field %#h {%#h}<br>\n",
TH1_LEN(argl[1]), argv[1], TH1_LEN(argl[2]), argv[2]);
}
for(idx=0; idx<nField; idx++){
if( memcmp(aField[idx].zName, argv[1], TH1_LEN(argl[1]))==0
&& aField[idx].zName[TH1_LEN(argl[1])]==0 ){
break;
}
}
if( idx>=nField ){
Th_ErrorMessage(g.interp, "no such TICKET column: ", argv[1], argl[1]);
return TH_ERROR;
}
|
| ︙ | ︙ | |||
927 928 929 930 931 932 933 934 935 936 937 938 939 940 |
}
for(i=0; i<nField; i++){
const char *zValue;
int nValue;
if( aField[i].zAppend ) continue;
zValue = Th_Fetch(aField[i].zName, &nValue);
if( zValue ){
while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; }
if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0)
|| memcmp(zValue, aField[i].zValue, nValue)!=0
||(int)strlen(aField[i].zValue)!=nValue
){
if( memcmp(aField[i].zName, "private_", 8)==0 ){
zValue = db_conceal(zValue, nValue);
| > | 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 |
}
for(i=0; i<nField; i++){
const char *zValue;
int nValue;
if( aField[i].zAppend ) continue;
zValue = Th_Fetch(aField[i].zName, &nValue);
if( zValue ){
nValue = TH1_LEN(nValue);
while( nValue>0 && fossil_isspace(zValue[nValue-1]) ){ nValue--; }
if( ((aField[i].mUsed & USEDBY_TICKETCHNG)!=0 && nValue>0)
|| memcmp(zValue, aField[i].zValue, nValue)!=0
||(int)strlen(aField[i].zValue)!=nValue
){
if( memcmp(aField[i].zName, "private_", 8)==0 ){
zValue = db_conceal(zValue, nValue);
|
| ︙ | ︙ | |||
1021 1022 1023 1024 1025 1026 1027 1028 |
initializeVariablesFromDb();
if( g.zPath[0]=='d' ) showAllFields();
form_begin(0, "%R/%s", g.zPath);
if( P("date_override") && g.perm.Setup ){
@ <input type="hidden" name="date_override" value="%h(P("date_override"))">
}
zScript = ticket_newpage_code();
if( g.zLogin && g.zLogin[0] ){
| > < < < | | | > > > | > | 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 |
initializeVariablesFromDb();
if( g.zPath[0]=='d' ) showAllFields();
form_begin(0, "%R/%s", g.zPath);
if( P("date_override") && g.perm.Setup ){
@ <input type="hidden" name="date_override" value="%h(P("date_override"))">
}
zScript = ticket_newpage_code();
Th_Store("private_contact", "");
if( g.zLogin && g.zLogin[0] ){
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.zLogin);
if( uid ){
char * zEmail =
db_text(0, "SELECT find_emailaddr(info) FROM user WHERE uid=%d",
uid);
if( zEmail ){
Th_StoreUnsafe("private_contact", zEmail);
fossil_free(zEmail);
}
}
}
Th_StoreUnsafe("login", login_name());
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
(void*)&zNewUuid, 0);
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br>\n", -1);
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
if( P("submitandnew") ){
cgi_redirect(mprintf("%R/tktnew/%s", zNewUuid));
}else{
cgi_redirect(mprintf("%R/tktview/%s", zNewUuid));
}
return;
}
captcha_generate(0);
@ </form>
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
style_finish_page();
}
|
| ︙ | ︙ | |||
1107 1108 1109 1110 1111 1112 1113 | getAllTicketFields(); initializeVariablesFromCGI(); initializeVariablesFromDb(); if( g.zPath[0]=='d' ) showAllFields(); form_begin(0, "%R/%s", g.zPath); @ <input type="hidden" name="name" value="%s(zName)"> zScript = ticket_editpage_code(); | | | 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 |
getAllTicketFields();
initializeVariablesFromCGI();
initializeVariablesFromDb();
if( g.zPath[0]=='d' ) showAllFields();
form_begin(0, "%R/%s", g.zPath);
@ <input type="hidden" name="name" value="%s(zName)">
zScript = ticket_editpage_code();
Th_StoreUnsafe("login", login_name());
Th_Store("date", db_text(0, "SELECT datetime('now')"));
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br>\n", -1);
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
cgi_redirect(mprintf("%R/tktview/%s", zName));
return;
|
| ︙ | ︙ |
Changes to src/tktsetup.c.
| ︙ | ︙ | |||
123 124 125 126 127 128 129 |
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
style_set_current_feature("tktsetup");
| | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
style_set_current_feature("tktsetup");
if( P("setup") ){
cgi_redirect("tktsetup");
}
isSubmit = P("submit")!=0;
z = P("x");
if( z==0 ){
z = db_get(zDbField, zDfltValue);
}
|
| ︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 171 172 173 174 175 | @ </p></blockquote> @ </div></form> @ <hr> @ <h2>Default %s(zTitle)</h2> @ <blockquote><pre> @ %h(zDfltValue) @ </pre></blockquote> style_finish_page(); } /* ** WEBPAGE: tktsetup_tab ** Administrative page for defining the "ticket" table used ** to hold ticket information. | > | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
@ </p></blockquote>
@ </div></form>
@ <hr>
@ <h2>Default %s(zTitle)</h2>
@ <blockquote><pre>
@ %h(zDfltValue)
@ </pre></blockquote>
style_submenu_element("Back", "%R/tktsetup");
style_finish_page();
}
/*
** WEBPAGE: tktsetup_tab
** Administrative page for defining the "ticket" table used
** to hold ticket information.
|
| ︙ | ︙ | |||
299 300 301 302 303 304 305 |
30
);
}
static const char zDefaultNew[] =
@ <th1>
@ if {![info exists mutype]} {set mutype Markdown}
| | | 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
30
);
}
static const char zDefaultNew[] =
@ <th1>
@ if {![info exists mutype]} {set mutype Markdown}
@ if {[info exists submit] || [info exists submitandnew]} {
@ set status Open
@ if {$mutype eq "HTML"} {
@ set mimetype "text/html"
@ } elseif {$mutype eq "Wiki"} {
@ set mimetype "text/x-fossil-wiki"
@ } elseif {$mutype eq "Markdown"} {
@ set mimetype text/x-markdown
|
| ︙ | ︙ | |||
347 348 349 350 351 352 353 354 355 356 357 358 359 360 | @ <tr> @ <td align="right">Severity:</td> @ <td align="left"><th1>combobox severity $severity_choices 1</th1></td> @ <td align="left">How debilitating is the problem? How badly does the problem @ affect the operation of the product?</td> @ </tr> @ @ <tr> @ <td align="right">EMail:</td> @ <td align="left"> @ <input name="private_contact" value="$<private_contact>" size="30"> @ </td> @ <td align="left"><u>Not publicly visible</u> @ Used by developers to contact you with questions.</td> | > > > > > > > > > > > > > > > > > > | 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 |
@ <tr>
@ <td align="right">Severity:</td>
@ <td align="left"><th1>combobox severity $severity_choices 1</th1></td>
@ <td align="left">How debilitating is the problem? How badly does the problem
@ affect the operation of the product?</td>
@ </tr>
@
@ <th1>
@ if {[capexpr {w}]} {
@ html {<tr><td class="tktDspLabel">Priority:</td><td>}
@ combobox priority $priority_choices 1
@ html {
@ <td align="left">How important is the affected functionality?</td>
@ </td></tr>
@ }
@
@ html {<tr><td class="tktDspLabel">Subsystem:</td><td>}
@ combobox subsystem $subsystem_choices 1
@ html {
@ <td align="left">Which subsystem is affected?</td>
@ </td></tr>
@ }
@ }
@ </th1>
@
@ <tr>
@ <td align="right">EMail:</td>
@ <td align="left">
@ <input name="private_contact" value="$<private_contact>" size="30">
@ </td>
@ <td align="left"><u>Not publicly visible</u>
@ Used by developers to contact you with questions.</td>
|
| ︙ | ︙ | |||
403 404 405 406 407 408 409 | @ @ <th1>enable_output [info exists preview]</th1> @ <tr> @ <td><td align="left"> @ <input type="submit" name="submit" value="Submit"> @ </td> @ <td align="left">After filling in the information above, press this | | > > > > > > > > | | 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 | @ @ <th1>enable_output [info exists preview]</th1> @ <tr> @ <td><td align="left"> @ <input type="submit" name="submit" value="Submit"> @ </td> @ <td align="left">After filling in the information above, press this @ button to create the new ticket.</td> @ </tr> @ @ <tr> @ <td><td align="left"> @ <input type="submit" name="submitandnew" value="Submit and New"> @ </td> @ <td align="left">Create the new ticket and start another @ ticket form with the inputs.</td> @ </tr> @ <th1>enable_output 1</th1> @ @ <tr> @ <td><td align="left"> @ <input type="submit" name="cancel" value="Cancel"> @ </td> @ <td>Abandon and forget this ticket.</td> @ </tr> @ </table> ; /* ** Return the code used to generate the new ticket page */ |
| ︙ | ︙ | |||
452 453 454 455 456 457 458 |
@ <table cellpadding="5">
@ <tr><td class="tktDspLabel">Ticket Hash:</td>
@ <th1>
@ if {[info exists tkt_uuid]} {
@ html "<td class='tktDspValue' colspan='3'>"
@ copybtn hash-tk 0 $tkt_uuid 2
@ if {[hascap s]} {
| | > > > > > > > | 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 |
@ <table cellpadding="5">
@ <tr><td class="tktDspLabel">Ticket Hash:</td>
@ <th1>
@ if {[info exists tkt_uuid]} {
@ html "<td class='tktDspValue' colspan='3'>"
@ copybtn hash-tk 0 $tkt_uuid 2
@ if {[hascap s]} {
@ puts " ($tkt_id)"
@ }
@ html "</td></tr>\n"
@ } else {
@ if {[hascap s]} {
@ html "<td class='tktDspValue' colspan='3'>Deleted "
@ html "(0)</td></tr>\n"
@ } else {
@ html "<td class='tktDspValue' colspan='3'>Deleted</td></tr>\n"
@ }
@ }
@
@ if {[capexpr {n}]} {
@ submenu link "Copy Ticket" /tktnew/$tkt_uuid
@ }
@ if {[capexpr {nk}]} {
@ submenu link "Edit Wiki" /wikiedit?name=ticket/$tkt_uuid
@ }
@ </th1>
@ <tr><td class="tktDspLabel">Title:</td>
@ <td class="tktDspValue" colspan="3">
@ $<title>
@ </td></tr>
@ <tr><td class="tktDspLabel">Status:</td><td class="tktDspValue">
@ $<status>
|
| ︙ | ︙ | |||
489 490 491 492 493 494 495 |
@ </td>
@ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue">
@ $<resolution>
@ </td></tr>
@ <tr><td class="tktDspLabel">Last Modified:</td><td class="tktDspValue">
@ <th1>
@ if {[info exists tkt_datetime]} {
| | > > > > > > > > > > > > > > | > < > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > | 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 |
@ </td>
@ <td class="tktDspLabel">Resolution:</td><td class="tktDspValue">
@ $<resolution>
@ </td></tr>
@ <tr><td class="tktDspLabel">Last Modified:</td><td class="tktDspValue">
@ <th1>
@ if {[info exists tkt_datetime]} {
@ puts $tkt_datetime
@ }
@ if {[info exists tkt_mage]} {
@ html "<br>[htmlize $tkt_mage] ago"
@ }
@ </th1>
@ </td>
@ <td class="tktDspLabel">Created:</td><td class="tktDspValue">
@ <th1>
@ if {[info exists tkt_datetime_creation]} {
@ puts $tkt_datetime_creation
@ }
@ if {[info exists tkt_cage]} {
@ html "<br>[htmlize $tkt_cage] ago"
@ }
@ </th1>
@ </td></tr>
@ <th1>enable_output [hascap e]</th1>
@ <tr>
@ <td class="tktDspLabel">Contact:</td><td class="tktDspValue" colspan="3">
@ $<private_contact>
@ </td>
@ </tr>
@ <th1>enable_output 1</th1>
@ <tr><td class="tktDspLabel">Version Found In:</td>
@ <td colspan="3" valign="top" class="tktDspValue">
@ <th1>
@ set versionlink ""
@ set urlfoundin [httpize $foundin]
@ set tagpattern {^[-0-9A-Za-z_\\.]+$}
@ if [regexp $tagpattern $foundin] {
@ query {SELECT count(*) AS match FROM tag
@ WHERE tagname=concat('sym-',$foundin)} {
@ if {$match} {set versionlink "timeline?t=$urlfoundin"}
@ }
@ }
@ set hashpattern {^[0-9a-f]+$}
@ if [regexp $hashpattern $foundin] {
@ set pattern $foundin*
@ query {SELECT count(*) AS match FROM blob WHERE uuid GLOB $pattern} {
@ if {$match} {set versionlink "info/$urlfoundin"}
@ }
@ }
@ if {$versionlink eq ""} {
@ puts $foundin
@ } else {
@ html "<a href=\""
@ puts $versionlink
@ html "\">"
@ puts $foundin
@ html "</a>"
@ }
@ </th1>
@ </td></tr>
@ </table>
@
@ <th1>
@ wiki_assoc "ticket" $tkt_uuid
@ </th1>
@
@ <table cellpadding="5" style="min-width:100%">
@ <th1>
@ if {[info exists comment]} {
@ if {[string length $comment]>10} {
@ html {
@ <tr><td class="tktDspLabel">Description:</td></tr>
@ <tr><td colspan="5" class="tktDspValue">
@ }
|
| ︙ | ︙ | |||
529 530 531 532 533 534 535 |
@ mimetype as xmimetype, icomment AS xcomment,
@ username AS xusername
@ FROM ticketchng
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
@ if {$seenRow} {
@ html "<hr>\n"
@ } else {
| | > | | | | | 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 |
@ mimetype as xmimetype, icomment AS xcomment,
@ username AS xusername
@ FROM ticketchng
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
@ if {$seenRow} {
@ html "<hr>\n"
@ } else {
@ html "<tr><td class='tktDspLabel' style='text-align:left'>\n"
@ html "User Comments:</td></tr>\n"
@ html "<tr><td colspan='5' class='tktDspValue'>\n"
@ set seenRow 1
@ }
@ html "<span class='tktDspCommenter'>"
@ puts $xlogin
@ if {$xlogin ne $xusername && [string length $xusername]>0} {
@ puts " (claiming to be $xusername)"
@ }
@ puts " added on $xdate:"
@ html "</span>\n"
@ if {$alwaysPlaintext || $xmimetype eq "text/plain"} {
@ set r [randhex]
@ if {$xmimetype ne "text/plain"} {puts "($xmimetype)\n"}
@ wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n"
@ } elseif {$xmimetype eq "text/x-fossil-wiki"} {
@ wiki "<p>\n[string trimright $xcomment]\n</p>\n"
@ } elseif {$xmimetype eq "text/x-markdown"} {
@ html [lindex [markdown $xcomment] 1]
@ } elseif {$xmimetype eq "text/html"} {
@ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
|
| ︙ | ︙ | |||
703 704 705 706 707 708 709 710 711 712 713 714 715 716 |
@ <tr>
@ <td align="right">
@ <input type="submit" name="cancel" value="Cancel">
@ </td>
@ <td>Abandon this edit</td>
@ </tr>
@
@ </table>
;
/*
** Return the code used to generate the edit ticket page
*/
const char *ticket_editpage_code(void){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
@ <tr>
@ <td align="right">
@ <input type="submit" name="cancel" value="Cancel">
@ </td>
@ <td>Abandon this edit</td>
@ </tr>
@
@ <th1>
@ set seenRow 0
@ set alwaysPlaintext [info exists plaintext]
@ query {SELECT datetime(tkt_mtime) AS xdate, login AS xlogin,
@ mimetype as xmimetype, icomment AS xcomment,
@ username AS xusername
@ FROM ticketchng
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
@ if {$seenRow} {
@ html "<hr>\n"
@ } else {
@ html "<tr><td colspan='2'><hr></td></tr>\n"
@ html "<tr><td colspan='2' class='tktDspLabel' style='text-align:left'>\n"
@ html "Previous User Comments:</td></tr>\n"
@ html "<tr><td colspan='2' class='tktDspValue'>\n"
@ set seenRow 1
@ }
@ html "<span class='tktDspCommenter'>"
@ puts $xlogin
@ if {$xlogin ne $xusername && [string length $xusername]>0} {
@ puts " (claiming to be $xusername)"
@ }
@ puts " added on $xdate:"
@ html "</span>\n"
@ if {$alwaysPlaintext || $xmimetype eq "text/plain"} {
@ set r [randhex]
@ if {$xmimetype ne "text/plain"} {puts "($xmimetype)\n"}
@ wiki "<verbatim-$r>[string trimright $xcomment]</verbatim-$r>\n"
@ } elseif {$xmimetype eq "text/x-fossil-wiki"} {
@ wiki "<p>\n[string trimright $xcomment]\n</p>\n"
@ } elseif {$xmimetype eq "text/x-markdown"} {
@ html [lindex [markdown $xcomment] 1]
@ } elseif {$xmimetype eq "text/html"} {
@ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
@ } else {
@ set r [randhex]
@ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
@ }
@ }
@ if {$seenRow} {html "</td></tr>\n"}
@ </th1>
@
@ </table>
;
/*
** Return the code used to generate the edit ticket page
*/
const char *ticket_editpage_code(void){
|
| ︙ | ︙ | |||
801 802 803 804 805 806 807 |
@ 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 '#',
| > | | 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 |
@ 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_ctime) AS 'created',
@ datetime(tkt_mtime) AS 'modified',
@ type,
@ status,
@ subsystem,
@ title,
@ comment AS '_comments'
@ FROM ticket
;
|
| ︙ | ︙ | |||
935 936 937 938 939 940 941 942 943 944 | @ <hr> @ <p> @ <input type="submit" name="submit" value="Apply Changes"> @ <input type="submit" name="setup" value="Cancel"> @ </p> @ </div></form> db_end_transaction(0); style_finish_page(); } | > | 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 |
@ <hr>
@ <p>
@ <input type="submit" name="submit" value="Apply Changes">
@ <input type="submit" name="setup" value="Cancel">
@ </p>
@ </div></form>
db_end_transaction(0);
style_submenu_element("Back", "%R/tktsetup");
style_finish_page();
}
|
Changes to src/undo.c.
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
blob_zero(&new);
old_exists = db_column_int(&q, 1);
old_exe = db_column_int(&q, 2);
if( old_exists ){
db_ephemeral_blob(&q, 0, &new);
}
if( file_unsafe_in_tree_path(zFullname) ){
| | | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
blob_zero(&new);
old_exists = db_column_int(&q, 1);
old_exe = db_column_int(&q, 2);
if( old_exists ){
db_ephemeral_blob(&q, 0, &new);
}
if( file_unsafe_in_tree_path(zFullname) ){
/* do nothing with this unsafe file */
}else if( old_exists ){
if( new_exists ){
fossil_print("%s %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
}else{
fossil_print("NEW %s\n", zPathname);
}
if( new_exists && (new_link || old_link) ){
|
| ︙ | ︙ |
Changes to src/unversioned.c.
| ︙ | ︙ | |||
216 217 218 219 220 221 222 |
if( fossil_isspace(zName[0]) ) return 1;
zName++;
}
return 0;
}
/*
| | | | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
if( fossil_isspace(zName[0]) ) return 1;
zName++;
}
return 0;
}
/*
** COMMAND: uv# abbrv-subcom
** COMMAND: unversioned abbrv-subcom
**
** Usage: %fossil unversioned SUBCOMMAND ARGS...
** or: %fossil uv SUBCOMMAND ARGS..
**
** Unversioned files (UV-files) are artifacts that are synced and are available
** for download but which do not preserve history. Only the most recent version
** of each UV-file is retained. Changes to an UV-file are permanent and cannot
|
| ︙ | ︙ | |||
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 | ** the name to be different in the repository versus ** what appears on disk, but it only allows adding ** a single file at a time. ** ** cat FILE ... Concatenate the content of FILEs to stdout. ** ** edit FILE Bring up FILE in a text editor for modification. ** ** export FILE OUTPUT Write the content of FILE into OUTPUT on disk ** ** list | ls Show all unversioned files held in the local ** repository. ** ** Options: ** --glob PATTERN Show only files that match ** --like PATTERN Show only files that match ** -l Show additional details for ** files that match. Implied ** when 'list' is used. ** ** revert ?URL? Restore the state of all unversioned files in the ** local repository to match the remote repository ** URL. ** ** Options: ** -v|--verbose Extra diagnostic output ** -n|--dry-run Show what would have happened ** ** remove|rm|delete FILE ... ** Remove unversioned files from the local repository. ** Changes are not pushed to other repositories until ** the next sync. ** ** Options: ** --glob PATTERN Remove files that match ** --like PATTERN Remove files that match ** ** sync ?URL? Synchronize the state of all unversioned files with ** the remote repository URL. The most recent version ** of each file is propagated to all repositories and ** all prior versions are permanently forgotten. ** The remote account requires the 'y' capability. ** ** Options: ** -v|--verbose Extra diagnostic output ** -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 | > > > > | 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 | ** the name to be different in the repository versus ** what appears on disk, but it only allows adding ** a single file at a time. ** ** cat FILE ... Concatenate the content of FILEs to stdout. ** ** edit FILE Bring up FILE in a text editor for modification. ** Options: ** --editor NAME Name of the text editor to use ** ** export FILE OUTPUT Write the content of FILE into OUTPUT on disk ** ** list | ls Show all unversioned files held in the local ** repository. ** ** Options: ** --glob PATTERN Show only files that match ** --like PATTERN Show only files that match ** -l Show additional details for ** files that match. Implied ** when 'list' is used. ** ** revert ?URL? Restore the state of all unversioned files in the ** local repository to match the remote repository ** URL. ** ** Options: ** -v|--verbose Extra diagnostic output ** -n|--dry-run Show what would have happened ** --proxy PROXY Use the specified HTTP proxy ** ** remove|rm|delete FILE ... ** Remove unversioned files from the local repository. ** Changes are not pushed to other repositories until ** the next sync. ** ** Options: ** --glob PATTERN Remove files that match ** --like PATTERN Remove files that match ** ** sync ?URL? Synchronize the state of all unversioned files with ** the remote repository URL. The most recent version ** of each file is propagated to all repositories and ** all prior versions are permanently forgotten. ** The remote account requires the 'y' capability. ** ** Options: ** -v|--verbose Extra diagnostic output ** -n|--dry-run Show what would have happened ** --proxy PROXY Use the specified HTTP proxy ** ** 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 |
| ︙ | ︙ | |||
357 358 359 360 361 362 363 |
}else if( strncmp(zCmd, "edit", nCmd)==0 ){
const char *zEditor; /* Name of the text-editor command */
const char *zTFile; /* Temporary file */
const char *zUVFile; /* Name of the unversioned file */
char *zCmd; /* Command to run the text editor */
Blob content; /* Content of the unversioned file */
| < < < > > > | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
}else if( strncmp(zCmd, "edit", nCmd)==0 ){
const char *zEditor; /* Name of the text-editor command */
const char *zTFile; /* Temporary file */
const char *zUVFile; /* Name of the unversioned file */
char *zCmd; /* Command to run the text editor */
Blob content; /* Content of the unversioned file */
zEditor = fossil_text_editor();
if( zEditor==0 ){
fossil_fatal("no text editor - set the VISUAL env variable");
}
verify_all_options();
if( g.argc!=4) usage("edit UVFILE");
zUVFile = g.argv[3];
zTFile = fossil_temp_filename();
if( zTFile==0 ) fossil_fatal("cannot find a temporary filename");
db_begin_transaction();
content_rcvid_init("#!fossil unversioned edit");
if( unversioned_content(zUVFile, &content)==0 ){
fossil_fatal("no such uv-file: %Q", zUVFile);
}
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
99 100 101 102 103 104 105 106 107 108 109 110 111 112 | ** --force-missing Force update if missing content after sync ** -K|--keep-merge-files On merge conflict, retain the temporary files ** used for merging, named *-baseline, *-original, ** and *-merge. ** --latest Acceptable in place of VERSION, update to ** latest version ** --nosync Do not auto-sync prior to update ** --setmtime Set timestamps of all files to match their ** SCM-side times (the timestamp of the last ** check-in which modified them). ** -v|--verbose Print status information about all files ** -W|--width WIDTH Width of lines (default is to auto-detect). ** Must be more than 20 or 0 (= no limit, ** resulting in a single line per entry). | > | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | ** --force-missing Force update if missing content after sync ** -K|--keep-merge-files On merge conflict, retain the temporary files ** used for merging, named *-baseline, *-original, ** and *-merge. ** --latest Acceptable in place of VERSION, update to ** latest version ** --nosync Do not auto-sync prior to update ** --proxy PROXY Use PROXY as http proxy during sync operation ** --setmtime Set timestamps of all files to match their ** SCM-side times (the timestamp of the last ** check-in which modified them). ** -v|--verbose Print status information about all files ** -W|--width WIDTH Width of lines (default is to auto-detect). ** Must be more than 20 or 0 (= no limit, ** resulting in a single line per entry). |
| ︙ | ︙ | |||
196 197 198 199 200 201 202 |
if( tid==0 || !is_a_version(tid) ){
fossil_fatal("no such check-in: %s", g.argv[2]);
}
}
}
/* If no VERSION is specified on the command-line, then look for a
| | | 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
if( tid==0 || !is_a_version(tid) ){
fossil_fatal("no such check-in: %s", g.argv[2]);
}
}
}
/* If no VERSION is specified on the command-line, then look for a
** descendant of the current version. If there are multiple descendants,
** look for one from the same branch as the current version. If there
** are still multiple descendants, show them all and refuse to update
** until the user selects one.
*/
if( tid==0 ){
int closeCode = 1;
compute_leaves(vid, closeCode);
|
| ︙ | ︙ | |||
512 513 514 515 516 517 518 |
zNext = db_changes() ? file_dirname(zDir) : 0;
fossil_free(zDir);
zDir = zNext;
}
}
}
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
| < < < < > > > | 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 |
zNext = db_changes() ? file_dirname(zDir) : 0;
fossil_free(zDir);
zDir = zNext;
}
}
}
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
if( nameChng ){
fossil_print("MERGE %s -> %s\n", zName, zNewName);
}else{
fossil_print("MERGE %s\n", zName);
}
if( islinkv || islinkt ){
fossil_print("***** Cannot merge symlink %s\n", zNewName);
zOp = "CONFLICT";
nConflict++;
}else{
/* Merge the changes in the current tree into the target version */
Blob r, t, v;
int rc;
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
content_get(ridt, &t);
content_get(ridv, &v);
rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags);
if( rc>=0 ){
|
| ︙ | ︙ | |||
569 570 571 572 573 574 575 |
}
fossil_print("\n");
nConflict++;
zOp = "ERROR";
zErrMsg = "cannot merge binary file";
nc = 1;
}
| < < | | | > > | 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 |
}
fossil_print("\n");
nConflict++;
zOp = "ERROR";
zErrMsg = "cannot merge binary file";
nc = 1;
}
blob_reset(&v);
blob_reset(&t);
blob_reset(&r);
}
if( nameChng && !dryRunFlag ) file_delete(zFullPath);
}else{
nUpdate--;
if( chnged ){
if( verboseFlag ) fossil_print("EDITED %s\n", zName);
}else{
db_bind_int(&mtimeXfer, ":idv", idv);
db_bind_int(&mtimeXfer, ":idt", idt);
|
| ︙ | ︙ |
Changes to src/user.c.
| ︙ | ︙ | |||
296 297 298 299 300 301 302 |
/*
** Prompt the user to enter a single line of text.
*/
void prompt_user(const char *zPrompt, Blob *pIn){
char *z;
char zLine[1000];
| | | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
/*
** Prompt the user to enter a single line of text.
*/
void prompt_user(const char *zPrompt, Blob *pIn){
char *z;
char zLine[1000];
blob_init(pIn, 0, 0);
fossil_force_newline();
fossil_print("%s", zPrompt);
fflush(stdout);
z = fgets(zLine, sizeof(zLine), stdin);
if( z ){
int n = (int)strlen(z);
if( n>0 && z[n-1]=='\n' ) fossil_new_line_started();
|
| ︙ | ︙ | |||
324 325 326 327 328 329 330 | ** ** Query or set the capabilities for user USERNAME ** ** > fossil user contact USERNAME ?CONTACT-INFO? ** ** Query or set contact information for user USERNAME ** | | | > > > > > > | | | 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 | ** ** Query or set the capabilities for user USERNAME ** ** > fossil user contact USERNAME ?CONTACT-INFO? ** ** Query or set contact information for user USERNAME ** ** > fossil user default ?OPTIONS? ?USERNAME? ** ** Query or set the default user. The default user is the ** user for command-line interaction. If USERNAME is an ** empty string, then the default user is unset from the ** repository and will subsequently be determined by the -U ** command-line option or by environment variables ** FOSSIL_USER, USER, LOGNAME, or USERNAME, in that order. ** OPTIONS: ** ** -v|--verbose Show how the default user is computed ** ** > fossil user list | ls ** ** List all users known to the repository ** ** > fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD? ** ** Create a new user in the repository. Users can never be ** deleted. They can be denied all access but they must continue |
| ︙ | ︙ | |||
384 385 386 387 388 389 390 |
"INSERT INTO user(login,pw,cap,info,mtime)"
"VALUES(%B,%Q,%B,%B,now())",
&login, zPw, &caps, &contact
);
db_protect_pop();
free(zPw);
}else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
| > > | | | > > > > > > | | | | | | | | > > > > > > > > > > > > > > > > > > > > > | 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 |
"INSERT INTO user(login,pw,cap,info,mtime)"
"VALUES(%B,%Q,%B,%B,now())",
&login, zPw, &caps, &contact
);
db_protect_pop();
free(zPw);
}else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
int eVerbose = find_option("verbose","v",0)!=0;
verify_all_options();
if( g.argc>3 ){
const char *zUser = g.argv[3];
if( fossil_strcmp(zUser,"")==0 || fossil_stricmp(zUser,"nobody")==0 ){
db_begin_transaction();
if( g.localOpen ){
db_multi_exec("DELETE FROM vvar WHERE name='default-user'");
}
db_unset("default-user",0);
db_commit_transaction();
}else{
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.argv[3]) ){
fossil_fatal("no such user: %s", g.argv[3]);
}
if( g.localOpen ){
db_lset("default-user", g.argv[3]);
}else{
db_set("default-user", g.argv[3], 0);
}
}
}
if( g.argc==3 || eVerbose ){
int eHow = user_select();
const char *zHow = "???";
switch( eHow ){
case 1: zHow = "-U option"; break;
case 2: zHow = "previously set"; break;
case 3: zHow = "local check-out"; break;
case 4: zHow = "repository"; break;
case 5: zHow = "FOSSIL_USER"; break;
case 6: zHow = "USER"; break;
case 7: zHow = "LOGNAME"; break;
case 8: zHow = "USERNAME"; break;
case 9: zHow = "URL"; break;
}
if( eVerbose ){
fossil_print("%s (determined by %s)\n", g.zLogin, zHow);
}else{
fossil_print("%s\n", g.zLogin);
}
}
}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 ){
|
| ︙ | ︙ | |||
495 496 497 498 499 500 501 | } /* ** Figure out what user is at the controls. ** ** (1) Use the --user and -U command-line options. ** | > > | | | | | | | | | | | | | | | | | | 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 |
}
/*
** Figure out what user is at the controls.
**
** (1) Use the --user and -U command-line options.
**
** (2) The name used for login (if there was a login).
**
** (3) If the local database is open, check in VVAR.
**
** (4) Check the default-user in the repository
**
** (5) Try the FOSSIL_USER environment variable.
**
** (6) Try the USER environment variable.
**
** (7) Try the LOGNAME environment variable.
**
** (8) Try the USERNAME environment variable.
**
** (9) Check if the user can be extracted from the remote URL.
**
** The user name is stored in g.zLogin. The uid is in g.userUid.
*/
int user_select(void){
UrlData url;
if( g.userUid ) return 1;
if( g.zLogin ){
if( attempt_user(g.zLogin)==0 ){
fossil_fatal("no such user: %s", g.zLogin);
}else{
return 2;
}
}
if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return 3;
if( attempt_user(db_get("default-user", 0)) ) return 4;
if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return 5;
if( attempt_user(fossil_getenv("USER")) ) return 6;
if( attempt_user(fossil_getenv("LOGNAME")) ) return 7;
if( attempt_user(fossil_getenv("USERNAME")) ) return 8;
memset(&url, 0, sizeof(url));
url_parse_local(0, URL_USE_CONFIG, &url);
if( url.user && attempt_user(url.user) ) return 9;
fossil_print(
"Cannot figure out who you are! Consider using the --user\n"
"command line option, setting your USER environment variable,\n"
"or setting a default user with \"fossil user default USER\".\n"
);
fossil_fatal("cannot determine user");
|
| ︙ | ︙ |
Changes to src/utf8.c.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 | #include "utf8.h" #include <sqlite3.h> #ifdef _WIN32 # include <windows.h> #endif #include "cygsup.h" | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include "utf8.h"
#include <sqlite3.h>
#ifdef _WIN32
# include <windows.h>
#endif
#include "cygsup.h"
#if defined(_WIN32)
/*
** Translate MBCS to UTF-8. Return a pointer to the translated text.
** Call fossil_mbcs_free() to deallocate any memory used to store the
** returned pointer when done.
*/
char *fossil_mbcs_to_utf8(const char *zMbcs){
extern char *sqlite3_win32_mbcs_to_utf8(const char*);
|
| ︙ | ︙ |
Changes to src/util.c.
| ︙ | ︙ | |||
664 665 666 667 668 669 670 | } /* ** Return the name of the users preferred text editor. Return NULL if ** not found. ** ** Search algorithm: | > | | | | > | > > > > > > > > > > > > | > < | | < | > | | > | | 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 |
}
/*
** Return the name of the users preferred text editor. Return NULL if
** not found.
**
** Search algorithm:
** (1) The value of the --editor command-line argument
** (2) The local "editor" setting
** (3) The global "editor" setting
** (4) The VISUAL environment variable
** (5) The EDITOR environment variable
** (6) Any of the following programs that are available:
** notepad, nano, pico, jove, edit, vi, vim, ed,
**
** The search only occurs once, the first time this routine is called.
** Second and subsequent invocations always return the same value.
*/
const char *fossil_text_editor(void){
static const char *zEditor = 0;
const char *azStdEd[] = {
"notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed"
};
int i = 0;
if( zEditor==0 ){
zEditor = find_option("editor",0,1);
}
if( zEditor==0 ){
zEditor = db_get("editor", 0);
}
if( zEditor==0 ){
zEditor = fossil_getenv("VISUAL");
}
if( zEditor==0 ){
zEditor = fossil_getenv("EDITOR");
}
while( zEditor==0 && i<count(azStdEd) ){
if( fossil_app_on_path(azStdEd[i],0) ){
zEditor = azStdEd[i];
}else{
i++;
}
}
if( zEditor && is_false(zEditor) ) zEditor = 0;
return zEditor;
}
/*
** Construct a temporary filename.
**
** The returned string is obtained from sqlite3_malloc() and must be
|
| ︙ | ︙ | |||
893 894 895 896 897 898 899 |
*/
int fossil_num_digits(int n){
return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3
: n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6
: n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
}
| < < > > > | > > > > > > > > > > > > > > > > | > > > | | > > > > > > > > > > > > > | > > > > > > | > > | > | 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 |
*/
int fossil_num_digits(int n){
return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3
: n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6
: n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
}
/*
** Search for an executable on the PATH environment variable.
** Return true (1) if found and false (0) if not found.
**
** Print the full pathname of the first location if ePrint==1. Print
** all pathnames for the executable if ePrint==2 or more.
*/
int fossil_app_on_path(const char *zBinary, int ePrint){
const char *zPath = fossil_getenv("PATH");
char *zFull;
int i;
int bExists;
int bFound = 0;
while( zPath && zPath[0] ){
#ifdef _WIN32
while( zPath[0]==';' ) zPath++;
for(i=0; zPath[i] && zPath[i]!=';'; i++){}
zFull = mprintf("%.*s\\%s.exe", i, zPath, zBinary);
bExists = file_access(zFull, R_OK);
if( bExists!=0 ){
fossil_free(zFull);
zFull = mprintf("%.*s\\%s.bat", i, zPath, zBinary);
bExists = file_access(zFull, R_OK);
}
#else
while( zPath[0]==':' ) zPath++;
for(i=0; zPath[i] && zPath[i]!=':'; i++){}
zFull = mprintf("%.*s/%s", i, zPath, zBinary);
bExists = file_access(zFull, X_OK);
#endif
if( bExists==0 && ePrint ){
fossil_print("%s\n", zFull);
}
fossil_free(zFull);
if( bExists==0 ){
if( ePrint<2 ) return 1;
bFound = 1;
}
zPath += i;
}
return bFound;
}
/*
** COMMAND: which*
**
** Usage: fossil which [-a] NAME ...
**
** For each NAME mentioned as an argument, print the first location on the
** on PATH of the executable with that name. Or, show all locations on PATH
** for each argument if the -a option is used.
**
** This command is a substitute for the unix "which" command, which is not
** always available, especially on Windows.
*/
void test_app_on_path(void){
int i;
int ePrint = 1;
if( find_option("all","a",0)!=0 ) ePrint = 2;
verify_all_options();
for(i=2; i<g.argc; i++){
if( fossil_app_on_path(g.argv[i], ePrint)==0 ){
fossil_print("NOT FOUND: %s\n", g.argv[i]);
}
}
}
/*
** Return the name of a command that will launch a web-browser.
*/
const char *fossil_web_browser(void){
const char *zBrowser = 0;
#if defined(_WIN32)
zBrowser = db_get("web-browser", "start \"\"");
#elif defined(__DARWIN__) || defined(__APPLE__) || defined(__HAIKU__)
zBrowser = db_get("web-browser", "open");
#else
zBrowser = db_get("web-browser", 0);
if( zBrowser==0 ){
static const char *const azBrowserProg[] =
{ "xdg-open", "gnome-open", "firefox", "google-chrome" };
int i;
zBrowser = "echo";
for(i=0; i<count(azBrowserProg); i++){
if( fossil_app_on_path(azBrowserProg[i],0) ){
zBrowser = azBrowserProg[i];
break;
}
}
zBrowser = mprintf("%s 2>/dev/null", zBrowser);
}
#endif
return zBrowser;
}
/*
** On non-Windows systems, calls nice(2) with the given level. Errors
|
| ︙ | ︙ |
Changes to src/wiki.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ** This file contains code to do formatting of wiki text. */ #include "config.h" #include <assert.h> #include <ctype.h> #include "wiki.h" /* ** Return true if the input string is a well-formed wiki page name. ** ** Well-formed wiki page names do not begin or end with whitespace, ** and do not contain tabs or other control characters and do not ** contain more than a single space character in a row. Well-formed ** names must be between 1 and 100 characters in length, inclusive. | > > > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** This file contains code to do formatting of wiki text. */ #include "config.h" #include <assert.h> #include <ctype.h> #include "wiki.h" #define has_prefix(literal_prfx, zStr) \ (fossil_strncmp((zStr), "" literal_prfx, (sizeof literal_prfx)-1)==0) /* ** Return true if the input string is a well-formed wiki page name. ** ** Well-formed wiki page names do not begin or end with whitespace, ** and do not contain tabs or other control characters and do not ** contain more than a single space character in a row. Well-formed ** names must be between 1 and 100 characters in length, inclusive. |
| ︙ | ︙ | |||
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 |
/* Return values from wiki_page_type() */
#if INTERFACE
# define WIKITYPE_UNKNOWN (-1)
# define WIKITYPE_NORMAL 0
# define WIKITYPE_BRANCH 1
# define WIKITYPE_CHECKIN 2
# define WIKITYPE_TAG 3
#endif
/*
** 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
| > | | | > > > > | 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 |
/* Return values from wiki_page_type() */
#if INTERFACE
# define WIKITYPE_UNKNOWN (-1)
# define WIKITYPE_NORMAL 0
# define WIKITYPE_BRANCH 1
# define WIKITYPE_CHECKIN 2
# define WIKITYPE_TAG 3
# define WIKITYPE_TICKET 4
#endif
/*
** 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( has_prefix("checkin/", zPageName)
&& db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
){
return WIKITYPE_CHECKIN;
}else
if( has_prefix("branch/", zPageName) ){
return WIKITYPE_BRANCH;
}else
if( has_prefix("tag/", zPageName) ){
return WIKITYPE_TAG;
}else
if( has_prefix("ticket/", zPageName) ){
return WIKITYPE_TICKET;
}
return WIKITYPE_NORMAL;
}
/*
** Returns a JSON-friendly string form of the integer value returned
** by wiki_page_type(zPageName).
*/
const char * wiki_page_type_name(const char *zPageName){
switch(wiki_page_type(zPageName)){
case WIKITYPE_CHECKIN: return "checkin";
case WIKITYPE_BRANCH: return "branch";
case WIKITYPE_TAG: return "tag";
case WIKITYPE_TICKET: return "ticket";
case WIKITYPE_NORMAL:
default: return "normal";
}
}
/*
** Add an appropriate style_header() for either the /wiki or /wikiedit page
|
| ︙ | ︙ | |||
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 |
cgi_redirectf("%R/timeline?t=%t",zPageName);
}else{
style_header("Notes About Tag %h", zPageName);
style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
}
break;
}
}
return eType;
}
/*
** Wiki pages with special names "branch/...", "checkin/...", and "tag/..."
** requires perm.Write privilege in addition to perm.WrWiki in order
** to write. This function determines whether the extra perm.Write
** is required and available. Return true if writing to the wiki page
** may proceed, and return false if permission is lacking.
*/
static int wiki_special_permission(const char *zPageName){
if( strncmp(zPageName,"branch/",7)!=0
&& strncmp(zPageName,"checkin/",8)!=0
&& strncmp(zPageName,"tag/",4)!=0
){
return 1;
}
if( db_get_boolean("wiki-about",1)==0 ){
return 1;
}
return g.perm.Write;
}
/*
** WEBPAGE: wiki
**
| > > > > > > > > > > > > > > | 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 |
cgi_redirectf("%R/timeline?t=%t",zPageName);
}else{
style_header("Notes About Tag %h", zPageName);
style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
}
break;
}
case WIKITYPE_TICKET: {
zPageName += 7;
if( zExtra[0]==0 && !P("p") ){
cgi_redirectf("%R/tktview/%s",zPageName);
}else{
style_header("Notes About Ticket %h", zPageName);
style_submenu_element("Ticket","%R/tktview/%s",zPageName);
}
break;
}
}
return eType;
}
/*
** Wiki pages with special names "branch/...", "checkin/...", and "tag/..."
** requires perm.Write privilege in addition to perm.WrWiki in order
** to write. This function determines whether the extra perm.Write
** is required and available. Return true if writing to the wiki page
** may proceed, and return false if permission is lacking.
*/
static int wiki_special_permission(const char *zPageName){
if( strncmp(zPageName,"branch/",7)!=0
&& strncmp(zPageName,"checkin/",8)!=0
&& strncmp(zPageName,"tag/",4)!=0
&& strncmp(zPageName,"ticket/",7)!=0
){
return 1;
}
if( db_get_boolean("wiki-about",1)==0 ){
return 1;
}
if( strncmp(zPageName,"ticket/",7)==0 ){
return g.perm.WrTkt;
}
return g.perm.Write;
}
/*
** WEBPAGE: wiki
**
|
| ︙ | ︙ | |||
1966 1967 1968 1969 1970 1971 1972 |
style_submenu_element("Active", "%R/wcontent");
}else{
style_submenu_element("All", "%R/wcontent?all=1");
}
cgi_check_for_malice();
showCkBr = db_exists(
"SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) "
| | > | 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 |
style_submenu_element("Active", "%R/wcontent");
}else{
style_submenu_element("All", "%R/wcontent?all=1");
}
cgi_check_for_malice();
showCkBr = db_exists(
"SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) "
"WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' OR "
" tn GLOB 'wiki-tag/*' OR tn GLOB 'wiki-ticket/*' ) "
" AND TYPEOF(tagxref.value+0)='integer'" );
if( showCkBr ){
showCkBr = P("showckbr")!=0;
style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0);
}
wiki_standard_submenu(W_ALL_BUT(W_LIST));
db_prepare(&q, listAllWikiPages/*works-like:""*/);
|
| ︙ | ︙ | |||
1996 1997 1998 1999 2000 2001 2002 |
double rWmtime = db_column_double(&q, 3);
sqlite3_int64 iMtime = (sqlite3_int64)(rWmtime*86400.0);
char *zAge;
int wcnt = db_column_int(&q, 4);
char *zWDisplayName;
if( !showCkBr &&
| | | > > | | | 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 |
double rWmtime = db_column_double(&q, 3);
sqlite3_int64 iMtime = (sqlite3_int64)(rWmtime*86400.0);
char *zAge;
int wcnt = db_column_int(&q, 4);
char *zWDisplayName;
if( !showCkBr &&
(has_prefix("checkin/", zWName) ||
has_prefix("branch/", zWName) ||
has_prefix("tag/", zWName) ||
has_prefix("ticket/", zWName) )){
continue;
}
if( has_prefix("checkin/",zWName) || has_prefix("ticket/",zWName) ){
zWDisplayName = mprintf("%.25s...", zWName);
}else{
zWDisplayName = fossil_strdup(zWName);
}
if( wrid==0 ){
if( !showAll ) continue;
@ <tr><td data-sortkey="%h(zSort)">\
@ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
}else{
@ <tr><td data-sortkey="%h(zSort)">\
|
| ︙ | ︙ | |||
2499 2500 2501 2502 2503 2504 2505 |
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 &&
| | | > > | 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 |
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 &&
(has_prefix("checkin/", zName) ||
has_prefix("branch/", zName) ||
has_prefix("tag/", zName) ||
has_prefix("ticket/", zName) ) ){
continue;
}
if( showIds ){
const char *zUuid = db_column_text(&q, 1);
fossil_print("%s ",zUuid);
}
fossil_print( "%s\n",zName);
|
| ︙ | ︙ | |||
2549 2550 2551 2552 2553 2554 2555 |
@ <div class="section accordion">About %s(zPrefix) %h(zName)</div>
}
}
/*
** Add an "Wiki" button in a submenu that links to the read-wiki page.
*/
| | | > > > | > > > > > > > > > > > > > | | 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 |
@ <div class="section accordion">About %s(zPrefix) %h(zName)</div>
}
}
/*
** Add an "Wiki" button in a submenu that links to the read-wiki page.
*/
static void wiki_submenu_to_read_wiki(
const char *zPrefix, /* "branch", "tag", or "checkin" */
const char *zName, /* Name of the object */
unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
){
if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0
&& 0!=fossil_strcmp("branch", zPrefix)
/* ^^^ https://fossil-scm.org/forum/forumpost/ff453de2f30791dd */
){
style_submenu_element("Wiki", "%R/wiki?name=%s/%t", zPrefix, zName);
}
}
/*
** Add an "Edit Wiki" button in a submenu that links to the edit-wiki page.
*/
static void wiki_submenu_to_edit_wiki(
const char *zPrefix, /* "branch", "tag", or "checkin" */
const char *zName, /* Name of the object */
unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
){
if( g.perm.WrWiki && (mFlags & WIKIASSOC_MENU_WRITE)!=0 ){
style_submenu_element("Edit Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName);
}
}
/*
** Check to see if there exists a wiki page with a name zPrefix/zName.
** If there is, then render a <div class='section'>..</div> and
** return true.
**
** If there is no such wiki page, return false.
*/
int wiki_render_associated(
const char *zPrefix, /* "branch", "tag", "ticket", or "checkin" */
const char *zName, /* Name of the object */
unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
){
int rid;
Manifest *pWiki;
if( !db_get_boolean("wiki-about",1) ) return 0;
rid = db_int(0,
|
| ︙ | ︙ | |||
2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 |
blob_init(&markdown, pWiki->zWiki, -1);
markdown_to_html(&markdown, &title, &tail);
if( blob_size(&title) ){
@ <div class="section accordion">%h(blob_str(&title))</div>
}else{
wiki_section_label(zPrefix, zName, mFlags);
}
wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
@ <div class="accordion_panel">
safe_html_context(DOCSRC_WIKI);
safe_html(&tail);
convert_href_and_output(&tail);
@ </div>
blob_reset(&tail);
| > | 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 |
blob_init(&markdown, pWiki->zWiki, -1);
markdown_to_html(&markdown, &title, &tail);
if( blob_size(&title) ){
@ <div class="section accordion">%h(blob_str(&title))</div>
}else{
wiki_section_label(zPrefix, zName, mFlags);
}
wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
@ <div class="accordion_panel">
safe_html_context(DOCSRC_WIKI);
safe_html(&tail);
convert_href_and_output(&tail);
@ </div>
blob_reset(&tail);
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 | #include <assert.h> #include "wikiformat.h" #if INTERFACE /* ** Allowed wiki transformation operations */ | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > | | 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 |
#include <assert.h>
#include "wikiformat.h"
#if INTERFACE
/*
** Allowed wiki transformation operations
*/
#define WIKI_HTMLONLY 0x0001 /* HTML markup only. No wiki */
#define WIKI_INLINE 0x0002 /* Do not surround with <p>..</p> */
/* avalable for reuse: 0x0004 --- formerly WIKI_NOBLOCK */
#define WIKI_BUTTONS 0x0008 /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS 0x0010 /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY 0x0020 /* No markup. Only decorate links */
#define WIKI_NEWLINE 0x0040 /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS 0x0080 /* Resolve hyperlinks as in markdown */
#define WIKI_SAFE 0x0100 /* Make the result safe for embedding */
#define WIKI_TARGET_BLANK 0x0200 /* Hyperlinks go to a new window */
#define WIKI_NOBRACKET 0x0400 /* Omit extra [..] around hyperlinks */
#define WIKI_ADMIN 0x0800 /* Ignore g.perm.Hyperlink */
#define WIKI_MARK 0x1000 /* Add <mark>..</mark> around problems */
/*
** Return values from wiki_convert
*/
#define RENDER_LINK 0x0001 /* One or more hyperlinks rendered */
#define RENDER_ENTITY 0x0002 /* One or more HTML entities (ex: <) */
#define RENDER_TAG 0x0004 /* One or more HTML tags */
#define RENDER_BLOCKTAG 0x0008 /* One or more HTML block tags (ex: <p>) */
#define RENDER_BLOCK 0x0010 /* Block wiki (paragraphs, etc.) */
#define RENDER_MARK 0x0020 /* Output contains <mark>..</mark> */
#define RENDER_BADLINK 0x0100 /* Bad hyperlink syntax seen */
#define RENDER_BADTARGET 0x0200 /* Bad hyperlink target */
#define RENDER_BADTAG 0x0400 /* Bad HTML tag or tag syntax */
#define RENDER_BADENTITY 0x0800 /* Bad HTML entity syntax */
#define RENDER_BADHTML 0x1000 /* Bad HTML seen */
#define RENDER_ERROR 0x8000 /* Some other kind of error */
/* Composite values: */
#define RENDER_ANYERROR 0x9f00 /* Mask for any kind of error */
#endif /* INTERFACE */
/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
ATTR_ALIGN = 1,
|
| ︙ | ︙ | |||
442 443 444 445 446 447 448 | ** State flags. Save the lower 16 bits for the WIKI_* flags. */ #define AT_NEWLINE 0x0010000 /* At start of a line */ #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */ #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */ #define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */ #define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */ | < | > | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
** State flags. Save the lower 16 bits for the WIKI_* flags.
*/
#define AT_NEWLINE 0x0010000 /* At start of a line */
#define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
#define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
#define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */
#define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */
#define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */
/*
** Current state of the rendering engine
*/
typedef struct Renderer Renderer;
struct Renderer {
Blob *pOut; /* Output appended to this blob */
int state; /* Flag that govern rendering */
int mRender; /* Mask of RENDER_* values to return */
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() */
|
| ︙ | ︙ | |||
677 678 679 680 681 682 683 684 685 686 687 688 689 690 |
** characters in that token. Write the token type into *pTokenType.
*/
static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){
int n;
if( z[0]=='<' ){
n = html_tag_length(z);
if( n>0 ){
*pTokenType = TOKEN_MARKUP;
return n;
}else{
*pTokenType = TOKEN_CHARACTER;
return 1;
}
}
| > > > > > > > > > < < < < | 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 |
** characters in that token. Write the token type into *pTokenType.
*/
static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){
int n;
if( z[0]=='<' ){
n = html_tag_length(z);
if( n>0 ){
p->mRender |= RENDER_TAG;
*pTokenType = TOKEN_MARKUP;
return n;
}else{
p->mRender |= RENDER_BADTAG;
*pTokenType = TOKEN_CHARACTER;
return 1;
}
}
if( z[0]=='&' ){
p->mRender |= RENDER_ENTITY;
if( (p->inVerbatim || !isElement(z)) ){
*pTokenType = TOKEN_CHARACTER;
return 1;
}
}
if( (p->state & ALLOW_WIKI)!=0 ){
if( z[0]=='\n' ){
n = paragraphBreakLength(z);
if( n>0 ){
*pTokenType = TOKEN_PARAGRAPH;
return n;
}else{
|
| ︙ | ︙ | |||
723 724 725 726 727 728 729 |
if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){
n = indentLength(z);
if( n>0 ){
*pTokenType = TOKEN_INDENT;
return n;
}
}
| | > | | > > > > > | > | > | | > > > > > > | > | | > > > > > > | 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 |
if( (p->state & AT_PARAGRAPH)!=0 && fossil_isspace(z[0]) ){
n = indentLength(z);
if( n>0 ){
*pTokenType = TOKEN_INDENT;
return n;
}
}
if( z[0]=='[' ){
if( (n = linkLength(z))>0 ){
*pTokenType = TOKEN_LINK;
return n;
}else if( p->state & WIKI_MARK ){
blob_append_string(p->pOut, "<mark>");
p->mRender |= RENDER_BADLINK|RENDER_MARK;
}else{
p->mRender |= RENDER_BADLINK;
}
}
}else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' ){
if( (n = linkLength(z))>0 ){
*pTokenType = TOKEN_LINK;
return n;
}else if( p->state & WIKI_MARK ){
blob_append_string(p->pOut, "<mark>");
p->mRender |= RENDER_BADLINK|RENDER_MARK;
}else{
p->mRender |= RENDER_BADLINK;
}
}
*pTokenType = TOKEN_TEXT;
return 1 + textLength(z+1, p->state);
}
/*
** Parse only Wiki links, return everything else as TOKEN_RAW.
**
** z points to the start of a token. Return the number of
** characters in that token. Write the token type into *pTokenType.
*/
static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
int n;
if( z[0]=='[' ){
if( (n = linkLength(z))>0 ){
*pTokenType = TOKEN_LINK;
return n;
}else if( p->state & WIKI_MARK ){
blob_append_string(p->pOut, "<mark>");
p->mRender |= RENDER_BADLINK|RENDER_MARK;
}else{
p->mRender |= RENDER_BADLINK;
}
}
*pTokenType = TOKEN_RAW;
return 1 + textLength(z+1, p->state);
}
/*
** A single markup is parsed into an instance of the following
|
| ︙ | ︙ | |||
1246 1247 1248 1249 1250 1251 1252 1253 | ** ** [WikiPageName] ** [wiki:WikiPageName] ** ** [2010-02-27 07:13] ** ** [InterMap:Link] -> Interwiki link */ | > > > > | > | 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 |
**
** [WikiPageName]
** [wiki:WikiPageName]
**
** [2010-02-27 07:13]
**
** [InterMap:Link] -> Interwiki link
**
** The return value is a mask of RENDER_* values indicating what happened.
** Probably the return value is 0 on success and RENDER_BADTARGET or
** RENDER_BADLINK if there are problems.
*/
int wiki_resolve_hyperlink(
Blob *pOut, /* Write the HTML output here */
int mFlags, /* Rendering option flags */
const char *zTarget, /* Hyperlink target; text within [...] */
char *zClose, /* Write hyperlink closing text here */
int nClose, /* Bytes available in zClose[] */
const char *zOrig, /* Complete document text */
const char *zTitle /* Title of the link */
){
const char *zTerm = "</a>";
const char *z;
char *zExtra = 0;
const char *zExtraNS = 0;
char *zRemote = 0;
int rc = 0;
if( zTitle ){
zExtra = mprintf(" title='%h'", zTitle);
zExtraNS = zExtra+1;
}else if( mFlags & WIKI_TARGET_BLANK ){
zExtra = mprintf(" target='_blank'");
zExtraNS = zExtra+1;
|
| ︙ | ︙ | |||
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 |
blob_appendf(pOut, "%s", zLB);
zTerm = "]";
}
}
}else if( !in_this_repo(zTarget) ){
if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
zTerm = "";
}else{
blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
zTerm = "]</span>";
}
| > > > > > | | > > > > > > > > > > > > > > > > > > > > > | 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 |
blob_appendf(pOut, "%s", zLB);
zTerm = "]";
}
}
}else if( !in_this_repo(zTarget) ){
if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
zTerm = "";
}else if( (mFlags & WIKI_MARK)!=0 ){
blob_appendf(pOut, "<mark>%s", zLB);
zTerm = "]</mark>";
rc |= RENDER_MARK;
}else{
blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
zTerm = "]</span>";
}
rc |= RENDER_BADTARGET;
}else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){
blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB);
zTerm = "]</a>";
}else{
zTerm = "";
}
if( zTerm[0]==']' && (mFlags & WIKI_NOBRACKET)!=0 ) zTerm++;
}else if( (zRemote = interwiki_url(zTarget))!=0 ){
blob_appendf(pOut, "<a href=\"%z\"%s>", zRemote, zExtra);
zTerm = "</a>";
}else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
/* The link is to a valid wiki page name */
const char *zOverride = wiki_is_overridden(zTarget);
if( zOverride ){
blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
}else{
blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
}
}else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
&& db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
/* Dates or date-and-times in ISO8601 resolve to a link to the
** timeline for that date */
blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
}else if( mFlags & WIKI_MARKDOWNLINKS ){
/* If none of the above, and if rendering links for markdown, then
** create a link to the literal text of the target */
blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
}else if( mFlags & WIKI_MARK ){
blob_appendf(pOut, "<mark>[");
zTerm = "]</mark>";
rc |= RENDER_BADTARGET|RENDER_MARK;
}else if( zOrig && zTarget>=&zOrig[2]
&& zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
/* If the hyperlink markup is not preceded by whitespace, then it
** is probably a C-language subscript or similar, not really a
** hyperlink. Just ignore it. */
zTerm = "";
}else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
/* Also ignore the link if various flags are set */
zTerm = "";
rc |= RENDER_BADTARGET;
}else{
blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
zTerm = "</span>";
rc |= RENDER_BADTARGET;
}
if( zExtra ) fossil_free(zExtra);
assert( (int)strlen(zTerm)<nClose );
sqlite3_snprintf(nClose, zClose, "%s", zTerm);
return rc;
}
/*
** Check zTarget to see if it looks like a valid hyperlink target.
** Return true if it does seem valid and false if not.
*/
int wiki_valid_link_target(char *zTarget){
char zClose[30];
Blob notUsed;
blob_init(¬Used, 0, 0);
wiki_resolve_hyperlink(¬Used, WIKI_NOBADLINKS|WIKI_ADMIN,
zTarget, zClose, sizeof(zClose)-1, 0, 0);
blob_reset(¬Used);
return zClose[0]!=0;
}
/*
** Check to see if the given parsed markup is the correct
** </verbatim> tag.
*/
static int endVerbatim(Renderer *p, ParsedMarkup *pMarkup){
|
| ︙ | ︙ | |||
1448 1449 1450 1451 1452 1453 1454 |
**
** This routine will probably modify the content of z[].
*/
static void wiki_render(Renderer *p, char *z){
int tokenType;
ParsedMarkup markup;
int n;
| < < < < < | | | | | | | < | < < | | | | | | | | | | | | | < < | < | | | | | | | | | | | | | < | < < | | | | | | | | | | | | | < < > | | | | | < > > > > > > > > > > > > > > > > | | 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 |
**
** This routine will probably modify the content of z[].
*/
static void wiki_render(Renderer *p, char *z){
int tokenType;
ParsedMarkup markup;
int n;
int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
char *zOrig = z;
/* Make sure the attribute constants and names still align
** following changes in the attribute list. */
assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
while( z[0] ){
if( wikiHtmlOnly ){
n = nextRawToken(z, p, &tokenType);
}else{
n = nextWikiToken(z, p, &tokenType);
}
p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
switch( tokenType ){
case TOKEN_PARAGRAPH: {
if( p->wikiList ){
popStackToTag(p, p->wikiList);
p->wikiList = 0;
}
endAutoParagraph(p);
blob_append_string(p->pOut, "\n\n");
p->wantAutoParagraph = 1;
p->state |= AT_PARAGRAPH|AT_NEWLINE;
break;
}
case TOKEN_NEWLINE: {
if( p->renderFlags & WIKI_NEWLINE ){
blob_append_string(p->pOut, "<br>\n");
}else{
blob_append_string(p->pOut, "\n");
}
p->state |= AT_NEWLINE;
break;
}
case TOKEN_BUL_LI: {
p->mRender |= RENDER_BLOCK;
if( p->wikiList!=MARKUP_UL ){
if( p->wikiList ){
popStackToTag(p, p->wikiList);
}
endAutoParagraph(p);
pushStack(p, MARKUP_UL);
blob_append_string(p->pOut, "<ul>");
p->wikiList = MARKUP_UL;
}
popStackToTag(p, MARKUP_LI);
startAutoParagraph(p);
pushStack(p, MARKUP_LI);
blob_append_string(p->pOut, "<li>");
break;
}
case TOKEN_NUM_LI: {
p->mRender |= RENDER_BLOCK;
if( p->wikiList!=MARKUP_OL ){
if( p->wikiList ){
popStackToTag(p, p->wikiList);
}
endAutoParagraph(p);
pushStack(p, MARKUP_OL);
blob_append_string(p->pOut, "<ol>");
p->wikiList = MARKUP_OL;
}
popStackToTag(p, MARKUP_LI);
startAutoParagraph(p);
pushStack(p, MARKUP_LI);
blob_append_string(p->pOut, "<li>");
break;
}
case TOKEN_ENUM: {
p->mRender |= RENDER_BLOCK;
if( p->wikiList!=MARKUP_OL ){
if( p->wikiList ){
popStackToTag(p, p->wikiList);
}
endAutoParagraph(p);
pushStack(p, MARKUP_OL);
blob_append_string(p->pOut, "<ol>");
p->wikiList = MARKUP_OL;
}
popStackToTag(p, MARKUP_LI);
startAutoParagraph(p);
pushStack(p, MARKUP_LI);
blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z));
break;
}
case TOKEN_INDENT: {
p->mRender |= RENDER_BLOCK;
assert( p->wikiList==0 );
pushStack(p, MARKUP_BLOCKQUOTE);
blob_append_string(p->pOut, "<blockquote>");
p->wantAutoParagraph = 0;
p->wikiList = MARKUP_BLOCKQUOTE;
break;
}
case TOKEN_CHARACTER: {
startAutoParagraph(p);
if( p->state & WIKI_MARK ){
blob_append_string(p->pOut, "<mark>");
p->mRender |= RENDER_MARK;
}
if( z[0]=='<' ){
p->mRender |= RENDER_BADTAG;
blob_append_string(p->pOut, "<");
}else if( z[0]=='&' ){
p->mRender |= RENDER_BADENTITY;
blob_append_string(p->pOut, "&");
}
if( p->state & WIKI_MARK ){
if( fossil_isalnum(z[1]) || (z[1]=='/' && fossil_isalnum(z[2])) ){
int kk;
for(kk=2; fossil_isalnum(z[kk]); kk++){}
blob_append(p->pOut, &z[1], kk-1);
n = kk;
}
blob_append_string(p->pOut, "</mark>");
}
break;
}
case TOKEN_LINK: {
char *zTarget;
char *zDisplay = 0;
int i, j;
int savedState;
char zClose[20];
char cS1 = 0;
int iS1 = 0;
startAutoParagraph(p);
p->mRender |= RENDER_LINK;
zTarget = &z[1];
for(i=1; z[i] && z[i]!=']'; i++){
if( z[i]=='|' && zDisplay==0 ){
zDisplay = &z[i+1];
for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
iS1 = j;
cS1 = z[j];
z[j] = 0;
}
}
z[i] = 0;
if( zDisplay==0 ){
zDisplay = zTarget + interwiki_removable_prefix(zTarget);
}else{
while( fossil_isspace(*zDisplay) ) zDisplay++;
}
p->mRender |= wiki_resolve_hyperlink(p->pOut, p->state,
zTarget, zClose, sizeof(zClose), zOrig, 0);
if( linksOnly || zClose[0]==0 || p->inVerbatim ){
if( cS1 ) z[iS1] = cS1;
if( zClose[0]!=']' ){
blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
}else{
blob_appendf(p->pOut, "%h%s", zTarget, zClose);
|
| ︙ | ︙ | |||
1685 1686 1687 1688 1689 1690 1691 1692 1693 |
}
}else
/* Render invalid markup literally. The markup appears in the
** final output as plain text.
*/
if( markup.iCode==MARKUP_INVALID ){
unparseMarkup(&markup);
startAutoParagraph(p);
| > > > > > > > | < > > < < < < < < | 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 |
}
}else
/* Render invalid markup literally. The markup appears in the
** final output as plain text.
*/
if( markup.iCode==MARKUP_INVALID ){
p->mRender |= RENDER_BADTAG;
unparseMarkup(&markup);
startAutoParagraph(p);
if( p->state & WIKI_MARK ){
p->mRender |= RENDER_MARK;
blob_append_string(p->pOut, "<mark>");
htmlize_to_blob(p->pOut, z, n);
blob_append_string(p->pOut, "</mark>");
}else{
blob_append_string(p->pOut, "<");
htmlize_to_blob(p->pOut, z+1, n-1);
}
}else
/* If the markup is not font-change markup ignore it if the
** font-change-only flag is set.
*/
if( (markup.iType&MUTYPE_FONT)==0 && (p->state & FONT_MARKUP_ONLY)!=0 ){
/* Do nothing */
}else
if( markup.iCode==MARKUP_NOWIKI ){
if( markup.endTag ){
p->state |= ALLOW_WIKI;
}else{
p->state &= ~ALLOW_WIKI;
}
}else
/* Generate end-tags */
if( markup.endTag ){
popStackToTag(p, markup.iCode);
}else
/* Push <div> markup onto the stack together with the id=ID attribute.
*/
|
| ︙ | ︙ | |||
1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 |
pushStack(p, markup.iCode);
}
}else
{
if( markup.iType==MUTYPE_FONT ){
startAutoParagraph(p);
}else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
p->wantAutoParagraph = 0;
}
if( markup.iCode==MARKUP_HR
|| markup.iCode==MARKUP_H1
|| markup.iCode==MARKUP_H2
|| markup.iCode==MARKUP_H3
|| markup.iCode==MARKUP_H4
| > | 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 |
pushStack(p, markup.iCode);
}
}else
{
if( markup.iType==MUTYPE_FONT ){
startAutoParagraph(p);
}else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
p->mRender |= RENDER_BLOCKTAG;
p->wantAutoParagraph = 0;
}
if( markup.iCode==MARKUP_HR
|| markup.iCode==MARKUP_H1
|| markup.iCode==MARKUP_H2
|| markup.iCode==MARKUP_H3
|| markup.iCode==MARKUP_H4
|
| ︙ | ︙ | |||
1827 1828 1829 1830 1831 1832 1833 1834 | /* ** Transform the text in the pIn blob. Write the results ** into the pOut blob. The pOut blob should already be ** initialized. The output is merely appended to pOut. ** If pOut is NULL, then the output is appended to the CGI ** reply. */ | > > | | 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 |
/*
** Transform the text in the pIn blob. Write the results
** into the pOut blob. The pOut blob should already be
** initialized. The output is merely appended to pOut.
** If pOut is NULL, then the output is appended to the CGI
** reply.
**
** Return a mask of RENDER_ flags indicating what happened.
*/
int wiki_convert(Blob *pIn, Blob *pOut, int flags){
Renderer renderer;
memset(&renderer, 0, sizeof(renderer));
renderer.renderFlags = flags;
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
if( flags & WIKI_INLINE ){
renderer.wantAutoParagraph = 0;
|
| ︙ | ︙ | |||
1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 |
wiki_render(&renderer, blob_str(pIn));
endAutoParagraph(&renderer);
while( renderer.nStack ){
popStack(&renderer);
}
blob_append_char(renderer.pOut, '\n');
free(renderer.aStack);
}
/*
** COMMAND: test-wiki-render
**
** Usage: %fossil test-wiki-render FILE [OPTIONS]
**
** Translate the input FILE from Fossil-wiki into HTML and write
** the resulting HTML on standard output.
**
** 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
| > > > > > > | | < > > > > > | > > > > | > > > | > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | | > | > > > > > > > > | 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 |
wiki_render(&renderer, blob_str(pIn));
endAutoParagraph(&renderer);
while( renderer.nStack ){
popStack(&renderer);
}
blob_append_char(renderer.pOut, '\n');
free(renderer.aStack);
return renderer.mRender;
}
/*
** COMMAND: test-wiki-render
**
** Usage: %fossil test-wiki-render FILE [OPTIONS]
**
** Translate the input FILE from Fossil-wiki into HTML and write
** the resulting HTML on standard output.
**
** Options:
** --buttons Set the WIKI_BUTTONS flag
** --dark-pikchr Render pikchrs in dark mode
** --flow Render as text using comment_format
** --htmlonly Set the WIKI_HTMLONLY flag
** --inline Set the WIKI_INLINE flag
** --linksonly Set the WIKI_LINKSONLY flag
** -m TEXT Use TEXT in place of the content of FILE
** --mark Add <mark>...</mark> around problems
** --nobadlinks Set the WIKI_NOBADLINKS flag
** --text Run the output through html_to_plaintext()
** --type Break down the return code from wiki_convert()
*/
void test_wiki_render(void){
Blob in, out;
int flags = 0;
int bText;
int bFlow = 0;
int showType = 0;
int mType;
const char *zIn;
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("mark",0,0)!=0 ) flags |= WIKI_MARK;
if( find_option("dark-pikchr",0,0)!=0 ){
pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
}
bText = find_option("text",0,0)!=0;
bFlow = find_option("flow",0,0)!=0;
showType = find_option("type",0,0)!=0;
zIn = find_option("msg","m",1);
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
verify_all_options();
if( (zIn==0 && g.argc!=3) || (zIn!=0 && g.argc!=2) ) usage("FILE");
blob_zero(&out);
if( zIn ){
blob_init(&in, zIn, -1);
}else{
blob_read_from_file(&in, g.argv[2], ExtFILE);
}
mType = wiki_convert(&in, &out, flags);
if( bText ){
Blob txt;
int htot = HTOT_TRIM;
if( terminal_is_vt100() ) htot |= HTOT_VT100;
if( bFlow ) htot |= HTOT_FLOW;
blob_init(&txt, 0, 0);
html_to_plaintext(blob_str(&out),&txt, htot);
blob_reset(&out);
out = txt;
}
if( bFlow ){
fossil_print(" ");
comment_print(blob_str(&out), 0, 3, terminal_get_width(80)-3,
get_comment_format());
}else{
blob_write_to_file(&out, "-");
}
if( showType ){
fossil_print("%.*c\nResult Codes:", terminal_get_width(80)-1, '*');
if( mType & RENDER_LINK ) fossil_print(" LINK");
if( mType & RENDER_ENTITY ) fossil_print(" ENTITY");
if( mType & RENDER_TAG ) fossil_print(" TAG");
if( mType & RENDER_BLOCKTAG ) fossil_print(" BLOCKTAG");
if( mType & RENDER_BLOCK ) fossil_print(" BLOCK");
if( mType & RENDER_MARK ) fossil_print(" MARK");
if( mType & RENDER_BADLINK ) fossil_print(" BADLINK");
if( mType & RENDER_BADTARGET ) fossil_print(" BADTARGET");
if( mType & RENDER_BADTAG ) fossil_print(" BADTAG");
if( mType & RENDER_BADENTITY ) fossil_print(" BADENTITY");
if( mType & RENDER_BADHTML ) fossil_print(" BADHTML");
if( mType & RENDER_ERROR ) fossil_print(" ERROR");
fossil_print("\n");
}
}
/*
** COMMAND: test-markdown-render
**
** Usage: %fossil test-markdown-render FILE ...
**
** Render markdown in FILE as HTML on stdout.
** Options:
**
** --dark-pikchr Render pikchrs in dark mode
** --lint-footnotes Print stats for footnotes-related issues
** --safe Restrict the output to use only "safe" HTML
** --text Run the output through html_to_plaintext().
*/
void test_markdown_render(void){
Blob in, out;
int i;
int bSafe = 0, bFnLint = 0, bText = 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 );
}
bText = find_option("text",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]);
}
markdown_to_html(&in, 0, &out);
safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED );
safe_html(&out);
if( bText ){
Blob txt;
blob_init(&txt, 0, 0);
html_to_plaintext(blob_str(&out), &txt, HTOT_VT100);
blob_reset(&out);
out = txt;
}
blob_write_to_file(&out, "-");
blob_reset(&in);
blob_reset(&out);
}
if( bFnLint && (g.ftntsIssues[0] || g.ftntsIssues[1]
|| g.ftntsIssues[2] || g.ftntsIssues[3] )){
fossil_fatal("There were issues with footnotes:\n"
|
| ︙ | ︙ | |||
1988 1989 1990 1991 1992 1993 1994 1995 | ** ** [target] ** [target|...] ** ** Where "target" can be either an artifact ID prefix or a wiki page ** name. For each such hyperlink found, add an entry to the ** backlink table. */ | > > | < < < < < | 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 |
**
** [target]
** [target|...]
**
** Where "target" can be either an artifact ID prefix or a wiki page
** name. For each such hyperlink found, add an entry to the
** backlink table.
**
** The return value is a mask of RENDER_ flags.
*/
int wiki_extract_links(
char *z, /* The wiki text from which to extract links */
Backlink *pBklnk, /* Backlink extraction context */
int flags /* wiki parsing flags */
){
Renderer renderer;
int tokenType;
ParsedMarkup markup;
int n;
int wikiHtmlOnly = 0;
memset(&renderer, 0, sizeof(renderer));
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
if( wikiUsesHtml() ){
renderer.state |= WIKI_HTMLONLY;
wikiHtmlOnly = 1;
}
while( z[0] ){
if( wikiHtmlOnly ){
n = nextRawToken(z, &renderer, &tokenType);
}else{
n = nextWikiToken(z, &renderer, &tokenType);
}
|
| ︙ | ︙ | |||
2091 2092 2093 2094 2095 2096 2097 |
if( markup.endTag ){
renderer.state |= ALLOW_WIKI;
}else{
renderer.state &= ~ALLOW_WIKI;
}
}else
| < < < < < < | 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 |
if( markup.endTag ){
renderer.state |= ALLOW_WIKI;
}else{
renderer.state &= ~ALLOW_WIKI;
}
}else
/* Generate end-tags */
if( markup.endTag ){
popStackToTag(&renderer, markup.iCode);
}else
/* Push <div> markup onto the stack together with the id=ID attribute.
*/
|
| ︙ | ︙ | |||
2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 |
default: {
break;
}
}
z += n;
}
free(renderer.aStack);
}
/*
** Return the length, in bytes, of the HTML token that z is pointing to.
*/
int html_token_length(const char *z){
int n;
| > | 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 |
default: {
break;
}
}
z += n;
}
free(renderer.aStack);
return renderer.mRender;
}
/*
** Return the length, in bytes, of the HTML token that z is pointing to.
*/
int html_token_length(const char *z){
int n;
|
| ︙ | ︙ | |||
2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 |
blob_zero(&out);
htmlTidy(blob_str(&in), &out);
blob_reset(&in);
fossil_puts(blob_buffer(&out), 0, blob_size(&out));
blob_reset(&out);
}
}
/*
** Remove all HTML markup from the input text. The output written into
** pOut is pure text.
**
** Put the title on the first line, if there is any <title> markup.
** If there is no <title>, then create a blank first line.
*/
| > > > > > > > > > > > > > > > > > > > > > > | | | | | | > > > > > > > > > > > > > > > > > | > > > | > | < | < | < < | < < | > | | | > | | < | > | | | > | | | > > | > > > | < < > > | < < | > | > | > > > > > > > | | 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 |
blob_zero(&out);
htmlTidy(blob_str(&in), &out);
blob_reset(&in);
fossil_puts(blob_buffer(&out), 0, blob_size(&out));
blob_reset(&out);
}
}
#if INTERFACE
/*
** Allowed flag options for html_to_plaintext().
*/
#define HTOT_VT100 0x01 /* <mark> becomes ^[[91m */
#define HTOT_FLOW 0x02 /* Collapse internal whitespace to a single space */
#define HTOT_TRIM 0x04 /* Trim off leading and trailing whitespace */
#endif /* INTERFACE */
/*
** Add <MARK> or </MARK> to the output, or similar VT-100 escape
** codes.
*/
static void addMark(Blob *pOut, int mFlags, int isClose){
static const char *az[4] = { "<MARK>", "</MARK>", "\033[91m", "\033[0m" };
int i = 0;
if( isClose ) i++;
if( mFlags & HTOT_VT100 ) i += 2;
blob_append(pOut, az[i], -1);
}
/*
** Remove all HTML markup from the input text. The output written into
** pOut is pure text.
**
** Put the title on the first line, if there is any <title> markup.
** If there is no <title>, then create a blank first line.
*/
void html_to_plaintext(const char *zIn, Blob *pOut, int mFlags){
int n;
int i, j;
int bFlow = 0; /* Transform internal WS into a single space */
int prevWS = 1; /* Previous output was whitespace or start of msg */
int nMark = 0; /* True if inside of <mark>..</mark> */
for(i=0; fossil_isspace(zIn[i]); i++){}
if( i>0 && (mFlags & HTOT_TRIM)==0 ){
blob_append(pOut, zIn, i);
}
zIn += i;
if( mFlags & HTOT_FLOW ) bFlow = 1;
while( zIn[0] ){
n = html_token_length(zIn);
if( zIn[0]=='<' && n>1 ){
int isCloseTag;
int eTag;
int eType;
char zTag[32];
prevWS = 0;
isCloseTag = zIn[1]=='/';
for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){
zTag[i] = fossil_tolower(zIn[j]);
}
zTag[i] = 0;
eTag = findTag(zTag);
eType = aMarkup[eTag].iType;
if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){
zIn += n;
while( zIn[0] ){
n = html_token_length(zIn);
if( fossil_strnicmp(zIn, "</style",7)==0 ) break;
zIn += n;
}
if( zIn[0]=='<' ) zIn += n;
continue;
}
if( eTag==MARKUP_INVALID && strcmp(zTag,"mark")==0 ){
if( isCloseTag && nMark ){
addMark(pOut, mFlags, 1);
nMark = 0;
}else if( !isCloseTag && !nMark ){
addMark(pOut, mFlags, 0);
nMark = 1;
}
zIn += n;
continue;
}
if( eTag==MARKUP_TITLE ){
if( isCloseTag && (mFlags & HTOT_FLOW)==0 ){
bFlow = 0;
}else{
bFlow = 1;
}
}
if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){
blob_append_char(pOut, '\n');
}
}else if( fossil_isspace(zIn[0]) ){
if( bFlow==0 ){
if( zIn[n]==0 && (mFlags & HTOT_TRIM) ) break;
blob_append(pOut, zIn, n);
}else if( !prevWS ){
prevWS = 1;
blob_append_char(pOut, ' ');
zIn += n;
n = 0;
}
}else if( zIn[0]=='&' ){
u32 c = '?';
prevWS = 0;
if( zIn[1]=='#' ){
c = atoi(&zIn[2]);
if( c==0 ) c = '?';
}else{
static const struct { int n; u32 c; char *z; } aEntity[] = {
{ 5, '&', "&" },
{ 4, '<', "<" },
{ 4, '>', ">" },
{ 6, ' ', " " },
{ 6, '"', """ },
};
int jj;
for(jj=0; jj<count(aEntity); jj++){
if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){
c = aEntity[jj].c;
break;
}
}
}
if( c<0x00080 ){
blob_append_char(pOut, c & 0xff);
}else if( c<0x00800 ){
blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f));
blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
}else if( c<0x10000 ){
blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f));
blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f));
blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
}else{
blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07));
blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f));
blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f));
blob_append_char(pOut, 0x80 + (u8)(c&0x3f));
}
}else{
prevWS = 0;
blob_append(pOut, zIn, n);
}
zIn += n;
}
if( nMark ){
addMark(pOut, mFlags, 1);
}
}
/*
** COMMAND: test-html-to-text
**
** Usage: %fossil test-html-to-text [OPTIONS] FILE ...
**
** Read all files named on the command-line. Convert the file
** content from HTML to text and write the results on standard
** output.
**
** This command is intended as a test and debug interface for
** the html_to_plaintext() routine.
**
** Options:
**
** --vt100 Translate <mark> and </mark> into ANSI/VT100
** escapes to highlight the contained text.
*/
void test_html_to_text(void){
Blob in, out;
int i;
int mFlags = 0;
if( find_option("vt100",0,0)!=0 ) mFlags |= HTOT_VT100;
for(i=2; i<g.argc; i++){
blob_read_from_file(&in, g.argv[i], ExtFILE);
blob_zero(&out);
html_to_plaintext(blob_str(&in), &out, mFlags);
blob_reset(&in);
fossil_puts(blob_buffer(&out), 0, blob_size(&out));
blob_reset(&out);
}
}
/****************************************************************************
|
| ︙ | ︙ |
Changes to src/winfile.c.
| ︙ | ︙ | |||
302 303 304 305 306 307 308 |
** undesirable in file name comparison, so lstrcmpiW() is only invoked in cases
** that are technically impossible and contradicting all known laws of physics.
*/
int win32_filenames_equal_nocase(
const wchar_t *fn1,
const wchar_t *fn2
){
| > > > > > > > > > > > > | | | | | | < < < < | | | 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 |
** undesirable in file name comparison, so lstrcmpiW() is only invoked in cases
** that are technically impossible and contradicting all known laws of physics.
*/
int win32_filenames_equal_nocase(
const wchar_t *fn1,
const wchar_t *fn2
){
/* ---- Data types used by dynamically loaded API functions. -------------- */
typedef struct { /* UNICODE_STRING from <ntdef.h> */
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} MY_UNICODE_STRING;
/* ---- Prototypes for dynamically loaded API functions. ------------------ */
typedef int (WINAPI *FNCOMPARESTRINGORDINAL)(LPCWCH,int,LPCWCH,int,BOOL);
typedef VOID (NTAPI *FNRTLINITUNICODESTRING)(MY_UNICODE_STRING*,PCWSTR);
typedef BOOLEAN (NTAPI *FNRTLEQUALUNICODESTRING)
(MY_UNICODE_STRING*,MY_UNICODE_STRING*,BOOLEAN);
/* ------------------------------------------------------------------------ */
static FNCOMPARESTRINGORDINAL fnCompareStringOrdinal;
static FNRTLINITUNICODESTRING fnRtlInitUnicodeString;
static FNRTLEQUALUNICODESTRING fnRtlEqualUnicodeString;
static int loaded_CompareStringOrdinal;
static int loaded_RtlUnicodeStringAPIs;
if( !loaded_CompareStringOrdinal ){
fnCompareStringOrdinal = (FNCOMPARESTRINGORDINAL)
GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal");
loaded_CompareStringOrdinal = 1;
}
if( fnCompareStringOrdinal ){
return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0;
}
if( !loaded_RtlUnicodeStringAPIs ){
fnRtlInitUnicodeString = (FNRTLINITUNICODESTRING)
GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString");
fnRtlEqualUnicodeString = (FNRTLEQUALUNICODESTRING)
GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString");
loaded_RtlUnicodeStringAPIs = 1;
}
if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){
MY_UNICODE_STRING u1, u2;
fnRtlInitUnicodeString(&u1,fn1);
fnRtlInitUnicodeString(&u2,fn2);
return (BOOLEAN/*unsigned char*/)fnRtlEqualUnicodeString(&u1,&u2,1);
}
/* In what kind of strange parallel universe are we? */
return lstrcmpiW(fn1,fn2)==0;
}
/* Helper macros to deal with directory separators. */
#define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' )
|
| ︙ | ︙ | |||
459 460 461 462 463 464 465 |
** Call the GetFileInformationByHandleEx() function on Windows Vista, and resort
** to the GetFileInformationByHandle() function on Windows XP. The result string
** is allocated by mprintf(), or NULL on failure.
*/
char *win32_file_id(
const char *zFileName
){
| > > > > > > > > > | | < < < | > | | 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 |
** Call the GetFileInformationByHandleEx() function on Windows Vista, and resort
** to the GetFileInformationByHandle() function on Windows XP. The result string
** is allocated by mprintf(), or NULL on failure.
*/
char *win32_file_id(
const char *zFileName
){
/* ---- Data types used by dynamically loaded API functions. -------------- */
typedef struct { /* FILE_ID_INFO from <winbase.h> */
ULONGLONG VolumeSerialNumber;
BYTE FileId[16];
} MY_FILE_ID_INFO;
/* ---- Prototypes for dynamically loaded API functions. ------------------ */
typedef int (WINAPI *FNGETFILEINFORMATIONBYHANDLEEX)
(HANDLE,int/*enum*/,MY_FILE_ID_INFO*,DWORD);
/* ------------------------------------------------------------------------ */
static FNGETFILEINFORMATIONBYHANDLEEX fnGetFileInformationByHandleEx;
static int loaded_fnGetFileInformationByHandleEx;
wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0);
HANDLE hFile;
char *zFileId = 0;
hFile = CreateFileW(
wzFileName,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if( hFile!=INVALID_HANDLE_VALUE ){
BY_HANDLE_FILE_INFORMATION fi;
MY_FILE_ID_INFO fi2;
if( !loaded_fnGetFileInformationByHandleEx ){
fnGetFileInformationByHandleEx = (FNGETFILEINFORMATIONBYHANDLEEX)
GetProcAddress(
GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx");
loaded_fnGetFileInformationByHandleEx = 1;
}
if( fnGetFileInformationByHandleEx ){
if( fnGetFileInformationByHandleEx(
hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){
zFileId = mprintf(
"%016llx/"
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 |
fi2.FileId[5], fi2.FileId[4],
fi2.FileId[3], fi2.FileId[2],
fi2.FileId[1], fi2.FileId[0]);
}
}
if( zFileId==0 ){
if( GetFileInformationByHandle(hFile,&fi) ){
| | | | 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
fi2.FileId[5], fi2.FileId[4],
fi2.FileId[3], fi2.FileId[2],
fi2.FileId[1], fi2.FileId[0]);
}
}
if( zFileId==0 ){
if( GetFileInformationByHandle(hFile,&fi) ){
ULARGE_INTEGER FileId = {{
/*.LowPart = */ fi.nFileIndexLow,
/*.HighPart = */ fi.nFileIndexHigh
}};
zFileId = mprintf(
"%08x/%016llx",
fi.dwVolumeSerialNumber,(u64)FileId.QuadPart);
}
}
CloseHandle(hFile);
}
fossil_path_free(wzFileName);
return zFileId;
}
#endif /* _WIN32 -- This code is for win32 only */
|
Changes to src/winhttp.c.
| ︙ | ︙ | |||
411 412 413 414 415 416 417 418 419 |
}
/*
** The repository name is only needed if there was no open check-out. This
** is designed to allow the open check-out for the interactive user to work
** with the local Fossil server started via the "ui" command.
*/
zIp = SocketAddr_toString(&p->addr);
if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){
| > > > > > > > > > | < < < < < < < < < < < | 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 |
}
/*
** The repository name is only needed if there was no open check-out. This
** is designed to allow the open check-out for the interactive user to work
** with the local Fossil server started via the "ui" command.
*/
aux = fossil_fopen(zCmdFName, "wb");
if( aux==0 ) goto end_request;
fprintf(aux, "%s--in %s\n", get_utf8_bom(0), zRequestFName);
zIp = SocketAddr_toString(&p->addr);
fprintf(aux, "--out %s\n--ipaddr %s\n", zReplyFName, zIp);
fossil_free(zIp);
fprintf(aux, "--as %s\n", g.zCmdName);
if( g.zErrlog && g.zErrlog[0] ){
fprintf(aux,"--errorlog %s\n", g.zErrlog);
}
if( (p->flags & HTTP_SERVER_HAD_CHECKOUT)==0 ){
fprintf(aux,"%s",g.zRepositoryName);
}
sqlite3_snprintf(sizeof(zCmd), zCmd,
"\"%s\" http -args \"%s\"%s%s",
g.nameOfExe, zCmdFName,
g.httpUseSSL ? "" : " --nossl", p->zOptions
);
in = fossil_fopen(zReplyFName, "w+b");
|
| ︙ | ︙ | |||
996 997 998 999 1000 1001 1002 | ** ** Creates a service. Available options include: ** ** -D|--display DISPLAY-NAME ** ** Sets the display name of the service. This name is shown ** by graphical interface programs. By default, the display name | | | 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 | ** ** Creates a service. Available options include: ** ** -D|--display DISPLAY-NAME ** ** Sets the display name of the service. This name is shown ** by graphical interface programs. By default, the display name ** is equal to the service name. ** ** -S|--start TYPE ** ** Sets the start type of the service. TYPE can be "manual", ** which means you need to start the service yourself with the ** 'fossil winsrv start' command or with the "net start" command ** from the operating system. If TYPE is set to "auto", the service |
| ︙ | ︙ | |||
1019 1020 1021 1022 1023 1024 1025 | ** used. ** ** -W|--password PASSWORD ** ** Password for the user account. ** ** The following options are more or less the same as for the "server" | | | 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 | ** used. ** ** -W|--password PASSWORD ** ** Password for the user account. ** ** The following options are more or less the same as for the "server" ** command and influence the behavior of the http server: ** ** --baseurl URL ** ** Use URL as the base (useful for reverse proxies) ** ** -P|--port TCPPORT ** |
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 |
pXfer->resync = db_column_int(&q, 1)-1;
}
}
db_finalize(&q);
if( cnt==0 ) pXfer->resync = 0;
return cnt;
}
/*
** Send an igot message for every artifact.
*/
static void send_all(Xfer *pXfer){
Stmt q;
db_prepare(&q,
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
pXfer->resync = db_column_int(&q, 1)-1;
}
}
db_finalize(&q);
if( cnt==0 ) pXfer->resync = 0;
return cnt;
}
/*
** Send an igot message for every cluster artifact that is not a phantom,
** is not shunned, is not private, and that is not in the UNCLUSTERED table.
** Return the number of cards sent.
*/
static int send_all_clusters(Xfer *pXfer){
Stmt q;
int cnt = 0;
const char *zExtra;
if( db_table_exists("temp","onremote") ){
zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)";
}else{
zExtra = "";
}
db_prepare(&q,
"SELECT uuid"
" FROM tagxref JOIN blob ON tagxref.rid=blob.rid AND tagxref.tagid=%d"
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
" AND NOT EXISTS(SELECT 1 FROM unclustered WHERE rid=blob.rid)"
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s",
TAG_CLUSTER, zExtra /*safe-for-%s*/
);
while( db_step(&q)==SQLITE_ROW ){
if( cnt==0 ) blob_appendf(pXfer->pOut, "# sending-clusters\n");
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
cnt++;
}
db_finalize(&q);
if( cnt ) blob_appendf(pXfer->pOut, "# end-of-clusters\n");
return cnt;
}
/*
** Send an igot message for every artifact.
*/
static void send_all(Xfer *pXfer){
Stmt q;
db_prepare(&q,
|
| ︙ | ︙ | |||
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 |
int sz = db_column_int(&uvq,3);
if( zHash==0 ){ sz = 0; zHash = "-"; }
blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n",
zName, mtime, zHash, sz);
}
db_finalize(&uvq);
}
/*
** Called when there is an attempt to transfer private content to and
** from a server without authorization.
*/
static void server_private_xfer_not_authorized(void){
| > > > > > > > > > > > > | | 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 |
int sz = db_column_int(&uvq,3);
if( zHash==0 ){ sz = 0; zHash = "-"; }
blob_appendf(pXfer->pOut, "uvigot %s %lld %s %d\n",
zName, mtime, zHash, sz);
}
db_finalize(&uvq);
}
/*
** Return a string that contains supplemental information about a
** "not authorized" error. The string might be empty if no additional
** information is available.
*/
static char *whyNotAuth(void){
if( g.useLocalauth && db_get_boolean("localauth",0)!=0 ){
return "\\sbecause\\sthe\\s'localauth'\\ssetting\\sis\\senabled";
}
return "";
}
/*
** Called when there is an attempt to transfer private content to and
** from a server without authorization.
*/
static void server_private_xfer_not_authorized(void){
@ error not\sauthorized\sto\ssync\sprivate\scontent%s(whyNotAuth())
}
/*
** Return the common TH1 code to evaluate prior to evaluating any other
** TH1 transfer notification scripts.
*/
const char *xfer_common_code(void){
|
| ︙ | ︙ | |||
1281 1282 1283 1284 1285 1286 1287 |
** file HASH DELTASRC SIZE \n CONTENT
**
** Server accepts a file from the client.
*/
if( blob_eq(&xfer.aToken[0], "file") ){
if( !isPush ){
cgi_reset_content();
| | | | 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 |
** file HASH DELTASRC SIZE \n CONTENT
**
** Server accepts a file from the client.
*/
if( blob_eq(&xfer.aToken[0], "file") ){
if( !isPush ){
cgi_reset_content();
@ error not\sauthorized\sto\swrite%s(whyNotAuth())
nErr++;
break;
}
xfer_accept_file(&xfer, 0, pzUuidList, pnUuidList);
if( blob_size(&xfer.err) ){
cgi_reset_content();
@ error %T(blob_str(&xfer.err))
nErr++;
break;
}
}else
/* cfile HASH USIZE CSIZE \n CONTENT
** cfile HASH DELTASRC USIZE CSIZE \n CONTENT
**
** Server accepts a compressed file from the client.
*/
if( blob_eq(&xfer.aToken[0], "cfile") ){
if( !isPush ){
cgi_reset_content();
@ error not\sauthorized\sto\swrite%s(whyNotAuth())
nErr++;
break;
}
xfer_accept_compressed_file(&xfer, pzUuidList, pnUuidList);
if( blob_size(&xfer.err) ){
cgi_reset_content();
@ error %T(blob_str(&xfer.err))
|
| ︙ | ︙ | |||
1426 1427 1428 1429 1430 1431 1432 |
nErr++;
break;
}
login_check_credentials();
if( blob_eq(&xfer.aToken[0], "pull") ){
if( !g.perm.Read ){
cgi_reset_content();
| | | | | > | 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 |
nErr++;
break;
}
login_check_credentials();
if( blob_eq(&xfer.aToken[0], "pull") ){
if( !g.perm.Read ){
cgi_reset_content();
@ error not\sauthorized\sto\sread%s(whyNotAuth())
nErr++;
break;
}
isPull = 1;
}else{
if( !g.perm.Write ){
if( !isPull ){
cgi_reset_content();
@ error not\sauthorized\sto\swrite%s(whyNotAuth())
nErr++;
}else{
@ message pull\sonly\s-\snot\sauthorized\sto\spush%s(whyNotAuth())
}
}else{
isPush = 1;
}
}
}else
/* clone ?PROTOCOL-VERSION? ?SEQUENCE-NUMBER?
**
** The client knows nothing. Tell all.
*/
if( blob_eq(&xfer.aToken[0], "clone") ){
int iVers;
login_check_credentials();
if( !g.perm.Clone ){
cgi_reset_content();
@ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
@ error not\sauthorized\sto\sclone%s(whyNotAuth())
nErr++;
break;
}
if( db_get_boolean("uv-sync",0) && !uvCatalogSent ){
@ pragma uv-pull-only
send_unversioned_catalog(&xfer);
uvCatalogSent = 1;
}
if( xfer.nToken==3
&& blob_is_int(&xfer.aToken[1], &iVers)
&& iVers>=2
){
int seqno, max;
if( iVers>=3 ){
cgi_set_content_type("application/x-fossil-uncompressed");
}
blob_is_int(&xfer.aToken[2], &seqno);
if( seqno<=0 ){
xfer_fatal_error("invalid clone sequence number");
db_rollback_transaction();
return;
}
max = db_int(0, "SELECT max(rid) FROM blob");
while( xfer.mxSend>(int)blob_size(xfer.pOut) && seqno<=max){
if( time(NULL) >= xfer.maxTime ) break;
if( iVers>=3 ){
send_compressed_file(&xfer, seqno);
|
| ︙ | ︙ | |||
1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 |
*/
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
&& blob_is_int(&xfer.aToken[2], &size) ){
const char *zName = blob_str(&xfer.aToken[1]);
Blob content;
if( size<0 ){
xfer_fatal_error("invalid config record");
return;
}
blob_zero(&content);
blob_extract(xfer.pIn, size, &content);
if( !g.perm.Admin ){
cgi_reset_content();
| > | | 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 |
*/
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
&& blob_is_int(&xfer.aToken[2], &size) ){
const char *zName = blob_str(&xfer.aToken[1]);
Blob content;
if( size<0 ){
xfer_fatal_error("invalid config record");
db_rollback_transaction();
return;
}
blob_zero(&content);
blob_extract(xfer.pIn, size, &content);
if( !g.perm.Admin ){
cgi_reset_content();
@ error not\sauthorized\sto\spush\sconfiguration%s(whyNotAuth())
nErr++;
break;
}
configure_receive(zName, &content, CONFIGSET_ALL);
blob_reset(&content);
blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
}else
|
| ︙ | ︙ | |||
1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 |
/* pragma req-links
**
** The client sends this message to the server to ask the server
** to tell it about alternative repositories in the reply.
*/
if( blob_eq(&xfer.aToken[1], "req-links") ){
bSendLinks = 1;
}
}else
/* Unknown message
*/
{
| > > > > > > > > > | 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 |
/* pragma req-links
**
** The client sends this message to the server to ask the server
** to tell it about alternative repositories in the reply.
*/
if( blob_eq(&xfer.aToken[1], "req-links") ){
bSendLinks = 1;
}else
/* pragma req-clusters
**
** This pragma requests that the server send igot cards for every
** cluster artifact that it knows about.
*/
if( blob_eq(&xfer.aToken[1], "req-clusters") ){
send_all_clusters(&xfer);
}
}else
/* Unknown message
*/
{
|
| ︙ | ︙ | |||
1979 1980 1981 1982 1983 1984 1985 |
){
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 */
| | | 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 |
){
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 */
int nFileRecv = 0; /* Number of files received */
int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
const char *zCookie; /* Server cookie */
i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */
i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
int cloneSeqno = 1; /* Sequence number for clones */
Blob send; /* Text we are sending to the server */
Blob recv; /* Reply we got back from the server */
|
| ︙ | ︙ | |||
2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 | const char *zOpType = 0;/* Push, Pull, Sync, Clone */ double rSkew = 0.0; /* Maximum time skew */ int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ int uvDoPush = 0; /* Generate uvfile messages to send to server */ int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */ 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)); | > | 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 | const char *zOpType = 0;/* Push, Pull, Sync, Clone */ double rSkew = 0.0; /* Maximum time skew */ int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ int uvDoPush = 0; /* Generate uvfile messages to send to server */ int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */ int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ int nGimmeRcvd = 0; /* Number of gimme cards recevied on the prev 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)); |
| ︙ | ︙ | |||
2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 |
/* Client sends gimme cards for phantoms
*/
if( (syncFlags & SYNC_PULL)!=0
|| ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
){
request_phantoms(&xfer, mxPhantomReq);
}
if( syncFlags & SYNC_PUSH ){
send_unsent(&xfer);
nCardSent += send_unclustered(&xfer);
if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
}
/* Client sends configuration parameter requests. On a clone, delay sending
** this until the second cycle since the login card might fail on
** the first cycle.
*/
if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
| > > > > > > | 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 |
/* Client sends gimme cards for phantoms
*/
if( (syncFlags & SYNC_PULL)!=0
|| ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
){
request_phantoms(&xfer, mxPhantomReq);
if( xfer.nGimmeSent>0 && nCycle==2 && (syncFlags & SYNC_PULL)!=0 ){
blob_appendf(&send, "pragma req-clusters\n");
}
}
if( syncFlags & SYNC_PUSH ){
send_unsent(&xfer);
nCardSent += send_unclustered(&xfer);
if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
if( nGimmeRcvd>0 && nCycle==2 ){
send_all_clusters(&xfer);
}
}
/* Client sends configuration parameter requests. On a clone, delay sending
** this until the second cycle since the login card might fail on
** the first cycle.
*/
if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
|
| ︙ | ︙ | |||
2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 |
/* Remember the URL of the sync target in the config file on the
** first successful round-trip */
if( nCycle==0 && db_is_writeable("repository") ){
xfer_syncwith(g.url.canonical, 0);
}
/* Output current stats */
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{
| > > < < | 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 |
/* Remember the URL of the sync target in the config file on the
** first successful round-trip */
if( nCycle==0 && db_is_writeable("repository") ){
xfer_syncwith(g.url.canonical, 0);
}
/* Output current stats */
nRoundtrip++;
nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
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{
if( bOutIsTty!=0 ){
fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
nRoundtrip, nArtifactSent, nArtifactRcvd);
}
}
nCardSent = 0;
nCardRcvd = 0;
|
| ︙ | ︙ | |||
2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 |
if( syncFlags & SYNC_PUSH ){
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
nCardSent++;
}
go = 0;
nUvGimmeSent = 0;
nUvFileRcvd = 0;
nPriorArtifact = nArtifactRcvd;
/* Process the reply that came back from the server */
while( blob_line(&recv, &xfer.line) ){
if( blob_buffer(&xfer.line)[0]=='#' ){
const char *zLine = blob_buffer(&xfer.line);
if( memcmp(zLine, "# timestamp ", 12)==0 ){
| > | 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 |
if( syncFlags & SYNC_PUSH ){
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
nCardSent++;
}
go = 0;
nUvGimmeSent = 0;
nUvFileRcvd = 0;
nGimmeRcvd = 0;
nPriorArtifact = nArtifactRcvd;
/* Process the reply that came back from the server */
while( blob_line(&recv, &xfer.line) ){
if( blob_buffer(&xfer.line)[0]=='#' ){
const char *zLine = blob_buffer(&xfer.line);
if( memcmp(zLine, "# timestamp ", 12)==0 ){
|
| ︙ | ︙ | |||
2447 2448 2449 2450 2451 2452 2453 |
if( blob_eq(&xfer.aToken[0], "gimme")
&& xfer.nToken==2
&& blob_is_hname(&xfer.aToken[1])
){
remote_unk(&xfer.aToken[1]);
if( syncFlags & SYNC_PUSH ){
int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
| > | > > | 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 |
if( blob_eq(&xfer.aToken[0], "gimme")
&& xfer.nToken==2
&& blob_is_hname(&xfer.aToken[1])
){
remote_unk(&xfer.aToken[1]);
if( syncFlags & SYNC_PUSH ){
int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
if( rid ){
send_file(&xfer, rid, &xfer.aToken[1], 0);
nGimmeRcvd++;
}
}
}else
/* igot HASH ?PRIVATEFLAG?
**
** Server announces that it has a particular file. If this is
** not a file that we have and we are pulling, then create a
|
| ︙ | ︙ |
Changes to src/zip.c.
| ︙ | ︙ | |||
560 561 562 563 564 565 566 567 568 569 |
fossil_free(azDir[i]);
}
fossil_free(azDir);
nDir = 0;
azDir = 0;
}
/*
** COMMAND: test-filezip
**
| > > > > > > > > | | > > > > > > > > > > > > > < < < < < | > | < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | | > > > | | | | | > | 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 |
fossil_free(azDir[i]);
}
fossil_free(azDir);
nDir = 0;
azDir = 0;
}
/* Functions found in shell.c */
extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_zipfile_init(sqlite3*,char**,const sqlite3_api_routines*);
/*
** COMMAND: test-filezip
**
** Usage: %fossil test-filezip [OPTIONS] ZIPFILE [FILENAME...]
**
** This command uses Fossil infrastructure or read or create a ZIP
** archive named by the ZIPFILE argument. With no options, a new
** ZIP archive is created and there must be at least one FILENAME
** argument. If the -l option is used, the contents of the named ZIP
** archive are listed on standard output. With the -x argument, the
** contents of the ZIP archive are extracted.
**
** There are two purposes for this command: (1) To server as a test
** platform for the Fossil ZIP archive generator, and (2) to provide
** rudimentary ZIP archive creation capabilities on platforms that do
** not have the "zip" command installed.
**
** Options:
**
** -h|--dereference Follow symlinks
** -l|--list List the contents of the ZIP archive
** -x|--extract Extract files from a ZIP archive
*/
void filezip_cmd(void){
int eFType = SymFILE;
int doList = 0;
int doExtract = 0;
char *zArchiveName;
if( find_option("dereference","h",0)!=0 ){
eFType = ExtFILE;
}
if( find_option("list","l",0)!=0 ){
doList = 1;
}
if( find_option("extract","x",0)!=0 ){
if( doList ){
fossil_fatal("incompatible options: -l and -x");
}
doExtract = 1;
}
if( g.argc<3 ){
usage("ARCHIVE FILES...");
}
zArchiveName = g.argv[2];
sqlite3_open(":memory:", &g.db);
if( doList ){
/* Do a content listing of a ZIP archive */
Stmt q;
int nRow = 0;
i64 szTotal = 0;
if( file_size(zArchiveName, eFType)<0 ){
fossil_fatal("No such ZIP archive: %s", zArchiveName);
}
if( g.argc>3 ){
fossil_fatal("extra arguments after \"fossil test-filezip -l ARCHIVE\"");
}
sqlite3_zipfile_init(g.db, 0, 0);
db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
db_prepare(&q, "SELECT sz, datetime(mtime,'unixepoch'), name FROM z1");
while( db_step(&q)==SQLITE_ROW ){
int sz = db_column_int(&q, 0);
szTotal += sz;
if( nRow==0 ){
fossil_print(" Length Date Time Name\n");
fossil_print("--------- ---------- ----- ----\n");
}
nRow++;
fossil_print("%9d %.16s %s\n", sz, db_column_text(&q,1),
db_column_text(&q,2));
}
if( nRow ){
fossil_print("--------- --------\n");
fossil_print("%9lld %16s %d files\n", szTotal, "", nRow);
}
db_finalize(&q);
}else if( doExtract ){
/* Extract files from an existing ZIP archive */
if( file_size(zArchiveName, eFType)<0 ){
fossil_fatal("No such ZIP archive: %s", zArchiveName);
}
if( g.argc>3 ){
fossil_fatal("extra arguments after \"fossil test-filezip -x ARCHIVE\"");
}
sqlite3_zipfile_init(g.db, 0, 0);
sqlite3_fileio_init(g.db, 0, 0);
db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
db_multi_exec("SELECT writefile(name,data) FROM z1");
}else{
/* Without the -x or -l options, construct a new ZIP archive */
int i;
Blob zip;
Blob file;
Archive sArchive;
memset(&sArchive, 0, sizeof(Archive));
sArchive.eType = ARCHIVE_ZIP;
sArchive.pBlob = &zip;
if( file_size(zArchiveName, eFType)>0 ){
fossil_fatal("ZIP archive %s already exists", zArchiveName);
}
zip_open();
for(i=3; i<g.argc; i++){
double rDate;
i64 iDate;
blob_zero(&file);
blob_read_from_file(&file, g.argv[i], eFType);
iDate = file_mtime(g.argv[i], eFType);
rDate = ((double)iDate)/86400.0 + 2440587.5;
zip_set_timedate(rDate);
zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType));
blob_reset(&file);
}
zip_close(&sArchive);
blob_write_to_file(&zip, g.argv[2]);
}
}
/*
** Given the RID for a manifest, construct a ZIP archive containing
** all files in the corresponding baseline.
**
** If RID is for an object that is not a real manifest, then the
|
| ︙ | ︙ | |||
650 651 652 653 654 655 656 |
nPrefix = blob_size(&filename);
pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
if( pManifest ){
int flg, eflg = 0;
char *zName = 0;
zip_set_timedate(pManifest->rDate);
| | | 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 |
nPrefix = blob_size(&filename);
pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
if( pManifest ){
int flg, eflg = 0;
char *zName = 0;
zip_set_timedate(pManifest->rDate);
flg = db_get_manifest_setting(blob_str(&hash));
if( flg ){
/* eflg is the effective flags, taking include/exclude into account */
if( (pInclude==0 || glob_match(pInclude, "manifest"))
&& !glob_match(pExclude, "manifest")
&& (flg & MFESTFLG_RAW) ){
eflg |= MFESTFLG_RAW;
}
|
| ︙ | ︙ | |||
925 926 927 928 929 930 931 932 933 934 935 936 937 938 |
char *zType; /* Human-readable archive type */
login_check_credentials();
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
if( fossil_strcmp(g.zPath, "sqlar")==0 ){
eType = ARCHIVE_SQLAR;
zType = "SQL";
}else{
eType = ARCHIVE_ZIP;
zType = "ZIP";
}
fossil_nice_default();
zName = fossil_strdup(PD("name",""));
z = P("r");
| > > > | 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 |
char *zType; /* Human-readable archive type */
login_check_credentials();
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
if( fossil_strcmp(g.zPath, "sqlar")==0 ){
eType = ARCHIVE_SQLAR;
zType = "SQL";
/* For some reason, SQL-archives are like catnip for robots. So
** don't allow them to be downloaded by user "nobody" */
if( g.zLogin==0 ){ login_needed(g.anon.Zip); return; }
}else{
eType = ARCHIVE_ZIP;
zType = "ZIP";
}
fossil_nice_default();
zName = fossil_strdup(PD("name",""));
z = P("r");
|
| ︙ | ︙ |
Deleted test/comment.test.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Added test/link-tester.html.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
<!DOCTYPE html>
<head><!--
This file is intended to be loaded from a fossil
repository, either using:
fossil ui --extpage test/link-tester.html
or by adding test/link-tester.* to uv and then:
fossil ui -page uv/link-tester.html
--></head>
<style>
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
header {
margin: 0.5em 0 0 0;
padding: 0 1em 0 1em;
z-index: 1;
}
#controlWrapper {
display: flex;
flex-direction: row;
border-bottom: 2px dotted;
padding-bottom: 0.5em;
}
#controlWrapper > button {
flex-grow: 1;
margin: 0.5em;
}
#selectWrapper {
display: flex;
flex-direction: column;
flex-grow: 8;
}
#selectPage {
flex-grow: 1;
margin: 1em;
padding: 1em;
}
#currentUrl {
font-family: monospace;
text-align: center;
}
#iframe {
flex-grow: 1; border: none; margin: 0; padding: 0;
display: block;
/* Absolute positioning is apparently the only way to get
the iframe to stretch to fill the page, but we have to
set its Y coordinate to something a bit below #controls. */
width: 100%;
height: calc(100% - 5em);
position: absolute;
top: 4em;
}
</style>
<body>
<header>
Fossil link test app. Select links from the list below to load
them. Use the arrow keys to cycle through the list. The links are
loaded within an iframe, so navigation within it will stay within
that frame.
</header>
<header id='controlWrapper'>
<button id='btn-prev'>←</button>
<div id='selectWrapper'>
<select id='selectPage'>
<option>/timeline</option>
<option>/dir</option>
</select>
<a target='_blank' id='currentUrl'></a>
</div>
<button id='btn-next'>→</button>
</header>
<iframe id='iframe'><!--populated via the UI--></iframe>
<script src='link-tester.js'></script>
<body>
|
Added test/link-tester.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
/**
JS code for link-tester.html. We cannot host this JS inline in that
file because fossil's default Content Security Policy won't let it
run that way.
*/
window.addEventListener("DOMContentLoaded", function(){
const E = function(s){
const e = document.querySelector(s);
if( !e ) throw new Error("Missing element: "+s);
return e;
};
const EAll = function(s){
const e = document.querySelectorAll(s);
if( !e || !e.length ) throw new Error("Missing elements: "+s);
return e;
};
const eIframe = E('#iframe');
const eSelect = E('#selectPage');
const eCurrentUrl = E('#currentUrl');
/*
Prepend the fossil instance's URL to each link. We have to guess
which part of the URL is the fossil CGI/server instance. The
following works when run (A) from under /uv or /ext and (B) from
/doc/branchname/test/link-tester.html.
*/
let urlTop;
let loc = (''+window.location);
let aLoc = loc.split('/')
aLoc.pop(); /* file name */
const thisDir = aLoc.join('/');
const rxDoc = /.*\/doc\/[^/]+\/.*/;
//console.log(rxDoc, loc, aLoc);
if( loc.match(rxDoc) ){
/* We're hopefully now at the top-most fossil-served
URL. */
aLoc.pop(); aLoc.pop(); /* /doc/foo */
aLoc.pop(); /* current dir name */
}else{
aLoc.pop(); /* current dir name */
}
urlTop = aLoc.join('/');
//console.log(urlTop, aLoc);
for( const o of eSelect.options ){
o.value = urlTop + (o.value || o.innerText);
}
const updateUrl = function(opt){
if( opt ){
let url = (opt.value || opt.innerText);
eCurrentUrl.innerText = url.replace(urlTop,'');
eCurrentUrl.setAttribute('href', url);
}else{
eCurrentUrl.innerText = '';
}
};
eSelect.addEventListener('change',function(ev){
const so = ev.target.options[ev.target.selectedIndex];
if( so ){
eIframe.setAttribute('src', so.value || so.innerText);
updateUrl(so);
}
});
/** Select the entry at the given ndx and fire a change event. */
const selectEntry = function(ndx){
if( ndx>=0 ){
eSelect.selectedIndex = ndx;
eSelect.dispatchEvent(new Event('change',{target:eSelect}));
}
};
/* Cycle to the next link in the list, accounting for separators and
wrapping around at either end. */
const cycleLink = function(dir/*<0 = prev, >0 = next*/){
let n = eSelect.selectedIndex + dir;
if( n < 0 ) n = eSelect.options.length-1;
else if( n>=eSelect.options.length ){
n = 0;
}
const opt = eSelect.options[n];
if( opt && opt.disabled ){
/* If that OPTION element is disabled, skip over it. */
eSelect.selectedIndex = n;
cycleLink(dir);
}else{
selectEntry(n);
}
};
E('#btn-prev').addEventListener('click', ()=>cycleLink(-1), false);
E('#btn-next').addEventListener('click', ()=>cycleLink(1), false);
/**
We have to adjust the iframe's size dynamically to account for
other widgets around it. iframes don't simply like to fill up all
available space without some help. If #controlWrapper only
contained the one SELECT element, CSS would be sufficient, but
once we add text around it, #controlWrapper's size becomes
unpredictable and we need JS to calculate it. We do this every
time the window size changes.
*/
const effectiveHeight = function f(e){
// Copied from fossil.dom.js
if(!e) return 0;
if(!f.measure){
f.measure = function callee(e, depth){
if(!e) return;
const m = e.getBoundingClientRect();
if(0===depth){
callee.top = m.top;
callee.bottom = m.bottom;
}else{
callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
callee.bottom = Math.max(callee.bottom, m.bottom);
}
Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
if(0===depth){
//console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
f.extra += callee.bottom - callee.top;
}
return f.extra;
};
}
f.extra = 0;
f.measure(e,0);
return f.extra;
};
/* Helper for the window-resized event handler below, to avoid
handling the resize until after it's finished. */
const debounce = function f(func, waitMs, immediate) {
// Copied from fossil.bootstrap.js
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);
};
};
/**
Resize eConstrained (the ifame element) so that it fits within
the page space not occupied by the list of elements eToAvoid.
*/
const ForceResizeKludge = (function(eToAvoid, eConstrained){
const resized = function f(){
if( f.$disabled ) return;
const wh = window.innerHeight;
let ht;
let extra = 0;
eToAvoid.forEach((e)=>e ? extra += effectiveHeight(e) : false);
ht = wh - extra;
if( ht < 100 ) ht = 100;
eConstrained.style.top = 'calc('+extra+'px + 2em)';
eConstrained.style.height =
eConstrained.style.maxHeight = "calc("+ ht+ "px - 2em)";
};
resized.$disabled = true/* gets deleted later */;
window.addEventListener('resize', debounce(resized, 250), false);
return resized;
})(
EAll('body > *:not(iframe)'),
eIframe
);
delete ForceResizeKludge.$disabled;
ForceResizeKludge();
selectEntry(0);
/**
Read link-tester.json, which should live in the same directory
as this file. It's expected to be an array with entries
in one of the following forms:
- "string" = Separator label (disabled)
- ["/path"] = path with itself as a label
- ["label", "/path"] = path with the given label
All paths are expected to have a "/" prefix and this script
accounts for mapping that to the fossil part of this script's
URL.
*/
window.fetch(thisDir+'/link-tester.json').then((r)=>r.json()).then(j=>{
//console.log("fetched",j);
eSelect.innerHTML = '';
const opt = function(arg){
const o = document.createElement('option');
//console.warn(arguments);
let rc = true;
if( 'string' === typeof arg ){
/* Grouping separator */
o.innerText = "--- " + arg + " ---";
o.setAttribute('disabled','');
rc = false;
}else if( 1===arg.length ){
o.innerText = arg[0];
o.value = urlTop + arg[0];
}else if( 2==arg.length ){
o.innerText = arg[0];
o.value = urlTop + arg[1];
}
eSelect.appendChild(o);
return rc;
};
let ndx = -1/*index of first non-disabled entry*/, i = 0;
for(const e of j){
if( opt(e) && ndx<0 ){
ndx = i;
}
++i;
}
selectEntry(ndx);
});
});
|
Added test/link-tester.json.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
[
"Timelines",
["Default", "/timeline"],
["anonymous", "/timeline?u=anonymous&y=a"],
["after date/time", "/timeline?n=12&y=ci&a=2024-12-31T20:29Z"],
["after hash", "/timeline?n=12&y=ci&a=3cb092c0e2f0ff26"],
["before date/time", "/timeline?n=12&y=ci&b=2024-12-31T20:30Z"],
["before hash", "/timeline?n=12&y=ci&b=3cb092c0e2f0ff26"],
["circa date/time", "/timeline?n=12&y=ci&c=2024-12-31T20:29Z"],
["circa hash", "/timeline?n=12&y=ci&c=3cb092c0e2f0ff26"],
["d=,p=", "/timeline?d=version-2.25&p=version-2.26"],
["from=,ft=", "/timeline?from=2765f04694d36e68&ft=release"],
["from=,ft=,min", "/timeline?from=2765f04694d36e68&ft=release&min"],
["from=,to=", "/timeline?from=version-2.25&to=version-2.26"],
["from=,to=,min", "/timeline?from=version-2.25&to=version-2.26&min"],
["omit-cr branch", "/timeline?r=omit-cr&m&c=7e97f4999b16ab75"],
["diff-eolws branch", "/timeline?r=diff-eolws&n=50"],
["Shortest path (from=,to=)",
"/timeline?from=e663bac6f7&to=a298a0e2f9&shortest"],
["Common Ancestor (me=,you=)",
"/timeline?me=e663bac6f7&you=a298a0e2f9"],
"Diff",
["Multiple edits on a single line", "/info/030035345c#chunk73"],
["Tricky alignment, multiple edits per line",
"/fdiff?v1=6da016415dc52d61&v2=af6df3466e3c4a88"],
["Column alignment with multibyte characters",
"/fdiff?v1=d1c60722e0b9d775&v2=58d1a8991bacb113"],
["Large diff of sqlite3.c - was once very slow",
"/fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6"],
["A difficult indentation change", "/info/bda00cbada#chunk49"],
["Inverse of the previous",
"/fdiff?v1=bc8100c9ee01b8c2&v2=1d2acc1a2a65c2bf#chunk42"],
["Another tricky indentation",
"/fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk13"],
["Inverse of the previous",
"/fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk13"],
["A tricky alignment",
"/fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk24"],
["sqlite3.c changes that are difficult to align",
"/fdiff?v1=21f9a00fe2fa4a17&v2=d5c4ff0532bd89c3#chunk5"],
["Lorem Ipsum in Greek", "/fdiff?v1=4f70c682e44f&v2=55659c6e062994f"],
["Inverted Greek Lorem Ipsum", "/fdiff?v2=4f70c682e44f&v1=55659c6e062994f"],
"Infos",
["Merge riser coalescing #1", "/info/eed3946bd92a499?diff=0"],
["Merge riser coalescing #2", "/info/ef6979eac9abded?diff=0"],
["Merge riser coalescing #3", "/info/9e1fa626e47f147?diff=0"],
["Merge riser coalescing #4", "/info/68bd2e7bedb8d05?diff=0"],
["Merge riser coalescing #5", "/info/7766e689926c703?diff=0"],
["Merge riser coalescing #6", "/info/3ea66260b5555d2?diff=0"],
["Merge riser coalescing #7", "/info/66ae70a54b20656?diff=0"],
["Context graph #1", "/info/b0f2a0ac53926c9?diff=0"],
["Context graph #2", "/info/303e7af7c31866c?diff=0"],
["Context graph #3", "/info/b31afcc2cab1dc4?diff=0"],
["Context graph #4", "/info/1a164e5fb76a46b?diff=0"],
["Context graph #5", "/info/2d75e87b760c0a9?diff=0"],
["Context graph #6", "/info/76442af7e13267bd?diff=0"],
["Info about the tip", "/info/tip"],
["/info/tip"],
"Admin",
["Users", "/setup_ulist"],
"Misc.",
["/skins"],
["/chat"]
]
|
Changes to test/many-www.tcl.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 | /taglist /reportlist /setup /dir /wcontent /attachlist /taglist | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | /taglist /reportlist /setup /dir /wcontent /attachlist /taglist /test-env /stat /rcvfromlist /urllist /modreq /info/d5c4 /test-all-help /leaves |
| ︙ | ︙ |
Changes to test/merge1.test.
| ︙ | ︙ | |||
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 |
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t23 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line ONE of the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line one OF the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line one OF the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line ONE of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
| > > > > | 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 |
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t23 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line ONE of the demo program - 1111
####### SUGGESTED CONFLICT RESOLUTION follows ###################
111 - This is line ONE OF the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line one OF the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line one OF the demo program - 1111
####### SUGGESTED CONFLICT RESOLUTION follows ###################
111 - This is line ONE OF the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line ONE of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
|
| ︙ | ︙ | |||
157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
000 - Zero lines added to the beginning of - 0000
111 - This is line one of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
| > > > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
####### SUGGESTED CONFLICT RESOLUTION follows ###################
000 - Zero lines added to the beginning of - 0000
111 - This is line one of the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
000 - Zero lines added to the beginning of - 0000
111 - This is line one of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
|
| ︙ | ︙ | |||
303 304 305 306 307 308 309 310 311 312 313 314 315 316 | efgh 2 ijkl 2 mnop 2 qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst uvwx | > > > > > > > > > | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | efgh 2 ijkl 2 mnop 2 qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ####### SUGGESTED CONFLICT RESOLUTION follows ################### efgh 2 ijkl 2 mnop 3 qrst 3 uvwx 3 yzAB 3 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst uvwx |
| ︙ | ︙ | |||
370 371 372 373 374 375 376 377 378 379 380 381 382 383 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 2) efgh 2 ijkl 2 mnop qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst | > > > > > > > > > | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 2) efgh 2 ijkl 2 mnop qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ####### SUGGESTED CONFLICT RESOLUTION follows ################### efgh 2 ijkl 2 mnop 3 qrst 3 uvwx 3 yzAB 3 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst |
| ︙ | ︙ |
Changes to test/merge3.test.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
set x [read_file t4]
regsub -all \
{<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \
$x {MINE:} x
regsub -all \
{\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \
$x {COM:} x
regsub -all \
{======= MERGED IN content follows =+ \(line \d+\)} \
$x {YOURS:} x
regsub -all \
| > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
set x [read_file t4]
regsub -all \
{<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \
$x {MINE:} x
regsub -all \
{####### SUGGESTED CONFLICT RESOLUTION follows #+} \
$x {BOT:} x
regsub -all \
{\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \
$x {COM:} x
regsub -all \
{======= MERGED IN content follows =+ \(line \d+\)} \
$x {YOURS:} x
regsub -all \
|
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
merge-test 3 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6 7 8 9
} {
1 2 3 4 5c 6 7 8 9
} {
| | | | | | | | 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 |
merge-test 3 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6 7 8 9
} {
1 2 3 4 5c 6 7 8 9
} {
1 2 MINE: 3b 4b 5b BOT: 3b 4b 5c COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9
} -expectError
merge-test 4 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8 9
} {
1 2 3 4 5c 6 7 8 9
} {
1 2 MINE: 3b 4b 5b 6b BOT: 3b 4b 5b 5c 6 COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9
} -expectError
merge-test 5 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8 9
} {
1 2 3 4 5c 6c 7c 8 9
} {
1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9
} -expectError
merge-test 6 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8b 9
} {
1 2 3 4 5c 6c 7c 8 9
} {
1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9
} -expectError
merge-test 7 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8b 9
} {
1 2 3 4 5c 6c 7c 8c 9
} {
1 2 MINE: 3b 4b 5b 6b 7 8b BOT: 3b 4b 5b 5c 6c 7c 8c COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9
} -expectError
merge-test 8 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8b 9b
} {
1 2 3 4 5c 6c 7c 8c 9
} {
1 2 MINE: 3b 4b 5b 6b 7 8b 9b BOT: 3b 4b 5b 5c 6c 7c 8c 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END
} -expectError
merge-test 9 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5 6 7 8b 9b
} {
1 2 3 4 5c 6c 7c 8 9
|
| ︙ | ︙ | |||
144 145 146 147 148 149 150 |
merge-test 11 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5 6 7 8b 9b
} {
1 2 3b 4c 5 6c 7c 8 9
} {
| | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
merge-test 11 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5 6 7 8b 9b
} {
1 2 3b 4c 5 6c 7c 8 9
} {
1 2 MINE: 3b 4b BOT: 3b 4c COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b
} -expectError
merge-test 12 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b4b 5 6 7 8b 9b
} {
1 2 3b4b 5 6c 7c 8 9
|
| ︙ | ︙ | |||
199 200 201 202 203 204 205 |
merge-test 24 {
1 2 3 4 5 6 7 8 9
} {
1 6 7 8 9
} {
1 2 3 4 9
} {
| | | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
merge-test 24 {
1 2 3 4 5 6 7 8 9
} {
1 6 7 8 9
} {
1 2 3 4 9
} {
1 MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
} -expectError
merge-test 25 {
1 2 3 4 5 6 7 8 9
} {
1 7 8 9
} {
1 2 3 9
} {
1 MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
} -expectError
merge-test 30 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 6 7 9
} {
|
| ︙ | ︙ | |||
254 255 256 257 258 259 260 |
merge-test 34 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 6 7 8 9
} {
| | | | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
merge-test 34 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 6 7 8 9
} {
1 MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9
} -expectError
merge-test 35 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 9
} {
1 7 8 9
} {
1 MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9
} -expectError
merge-test 40 {
2 3 4 5 6 7 8
} {
3 4 5 6 7 8
} {
|
| ︙ | ︙ | |||
309 310 311 312 313 314 315 |
merge-test 44 {
2 3 4 5 6 7 8
} {
6 7 8
} {
2 3 4
} {
| | | | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
merge-test 44 {
2 3 4 5 6 7 8
} {
6 7 8
} {
2 3 4
} {
MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
} -expectError
merge-test 45 {
2 3 4 5 6 7 8
} {
7 8
} {
2 3
} {
MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
} -expectError
merge-test 50 {
2 3 4 5 6 7 8
} {
2 3 4 5 6 7
} {
|
| ︙ | ︙ | |||
363 364 365 366 367 368 369 |
merge-test 54 {
2 3 4 5 6 7 8
} {
2 3 4
} {
6 7 8
} {
| | | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
merge-test 54 {
2 3 4 5 6 7 8
} {
2 3 4
} {
6 7 8
} {
MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END
} -expectError
merge-test 55 {
2 3 4 5 6 7 8
} {
2 3
} {
7 8
} {
MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END
} -expectError
merge-test 60 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3 4 5 6 7 8 9
} {
|
| ︙ | ︙ | |||
418 419 420 421 422 423 424 |
merge-test 64 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
1 2 3 4 9
} {
| | | | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
merge-test 64 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
1 2 3 4 9
} {
1 MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
} -expectError
merge-test 65 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5b 6b 7 8 9
} {
1 2 3 9
} {
1 MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
} -expectError
merge-test 70 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 6 7 9
} {
|
| ︙ | ︙ | |||
473 474 475 476 477 478 479 |
merge-test 74 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
| | | | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 |
merge-test 74 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
1 MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9
} -expectError
merge-test 75 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 9
} {
1 2b 3b 4b 5b 6b 7 8 9
} {
1 MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9
} -expectError
merge-test 80 {
2 3 4 5 6 7 8
} {
2b 3 4 5 6 7 8
} {
|
| ︙ | ︙ | |||
528 529 530 531 532 533 534 |
merge-test 84 {
2 3 4 5 6 7 8
} {
2b 3b 4b 5b 6 7 8
} {
2 3 4
} {
| | | | 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 |
merge-test 84 {
2 3 4 5 6 7 8
} {
2b 3b 4b 5b 6 7 8
} {
2 3 4
} {
MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
} -expectError
merge-test 85 {
2 3 4 5 6 7 8
} {
2b 3b 4b 5b 6b 7 8
} {
2 3
} {
MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
} -expectError
merge-test 90 {
2 3 4 5 6 7 8
} {
2 3 4 5 6 7
} {
|
| ︙ | ︙ | |||
583 584 585 586 587 588 589 |
merge-test 94 {
2 3 4 5 6 7 8
} {
2 3 4
} {
2b 3b 4b 5b 6 7 8
} {
| | | | 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 |
merge-test 94 {
2 3 4 5 6 7 8
} {
2 3 4
} {
2b 3b 4b 5b 6 7 8
} {
MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END
} -expectError
merge-test 95 {
2 3 4 5 6 7 8
} {
2 3
} {
2b 3b 4b 5b 6b 7 8
} {
MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END
} -expectError
merge-test 100 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3 4 5 7 8 9 a b c d e
} {
|
| ︙ | ︙ | |||
629 630 631 632 633 634 635 |
merge-test 103 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 7 8 9b
} {
1 2 3 4 5 7 8 9b a b c d e
} {
| | | | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 |
merge-test 103 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 7 8 9b
} {
1 2 3 4 5 7 8 9b a b c d e
} {
1 2 3 4 5 7 8 MINE: 9b BOT: 9b a b c d e COM: 9 YOURS: 9b a b c d e END
} -expectError
merge-test 104 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 7 8 9b a b c d e
} {
1 2 3 4 5 7 8 9b
} {
1 2 3 4 5 7 8 MINE: 9b a b c d e BOT: 9b COM: 9 YOURS: 9b END
} -expectError
###############################################################################
test_cleanup
|
Changes to test/merge4.test.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
write_file t1 [join [string trim $basis] \n]\n
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args
set x [read_file t4]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x
set x [split [string trim $x] \n]
set y [read_file t5]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y
set y [split [string trim $y] \n]
set result1 [string trim $result1]
if {$x!=$result1} {
protOut " Expected \[$result1\]"
protOut " Got \[$x\]"
| > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
write_file t1 [join [string trim $basis] \n]\n
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args
set x [read_file t4]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x
regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $x {#} x
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x
set x [split [string trim $x] \n]
set y [read_file t5]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y
regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $y {#} y
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y
set y [split [string trim $y] \n]
set result1 [string trim $result1]
if {$x!=$result1} {
protOut " Expected \[$result1\]"
protOut " Got \[$x\]"
|
| ︙ | ︙ | |||
56 57 58 59 60 61 62 |
merge-test 1000 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5 6b 7b 8b 9
} {
1 2 3 4c 5c 6c 7 8 9
} {
| | | | | | 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 |
merge-test 1000 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5 6b 7b 8b 9
} {
1 2 3 4c 5c 6c 7 8 9
} {
1 > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 < 9
} {
1 > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b < 9
} -expectError
merge-test 1001 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4 5 6 7b 8b 9
} {
1 2 3 4c 5c 6c 7 8 9
} {
1 2b 3b 4c 5c 6c 7b 8b 9
} {
1 2b 3b 4c 5c 6c 7b 8b 9
}
merge-test 1002 {
2 3 4 5 6 7 8
} {
2b 3b 4b 5 6b 7b 8b
} {
2 3 4c 5c 6c 7 8
} {
> 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 <
} {
> 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b <
} -expectError
merge-test 1003 {
2 3 4 5 6 7 8
} {
2b 3b 4 5 6 7b 8b
} {
2 3 4c 5c 6c 7 8
|
| ︙ | ︙ |
Changes to test/tester.tcl.
| ︙ | ︙ | |||
330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
email-url \
empty-dirs \
encoding-glob \
exec-rel-paths \
fileedit-glob \
forbid-delta-manifests \
forum-close-policy \
gdiff-command \
gmerge-command \
hash-digits \
hooks \
http-port \
https-login \
ignore-glob \
| > | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
email-url \
empty-dirs \
encoding-glob \
exec-rel-paths \
fileedit-glob \
forbid-delta-manifests \
forum-close-policy \
forum-title \
gdiff-command \
gmerge-command \
hash-digits \
hooks \
http-port \
https-login \
ignore-glob \
|
| ︙ | ︙ | |||
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 |
max-upload \
mimetypes \
mtime-changes \
mv-rm-files \
pgp-command \
preferred-diff-type \
proxy \
redirect-to-https \
relative-paths \
repo-cksum \
repolist-skin \
robot-restrict \
robots-txt \
safe-html \
self-pw-reset \
self-register \
sitemap-extra \
ssh-command \
ssl-ca-location \
ssl-identity \
tclsh \
th1-setup \
th1-uri-regexp \
ticket-default-report \
user-color-map \
uv-sync \
web-browser]
fossil test-th-eval "hasfeature legacyMvRm"
if {[normalize_result] eq "1"} {
lappend result mv-rm-files
}
| > > > > > > > > | 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 |
max-upload \
mimetypes \
mtime-changes \
mv-rm-files \
pgp-command \
preferred-diff-type \
proxy \
raw-bgcolor \
redirect-to-https \
relative-paths \
repo-cksum \
repolist-skin \
robot-restrict \
robots-txt \
safe-html \
self-pw-reset \
self-register \
sitemap-extra \
ssh-command \
ssl-ca-location \
ssl-identity \
tclsh \
th1-setup \
th1-uri-regexp \
ticket-default-report \
timeline-hard-newlines \
timeline-plaintext \
timeline-truncate-at-blank \
timeline-tslink-info \
timeline-utc \
user-color-map \
verify-comments \
uv-sync \
vuln-report \
web-browser]
fossil test-th-eval "hasfeature legacyMvRm"
if {[normalize_result] eq "1"} {
lappend result mv-rm-files
}
|
| ︙ | ︙ |
Added test/th1-taint.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
#
# Copyright (c) 2025 D. Richard Hipp
#
# 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".)
#
# This program is distributed in the hope that it will be useful,
# but without any warranty; without even the implied warranty of
# merchantability or fitness for a particular purpose.
#
# Author contact information:
# drh@hwaci.com
# http://www.hwaci.com/drh/
#
############################################################################
#
# TH1 Commands
#
set path [file dirname [info script]]; test_setup
proc taint-test {testnum th1script expected} {
global fossilexe
set rc [catch {exec $fossilexe test-th-eval $th1script} got]
if {$rc} {
test th1-taint-$testnum 0
puts $got
return
}
if {$got ne $expected} {
test th1-taint-$testnum 0
puts " Expected: $expected"
puts " Got: $got"
} else {
test th1-taint-$testnum 1
}
}
taint-test 10 {string is tainted abcd} 0
taint-test 20 {string is tainted [taint abcd]} 1
taint-test 30 {string is tainted [untaint [taint abcd]]} 0
taint-test 40 {string is tainted [untaint abcde]} 0
taint-test 50 {string is tainted "abc[taint def]ghi"} 1
taint-test 60 {set t1 [taint abc]; string is tainted "123 $t1 456"} 1
taint-test 100 {set t1 [taint abc]; lappend t1 def; string is tainted $t1} 1
taint-test 110 {set t1 abc; lappend t1 [taint def]; string is tainted $t1} 1
taint-test 200 {string is tainted [list abc def ghi]} 0
taint-test 210 {string is tainted [list [taint abc] def ghi]} 1
taint-test 220 {string is tainted [list abc [taint def] ghi]} 1
taint-test 230 {string is tainted [list abc def [taint ghi]]} 1
taint-test 300 {
set res {}
foreach x [list abc [taint def] ghi] {
lappend res [string is tainted $x]
}
set res
} {1 1 1}
taint-test 310 {
set res {}
foreach {x y} [list abc [taint def] ghi jkl] {
lappend res [string is tainted $x] [string is tainted $y]
}
set res
} {1 1 1 1}
taint-test 400 {string is tainted [lindex "abc [taint def] ghi" 0]} 1
taint-test 410 {string is tainted [lindex "abc [taint def] ghi" 1]} 1
taint-test 420 {string is tainted [lindex "abc [taint def] ghi" 2]} 1
taint-test 430 {string is tainted [lindex "abc [taint def] ghi" 3]} 0
taint-test 500 {string is tainted [string index [taint abcdefg] 3]} 1
taint-test 600 {string is tainted [string range [taint abcdefg] 3 5]} 1
taint-test 700 {string is tainted [string trim [taint " abcdefg "]]} 1
taint-test 710 {string is tainted [string trimright [taint " abcdefg "]]} 1
taint-test 720 {string is tainted [string trimleft [taint " abcdefg "]]} 1
test_cleanup
|
Changes to test/th1.test.
| ︙ | ︙ | |||
793 794 795 796 797 798 799 |
test th1-defHeader-2 {[string match *<body> [normalize_result]] || \
[string match "*<body class=\"\$current_feature\
rpage-\$requested_page\
cpage-\$canonical_page\">" [normalize_result]]}
###############################################################################
| | | | | | 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 |
test th1-defHeader-2 {[string match *<body> [normalize_result]] || \
[string match "*<body class=\"\$current_feature\
rpage-\$requested_page\
cpage-\$canonical_page\">" [normalize_result]]}
###############################################################################
#fossil test-th-eval "styleHeader {Page Title Here}"
#test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
###############################################################################
test_in_checkout th1-header-2 {
fossil test-th-eval --open-config "styleHeader {Page Title Here}"
} {[regexp -- {<title>Fossil: Page Title Here</title>} $RESULT]}
###############################################################################
#fossil test-th-eval "styleFooter"
#test th1-footer-1 {$RESULT eq {TH_ERROR: repository unavailable}}
###############################################################################
fossil test-th-eval --open-config "styleFooter"
test th1-footer-2 {$RESULT eq {}}
###############################################################################
|
| ︙ | ︙ | |||
877 878 879 880 881 882 883 |
fossil test-th-eval "artifact"
test th1-artifact-1 {$RESULT eq \
{TH_ERROR: wrong # args: should be "artifact ID ?FILENAME?"}}
###############################################################################
| | | | | | | | | | 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 |
fossil test-th-eval "artifact"
test th1-artifact-1 {$RESULT eq \
{TH_ERROR: wrong # args: should be "artifact ID ?FILENAME?"}}
###############################################################################
#fossil test-th-eval "artifact tip"
#test th1-artifact-2 {$RESULT eq {TH_ERROR: repository unavailable}}
###############################################################################
test_in_checkout th1-artifact-3 {
fossil test-th-eval --open-config "artifact tip"
} {[regexp -- {F test/th1\.test [0-9a-f]{40,64}} $RESULT]}
###############################################################################
#fossil test-th-eval "artifact 0000000000"
#test th1-artifact-4 {$RESULT eq {TH_ERROR: repository unavailable}}
###############################################################################
fossil test-th-eval --open-config "artifact 0000000000"
test th1-artifact-5 {$RESULT eq {TH_ERROR: name not found}}
###############################################################################
#fossil test-th-eval "artifact tip test/th1.test"
#test th1-artifact-6 {$RESULT eq {TH_ERROR: repository unavailable}}
###############################################################################
test_in_checkout th1-artifact-7 {
fossil test-th-eval --open-config "artifact tip test/th1.test"
} {[regexp -- {th1-artifact-7} $RESULT]}
###############################################################################
#fossil test-th-eval "artifact 0000000000 test/th1.test"
#test th1-artifact-8 {$RESULT eq {TH_ERROR: repository unavailable}}
###############################################################################
fossil test-th-eval --open-config "artifact 0000000000 test/th1.test"
test th1-artifact-9 {$RESULT eq {TH_ERROR: manifest not found}}
###############################################################################
|
| ︙ | ︙ | |||
945 946 947 948 949 950 951 |
test th1-globalState-2 {$RESULT eq \
[fossil test-th-eval --open-config checkout]}
}
}
###############################################################################
| | | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 |
test th1-globalState-2 {$RESULT eq \
[fossil test-th-eval --open-config checkout]}
}
}
###############################################################################
#fossil test-th-eval "globalState configuration"
#test th1-globalState-3 {[string length $RESULT] == 0}
###############################################################################
fossil test-th-eval --open-config "globalState configuration"
test th1-globalState-4 {[string length $RESULT] > 0}
###############################################################################
|
| ︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 |
###############################################################################
fossil test-th-eval "globalState flags"
test th1-globalState-16 {$RESULT eq "0"}
###############################################################################
| | | | | | | | | | | | | | | | | | | | | < > | 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 |
###############################################################################
fossil test-th-eval "globalState flags"
test th1-globalState-16 {$RESULT eq "0"}
###############################################################################
#fossil test-th-eval "reinitialize; globalState configuration"
#test th1-reinitialize-1 {$RESULT eq ""}
###############################################################################
fossil test-th-eval "reinitialize 1; globalState configuration"
test th1-reinitialize-2 {$RESULT ne ""}
###############################################################################
#
# NOTE: This test will fail if the command names are added to TH1, or
# moved from Tcl builds to plain or the reverse. Sorting the
# command lists eliminates a dependence on order.
#
#fossil test-th-eval "info commands"
#set sorted_result [lsort $RESULT]
#protOut "Sorted: $sorted_result"
#set base_commands {anoncap anycap array artifact break breakpoint \
# builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
# combobox continue copybtn date decorate defHeader dir \
# enable_output encode64 error expr for foreach getParameter glob_match \
# globalState hascap hasfeature html htmlize http httpize if info \
# insertCsrf lappend lindex linecount list llength lsearch markdown nonce \
# proc puts query randhex redirect regexp reinitialize rename render \
# repository return searchable set setParameter setting stime string \
# styleFooter styleHeader styleScript submenu tclReady trace unset \
# unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
#set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
#if {$th1Tcl} {
# test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
#} else {
# test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
#}
###############################################################################
fossil test-th-eval "info vars"
if {$th1Hooks} {
test th1-info-vars-1 {[lsort $RESULT] eq \
|
| ︙ | ︙ | |||
1324 1325 1326 1327 1328 1329 1330 |
test th1-string-is-3 {$RESULT eq \
{TH_ERROR: wrong # args: should be "string is class string"}}
###############################################################################
fossil test-th-eval {string is other 123}
test th1-string-is-4 {$RESULT eq \
| | | 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 |
test th1-string-is-3 {$RESULT eq \
{TH_ERROR: wrong # args: should be "string is class string"}}
###############################################################################
fossil test-th-eval {string is other 123}
test th1-string-is-4 {$RESULT eq \
"TH_ERROR: Expected alnum, double, integer, list, or tainted, got: other"}
###############################################################################
fossil test-th-eval {string is alnum 123}
test th1-string-is-5 {$RESULT eq "1"}
###############################################################################
|
| ︙ | ︙ |
Changes to test/update.test.
| ︙ | ︙ | |||
55 56 57 58 59 60 61 |
# Make sure we are not in an open repository and initialize new repository
test_setup
###############################################################################
fossil update --verbose
test update-already-up-to-date {
| | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# Make sure we are not in an open repository and initialize new repository
test_setup
###############################################################################
fossil update --verbose
test update-already-up-to-date {
[regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date.$} $RESULT]
}
# Remaining tests are carried out in the order update_cmd() performs checks.
#
# Common approach for tests below:
# 1. Set the testname
# 2. Set the file name, done by calling update_setup
|
| ︙ | ︙ |
Changes to tools/emcc.sh.in.
|
| | > | | > > | > | | | | | | | | > > > | < | | | | | 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 |
#!/bin/sh
# ^^^^^^^ Please try to keep this script Bourne-compatible.
########################################################################
# WARNING: emcc.sh is generated from emcc.sh.in by the configure
# process. Do not edit emcc.sh directly, as it may be deleted or
# overwritten by the configure script.
#
# A wrapper around the emcc compiler which uses configure-time state
# to locate the Emscripten SDK and import the SDK's environment
# script, if needed.
########################################################################
# EMSDK_HOME comes from the configure --with-emsdk=/dir flag.
# EMSDK_ENV_SH is ${thatDir}/emsdk_env.sh and is also set by the
# configure process.
EMSDK_HOME="@EMSDK_HOME@"
EMSDK_ENV_SH="@EMSDK_ENV_SH@"
emcc="@BIN_EMCC@"
if [ x = "x${emcc}" ]; then
emcc=`which emcc 2>/dev/null`
fi
if [ x = "x${emcc}" ]; then
# If emcc is not found in the path, try to find it via an emsdk
# installation. The SDK variant is the official installation style
# supported by the Emscripten project, but emcc is also available
# via package managers on some OSes.
if [ x = "x${EMSDK_HOME}" ]; then
echo "EMSDK_HOME is not set. Pass --with-emsdk=/path/to/emsdk" \
"to the configure script." 1>&2
exit 1
fi
if [ x = "x${EMSDK_ENV_SH}" ]; then
if [ -f "${EMSDK_HOME}/emsdk_env.sh" ]; then
EMSDK_ENV_SH="${EMSDK_HOME}/emsdk_env.sh"
else
echo "EMSDK_ENV_SH is not set. Expecting configure script to set it." 1>&2
exit 2
fi
fi
if [ ! -f "${EMSDK_ENV_SH}" ]; then
echo "emsdk_env script not found: $EMSDK_ENV_SH" 1>&2
exit 3
fi
# $EMSDK is part of the state set by emsdk_env.sh.
if [ x = "x${EMSDK}" ]; then
EMSDK_QUIET=1
export EMSDK_QUIET
# ^^^ Squelches informational output from ${EMSDK_ENV_SH}.
source "${EMSDK_ENV_SH}" || {
rc=$?
echo "Error sourcing ${EMSDK_ENV_SH}"
exit $rc
}
fi
emcc=`which emcc 2>/dev/null`
if [ x = "x${emcc}" ]; then
echo "emcc not found in PATH. Normally that's set up by ${EMSDK_ENV_SH}." 1>&2
exit 4
fi
fi
exec $emcc "$@"
|
Added tools/fake-smtpd.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
#!/usr/bin/tclsh
#
# This script is a testing aid for working on the Relay notification method
# in Fossil.
#
# This script listens for connections on port 25 or probably some other TCP
# port specified by the "--port N" option. It pretend to be an SMTP server,
# though it does not actually relay any email. Instead, it just prints the
# SMTP conversation on stdout.
#
# If the "--max N" option is used, then the fake SMTP server shuts down
# with an error after receiving N messages from the client. This can be
# used to test retry capabilities in the client.
#
# Suggested Test Procedure For Fossil Relay Notifications
#
# 1. Bring up "fossil ui"
# 2. Configure notification for relay to localhost:8025
# 3. Start this script in a separate window. Something like:
# tclsh fake-smtpd.tcl -port 8025 -max 100
# 4. Send test messages using Fossil
#
proc conn_puts {chan txt} {
puts "S: $txt"
puts $chan $txt
flush $chan
}
set mxMsg 100000000
proc connection {chan ip port} {
global mxMsg
set nMsg 0
puts "*** begin connection from $ip:$port ***"
conn_puts $chan "220 localhost fake-SMTPD"
set inData 0
while {1} {
set line [string trimright [gets $chan]]
if {$line eq ""} {
if {[eof $chan]} break
}
puts "C: $line"
incr nMsg
if {$inData} {
if {$line eq "."} {
set inData 0
conn_puts $chan "250 Ok"
}
} elseif {$nMsg>$mxMsg} {
conn_puts $chan "999 I'm done!"
break
} elseif {[string match "HELO *" $line]} {
conn_puts $chan "250 Ok"
} elseif {[string match "EHLO *" $line]} {
conn_puts $chan "250-SIZE 100000"
conn_puts $chan "250 HELP"
} elseif {[string match "DATA*" $line]} {
conn_puts $chan "354 End data with <CR><LF>.<CR><LF>"
set inData 1
} elseif {[string match "QUIT*" $line]} {
conn_puts $chan "221 Bye"
break
} else {
conn_puts $chan "250 Ok"
}
}
puts "*** connection closed ($nMsg messages) ***"
close $chan
}
set port 25
set argc [llength $argv]
for {set i 0} {$i<$argc-1} {incr i} {
set arg [lindex $argv $i]
if {$arg eq "-port" || $arg eq "--port"} {
incr i
set port [lindex $argv $i]
}
if {$arg eq "-max" || $arg eq "--max"} {
incr i
set mxMsg [lindex $argv $i]
}
}
puts "listening on localhost:$port"
socket -server connection $port
set forever 0
vwait forever
|
Added tools/find-fossil-cgis.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
#!/usr/bin/tclsh
#
# This script scans a directory hierarchy looking for Fossil CGI files -
# the files that are used to launch Fossil as a CGI program. For each
# such file found, in prints the name of the file and also the file
# content, indented, if the --print option is used.
#
# tclsh find-fossil-cgis.tcl [OPTIONS] DIRECTORY
#
# The argument is the directory from which to begin the search.
#
# OPTIONS can be zero or more of the following:
#
# --has REGEXP Only show the CGI if the body matches REGEXP.
# May be repeated multiple times, in which case
# all must match.
#
# --hasnot REGEXP Only show the CGI if it does NOT match the
# REGEXP.
#
# --print Show the content of the CGI, indented by
# three spaces
#
# --symlink Process DIRECTORY arguments that are symlinks.
# Normally symlinks are silently ignored.
#
# -v Show progress information for debugging
#
# EXAMPLE USE CASES:
#
# Find all CGIs that do not have the "errorlog:" property set
#
# find-fossil-cgis.tcl *.website --has '\nrepository:' \
# --hasnot '\nerrorlog:'
#
# Add the errorlog: property to any CGI that does not have it:
#
# find-fossil-cgis.tcl *.website --has '\nrepository:' \
# --hasnot '\nerrorlog:' | while read x
# do
# echo 'errorlog: /logs/errors.txt' >>$x
# done
#
# Find and print all CGIs that do redirects
#
# find-fossil-cgis.tcl *.website --has '\nredirect:' --print
#
# Find the CGIs in directory $dir. Invoke recursively to
# scan subdirectories.
#
proc find_in_one_dir {dir} {
global HAS HASNOT PRINT V
if {$V>0} {
puts "# $dir"
}
foreach obj [lsort [glob -nocomplain -directory $dir *]] {
if {[file isdir $obj]} {
find_in_one_dir $obj
continue
}
if {![file isfile $obj]} continue
if {[file size $obj]>5000} continue
if {![file exec $obj]} continue
if {![file readable $obj]} continue
set fd [open $obj rb]
set txt [read $fd]
close $fd
if {![string match #!* $txt]} continue
if {![regexp {fossil} $txt]} continue
if {![regexp {\nrepository: } $txt] &&
![regexp {\ndirectory: } $txt] &&
![regexp {\nredirect: } $txt]} continue
set ok 1
foreach re $HAS {
if {![regexp $re $txt]} {set ok 0; break;}
}
if {!$ok} continue
foreach re $HASNOT {
if {[regexp $re $txt]} {set ok 0; break;}
}
if {!$ok} continue
#
# At this point assume we have found a CGI file.
#
puts $obj
if {$PRINT} {
regsub -all {\n} [string trim $txt] "\n " out
puts " $out"
}
}
}
set HAS [list]
set HASNOT [list]
set PRINT 0
set SYMLINK 0
set V 0
set N [llength $argv]
set DIRLIST [list]
# First pass: Gather all the command-line arguments but do no
# processing.
#
for {set i 0} {$i<$N} {incr i} {
set dir [lindex $argv $i]
if {($dir eq "-has" || $dir eq "--has") && $i<[expr {$N-1}]} {
incr i
lappend HAS [lindex $argv $i]
continue
}
if {($dir eq "-hasnot" || $dir eq "--hasnot") && $i<[expr {$N-1}]} {
incr i
lappend HASNOT [lindex $argv $i]
continue
}
if {$dir eq "-print" || $dir eq "--print"} {
set PRINT 1
continue
}
if {$dir eq "-symlink" || $dir eq "--symlink"} {
set SYMLINK 1
continue
}
if {$dir eq "-v"} {
set V 1
continue
}
if {[file type $dir]=="directory"} {
lappend DIRLIST $dir
}
}
# Second pass: Process the non-option arguments.
#
foreach dir $DIRLIST {
set type [file type $dir]
if {$type eq "directory" || ($SYMLINK && $type eq "link")} {
find_in_one_dir $dir
}
}
|
Changes to tools/fossil-stress.tcl.
| ︙ | ︙ | |||
91 92 93 94 95 96 97 | /vdiff?from=2015-01-01&to=trunk&diff=0 /wcontent /fileage /dir /tree /uvlist /stat | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | /vdiff?from=2015-01-01&to=trunk&diff=0 /wcontent /fileage /dir /tree /uvlist /stat /test-env /sitemap /hash-collisions /artifact_stats /bloblist /bigbloblist /wiki_rules /md_rules |
| ︙ | ︙ |
Changes to tools/makemake.tcl.
| ︙ | ︙ | |||
130 131 132 133 134 135 136 137 138 139 140 141 142 143 | loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name patch path | > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path |
| ︙ | ︙ | |||
237 238 239 240 241 242 243 244 245 | -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 | > | | > | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP } #lappend SQLITE_OPTIONS -DSQLITE_ENABLE_FTS3=1 |
| ︙ | ︙ | |||
364 365 366 367 368 369 370 |
}
writeln [string map [list \
<<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \
<<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \
<<<PIKCHR_OPTIONS>>> [join $PIKCHR_OPTIONS " \\\n "] \
<<<NEXT_LINE>>> \\] {
| | < < < > > > > > > | | 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 |
}
writeln [string map [list \
<<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \
<<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \
<<<PIKCHR_OPTIONS>>> [join $PIKCHR_OPTIONS " \\\n "] \
<<<NEXT_LINE>>> \\] {
all: $(APPNAME)
install: all
mkdir -p $(INSTALLDIR)
cp $(APPNAME) $(INSTALLDIR)
codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1
$(OBJDIR)/codecheck1 $(TRANS_SRC)
$(OBJDIR)/translate: $(SRCDIR_tools)/translate.c
-mkdir -p $(OBJDIR)
$(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c
$(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c
-mkdir -p $(OBJDIR)
$(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c
$(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c
-mkdir -p $(OBJDIR)
$(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c
$(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c
-mkdir -p $(OBJDIR)
$(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c
$(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c
-mkdir -p $(OBJDIR)
$(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c
$(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c
-mkdir -p $(OBJDIR)
$(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c
# Run the test suite.
# Other flags that can be included in TESTFLAGS are:
#
# -halt Stop testing after the first failed test
# -keep Keep the temporary workspace for debugging
# -prot Write a detailed log of the tests to the file ./prot
# -verbose Include even more details in the output
# -quiet Hide most output from the terminal
# -strict Treat known bugs as failures
#
# TESTFLAGS can also include names of specific test files to limit
# the run to just those test cases.
#
test: $(APPNAME)
$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS)
$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h
$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid <<<NEXT_LINE>>>
$(SRCDIR)/../manifest <<<NEXT_LINE>>>
$(SRCDIR)/../VERSION >$(OBJDIR)/VERSION.h
|
| ︙ | ︙ | |||
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
writeln "\$(OBJDIR)/shell.o:\t\$(SQLITE3_SHELL_SRC) \$(SRCDIR_extsrc)/sqlite3.h"
writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) \$(SEE_FLAGS) \$(LINENOISE_DEF.\$(USE_LINENOISE)) -c \$(SQLITE3_SHELL_SRC) -o \$@\n"
writeln "\$(OBJDIR)/linenoise.o:\t\$(SRCDIR_extsrc)/linenoise.c \$(SRCDIR_extsrc)/linenoise.h"
writeln "\t\$(XTCC) -c \$(SRCDIR_extsrc)/linenoise.c -o \$@\n"
writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n"
writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n"
writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n"
writeln [string map [list <<<NEXT_LINE>>> \\] {
| > > > | | | | | > | 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 |
writeln "\$(OBJDIR)/shell.o:\t\$(SQLITE3_SHELL_SRC) \$(SRCDIR_extsrc)/sqlite3.h"
writeln "\t\$(XTCC) \$(SHELL_OPTIONS) \$(SHELL_CFLAGS) \$(SEE_FLAGS) \$(LINENOISE_DEF.\$(USE_LINENOISE)) -c \$(SQLITE3_SHELL_SRC) -o \$@\n"
writeln "\$(OBJDIR)/linenoise.o:\t\$(SRCDIR_extsrc)/linenoise.c \$(SRCDIR_extsrc)/linenoise.h"
writeln "\t\$(XTCC) -c \$(SRCDIR_extsrc)/linenoise.c -o \$@\n"
writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
writeln "\t-mkdir -p \$(OBJDIR)\n"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n"
writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
writeln "\t-mkdir -p \$(OBJDIR)\n"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n"
writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
writeln "\t-mkdir -p \$(OBJDIR)\n"
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n"
writeln [string map [list <<<NEXT_LINE>>> \\] {
$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(OBJDIR)/mkversion
$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(OBJDIR)/mkversion
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>>
-sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore <<<NEXT_LINE>>>
-sEXPORTED_FUNCTIONS=_pikchr,_pikchr_version $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>>
-sENVIRONMENT=web <<<NEXT_LINE>>>
-sMODULARIZE <<<NEXT_LINE>>>
-sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>>
--minify 0
$(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc)
@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
wasm: $(SRCDIR_extsrc)/pikchr.js
#
# compile_commands.json support...
#
# We have to avoid applying compile_commands support to the in-tree
|
| ︙ | ︙ |
Changes to tools/mkindex.c.
| ︙ | ︙ | |||
83 84 85 86 87 88 89 | #include <assert.h> #include <string.h> /*************************************************************************** ** These macros must match similar macros in dispatch.c. ** ** Allowed values for CmdOrPage.eCmdFlags. */ | | | | | | | | | | | | | | | | > | 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 |
#include <assert.h>
#include <string.h>
/***************************************************************************
** These macros must match similar macros in dispatch.c.
**
** Allowed values for CmdOrPage.eCmdFlags. */
#define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */
#define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */
#define CMDFLAG_TEST 0x00004 /* Commands for testing only */
#define CMDFLAG_WEBPAGE 0x00008 /* Web pages */
#define CMDFLAG_COMMAND 0x00010 /* A command */
#define CMDFLAG_SETTING 0x00020 /* A setting */
#define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */
#define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */
#define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */
#define CMDFLAG_RAWCONTENT 0x00200 /* Do not interpret webpage content */
#define CMDFLAG_SENSITIVE 0x00400 /* Security-sensitive setting */
#define CMDFLAG_HIDDEN 0x00800 /* Elide from most listings */
#define CMDFLAG_LDAVG_EXEMPT 0x01000 /* Exempt from load_control() */
#define CMDFLAG_ALIAS 0x02000 /* Command aliases */
#define CMDFLAG_KEEPEMPTY 0x04000 /* Do not unset empty settings */
#define CMDFLAG_ABBREVSUBCMD 0x08000 /* Abbreviated subcmd in help text */
/**************************************************************************/
/*
** Each entry looks like this:
*/
typedef struct Entry {
int eType; /* CMDFLAG_* values */
|
| ︙ | ︙ | |||
278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8);
}else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){
aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
}else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
}else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){
aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT;
}else{
fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
zFile, nLine, j, &zLine[i]);
nErr++;
}
}
| > > > | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
aEntry[nUsed].zDflt = string_dup(&zLine[i+8], j-8);
}else if( j>9 && strncmp(&zLine[i], "variable=", 9)==0 ){
aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
}else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
}else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){
aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT;
}else if( (j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0)
|| (j==12 && strncmp(&zLine[i], "abbrv-subcom", 12)==0) ){
aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD;
}else{
fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
zFile, nLine, j, &zLine[i]);
nErr++;
}
}
|
| ︙ | ︙ |
Added tools/randomize-js-names.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
#!/usr/bin/tclsh
#
# This script is run as part of "make wasm". After emcc has
# run to generate extsrc/pikchr.wasm and extsrc/pikchr.js from
# extsrc/pikchr.c, we need to make changes to these filenames to
# work around caching problems.
#
# (1) in extsrc/pikchr.js -> change "pikchr.wasm" into
# "pikchr-vNNNNNNNN.wasm" where Ns are random digits.
#
# (2) in extsrc/pikchr-worker.js -> change "pikchr-vNNNNNNNN.js"
# by altering the random digits N.
#
set DIR extsrc
if {[llength $argv]>0} {
set DIR [lindex $argv 0]
}
set R [expr {int(rand()*10000000000)+1000000000}]
set in [open $DIR/pikchr.js rb]
set f1 [read $in]
close $in
set f1mod [regsub {\ypikchr(-v\d+)?\.wasm\y} $f1 "pikchr-v$R.wasm"]
set out [open $DIR/pikchr.js wb]
puts -nonewline $out $f1mod
close $out
puts "modified $DIR/pikchr.js to reference \"pikchr-v$R.wasm\""
set in [open $DIR/pikchr-worker.js rb]
set f1 [read $in]
close $in
set f1mod [regsub {\ypikchr(-v\d+)?\.js\y} $f1 "pikchr-v$R.js"]
set out [open $DIR/pikchr-worker.js wb]
puts -nonewline $out $f1mod
close $out
puts "modified $DIR/pikchr-worker.js to reference \"pikchr-v$R.js\""
|
Changes to tools/translate.c.
| ︙ | ︙ | |||
76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
*/
static int inStr = 0;
/*
** Name of files being processed
*/
static const char *zInFile = "(stdin)";
/*
** Terminate an active cgi_printf() or free string
*/
static void end_block(FILE *out){
if( inPrint ){
zArg[nArg] = 0;
| > > > > > > > > > > > | 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 |
*/
static int inStr = 0;
/*
** Name of files being processed
*/
static const char *zInFile = "(stdin)";
/*
** The `fossil_isspace()' function copied from the Fossil source code.
** Some MSVC runtime library versions of `isspace()' break with an `assert()' if
** the input is smaller than -1 or greater than 255 in debug builds, due to sign
** extension when promoting `signed char' to `int' for non-ASCII characters. Use
** an `isspace()' replacement instead of explicit type casts to `unsigned char'.
*/
int fossil_isspace(char c){
return c==' ' || (c<='\r' && c>='\t');
}
/*
** Terminate an active cgi_printf() or free string
*/
static void end_block(FILE *out){
if( inPrint ){
zArg[nArg] = 0;
|
| ︙ | ︙ | |||
104 105 106 107 108 109 110 |
int lineNo = 0; /* Line number */
char zLine[2000]; /* A single line of input */
char zOut[4000]; /* The input line translated into appropriate output */
c1 = c2 = '-';
while( fgets(zLine, sizeof(zLine), in) ){
lineNo++;
| | | | | | 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 |
int lineNo = 0; /* Line number */
char zLine[2000]; /* A single line of input */
char zOut[4000]; /* The input line translated into appropriate output */
c1 = c2 = '-';
while( fgets(zLine, sizeof(zLine), in) ){
lineNo++;
for(i=0; zLine[i] && fossil_isspace(zLine[i]); i++){}
if( zLine[i]!='@' ){
if( inPrint || inStr ) end_block(out);
fprintf(out,"%s",zLine);
/* 0123456789 12345 */
if( strncmp(zLine, "/* @-comment: ", 14)==0 ){
c1 = zLine[14];
c2 = zLine[15];
}
i += strlen(&zLine[i]);
while( i>0 && fossil_isspace(zLine[i-1]) ){ i--; }
lastWasEq = i>0 && zLine[i-1]=='=';
lastWasComma = i>0 && zLine[i-1]==',';
}else if( lastWasEq || lastWasComma){
/* If the last non-whitespace character before the first @ was
** an "="(var init/set) or a ","(const definition in list) then
** generate a string literal. But skip comments
** consisting of all text between c1 and c2 (default "--")
** and end of line.
*/
int indent, omitline;
char *zNewline = "\\n";
i++;
if( fossil_isspace(zLine[i]) ){ i++; }
indent = i - 2;
if( indent<0 ) indent = 0;
omitline = 0;
for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){
omitline = 1; break;
}
if( zLine[i]=='\\' && (zLine[i+1]==0 || zLine[i+1]=='\r'
|| zLine[i+1]=='\n') ){
zLine[i] = 0;
zNewline = "";
/* fprintf(stderr, "%s:%d: omit newline\n", zInFile, lineNo); */
break;
}
if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; }
zOut[j++] = zLine[i];
}
if( zNewline[0] ) while( j>0 && fossil_isspace(zOut[j-1]) ){ j--; }
zOut[j] = 0;
if( j<=0 && omitline ){
fprintf(out,"\n");
}else{
fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline);
}
}else{
|
| ︙ | ︙ | |||
169 170 171 172 173 174 175 |
*/
const char *zNewline = "\\n";
int indent;
int nC;
int nParam;
char c;
i++;
| | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
*/
const char *zNewline = "\\n";
int indent;
int nC;
int nParam;
char c;
i++;
if( fossil_isspace(zLine[i]) ){ i++; }
indent = i;
for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r'
|| zLine[i+1]=='\n') ){
zNewline = "";
break;
}
|
| ︙ | ︙ |
Changes to win/Makefile.dmc.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 | SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi | | | | | | | 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 | SSL = CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E codecheck1$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) codecheck1$E $(SRC) $(DMDIR)\bin\link @link $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR_tools)\translate.c |
| ︙ | ︙ | |||
633 634 635 636 637 638 639 640 641 642 643 644 645 646 | +translate$E $** > $@ $(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h $(TCC) -o$@ -c markdown_html_.c markdown_html_.c : $(SRCDIR)\markdown_html.c +translate$E $** > $@ $(OBJDIR)\md5$O : md5_.c md5.h $(TCC) -o$@ -c md5_.c md5_.c : $(SRCDIR)\md5.c +translate$E $** > $@ | > > > > > > | 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 | +translate$E $** > $@ $(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h $(TCC) -o$@ -c markdown_html_.c markdown_html_.c : $(SRCDIR)\markdown_html.c +translate$E $** > $@ $(OBJDIR)\match$O : match_.c match.h $(TCC) -o$@ -c match_.c match_.c : $(SRCDIR)\match.c +translate$E $** > $@ $(OBJDIR)\md5$O : md5_.c md5.h $(TCC) -o$@ -c md5_.c md5_.c : $(SRCDIR)\md5.c +translate$E $** > $@ |
| ︙ | ︙ | |||
1007 1008 1009 1010 1011 1012 1013 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h | | | 1013 1014 1015 1016 1017 1018 1019 1020 1021 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h @copy /Y nul: headers |
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
483 484 485 486 487 488 489 490 491 492 493 494 495 496 | $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ | > | 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 | $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/match.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ |
| ︙ | ︙ | |||
748 749 750 751 752 753 754 755 756 757 758 759 760 761 | $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ | > | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/match_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ |
| ︙ | ︙ | |||
897 898 899 900 901 902 903 904 905 906 907 908 909 910 | $(OBJDIR)/loadctrl.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/patch.o \ $(OBJDIR)/path.o \ | > | 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 | $(OBJDIR)/loadctrl.o \ $(OBJDIR)/login.o \ $(OBJDIR)/lookslike.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/markdown.o \ $(OBJDIR)/markdown_html.o \ $(OBJDIR)/match.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ $(OBJDIR)/moderate.o \ $(OBJDIR)/name.o \ $(OBJDIR)/patch.o \ $(OBJDIR)/path.o \ |
| ︙ | ︙ | |||
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 | $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ | > | 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 | $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/match_.c:$(OBJDIR)/match.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ |
| ︙ | ︙ | |||
2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 | $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c | > > > > > > > > | 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 | $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/match_.c: $(SRCDIR)/match.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/match.c >$@ $(OBJDIR)/match.o: $(OBJDIR)/match_.c $(OBJDIR)/match.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/match.o -c $(OBJDIR)/match_.c $(OBJDIR)/match.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c |
| ︙ | ︙ | |||
2514 2515 2516 2517 2518 2519 2520 2521 2522 |
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_MAX_EXPR_DEPTH=0 \
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_FTS4 \
| > | > | | 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 |
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_MAX_EXPR_DEPTH=0 \
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_FTS4 \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_ENABLE_MATH_FUNCTIONS \
-DSQLITE_ENABLE_SETLK_TIMEOUT \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_HAVE_ZLIB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_TRUSTED_SCHEMA=0 \
-DHAVE_USLEEP \
-DSQLITE_WIN32_NO_ANSI \
$(MINGW_OPTIONS) \
|
| ︙ | ︙ | |||
2542 2543 2544 2545 2546 2547 2548 2549 2550 |
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_MAX_EXPR_DEPTH=0 \
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_FTS4 \
| > | > | | 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 |
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_MAX_EXPR_DEPTH=0 \
-DSQLITE_ENABLE_LOCKING_STYLE=0 \
-DSQLITE_DEFAULT_FILE_FORMAT=4 \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_FTS4 \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_ENABLE_MATH_FUNCTIONS \
-DSQLITE_ENABLE_SETLK_TIMEOUT \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_HAVE_ZLIB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_TRUSTED_SCHEMA=0 \
-DHAVE_USLEEP \
-Dmain=sqlite3_shell \
-DSQLITE_SHELL_IS_UTF8=1 \
|
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
309 310 311 312 313 314 315 316 317 |
/DSQLITE_OMIT_DEPRECATED \
/DSQLITE_OMIT_PROGRESS_CALLBACK \
/DSQLITE_OMIT_SHARED_CACHE \
/DSQLITE_OMIT_LOAD_EXTENSION \
/DSQLITE_MAX_EXPR_DEPTH=0 \
/DSQLITE_ENABLE_LOCKING_STYLE=0 \
/DSQLITE_DEFAULT_FILE_FORMAT=4 \
/DSQLITE_ENABLE_EXPLAIN_COMMENTS \
/DSQLITE_ENABLE_FTS4 \
| > | > | | 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
/DSQLITE_OMIT_DEPRECATED \
/DSQLITE_OMIT_PROGRESS_CALLBACK \
/DSQLITE_OMIT_SHARED_CACHE \
/DSQLITE_OMIT_LOAD_EXTENSION \
/DSQLITE_MAX_EXPR_DEPTH=0 \
/DSQLITE_ENABLE_LOCKING_STYLE=0 \
/DSQLITE_DEFAULT_FILE_FORMAT=4 \
/DSQLITE_ENABLE_DBSTAT_VTAB \
/DSQLITE_ENABLE_EXPLAIN_COMMENTS \
/DSQLITE_ENABLE_FTS4 \
/DSQLITE_ENABLE_FTS5 \
/DSQLITE_ENABLE_MATH_FUNCTIONS \
/DSQLITE_ENABLE_SETLK_TIMEOUT \
/DSQLITE_ENABLE_STMTVTAB \
/DSQLITE_HAVE_ZLIB \
/DSQLITE_ENABLE_DBPAGE_VTAB \
/DSQLITE_TRUSTED_SCHEMA=0 \
/DHAVE_USLEEP \
/DSQLITE_WIN32_NO_ANSI
|
| ︙ | ︙ | |||
334 335 336 337 338 339 340 341 342 |
/DSQLITE_OMIT_DEPRECATED \
/DSQLITE_OMIT_PROGRESS_CALLBACK \
/DSQLITE_OMIT_SHARED_CACHE \
/DSQLITE_OMIT_LOAD_EXTENSION \
/DSQLITE_MAX_EXPR_DEPTH=0 \
/DSQLITE_ENABLE_LOCKING_STYLE=0 \
/DSQLITE_DEFAULT_FILE_FORMAT=4 \
/DSQLITE_ENABLE_EXPLAIN_COMMENTS \
/DSQLITE_ENABLE_FTS4 \
| > | > | | 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
/DSQLITE_OMIT_DEPRECATED \
/DSQLITE_OMIT_PROGRESS_CALLBACK \
/DSQLITE_OMIT_SHARED_CACHE \
/DSQLITE_OMIT_LOAD_EXTENSION \
/DSQLITE_MAX_EXPR_DEPTH=0 \
/DSQLITE_ENABLE_LOCKING_STYLE=0 \
/DSQLITE_DEFAULT_FILE_FORMAT=4 \
/DSQLITE_ENABLE_DBSTAT_VTAB \
/DSQLITE_ENABLE_EXPLAIN_COMMENTS \
/DSQLITE_ENABLE_FTS4 \
/DSQLITE_ENABLE_FTS5 \
/DSQLITE_ENABLE_MATH_FUNCTIONS \
/DSQLITE_ENABLE_SETLK_TIMEOUT \
/DSQLITE_ENABLE_STMTVTAB \
/DSQLITE_HAVE_ZLIB \
/DSQLITE_ENABLE_DBPAGE_VTAB \
/DSQLITE_TRUSTED_SCHEMA=0 \
/DHAVE_USLEEP \
/Dmain=sqlite3_shell \
/DSQLITE_SHELL_IS_UTF8=1 \
|
| ︙ | ︙ | |||
441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
"$(OX)\loadctrl_.c" \
"$(OX)\login_.c" \
"$(OX)\lookslike_.c" \
"$(OX)\main_.c" \
"$(OX)\manifest_.c" \
"$(OX)\markdown_.c" \
"$(OX)\markdown_html_.c" \
"$(OX)\md5_.c" \
"$(OX)\merge_.c" \
"$(OX)\merge3_.c" \
"$(OX)\moderate_.c" \
"$(OX)\name_.c" \
"$(OX)\patch_.c" \
"$(OX)\path_.c" \
| > | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
"$(OX)\loadctrl_.c" \
"$(OX)\login_.c" \
"$(OX)\lookslike_.c" \
"$(OX)\main_.c" \
"$(OX)\manifest_.c" \
"$(OX)\markdown_.c" \
"$(OX)\markdown_html_.c" \
"$(OX)\match_.c" \
"$(OX)\md5_.c" \
"$(OX)\merge_.c" \
"$(OX)\merge3_.c" \
"$(OX)\moderate_.c" \
"$(OX)\name_.c" \
"$(OX)\patch_.c" \
"$(OX)\path_.c" \
|
| ︙ | ︙ | |||
706 707 708 709 710 711 712 713 714 715 716 717 718 719 |
"$(OX)\loadctrl$O" \
"$(OX)\login$O" \
"$(OX)\lookslike$O" \
"$(OX)\main$O" \
"$(OX)\manifest$O" \
"$(OX)\markdown$O" \
"$(OX)\markdown_html$O" \
"$(OX)\md5$O" \
"$(OX)\merge$O" \
"$(OX)\merge3$O" \
"$(OX)\moderate$O" \
"$(OX)\name$O" \
"$(OX)\patch$O" \
"$(OX)\path$O" \
| > | 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 |
"$(OX)\loadctrl$O" \
"$(OX)\login$O" \
"$(OX)\lookslike$O" \
"$(OX)\main$O" \
"$(OX)\manifest$O" \
"$(OX)\markdown$O" \
"$(OX)\markdown_html$O" \
"$(OX)\match$O" \
"$(OX)\md5$O" \
"$(OX)\merge$O" \
"$(OX)\merge3$O" \
"$(OX)\moderate$O" \
"$(OX)\name$O" \
"$(OX)\patch$O" \
"$(OX)\path$O" \
|
| ︙ | ︙ | |||
955 956 957 958 959 960 961 962 963 964 965 966 967 968 | echo "$(OX)\loadctrl.obj" >> $@ echo "$(OX)\login.obj" >> $@ echo "$(OX)\lookslike.obj" >> $@ echo "$(OX)\main.obj" >> $@ echo "$(OX)\manifest.obj" >> $@ echo "$(OX)\markdown.obj" >> $@ echo "$(OX)\markdown_html.obj" >> $@ echo "$(OX)\md5.obj" >> $@ echo "$(OX)\merge.obj" >> $@ echo "$(OX)\merge3.obj" >> $@ echo "$(OX)\moderate.obj" >> $@ echo "$(OX)\name.obj" >> $@ echo "$(OX)\patch.obj" >> $@ echo "$(OX)\path.obj" >> $@ | > | 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 | echo "$(OX)\loadctrl.obj" >> $@ echo "$(OX)\login.obj" >> $@ echo "$(OX)\lookslike.obj" >> $@ echo "$(OX)\main.obj" >> $@ echo "$(OX)\manifest.obj" >> $@ echo "$(OX)\markdown.obj" >> $@ echo "$(OX)\markdown_html.obj" >> $@ echo "$(OX)\match.obj" >> $@ echo "$(OX)\md5.obj" >> $@ echo "$(OX)\merge.obj" >> $@ echo "$(OX)\merge3.obj" >> $@ echo "$(OX)\moderate.obj" >> $@ echo "$(OX)\name.obj" >> $@ echo "$(OX)\patch.obj" >> $@ echo "$(OX)\path.obj" >> $@ |
| ︙ | ︙ | |||
1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 | "$(OBJDIR)\translate$E" $** > $@ "$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c" "$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c" "$(OX)\md5_.c" : "$(SRCDIR)\md5.c" "$(OBJDIR)\translate$E" $** > $@ | > > > > > > | 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 | "$(OBJDIR)\translate$E" $** > $@ "$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c" "$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\match$O" : "$(OX)\match_.c" "$(OX)\match.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\match_.c" "$(OX)\match_.c" : "$(SRCDIR)\match.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c" "$(OX)\md5_.c" : "$(SRCDIR)\md5.c" "$(OBJDIR)\translate$E" $** > $@ |
| ︙ | ︙ | |||
2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 | "$(OX)\loadctrl_.c":"$(OX)\loadctrl.h" \ "$(OX)\login_.c":"$(OX)\login.h" \ "$(OX)\lookslike_.c":"$(OX)\lookslike.h" \ "$(OX)\main_.c":"$(OX)\main.h" \ "$(OX)\manifest_.c":"$(OX)\manifest.h" \ "$(OX)\markdown_.c":"$(OX)\markdown.h" \ "$(OX)\markdown_html_.c":"$(OX)\markdown_html.h" \ "$(OX)\md5_.c":"$(OX)\md5.h" \ "$(OX)\merge_.c":"$(OX)\merge.h" \ "$(OX)\merge3_.c":"$(OX)\merge3.h" \ "$(OX)\moderate_.c":"$(OX)\moderate.h" \ "$(OX)\name_.c":"$(OX)\name.h" \ "$(OX)\patch_.c":"$(OX)\patch.h" \ "$(OX)\path_.c":"$(OX)\path.h" \ | > | 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 | "$(OX)\loadctrl_.c":"$(OX)\loadctrl.h" \ "$(OX)\login_.c":"$(OX)\login.h" \ "$(OX)\lookslike_.c":"$(OX)\lookslike.h" \ "$(OX)\main_.c":"$(OX)\main.h" \ "$(OX)\manifest_.c":"$(OX)\manifest.h" \ "$(OX)\markdown_.c":"$(OX)\markdown.h" \ "$(OX)\markdown_html_.c":"$(OX)\markdown_html.h" \ "$(OX)\match_.c":"$(OX)\match.h" \ "$(OX)\md5_.c":"$(OX)\md5.h" \ "$(OX)\merge_.c":"$(OX)\merge.h" \ "$(OX)\merge3_.c":"$(OX)\merge3.h" \ "$(OX)\moderate_.c":"$(OX)\moderate.h" \ "$(OX)\name_.c":"$(OX)\name.h" \ "$(OX)\patch_.c":"$(OX)\patch.h" \ "$(OX)\path_.c":"$(OX)\path.h" \ |
| ︙ | ︙ |
Added win/build32.bat.
> > > | 1 2 3 | REM Based on /wiki/Release%20Build%20How-To nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_ENABLE_WINXP=1 OPTIMIZATIONS=4 clean fossil.exe dumpbin /dependents fossil.exe |
Added win/build64.bat.
> > > | 1 2 3 | REM Based on /wiki/Release%20Build%20How-To nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 OPTIMIZATIONS=4 clean fossil.exe dumpbin /dependents fossil.exe |
Changes to www/aboutcgi.wiki.
| ︙ | ︙ | |||
65 66 67 68 69 70 71 |
In this example: "timeline/four".
<tr><td>QUERY_STRING
<td>The query string that follows the "?" in the URL, if there is one.
</table>
There are other CGI environment variables beyond those listed above.
Many Fossil servers implement the
| | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
In this example: "timeline/four".
<tr><td>QUERY_STRING
<td>The query string that follows the "?" in the URL, if there is one.
</table>
There are other CGI environment variables beyond those listed above.
Many Fossil servers implement the
[https://fossil-scm.org/home/test-env/two/three?abc=xyz|test-env]
webpage that shows some of the CGI environment
variables that Fossil pays attention to.
In addition to setting various CGI environment variables, if the HTTP
request contains POST content, then the web server relays the POST content
to standard input of the CGI script.
|
| ︙ | ︙ |
Changes to www/alerts.md.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # Email Alerts ## Overview Beginning with version 2.7, Fossil can send email messages to subscribers to alert them to changes in the repository: * New [checkins](/help?cmd=ci) * [Ticket](./tickets.wiki) changes * [Wiki](./wikitheory.wiki) page changes * New and edited [forum](./forum.wiki) posts * Announcements Subscribers can elect to receive emails as soon as these events happen, or they can receive a daily digest of the events instead. Email alerts are sent by a [Fossil server](./server/), which must be [set up](#quick) by the Fossil administrator to send email. | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # Email Alerts ## Overview Beginning with version 2.7, Fossil can send email messages to subscribers to alert them to changes in the repository: * New [checkins](/help?cmd=ci) * [Ticket](./tickets.wiki) changes * [Wiki](./wikitheory.wiki) page changes * New and edited [forum](./forum.wiki) posts * Users receiving [new permissions](./caps/index.md) (admins only) * Announcements Subscribers can elect to receive emails as soon as these events happen, or they can receive a daily digest of the events instead. Email alerts are sent by a [Fossil server](./server/), which must be [set up](#quick) by the Fossil administrator to send email. |
| ︙ | ︙ | |||
32 33 34 35 36 37 38 | system. To follow this guide, you will need a Fossil UI browser window open to the [Admin → Notification](/setup_notification) Fossil UI screen on the Fossil server that will be sending these email alerts, logged in as a user with [**Admin** capability](./caps/ref.html#a). It is not possible to work on a clone of the server's repository and push the configuration changes up to that repo as an Admin user, [on purpose](#backup). | < < < < < | | > | | 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 | system. To follow this guide, you will need a Fossil UI browser window open to the [Admin → Notification](/setup_notification) Fossil UI screen on the Fossil server that will be sending these email alerts, logged in as a user with [**Admin** capability](./caps/ref.html#a). It is not possible to work on a clone of the server's repository and push the configuration changes up to that repo as an Admin user, [on purpose](#backup). <a id="cd"></a> You will also need a CLI window open with its working directory changed to a checkout directory of the Fossil repository you are setting up to send email. If you don't `cd` to such a checkout directory first, you'll need to add `-R /path/to/repo.fossil` to each `fossil` command below to tell Fossil which repository you mean it to apply the command to. There are other prerequisites for email service, but since they vary depending on the configuration you choose, we'll cover these inline below. <a id="quick"></a> ## Quick Email Service Setup If you've already got a working OpenSMTPD, Postfix, Exim, Sendmail, or similar server on the machine running your Fossil instance(s), and you aren't using Fossil's [chroot jail feature](./chroot.md) to wall Fossil off from the rest of the machine, it's fairly simple to set up email alerts. (Otherwise, skip [ahead](#advanced) to the sections on advanced email service setup.) This is our "quick setup" option even though setting up an SMTP mail server is not trivial, because there are many other reasons to have such |
| ︙ | ︙ | |||
517 518 519 520 521 522 523 524 525 526 527 528 529 530 | * The Verified checkbox is initially unchecked for subscriber-only email addresses until the user clicks the link in the verification email. This checkbox lets the Fossil Admin user manually verify the user, such as in the case where the verification email message got lost. Unchecking this box does not cause another verification email to be sent. This screen also allows a Fossil Admin user to perform other activities on behalf of a subscriber which they could do themselves, such as to [unsubscribe](#unsub) them. <a id="backup"></a> | > > > > | 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 | * The Verified checkbox is initially unchecked for subscriber-only email addresses until the user clicks the link in the verification email. This checkbox lets the Fossil Admin user manually verify the user, such as in the case where the verification email message got lost. Unchecking this box does not cause another verification email to be sent. * Admin users (only) may activate the "user elevation" subscription, which sends a notification when a user is created or is explicitly assigned permission they did not formerly have. This screen also allows a Fossil Admin user to perform other activities on behalf of a subscriber which they could do themselves, such as to [unsubscribe](#unsub) them. <a id="backup"></a> |
| ︙ | ︙ |
Changes to www/build.wiki.
| ︙ | ︙ | |||
145 146 147 148 149 150 151 | want to make minor edits to Makefile.classic to configure the build for your system.</li> <li><i>MinGW / MinGW-w64</i> → The best-supported path is to build via the MinGW specific Makefile under a POSIX build of GNU make: "<b>make -f win/Makefile.mingw</b>".</li> | < < < < < < < < < < < < < < < < < | > | > > > > > > | > > > > | > | > > > | 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 | want to make minor edits to Makefile.classic to configure the build for your system.</li> <li><i>MinGW / MinGW-w64</i> → The best-supported path is to build via the MinGW specific Makefile under a POSIX build of GNU make: "<b>make -f win/Makefile.mingw</b>".</li> To enable the native [./th1.md#tclEval | Tcl integration feature], use a command line like the following (all on one line): <pre>make -f win/Makefile.mingw FOSSIL_ENABLE_TCL=1 FOSSIL_ENABLE_TCL_STUBS=1 FOSSIL_ENABLE_TCL_PRIVATE_STUBS=1</pre> <li><i>MSYS2 / Cygwin</i> → This is something of a hybrid between options "a" and "c" above: it configures and builds <code>fossil.exe</code> much as on Linux, but you get a native Windows executable out at the end. The primary downside is that this type of executable can become confused when attempting to interoperate with fully native Windows EXEs by making assumptions that only hold true when all elements are running under the Cygwin/MSYS environment. The MSVC and MinGW options do not have this limitation. Even so, there is value to Linux/Unix natives in having this hybrid while off in Windows-land. The simpler of the two paths is MSYS2, since it lets you install the necessary prerequisites in a single command after installing the base environment: <pre>pacman -sS gcc make openssl-devel zlib-devel</pre> The equivalent in Cygwin's <code>setup.exe</code> requires stepping through the GUI package chooser, and then if you miss one of the prereqs, going all the way back through it again until you get it right. <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. |
| ︙ | ︙ | |||
340 341 342 343 344 345 346 | Fossil has builtin support for processing specific features using <tt>libfuzzer</tt>. The features which can be tested this way are found in the help text for the [/help?cmd=test-fuzz|test-fuzz command]. Fuzzing requires: | < < | < < < < < < | < < > < < < | | | | 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 |
Fossil has builtin support for processing specific features using
<tt>libfuzzer</tt>. The features which can be tested this way are
found in the help text for the [/help?cmd=test-fuzz|test-fuzz
command].
Fuzzing requires:
* The clang C compiler.
* libfuzzer. On Ubuntu-derived systems, it can be installed with
<tt>apt install libfuzzer-XYZ</tt>, where XYZ is a version number
(several versions may be available on any given system)
Compile as follows:
<pre><code>make clean
make TCCFLAGS='-DFOSSIL_FUZZ -fsanitize=fuzzer,address,undefined -O0 -g' CC=clang
</code></pre>
The resulting <tt>fossil</tt> binary differs from the standard
one primarily in that it runs the <tt>test-fuzz</tt> command by
default. It needs to be told what to fuzz and needs to be given a
directory of input files to seed the fuzzer with:
<pre><code>$ mkdir cases
# Copy input files into ./cases. e.g. when fuzzing the markdown
# processor, copy any to-be-tested .md files into that directory.
# Then start the fuzzer:
$ ./fossil-fuzz --fuzztype markdown cases
</code></pre>
As it works, it writes its mutated test files into the "cases"
directory, each one named in the form of a hash. When it finds a
problem it will produce a stack trace for the offending code, will
output the name of the file which triggered the crash (named
<tt>cases/SOME_HASH</tt>) and may, depending on the nature of the
problem, produce a file named <tt>crash-SOMETHING</tt>. In theory the
crash file can be fed directly back into the fuzzer to reproduce the
problem:
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 |
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:
| > > | | 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
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>$ rm extsrc/pikchr.{js,wasm}
# ^^^^ that rm has proven necessary in order to ensure rebuilds
$ 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
|
| ︙ | ︙ |
Changes to www/cgi.wiki.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 78 | If it is present, and if the [#directory:|<b>directory:</b>] option is used, and if the PATH_INFO string is empty, then Fossil will show a list of available Fossil repositories. The "skin" of the reply is determined by the first repository in the list that has a non-zero [/help?cmd=repolist-skin|repolist-skin] setting. If no repository has such a non-zero repolist-skin setting, then | > | > > > > > > > > | 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 | If it is present, and if the [#directory:|<b>directory:</b>] option is used, and if the PATH_INFO string is empty, then Fossil will show a list of available Fossil repositories. The "skin" of the reply is determined by the first repository in the list that has a non-zero [/help?cmd=repolist-skin|repolist-skin] setting. If no repository has such a non-zero repolist-skin setting, then the repository list is generic HTML without any decoration, with the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt> environment variable. The variable can be defined in the CGI control file using the [#setenv|<tt>setenv:</tt>] statement. The "Project Description" and "Login-Group" columns on the repolist page are optional. They are hidden by default. Show them by etting the <tt>FOSSIL_REPOLIST_SHOW</tt> environment variable to a string that contains substrings "description" and/or "login-group". The repolist-generated page recurses into subdirectories and will list all <tt>*.fossil</tt> files found, with the following exceptions: * Filenames starting with a period are treated as "hidden" and skipped. * Subdirectory names which match the base name of a fossil file in |
| ︙ | ︙ |
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 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 |
<title>Change Log</title>
<h2 id='v2_27'>Changes for version 2.27 (pending)</h2><ol>
<li> Fix a SQL injection on the [/help?cmd=/file|/file page]. Thanks to
additional defenses built into Fossil, as well as good luck, this injection
is not exploitable for either data exfiltration or privilege escalation. The
only possible result of invoking the injection is a harmless SQL syntax error.
(The [https://en.wikipedia.org/wiki/Swiss_cheese_model|holes in the Swiss cheese]
did not line up!)
<li> Enhance the chng= query parameter on the [/help?cmd=/timeline|timeline page]
so that it works with other query parameters like p=, d=, from=, and to=.
<li> Always include nodes identify by sel1= and sel2= in the /timeline display.
<li> Enable the --editor option on the [/help?cmd=amend|fossil amend] command.
<li> Require at least an anonymous login to access the /blame page and similar,
to help prevent robots from soaking up excess CPU time on such pages.
<li> When walking the filesystem looking for Fossil repositories, avoid descending
into directories named "/proc".
</ol>
<h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
<li>Enhancements to [/help?cmd=diff|fossil diff] and similar:
<ol type="a">
<li> The argument to the --from option can be a directory name, causing
Fossil to use files under that directory as the baseline for the diff.
<li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting]
is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
are available, or a --by diff if not.
<li> The "Reload" button is added to --tk diffs, to bring the displayed
diff up to date with the latest changes on disk.
<li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
diffs of multiple files.
</ol>
<li>Added the [/help?cmd=/ckout|/ckout web page] to provide information
about pending changes in a working check-out
<li>Enhancements to the [/help?cmd=ui|fossil ui] command:
<ol type="a">
<li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its
start page. Or, if the new "--from PATH" option is present, the
default start page becomes "/ckout?exbase=PATH".
<li> The new "--extpage FILENAME" option opens the named file as if it
where in a [./serverext.wiki|CGI extension]. Example usage: the
person editing this change log has
"fossil ui --extpage www/changes.wiki" running and hence can
press "Reload" on the web browser to view edits.
<li> Accept both IPv4 and IPv6 connections on all platforms, including
Windows and OpenBSD. This also applies to the "fossil server"
command.
</ol>
<li>Enhancements to [/help?cmd=merge|fossil merge]:
<ol type="a">
<li> Added the [/help?cmd=merge-info|fossil merge-info] command and
especially the --tk option to that command, to provide analysis
of the most recent merge or update operation.
<li> When a merge conflict occurs, a new section is added to the conflict
text that shows Fossil's suggested resolution to the conflict.
</ol>
<li>Enhancements to [/help?cmd=commit|fossil commit]:
<ol type="a">
<li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks)
in the check-in comment, it will alert the developer and give
him or her the opportunity to edit the comment before continuing.
This feature is controllable by the
[/help?cmd=verify-comments|verify-comments setting].
<li> The new "--if-changes" option causes the commit to become
a quiet no-op if there are no pending changes.
<li> Added the ability to sign check-ins with SSH keys. Artifacts signed
this way are ignored by all previous fossil versions, as if they
were plain-text file content instead of Fossil artifacts.
<li> Issue a warning if a user tries to commit on a check-in where the
branch has been changed.
<li> The interactive checkin comment prompt shows the formatting rules
set for that repository.
<li> Add the "--editor" option.
</ol>
<li>Deprecate the --comfmtflags and --comment-format global options and
no longer list them in the built-in help, but keep them working for
backwards compatibility.
Alternative TTY comment formatting can still be specified using the
[/help?cmd=comment-format|comment-format setting], if desired. The
default comment format is now called "canonical", not "legacy".
<li>Enhancements to the [/help?cmd=/timeline|/timeline page]:
<ol type="a">
<li> Added the "ml=" ("Merge-in List") query parameter that works
like "rl=" ("Related List") but adds "mionly" style related
check-ins instead of the full "rel" style.
<li> For "tl=", "rl=", and "ml=", the order of the branches in the
graph now tries to match the order of the branches named in
the list.
<li> The "ms=" ("Match Style") query parameter is honored for
"tl=", "rl=", and "ml=".
<li> New query parameter "sl=BRANCHLIST" ("Sort List") strives to
put branches in the specified order in the graph. This
overrides any "tl=" or similar ordering.
<li> In the various "from=","to=" query formats, if the one of the
end points is an ancestor of the other, then the "rel" modifier
omits check-ins that are not ancestors of the newer endpoint.
<li> For "tl=" and similar query parameters, if the pattern contains
GLOB characters, then the matching style ("ms=") is set to GLOB
automatically and the "ms=" query parameter can be omitted.
<li> Enhance the "ymd" query parameter so that when used like
"ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of
dates specified.
<li> Accept the "Z" (Zulu-time) suffix on date arguments for the
"ymd" and "yw" query parameters.
<li> The new "min" query parameter, when added to a from=,to= query,
collapses long runs of check-ins on the same branch into just
end-points.
<li> The p= and d= parameters can now reference different check-ins,
in which case the timeline shows those check-ins that are both
ancestors of p= and descendants of d=.
<li> The saturation and intensity of user-specified checkin and branch
background colors are automatically adjusted to keep the colors
compatible with the current skin, unless the
[/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on.
</ol>
<li>The [/help?cmd=/docfile|/docfile webpage] was added. It works like
/doc but keeps the title of markdown documents with the document rather
that moving it up to the page title.
<li>Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
and debugging
<li>Added the "artifact_to_json(NAME)" SQL function that returns a JSON
decoding of the artifact described by NAME.
<li>Improvements to the [/help?cmd=patch|fossil patch] command:
<ol type="a">
<li> Fix a bug in "fossil patch create" that causes
[/help?cmd=revert|fossil revert] operations that happened
on individualfiles after a [/help?cmd=merge|fossil merge]
to be omitted from the patch.
<li> Added the [/help?cmd=patch|patch alias] command for managing
aliases for remote checkout names.
</ol>
<li>Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
<ol type="a">
<li> Add the ability to search the help text, either in the UI
(on the [/help?cmd=/search|/search page]) or from the command-line
(using the "[/help?cmd=search|fossil search -h PATTERN]" command.)
<li> Accepts an optional SUBCOMMAND argument following the
COMMAND argument and only shows results for the specified
subcommand, not the entire command.
<li> The -u (--usage) option shows only the command-line syntax
<li> The -o (--options) option shows only the command-line options
</ol>
<li>Enhancements to the [./tickets.wiki|ticket system]:
<ol type="a">
<li> Added the ability to attach wiki pages to a ticket for extended
descriptions.
<li> Added submenu to the 'View Ticket' page, to use it as
template for a new ticket.
<li> Added button 'Submit and New' to create multiple tickets
in a row.
<li> Link the version field in ticket view to a matching checkin or tag.
<li> Show creation time in report and ticket view.
<li> Show previous comments in edit ticket as reference.
</ol>
<li>Added the "hash" query parameter to the
[/help?cmd=/whatis|/whatis webpage].
<li>Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
which alerts subscribers when an admin creates a new user or
when a user's permissions change.
<li>If the FOSSIL_REPOLIST_SHOW environment variable exists and contains
the substring "description", then the project description for each repository
is shown on the repository list page. The login-group for each project is
now only shown if the FOSSIL_REPOLIST_SHOW environment variable exists and
contains the substring "login-group". ([./cgi.wiki#repolist|More information])
<li>The [/doc/trunk/www/th1.md|TH1 script language] is enhanced for improved
security:
<ol type="a">
<li> TH1 now makes a distinction between
[/doc/trunk/www/th1.md#taint|tainted and untainted string values].
This makes it more difficult to write custom TH1 scripts that
contain XSS or SQL-injection bugs. The
[/help?cmd=vuln-report|vuln-report] setting was added to control
what Fossil does when it encounters a potential TH1
security problem.
<li> The "--th" option was removed from the [/help?cmd=pikchr|fossil pikchr]
command.
<li> The "enable_htmlify" TH1 command was removed.
</ol>
<li>Make [/help?cmd=/chat|/chat] better-behaved during server outages, reducing
the frequency of reconnection attempts over time and providing feedback
to the user when the connection is down.
<li>The [/help?cmd=/sqlar|/sqlar] page does not work for users who are not logged
in, nor are links to that page displayed to users who are not logged in. Being
logged in as "anonymous" is sufficient to overcome this restriction, assuming
that "anonymous" can download tarballs and ZIP archives.
<li>Many other minor fixes and additions.
</ol>
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</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
|
| ︙ | ︙ | |||
1034 1035 1036 1037 1038 1039 1040 |
"Columnar" view options on timelines.
* Common display settings (such as the "view" option and the number
of rows in a timeline) are held in a cookie and thus persist
across multiple pages.
* Rework the skin editing process so that changes are implemented
on one of nine /draft pages, evaluated, then merged back to the
default.
| | < | 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 |
"Columnar" view options on timelines.
* Common display settings (such as the "view" option and the number
of rows in a timeline) are held in a cookie and thus persist
across multiple pages.
* Rework the skin editing process so that changes are implemented
on one of nine /draft pages, evaluated, then merged back to the
default.
* Added the [/timeline?skin=ardoise&once|Ardoise] skin.
* Fix the "fossil server" command on Unix to be much more responsive
to multiple simultaneous web requests.
* Use the IPv6 stack for the "fossil ui" and "fossil server"
commands on Windows.
* Support for [https://sqlite.org/sqlar|SQL Archives] as a download
option.
* Fossil now automatically generates the
|
| ︙ | ︙ |
Changes to www/chat.md.
| ︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 171 172 173 174 175 | in a new timeline entry is announced in the chatroom. The announcement appears to come from a user whose name is given by the chat-timeline-user setting. This mechanism is similar to [email notification](./alerts.md) except that the notification is sent via chat instead of via email. ## Implementation Details *You do not need to understand how Fossil chat works in order to use it. But many developers prefer to know how their tools work. This section is provided for the benefit of those curious developers.* | > > > > > > > > > > > > > > > > > > | 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 |
in a new timeline entry is announced in the chatroom. The announcement
appears to come from a user whose name is given by the chat-timeline-user
setting.
This mechanism is similar to [email notification](./alerts.md) except that
the notification is sent via chat instead of via email.
## Quirks
- There is no message-editing capability. This is by design and
desire of `/chat`'s developers.
- When `/chat` has problems connecting to the message poller (see
the next section) it will provide a subtle visual indicator of the
connection problem - a dotted line along the top of the input
field. If the connection recovers within a short time, that
indicator will go away, otherwise it will pop up a loud message
signifying that the connection to the poller is down and how long
it will wait to retry (progressively longer, up to some
maximum). `/chat` will recover automatically when the server is
reachable. Trying to send messages while the poller connection is
down is permitted, and the poller will attempt to recover
immediately if sending of a message succeeds. That applies to any
operations which send traffic, e.g. if the per-message "toggle
text mode" button is activated or a message is globally deleted.
## Implementation Details
*You do not need to understand how Fossil chat works in order to use it.
But many developers prefer to know how their tools work.
This section is provided for the benefit of those curious developers.*
|
| ︙ | ︙ |
Changes to www/chroot.md.
1 2 3 4 5 | # The Server Chroot Jail If you run Fossil as root in any mode that [serves data on the network][srv], and you're running it on Unix or a compatible OS, Fossil will drop itself into a [`chroot(2)` jail][cj] shortly after starting | | | | < | | | > | | > | > | > | 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 |
# The Server Chroot Jail
If you run Fossil as root in any mode that [serves data on the
network][srv], and you're running it on Unix or a compatible OS, Fossil
will drop itself into a [`chroot(2)` jail][cj] shortly after starting
up. The usual reason for launching Fossil
as root to allow it to bind to TCP port 80 for HTTP
service, since normal users are restricted to ports 1024 and higher.
Fossil uses the owner of the Fossil repository file as its new user
ID when it drops root privileges.
When Fossil enters a chroot jail, it needs to have all of its dependencies
inside the chroot jail in order to continue work. There are several
resources that need to be inside the chroot jail with Fossil in order for
Fossil to work correctly:
* the repository file(s)
* `/dev/null` — create it with `mknod(8)` inside the jail directory
([Linux example][mnl], [OpenBSD example][obsd])
* `/dev/urandom` — ditto
* `/proc` — you might need to mount this virtual filesystem inside the
jail on Linux systems that make use of [Fossil’s server load
shedding feature][fls]
* any shared libraries your `fossil` binary is linked to, unless you
[configured Fossil with `--static`][bld] to avoid it
Fossil does all of this as one of many layers of defense against
hacks and exploits. You can prevent Fossil from entering the chroot
jail using the <tt>--nojail</tt> option to the
[fossil server command](/help?cmd=server)
but you cannot make Fossil hold onto root privileges. Fossil always drops
root privilege before accepting inputs, for security.
[bld]: https://fossil-scm.org/home/doc/trunk/www/build.wiki
[cj]: https://en.wikipedia.org/wiki/Chroot
[fls]: ./loadmgmt.md
[mnl]: https://fossil-scm.org/forum/forumpost/90caff30cb
[srv]: ./server/
[obsd]: ./server/openbsd/fastcgi.md#chroot
|
Changes to www/containers.md.
| ︙ | ︙ | |||
668 669 670 671 672 673 674 |
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
| | | 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 |
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://docs.podman.io/en/latest/index.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
|
| ︙ | ︙ |
Changes to www/customskin.md.
| ︙ | ︙ | |||
308 309 310 311 312 313 314 315 316 317 318 319 320 321 | baseline skin into a temporary working directory (here called "./newskin") and then launch the [fossil ui](/help?cmd=ui) command with the "--skin ./newskin" option. If the argument to the --skin option contains a "/" character, then the five control files are read out of the directory named. You can then edit the control files in the ./newskin folder using you favorite text editor, and press "Reload" on your browser to see the effects. ### Disabling The Web Browser Cache During Development Fossil is aggressive about asking the web browser to cache resources. While developing a new skin, it is often helpful to put your web browser into developer mode and disable the cache. If you fail to do this, then you might make some change to your skin | > | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | baseline skin into a temporary working directory (here called "./newskin") and then launch the [fossil ui](/help?cmd=ui) command with the "--skin ./newskin" option. If the argument to the --skin option contains a "/" character, then the five control files are read out of the directory named. You can then edit the control files in the ./newskin folder using you favorite text editor, and press "Reload" on your browser to see the effects. ### Disabling The Web Browser Cache During Development Fossil is aggressive about asking the web browser to cache resources. While developing a new skin, it is often helpful to put your web browser into developer mode and disable the cache. If you fail to do this, then you might make some change to your skin |
| ︙ | ︙ | |||
524 525 526 527 528 529 530 |
3. Edit the *.txt files in SKINDIR. After making each small change,
press Reload on the web browser to see the effect of that change.
Iterate until the desired look is achieved.
4. Copy/paste the resulting css.txt, details.txt,
header.txt, and footer.txt files
into the CSS, details, header, and footer configuration screens
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
3. Edit the *.txt files in SKINDIR. After making each small change,
press Reload on the web browser to see the effect of that change.
Iterate until the desired look is achieved.
4. Copy/paste the resulting css.txt, details.txt,
header.txt, and footer.txt files
into the CSS, details, header, and footer configuration screens
under the Admin/Skins menu. Alternately, import them using the
process described below.
An alternative to step 4 is to convert the skin files into a form
which can be imported into a repository using `fossil config import`.
It requires compiling [a small tool from the fossil source
tree](/file/tools/skintxt2config.c):
>
```
$ cc -o s2c /path/to/fossil/checkout/tools/skintxt2config.c
```
With that in place, the custom skin files can be converted with:
>
```
$ ./s2c yourskin/*.txt > skin.config
```
It can be imported into an arbitrary fossil repository with:
>
```
$ fossil config import skin.config
```
And it can be pushed to a remote repository with:
>
```
$ fossil config push skin
```
That approach has proven to be an effective way to locally develop
skin changes then push them to a "live" site.
## See Also
* [Customizing the Timeline Graph](customgraph.md)
|
Changes to www/env-opts.md.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 | `--comfmtflags NUMBER`: Specify flags that control how check-in comments and certain other text outputs are formatted for display. The flags are individual bits in `NUMBER`, which must be specified in base 10: * _0_ — Uses the revised algorithm with no special handling. | | > > > > > | 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 |
`--comfmtflags NUMBER`: Specify flags that control how check-in comments
and certain other text outputs are formatted for display. The flags are
individual bits in `NUMBER`, which must be specified in base 10:
* _0_ — Uses the revised algorithm with no special handling.
* _1_ — Uses the canonical algorithm, other flags are ignored.
* _2_ — Trims leading and trailing carriage-returns and line-feeds
where they do not materially impact pre-existing formatting
(i.e. at the start of the comment string _and_ right before
line indentation).
* _4_ — Trims leading and trailing spaces where they do not materially
impact the pre-existing formatting (i.e. at the start of the
comment string _and_ right before line indentation).
* _8_ — Attempts to break lines on word boundaries while honoring the
logical line length.
* _16_ — Looks for the original comment text within the text being
printed. Upon matching, a new line will be emitted, thus
preserving more of the pre-existing formatting.
`--comment-format NUMBER`: Alias for `--comfmtflags NUMBER`.
> NOTE: As of Fossil version 2.26, use of the `--comfmtflags` and
> `--comment-format` options is no longer recommended and they are
> no longer documented, but retained for backwards compatibility.
`--errorlog ERRLOG`: Name a file to which fossil will log panics,
errors, and warnings.
`--help`: If `--help` is found anywhere on the command line, translate
the command to `fossil help cmdname` where `cmdname` is the first
|
| ︙ | ︙ | |||
141 142 143 144 145 146 147 148 149 150 151 152 153 154 | local (or remote) testing of the moderation subsystem and its impact on the contents and status of wiki pages. `FOSSIL_HOME`: Location of [configuration database][configdb]. See the [configuration database location][configloc] description for additional information. `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for SEE as text to be hashed into the actual encryption key. This has no effect if Fossil was not compiled with SEE support enabled. `FOSSIL_USER`: Name of the default user account if the checkout, local | > > > > > > > > > > > | 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 | local (or remote) testing of the moderation subsystem and its impact on the contents and status of wiki pages. `FOSSIL_HOME`: Location of [configuration database][configdb]. See the [configuration database location][configloc] description for additional information. `FOSSIL_REPOLIST_TITLE`: The page title of the "Repository List" page loaded by the `fossil all ui` or `fossil ui /` commands. Only used if none of the listed repositories has the `repolist_skin` property set. Can be set from the [CGI control file][cgictlfile]. `FOSSIL_REPOLIST_SHOW`: If this variable exists and has a text value that contains the substring "description", then the "Project Description" column appears on the repolist page. If it contains the substring "login-group", then the Login-Group column appears on the repolist page. Can be set from the [CGI control file][cgictlfile]. `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for SEE as text to be hashed into the actual encryption key. This has no effect if Fossil was not compiled with SEE support enabled. `FOSSIL_USER`: Name of the default user account if the checkout, local |
| ︙ | ︙ | |||
208 209 210 211 212 213 214 215 216 217 218 219 220 221 | `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is used as the location of the `~/.fossil` file. `LOGNAME`: Name of the logged in user on many Unix-like platforms. Used as the fossil user name if `FOSSIL_USER` is not specified. See the discussion of Fossil Username below for a lot more detail. `PATH`: Used by most platforms to locate programs invoked without a fully qualified name. Explicitly used by `fossil ui` on certain platforms to choose the browser to launch. `PATH_INFO`: If defined, included in error log messages. | > > > > | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is used as the location of the `~/.fossil` file. `LOGNAME`: Name of the logged in user on many Unix-like platforms. Used as the fossil user name if `FOSSIL_USER` is not specified. See the discussion of Fossil Username below for a lot more detail. `NO_COLOR`: If defined and not set to a `false` value (i.e. "off", "no", "false", "0"), the `fossil search` command skips colorization of console output using ANSI escape codes (VT100). `PATH`: Used by most platforms to locate programs invoked without a fully qualified name. Explicitly used by `fossil ui` on certain platforms to choose the browser to launch. `PATH_INFO`: If defined, included in error log messages. |
| ︙ | ︙ | |||
462 463 464 465 466 467 468 | will happen on all platforms. ### Web browser Occasionally, fossil wants to launch a web browser for the user, most obviously as part of the `fossil ui` command. In that specific case, | | | > | 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 | will happen on all platforms. ### Web browser Occasionally, fossil wants to launch a web browser for the user, most obviously as part of the `fossil ui` command. In that specific case, the browser is launched pointing at the web server started by `fossil ui` listening on a private TCP port. On all platforms, if the local or global settings `web-browser` is set, that is the command used to open a URL. Otherwise, the specific actions vary by platform. On Unix-like platforms other than Apple's, it looks for the first program from the list `xdg-open`, `gnome-open`, `firefox`, and `google-chrome` that it can find on the `PATH`. On Apple platforms, it assumes that `open` is the command to open a URL in the user's configured default browser. On Windows platforms, it assumes that `start` is the command to open a URL in the user's configured default browser. [configdb]: ./tech_overview.wiki#configdb [configloc]: ./tech_overview.wiki#configloc [cgictlfile]: ./cgi.wiki |
Changes to www/fileformat.wiki.
| ︙ | ︙ | |||
59 60 61 62 63 64 65 | repository. Fossil recognizes the following kinds of structural artifacts: <ul> <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | repository. Fossil recognizes the following kinds of structural artifacts: <ul> <li> [#manifest | Manifests] </li> <li> [#cluster | Clusters] </li> <li> [#ctrl | Control (a.k.a. Tag) Artifacts] </li> <li> [#wikichng | Wiki Pages] </li> <li> [#tktchng | Ticket Changes] </li> <li> [#attachment | Attachments] </li> <li> [#event | TechNotes] </li> <li> [#forum | Forum Posts] </li> </ul> |
| ︙ | ︙ | |||
171 172 173 174 175 176 177 | is optional. The file format might be extended with new permission letters in the future. The optional 4th argument is the name of the same file as it existed in the parent check-in. If the name of the file is unchanged from its parent, then the 4th argument is omitted. A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype | | > > > | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | is optional. The file format might be extended with new permission letters in the future. The optional 4th argument is the name of the same file as it existed in the parent check-in. If the name of the file is unchanged from its parent, then the 4th argument is omitted. A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype is used. Note that the <b>N</b> card has never actually been used by any Fossil implementation. The implementation has always interpreted check-in comments according to the [/wiki_rules|Fossil Wiki formatting rules]. There are no current plans to ever change that. A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card. The <b>P</b> card has a varying number of arguments that define other manifests from which the current manifest is derived. Each argument is a lowercase hexadecimal artifact hash of a predecessor manifest. All arguments to the <b>P</b> card must be unique within that card. |
| ︙ | ︙ | |||
271 272 273 274 275 276 277 | 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]. | | | | > > > > | 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 | 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 (a.k.a. Tag) 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> Control articles are also referred to as Tag artifacts, but tags can also be applied via other artifact types, as described in [#summary|the Card Summary table]. 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 |
| ︙ | ︙ |
Changes to www/fossil-is-not-relational.md.
| ︙ | ︙ | |||
92 93 94 95 96 97 98 | Notably, the artifact file format <u>does not</u>... - Specify any specific storage mechanism for the SCM's raw bytes, which includes both artifacts themselves and client-side file content. The file format refers to all such content solely by its unique hash value. | | | 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | Notably, the artifact file format <u>does not</u>... - Specify any specific storage mechanism for the SCM's raw bytes, which includes both artifacts themselves and client-side file content. The file format refers to all such content solely by its unique hash value. - Specify any optimizations such as storing file-level changes as deltas between two versions of that content. Such aspects are all considered to be implementation details of higher-level applications (be they in the main fossil binary or a hypothetical 3rd-party application), and have no effect on the underlying artifact data model. That said, in Fossil: |
| ︙ | ︙ |
Changes to www/fossil-v-git.wiki.
| ︙ | ︙ | |||
272 273 274 275 276 277 278 | It is common in Fossil to ask to see [/timeline?df=release&y=ci|all check-ins since the last release]. Git lets you see "what came before". Fossil makes it just as easy to also see "what came after". Leaf check-ins in Git that lack a "ref" become "detached," making them difficult to locate and subject to garbage collection. This | | | 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | It is common in Fossil to ask to see [/timeline?df=release&y=ci|all check-ins since the last release]. Git lets you see "what came before". Fossil makes it just as easy to also see "what came after". Leaf check-ins in Git that lack a "ref" become "detached," making them difficult to locate and subject to garbage collection. This [https://stackoverflow.com/q/3965676 | detached head state] problem has caused grief for [https://www.google.com/search?q=git+detached+head+state | many Git users]. With Fossil, detached heads are simply impossible because we can always find our way back into the Merkle tree using one or more of the relations in the SQL database. |
| ︙ | ︙ | |||
340 341 342 343 344 345 346 | Fossil's user-visible functionality. Fossil isn't entirely C and SQL code. Its web UI [./javascript.md | uses JavaScript where necessary]. The server-side UI scripting uses a custom minimal [https://en.wikipedia.org/wiki/Tcl|Tcl] dialect called | | | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | Fossil's user-visible functionality. Fossil isn't entirely C and SQL code. Its web UI [./javascript.md | uses JavaScript where necessary]. The server-side UI scripting uses a custom minimal [https://en.wikipedia.org/wiki/Tcl|Tcl] dialect called [./th1.md|TH1], which is embedded into Fossil itself. Fossil's build system and test suite are largely based on Tcl.⁵ All of this is quite portable. About half of Git's code is POSIX C, and about a third is POSIX shell code. This is largely why the so-called "Git for Windows" distributions (both [https://git-scm.com/download/win|first-party] and [https://gitforwindows.org/|third-party]) are actually an |
| ︙ | ︙ |
Changes to www/gsoc-ideas.md.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 | features to work on in the UI. # UI, Look and Feel Tasks for those interested in graphic/web design: * Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d) | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | features to work on in the UI. # UI, Look and Feel Tasks for those interested in graphic/web design: * Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d) * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_\(software\)&action=history) * Allow diffing of Forum posts * General touch-ups in the existing skins. This may, depending on how deep one cares to dig, require digging into C code to find, and potentially modify, how the HTML is generated. * Creation of one or more new skins. This does not specifically require any C know-how. * Complete per-feature CSS facilities in [the Inskinerator](https://tangentsoft.com/inskinerator/dir) and add features to the Inskinerator |
| ︙ | ︙ |
Changes to www/hooks.md.
| ︙ | ︙ | |||
114 115 116 117 118 119 120 |
a write-transaction on the repository when the before-commit
hook is running, so the repository needs to be in WAL mode if the
script needs to access the repository.
* The %A substitution is the name of a "commit description file" that
shows the details of the commit in progress. To see what a
"commit description file" looks like, set a before-commit hook
| | | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
a write-transaction on the repository when the before-commit
hook is running, so the repository needs to be in WAL mode if the
script needs to access the repository.
* The %A substitution is the name of a "commit description file" that
shows the details of the commit in progress. To see what a
"commit description file" looks like, set a before-commit hook
with a command of "cat %A" and then run a sample commit with
the --dry-run option.
* If any before-commit hook returns a non-zero exit code, then
the commit is abandoned. All
before-commit hooks must exit(0) in order for the commit to
proceed.
|
| ︙ | ︙ |
Changes to www/index.wiki.
| ︙ | ︙ | |||
82 83 84 85 86 87 88 |
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>
| | | | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
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.26 ([/timeline?c=version-2.26|2025-04-30])</h3>
* [/uv/download.html|Download]
* [./changes.wiki#v2_26|Change Summary]
* [/timeline?p=version-2.26&d=version-2.25&y=ci|Check-ins in version 2.26]
* [/timeline?df=version-2.26&y=ci|Check-ins derived from the 2.26 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.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 | <h3>Converting Repositories on Windows</h3> The above commands work best on proper POSIX systems like Linux, macOS, and the BSDs, where everything <tt>git</tt> sends is consumed by <tt>fossil</tt> as soon as it can manage, with both programs working concurrently. | | > | | < | > | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | <h3>Converting Repositories on Windows</h3> The above commands work best on proper POSIX systems like Linux, macOS, and the BSDs, where everything <tt>git</tt> sends is consumed by <tt>fossil</tt> as soon as it can manage, with both programs working concurrently. Historically, PowerShell indiscriminately sent objects — as opposed to raw bytes — through its pipes, and buffered standard input for external processes. This made it choke on the conversion when the in-flight repository size exceeded available memory. Starting with version 7.4 (2023-11-16), PowerShell supports byte stream piping between native commands and file redirection. If you are stuck with an older version, one workaround is to fall back to <tt>cmd.exe</tt> — which doesn't seem to be affected by this problem. Nevertheless, we instead recommend using Microsoft's own [https://learn.microsoft.com/en-us/windows/wsl/ | Windows Subsystem for Linux] or either of the two popular "Git for Windows" distributions based on MSYS2. They handle pipes the POSIX way, avoiding any dependency on the amount of data involved. <h2>Fossil → Git</h2> |
| ︙ | ︙ |
Changes to www/loadmgmt.md.
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
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
| | | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
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
it is possible to activate the load-average limiter on that repository.
If the load average shows exactly "0.0", then that means that Fossil is
unable to find the load average. This can either be because it is in a
`chroot(2)` jail without `/proc` access, or because it is running on a
system that does not support `getloadavg()` and so the load-average
limiter will not function.
[503]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4
[hte]: /help?cmd=/test-env
[gla]: https://linux.die.net/man/3/getloadavg
[lin]: http://www.linode.com
[sh]: ./selfhost.wiki
|
Changes to www/mirrorlimitations.md.
| ︙ | ︙ | |||
44 45 46 47 48 49 50 51 52 53 54 55 56 57 | check-in of each branch. Depending on the check-in graph topology, this is sufficient to infer the branch for many historical check-ins as well. However, complex histories with lots of cross-merging can lead to ambiguities. Fossil keeps track of historical branch names unambiguously, but the extra details about branch names that Fossil keeps at hand cannot be exported to Git. ## (4) Non-unique Tags Git requires tags to be unique: each tag must refer to exactly one check-in. Fossil does not have this restriction, and so it is common in Fossil to tag multiple check-ins with the same name. For example, it is common in Fossil to tag each check-in creating a release both | > > > > > > > > > > > > > > > > > > > | 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 | check-in of each branch. Depending on the check-in graph topology, this is sufficient to infer the branch for many historical check-ins as well. However, complex histories with lots of cross-merging can lead to ambiguities. Fossil keeps track of historical branch names unambiguously, but the extra details about branch names that Fossil keeps at hand cannot be exported to Git. An example of the kinds of ambiguities that arise when branch names are not tracked is a "diamond-merge" history. In a diamond-merge, a long-running development branch merges enhancements from trunk from time to time and also periodically merges the development changes back to trunk at moments when the branch is stable. An example of diamond-merge in the Fossil source tree itself can be seen at on the [bv-corrections01 branch](/timeline?r=bv-corrections01). The distinction between checkins on the branch and checkins on trunk would be lost in Git, which does not track branches for individual checkins, and so you cannot (easily) tell which checkins are part of the branch and which are part of trunk in a diamond-merge history on Git. For that reason, diamond-merge histories are considered an anti-pattern in Git and the usual recommendation for Git users is to employ [rebase](./rebaseharm.md) to clean the history up. The point here is that if your project has a diamond-merge history that shows up cleanly in Fossil, it will export to Git and still be technically correct, but the history display might be a jumbled mess that is difficult for humans to comprehend. ## (4) Non-unique Tags Git requires tags to be unique: each tag must refer to exactly one check-in. Fossil does not have this restriction, and so it is common in Fossil to tag multiple check-ins with the same name. For example, it is common in Fossil to tag each check-in creating a release both |
| ︙ | ︙ |
Changes to www/permutedindex.html.
| ︙ | ︙ | |||
103 104 105 106 107 108 109 110 111 112 113 114 115 116 | <li><a href="pop.wiki">Principles Of Operation</a></li> <li><a href="qandc.wiki">Questions And Criticisms</a></li> <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> <li><a href="rebaseharm.md">Rebase Considered Harmful</a></li> <li><a href="reviews.wiki">Reviews</a></li> <li><a href="chroot.md">Server Chroot Jail</a></li> <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> <li><a href="../../../sitemap">Site Map</a></li> <li><a href="style.wiki">Source Code Style Guidelines</a></li> <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> <li><a href="ssl-server.md">SSL/TLS Server Mode</a></li> <li><a href="backoffice.md">The "Backoffice" mechanism of Fossil</a></li> <li><a href="patchcmd.md">The "fossil patch" Command</a></li> <li><a href="blame.wiki">The Annotate/Blame Algorithm Of Fossil</a></li> | > | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | <li><a href="pop.wiki">Principles Of Operation</a></li> <li><a href="qandc.wiki">Questions And Criticisms</a></li> <li><a href="quotes.wiki">Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</a></li> <li><a href="rebaseharm.md">Rebase Considered Harmful</a></li> <li><a href="reviews.wiki">Reviews</a></li> <li><a href="chroot.md">Server Chroot Jail</a></li> <li><a href="shunning.wiki">Shunning: Deleting Content From Fossil</a></li> <li><a href="signing.md">Signing Check-ins</a></li> <li><a href="../../../sitemap">Site Map</a></li> <li><a href="style.wiki">Source Code Style Guidelines</a></li> <li><a href="tech_overview.wiki">SQLite Databases Used By Fossil</a></li> <li><a href="ssl-server.md">SSL/TLS Server Mode</a></li> <li><a href="backoffice.md">The "Backoffice" mechanism of Fossil</a></li> <li><a href="patchcmd.md">The "fossil patch" Command</a></li> <li><a href="blame.wiki">The Annotate/Blame Algorithm Of Fossil</a></li> |
| ︙ | ︙ |
Changes to www/quickstart.wiki.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 | 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 | | | < < | | | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | 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 |
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.25 [8f798279d5] 2024-11-06 12:59:09 UTC
</b></pre>
<h2 id="workflow" name="fslclone">General Work Flow</h2>
Fossil works with [./glossary.md#repository | repository files]
and [./glossary.md#check-out | check-out directories] using a
workflow like this:
<ul>
<li>Create or clone a repository file. ([/help/init|fossil init] or
[/help/clone | fossil clone])
<li>Check out a local tree. ([/help/open | fossil open])
<li>Perform operations on the repository (including repository
configuration).
</ul>
Fossil can be entirely driven from the command line. Many features
can also be conveniently accessed from the built-in web user interface.
The following sections give a brief overview of these
operations.
<h2 id="new">Starting A New Project</h2>
To start a new project with Fossil, [/help/init | create a new empty repository]:
<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 it is only required if you are going to use the
<tt>[/help/server | fossil server DIRECTORY]</tt> feature.
Next, do something along the lines of:
<pre>
<b>mkdir -p ~/src/project/trunk</b>
<b>cd ~/src/project/trunk</b>
<b>fossil open</b> <i>repository-filename</i>
<b>fossil add</b> foo.c bar.h qux.md
<b>fossil commit</b>
</pre>
If your project directory already exists, obviating the <b>mkdir</b>
step, you will instead need to add the <tt>--force</tt> flag to the
<b>open</b> command to authorize Fossil to open the repo into a
non-empty checkout directory. (This is to avoid accidental opens into,
for example, your home directory.)
The convention of naming your checkout directory after a long-lived
branch name like "trunk" is in support of Fossil's ability to have as
many open checkouts as you like. This author frequently has additional
checkout directories named <tt>../release</tt>, <tt>../scratch</tt>,
etc. The release directory is open to the branch of the same name, while
the scratch directory is used when disturbing one of the other
long-lived checkout directories is undesireable, as when performing a
[/help/bisect | bisect] operation.
<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, a process called
"[/help/clone | cloning]".
This is done as follows:
<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
|
| ︙ | ︙ | |||
79 80 81 82 83 84 85 | 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 | | > > > > > > > > | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | 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 (intial remote-access password is "yoWgDR42iv")> </b></pre> This <i>exampleuser</i> will be used by Fossil as the author of commits when you checkin changes to the repository. It is also used by Fossil when you make your repository available to others using the built-in server mode by running <tt>[/help/server | fossil server]</tt> and will also be used when running <tt>[/help/ui | fossil ui]</tt> to view the repository through the Fossil UI. See the quick start topic for setting up a <a href="#server">server</a> for more details. 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. |
| ︙ | ︙ | |||
125 126 127 128 129 130 131 | [https://www.mercurial-scm.org/|Mercurial]. Fossil can also import [https://subversion.apache.org/|Subversion projects] directly. <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 | | < | < < | 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 |
[https://www.mercurial-scm.org/|Mercurial].
Fossil can also import [https://subversion.apache.org/|Subversion projects] directly.
<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, <tt>cd</tt> into that directory, and then:
<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>
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>
|
| ︙ | ︙ | |||
166 167 168 169 170 171 172 | 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, | | | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | 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 |
| ︙ | ︙ | |||
292 293 294 295 296 297 298 | the first checkin, and the default for any time a branch name is needed but not specified. This will get you started on identifying checkins. The <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including how timestamps can also be used. | | | < | < < > | > | < | | | > > > > | > > > > > > | > > > > > > > > | | | > > > | 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 | the first checkin, and the default for any time a branch name is needed but not specified. This will get you started on identifying checkins. The <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including how timestamps can also be used. <h2 id="config">Accessing Your Local Repository's Web User Interface</h2> After you create a new repository, you usually want to do some local configuration. This is most easily accomplished by firing up the Fossil UI: <pre> <b>fossil ui</b> <i>repository-filename</i> </pre> You can shorten that to just [/help/ui | <b>fossil ui</b>] if you are inside a checked-out local tree. This command starts an internal web server, after which Fossil automatically launches your default browser, pointed at itself, presenting a special view of the repository, its web user interface. You may override Fossil's logic for selecting the default browser so: <pre> <b>fossil setting web-browser</b> <i>path-to-web-browser</i> </pre> When launched this way, Fossil binds its internal web server to the IP loopback address, 127.0.0.1, which it treats specially, bypassing all user controls, effectively giving visitors the [./caps/admin-v-setup.md#apsu | all-powerful Setup capabliity]. Why is that a good idea, you ask? Because it is a safe presumption that only someone with direct file access to the repository database file could be using the resulting web interface. Anyone who can modify the repo DB directly could give themselves any and all access with a SQL query, or even by direct file manipulation; no amount of access control matters to such a user. (Contrast the [#server | many <i>other</i> ways] of setting Fossil up as an HTTP server, where the repo DB is on the other side of the HTTP server wall, inaccessible by all means other than Fossil's own mediation. For this reason, the "localhost bypasses access control" policy does <i>not</i> apply to these other interfaces. That is a very good thing, since without this difference in policy, it would be unsafe to bind a [/help?cmd=server | <b>fossil server</b>] instance to localhost on a high-numbered port and then reverse-proxy it out to the world via HTTPS, a practice this author does engage in, with confidence.) Once you are finished configuring Fossil, you may safely Control-C out of the <b>fossil ui</b> command to shut down this privileged built-in web server. Moreover, you may by grace of SQLite do this <i>at any time</i>: all changes are either committed durably to the repo DB or rolled back, in their totality. This includes configuration changes. <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: |
| ︙ | ︙ | |||
382 383 384 385 386 387 388 | 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> | | | < < < < | 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | 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> Use the --branch option to the [/help/commit | commit] command to start a new branch at the point of need. ([./gitusers.md#bneed | Contrast git].) 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: |
| ︙ | ︙ | |||
440 441 442 443 444 445 446 | 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> | > | < < > | | < < < | < < | > > | < < | < < < < | | | < > | | > < < | | < | | 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 |
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>
In addition to the inward-facing <b>fossil ui</b> mode covered [#config
| above], Fossil can also act as an outward-facing web server:
<pre>
<b>[/help/server | fossil server]</b> <i>repository-filename</i>
</pre>
Just as with <b>fossil ui</b>, you may omit the
<i>repository-filename</i> parameter when running this from within an open
check-out.
<i>Unlike</i> <b>fossil ui</b> mode, Fossil binds to all network
interfaces by default in this mode, and it enforces the configured
[./caps/ | role-based access controls]. Further, because it is meant to
provide external web service, it doesn't try to launch a local web
browser pointing to a "Fossil UI" presentation; external visitors see
your repository's configured home page instead.
To serve varying needs, there are additional ways to serve a Fossil repo
to external users:
<ul>
<li>[./server/any/cgi.md|CGI], as used by Fossil's [./selfhost.wiki |
self-hosting repositories]
<li>[./server/any/scgi.md|SCGI]
<li>[./server/any/inetd.md|inetd]
<li>[./server/debian/service.md|systemd]
</ul>
…along with [./server/#matrix | several other options].
We recommend that you read the [./server/whyuseaserver.wiki | Benefits
of a Fossil Server] article, because you might <i>need</i> to do this
and not yet know it.
<h2 id="proxy">HTTP Proxies</h2>
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,
|
| ︙ | ︙ |
Changes to www/reviews.wiki.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 | 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"> | | | | | | | < < | | | | | | | < | > | | 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 | 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="https://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 bindings for it for Mozilla's SpiderMonkey JavaScript engine), and (B) it could run as a CGI. That second point might seem a bit archaic, but in practice CGI is the only way most hosted sites can set up a shared source repository with multiple user IDs. (i'm not about to give out my only account password or SSH key for my hosted sites, no matter how much I trust the other developers, and none of my hosters allow me to run standalone servers or add Apache modules.) So I tried it out. The thing which bugged me most about it was having to type "commit" or "com" instead of "ci" for checking in (as is custom in all other systems I've used), despite the fact that fossil uses "ci" as a filter in things like the timeline view. Looking back now, I have used fossil for about about 95% of my work in the past year, in over 15 source trees, and I now get tripped up when I have to use svn or cvs. So, having got over typing "fossil com -m ...", here's why I love it so much... Point #1: CGI Again, this sounds archaic, but fossil has allowed me to share source trees which I cannot justifiably host in other projects I work on (they don't belong to those projects), which I cannot host in google code (because google code doesn't allow/recognize Public Domain as a license, and I refuse to relicense just to accommodate them), and for which SourceForge is overkill (and way too slow). With fossil I can create a new repo, have it installed on my hoster (https://fossil.wanderinghorse.net), and be commiting code to it within 5 minutes. Point #2: Wiki I hate wikis. I really do. Always have. They all have a different syntax and the content tends to get really disorganized really quickly. Their nature makes it difficult to reorganize them without |
| ︙ | ︙ |
Changes to www/selfhost.wiki.
1 2 | <title>Fossil Self-Hosting 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 | <title>Fossil Self-Hosting Repositories</title> Fossil has self-hosted since 2007-07-21. As of 2025-02-11 there are three publicly accessible repositories for the Fossil source code: 1. [https://fossil-scm.org/] 2. [https://www2.fossil-scm.org/] 3. [https://www3.fossil-scm.org/] The canonical repository is (1). Repositories (2) and (3) automatically stay in synchronization with (1) via a <a href="http://en.wikipedia.org/wiki/Cron">cron job</a> that invokes "fossil sync" at regular intervals. Repository (2) also publishes a [https://github.com/drhsqlite/fossil-mirror|GitHub mirror of Fossil] as a demonstration of [./mirrortogithub.md|how that can be done]. Note that the two secondary repositories are more than just read-only mirrors. All three servers support full read/write capabilities. Changes (such as new tickets or wiki or check-ins) can be implemented on any of the three servers and those changes automatically propagate to the other two servers. Server (1) runs as a [./aboutcgi.wiki|CGI script] on a <a href="http://www.linode.com/">Linode</a> located in Dallas, TX - on the same virtual machine that hosts <a href="http://www.sqlite.org/">SQLite</a> and over a 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 a single physical box. The CGI script that runs the canonical Fossil |
| ︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 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 | > | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | 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] (update: $6/month now) 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</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/debian/service.md.
| ︙ | ︙ | |||
50 51 52 53 54 55 56 | restrictions on TCP ports in every OS where `systemd` runs, but you can 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`: | | | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | restrictions on TCP ports in every OS where `systemd` runs, but you can 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 |
| ︙ | ︙ | |||
162 163 164 165 166 167 168 | socket listener, which `systemd` calls “[socket activation][sa],” 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`: | | | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | socket listener, which `systemd` calls “[socket activation][sa],” 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 |
| ︙ | ︙ | |||
187 188 189 190 191 192 193 | This configuration says more or less the same thing as the socket part of an `inetd` entry [exemplified elsewhere in this documentation](../any/inetd.md). Next, create the service definition file in that same directory as `fossil@.service`: | | | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | This configuration says more or less the same thing as the socket part of an `inetd` 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 |
| ︙ | ︙ |
Changes to www/server/windows/service.md.
| ︙ | ︙ | |||
53 54 55 56 57 58 59 | When the Fossil server will be used at times that files may be locked during virus scanning, it is prudent to arrange that its directory used for temporary files is exempted from such scanning. Ordinarily, this will be a subdirectory named "fossil" in the temporary directory given by the Windows GetTempPath(...) API, [namely](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw#remarks) the value of the first existing environment variable from `%TMP%`, `%TEMP%`, `%USERPROFILE%`, and `%SystemRoot%`; you can look for their actual values in | | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | When the Fossil server will be used at times that files may be locked during virus scanning, it is prudent to arrange that its directory used for temporary files is exempted from such scanning. Ordinarily, this will be a subdirectory named "fossil" in the temporary directory given by the Windows GetTempPath(...) API, [namely](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw#remarks) the value of the first existing environment variable from `%TMP%`, `%TEMP%`, `%USERPROFILE%`, and `%SystemRoot%`; you can look for their actual values in your system by accessing the `/test-env` webpage. Excluding this subdirectory will avoid certain rare failures where the fossil.exe process is unable to use the directory normally during a scan. ### <a id='PowerShell'></a>Advanced service installation using PowerShell As great as `fossil winsrv` is, it does not have one to one reflection of all of the `fossil server` [options](/help?cmd=server). When you need to use some of |
| ︙ | ︙ |
Changes to www/serverext.wiki.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 | 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> | | | > | > > | | 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 |
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:
|
| ︙ | ︙ | |||
115 116 117 118 119 120 121 122 123 124 125 126 | 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 the user upload a file using a form, and then displays that file in the reply. There is a link on the page that causes the fileup1 script to return a copy of its own source-code, so you can see how it works. <h2 id="cgi-inputs">3.0 CGI Inputs</h2> The /ext extension mechanism is an ordinary CGI interface. Parameters are passed to the CGI program using environment variables. The following | > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | 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 the user upload a file using a form, and then displays that file in the reply. There is a link on the page that causes the fileup1 script to return a copy of its own source-code, so you can see how it works. <h3>2.3 Example #3</h3> For Fossil versions dated 2025-03-23 and later, the "--extpage FILENAME" option to the [/help?cmd=ui|fossil ui] command is a short cut that treats FILENAME as a CGI extension. When the ui command starts up a new web browser pages, it points that page to the FILENAME extension. So if FILENAME is a static content file (such as an HTML file or [/md_rules|Markdown] or [/wiki_rules|Wiki] document), then the rendered content of the file is displayed. Meanwhile, the user can be editing the source text for that document in a separate window, and periodically pressing "Reload" on the web browser to instantly view the rendered results. For example, the author of this documentation page is running "<tt>fossil ui --extpage www/serverext.wiki</tt>" while editing this very paragraph, and presses Reload from time to time to view his edits. A same idea applies when developing new CGI applications using a script language (for example using [https://wapp.tcl.tk|Wapp]). Run the command "<tt>fossil ui --extpage SCRIPT</tt>" where SCRIPT is the name of the application script, while editing that script in a separate window, then press Reload periodically on the web browser to test the script. <h2 id="cgi-inputs">3.0 CGI Inputs</h2> The /ext extension mechanism is an ordinary CGI interface. Parameters are passed to the CGI program using environment variables. The following standard CGI environment variables are supplied: * AUTH_TYPE * AUTH_CONTENT * CONTENT_LENGTH * CONTENT_TYPE * DOCUMENT_ROOT * GATEWAY_INTERFACE |
| ︙ | ︙ | |||
159 160 161 162 163 164 165 | Do a web search for "[https://duckduckgo.com/?q=cgi+environment_variables|cgi environment variables]" to find more detail about what each of the above variables mean and how they are used. Live listings of the values of some or all of these environment variables can be found at links like these: | | | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | Do a web search for "[https://duckduckgo.com/?q=cgi+environment_variables|cgi environment variables]" to find more detail about what each of the above variables mean and how they are used. Live listings of the values of some or all of these environment variables can be found at links like these: * [https://fossil-scm.org/home/test-env] * [https://sqlite.org/src/ext/checklist/top/env] In addition to the standard CGI environment variables listed above, Fossil adds the following: * FOSSIL_CAPABILITIES * FOSSIL_NONCE |
| ︙ | ︙ |
Changes to www/settings.wiki.
1 2 | <title>Fossil Settings</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 | <title>Fossil Settings</title> <h1>Using Fossil Settings</h1> Settings control the behaviour of fossil. They are set with the <tt>fossil settings</tt> command, or through the web interface in the Settings page in the Admin section. For a list of all settings, view the Settings page, or type <tt>fossil help settings</tt> from the command line. <h2 id="repo">1.0 Repository settings</h2> Settings are set on a per-repository basis. When you clone a repository, a subset of settings are copied to your local repository. If you make a change to a setting on your local repository, it is not synced back to the server when you <tt>push</tt> or <tt>sync</tt>. If you make a change on the server, you need to manually make the change on all repositories which are cloned from this repository. You can also set a setting globally on your local machine. The value will be used for all repositories cloned to your machine, unless overridden explicitly in a particular repository. Global settings can be set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> command. <h2 id="versionable">2.0 "Versionable" settings</h2> Most of the settings control the behaviour of fossil on your local machine, largely acting to reflect your preference on how you want to use Fossil, how you communicate with the server, or options for hosting a repository on the web. However, for historical reasons, some settings affect how you work with |
| ︙ | ︙ |
Added www/signing.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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# Signing Check-ins
Fossil can sign check-in manifests. A basic concept in public-key
cryptography, signing can bring some advantages such as authentication and
non-repudiation. In practice, a serious obstacle is the public key
infrastructure – that is, the problem of reliably verifying that a given
public key belongs to its supposed owner (also known as _"signing is easy,
verifying is hard"_).
Fossil neither creates nor verifies signatures by itself, instead relying on
external tools that have to be installed side-by-side. Historically, the tool
most employed for this task was [GnuPG](https://gnupg.org); recently, there has
been an increase in the usage of [OpenSSH](https://openssh.com) (the minimum
required version is 8.1, released on 2019-10-09).
## Signing a check-in
The `clearsign` setting must be on; this will cause every check-in to be signed
(unless you provide the `--nosign` flag to `fossil commit`). To this end,
Fossil calls the command given by the `pgp-command` setting.
Fossil needs a non-detached signature that includes the rest of the usual
manifest. For GnuPG, this is no problem, but as of 2025 (version 9.9p1) OpenSSH
can create **and verify** only detached signatures; Fossil itself must
attach this signature to the manifest prior to committing. This makes the
verification more complex, as additional steps are needed to extract the
signature and feed it into OpenSSH.
### GnuPG
The `pgp-command` setting defaults to
`gpg --clearsign -o`.
(A possible interesting option to `gpg --clearsign` is `-u`, to specify the
user to be used for signing.)
### OpenSSH
A reasonable value for `pgp-command` is
```
ssh-keygen -q -Y sign -n fossilscm -f ~/.ssh/id_ed25519
```
for Linux, and
```
ssh-keygen -q -Y sign -n fossilscm -f %USERPROFILE%/.ssh/id_ed25519
```
for Windows, changing as appropriate `-f` to the path of the private key to be
used.
The value for `-n` (the _namespace_) can be changed at will, but care has to be
taken to use the same value when verifying the signature.
Fossil versions prior to 2.26 do not understand SSH signatures and
will treat artifacts signed this way as opaque blobs, not Fossil
artifacts.
## Verifying a signature
Fossil does not provide an internal method for verifying signatures and
relies – like it does for signing – on external tools.
### GnuPG
Assuming you used the
default GPG command for signing, one can verify the signature using
```
fossil artifact <CHECK-IN> | gpg --verify
```
### OpenSSH
The user and the key that was used to create the signature must be listed
together in the `ALLOWED_SIGNERS` file (see
[documentation](https://man.openbsd.org/ssh-keygen#ALLOWED_SIGNERS)).
Note that in that file, the "@DOMAIN" bit for the principal is only a
recommendation; you can (or even _should_) simply use your Fossil user name.
As mentioned, for lack of an OpenSSH built-in non-detached signature mechanism,
the burden of extracting the relevant part of the signed check-in is on the
user.
The following recipes are provided only as examples and can be easily extended
to fully-fledged scripts.
#### For Linux:
```bash
fsig=$(mktemp /tmp/__fsig.XXXXXX) && \
fusr=$(fossil artifact tip \
| awk -v m="${fsig}" -v s="${fsig}.sig" \
'/^-----BEGIN SSH SIGNED/{of=m;next} \
/^-----BEGIN SSH SIGNATURE/{of=s} \
/^U /{usr=$2} \
/./{if(!of){exit 42};print >> of} END{print usr}') && \
ssh-keygen -Y verify -f ~/.ssh/allowed_signers -I ${fusr} -n fossilscm \
-s "${fsig}.sig" < "${fsig}" || echo "No SSH signed check-in" && \
rm -f "${fsig}.sig" "${fsig}" && \
unset -v fsig fusr
```
#### For Windows (cmd):
The following incantation makes use of `awk` and `dos2unix`, standard Unix
tools but requiring separate installation on Windows (for example,using
[BusyBox](https://frippery.org/busybox/#downloads)). The usage of `awk` can be
replaced with the Windows basic tool `findstr`, leading to a longer recipe.
```bat
fossil artifact <CHECK-IN> | awk -v m="__fsig" -v s="__fsig.sig" ^
"/^-----BEGIN SSH SIGNED/{of=m;next} /^-----BEGIN SSH SIGNATURE/{of=s} /./{if(!of){exit 42};print >> of}"
if %errorlevel% equ 42 (echo No SSH signed check-in)
REM ---Skip remaining lines if no SSH signed message---
for /f "tokens=2" %i in ('findstr /b "U " __fsig') do set fusr=%i
dos2unix __fsig __fsig.sig
ssh-keygen -Y verify -f %USERPROFILE%\.ssh\allowed_signers -I "%fusr%" ^
-n fossilscm -s __fsig.sig < __fsig
del __fsig __fsig.sig 2>nul & set "fusr="
```
|
Changes to www/ssl-server.md.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 | proxy that handled the SSL/TLS decryption/encryption and passed cleartext down to Fossil. [0]: ./ssl.wiki [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13 Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13), | | | < < < < | | | < < < | | | > | | | > > | | > > > | > > > | > > | > | > > > > > > > > | < < < > > > > | > > | < | > > > > | > > > > > > > > | > > > < | > > > < < < > | > | | | | | > > | | | | > > > > > > > > | > > > > > > | > | > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > < < < | > | | | | < < | > | | > | > > | > > | > > | > | | | > > | 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 |
proxy that handled the SSL/TLS decryption/encryption and passed cleartext
down to Fossil.
[0]: ./ssl.wiki
[1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13
Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13),
Fossil servers are now able to converse directly over TLS. Commands like
* "[fossil server](/help?cmd=server)"
* "[fossil ui](/help?cmd=ui)", and
* "[fossil http](/help?cmd=http)"
may now handle the encryption natively when suitably configured, without
requiring a third-party proxy layer.
## <a id="usage"></a>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
Here, we are passing the magic name "unsafe-builtin" to cause Fossil to
use a [hard-coded self-signed cert][hcssc] rather than one obtained from
a recognized [Certificate Authority][CA], or "CA".
As the name implies, this self-signed cert is _not secure_ and should
only be used for testing. Your web browser is likely to complain
bitterly about it and will refuse to display the pages using the
"unsafe-builtin" cert until you placate it. The complexity of the
ceremony demanded depends on how paranoid your browser’s creators have
decided to be. It may require as little as clicking a single big "I know
the risks" type of button, or it may require a sequence be several
clicks designed to discourage the “yes, yes, just let me do the thing”
crowd lest they run themselves into trouble by disregarding well-meant
warnings.
Our purpose here is to show you an alternate path that will avoid the
issue entirely, not weigh in on which browser handles self-signed
certificates best.
[CA]: https://en.wikipedia.org/wiki/Certificate_authority
[hcssc]: /info/c2a7b14c3f541edb96?ln=89-116
## <a id="about"></a>About Certs
The X.509 certificate system used by browsers to secure TLS connections
is based on asymmetric public-key cryptography. The methods for
obtaining one vary widely, with a resulting tradeoff we may summarize as
trustworthiness versus convenience, the latter characteristic falling as
the former rises.(^No strict correlation exists. CAs have invented
highly inconvenient certification schemes that offer little additional
real-world trustworthiness. Extreme cases along this axis may be fairly
characterized as [security theater][st]. We focus in this document on
well-balanced trade-offs between decreasing convenience and useful
levels of trustworthiness gained thereby.)
The self-signed method demonstrated above offers approximately zero
trustworthiness, though not zero _value_ since it does still provide
connection encryption.
More trustworthy methods are necessarily less convenient. One such is to
send your public key and the name of the domain you want to protect to a
recognized CA, which then performs one or more tests to convince itself
that the requester is in control of that domain. If the CA’s tests all
pass, it produces an X.509 certificate bound to that domain, which
includes assorted other information under the CA’s digital signature
attesting to the validity of the document’s contents. The result is sent
back to the requester, which may then use it to transitively attest to
these tests’ success: presuming one cannot fake the type of signature
used, the document must have been signed by the trusted, recognized CA.
There is one element of the assorted information included with a
certificate that is neither supplied by the requester nor rubber-stamped
on it in passing by the CA. It also generates a one-time key pair and
stores the public half in the certificate. The cryptosystem this keypair
is intended to work with varies both by the CA and by time, as older
systems become obsolete. Details aside, the CA then puts this matching
private half of the key in a separate file, often encrypted under a
separate cryptosystem for security.
SSL/TLS servers need both resulting halves to make these attestations,
but they send only the public half to the client when establishing the
connection. The client then makes its own checks to determine whether it
trusts the attestations being made.
A properly written and administered server never releases the private
key to anyone. Ideally, it goes directly from the CA to the requesting
server and never moves from there; then when it expires, the server
deletes it permanently.
[st]: https://en.wikipedia.org/wiki/Security_theater
## <a id="startup"></a>How To Tell Fossil About Your Cert And Private Key
As we saw [above](#usage),
if you do not have your own cert and private key, you can ask Fossil
to use "unsafe-builtin", which is a self-signed cert that is built into
Fossil. This is wildly insecure, since the private key is not really private;
it is [in plain sight][hcssc] in the Fossil
source tree for anybody to read. <b>Never add the private key that is
built into Fossil to your OS's trust store</b> as doing so will severely
compromise your computer.[^ssattack] This built-in cert is only useful for testing.
If you want actual security, you will need to come up with your own private
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.
### <a id="cat"></a>Separate or Concatenated?
Given a single concatenated 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](./chroot.md) at `/home/www` and drop all root privileges.
This method of combining your cert and private key into a single big PEM
file carries risks, one of which is that the system administrator must
make both halves readable by the user running the Fossil server. Given
the chroot jail feature, a more secure scheme separates the halves so
that only root can read the private half, which then means that when
Fossil drops its root privileges, it becomes unable to access the
private key on disk. Fossil’s `server` feature includes the `--pkey`
option to allow for that use case:
fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil
[^ssattack]: ^How, you ask? Because the keys are known, they can be used
to provide signed certificates for **any** other domain. One foolish
enough to tell their OS’s TLS mechanisms to trust the signing
certificate is implicitly handing over all TLS encryption controls
to any attacker that knows they did this. Don’t do it.
### <a id="chain"></a>Chains and Links
The file name “`fullchain.pem`” used above is a reference to a term of
art within this world of TLS protocols and their associated X.509
certificates. Within the simplistic scheme originally envisioned by the
creators of SSL — the predecessor to TLS — we were all expected to agree
on a single set of CA root authorities, and we would all agree to get
our certificates from one of them. The real world is more complicated:
* The closest we have to universal acceptance of CAs is via the
[CA/Browser Forum][CAB], and even within its select membership there
is continual argument over which roots are trustworthy. (Hashing
that out is arguably this group’s key purpose.)
* CAB’s decision regarding trustworthiness may not match that of any
given system’s administrator. There are solid, defensible reasons to
prune back the stock CA root set included with your browser, then to
augment it with ones CAB _doesn’t_ trust.
* TLS isn’t limited to use between web browsers and public Internet
sites. Several common use cases preclude use of the process CAB
envisions, with servers able to contact Internet-based CA roots as
part of proving their identity. Different use cases demand different
CA root authority stores.
The most common of these divergent cases are servers behind strict
firewalls and edge devices that never interact with the public
Internet. This class ranges from cheap home IoT devices to the
internal equipment managed by IT for a massive global corporation.
Your private Fossil server is liable to fall into that last category.
This may then require that you generate a more complicated “chain” of
certificates for Fossil to use here, without which the client may not be
able to get back to a CA root it trusts. This is true regardless of
whether that client is another copy of Fossil or a web browser
traversing Fossil’s web UI, though that fact complicates matters by
allowing for multiple classes of client, each of which may have their
own rules for modifying the stock certificate scheme.
This is distressingly common, in fact: Fossil links to OpenSSL to
provide its TLS support, but there is a good chance that your browser
uses another TLS implementation entirely. They may or may not agree on a
single CA root store.
How you accommodate all this complexity varies by the CA and other
details. As but one example, Firefox’s “View Certificate” feature offers
_two_ ways to download a given web site’s certificate: the cert alone or
the “chain” leading back to the root. Depending on the use case, the
standalone certificate might suffice, or you might need some type of
cert chain. Complicating this is that the last link in the chain may be
left off when it is for a mutually trusted CA root, implicitly
completing the chain.
[CAB]: https://en.wikipedia.org/wiki/CA/Browser_Forum
## <a id="acme"></a>The ACME Protocol
The [ACME Protocol][2] simplifies all this by automating the process of
proving to a recognized public CA that you are in control of a given
website. Without this proof, no valid CA will issue a cert for that
domain, as that allows fraudulent impersonation.
The primary implementation of ACME is [certbot], a product of the Let’s
Encrypt organization.
Here is, in a nutshell, what certbot will do to obtain your cert:
1. It sends your "signing request" (the document that contains
your public key and your domain name) to the CA.
2. After receiving the signing request, the CA needs to verify that
you control the domain of the cert. One of several methods certbot has
for accomplishing this is to create a secret token and place it at
a well-known location, then tell the CA about it over ACME.
3. The CA then tries pulling that token, which if successful proves
that the requester is able to create arbitrary data on the server,
implicitly proving control over that server. This must be done
over the unencrypted HTTP protocol since TLS isn’t working yet.
4. If satisfied by this proof of control, the CA then creates the
keypair described above and bakes the public half into the
certificate it signs. It then sends this and the private half of
the key back to certbot.
5. Certbot stores these halves separately for the reasons sketched
out above.
6. It then deletes the secret one-time-use token it used to prove
domain control. ACME’s design precludes replay attacks.
In order for all of this to happen, certbot needs to be able to create
a subdirectory named ".well-known", within a directory you specify,
then populate that subdirectory with a token file of some kind. To support
this, the "[fossil server](/help?cmd=server)" and
"[fossil http](/help?cmd=http)" commands have the --acme option.
When specified, Fossil sees a URL where the path
begins with ".well-known", then instead of doing its normal processing, it
looks for a file with that pathname and returns it to the client. If
the "server" or "http" command is referencing a single Fossil repository,
then the ".well-known" sub-directory should be in the same directory as
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, you may either pass the two halves
to Fossil separately using the `--pkey` and `--cert` options described
above, or you may concatenate them and pass that via `--cert` alone.
[2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment
[certbot]: https://certbot.eff.org
|
Changes to www/sync.wiki.
| ︙ | ︙ | |||
56 57 58 59 60 61 62 | 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 using the [/help?cmd=server|"fossil server" command], or it might be launched from inetd or xinetd using the | | | | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 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 using the [/help?cmd=server|"fossil server" command], or it might be launched from inetd or xinetd using the [/help?cmd=http|"fossil http" command]. Or the server might be [./server/any/cgi.md|launched from CGI] or from [./server/any/scgi.md|SCGI]. (See "[./server/|How To Configure A Fossil Server]" for details.) The specifics of how the server listens for incoming HTTP requests is immaterial to this protocol. The important point is that the server is listening for requests and the client is the issuer of the requests. A single [/help?cmd=push|push], [/help?cmd=pull|pull], or [/help?cmd=sync|sync] might involve multiple HTTP requests. The client maintains state between all requests. But on the server side, each request is independent. The server does not preserve any information about the client from one request to the next. Note: Throughout this article, we use the terms "server" and "client" |
| ︙ | ︙ | |||
148 149 150 151 152 153 154 | 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 | | | | | 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 | 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/home</pre> Then the URL that is really used to do the synchronization will be: <pre>https://fossil-scm.org/home/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 /home/xfer HTTP/1.0 Host: fossil-scm.hwaci.com:80 Content-Type: application/x-fossil Content-Length: 4216 </pre> <i><pre>content...</pre></i> |
| ︙ | ︙ | |||
248 249 250 251 252 253 254 | 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 | | | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | 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> |
| ︙ | ︙ | |||
271 272 273 274 275 276 277 | The first argument of a file 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 last argument of the file card is the number of bytes of payload that immediately follow the file card. If the file card has only two arguments, that means the payload is the complete content of the artifact. If the file card has three | | | | > > > > | 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 | The first argument of a file 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 last argument of the file card is the number of bytes of payload that immediately follow the file card. If the file card has only two arguments, that means the payload is the complete content of the artifact. If the file card has three arguments, then the payload is a [./delta_format.wiki|delta] and the second argument is 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. In this case, the containing response body is <em>not</em> compressed separately because the vast majority of the response is already compressed in cfile cards. In practice, version "3" is significantly faster than version "2". 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. |
| ︙ | ︙ | |||
409 410 411 412 413 414 415 | 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 | | | 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 | 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 1. 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> |
| ︙ | ︙ | |||
787 788 789 790 791 792 793 794 795 796 797 798 799 800 | <li><b>ci-unlock</b> <i>CLIENT-ID</i> A client sends the "ci-unlock" pragma to the server after 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. | > > > > > > > > | 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 | <li><b>ci-unlock</b> <i>CLIENT-ID</i> A client sends the "ci-unlock" pragma to the server after 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. <li><b>req-clusters</b> A client sends the "req-clusters" pragma to the server to ask the server to reply with "igot" cards for every [./fileformat.wiki#cluster|cluster artifact] that it holds. The client typically does this when it thinks that it might be attempting to pull a long chain of cluster artifacts. Sending the artifacts all at once can dramatically reduce the number of round trip messages needed to complete the synchronization. </ol> <h3 id="comment">3.12 Comment Cards</h3> Any card that begins with "#" (ASCII 0x23) is a comment card and is silently ignored. |
| ︙ | ︙ | |||
1028 1029 1030 1031 1032 1033 1034 |
card per line.
<li>Card formats are:
<ul>
<li> <b>login</b> <i>userid nonce signature</i>
<li> <b>push</b> <i>servercode projectcode</i>
<li> <b>pull</b> <i>servercode projectcode</i>
<li> <b>clone</b>
| | | 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 |
card per line.
<li>Card formats are:
<ul>
<li> <b>login</b> <i>userid nonce signature</i>
<li> <b>push</b> <i>servercode projectcode</i>
<li> <b>pull</b> <i>servercode projectcode</i>
<li> <b>clone</b>
<li> <b>clone</b> <i>protocol-version sequence-number</i>
<li> <b>file</b> <i>artifact-id size</i> <b>\n</b> <i>content</i>
<li> <b>file</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
<li> <b>cfile</b> <i>artifact-id size</i> <b>\n</b> <i>content</i>
<li> <b>cfile</b> <i>artifact-id delta-artifact-id size</i> <b>\n</b> <i>content</i>
<li> <b>uvfile</b> <i>name mtime hash size flags</i> <b>\n</b> <i>content</i>
<li> <b>private</b>
<li> <b>igot</b> <i>artifact-id</i> ?<i>flag</i>?
|
| ︙ | ︙ |
Changes to www/th1.md.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 | TH1 began as a minimalist re-implementation of the Tcl scripting language. There was a need to test the SQLite library on Symbian phones, but at that time all of the test cases for SQLite were written in Tcl and Tcl could not be easily compiled on the SymbianOS. So TH1 was developed as a cut-down version of Tcl that would facilitate running the SQLite test scripts on SymbianOS. | | < | > > > > > > > > > > > > > > > | | | | 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 |
TH1 began as a minimalist re-implementation of the Tcl scripting language.
There was a need to test the SQLite library on Symbian phones, but at that
time all of the test cases for SQLite were written in Tcl and Tcl could not
be easily compiled on the SymbianOS. So TH1 was developed as a cut-down
version of Tcl that would facilitate running the SQLite test scripts on
SymbianOS.
Fossil was first being designed at about the same time.
Early prototypes of Fossil were written in pure Tcl. But as the development
shifted toward the use of C-code, the need arose to have a Tcl-like
scripting language to help with code generation. TH1 was small and
light-weight and used minimal resources and seemed ideally suited for the
task.
The name "TH1" stands for "Test Harness 1",
since its original purpose was to serve as testing harness
for SQLite.
Where TH1 Is Used In Fossil
---------------------------
* In the header and footer for [skins](./customskin.md)
text within `<th1>...</th1>` is run as a TH1 script.
([example](/builtin/skins/default/header.txt))
* This display of [tickets](./bugtheory.wiki) is controlled by TH1
scripts, so that the ticket format can be customized for each
project. Administrators can visit the <b>/tktsetup</b> page in
their repositories to view and customize these scripts.
([example usage](./custom_ticket.wiki))
Overview Of The Tcl/TH1 Language
--------------------------------
TH1 is a string-processing language. All values are strings. Any numerical
operations are accomplished by converting from string to numeric, performing
the computation, then converting the result back into a string. (This might
seem inefficient, but it is faster than people imagine, and numeric
computations do not come up very often for the kinds of work that TH1 does,
so it has never been an issue.)
A TH1 script consists of a sequence of commands.
Each command is terminated by the first *unescaped* newline or ";" character.
The text of the command (excluding the newline or semicolon terminator)
is broken into space-separated tokens. The first token is the command
name and subsequent tokens are the arguments. In this sense, TH1 syntax
is similar to the familiar command-line shell syntax.
|
| ︙ | ︙ | |||
66 67 68 69 70 71 72 |
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"`.
The `if` command evaluates its first argument (the second token)
| | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
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"`.
The `if` command evaluates its first argument (the second token)
as an expression, and if that expression is true, it evaluates its
second argument (the third token) as a TH1 script.
If the expression is false and the third argument is `else`, then
the fourth argument is evaluated as a TH1 expression.
So, you see, even though the example above spans five lines, it is really
just a single command.
|
| ︙ | ︙ | |||
104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
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
----------------------------
The original Tcl language (after which TH1 is modeled) has a very rich
repertoire of commands. TH1, as it is designed to be minimalist and
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
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.
<a id="taint"></a>Tainted And Untainted Strings
-----------------------------------------------
Beginning with Fossil version 2.26 (circa 2025), TH1 distinguishes between
"tainted" and "untainted" strings. Tainted strings are strings that are
derived from user inputs that might contain text that is designed to subvert
the script. Untainted strings are known to come from secure sources and
are assumed to contain no malicious content.
Beginning with Fossil version 2.26, and depending on the value of the
[vuln-report setting](/help?cmd=vuln-report), TH1 will prevent tainted
strings from being used in ways that might lead to XSS or SQL-injection
attacks. This feature helps to ensure that XSS and SQL-injection
vulnerabilities are not *accidentally* added to Fossil when
custom TH1 scripts for headers or footers or tickets are added to a
repository. Note that the tainted/untainted distinction in strings does
not make it impossible to introduce XSS and SQL-injections vulnerabilities
using poorly-written TH1 scripts; it just makes it more difficult and
less likely to happen by accident. Developers must still consider the
security implications of TH1 customizations they add to Fossil, and take
appropriate precautions when writing custom TH1. Peer review of TH1
script changes is encouraged.
In Fossil version 2.26, if the vuln-report setting is set to "block"
or "fatal", the [html](#html) and [query](#query) TH1 commands will
fail with an error if their argument is a tainted string. This helps
to prevent XSS and SQL-injection attacks, respectively. Note that
the default value of the vuln-report setting is "log", which allows those
commands to continue working and only writes a warning message into the
error log. <b>Future versions of Fossil may change the default value
of the vuln-report setting to "block" or "fatal".</b> Fossil users
with customized TH1 scripts are encouraged to audit their customizations
and fix any potential vulnerabilities soon, so as to avoid breakage
caused by future upgrades. <b>Future versions of Fossil might also
place additional restrictions on the use of tainted strings.</b>
For example, it is likely that future versions of Fossil will disallow
using tainted strings as script, for example as the body of a "for"
loop or of a "proc".
Summary of Core TH1 Commands
----------------------------
The original Tcl language (after which TH1 is modeled) has a very rich
repertoire of commands. TH1, as it is designed to be minimalist and
|
| ︙ | ︙ | |||
145 146 147 148 149 150 151 152 153 154 155 156 157 158 | * string index STRING INDEX * string is CLASS STRING * string last NEEDLE HAYSTACK ?START-INDEX? * string match PATTERN STRING * string length STRING * string range STRING FIRST LAST * string repeat STRING COUNT * unset VARNAME * uplevel ?LEVEL? SCRIPT * upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR? All of the above commands work as in the original Tcl. Refer to the <a href="https://www.tcl-lang.org/man/tcl/contents.htm">Tcl documentation</a> for details. | > > > | 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | * string index STRING INDEX * string is CLASS STRING * string last NEEDLE HAYSTACK ?START-INDEX? * string match PATTERN STRING * string length STRING * string range STRING FIRST LAST * string repeat STRING COUNT * string trim STRING * string trimleft STRING * string trimright STRING * unset VARNAME * uplevel ?LEVEL? SCRIPT * upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR? All of the above commands work as in the original Tcl. Refer to the <a href="https://www.tcl-lang.org/man/tcl/contents.htm">Tcl documentation</a> for details. |
| ︙ | ︙ | |||
180 181 182 183 184 185 186 | * [checkout](#checkout) * [combobox](#combobox) * [copybtn](#copybtn) * [date](#date) * [decorate](#decorate) * [defHeader](#defHeader) * [dir](#dir) | < | 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | * [checkout](#checkout) * [combobox](#combobox) * [copybtn](#copybtn) * [date](#date) * [decorate](#decorate) * [defHeader](#defHeader) * [dir](#dir) * [enable\_output](#enable_output) * [encode64](#encode64) * [getParameter](#getParameter) * [glob\_match](#glob_match) * [globalState](#globalState) * [hascap](#hascap) * [hasfeature](#hasfeature) |
| ︙ | ︙ | |||
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 | * [setParameter](#setParameter) * [setting](#setting) * [stime](#stime) * [styleHeader](#styleHeader) * [styleFooter](#styleFooter) * [styleScript](#styleScript) * [submenu](#submenu) * [tclEval](#tclEval) * [tclExpr](#tclExpr) * [tclInvoke](#tclInvoke) * [tclIsSafe](#tclIsSafe) * [tclMakeSafe](#tclMakeSafe) * [tclReady](#tclReady) * [trace](#trace) * [unversioned content](#unversioned_content) * [unversioned list](#unversioned_list) * [utime](#utime) * [verifyCsrf](#verifyCsrf) * [verifyLogin](#verifyLogin) * [wiki](#wiki) Each of the commands above is documented by a block comment above their implementation in the th\_main.c or th\_tcl.c source files. All commands starting with "tcl", with the exception of "tclReady", require the Tcl integration subsystem be included at compile-time. Additionally, the "tcl" repository setting must be enabled at runtime | > > > | 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 | * [setParameter](#setParameter) * [setting](#setting) * [stime](#stime) * [styleHeader](#styleHeader) * [styleFooter](#styleFooter) * [styleScript](#styleScript) * [submenu](#submenu) * [taint](#taintCmd) * [tclEval](#tclEval) * [tclExpr](#tclExpr) * [tclInvoke](#tclInvoke) * [tclIsSafe](#tclIsSafe) * [tclMakeSafe](#tclMakeSafe) * [tclReady](#tclReady) * [trace](#trace) * [untaint](#untaintCmd) * [unversioned content](#unversioned_content) * [unversioned list](#unversioned_list) * [utime](#utime) * [verifyCsrf](#verifyCsrf) * [verifyLogin](#verifyLogin) * [wiki](#wiki) * [wiki_assoc](#wiki_assoc) Each of the commands above is documented by a block comment above their implementation in the th\_main.c or th\_tcl.c source files. All commands starting with "tcl", with the exception of "tclReady", require the Tcl integration subsystem be included at compile-time. Additionally, the "tcl" repository setting must be enabled at runtime |
| ︙ | ︙ | |||
269 270 271 272 273 274 275 | <a id="bireqjs"></a>TH1 builtin_request_js Command -------------------------------------------------- * builtin_request_js NAME | | | | 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 | <a id="bireqjs"></a>TH1 builtin_request_js Command -------------------------------------------------- * builtin_request_js NAME NAME must be the name of one of the [built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$). This command causes that javascript file to be appended to the delivered 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 are 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. |
| ︙ | ︙ | |||
410 411 412 413 414 415 416 | Returns a list containing all files in CHECKIN. If GLOB is given only the files matching the pattern GLOB within CHECKIN will be returned. If DETAILS is non-zero, the result will be a list-of-lists, with each element containing at least three elements: the file name, the file size (in bytes), and the file last modification time (relative to the time zone configured for the repository). | < < < < < < < < < < < < < < < < < < | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 | Returns a list containing all files in CHECKIN. If GLOB is given only the files matching the pattern GLOB within CHECKIN will be returned. If DETAILS is non-zero, the result will be a list-of-lists, with each element containing at least three elements: the file name, the file size (in bytes), and the file last modification time (relative to the time zone configured for the repository). <a id="enable_output"></a>TH1 enable\_output Command ------------------------------------------------------ * enable\_output BOOLEAN Enable or disable sending output when the combobox, copybtn, puts, or wiki |
| ︙ | ︙ | |||
524 525 526 527 528 529 530 | raise a script error. <a id="html"></a>TH1 html Command ----------------------------------- * html STRING | | > > > > > > > > > > > > > > | 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 | raise a script error. <a id="html"></a>TH1 html Command ----------------------------------- * html STRING Outputs the STRING literally. It is assumed that STRING contains valid HTML, or that if STRING contains any characters that are significant to HTML (such as `<`, `>`, `'`, or `&`) have already been escaped, perhaps by the [htmlize](#htmlize) command. Use the [puts](#puts) command to output text that might contain unescaped HTML markup. **Beware of XSS attacks!** If the STRING value to the html command can be controlled by a hostile user, then he might be able to sneak in malicious HTML or Javascript which could result in a cross-site scripting (XSS) attack. Be careful that all text that in STRING that might come from user input has been sanitized by the [htmlize](#htmlize) command or similar. In recent versions of Fossil, the STRING value must be [untainted](#taint) or else the "html" command will fail. <a id="htmlize"></a>TH1 htmlize Command ----------------------------------------- * htmlize STRING Escape all characters of STRING which have special meaning in HTML. |
| ︙ | ︙ | |||
592 593 594 595 596 597 598 | Returns the value of the cryptographic nonce for the request being processed. <a id="puts"></a>TH1 puts Command ----------------------------------- * puts STRING | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 |
Returns the value of the cryptographic nonce for the request being processed.
<a id="puts"></a>TH1 puts Command
-----------------------------------
* puts STRING
Outputs STRING. Characters within STRING that have special meaning
in HTML are escaped prior to being output. Thus is it safe for STRING
to be derived from user inputs. See also the [html](#html) command
which behaves similarly except does not escape HTML markup. This
command ("puts") is safe to use on [tainted strings](#taint), but the "html"
command is not.
<a id="query"></a>TH1 query Command
-------------------------------------
* query ?-nocomplain? SQL CODE
Runs the SQL query given by the SQL argument. For each row in the result
set, run CODE.
In SQL, parameters such as $var are filled in using the value of variable
"var". Result values are stored in variables with the column name prior
to each invocation of CODE. The names of the variables in which results
are stored can be controlled using "AS name" clauses in the SQL. As
the database will often contain content that originates from untrusted
users, all result values are marked as [tainted](#taint).
**Beware of SQL injections in the `query` command!**
The SQL argument to the query command should always be literal SQL
text enclosed in {...}. The SQL argument should never be a double-quoted
string or the value of a \$variable, as those constructs can lead to
an SQL Injection attack. If you need to include the values of one or
more TH1 variables as part of the SQL, then put \$variable inside the
{...}. The \$variable keyword will then get passed down into the SQLite
parser which knows to look up the value of \$variable in the TH1 symbol
table. For example:
~~~
query {SELECT res FROM tab1 WHERE key=$mykey} {...}
~~~
SQLite will see the \$mykey token in the SQL and will know to resolve it
to the value of the "mykey" TH1 variable, safely and without the possibility
of SQL injection. The following is unsafe:
~~~
query "SELECT res FROM tab1 WHERE key='$mykey'" {...} ;# <-- UNSAFE!
~~~
In this second example, TH1 does the expansion of `$mykey` prior to passing
the text down into SQLite. So if `$mykey` contains a single-quote character,
followed by additional hostile text, that will result in an SQL injection.
To help guard against SQL-injections, recent versions of Fossil require
that the SQL argument be [untainted](#taint) or else the "query" command
will fail.
<a id="randhex"></a>TH1 randhex Command
-----------------------------------------
* randhex N
Returns a string of N*2 random hexadecimal digits with N<50. If N is
|
| ︙ | ︙ | |||
635 636 637 638 639 640 641 642 643 644 645 646 647 648 | --------------------------------------- * regexp ?-nocase? ?--? exp string Checks the string against the specified regular expression and returns non-zero if it matches. If the regular expression is invalid or cannot be compiled, an error will be generated. <a id="reinitialize"></a>TH1 reinitialize Command --------------------------------------------------- * reinitialize ?FLAGS? Reinitializes the TH1 interpreter using the specified flags. | > > | 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 | --------------------------------------- * regexp ?-nocase? ?--? exp string Checks the string against the specified regular expression and returns non-zero if it matches. If the regular expression is invalid or cannot be compiled, an error will be generated. See [fossil grep](./grep.md) for details on the regexp syntax. <a id="reinitialize"></a>TH1 reinitialize Command --------------------------------------------------- * reinitialize ?FLAGS? Reinitializes the TH1 interpreter using the specified flags. |
| ︙ | ︙ | |||
738 739 740 741 742 743 744 745 746 747 748 749 750 751 | <a id="submenu"></a>TH1 submenu Command ----------------------------------------- * submenu link LABEL URL Add hyperlink to the submenu of the current page. <a id="tclEval"></a>TH1 tclEval Command ----------------------------------------- **This command requires the Tcl integration feature.** * tclEval arg ?arg ...? | > > > > > > > > > > > > > > | 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 | <a id="submenu"></a>TH1 submenu Command ----------------------------------------- * submenu link LABEL URL Add hyperlink to the submenu of the current page. <a id="taintCmd"></a>TH1 taint Command ----------------------------------------- * taint STRING This command returns a copy of STRING that has been marked as [tainted](#taint). Tainted strings are strings which might be controlled by an attacker and might contain hostile inputs and are thus unsafe to use in certain contexts. For example, tainted strings should not be output as part of a webpage as they might contain rogue HTML or Javascript that could lead to an XSS vulnerability. Similarly, tainted strings should not be run as SQL since they might contain an SQL-injection vulerability. <a id="tclEval"></a>TH1 tclEval Command ----------------------------------------- **This command requires the Tcl integration feature.** * tclEval arg ?arg ...? |
| ︙ | ︙ | |||
809 810 811 812 813 814 815 816 817 818 819 820 821 822 | <a id="trace"></a>TH1 trace Command ------------------------------------- * trace STRING Generates a TH1 trace message if TH1 tracing is enabled. <a id="unversioned_content"></a>TH1 unversioned content Command ----------------------------------------------------------------- * unversioned content FILENAME Attempts to locate the specified unversioned file and return its contents. An error is generated if the repository is not open or the unversioned file | > > > > > > > > > > > > | 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 | <a id="trace"></a>TH1 trace Command ------------------------------------- * trace STRING Generates a TH1 trace message if TH1 tracing is enabled. <a id="untaintCmd"></a>TH1 taint Command ----------------------------------------- * untaint STRING This command returns a copy of STRING that has been marked as [untainted](#taint). Untainted strings are strings which are believed to be free of potentially hostile content. Use this command with caution, as it overwrites the tainted-string protection mechanisms that are built into TH1. If you do not understand all the implications of executing this command, then do not use it. <a id="unversioned_content"></a>TH1 unversioned content Command ----------------------------------------------------------------- * unversioned content FILENAME Attempts to locate the specified unversioned file and return its contents. An error is generated if the repository is not open or the unversioned file |
| ︙ | ︙ | |||
859 860 861 862 863 864 865 866 867 868 869 870 871 872 | <a id="wiki"></a>TH1 wiki Command ----------------------------------- * wiki STRING Renders STRING as wiki content. Tcl Integration Commands ------------------------ When the Tcl integration subsystem is enabled, several commands are added to the Tcl interpreter. They are used to allow Tcl scripts access to the Fossil functionality provided via TH1. The following is a summary of the | > > > > > > > > > | 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 | <a id="wiki"></a>TH1 wiki Command ----------------------------------- * wiki STRING Renders STRING as wiki content. <a id="wiki_assoc"></a>TH1 wiki_assoc Command ----------------------------------- * wiki_assoc STRING STRING Renders the special wiki. The first string refers to the namespace (checkin, branch, tag, ticket). The second string specifies the concrete wiki page to be rendered. Tcl Integration Commands ------------------------ When the Tcl integration subsystem is enabled, several commands are added to the Tcl interpreter. They are used to allow Tcl scripts access to the Fossil functionality provided via TH1. The following is a summary of the |
| ︙ | ︙ |
Added www/title-test.md.
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | # Markdown Doc Title > & " ' < Test Test of unusual characters in the title of Markdown formatted documents. The title should read: > Markdown Doc Title > & " ' < Test See also: * [](/doc/trunk/www/title-test.wiki) * [](/wiki?name=Test+Wiki+>+%26+%22+%27+%3c+Title&p) * [](/forumpost/481ab1f9) |
Added www/title-test.wiki.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <title>Wiki Doc Title > & " ' < Test</title> Test of unusual characters in the title of Fossil-wiki formatted documents. The title should read: <big><b><verbatim> Wiki Doc Title > & " ' < Test </verbatim></b></big> See also: * [/doc/trunk/www/title-test.md] * [/wiki?name=Test+Wiki+>+%26+%22+%27+%3c+Title&p] * [/forumpost/481ab1f9] |