Fossil SCM
Merge latest trunk. This branch is more a "proof-of-concept" then a final implementation.
Commit
8841f6d48fa215ec18e0fe5ed00a31dfb6221285
Parent
59062c3d680bef6…
113 files changed
+26
+5
-14
+1
-1
+1
-1
+29
+82
+1
+43
+68
+3
+3
+1
+24
+89
+68
+23
+89
+1
+3
+3
+27
+1
+1
+18
+6
+1
+13
+105
+1
+38
+2
-2
+154
-38
+10
-3
+10
-3
+10
-2
+4
-1
+13
-5
+14
-11
+4
-4
+192
+102
-33
+1
-1
+9
+130
-126
+4
-4
+19
+69
-55
+25
-5
+4
-4
-19
+26
-5
+13
-8
+56
-46
+1
-1
+2
-4
+5
-5
+5
-5
+2
-1
+79
-15
+39
-2
+59
-59
+22
-9
+64
-53
+224
+5
-5
+6
-6
+32
-13
+3
-3
+1
-1
+1
-1
+9
-9
+89
-30
+35
-12
+621
-83
+62
-27
+7
-7
+300
-139
+1
+196
-1310
+34
-2
+759
-522
+31
-13
+6
+535
+92
-240
+5
-5
+3
-2
+1
-1
+2
-2
+13
-689
+1
-1
+17
-17
+12
-1
+5
-4
+10
+2
-2
+32
-8
+72
+2
-2
+1
-1
+12
-11
+1
-1
+11
-5
+42
-4
+42
-4
+38
-3
+2
-2
+1
-1
+75
-13
+2
-2
+5
-5
+2
-1
+3
-1
~
.dockerignore
~
Dockerfile
~
VERSION
~
auto.def
~
skins/README.md
~
skins/black_and_white/css.txt
~
skins/black_and_white/footer.txt
~
skins/black_and_white/header.txt
~
skins/default/css.txt
~
skins/default/footer.txt
~
skins/default/header.txt
~
skins/eagle/css.txt
~
skins/eagle/footer.txt
~
skins/eagle/header.txt
~
skins/enhanced1/css.txt
~
skins/enhanced1/footer.txt
~
skins/enhanced1/header.txt
~
skins/etienne1/README.md
~
skins/etienne1/css.txt
~
skins/etienne1/footer.txt
~
skins/etienne1/header.txt
~
skins/khaki/css.txt
~
skins/khaki/footer.txt
~
skins/khaki/header.txt
~
skins/plain_gray/css.txt
~
skins/plain_gray/footer.txt
~
skins/plain_gray/header.txt
~
skins/rounded1/css.txt
~
skins/rounded1/footer.txt
~
skins/rounded1/header.txt
~
src/attach.c
~
src/branch.c
~
src/browse.c
~
src/browse.c
~
src/builtin.c
~
src/cgi.c
~
src/checkin.c
~
src/clone.c
~
src/codecheck1.c
~
src/configure.c
~
src/db.c
~
src/deltacmd.c
~
src/diff.tcl
~
src/doc.c
~
src/export.c
~
src/file.c
~
src/finfo.c
~
src/foci.c
~
src/fusefs.c
~
src/glob.c
~
src/graph.c
~
src/http.c
~
src/http_socket.c
~
src/http_transport.c
~
src/info.c
~
src/json_branch.c
~
src/leaf.c
~
src/login.c
~
src/main.c
~
src/main.mk
~
src/makeheaders.c
~
src/makemake.tcl
~
src/manifest.c
~
src/markdown.md
~
src/md5.c
~
src/merge.c
~
src/mkbuiltin.c
~
src/mkindex.c
~
src/mkversion.c
~
src/publish.c
~
src/regexp.c
~
src/report.c
~
src/schema.c
~
src/search.c
~
src/setup.c
~
src/sha1.c
~
src/shell.c
~
src/sitemap.c
~
src/skins.c
~
src/sqlcmd.c
~
src/sqlite3.c
~
src/sqlite3.h
~
src/stat.c
~
src/statrep.c
~
src/style.c
~
src/sync.c
~
src/tag.c
~
src/th.h
~
src/th_main.c
~
src/timeline.c
~
src/translate.c
~
src/update.c
~
src/url.c
~
src/user.c
~
src/util.c
~
src/verify.c
~
src/wiki.c
~
src/wikiformat.c
~
src/xfer.c
~
test/diff-test-1.wiki
~
test/graph-test-1.wiki
~
win/Makefile.PellesCGMake
~
win/Makefile.dmc
~
win/Makefile.mingw
~
win/Makefile.mingw.mistachkin
~
win/Makefile.msc
~
win/include/unistd.h
~
www/build.wiki
~
www/changes.wiki
~
www/delta_format.wiki
~
www/fileformat.wiki
~
www/hints.wiki
~
www/qandc.wiki
+26
| --- a/.dockerignore | ||
| +++ b/.dockerignore | ||
| @@ -0,0 +1,26 @@ | ||
| 1 | +_FOSSIL_ | |
| 2 | +.fslckout | |
| 3 | +ajax | |
| 4 | +art | |
| 5 | +autosetup | |
| 6 | +bld | |
| 7 | +compat | |
| 8 | +debian | |
| 9 | +fossil | |
| 10 | +fossil.exe | |
| 11 | +setup | |
| 12 | +src | |
| 13 | +test | |
| 14 | +tools | |
| 15 | +win | |
| 16 | +wbld | |
| 17 | +win | |
| 18 | +www | |
| 19 | +*.a | |
| 20 | +*.lib | |
| 21 | +*.log | |
| 22 | +*.manifest | |
| 23 | +*.o | |
| 24 | +*.obj | |
| 25 | +*.pdb | |
| 26 | +*.res |
| --- a/.dockerignore | |
| +++ b/.dockerignore | |
| @@ -0,0 +1,26 @@ | |
| --- a/.dockerignore | |
| +++ b/.dockerignore | |
| @@ -0,0 +1,26 @@ | |
| 1 | _FOSSIL_ |
| 2 | .fslckout |
| 3 | ajax |
| 4 | art |
| 5 | autosetup |
| 6 | bld |
| 7 | compat |
| 8 | debian |
| 9 | fossil |
| 10 | fossil.exe |
| 11 | setup |
| 12 | src |
| 13 | test |
| 14 | tools |
| 15 | win |
| 16 | wbld |
| 17 | win |
| 18 | www |
| 19 | *.a |
| 20 | *.lib |
| 21 | *.log |
| 22 | *.manifest |
| 23 | *.o |
| 24 | *.obj |
| 25 | *.pdb |
| 26 | *.res |
+5
-14
| --- Dockerfile | ||
| +++ Dockerfile | ||
| @@ -2,35 +2,26 @@ | ||
| 2 | 2 | # Dockerfile for Fossil |
| 3 | 3 | ### |
| 4 | 4 | FROM fedora:21 |
| 5 | 5 | |
| 6 | 6 | ### Now install some additional parts we will need for the build |
| 7 | -# RUN yum update -y && yum clean all | |
| 8 | -RUN yum install -y gcc make zlib-devel openssl-devel tcl-devel && yum clean all | |
| 9 | -RUN groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil | |
| 7 | +RUN yum update -y && yum install -y gcc make zlib-devel openssl-devel tcl tar && yum clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil | |
| 10 | 8 | |
| 11 | 9 | ### If you want to build "release", change the next line accordingly. |
| 12 | 10 | ENV FOSSIL_INSTALL_VERSION trunk |
| 13 | 11 | |
| 14 | 12 | RUN curl "http://www.fossil-scm.org/index.html/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx |
| 15 | -RUN cd fossil-src && ./configure --lineedit=0 --json --with-tcl --with-tcl-stubs --with-tcl-private-stubs && make; | |
| 16 | -RUN cp fossil-src/fossil /usr/bin | |
| 17 | -RUN rm -rf fossil-src | |
| 18 | -RUN chmod a+rx /usr/bin/fossil | |
| 19 | -RUN mkdir -p /opt/fossil | |
| 20 | -RUN chown fossil:fossil /opt/fossil | |
| 13 | +RUN cd fossil-src && ./configure --disable-lineedit --disable-fusefs --json --with-th1-docs --with-th1-hooks --with-tcl --with-tcl-stubs --with-tcl-private-stubs && make; | |
| 14 | +RUN cp fossil-src/fossil /usr/bin && rm -rf fossil-src && chmod a+rx /usr/bin/fossil && mkdir -p /opt/fossil && chown fossil:fossil /opt/fossil | |
| 21 | 15 | |
| 22 | 16 | ### Build is done, remove modules no longer needed |
| 23 | -RUN yum remove -y gcc make zlib-devel openssl-devel tcl-devel && yum clean all | |
| 17 | +RUN yum remove -y gcc make zlib-devel openssl-devel tar && yum clean all | |
| 24 | 18 | |
| 25 | 19 | USER fossil |
| 26 | 20 | |
| 27 | 21 | ENV HOME /opt/fossil |
| 28 | 22 | |
| 29 | -RUN fossil new --docker -A admin /opt/fossil/repository.fossil | |
| 30 | -RUN fossil user password -R /opt/fossil/repository.fossil admin admin | |
| 31 | -RUN fossil cache init -R /opt/fossil/repository.fossil | |
| 23 | +RUN fossil new --docker -A admin /opt/fossil/repository.fossil && fossil user password -R /opt/fossil/repository.fossil admin admin && fossil cache init -R /opt/fossil/repository.fossil | |
| 32 | 24 | |
| 33 | 25 | EXPOSE 8080 |
| 34 | 26 | |
| 35 | 27 | CMD ["/usr/bin/fossil", "server", "/opt/fossil/repository.fossil"] |
| 36 | - | |
| 37 | 28 |
| --- Dockerfile | |
| +++ Dockerfile | |
| @@ -2,35 +2,26 @@ | |
| 2 | # Dockerfile for Fossil |
| 3 | ### |
| 4 | FROM fedora:21 |
| 5 | |
| 6 | ### Now install some additional parts we will need for the build |
| 7 | # RUN yum update -y && yum clean all |
| 8 | RUN yum install -y gcc make zlib-devel openssl-devel tcl-devel && yum clean all |
| 9 | RUN groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil |
| 10 | |
| 11 | ### If you want to build "release", change the next line accordingly. |
| 12 | ENV FOSSIL_INSTALL_VERSION trunk |
| 13 | |
| 14 | RUN curl "http://www.fossil-scm.org/index.html/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx |
| 15 | RUN cd fossil-src && ./configure --lineedit=0 --json --with-tcl --with-tcl-stubs --with-tcl-private-stubs && make; |
| 16 | RUN cp fossil-src/fossil /usr/bin |
| 17 | RUN rm -rf fossil-src |
| 18 | RUN chmod a+rx /usr/bin/fossil |
| 19 | RUN mkdir -p /opt/fossil |
| 20 | RUN chown fossil:fossil /opt/fossil |
| 21 | |
| 22 | ### Build is done, remove modules no longer needed |
| 23 | RUN yum remove -y gcc make zlib-devel openssl-devel tcl-devel && yum clean all |
| 24 | |
| 25 | USER fossil |
| 26 | |
| 27 | ENV HOME /opt/fossil |
| 28 | |
| 29 | RUN fossil new --docker -A admin /opt/fossil/repository.fossil |
| 30 | RUN fossil user password -R /opt/fossil/repository.fossil admin admin |
| 31 | RUN fossil cache init -R /opt/fossil/repository.fossil |
| 32 | |
| 33 | EXPOSE 8080 |
| 34 | |
| 35 | CMD ["/usr/bin/fossil", "server", "/opt/fossil/repository.fossil"] |
| 36 | |
| 37 |
| --- Dockerfile | |
| +++ Dockerfile | |
| @@ -2,35 +2,26 @@ | |
| 2 | # Dockerfile for Fossil |
| 3 | ### |
| 4 | FROM fedora:21 |
| 5 | |
| 6 | ### Now install some additional parts we will need for the build |
| 7 | RUN yum update -y && yum install -y gcc make zlib-devel openssl-devel tcl tar && yum clean all && groupadd -r fossil -g 433 && useradd -u 431 -r -g fossil -d /opt/fossil -s /sbin/nologin -c "Fossil user" fossil |
| 8 | |
| 9 | ### If you want to build "release", change the next line accordingly. |
| 10 | ENV FOSSIL_INSTALL_VERSION trunk |
| 11 | |
| 12 | RUN curl "http://www.fossil-scm.org/index.html/tarball/fossil-src.tar.gz?name=fossil-src&uuid=${FOSSIL_INSTALL_VERSION}" | tar zx |
| 13 | RUN cd fossil-src && ./configure --disable-lineedit --disable-fusefs --json --with-th1-docs --with-th1-hooks --with-tcl --with-tcl-stubs --with-tcl-private-stubs && make; |
| 14 | RUN cp fossil-src/fossil /usr/bin && rm -rf fossil-src && chmod a+rx /usr/bin/fossil && mkdir -p /opt/fossil && chown fossil:fossil /opt/fossil |
| 15 | |
| 16 | ### Build is done, remove modules no longer needed |
| 17 | RUN yum remove -y gcc make zlib-devel openssl-devel tar && yum clean all |
| 18 | |
| 19 | USER fossil |
| 20 | |
| 21 | ENV HOME /opt/fossil |
| 22 | |
| 23 | RUN fossil new --docker -A admin /opt/fossil/repository.fossil && fossil user password -R /opt/fossil/repository.fossil admin admin && fossil cache init -R /opt/fossil/repository.fossil |
| 24 | |
| 25 | EXPOSE 8080 |
| 26 | |
| 27 | CMD ["/usr/bin/fossil", "server", "/opt/fossil/repository.fossil"] |
| 28 |
M
VERSION
+1
-1
| --- VERSION | ||
| +++ VERSION | ||
| @@ -1,1 +1,1 @@ | ||
| 1 | -1.30 | |
| 1 | +1.31 | |
| 2 | 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 1.30 |
| 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 1.31 |
| 2 |
M
auto.def
+1
-1
| --- auto.def | ||
| +++ auto.def | ||
| @@ -68,11 +68,11 @@ | ||
| 68 | 68 | |
| 69 | 69 | find_internal_sqlite |
| 70 | 70 | } |
| 71 | 71 | |
| 72 | 72 | if {[string match *-solaris* [get-define host]]} { |
| 73 | - define-append EXTRA_CFLAGS -D_XOPEN_SOURCE=500 | |
| 73 | + define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__} | |
| 74 | 74 | } |
| 75 | 75 | |
| 76 | 76 | if {[opt-bool fossil-debug]} { |
| 77 | 77 | define-append EXTRA_CFLAGS -DFOSSIL_DEBUG |
| 78 | 78 | msg-result "Debugging support enabled" |
| 79 | 79 | |
| 80 | 80 | ADDED skins/README.md |
| 81 | 81 | ADDED skins/black_and_white/css.txt |
| 82 | 82 | ADDED skins/black_and_white/footer.txt |
| 83 | 83 | ADDED skins/black_and_white/header.txt |
| 84 | 84 | ADDED skins/default/css.txt |
| 85 | 85 | ADDED skins/default/footer.txt |
| 86 | 86 | ADDED skins/default/header.txt |
| 87 | 87 | ADDED skins/eagle/css.txt |
| 88 | 88 | ADDED skins/eagle/footer.txt |
| 89 | 89 | ADDED skins/eagle/header.txt |
| 90 | 90 | ADDED skins/enhanced1/css.txt |
| 91 | 91 | ADDED skins/enhanced1/footer.txt |
| 92 | 92 | ADDED skins/enhanced1/header.txt |
| 93 | 93 | ADDED skins/etienne1/README.md |
| 94 | 94 | ADDED skins/etienne1/css.txt |
| 95 | 95 | ADDED skins/etienne1/footer.txt |
| 96 | 96 | ADDED skins/etienne1/header.txt |
| 97 | 97 | ADDED skins/khaki/css.txt |
| 98 | 98 | ADDED skins/khaki/footer.txt |
| 99 | 99 | ADDED skins/khaki/header.txt |
| 100 | 100 | ADDED skins/plain_gray/css.txt |
| 101 | 101 | ADDED skins/plain_gray/footer.txt |
| 102 | 102 | ADDED skins/plain_gray/header.txt |
| 103 | 103 | ADDED skins/rounded1/css.txt |
| 104 | 104 | ADDED skins/rounded1/footer.txt |
| 105 | 105 | ADDED skins/rounded1/header.txt |
| --- auto.def | |
| +++ auto.def | |
| @@ -68,11 +68,11 @@ | |
| 68 | |
| 69 | find_internal_sqlite |
| 70 | } |
| 71 | |
| 72 | if {[string match *-solaris* [get-define host]]} { |
| 73 | define-append EXTRA_CFLAGS -D_XOPEN_SOURCE=500 |
| 74 | } |
| 75 | |
| 76 | if {[opt-bool fossil-debug]} { |
| 77 | define-append EXTRA_CFLAGS -DFOSSIL_DEBUG |
| 78 | msg-result "Debugging support enabled" |
| 79 | |
| 80 | DDED skins/README.md |
| 81 | DDED skins/black_and_white/css.txt |
| 82 | DDED skins/black_and_white/footer.txt |
| 83 | DDED skins/black_and_white/header.txt |
| 84 | DDED skins/default/css.txt |
| 85 | DDED skins/default/footer.txt |
| 86 | DDED skins/default/header.txt |
| 87 | DDED skins/eagle/css.txt |
| 88 | DDED skins/eagle/footer.txt |
| 89 | DDED skins/eagle/header.txt |
| 90 | DDED skins/enhanced1/css.txt |
| 91 | DDED skins/enhanced1/footer.txt |
| 92 | DDED skins/enhanced1/header.txt |
| 93 | DDED skins/etienne1/README.md |
| 94 | DDED skins/etienne1/css.txt |
| 95 | DDED skins/etienne1/footer.txt |
| 96 | DDED skins/etienne1/header.txt |
| 97 | DDED skins/khaki/css.txt |
| 98 | DDED skins/khaki/footer.txt |
| 99 | DDED skins/khaki/header.txt |
| 100 | DDED skins/plain_gray/css.txt |
| 101 | DDED skins/plain_gray/footer.txt |
| 102 | DDED skins/plain_gray/header.txt |
| 103 | DDED skins/rounded1/css.txt |
| 104 | DDED skins/rounded1/footer.txt |
| 105 | DDED skins/rounded1/header.txt |
| --- auto.def | |
| +++ auto.def | |
| @@ -68,11 +68,11 @@ | |
| 68 | |
| 69 | find_internal_sqlite |
| 70 | } |
| 71 | |
| 72 | if {[string match *-solaris* [get-define host]]} { |
| 73 | define-append EXTRA_CFLAGS {-D_XOPEN_SOURCE=500 -D__EXTENSIONS__} |
| 74 | } |
| 75 | |
| 76 | if {[opt-bool fossil-debug]} { |
| 77 | define-append EXTRA_CFLAGS -DFOSSIL_DEBUG |
| 78 | msg-result "Debugging support enabled" |
| 79 | |
| 80 | DDED skins/README.md |
| 81 | DDED skins/black_and_white/css.txt |
| 82 | DDED skins/black_and_white/footer.txt |
| 83 | DDED skins/black_and_white/header.txt |
| 84 | DDED skins/default/css.txt |
| 85 | DDED skins/default/footer.txt |
| 86 | DDED skins/default/header.txt |
| 87 | DDED skins/eagle/css.txt |
| 88 | DDED skins/eagle/footer.txt |
| 89 | DDED skins/eagle/header.txt |
| 90 | DDED skins/enhanced1/css.txt |
| 91 | DDED skins/enhanced1/footer.txt |
| 92 | DDED skins/enhanced1/header.txt |
| 93 | DDED skins/etienne1/README.md |
| 94 | DDED skins/etienne1/css.txt |
| 95 | DDED skins/etienne1/footer.txt |
| 96 | DDED skins/etienne1/header.txt |
| 97 | DDED skins/khaki/css.txt |
| 98 | DDED skins/khaki/footer.txt |
| 99 | DDED skins/khaki/header.txt |
| 100 | DDED skins/plain_gray/css.txt |
| 101 | DDED skins/plain_gray/footer.txt |
| 102 | DDED skins/plain_gray/header.txt |
| 103 | DDED skins/rounded1/css.txt |
| 104 | DDED skins/rounded1/footer.txt |
| 105 | DDED skins/rounded1/header.txt |
+29
| --- a/skins/README.md | ||
| +++ b/skins/README.md | ||
| @@ -0,0 +1,29 @@ | ||
| 1 | +Built-in Skins | |
| 2 | +============== | |
| 3 | + | |
| 4 | +Each subdirectory under this folder describes a three files in each subheader, | |
| 5 | +and the foothe Content Footer | |
| 6 | + | |
| 7 | +To improve an exieing built-in skin, simply edit the appropriate | |
| 8 | +files and recompile. | |
| 9 | + | |
| 10 | +To add a new skin: | |
| 11 | + | |
| 12 | + 1. Create a new subdirectory under skins/. (The new directory is | |
| 13 | + called "skins/newskin" below but you should use a new original | |
| 14 | + name, of course.) | |
| 15 | + | |
| 16 | + 2. Add files skins/newskin/css.txt, skins and skinheader.txt, | |
| 17 | + . Go to the src/ footer.txt.the src/ directory and rerun "tclsh makemake.tcl". This | |
| 18 | + step rebuilds the various makefiles so that they have dependencies | |
| 19 | + on the skin files you just installed. | |
| 20 | + | |
| 21 | + 4. Edit the BuiltinSkin[] array near the top of the src/skins.c source | |
| 22 | + file so that it describes and references the "newskin" skin. | |
| 23 | + | |
| 24 | + 5. Type "make" to rebuild. | |
| 25 | + | |
| 26 | +Development Hints | |
| 27 | +----------------- | |
| 28 | + | |
| 29 | +One way to develop a new skin is to copy th |
| --- a/skins/README.md | |
| +++ b/skins/README.md | |
| @@ -0,0 +1,29 @@ | |
| --- a/skins/README.md | |
| +++ b/skins/README.md | |
| @@ -0,0 +1,29 @@ | |
| 1 | Built-in Skins |
| 2 | ============== |
| 3 | |
| 4 | Each subdirectory under this folder describes a three files in each subheader, |
| 5 | and the foothe Content Footer |
| 6 | |
| 7 | To improve an exieing built-in skin, simply edit the appropriate |
| 8 | files and recompile. |
| 9 | |
| 10 | To add a new skin: |
| 11 | |
| 12 | 1. Create a new subdirectory under skins/. (The new directory is |
| 13 | called "skins/newskin" below but you should use a new original |
| 14 | name, of course.) |
| 15 | |
| 16 | 2. Add files skins/newskin/css.txt, skins and skinheader.txt, |
| 17 | . Go to the src/ footer.txt.the src/ directory and rerun "tclsh makemake.tcl". This |
| 18 | step rebuilds the various makefiles so that they have dependencies |
| 19 | on the skin files you just installed. |
| 20 | |
| 21 | 4. Edit the BuiltinSkin[] array near the top of the src/skins.c source |
| 22 | file so that it describes and references the "newskin" skin. |
| 23 | |
| 24 | 5. Type "make" to rebuild. |
| 25 | |
| 26 | Development Hints |
| 27 | ----------------- |
| 28 | |
| 29 | One way to develop a new skin is to copy th |
| --- a/skins/black_and_white/css.txt | ||
| +++ b/skins/black_and_white/css.txt | ||
| @@ -0,0 +1,82 @@ | ||
| 1 | +/* General settings for the entire page */ | |
| 2 | +body { | |
| 3 | + margin:0px 0px 0px 0px; | |
| 4 | + padding:0px; | |
| 5 | + font-family:verdana, arial, helvetica, "sans serif"; | |
| 6 | + color:#333; | |
| 7 | + background-color:white; | |
| 8 | +mx-text-size-adjust: none; | |
| 9 | +} | |
| 10 | + | |
| 11 | +/* consistent colours */ | |
| 12 | +h2 { | |
| 13 | + color: #333; | |
| 14 | +} | |
| 15 | +h3 { | |
| 16 | + color: #333; | |
| 17 | +} | |
| 18 | + | |
| 19 | +/* The project logo in the upper left-hand corner of each page */ | |
| 20 | +div.logo { | |
| 21 | + display: table-cell; | |
| 22 | + text-align: left; | |
| 23 | + vertical-align: bottom; | |
| 24 | + font-weight: bold; | |
| 25 | + color: #333; | |
| 26 | + white-space: nowrap; | |
| 27 | +} | |
| 28 | + | |
| 29 | +/* The page title centered at the top of each page */ | |
| 30 | +div.title { | |
| 31 | + -size: 2em; | |
| 32 | + font-weight: bold; | |
| 33 | + text-align: center; | |
| 34 | + color: #333; | |
| 35 | + vertical-align: bot The login status message in the top right-hand corner */ | |
| 36 | +div.status { | |
| 37 | + display: table-cell; | |
| 38 | + padding-right: 10px; | |
| 39 | + text-align: right; | |
| 40 | + vertical-align: bottom; | |
| 41 | + padding-bottom: 5px; | |
| 42 | + color: #333; | |
| 43 | + font-size: 0.8em; | |
| 44 | + font-weight: bold; | |
| 45 | + white-space: nowrap; | |
| 46 | +} | |
| 47 | + | |
| 48 | +/* The header across the top of the page */ | |
| 49 | +div.header { | |
| 50 | + margin:10px 0px 10px 0px; | |
| 51 | + padding:1px 0px 0px 20px; | |
| 52 | + border-style:solid; | |
| 53 | + border-color:black; | |
| 54 | + border-width:1px 0px; | |
| 55 | + background-color:#eee; | |
| 56 | +} | |
| 57 | + | |
| 58 | +/* The main menu bar that appears at the top left of the page beneath | |
| 59 | +** the header. Width must be co-ordinated wdiv.mainmenu { | |
| 60 | + float: left; | |
| 61 | + margin-left: 10px; | |
| 62 | + margin-right: vertical-align: bottpadding:5px; | |
| 63 | + background-color:#eee; | |
| 64 | + border:1px solid #999; | |
| 65 | + width:8em; | |
| 66 | +} | |
| 67 | + | |
| 68 | +/* Main menu is now a list */ | |
| 69 | +diin menu is now a list */div.mainmenu a, di:none; | |
| 70 | +} | |
| 71 | +nav.mainmenu a, nav.mainmenu a:visited{ | |
| 72 | + padding: 1px 10px 1px 10px; | |
| 73 | + color: #333; | |
| 74 | +div.mainmenu a:hover { | |
| 75 | + color: #eee; | |
| 76 | + background-color: #333; | |
| 77 | +} | |
| 78 | + | |
| 79 | +/* Container for the sub-menu and content so they don't spread | |
| 80 | +** out underneath the main menu */ | |
| 81 | +#container { | |
| 82 | + padding-leftdidiv.submenu a, didi/* General settings for the en |
| --- a/skins/black_and_white/css.txt | |
| +++ b/skins/black_and_white/css.txt | |
| @@ -0,0 +1,82 @@ | |
| --- a/skins/black_and_white/css.txt | |
| +++ b/skins/black_and_white/css.txt | |
| @@ -0,0 +1,82 @@ | |
| 1 | /* General settings for the entire page */ |
| 2 | body { |
| 3 | margin:0px 0px 0px 0px; |
| 4 | padding:0px; |
| 5 | font-family:verdana, arial, helvetica, "sans serif"; |
| 6 | color:#333; |
| 7 | background-color:white; |
| 8 | mx-text-size-adjust: none; |
| 9 | } |
| 10 | |
| 11 | /* consistent colours */ |
| 12 | h2 { |
| 13 | color: #333; |
| 14 | } |
| 15 | h3 { |
| 16 | color: #333; |
| 17 | } |
| 18 | |
| 19 | /* The project logo in the upper left-hand corner of each page */ |
| 20 | div.logo { |
| 21 | display: table-cell; |
| 22 | text-align: left; |
| 23 | vertical-align: bottom; |
| 24 | font-weight: bold; |
| 25 | color: #333; |
| 26 | white-space: nowrap; |
| 27 | } |
| 28 | |
| 29 | /* The page title centered at the top of each page */ |
| 30 | div.title { |
| 31 | -size: 2em; |
| 32 | font-weight: bold; |
| 33 | text-align: center; |
| 34 | color: #333; |
| 35 | vertical-align: bot The login status message in the top right-hand corner */ |
| 36 | div.status { |
| 37 | display: table-cell; |
| 38 | padding-right: 10px; |
| 39 | text-align: right; |
| 40 | vertical-align: bottom; |
| 41 | padding-bottom: 5px; |
| 42 | color: #333; |
| 43 | font-size: 0.8em; |
| 44 | font-weight: bold; |
| 45 | white-space: nowrap; |
| 46 | } |
| 47 | |
| 48 | /* The header across the top of the page */ |
| 49 | div.header { |
| 50 | margin:10px 0px 10px 0px; |
| 51 | padding:1px 0px 0px 20px; |
| 52 | border-style:solid; |
| 53 | border-color:black; |
| 54 | border-width:1px 0px; |
| 55 | background-color:#eee; |
| 56 | } |
| 57 | |
| 58 | /* The main menu bar that appears at the top left of the page beneath |
| 59 | ** the header. Width must be co-ordinated wdiv.mainmenu { |
| 60 | float: left; |
| 61 | margin-left: 10px; |
| 62 | margin-right: vertical-align: bottpadding:5px; |
| 63 | background-color:#eee; |
| 64 | border:1px solid #999; |
| 65 | width:8em; |
| 66 | } |
| 67 | |
| 68 | /* Main menu is now a list */ |
| 69 | diin menu is now a list */div.mainmenu a, di:none; |
| 70 | } |
| 71 | nav.mainmenu a, nav.mainmenu a:visited{ |
| 72 | padding: 1px 10px 1px 10px; |
| 73 | color: #333; |
| 74 | div.mainmenu a:hover { |
| 75 | color: #eee; |
| 76 | background-color: #333; |
| 77 | } |
| 78 | |
| 79 | /* Container for the sub-menu and content so they don't spread |
| 80 | ** out underneath the main menu */ |
| 81 | #container { |
| 82 | padding-leftdidiv.submenu a, didi/* General settings for the en |
| --- a/skins/black_and_white/footer.txt | ||
| +++ b/skins/black_and_white/footer.txt | ||
| @@ -0,0 +1 @@ | ||
| 1 | +< |
| --- a/skins/black_and_white/footer.txt | |
| +++ b/skins/black_and_white/footer.txt | |
| @@ -0,0 +1 @@ | |
| --- a/skins/black_and_white/footer.txt | |
| +++ b/skins/black_and_white/footer.txt | |
| @@ -0,0 +1 @@ | |
| 1 | < |
| --- a/skins/black_and_white/header.txt | ||
| +++ b/skins/black_and_white/header.txt | ||
| @@ -0,0 +1,43 @@ | ||
| 1 | +<html> | |
| 2 | +<head> | |
| 3 | +<base href="$baseu>$<project_name> | |
| 4 | + </div> | |
| 5 | + <div class="title">$<title></div> | |
| 6 | + <diiv class="status"><th1> | |
| 7 | + if {[i puts "Logged in as $login" | |
| 8 | + } else { | |
| 9 | + in as $login" | |
| 10 | + } else { | |
| 11 | + } | |
| 12 | + </th1></div> | |
| 13 | +</div> | |
| 14 | +<diiter class} $mainmenu { | |
| 15 | + html "<a href='$home$index_page'>Htimeline'>Timelintree?ci=tip'>Filbrlist'>Branches</a>\n"taglist'>Tags</a>\n" | |
| 16 | +} | |
| 17 | +if {[anycap 23456] || [anoncap 2] || [anoncap 3]forum'>Forum</a>\n"[anoncap r]ticket'>Tickewiki'>Wiki</a>\n" | |
| 18 | +} | |
| 19 | +if {[hascap s]etup'>Admin<etup_ulist'>Users</a>\n" | |
| 20 | +} | |
| 21 | +if {[i puts "Logged in as $lohtml "<a href='$home/login'>Login<: $<title></title> | |
| 22 | +<link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 23 | + href="$home/timeline.rss"> | |
| 24 | +<link rel="styleshasd> | |
| 25 | +<base href="$baseu>$<project_name> | |
| 26 | + </div> | |
| 27 | + <div class="title"<html> | |
| 28 | +<ss} $mainmenu { | |
| 29 | + html "<a href='$home$index_page'>Htimeline'>Timelintree?ci=tip'>Filbrlist'>Branches</a>\n"taglist'>Tags</a>\n" | |
| 30 | +} | |
| 31 | +if {[anycap 23456] || [anoncap 2] |reportlisoncap 3]forum'>Forum</a>\n"[anoncap r]ticket'>Tickewikiv class="logo"> | |
| 32 | + <img src="$logo_image_url" alt="logo"> | |
| 33 | + <br />$<projhascap ="RSS Feed" | |
| 34 | + href="$home/tss="status"><th1> | |
| 35 | + if {[ihastatus"><th1> | |
| 36 | + if {[i puts "Logged in as $login" | |
| 37 | + } else { | |
| 38 | + in as $login" | |
| 39 | + } else { | |
| 40 | + } | |
| 41 | + </th1></div> | |
| 42 | +</div> | |
| 43 | +<diiter clas |
| --- a/skins/black_and_white/header.txt | |
| +++ b/skins/black_and_white/header.txt | |
| @@ -0,0 +1,43 @@ | |
| --- a/skins/black_and_white/header.txt | |
| +++ b/skins/black_and_white/header.txt | |
| @@ -0,0 +1,43 @@ | |
| 1 | <html> |
| 2 | <head> |
| 3 | <base href="$baseu>$<project_name> |
| 4 | </div> |
| 5 | <div class="title">$<title></div> |
| 6 | <diiv class="status"><th1> |
| 7 | if {[i puts "Logged in as $login" |
| 8 | } else { |
| 9 | in as $login" |
| 10 | } else { |
| 11 | } |
| 12 | </th1></div> |
| 13 | </div> |
| 14 | <diiter class} $mainmenu { |
| 15 | html "<a href='$home$index_page'>Htimeline'>Timelintree?ci=tip'>Filbrlist'>Branches</a>\n"taglist'>Tags</a>\n" |
| 16 | } |
| 17 | if {[anycap 23456] || [anoncap 2] || [anoncap 3]forum'>Forum</a>\n"[anoncap r]ticket'>Tickewiki'>Wiki</a>\n" |
| 18 | } |
| 19 | if {[hascap s]etup'>Admin<etup_ulist'>Users</a>\n" |
| 20 | } |
| 21 | if {[i puts "Logged in as $lohtml "<a href='$home/login'>Login<: $<title></title> |
| 22 | <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 23 | href="$home/timeline.rss"> |
| 24 | <link rel="styleshasd> |
| 25 | <base href="$baseu>$<project_name> |
| 26 | </div> |
| 27 | <div class="title"<html> |
| 28 | <ss} $mainmenu { |
| 29 | html "<a href='$home$index_page'>Htimeline'>Timelintree?ci=tip'>Filbrlist'>Branches</a>\n"taglist'>Tags</a>\n" |
| 30 | } |
| 31 | if {[anycap 23456] || [anoncap 2] |reportlisoncap 3]forum'>Forum</a>\n"[anoncap r]ticket'>Tickewikiv class="logo"> |
| 32 | <img src="$logo_image_url" alt="logo"> |
| 33 | <br />$<projhascap ="RSS Feed" |
| 34 | href="$home/tss="status"><th1> |
| 35 | if {[ihastatus"><th1> |
| 36 | if {[i puts "Logged in as $login" |
| 37 | } else { |
| 38 | in as $login" |
| 39 | } else { |
| 40 | } |
| 41 | </th1></div> |
| 42 | </div> |
| 43 | <diiter clas |
+68
| --- a/skins/default/css.txt | ||
| +++ b/skins/default/css.txt | ||
| @@ -0,0 +1,68 @@ | ||
| 1 | +/* General settings for the entire page */ | |
| 2 | +body { | |
| 3 | + margin: 0ex 1ex; | |
| 4 | + padding: 0px; | |
| 5 | + background-color: white; | |
| 6 | + font-family: sans-serif; | |
| 7 | +mx-text-size-adjust: none; | |
| 8 | +} | |
| 9 | + | |
| 10 | +/* The project logo in the upper left-hand corner of each page */ | |
| 11 | +div.logo { | |
| 12 | + display: table-cell; | |
| 13 | + text-align: center; | |
| 14 | + vertical-align: bottom; | |
| 15 | + font-weight: bold; | |
| 16 | + co20or: #558195; | |
| 17 | + min-width: 50px; | |
| 18 | + white-space: nowrap; | |
| 19 | +} | |
| 20 | + | |
| 21 | +/* The page title centered at the top of each page */ | |
| 22 | +div.title { | |
| 23 | + display: table-cell; | |
| 24 | + font-size: 2em; | |
| 25 | + font-weight: bold; | |
| 26 | + text-align: center; | |
| 27 | + padding: 0 0 0 1em; | |
| 28 | + color: #558195; | |
| 29 | + vertical-align: bottom; | |
| 30 | + width: 100%; | |
| 31 | +} | |
| 32 | + | |
| 33 | +/* The login status message in the top right-hand corner */ | |
| 34 | +div.status { | |
| 35 | + display: table-cell; | |
| 36 | + text-align: right; | |
| 37 | + vertical-align: bottom; | |
| 38 | + color: #558195; | |
| 39 | + font-size: 0.8min-width: 20or: #558195e-space: nowrap; | |
| 40 | +} | |
| 41 | + | |
| 42 | +/* The header across the top of the page */ | |
| 43 | +div.header { | |
| 44 | + display: table; | |
| 45 | + width: 100%; | |
| 46 | +} | |
| 47 | + | |
| 48 | +/* The main menu bar that appears at the top of the page beneath | |
| 49 | +** the header */ | |
| 50 | +diisplay: table; | |
| 51 | + winmenu { | |
| 52 | + padding: 5px; | |
| 53 | + font-size: 0.9em; | |
| 54 | + font-weight: bold; | |
| 55 | + text-align: center; | |
| 56 | + letter-spacing: 1px; | |
| 57 | + background-color: #558195; | |
| 58 | + border-top-left-radius: 8px; | |
| 59 | + border-top-right-radius: 8px; | |
| 60 | + color: white; | |
| 61 | +} | |
| 62 | + | |
| 63 | +/* The submenu bar that *sometimes* appedis below the main menu */ | |
| 64 | +div.submenu, div.sectionmenu { | |
| 65 | + padding: 3px 10px 3px 0px; | |
| 66 | + font-size: 0.9em; | |
| 67 | + text-align: center; | |
| 68 | + background-color: div.mainmenu a, div |
| --- a/skins/default/css.txt | |
| +++ b/skins/default/css.txt | |
| @@ -0,0 +1,68 @@ | |
| --- a/skins/default/css.txt | |
| +++ b/skins/default/css.txt | |
| @@ -0,0 +1,68 @@ | |
| 1 | /* General settings for the entire page */ |
| 2 | body { |
| 3 | margin: 0ex 1ex; |
| 4 | padding: 0px; |
| 5 | background-color: white; |
| 6 | font-family: sans-serif; |
| 7 | mx-text-size-adjust: none; |
| 8 | } |
| 9 | |
| 10 | /* The project logo in the upper left-hand corner of each page */ |
| 11 | div.logo { |
| 12 | display: table-cell; |
| 13 | text-align: center; |
| 14 | vertical-align: bottom; |
| 15 | font-weight: bold; |
| 16 | co20or: #558195; |
| 17 | min-width: 50px; |
| 18 | white-space: nowrap; |
| 19 | } |
| 20 | |
| 21 | /* The page title centered at the top of each page */ |
| 22 | div.title { |
| 23 | display: table-cell; |
| 24 | font-size: 2em; |
| 25 | font-weight: bold; |
| 26 | text-align: center; |
| 27 | padding: 0 0 0 1em; |
| 28 | color: #558195; |
| 29 | vertical-align: bottom; |
| 30 | width: 100%; |
| 31 | } |
| 32 | |
| 33 | /* The login status message in the top right-hand corner */ |
| 34 | div.status { |
| 35 | display: table-cell; |
| 36 | text-align: right; |
| 37 | vertical-align: bottom; |
| 38 | color: #558195; |
| 39 | font-size: 0.8min-width: 20or: #558195e-space: nowrap; |
| 40 | } |
| 41 | |
| 42 | /* The header across the top of the page */ |
| 43 | div.header { |
| 44 | display: table; |
| 45 | width: 100%; |
| 46 | } |
| 47 | |
| 48 | /* The main menu bar that appears at the top of the page beneath |
| 49 | ** the header */ |
| 50 | diisplay: table; |
| 51 | winmenu { |
| 52 | padding: 5px; |
| 53 | font-size: 0.9em; |
| 54 | font-weight: bold; |
| 55 | text-align: center; |
| 56 | letter-spacing: 1px; |
| 57 | background-color: #558195; |
| 58 | border-top-left-radius: 8px; |
| 59 | border-top-right-radius: 8px; |
| 60 | color: white; |
| 61 | } |
| 62 | |
| 63 | /* The submenu bar that *sometimes* appedis below the main menu */ |
| 64 | div.submenu, div.sectionmenu { |
| 65 | padding: 3px 10px 3px 0px; |
| 66 | font-size: 0.9em; |
| 67 | text-align: center; |
| 68 | background-color: div.mainmenu a, div |
| --- a/skins/default/footer.txt | ||
| +++ b/skins/default/footer.txt | ||
| @@ -0,0 +1,3 @@ | ||
| 1 | +<div class="footer"> | |
| 2 | +This page was generated in about | |
| 3 | +<th1>puts [expr {([utime]+[stime]+1000)/100 |
| --- a/skins/default/footer.txt | |
| +++ b/skins/default/footer.txt | |
| @@ -0,0 +1,3 @@ | |
| --- a/skins/default/footer.txt | |
| +++ b/skins/default/footer.txt | |
| @@ -0,0 +1,3 @@ | |
| 1 | <div class="footer"> |
| 2 | This page was generated in about |
| 3 | <th1>puts [expr {([utime]+[stime]+1000)/100 |
| --- a/skins/default/header.txt | ||
| +++ b/skins/default/header.txt | ||
| @@ -0,0 +1,3 @@ | ||
| 1 | +reportlis� }her} Wiki wideonlhas { | |
| 2 | +="her} Wiki wideonly | |
| 3 | +} |
| --- a/skins/default/header.txt | |
| +++ b/skins/default/header.txt | |
| @@ -0,0 +1,3 @@ | |
| --- a/skins/default/header.txt | |
| +++ b/skins/default/header.txt | |
| @@ -0,0 +1,3 @@ | |
| 1 | reportlis� }her} Wiki wideonlhas { |
| 2 | ="her} Wiki wideonly |
| 3 | } |
+1
| --- a/skins/eagle/css.txt | ||
| +++ b/skins/eagle/css.txt | ||
| @@ -0,0 +1 @@ | ||
| 1 | +/#canvas#485D7B |
| --- a/skins/eagle/css.txt | |
| +++ b/skins/eagle/css.txt | |
| @@ -0,0 +1 @@ | |
| --- a/skins/eagle/css.txt | |
| +++ b/skins/eagle/css.txt | |
| @@ -0,0 +1 @@ | |
| 1 | /#canvas#485D7B |
+24
| --- a/skins/eagle/footer.txt | ||
| +++ b/skins/eagle/footer.txt | ||
| @@ -0,0 +1,24 @@ | ||
| 1 | +<div class="footer"> | |
| 2 | + <th1> | |
| 3 | + proc getTclVersion {} { | |
| 4 | + if {[catch {tclEval info patchlevel} tclVersion] == 0} { | |
| 5 | + return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" | |
| 6 | + } | |
| 7 | + return "" | |
| 8 | + } | |
| 9 | + proc getVersion { version } { | |
| 10 | + set length [string length $version] | |
| 11 | + return [string range $version 1 [expr {$length - 2}]] | |
| 12 | + } | |
| 13 | + set version [getVersion $manifest_version] | |
| 14 | + set tclVersion [getTclVersiowww.n] | |
| 15 | + set fossilUrl https://fossil-scm.org | |
| 16 | + set fossilDate [string range $manifest_date 0 9]T[string range $manifest_date 11 end] | |
| 17 | + </th1> | |
| 18 | + This page was generated in about | |
| 19 | + <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by | |
| 20 | + <a href="$fossilUrl/">Fossil</a> | |
| 21 | + version $release_version $tclVersion | |
| 22 | + <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> | |
| 23 | + <a href="$fossilUrl/index.html/timeline?c=$fossilDate&div> | |
| 24 | +</body></html> |
| --- a/skins/eagle/footer.txt | |
| +++ b/skins/eagle/footer.txt | |
| @@ -0,0 +1,24 @@ | |
| --- a/skins/eagle/footer.txt | |
| +++ b/skins/eagle/footer.txt | |
| @@ -0,0 +1,24 @@ | |
| 1 | <div class="footer"> |
| 2 | <th1> |
| 3 | proc getTclVersion {} { |
| 4 | if {[catch {tclEval info patchlevel} tclVersion] == 0} { |
| 5 | return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" |
| 6 | } |
| 7 | return "" |
| 8 | } |
| 9 | proc getVersion { version } { |
| 10 | set length [string length $version] |
| 11 | return [string range $version 1 [expr {$length - 2}]] |
| 12 | } |
| 13 | set version [getVersion $manifest_version] |
| 14 | set tclVersion [getTclVersiowww.n] |
| 15 | set fossilUrl https://fossil-scm.org |
| 16 | set fossilDate [string range $manifest_date 0 9]T[string range $manifest_date 11 end] |
| 17 | </th1> |
| 18 | This page was generated in about |
| 19 | <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by |
| 20 | <a href="$fossilUrl/">Fossil</a> |
| 21 | version $release_version $tclVersion |
| 22 | <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> |
| 23 | <a href="$fossilUrl/index.html/timeline?c=$fossilDate&div> |
| 24 | </body></html> |
+89
| --- a/skins/eagle/header.txt | ||
| +++ b/skins/eagle/header.txt | ||
| @@ -0,0 +1,89 @@ | ||
| 1 | +<html> | |
| 2 | +<head> | |
| 3 | +<base href="$baseurl/$current_page" /> | |
| 4 | +<title>$<project_name>: $<title></title> | |
| 5 | +<link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 6 | + href="$home/timeline.rss" /> | |
| 7 | +<link rel="stylesheet" href="$stylesheet_url" type="text/css" | |
| 8 | + media=<div class="header"> | |
| 9 | + <div class="logo"> | |
| 10 | + <th1> | |
| 11 | + ## | |
| 12 | + ## NOTE: The purpose of this procedure is to take the base URL of the | |
| 13 | + ## Fossil project and return the root of the entire web site using | |
| 14 | + ## the same URI scheme as the base URL (e.g. http or https). | |
| 15 | + ## | |
| 16 | + proc getLogoUrl { baseurl } { | |
| 17 | + set idx(first) [string first // $baseurl] | |
| 18 | + if {$idx(first) != -1} { | |
| 19 | + ## | |
| 20 | + ## NOTE: Skip second slash. | |
| 21 | + ## | |
| 22 | + set idx(first+1) [expr {$idx(first) + 2}] | |
| 23 | + ## | |
| 24 | + ## NOTE: (part 1) The [string first] command does NOT actually | |
| 25 | + ## support the optional startIndex argument as specified | |
| 26 | + ## in the TH1 support manual; therefore, we fake it by | |
| 27 | + ## using the [string range] command and then adding the | |
| 28 | + project_nameadding the | |
| 29 | + ## necessary offset to the result<th1> | |
| 30 | +low). In Tc, we could use the following ins} elscript | |
| 31 | + </th1> | |
| 32 | + ## | |
| 33 | + set idx(nextRange) [string range $baseurl $idx(first+1) funcdivif {$iif(e){ | |
| 34 | + ## NOTE: (part 2) Add thefunction f(n) { | |
| 35 | +Add the nece} | |
| 36 | +for the next slash (i.e the itial searnext) [exp}] | |
| 37 | + ## | |
| 38 | + ] | |
| 39 | + ## | |
| 40 | + ## } | |
| 41 | +} | |
| 42 | +next slash. | |
| 43 | + ><th1> | |
| 44 | +set sitemap 0 | |
| 45 | +scheme and host fthe base URL. | |
| 46 | + ## | |
| 47 | + "updateClock();"$idx(first)] | |
| 48 | + sest [string range $baseurl $idx> | |
| 49 | + <th1> | |
| 50 | + ## | |
| 51 | + ## NOT | |
| 52 | +<th1> | |
| 53 | +proc menulink {url name} { | |
| 54 | +name</a>\n" | |
| 55 | + } | |
| 56 | +} | |
| 57 | +menindex_page'>Home</a>\n" | |
| 58 | +name</a>\n" | |
| 59 | + } | |
| 60 | +} | |
| 61 | +me/help'>Help<>$name</a>\n" | |
| 62 | + } | |
| 63 | +} | |
| 64 | +me/timeline'>Timeline</a>$name</a>\n" | |
| 65 | + } | |
| 66 | +} | |
| 67 | +me/tree?ci=tip'>Filhas>$name</a>\n" | |
| 68 | + } | |
| 69 | +} | |
| 70 | +me/brlist'>Branches</a>\n" | |
| 71 | +>$name</a>\n" | |
| 72 | + } | |
| 73 | +} | |
| 74 | +hasaglist'>Tags</>$name</a>\n" | |
| 75 | + } | |
| 76 | +} | |
| 77 | +me/ticket'>Tickets</>$name</a>\n" | |
| 78 | + } | |
| 79 | +} | |
| 80 | +me/wikihashas name</a>\n" | |
| 81 | + } | |
| 82 | +} | |
| 83 | +me/setup_ulist'>Users</a>\n"<html>reportlisd> | |
| 84 | +<base href="$baseuse href="$baseurl/$c/login'>Logout</a>\n" | |
| 85 | +} else { | |
| 86 | +>$name</a>\n" | |
| 87 | + } | |
| 88 | +} | |
| 89 | +me |
| --- a/skins/eagle/header.txt | |
| +++ b/skins/eagle/header.txt | |
| @@ -0,0 +1,89 @@ | |
| --- a/skins/eagle/header.txt | |
| +++ b/skins/eagle/header.txt | |
| @@ -0,0 +1,89 @@ | |
| 1 | <html> |
| 2 | <head> |
| 3 | <base href="$baseurl/$current_page" /> |
| 4 | <title>$<project_name>: $<title></title> |
| 5 | <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 6 | href="$home/timeline.rss" /> |
| 7 | <link rel="stylesheet" href="$stylesheet_url" type="text/css" |
| 8 | media=<div class="header"> |
| 9 | <div class="logo"> |
| 10 | <th1> |
| 11 | ## |
| 12 | ## NOTE: The purpose of this procedure is to take the base URL of the |
| 13 | ## Fossil project and return the root of the entire web site using |
| 14 | ## the same URI scheme as the base URL (e.g. http or https). |
| 15 | ## |
| 16 | proc getLogoUrl { baseurl } { |
| 17 | set idx(first) [string first // $baseurl] |
| 18 | if {$idx(first) != -1} { |
| 19 | ## |
| 20 | ## NOTE: Skip second slash. |
| 21 | ## |
| 22 | set idx(first+1) [expr {$idx(first) + 2}] |
| 23 | ## |
| 24 | ## NOTE: (part 1) The [string first] command does NOT actually |
| 25 | ## support the optional startIndex argument as specified |
| 26 | ## in the TH1 support manual; therefore, we fake it by |
| 27 | ## using the [string range] command and then adding the |
| 28 | project_nameadding the |
| 29 | ## necessary offset to the result<th1> |
| 30 | low). In Tc, we could use the following ins} elscript |
| 31 | </th1> |
| 32 | ## |
| 33 | set idx(nextRange) [string range $baseurl $idx(first+1) funcdivif {$iif(e){ |
| 34 | ## NOTE: (part 2) Add thefunction f(n) { |
| 35 | Add the nece} |
| 36 | for the next slash (i.e the itial searnext) [exp}] |
| 37 | ## |
| 38 | ] |
| 39 | ## |
| 40 | ## } |
| 41 | } |
| 42 | next slash. |
| 43 | ><th1> |
| 44 | set sitemap 0 |
| 45 | scheme and host fthe base URL. |
| 46 | ## |
| 47 | "updateClock();"$idx(first)] |
| 48 | sest [string range $baseurl $idx> |
| 49 | <th1> |
| 50 | ## |
| 51 | ## NOT |
| 52 | <th1> |
| 53 | proc menulink {url name} { |
| 54 | name</a>\n" |
| 55 | } |
| 56 | } |
| 57 | menindex_page'>Home</a>\n" |
| 58 | name</a>\n" |
| 59 | } |
| 60 | } |
| 61 | me/help'>Help<>$name</a>\n" |
| 62 | } |
| 63 | } |
| 64 | me/timeline'>Timeline</a>$name</a>\n" |
| 65 | } |
| 66 | } |
| 67 | me/tree?ci=tip'>Filhas>$name</a>\n" |
| 68 | } |
| 69 | } |
| 70 | me/brlist'>Branches</a>\n" |
| 71 | >$name</a>\n" |
| 72 | } |
| 73 | } |
| 74 | hasaglist'>Tags</>$name</a>\n" |
| 75 | } |
| 76 | } |
| 77 | me/ticket'>Tickets</>$name</a>\n" |
| 78 | } |
| 79 | } |
| 80 | me/wikihashas name</a>\n" |
| 81 | } |
| 82 | } |
| 83 | me/setup_ulist'>Users</a>\n"<html>reportlisd> |
| 84 | <base href="$baseuse href="$baseurl/$c/login'>Logout</a>\n" |
| 85 | } else { |
| 86 | >$name</a>\n" |
| 87 | } |
| 88 | } |
| 89 | me |
+68
| --- a/skins/enhanced1/css.txt | ||
| +++ b/skins/enhanced1/css.txt | ||
| @@ -0,0 +1,68 @@ | ||
| 1 | +/* General settings for the entire page */ | |
| 2 | +body { | |
| 3 | + margin: 0ex 1ex; | |
| 4 | + padding: 0px; | |
| 5 | + background-color: white; | |
| 6 | + font-family: sans-serif; | |
| 7 | +mx-text-size-adjust: none; | |
| 8 | +} | |
| 9 | + | |
| 10 | +/* The project logo in the upper left-hand corner of each page */ | |
| 11 | +div.logo { | |
| 12 | + display: table-cell; | |
| 13 | + text-align: center; | |
| 14 | + vertical-align: bottom; | |
| 15 | + font-weight: bold; | |
| 16 | + co20or: #558195; | |
| 17 | + min-width: 50px; | |
| 18 | + white-space: nowrap; | |
| 19 | +} | |
| 20 | + | |
| 21 | +/* The page title centered at the top of each page */ | |
| 22 | +div.title { | |
| 23 | + display: table-cell; | |
| 24 | + font-size: 2em; | |
| 25 | + font-weight: bold; | |
| 26 | + text-align: center; | |
| 27 | + padding: 0 0 0 1em; | |
| 28 | + color: #558195; | |
| 29 | + vertical-align: bottom; | |
| 30 | + width: 100%; | |
| 31 | +} | |
| 32 | + | |
| 33 | +/* The login status message in the top right-hand corner */ | |
| 34 | +div.status { | |
| 35 | + display: table-cell; | |
| 36 | + text-align: right; | |
| 37 | + vertical-align: bottom; | |
| 38 | + color: #558195; | |
| 39 | + font-size: 0.8min-width: 20or: #558195e-space: nowrap; | |
| 40 | +} | |
| 41 | + | |
| 42 | +/* The header across the top of the page */ | |
| 43 | +div.header { | |
| 44 | + display: table; | |
| 45 | + width: 100%; | |
| 46 | +} | |
| 47 | + | |
| 48 | +/* The main menu bar that appears at the top of the page beneath | |
| 49 | +** the header */ | |
| 50 | +diisplay: table; | |
| 51 | + winmenu { | |
| 52 | + padding: 5px; | |
| 53 | + font-size: 0.9em; | |
| 54 | + font-weight: bold; | |
| 55 | + text-align: center; | |
| 56 | + letter-spacing: 1px; | |
| 57 | + background-color: #558195; | |
| 58 | + border-top-left-radius: 8px; | |
| 59 | + border-top-right-radius: 8px; | |
| 60 | + color: white; | |
| 61 | +} | |
| 62 | + | |
| 63 | +/* The submenu bar that *sometimes* appedis below the main menu */ | |
| 64 | +div.submenu, div.sectionmenu { | |
| 65 | + padding: 3px 10px 3px 0px; | |
| 66 | + font-size: 0.9em; | |
| 67 | + text-align: center; | |
| 68 | + background-color: div.mainmenu a, div |
| --- a/skins/enhanced1/css.txt | |
| +++ b/skins/enhanced1/css.txt | |
| @@ -0,0 +1,68 @@ | |
| --- a/skins/enhanced1/css.txt | |
| +++ b/skins/enhanced1/css.txt | |
| @@ -0,0 +1,68 @@ | |
| 1 | /* General settings for the entire page */ |
| 2 | body { |
| 3 | margin: 0ex 1ex; |
| 4 | padding: 0px; |
| 5 | background-color: white; |
| 6 | font-family: sans-serif; |
| 7 | mx-text-size-adjust: none; |
| 8 | } |
| 9 | |
| 10 | /* The project logo in the upper left-hand corner of each page */ |
| 11 | div.logo { |
| 12 | display: table-cell; |
| 13 | text-align: center; |
| 14 | vertical-align: bottom; |
| 15 | font-weight: bold; |
| 16 | co20or: #558195; |
| 17 | min-width: 50px; |
| 18 | white-space: nowrap; |
| 19 | } |
| 20 | |
| 21 | /* The page title centered at the top of each page */ |
| 22 | div.title { |
| 23 | display: table-cell; |
| 24 | font-size: 2em; |
| 25 | font-weight: bold; |
| 26 | text-align: center; |
| 27 | padding: 0 0 0 1em; |
| 28 | color: #558195; |
| 29 | vertical-align: bottom; |
| 30 | width: 100%; |
| 31 | } |
| 32 | |
| 33 | /* The login status message in the top right-hand corner */ |
| 34 | div.status { |
| 35 | display: table-cell; |
| 36 | text-align: right; |
| 37 | vertical-align: bottom; |
| 38 | color: #558195; |
| 39 | font-size: 0.8min-width: 20or: #558195e-space: nowrap; |
| 40 | } |
| 41 | |
| 42 | /* The header across the top of the page */ |
| 43 | div.header { |
| 44 | display: table; |
| 45 | width: 100%; |
| 46 | } |
| 47 | |
| 48 | /* The main menu bar that appears at the top of the page beneath |
| 49 | ** the header */ |
| 50 | diisplay: table; |
| 51 | winmenu { |
| 52 | padding: 5px; |
| 53 | font-size: 0.9em; |
| 54 | font-weight: bold; |
| 55 | text-align: center; |
| 56 | letter-spacing: 1px; |
| 57 | background-color: #558195; |
| 58 | border-top-left-radius: 8px; |
| 59 | border-top-right-radius: 8px; |
| 60 | color: white; |
| 61 | } |
| 62 | |
| 63 | /* The submenu bar that *sometimes* appedis below the main menu */ |
| 64 | div.submenu, div.sectionmenu { |
| 65 | padding: 3px 10px 3px 0px; |
| 66 | font-size: 0.9em; |
| 67 | text-align: center; |
| 68 | background-color: div.mainmenu a, div |
| --- a/skins/enhanced1/footer.txt | ||
| +++ b/skins/enhanced1/footer.txt | ||
| @@ -0,0 +1,23 @@ | ||
| 1 | +<div class="footer"> | |
| 2 | + <th1> | |
| 3 | + proc getTclVersion {} { | |
| 4 | + if {[catch {tclEval info patchlevel} tclVersion] == 0} { | |
| 5 | + return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" | |
| 6 | + } | |
| 7 | + return "" | |
| 8 | + } | |
| 9 | + proc getVersion { version } { | |
| 10 | + set length [string length $version] | |
| 11 | + return [string range $version 1 [expr {$length - 2}]] | |
| 12 | + } | |
| 13 | + set version [getVersion $manifest_version] | |
| 14 | + set tclVersion [getTclVersiowww.n] | |
| 15 | + set fossilUrl https://fossil-scm.org | |
| 16 | + e $manifest_date 11 end] | |
| 17 | + </th1> | |
| 18 | + This page was generated in about | |
| 19 | + <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by | |
| 20 | + <a href="$fossilUrl/">Fossil</a> | |
| 21 | + version $release_version $tclVersion | |
| 22 | + <a href="$fossilUrl/index.html/info/$version">$manifest_vermanifest_date&div> | |
| 23 | +</body></html> |
| --- a/skins/enhanced1/footer.txt | |
| +++ b/skins/enhanced1/footer.txt | |
| @@ -0,0 +1,23 @@ | |
| --- a/skins/enhanced1/footer.txt | |
| +++ b/skins/enhanced1/footer.txt | |
| @@ -0,0 +1,23 @@ | |
| 1 | <div class="footer"> |
| 2 | <th1> |
| 3 | proc getTclVersion {} { |
| 4 | if {[catch {tclEval info patchlevel} tclVersion] == 0} { |
| 5 | return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" |
| 6 | } |
| 7 | return "" |
| 8 | } |
| 9 | proc getVersion { version } { |
| 10 | set length [string length $version] |
| 11 | return [string range $version 1 [expr {$length - 2}]] |
| 12 | } |
| 13 | set version [getVersion $manifest_version] |
| 14 | set tclVersion [getTclVersiowww.n] |
| 15 | set fossilUrl https://fossil-scm.org |
| 16 | e $manifest_date 11 end] |
| 17 | </th1> |
| 18 | This page was generated in about |
| 19 | <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by |
| 20 | <a href="$fossilUrl/">Fossil</a> |
| 21 | version $release_version $tclVersion |
| 22 | <a href="$fossilUrl/index.html/info/$version">$manifest_vermanifest_date&div> |
| 23 | </body></html> |
| --- a/skins/enhanced1/header.txt | ||
| +++ b/skins/enhanced1/header.txt | ||
| @@ -0,0 +1,89 @@ | ||
| 1 | +<html> | |
| 2 | +<head> | |
| 3 | +<base href="$baseurl/$current_page" /> | |
| 4 | +<title>$<project_name>: $<title></title> | |
| 5 | +<link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 6 | + href="$home/timeline.rss" /> | |
| 7 | +<link rel="stylesheet" href="$stylesheet_url" type="text/css" | |
| 8 | + media=<div class="header"> | |
| 9 | + <div class="logo"> | |
| 10 | + <th1> | |
| 11 | + ## | |
| 12 | + ## NOTE: The purpose of this procedure is to take the base URL of the | |
| 13 | + ## Fossil project and return the root of the entire web site using | |
| 14 | + ## the same URI scheme as the base URL (e.g. http or https). | |
| 15 | + ## | |
| 16 | + proc getLogoUrl { baseurl } { | |
| 17 | + set idx(first) [string first // $baseurl] | |
| 18 | + if {$idx(first) != -1} { | |
| 19 | + ## | |
| 20 | + ## NOTE: Skip second slash. | |
| 21 | + ## | |
| 22 | + set idx(first+1) [expr {$idx(first) + 2}] | |
| 23 | + ## | |
| 24 | + ## NOTE: (part 1) The [string first] command does NOT actually | |
| 25 | + ## support the optional startIndex argument as specified | |
| 26 | + ## in the TH1 support manual; therefore, we fake it by | |
| 27 | + ## using the [stange] command and then adding the | |
| 28 | + project_nameadding the | |
| 29 | + ## necessary offset to the result<th1> | |
| 30 | +low). In Tc, we could use the following ins} elscript | |
| 31 | + </th1> | |
| 32 | + ## | |
| 33 | + set idx(nextRange) [string range $baseurl $idx(first+1) funcdivif {$iif(e){ | |
| 34 | + ## NOTE: (part 2) Add thefunction f(n) { | |
| 35 | +Add the nece} | |
| 36 | +for the next slash (i.e the itial searnext) [exp}] | |
| 37 | + ## | |
| 38 | + ] | |
| 39 | + ## | |
| 40 | + ## } | |
| 41 | +} | |
| 42 | +next slash. | |
| 43 | + ><th1> | |
| 44 | +set sitemap 0 | |
| 45 | +scheme and host fthe base URL. | |
| 46 | + ## | |
| 47 | + "updateClock();"$idx(first)] | |
| 48 | + sest [string range $baseurl $idx> | |
| 49 | + <th1> | |
| 50 | + ## | |
| 51 | + ## NOT | |
| 52 | +<th1> | |
| 53 | +proc menulink {url name} { | |
| 54 | +name</a>\n" | |
| 55 | + } | |
| 56 | +} | |
| 57 | +menindex_page'>Home</a>\n" | |
| 58 | +name</a>\n" | |
| 59 | + } | |
| 60 | +} | |
| 61 | +me/help'>Help<>$name</a>\n" | |
| 62 | + } | |
| 63 | +} | |
| 64 | +me/timeline'>Timeline</a>$name</a>\n" | |
| 65 | + } | |
| 66 | +} | |
| 67 | +me/tree?ci=tip'>Filhas>$name</a>\n" | |
| 68 | + } | |
| 69 | +} | |
| 70 | +me/brlist'>Branches</a>\n" | |
| 71 | +>$name</a>\n" | |
| 72 | + } | |
| 73 | +} | |
| 74 | +hasaglist'>Tags</>$name</a>\n" | |
| 75 | + } | |
| 76 | +} | |
| 77 | +me/ticket'>Tickets</>$name</a>\n" | |
| 78 | + } | |
| 79 | +} | |
| 80 | +me/wikihashas name</a>\n" | |
| 81 | + } | |
| 82 | +} | |
| 83 | +me/setup_ulist'>Users</a>\n"<html>reportlisd> | |
| 84 | +<base href="$baseuse href="$baseurl/$c/login'>Logout</a>\n" | |
| 85 | +} else { | |
| 86 | +>$name</a>\n" | |
| 87 | + } | |
| 88 | +} | |
| 89 | +me |
| --- a/skins/enhanced1/header.txt | |
| +++ b/skins/enhanced1/header.txt | |
| @@ -0,0 +1,89 @@ | |
| --- a/skins/enhanced1/header.txt | |
| +++ b/skins/enhanced1/header.txt | |
| @@ -0,0 +1,89 @@ | |
| 1 | <html> |
| 2 | <head> |
| 3 | <base href="$baseurl/$current_page" /> |
| 4 | <title>$<project_name>: $<title></title> |
| 5 | <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 6 | href="$home/timeline.rss" /> |
| 7 | <link rel="stylesheet" href="$stylesheet_url" type="text/css" |
| 8 | media=<div class="header"> |
| 9 | <div class="logo"> |
| 10 | <th1> |
| 11 | ## |
| 12 | ## NOTE: The purpose of this procedure is to take the base URL of the |
| 13 | ## Fossil project and return the root of the entire web site using |
| 14 | ## the same URI scheme as the base URL (e.g. http or https). |
| 15 | ## |
| 16 | proc getLogoUrl { baseurl } { |
| 17 | set idx(first) [string first // $baseurl] |
| 18 | if {$idx(first) != -1} { |
| 19 | ## |
| 20 | ## NOTE: Skip second slash. |
| 21 | ## |
| 22 | set idx(first+1) [expr {$idx(first) + 2}] |
| 23 | ## |
| 24 | ## NOTE: (part 1) The [string first] command does NOT actually |
| 25 | ## support the optional startIndex argument as specified |
| 26 | ## in the TH1 support manual; therefore, we fake it by |
| 27 | ## using the [stange] command and then adding the |
| 28 | project_nameadding the |
| 29 | ## necessary offset to the result<th1> |
| 30 | low). In Tc, we could use the following ins} elscript |
| 31 | </th1> |
| 32 | ## |
| 33 | set idx(nextRange) [string range $baseurl $idx(first+1) funcdivif {$iif(e){ |
| 34 | ## NOTE: (part 2) Add thefunction f(n) { |
| 35 | Add the nece} |
| 36 | for the next slash (i.e the itial searnext) [exp}] |
| 37 | ## |
| 38 | ] |
| 39 | ## |
| 40 | ## } |
| 41 | } |
| 42 | next slash. |
| 43 | ><th1> |
| 44 | set sitemap 0 |
| 45 | scheme and host fthe base URL. |
| 46 | ## |
| 47 | "updateClock();"$idx(first)] |
| 48 | sest [string range $baseurl $idx> |
| 49 | <th1> |
| 50 | ## |
| 51 | ## NOT |
| 52 | <th1> |
| 53 | proc menulink {url name} { |
| 54 | name</a>\n" |
| 55 | } |
| 56 | } |
| 57 | menindex_page'>Home</a>\n" |
| 58 | name</a>\n" |
| 59 | } |
| 60 | } |
| 61 | me/help'>Help<>$name</a>\n" |
| 62 | } |
| 63 | } |
| 64 | me/timeline'>Timeline</a>$name</a>\n" |
| 65 | } |
| 66 | } |
| 67 | me/tree?ci=tip'>Filhas>$name</a>\n" |
| 68 | } |
| 69 | } |
| 70 | me/brlist'>Branches</a>\n" |
| 71 | >$name</a>\n" |
| 72 | } |
| 73 | } |
| 74 | hasaglist'>Tags</>$name</a>\n" |
| 75 | } |
| 76 | } |
| 77 | me/ticket'>Tickets</>$name</a>\n" |
| 78 | } |
| 79 | } |
| 80 | me/wikihashas name</a>\n" |
| 81 | } |
| 82 | } |
| 83 | me/setup_ulist'>Users</a>\n"<html>reportlisd> |
| 84 | <base href="$baseuse href="$baseurl/$c/login'>Logout</a>\n" |
| 85 | } else { |
| 86 | >$name</a>\n" |
| 87 | } |
| 88 | } |
| 89 | me |
| --- a/skins/etienne1/README.md | ||
| +++ b/skins/etienne1/README.md | ||
| @@ -0,0 +1 @@ | ||
| 1 | +This skin was contributed by Étienne Deparis. |
| --- a/skins/etienne1/README.md | |
| +++ b/skins/etienne1/README.md | |
| @@ -0,0 +1 @@ | |
| --- a/skins/etienne1/README.md | |
| +++ b/skins/etienne1/README.md | |
| @@ -0,0 +1 @@ | |
| 1 | This skin was contributed by Étienne Deparis. |
| --- a/skins/etienne1/css.txt | ||
| +++ b/skins/etienne1/css.txt | ||
| @@ -0,0 +1,3 @@ | ||
| 1 | +max-width: 33%.sub30max-width: width: 96max-max-width: 800px;5f9f} | |
| 2 | + | |
| 3 | +.submenu a:hover {bottom: 1px solid #fff;border: 0px, |
| --- a/skins/etienne1/css.txt | |
| +++ b/skins/etienne1/css.txt | |
| @@ -0,0 +1,3 @@ | |
| --- a/skins/etienne1/css.txt | |
| +++ b/skins/etienne1/css.txt | |
| @@ -0,0 +1,3 @@ | |
| 1 | max-width: 33%.sub30max-width: width: 96max-max-width: 800px;5f9f} |
| 2 | |
| 3 | .submenu a:hover {bottom: 1px solid #fff;border: 0px, |
| --- a/skins/etienne1/footer.txt | ||
| +++ b/skins/etienne1/footer.txt | ||
| @@ -0,0 +1,3 @@ | ||
| 1 | +<div class="footer"> | |
| 2 | +This page was generated in about | |
| 3 | +<th1>puts [expr {([utime]+[stime]+1000)/100 |
| --- a/skins/etienne1/footer.txt | |
| +++ b/skins/etienne1/footer.txt | |
| @@ -0,0 +1,3 @@ | |
| --- a/skins/etienne1/footer.txt | |
| +++ b/skins/etienne1/footer.txt | |
| @@ -0,0 +1,3 @@ | |
| 1 | <div class="footer"> |
| 2 | This page was generated in about |
| 3 | <th1>puts [expr {([utime]+[stime]+1000)/100 |
| --- a/skins/etienne1/header.txt | ||
| +++ b/skins/etienne1/header.txt | ||
| @@ -0,0 +1,27 @@ | ||
| 1 | +<html> | |
| 2 | + <head> | |
| 3 | + <base href="$baseurl/$current_page" /> | |
| 4 | + <title>$<project_name>: $<title></title> | |
| 5 | + <link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 6 | + href="$home/timeline.rss" /> | |
| 7 | + <lihome/style.css?default" type="text/css" | |
| 8 | + media="screen" /> | |
| 9 | + </head> | |
| 10 | + | |
| 11 | + <bodyth1> | |
| 12 | + if {[info exheader"> | |
| 13 | + t_name"> | |
| 14 | + </a> | |
| 15 | + <<h1>$<project_name></h1>$<title>us"><th1> | |
| 16 | + 1> | |
| 17 | + if {[info exists login]} { | |
| 18 | + if {[info exists login]} { | |
| 19 | + } els$login — } els} } else { | |
| 20 | + t h$login</a>\n" | |
| 21 | + } | |
| 22 | + h1> | |
| 23 | + if {[info exmainmbtn' href='$proc menulink {url name} { | |
| 24 | + upvar current_page current | |
| 25 | + upvar home homa h <th1> | |
| 26 | +proc isin {val lst} { | |
| 27 | + set tot [llreportlisrep |
| --- a/skins/etienne1/header.txt | |
| +++ b/skins/etienne1/header.txt | |
| @@ -0,0 +1,27 @@ | |
| --- a/skins/etienne1/header.txt | |
| +++ b/skins/etienne1/header.txt | |
| @@ -0,0 +1,27 @@ | |
| 1 | <html> |
| 2 | <head> |
| 3 | <base href="$baseurl/$current_page" /> |
| 4 | <title>$<project_name>: $<title></title> |
| 5 | <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 6 | href="$home/timeline.rss" /> |
| 7 | <lihome/style.css?default" type="text/css" |
| 8 | media="screen" /> |
| 9 | </head> |
| 10 | |
| 11 | <bodyth1> |
| 12 | if {[info exheader"> |
| 13 | t_name"> |
| 14 | </a> |
| 15 | <<h1>$<project_name></h1>$<title>us"><th1> |
| 16 | 1> |
| 17 | if {[info exists login]} { |
| 18 | if {[info exists login]} { |
| 19 | } els$login — } els} } else { |
| 20 | t h$login</a>\n" |
| 21 | } |
| 22 | h1> |
| 23 | if {[info exmainmbtn' href='$proc menulink {url name} { |
| 24 | upvar current_page current |
| 25 | upvar home homa h <th1> |
| 26 | proc isin {val lst} { |
| 27 | set tot [llreportlisrep |
+1
| --- a/skins/khaki/css.txt | ||
| +++ b/skins/khaki/css.txt | ||
| @@ -0,0 +1 @@ | ||
| 1 | +/* General settings for t |
| --- a/skins/khaki/css.txt | |
| +++ b/skins/khaki/css.txt | |
| @@ -0,0 +1 @@ | |
| --- a/skins/khaki/css.txt | |
| +++ b/skins/khaki/css.txt | |
| @@ -0,0 +1 @@ | |
| 1 | /* General settings for t |
| --- a/skins/khaki/footer.txt | ||
| +++ b/skins/khaki/footer.txt | ||
| @@ -0,0 +1 @@ | ||
| 1 | +< |
| --- a/skins/khaki/footer.txt | |
| +++ b/skins/khaki/footer.txt | |
| @@ -0,0 +1 @@ | |
| --- a/skins/khaki/footer.txt | |
| +++ b/skins/khaki/footer.txt | |
| @@ -0,0 +1 @@ | |
| 1 | < |
+18
| --- a/skins/khaki/header.txt | ||
| +++ b/skins/khaki/header.txt | ||
| @@ -0,0 +1,18 @@ | ||
| 1 | +<div class="header"> | |
| 2 | + <div class="title">$<title></div> | |
| 3 | + <div class="status"> | |
| 4 | + <div class="logo">$<project_name>if {[info exists login]} { | |
| 5 | + o exists login]} { | |
| 6 | + puts } else { | |
| 7 | + puts "Not logged in" | |
| 8 | + } | |
| 9 | + </th1></div> | |
| 10 | +</he><th1> | |
| 11 | +html "<a id='hbbtndiv> | |
| 12 | +<dime/sitemap' aria-la | |
| 13 | +<th1> | |
| 14 | +home$index_page'>Homehome/timeline'>Timeline</home/tree?ci=tip'>Files<home/brlist'>Branches</a>\n"home/taglist'>Tags</a>\n" | |
| 15 | +} | |
| 16 | +if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {home/forum'>Forum<home/ticket'>Tickets<home/wiki'>Wikihome/setup'>Admin</a>home/setup_ulist'>Users</a>\n" | |
| 17 | +} | |
| 18 | + o exists loginoncap rhascap ohhascap ohascap hasreportlis |
| --- a/skins/khaki/header.txt | |
| +++ b/skins/khaki/header.txt | |
| @@ -0,0 +1,18 @@ | |
| --- a/skins/khaki/header.txt | |
| +++ b/skins/khaki/header.txt | |
| @@ -0,0 +1,18 @@ | |
| 1 | <div class="header"> |
| 2 | <div class="title">$<title></div> |
| 3 | <div class="status"> |
| 4 | <div class="logo">$<project_name>if {[info exists login]} { |
| 5 | o exists login]} { |
| 6 | puts } else { |
| 7 | puts "Not logged in" |
| 8 | } |
| 9 | </th1></div> |
| 10 | </he><th1> |
| 11 | html "<a id='hbbtndiv> |
| 12 | <dime/sitemap' aria-la |
| 13 | <th1> |
| 14 | home$index_page'>Homehome/timeline'>Timeline</home/tree?ci=tip'>Files<home/brlist'>Branches</a>\n"home/taglist'>Tags</a>\n" |
| 15 | } |
| 16 | if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {home/forum'>Forum<home/ticket'>Tickets<home/wiki'>Wikihome/setup'>Admin</a>home/setup_ulist'>Users</a>\n" |
| 17 | } |
| 18 | o exists loginoncap rhascap ohhascap ohascap hasreportlis |
| --- a/skins/plain_gray/css.txt | ||
| +++ b/skins/plain_gray/css.txt | ||
| @@ -0,0 +1,6 @@ | ||
| 1 | +/* General settings for the entire page */ | |
| 2 | +body { | |
| 3 | + margin: 0ex 1ex; | |
| 4 | + padding: 0px; | |
| 5 | + background-color: white; | |
| 6 | + font-family: sans-serif; |
| --- a/skins/plain_gray/css.txt | |
| +++ b/skins/plain_gray/css.txt | |
| @@ -0,0 +1,6 @@ | |
| --- a/skins/plain_gray/css.txt | |
| +++ b/skins/plain_gray/css.txt | |
| @@ -0,0 +1,6 @@ | |
| 1 | /* General settings for the entire page */ |
| 2 | body { |
| 3 | margin: 0ex 1ex; |
| 4 | padding: 0px; |
| 5 | background-color: white; |
| 6 | font-family: sans-serif; |
| --- a/skins/plain_gray/footer.txt | ||
| +++ b/skins/plain_gray/footer.txt | ||
| @@ -0,0 +1 @@ | ||
| 1 | +< |
| --- a/skins/plain_gray/footer.txt | |
| +++ b/skins/plain_gray/footer.txt | |
| @@ -0,0 +1 @@ | |
| --- a/skins/plain_gray/footer.txt | |
| +++ b/skins/plain_gray/footer.txt | |
| @@ -0,0 +1 @@ | |
| 1 | < |
| --- a/skins/plain_gray/header.txt | ||
| +++ b/skins/plain_gray/header.txt | ||
| @@ -0,0 +1,13 @@ | ||
| 1 | +<html> | |
| 2 | +<head> | |
| 3 | +<base href="$baseucap r: $<title></title> | |
| 4 | +<link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 5 | + href="$home/timeline.rss"> | |
| 6 | +<link rel="stylesheet" href="$stylesheet_url" type="text/css" | |
| 7 | + media="screen"> | |
| 8 | +</head> | |
| 9 | +<body> | |
| 10 | +<div class="headjoroncap rhashascap oase href="$baseucap r: $<tit<html> | |
| 11 | +<head> | |
| 12 | +<base href="$baseucap r: $<title></title> | |
| 13 | +<link rel="alternathascap hasreportlis |
| --- a/skins/plain_gray/header.txt | |
| +++ b/skins/plain_gray/header.txt | |
| @@ -0,0 +1,13 @@ | |
| --- a/skins/plain_gray/header.txt | |
| +++ b/skins/plain_gray/header.txt | |
| @@ -0,0 +1,13 @@ | |
| 1 | <html> |
| 2 | <head> |
| 3 | <base href="$baseucap r: $<title></title> |
| 4 | <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 5 | href="$home/timeline.rss"> |
| 6 | <link rel="stylesheet" href="$stylesheet_url" type="text/css" |
| 7 | media="screen"> |
| 8 | </head> |
| 9 | <body> |
| 10 | <div class="headjoroncap rhashascap oase href="$baseucap r: $<tit<html> |
| 11 | <head> |
| 12 | <base href="$baseucap r: $<title></title> |
| 13 | <link rel="alternathascap hasreportlis |
+105
| --- a/skins/rounded1/css.txt | ||
| +++ b/skins/rounded1/css.txt | ||
| @@ -0,0 +1,105 @@ | ||
| 1 | +/* General settings for the entire page */ | |
| 2 | +html { | |
| 3 | + min-height: 100%; | |
| 4 | +} | |
| 5 | +body { | |
| 6 | + margin: 0ex 1ex; | |
| 7 | + padding: 0px; | |
| 8 | + background-color: white; | |
| 9 | + color: #333; | |
| 10 | + font-family: Verdana, sans-serif; | |
| 11 | + font-size: 0.8em; | |
| 12 | +mx-text-size-adjust: none; | |
| 13 | +} | |
| 14 | + | |
| 15 | +/* The project logo in the upper left-hand corner of each page */ | |
| 16 | +div.logo { | |
| 17 | + display: table-cell; | |
| 18 | + text-align: right; | |
| 19 | + vertical-align: bottom; | |
| 20 | + font-weight: normal; | |
| 21 | + white-space: nowrap; | |
| 22 | +} | |
| 23 | + | |
| 24 | +/* Widths */ | |
| 25 | +div.header, div.mainmenu, div.submenu, div.content, div.footer { | |
| 26 | + max-width: 900px; | |
| 27 | + margin: auto; | |
| 28 | + padding: 3px 20px 3px 20px; | |
| 29 | + clear: both; | |
| 30 | +} | |
| 31 | + | |
| 32 | +/* The page title at the top of each page */ | |
| 33 | +div.title { | |
| 34 | + display: table-cell; | |
| 35 | + padding-left: 10px; | |
| 36 | + font-size: 2em; | |
| 37 | + margin: 10px 0 10px -20px; | |
| 38 | + vertical-align: bottom; | |
| 39 | + text-align: left; | |
| 40 | + width: 80%; | |
| 41 | + font-family: Verdana, sans-serif; | |
| 42 | + font-weight: bold; | |
| 43 | + color: #558195; | |
| 44 | + text-shadow: 0px 2px 2px #999999; | |
| 45 | +} | |
| 46 | + | |
| 47 | +/* The login status message in the top right-hand corner */ | |
| 48 | +div.status { | |
| 49 | + display: table-cell; | |
| 50 | + text-align: right; | |
| 51 | + vertical-align: bottom; | |
| 52 | + color: #333; | |
| 53 | + margin-right: -20px; | |
| 54 | + white-space: nowrap; | |
| 55 | +} | |
| 56 | + | |
| 57 | +/* The main menu bar that appears at the top of the page beneath | |
| 58 | + ** the header */ | |
| 59 | +div.mainmenu { | |
| 60 | + text-align: center; | |
| 61 | + color: white; | |
| 62 | + border-top-left-radius: 5px; | |
| 63 | + border-top-right-radius: 5px; | |
| 64 | + vertical-align: middle; | |
| 65 | + padding-top: 8px; | |
| 66 | + padding-bottom: 8px; | |
| 67 | + background-color: #446979; | |
| 68 | + box-shadow: 0px 3px 4px #333333; | |
| 69 | +} | |
| 70 | + | |
| 71 | +/* The submenu bar that *sometimes* appears below the main menu */ | |
| 72 | +div.submenu { | |
| 73 | + padding-top:10px; | |
| 74 | + padding-bottom:0; | |
| 75 | + text-align: right; | |
| 76 | + color: #000; | |
| 77 | + background-color: #fff; | |
| 78 | + height: 1.5em; | |
| 79 | + vertical-align:middle; | |
| 80 | + box-shadow: 0px 3px 4px #999; | |
| 81 | +} | |
| 82 | +div.mainmenu a, div.mainmenu a:visited { | |
| 83 | + padding: 3px 10px 3px 10px; | |
| 84 | + color: white; | |
| 85 | + text-decoration: none; | |
| 86 | +} | |
| 87 | +div.submenu a, div.submenu a:visited, .button, .button, div.submenu label, | |
| 88 | +div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { | |
| 89 | + padding: 2px 8px; | |
| 90 | + color: #000; | |
| 91 | + font-family: Arial; | |
| 92 | + text-decoration: none; | |
| 93 | + margin:auto; | |
| 94 | + border-radius: 5px; | |
| 95 | + background-color: #e0e0e0; | |
| 96 | + text-shadow: 0px -1px 0px #eee; | |
| 97 | + border: 1px solid #000; | |
| 98 | +} | |
| 99 | + | |
| 100 | +div.mainmenu a:hover { | |
| 101 | + color: #000; | |
| 102 | + background-co cursor: pointercolor: white; | |
| 103 | +} | |
| 104 | + | |
| 105 | +div.submenu a:hover, div: 10px 0 |
| --- a/skins/rounded1/css.txt | |
| +++ b/skins/rounded1/css.txt | |
| @@ -0,0 +1,105 @@ | |
| --- a/skins/rounded1/css.txt | |
| +++ b/skins/rounded1/css.txt | |
| @@ -0,0 +1,105 @@ | |
| 1 | /* General settings for the entire page */ |
| 2 | html { |
| 3 | min-height: 100%; |
| 4 | } |
| 5 | body { |
| 6 | margin: 0ex 1ex; |
| 7 | padding: 0px; |
| 8 | background-color: white; |
| 9 | color: #333; |
| 10 | font-family: Verdana, sans-serif; |
| 11 | font-size: 0.8em; |
| 12 | mx-text-size-adjust: none; |
| 13 | } |
| 14 | |
| 15 | /* The project logo in the upper left-hand corner of each page */ |
| 16 | div.logo { |
| 17 | display: table-cell; |
| 18 | text-align: right; |
| 19 | vertical-align: bottom; |
| 20 | font-weight: normal; |
| 21 | white-space: nowrap; |
| 22 | } |
| 23 | |
| 24 | /* Widths */ |
| 25 | div.header, div.mainmenu, div.submenu, div.content, div.footer { |
| 26 | max-width: 900px; |
| 27 | margin: auto; |
| 28 | padding: 3px 20px 3px 20px; |
| 29 | clear: both; |
| 30 | } |
| 31 | |
| 32 | /* The page title at the top of each page */ |
| 33 | div.title { |
| 34 | display: table-cell; |
| 35 | padding-left: 10px; |
| 36 | font-size: 2em; |
| 37 | margin: 10px 0 10px -20px; |
| 38 | vertical-align: bottom; |
| 39 | text-align: left; |
| 40 | width: 80%; |
| 41 | font-family: Verdana, sans-serif; |
| 42 | font-weight: bold; |
| 43 | color: #558195; |
| 44 | text-shadow: 0px 2px 2px #999999; |
| 45 | } |
| 46 | |
| 47 | /* The login status message in the top right-hand corner */ |
| 48 | div.status { |
| 49 | display: table-cell; |
| 50 | text-align: right; |
| 51 | vertical-align: bottom; |
| 52 | color: #333; |
| 53 | margin-right: -20px; |
| 54 | white-space: nowrap; |
| 55 | } |
| 56 | |
| 57 | /* The main menu bar that appears at the top of the page beneath |
| 58 | ** the header */ |
| 59 | div.mainmenu { |
| 60 | text-align: center; |
| 61 | color: white; |
| 62 | border-top-left-radius: 5px; |
| 63 | border-top-right-radius: 5px; |
| 64 | vertical-align: middle; |
| 65 | padding-top: 8px; |
| 66 | padding-bottom: 8px; |
| 67 | background-color: #446979; |
| 68 | box-shadow: 0px 3px 4px #333333; |
| 69 | } |
| 70 | |
| 71 | /* The submenu bar that *sometimes* appears below the main menu */ |
| 72 | div.submenu { |
| 73 | padding-top:10px; |
| 74 | padding-bottom:0; |
| 75 | text-align: right; |
| 76 | color: #000; |
| 77 | background-color: #fff; |
| 78 | height: 1.5em; |
| 79 | vertical-align:middle; |
| 80 | box-shadow: 0px 3px 4px #999; |
| 81 | } |
| 82 | div.mainmenu a, div.mainmenu a:visited { |
| 83 | padding: 3px 10px 3px 10px; |
| 84 | color: white; |
| 85 | text-decoration: none; |
| 86 | } |
| 87 | div.submenu a, div.submenu a:visited, .button, .button, div.submenu label, |
| 88 | div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { |
| 89 | padding: 2px 8px; |
| 90 | color: #000; |
| 91 | font-family: Arial; |
| 92 | text-decoration: none; |
| 93 | margin:auto; |
| 94 | border-radius: 5px; |
| 95 | background-color: #e0e0e0; |
| 96 | text-shadow: 0px -1px 0px #eee; |
| 97 | border: 1px solid #000; |
| 98 | } |
| 99 | |
| 100 | div.mainmenu a:hover { |
| 101 | color: #000; |
| 102 | background-co cursor: pointercolor: white; |
| 103 | } |
| 104 | |
| 105 | div.submenu a:hover, div: 10px 0 |
| --- a/skins/rounded1/footer.txt | ||
| +++ b/skins/rounded1/footer.txt | ||
| @@ -0,0 +1 @@ | ||
| 1 | +< |
| --- a/skins/rounded1/footer.txt | |
| +++ b/skins/rounded1/footer.txt | |
| @@ -0,0 +1 @@ | |
| --- a/skins/rounded1/footer.txt | |
| +++ b/skins/rounded1/footer.txt | |
| @@ -0,0 +1 @@ | |
| 1 | < |
| --- a/skins/rounded1/header.txt | ||
| +++ b/skins/rounded1/header.txt | ||
| @@ -0,0 +1,38 @@ | ||
| 1 | +<div class="header"> | |
| 2 | + <div class="logo"> | |
| 3 | + <img src="$logo_image_url" alt="logo"> | |
| 4 | + <br />$<project_name> | |
| 5 | + </div> | |
| 6 | + <div class="title">$<title></div> | |
| 7 | + <div class="status"><th1> | |
| 8 | + if {[info exists login]} { | |
| 9 | + puts "Logged in as $login" | |
| 10 | + } else { | |
| 11 | + puts "Not logged in" | |
| 12 | + } | |
| 13 | + </th1></div> | |
| 14 | +</div> | |
| 15 | +<div class="mainmenu"> | |
| 16 | +<th1> | |
| 17 | +html "<a href='$home$index_page'>Home</a>\n" | |
| 18 | +if {[anycap jor]} { | |
| 19 | + html "<a href='$home/} { | |
| 20 | + html "<a href='$home/timeline'>Timeline</a>\n" | |
| 21 | +} | |
| 22 | +if {[anoncap oh]} { | |
| 23 | + html "<a href='$home/tree?ci=tip'>Files</a>\n" | |
| 24 | +} | |
| 25 | +if {[anoncap o]} { | |
| 26 | + html "<a href='$home/brlist'>Branches</a>\n" | |
| 27 | + html "<a href='$home/tagoncap r]} { | |
| 28 | + html "<a href='$home/ticket'>Tickets</a>\n" | |
| 29 | +} | |
| 30 | +if {[anoncap j]} { | |
| 31 | + has"header"> | |
| 32 | + <div class="logo"> | |
| 33 | + <img src="$logo_image_url" alt="<div claeif {[hascap a]} { | |
| 34 | + html "<a href='$home/setup_ulist'>Users</a>\n" | |
| 35 | +} | |
| 36 | + clashascap "<a href='$home/ticket'>Ticket} else { | |
| 37 | + html "<a href='$hhase { | |
| 38 | + html "<a href='$reportlislogin'>L |
| --- a/skins/rounded1/header.txt | |
| +++ b/skins/rounded1/header.txt | |
| @@ -0,0 +1,38 @@ | |
| --- a/skins/rounded1/header.txt | |
| +++ b/skins/rounded1/header.txt | |
| @@ -0,0 +1,38 @@ | |
| 1 | <div class="header"> |
| 2 | <div class="logo"> |
| 3 | <img src="$logo_image_url" alt="logo"> |
| 4 | <br />$<project_name> |
| 5 | </div> |
| 6 | <div class="title">$<title></div> |
| 7 | <div class="status"><th1> |
| 8 | if {[info exists login]} { |
| 9 | puts "Logged in as $login" |
| 10 | } else { |
| 11 | puts "Not logged in" |
| 12 | } |
| 13 | </th1></div> |
| 14 | </div> |
| 15 | <div class="mainmenu"> |
| 16 | <th1> |
| 17 | html "<a href='$home$index_page'>Home</a>\n" |
| 18 | if {[anycap jor]} { |
| 19 | html "<a href='$home/} { |
| 20 | html "<a href='$home/timeline'>Timeline</a>\n" |
| 21 | } |
| 22 | if {[anoncap oh]} { |
| 23 | html "<a href='$home/tree?ci=tip'>Files</a>\n" |
| 24 | } |
| 25 | if {[anoncap o]} { |
| 26 | html "<a href='$home/brlist'>Branches</a>\n" |
| 27 | html "<a href='$home/tagoncap r]} { |
| 28 | html "<a href='$home/ticket'>Tickets</a>\n" |
| 29 | } |
| 30 | if {[anoncap j]} { |
| 31 | has"header"> |
| 32 | <div class="logo"> |
| 33 | <img src="$logo_image_url" alt="<div claeif {[hascap a]} { |
| 34 | html "<a href='$home/setup_ulist'>Users</a>\n" |
| 35 | } |
| 36 | clashascap "<a href='$home/ticket'>Ticket} else { |
| 37 | html "<a href='$hhase { |
| 38 | html "<a href='$reportlislogin'>L |
+2
-2
| --- src/attach.c | ||
| +++ src/attach.c | ||
| @@ -90,12 +90,12 @@ | ||
| 90 | 90 | @ <li><p> |
| 91 | 91 | @ Attachment %z(href("%R/ainfo/%s",zUuid))%S(zUuid)</a> |
| 92 | 92 | if( moderation_pending(attachid) ){ |
| 93 | 93 | @ <span class="modpending">*** Awaiting Moderator Approval ***</span> |
| 94 | 94 | } |
| 95 | - @ <br><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a> | |
| 96 | - @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> | |
| 95 | + @ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> | |
| 96 | + @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> | |
| 97 | 97 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 98 | 98 | if( zComment && zComment[0] ){ |
| 99 | 99 | @ %!w(zComment)<br /> |
| 100 | 100 | } |
| 101 | 101 | if( zPage==0 && zTkt==0 ){ |
| 102 | 102 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -90,12 +90,12 @@ | |
| 90 | @ <li><p> |
| 91 | @ Attachment %z(href("%R/ainfo/%s",zUuid))%S(zUuid)</a> |
| 92 | if( moderation_pending(attachid) ){ |
| 93 | @ <span class="modpending">*** Awaiting Moderator Approval ***</span> |
| 94 | } |
| 95 | @ <br><a href="/attachview?%s(zUrlTail)">%h(zFilename)</a> |
| 96 | @ [<a href="/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> |
| 97 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 98 | if( zComment && zComment[0] ){ |
| 99 | @ %!w(zComment)<br /> |
| 100 | } |
| 101 | if( zPage==0 && zTkt==0 ){ |
| 102 |
| --- src/attach.c | |
| +++ src/attach.c | |
| @@ -90,12 +90,12 @@ | |
| 90 | @ <li><p> |
| 91 | @ Attachment %z(href("%R/ainfo/%s",zUuid))%S(zUuid)</a> |
| 92 | if( moderation_pending(attachid) ){ |
| 93 | @ <span class="modpending">*** Awaiting Moderator Approval ***</span> |
| 94 | } |
| 95 | @ <br><a href="%R/attachview?%s(zUrlTail)">%h(zFilename)</a> |
| 96 | @ [<a href="%R/attachdownload/%t(zFilename)?%s(zUrlTail)">download</a>]<br /> |
| 97 | if( zComment ) while( fossil_isspace(zComment[0]) ) zComment++; |
| 98 | if( zComment && zComment[0] ){ |
| 99 | @ %!w(zComment)<br /> |
| 100 | } |
| 101 | if( zPage==0 && zTkt==0 ){ |
| 102 |
+154
-38
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -179,47 +179,67 @@ | ||
| 179 | 179 | |
| 180 | 180 | /* Do an autosync push, if requested */ |
| 181 | 181 | if( !isPrivate ) autosync_loop(SYNC_PUSH, db_get_int("autosync-tries", 1)); |
| 182 | 182 | } |
| 183 | 183 | |
| 184 | +#if INTERFACE | |
| 185 | +/* | |
| 186 | +** Allows bits in the mBplqFlags parameter to branch_prepare_list_query(). | |
| 187 | +*/ | |
| 188 | +#define BRL_CLOSED_ONLY 0x001 /* Show only closed branches */ | |
| 189 | +#define BRL_OPEN_ONLY 0x002 /* Show only open branches */ | |
| 190 | +#define BRL_BOTH 0x003 /* Show both open and closed branches */ | |
| 191 | +#define BRL_OPEN_CLOSED_MASK 0x003 | |
| 192 | +#define BRL_MTIME 0x004 /* Include lastest check-in time */ | |
| 193 | +#dfeine BRL_ORDERBY_MTIME 0x008 /* Sort by MTIME. (otherwise sort by name)*/ | |
| 194 | + | |
| 195 | +#endif /* INTERFACE */ | |
| 196 | + | |
| 184 | 197 | /* |
| 185 | 198 | ** Prepare a query that will list branches. |
| 186 | 199 | ** |
| 187 | 200 | ** If (which<0) then the query pulls only closed branches. If |
| 188 | 201 | ** (which>0) then the query pulls all (closed and opened) |
| 189 | 202 | ** branches. Else the query pulls currently-opened branches. |
| 190 | 203 | */ |
| 191 | -void branch_prepare_list_query(Stmt *pQuery, int which ){ | |
| 192 | - if( which < 0 ){ | |
| 193 | - db_prepare(pQuery, | |
| 194 | - "SELECT value FROM tagxref" | |
| 195 | - " WHERE tagid=%d AND value NOT NULL " | |
| 196 | - "EXCEPT " | |
| 197 | - "SELECT value FROM tagxref" | |
| 198 | - " WHERE tagid=%d" | |
| 199 | - " AND rid IN leaf" | |
| 200 | - " AND NOT %z" | |
| 201 | - " ORDER BY value COLLATE nocase /*sort*/", | |
| 202 | - TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") | |
| 203 | - ); | |
| 204 | - }else if( which>0 ){ | |
| 205 | - db_prepare(pQuery, | |
| 206 | - "SELECT DISTINCT value FROM tagxref" | |
| 207 | - " WHERE tagid=%d AND value NOT NULL" | |
| 208 | - " AND rid IN leaf" | |
| 209 | - " ORDER BY value COLLATE nocase /*sort*/", | |
| 210 | - TAG_BRANCH | |
| 211 | - ); | |
| 212 | - }else{ | |
| 213 | - db_prepare(pQuery, | |
| 214 | - "SELECT DISTINCT value FROM tagxref" | |
| 215 | - " WHERE tagid=%d AND value NOT NULL" | |
| 216 | - " AND rid IN leaf" | |
| 217 | - " AND NOT %z" | |
| 218 | - " ORDER BY value COLLATE nocase /*sort*/", | |
| 219 | - TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") | |
| 220 | - ); | |
| 204 | +void branch_prepare_list_query(Stmt *pQuery, int brFlags){ | |
| 205 | + switch( brFlags & BRL_OPEN_CLOSED_MASK ){ | |
| 206 | + case BRL_CLOSED_ONLY: { | |
| 207 | + db_prepare(pQuery, | |
| 208 | + "SELECT value FROM tagxref" | |
| 209 | + " WHERE tagid=%d AND value NOT NULL " | |
| 210 | + "EXCEPT " | |
| 211 | + "SELECT value FROM tagxref" | |
| 212 | + " WHERE tagid=%d" | |
| 213 | + " AND rid IN leaf" | |
| 214 | + " AND NOT %z" | |
| 215 | + " ORDER BY value COLLATE nocase /*sort*/", | |
| 216 | + TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") | |
| 217 | + ); | |
| 218 | + break; | |
| 219 | + } | |
| 220 | + case BRL_BOTH: { | |
| 221 | + db_prepare(pQuery, | |
| 222 | + "SELECT DISTINCT value FROM tagxref" | |
| 223 | + " WHERE tagid=%d AND value NOT NULL" | |
| 224 | + " AND rid IN leaf" | |
| 225 | + " ORDER BY value COLLATE nocase /*sort*/", | |
| 226 | + TAG_BRANCH | |
| 227 | + ); | |
| 228 | + break; | |
| 229 | + } | |
| 230 | + case BRL_OPEN_ONLY: { | |
| 231 | + db_prepare(pQuery, | |
| 232 | + "SELECT DISTINCT value FROM tagxref" | |
| 233 | + " WHERE tagid=%d AND value NOT NULL" | |
| 234 | + " AND rid IN leaf" | |
| 235 | + " AND NOT %z" | |
| 236 | + " ORDER BY value COLLATE nocase /*sort*/", | |
| 237 | + TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") | |
| 238 | + ); | |
| 239 | + break; | |
| 240 | + } | |
| 221 | 241 | } |
| 222 | 242 | } |
| 223 | 243 | |
| 224 | 244 | |
| 225 | 245 | /* |
| @@ -260,19 +280,20 @@ | ||
| 260 | 280 | branch_new(); |
| 261 | 281 | }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", n)==0) ){ |
| 262 | 282 | Stmt q; |
| 263 | 283 | int vid; |
| 264 | 284 | char *zCurrent = 0; |
| 265 | - int showAll = find_option("all","a",0)!=0; | |
| 266 | - int showClosed = find_option("closed","c",0)!=0; | |
| 285 | + int brFlags = BRL_OPEN_ONLY; | |
| 286 | + if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH; | |
| 287 | + if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY; | |
| 267 | 288 | |
| 268 | 289 | if( g.localOpen ){ |
| 269 | 290 | vid = db_lget_int("checkout", 0); |
| 270 | 291 | zCurrent = db_text(0, "SELECT value FROM tagxref" |
| 271 | 292 | " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH); |
| 272 | 293 | } |
| 273 | - branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0)); | |
| 294 | + branch_prepare_list_query(&q, brFlags); | |
| 274 | 295 | while( db_step(&q)==SQLITE_ROW ){ |
| 275 | 296 | const char *zBr = db_column_text(&q, 0); |
| 276 | 297 | int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0; |
| 277 | 298 | fossil_print("%s%s\n", (isCur ? "* " : " "), zBr); |
| 278 | 299 | } |
| @@ -280,36 +301,131 @@ | ||
| 280 | 301 | }else{ |
| 281 | 302 | fossil_fatal("branch subcommand should be one of: " |
| 282 | 303 | "new list ls"); |
| 283 | 304 | } |
| 284 | 305 | } |
| 306 | + | |
| 307 | +static const char brlistQuery[] = | |
| 308 | +@ SELECT | |
| 309 | +@ tagxref.value, | |
| 310 | +@ max(event.mtime), | |
| 311 | +@ EXISTS(SELECT 1 FROM tagxref AS tx | |
| 312 | +@ WHERE tx.rid=tagxref.rid | |
| 313 | +@ AND tx.tagid=(SELECT tagid FROM tag WHERE tagname='closed') | |
| 314 | +@ AND tx.tagtype>0), | |
| 315 | +@ (SELECT tagxref.value | |
| 316 | +@ FROM plink CROSS JOIN tagxref | |
| 317 | +@ WHERE plink.pid=event.objid | |
| 318 | +@ AND tagxref.rid=plink.cid | |
| 319 | +@ AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='branch') | |
| 320 | +@ AND tagtype>0), | |
| 321 | +@ count(*), | |
| 322 | +@ (SELECT uuid FROM blob WHERE rid=tagxref.rid) | |
| 323 | +@ FROM tagxref, tag, event | |
| 324 | +@ WHERE tagxref.tagid=tag.tagid | |
| 325 | +@ AND tagxref.tagtype>0 | |
| 326 | +@ AND tag.tagname='branch' | |
| 327 | +@ AND event.objid=tagxref.rid | |
| 328 | +@ GROUP BY 1 | |
| 329 | +@ ORDER BY 2 DESC; | |
| 330 | +; | |
| 331 | + | |
| 332 | +/* | |
| 333 | +** This is the new-style branch-list page that shows the branch names | |
| 334 | +** together with their ages (time of last check-in) and whether or not | |
| 335 | +** they are closed or merged to another branch. | |
| 336 | +** | |
| 337 | +** Control jumps to this routine from brlist_page() (the /brlist handler) | |
| 338 | +** if there are no query parameters. | |
| 339 | +*/ | |
| 340 | +static void new_brlist_page(void){ | |
| 341 | + Stmt q; | |
| 342 | + double rNow; | |
| 343 | + login_check_credentials(); | |
| 344 | + if( !g.perm.Read ){ login_needed(); return; } | |
| 345 | + style_header("Branches"); | |
| 346 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 347 | + login_anonymous_available(); | |
| 348 | + | |
| 349 | + db_prepare(&q, brlistQuery/*works-like:""*/); | |
| 350 | + rNow = db_double(0.0, "SELECT julianday('now')"); | |
| 351 | + @ <div class="brlist"><table id="branchlisttable"> | |
| 352 | + @ <thead><tr> | |
| 353 | + @ <th>Branch Name</th> | |
| 354 | + @ <th>Age</th> | |
| 355 | + @ <th>Checkins</th> | |
| 356 | + @ <th>Status</th> | |
| 357 | + @ <th>Resolution</th> | |
| 358 | + @ </tr></thead><tbody> | |
| 359 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 360 | + const char *zBranch = db_column_text(&q, 0); | |
| 361 | + double rMtime = db_column_double(&q, 1); | |
| 362 | + int isClosed = db_column_int(&q, 2); | |
| 363 | + const char *zMergeTo = db_column_text(&q, 3); | |
| 364 | + int nCkin = db_column_int(&q, 4); | |
| 365 | + const char *zLastCkin = db_column_text(&q, 5); | |
| 366 | + char *zAge = human_readable_age(rNow - rMtime); | |
| 367 | + sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); | |
| 368 | + if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0; | |
| 369 | + @ <tr> | |
| 370 | + @ <td>%z(href("%R/timeline?n=100&r=%T",zBranch))%h(zBranch)</a></td> | |
| 371 | + @ <td data-sortkey="%016llx(-iMtime)">%s(zAge)</td> | |
| 372 | + @ <td>%d(nCkin)</td> | |
| 373 | + fossil_free(zAge); | |
| 374 | + @ <td>%s(isClosed?"closed":"")</td> | |
| 375 | + if( zMergeTo ){ | |
| 376 | + @ <td>merged into | |
| 377 | + @ %z(href("%R/timeline?f=%s",zLastCkin))%h(zMergeTo)</a></td> | |
| 378 | + }else{ | |
| 379 | + @ <td></td> | |
| 380 | + } | |
| 381 | + @ </tr> | |
| 382 | + } | |
| 383 | + @ </tbody></table></div> | |
| 384 | + db_finalize(&q); | |
| 385 | + output_table_sorting_javascript("branchlisttable","tkNtt",2); | |
| 386 | + style_footer(); | |
| 387 | +} | |
| 285 | 388 | |
| 286 | 389 | /* |
| 287 | 390 | ** WEBPAGE: brlist |
| 391 | +** Show a list of branches | |
| 392 | +** Query parameters: | |
| 288 | 393 | ** |
| 289 | -** Show a timeline of all branches | |
| 394 | +** all Show all branches | |
| 395 | +** closed Show only closed branches | |
| 396 | +** open Show only open branches (default behavior) | |
| 397 | +** colortest Show all branches with automatic color | |
| 290 | 398 | */ |
| 291 | 399 | void brlist_page(void){ |
| 292 | 400 | Stmt q; |
| 293 | 401 | int cnt; |
| 294 | 402 | int showClosed = P("closed")!=0; |
| 295 | 403 | int showAll = P("all")!=0; |
| 404 | + int showOpen = P("open")!=0; | |
| 296 | 405 | int colorTest = P("colortest")!=0; |
| 406 | + int brFlags = BRL_OPEN_ONLY; | |
| 297 | 407 | |
| 408 | + if( showClosed==0 && showAll==0 && showOpen==0 && colorTest==0 ){ | |
| 409 | + new_brlist_page(); | |
| 410 | + return; | |
| 411 | + } | |
| 298 | 412 | login_check_credentials(); |
| 299 | 413 | if( !g.perm.Read ){ login_needed(); return; } |
| 300 | 414 | if( colorTest ){ |
| 301 | 415 | showClosed = 0; |
| 302 | 416 | showAll = 1; |
| 303 | 417 | } |
| 418 | + if( showAll ) brFlags = BRL_BOTH; | |
| 419 | + if( showClosed ) brFlags = BRL_CLOSED_ONLY; | |
| 304 | 420 | |
| 305 | 421 | style_header("%s", showClosed ? "Closed Branches" : |
| 306 | 422 | showAll ? "All Branches" : "Open Branches"); |
| 307 | 423 | style_submenu_element("Timeline", "Timeline", "brtimeline"); |
| 308 | 424 | if( showClosed ){ |
| 309 | 425 | style_submenu_element("All", "All", "brlist?all"); |
| 310 | - style_submenu_element("Open","Open","brlist"); | |
| 426 | + style_submenu_element("Open","Open","brlist?open"); | |
| 311 | 427 | }else if( showAll ){ |
| 312 | 428 | style_submenu_element("Closed", "Closed", "brlist?closed"); |
| 313 | 429 | style_submenu_element("Open","Open","brlist"); |
| 314 | 430 | }else{ |
| 315 | 431 | style_submenu_element("All", "All", "brlist?all"); |
| @@ -335,21 +451,21 @@ | ||
| 335 | 451 | @ Closed branches are fixed and do not change (unless they are first |
| 336 | 452 | @ reopened).</li> |
| 337 | 453 | @ </ol> |
| 338 | 454 | style_sidebox_end(); |
| 339 | 455 | |
| 340 | - branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0)); | |
| 456 | + branch_prepare_list_query(&q, brFlags); | |
| 341 | 457 | cnt = 0; |
| 342 | 458 | while( db_step(&q)==SQLITE_ROW ){ |
| 343 | 459 | const char *zBr = db_column_text(&q, 0); |
| 344 | 460 | if( cnt==0 ){ |
| 345 | 461 | if( colorTest ){ |
| 346 | 462 | @ <h2>Default background colors for all branches:</h2> |
| 463 | + }else if( showClosed ){ | |
| 464 | + @ <h2>Closed Branches:</h2> | |
| 347 | 465 | }else if( showAll ){ |
| 348 | 466 | @ <h2>All Branches:</h2> |
| 349 | - }else if( showClosed ){ | |
| 350 | - @ <h2>Closed Branches:</h2> | |
| 351 | 467 | }else{ |
| 352 | 468 | @ <h2>Open Branches:</h2> |
| 353 | 469 | } |
| 354 | 470 | @ <ul> |
| 355 | 471 | cnt++; |
| 356 | 472 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -179,47 +179,67 @@ | |
| 179 | |
| 180 | /* Do an autosync push, if requested */ |
| 181 | if( !isPrivate ) autosync_loop(SYNC_PUSH, db_get_int("autosync-tries", 1)); |
| 182 | } |
| 183 | |
| 184 | /* |
| 185 | ** Prepare a query that will list branches. |
| 186 | ** |
| 187 | ** If (which<0) then the query pulls only closed branches. If |
| 188 | ** (which>0) then the query pulls all (closed and opened) |
| 189 | ** branches. Else the query pulls currently-opened branches. |
| 190 | */ |
| 191 | void branch_prepare_list_query(Stmt *pQuery, int which ){ |
| 192 | if( which < 0 ){ |
| 193 | db_prepare(pQuery, |
| 194 | "SELECT value FROM tagxref" |
| 195 | " WHERE tagid=%d AND value NOT NULL " |
| 196 | "EXCEPT " |
| 197 | "SELECT value FROM tagxref" |
| 198 | " WHERE tagid=%d" |
| 199 | " AND rid IN leaf" |
| 200 | " AND NOT %z" |
| 201 | " ORDER BY value COLLATE nocase /*sort*/", |
| 202 | TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") |
| 203 | ); |
| 204 | }else if( which>0 ){ |
| 205 | db_prepare(pQuery, |
| 206 | "SELECT DISTINCT value FROM tagxref" |
| 207 | " WHERE tagid=%d AND value NOT NULL" |
| 208 | " AND rid IN leaf" |
| 209 | " ORDER BY value COLLATE nocase /*sort*/", |
| 210 | TAG_BRANCH |
| 211 | ); |
| 212 | }else{ |
| 213 | db_prepare(pQuery, |
| 214 | "SELECT DISTINCT value FROM tagxref" |
| 215 | " WHERE tagid=%d AND value NOT NULL" |
| 216 | " AND rid IN leaf" |
| 217 | " AND NOT %z" |
| 218 | " ORDER BY value COLLATE nocase /*sort*/", |
| 219 | TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") |
| 220 | ); |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | |
| 225 | /* |
| @@ -260,19 +280,20 @@ | |
| 260 | branch_new(); |
| 261 | }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", n)==0) ){ |
| 262 | Stmt q; |
| 263 | int vid; |
| 264 | char *zCurrent = 0; |
| 265 | int showAll = find_option("all","a",0)!=0; |
| 266 | int showClosed = find_option("closed","c",0)!=0; |
| 267 | |
| 268 | if( g.localOpen ){ |
| 269 | vid = db_lget_int("checkout", 0); |
| 270 | zCurrent = db_text(0, "SELECT value FROM tagxref" |
| 271 | " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH); |
| 272 | } |
| 273 | branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0)); |
| 274 | while( db_step(&q)==SQLITE_ROW ){ |
| 275 | const char *zBr = db_column_text(&q, 0); |
| 276 | int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0; |
| 277 | fossil_print("%s%s\n", (isCur ? "* " : " "), zBr); |
| 278 | } |
| @@ -280,36 +301,131 @@ | |
| 280 | }else{ |
| 281 | fossil_fatal("branch subcommand should be one of: " |
| 282 | "new list ls"); |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | /* |
| 287 | ** WEBPAGE: brlist |
| 288 | ** |
| 289 | ** Show a timeline of all branches |
| 290 | */ |
| 291 | void brlist_page(void){ |
| 292 | Stmt q; |
| 293 | int cnt; |
| 294 | int showClosed = P("closed")!=0; |
| 295 | int showAll = P("all")!=0; |
| 296 | int colorTest = P("colortest")!=0; |
| 297 | |
| 298 | login_check_credentials(); |
| 299 | if( !g.perm.Read ){ login_needed(); return; } |
| 300 | if( colorTest ){ |
| 301 | showClosed = 0; |
| 302 | showAll = 1; |
| 303 | } |
| 304 | |
| 305 | style_header("%s", showClosed ? "Closed Branches" : |
| 306 | showAll ? "All Branches" : "Open Branches"); |
| 307 | style_submenu_element("Timeline", "Timeline", "brtimeline"); |
| 308 | if( showClosed ){ |
| 309 | style_submenu_element("All", "All", "brlist?all"); |
| 310 | style_submenu_element("Open","Open","brlist"); |
| 311 | }else if( showAll ){ |
| 312 | style_submenu_element("Closed", "Closed", "brlist?closed"); |
| 313 | style_submenu_element("Open","Open","brlist"); |
| 314 | }else{ |
| 315 | style_submenu_element("All", "All", "brlist?all"); |
| @@ -335,21 +451,21 @@ | |
| 335 | @ Closed branches are fixed and do not change (unless they are first |
| 336 | @ reopened).</li> |
| 337 | @ </ol> |
| 338 | style_sidebox_end(); |
| 339 | |
| 340 | branch_prepare_list_query(&q, showAll?1:(showClosed?-1:0)); |
| 341 | cnt = 0; |
| 342 | while( db_step(&q)==SQLITE_ROW ){ |
| 343 | const char *zBr = db_column_text(&q, 0); |
| 344 | if( cnt==0 ){ |
| 345 | if( colorTest ){ |
| 346 | @ <h2>Default background colors for all branches:</h2> |
| 347 | }else if( showAll ){ |
| 348 | @ <h2>All Branches:</h2> |
| 349 | }else if( showClosed ){ |
| 350 | @ <h2>Closed Branches:</h2> |
| 351 | }else{ |
| 352 | @ <h2>Open Branches:</h2> |
| 353 | } |
| 354 | @ <ul> |
| 355 | cnt++; |
| 356 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -179,47 +179,67 @@ | |
| 179 | |
| 180 | /* Do an autosync push, if requested */ |
| 181 | if( !isPrivate ) autosync_loop(SYNC_PUSH, db_get_int("autosync-tries", 1)); |
| 182 | } |
| 183 | |
| 184 | #if INTERFACE |
| 185 | /* |
| 186 | ** Allows bits in the mBplqFlags parameter to branch_prepare_list_query(). |
| 187 | */ |
| 188 | #define BRL_CLOSED_ONLY 0x001 /* Show only closed branches */ |
| 189 | #define BRL_OPEN_ONLY 0x002 /* Show only open branches */ |
| 190 | #define BRL_BOTH 0x003 /* Show both open and closed branches */ |
| 191 | #define BRL_OPEN_CLOSED_MASK 0x003 |
| 192 | #define BRL_MTIME 0x004 /* Include lastest check-in time */ |
| 193 | #dfeine BRL_ORDERBY_MTIME 0x008 /* Sort by MTIME. (otherwise sort by name)*/ |
| 194 | |
| 195 | #endif /* INTERFACE */ |
| 196 | |
| 197 | /* |
| 198 | ** Prepare a query that will list branches. |
| 199 | ** |
| 200 | ** If (which<0) then the query pulls only closed branches. If |
| 201 | ** (which>0) then the query pulls all (closed and opened) |
| 202 | ** branches. Else the query pulls currently-opened branches. |
| 203 | */ |
| 204 | void branch_prepare_list_query(Stmt *pQuery, int brFlags){ |
| 205 | switch( brFlags & BRL_OPEN_CLOSED_MASK ){ |
| 206 | case BRL_CLOSED_ONLY: { |
| 207 | db_prepare(pQuery, |
| 208 | "SELECT value FROM tagxref" |
| 209 | " WHERE tagid=%d AND value NOT NULL " |
| 210 | "EXCEPT " |
| 211 | "SELECT value FROM tagxref" |
| 212 | " WHERE tagid=%d" |
| 213 | " AND rid IN leaf" |
| 214 | " AND NOT %z" |
| 215 | " ORDER BY value COLLATE nocase /*sort*/", |
| 216 | TAG_BRANCH, TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") |
| 217 | ); |
| 218 | break; |
| 219 | } |
| 220 | case BRL_BOTH: { |
| 221 | db_prepare(pQuery, |
| 222 | "SELECT DISTINCT value FROM tagxref" |
| 223 | " WHERE tagid=%d AND value NOT NULL" |
| 224 | " AND rid IN leaf" |
| 225 | " ORDER BY value COLLATE nocase /*sort*/", |
| 226 | TAG_BRANCH |
| 227 | ); |
| 228 | break; |
| 229 | } |
| 230 | case BRL_OPEN_ONLY: { |
| 231 | db_prepare(pQuery, |
| 232 | "SELECT DISTINCT value FROM tagxref" |
| 233 | " WHERE tagid=%d AND value NOT NULL" |
| 234 | " AND rid IN leaf" |
| 235 | " AND NOT %z" |
| 236 | " ORDER BY value COLLATE nocase /*sort*/", |
| 237 | TAG_BRANCH, leaf_is_closed_sql("tagxref.rid") |
| 238 | ); |
| 239 | break; |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | |
| 245 | /* |
| @@ -260,19 +280,20 @@ | |
| 280 | branch_new(); |
| 281 | }else if( (strncmp(zCmd,"list",n)==0)||(strncmp(zCmd, "ls", n)==0) ){ |
| 282 | Stmt q; |
| 283 | int vid; |
| 284 | char *zCurrent = 0; |
| 285 | int brFlags = BRL_OPEN_ONLY; |
| 286 | if( find_option("all","a",0)!=0 ) brFlags = BRL_BOTH; |
| 287 | if( find_option("closed","c",0)!=0 ) brFlags = BRL_CLOSED_ONLY; |
| 288 | |
| 289 | if( g.localOpen ){ |
| 290 | vid = db_lget_int("checkout", 0); |
| 291 | zCurrent = db_text(0, "SELECT value FROM tagxref" |
| 292 | " WHERE rid=%d AND tagid=%d", vid, TAG_BRANCH); |
| 293 | } |
| 294 | branch_prepare_list_query(&q, brFlags); |
| 295 | while( db_step(&q)==SQLITE_ROW ){ |
| 296 | const char *zBr = db_column_text(&q, 0); |
| 297 | int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0; |
| 298 | fossil_print("%s%s\n", (isCur ? "* " : " "), zBr); |
| 299 | } |
| @@ -280,36 +301,131 @@ | |
| 301 | }else{ |
| 302 | fossil_fatal("branch subcommand should be one of: " |
| 303 | "new list ls"); |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | static const char brlistQuery[] = |
| 308 | @ SELECT |
| 309 | @ tagxref.value, |
| 310 | @ max(event.mtime), |
| 311 | @ EXISTS(SELECT 1 FROM tagxref AS tx |
| 312 | @ WHERE tx.rid=tagxref.rid |
| 313 | @ AND tx.tagid=(SELECT tagid FROM tag WHERE tagname='closed') |
| 314 | @ AND tx.tagtype>0), |
| 315 | @ (SELECT tagxref.value |
| 316 | @ FROM plink CROSS JOIN tagxref |
| 317 | @ WHERE plink.pid=event.objid |
| 318 | @ AND tagxref.rid=plink.cid |
| 319 | @ AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='branch') |
| 320 | @ AND tagtype>0), |
| 321 | @ count(*), |
| 322 | @ (SELECT uuid FROM blob WHERE rid=tagxref.rid) |
| 323 | @ FROM tagxref, tag, event |
| 324 | @ WHERE tagxref.tagid=tag.tagid |
| 325 | @ AND tagxref.tagtype>0 |
| 326 | @ AND tag.tagname='branch' |
| 327 | @ AND event.objid=tagxref.rid |
| 328 | @ GROUP BY 1 |
| 329 | @ ORDER BY 2 DESC; |
| 330 | ; |
| 331 | |
| 332 | /* |
| 333 | ** This is the new-style branch-list page that shows the branch names |
| 334 | ** together with their ages (time of last check-in) and whether or not |
| 335 | ** they are closed or merged to another branch. |
| 336 | ** |
| 337 | ** Control jumps to this routine from brlist_page() (the /brlist handler) |
| 338 | ** if there are no query parameters. |
| 339 | */ |
| 340 | static void new_brlist_page(void){ |
| 341 | Stmt q; |
| 342 | double rNow; |
| 343 | login_check_credentials(); |
| 344 | if( !g.perm.Read ){ login_needed(); return; } |
| 345 | style_header("Branches"); |
| 346 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 347 | login_anonymous_available(); |
| 348 | |
| 349 | db_prepare(&q, brlistQuery/*works-like:""*/); |
| 350 | rNow = db_double(0.0, "SELECT julianday('now')"); |
| 351 | @ <div class="brlist"><table id="branchlisttable"> |
| 352 | @ <thead><tr> |
| 353 | @ <th>Branch Name</th> |
| 354 | @ <th>Age</th> |
| 355 | @ <th>Checkins</th> |
| 356 | @ <th>Status</th> |
| 357 | @ <th>Resolution</th> |
| 358 | @ </tr></thead><tbody> |
| 359 | while( db_step(&q)==SQLITE_ROW ){ |
| 360 | const char *zBranch = db_column_text(&q, 0); |
| 361 | double rMtime = db_column_double(&q, 1); |
| 362 | int isClosed = db_column_int(&q, 2); |
| 363 | const char *zMergeTo = db_column_text(&q, 3); |
| 364 | int nCkin = db_column_int(&q, 4); |
| 365 | const char *zLastCkin = db_column_text(&q, 5); |
| 366 | char *zAge = human_readable_age(rNow - rMtime); |
| 367 | sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); |
| 368 | if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0; |
| 369 | @ <tr> |
| 370 | @ <td>%z(href("%R/timeline?n=100&r=%T",zBranch))%h(zBranch)</a></td> |
| 371 | @ <td data-sortkey="%016llx(-iMtime)">%s(zAge)</td> |
| 372 | @ <td>%d(nCkin)</td> |
| 373 | fossil_free(zAge); |
| 374 | @ <td>%s(isClosed?"closed":"")</td> |
| 375 | if( zMergeTo ){ |
| 376 | @ <td>merged into |
| 377 | @ %z(href("%R/timeline?f=%s",zLastCkin))%h(zMergeTo)</a></td> |
| 378 | }else{ |
| 379 | @ <td></td> |
| 380 | } |
| 381 | @ </tr> |
| 382 | } |
| 383 | @ </tbody></table></div> |
| 384 | db_finalize(&q); |
| 385 | output_table_sorting_javascript("branchlisttable","tkNtt",2); |
| 386 | style_footer(); |
| 387 | } |
| 388 | |
| 389 | /* |
| 390 | ** WEBPAGE: brlist |
| 391 | ** Show a list of branches |
| 392 | ** Query parameters: |
| 393 | ** |
| 394 | ** all Show all branches |
| 395 | ** closed Show only closed branches |
| 396 | ** open Show only open branches (default behavior) |
| 397 | ** colortest Show all branches with automatic color |
| 398 | */ |
| 399 | void brlist_page(void){ |
| 400 | Stmt q; |
| 401 | int cnt; |
| 402 | int showClosed = P("closed")!=0; |
| 403 | int showAll = P("all")!=0; |
| 404 | int showOpen = P("open")!=0; |
| 405 | int colorTest = P("colortest")!=0; |
| 406 | int brFlags = BRL_OPEN_ONLY; |
| 407 | |
| 408 | if( showClosed==0 && showAll==0 && showOpen==0 && colorTest==0 ){ |
| 409 | new_brlist_page(); |
| 410 | return; |
| 411 | } |
| 412 | login_check_credentials(); |
| 413 | if( !g.perm.Read ){ login_needed(); return; } |
| 414 | if( colorTest ){ |
| 415 | showClosed = 0; |
| 416 | showAll = 1; |
| 417 | } |
| 418 | if( showAll ) brFlags = BRL_BOTH; |
| 419 | if( showClosed ) brFlags = BRL_CLOSED_ONLY; |
| 420 | |
| 421 | style_header("%s", showClosed ? "Closed Branches" : |
| 422 | showAll ? "All Branches" : "Open Branches"); |
| 423 | style_submenu_element("Timeline", "Timeline", "brtimeline"); |
| 424 | if( showClosed ){ |
| 425 | style_submenu_element("All", "All", "brlist?all"); |
| 426 | style_submenu_element("Open","Open","brlist?open"); |
| 427 | }else if( showAll ){ |
| 428 | style_submenu_element("Closed", "Closed", "brlist?closed"); |
| 429 | style_submenu_element("Open","Open","brlist"); |
| 430 | }else{ |
| 431 | style_submenu_element("All", "All", "brlist?all"); |
| @@ -335,21 +451,21 @@ | |
| 451 | @ Closed branches are fixed and do not change (unless they are first |
| 452 | @ reopened).</li> |
| 453 | @ </ol> |
| 454 | style_sidebox_end(); |
| 455 | |
| 456 | branch_prepare_list_query(&q, brFlags); |
| 457 | cnt = 0; |
| 458 | while( db_step(&q)==SQLITE_ROW ){ |
| 459 | const char *zBr = db_column_text(&q, 0); |
| 460 | if( cnt==0 ){ |
| 461 | if( colorTest ){ |
| 462 | @ <h2>Default background colors for all branches:</h2> |
| 463 | }else if( showClosed ){ |
| 464 | @ <h2>Closed Branches:</h2> |
| 465 | }else if( showAll ){ |
| 466 | @ <h2>All Branches:</h2> |
| 467 | }else{ |
| 468 | @ <h2>Open Branches:</h2> |
| 469 | } |
| 470 | @ <ul> |
| 471 | cnt++; |
| 472 |
+10
-3
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -133,10 +133,11 @@ | ||
| 133 | 133 | if( strcmp(PD("type",""),"tree")==0 ){ page_tree(); return; } |
| 134 | 134 | login_check_credentials(); |
| 135 | 135 | if( !g.perm.Read ){ login_needed(); return; } |
| 136 | 136 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 137 | 137 | style_header("File List"); |
| 138 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 138 | 139 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 139 | 140 | pathelementFunc, 0, 0); |
| 140 | 141 | url_initialize(&sURI, "dir"); |
| 141 | 142 | |
| 142 | 143 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| @@ -555,10 +556,11 @@ | ||
| 555 | 556 | style_header("Folder Hierarchy"); |
| 556 | 557 | }else{ |
| 557 | 558 | showDirOnly = 0; |
| 558 | 559 | style_header("File Tree"); |
| 559 | 560 | } |
| 561 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 560 | 562 | if( P("expand")!=0 ){ |
| 561 | 563 | startExpanded = 1; |
| 562 | 564 | url_add_parameter(&sURI, "expand", "1"); |
| 563 | 565 | }else{ |
| 564 | 566 | startExpanded = 0; |
| @@ -635,10 +637,12 @@ | ||
| 635 | 637 | } |
| 636 | 638 | if( linkTip ){ |
| 637 | 639 | style_submenu_element("Tip", "Tip", "%s", |
| 638 | 640 | url_render(&sURI, "ci", "tip", 0, 0)); |
| 639 | 641 | } |
| 642 | + style_submenu_element("Flat-View", "Flat-View", "%s", | |
| 643 | + url_render(&sURI, "type", "flat", 0, 0)); | |
| 640 | 644 | |
| 641 | 645 | /* Compute the file hierarchy. |
| 642 | 646 | */ |
| 643 | 647 | if( zCI ){ |
| 644 | 648 | Stmt q; |
| @@ -652,10 +656,13 @@ | ||
| 652 | 656 | ); |
| 653 | 657 | while( db_step(&q)==SQLITE_ROW ){ |
| 654 | 658 | const char *zFile = db_column_text(&q,0); |
| 655 | 659 | const char *zUuid = db_column_text(&q,1); |
| 656 | 660 | double mtime = db_column_double(&q,2); |
| 661 | + if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){ | |
| 662 | + continue; | |
| 663 | + } | |
| 657 | 664 | if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; |
| 658 | 665 | tree_add_node(&sTree, zFile, zUuid, mtime); |
| 659 | 666 | nFile++; |
| 660 | 667 | } |
| 661 | 668 | db_finalize(&q); |
| @@ -931,11 +938,11 @@ | ||
| 931 | 938 | |
| 932 | 939 | /* |
| 933 | 940 | ** SQL used to compute the age of all files in checkin :ckin whose |
| 934 | 941 | ** names match :glob |
| 935 | 942 | */ |
| 936 | -static const char zComputeFileAgeSetup[] = | |
| 943 | +static const char zComputeFileAgeSetup[] = | |
| 937 | 944 | @ CREATE TABLE IF NOT EXISTS temp.fileage( |
| 938 | 945 | @ fnid INTEGER PRIMARY KEY, |
| 939 | 946 | @ fid INTEGER, |
| 940 | 947 | @ mid INTEGER, |
| 941 | 948 | @ mtime DATETIME, |
| @@ -942,11 +949,11 @@ | ||
| 942 | 949 | @ pathname TEXT |
| 943 | 950 | @ ); |
| 944 | 951 | @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; |
| 945 | 952 | ; |
| 946 | 953 | |
| 947 | -static const char zComputeFileAgeRun[] = | |
| 954 | +static const char zComputeFileAgeRun[] = | |
| 948 | 955 | @ WITH RECURSIVE |
| 949 | 956 | @ ckin(x) AS (VALUES(:ckin) UNION ALL |
| 950 | 957 | @ SELECT pid FROM ckin, plink WHERE cid=x AND isprim) |
| 951 | 958 | @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname) |
| 952 | 959 | @ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name |
| @@ -1068,11 +1075,11 @@ | ||
| 1068 | 1075 | style_header("File Ages"); |
| 1069 | 1076 | zGlob = P("glob"); |
| 1070 | 1077 | compute_fileage(rid,zGlob); |
| 1071 | 1078 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1072 | 1079 | |
| 1073 | - @ <h2>Files in | |
| 1080 | + @ <h2>Files in | |
| 1074 | 1081 | @ %z(href("%R/info?name=%T",zUuid))[%S(zUuid)]</a> |
| 1075 | 1082 | if( zGlob && zGlob[0] ){ |
| 1076 | 1083 | @ that match "%h(zGlob)" and |
| 1077 | 1084 | } |
| 1078 | 1085 | @ ordered by check-in time</h2> |
| 1079 | 1086 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -133,10 +133,11 @@ | |
| 133 | if( strcmp(PD("type",""),"tree")==0 ){ page_tree(); return; } |
| 134 | login_check_credentials(); |
| 135 | if( !g.perm.Read ){ login_needed(); return; } |
| 136 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 137 | style_header("File List"); |
| 138 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 139 | pathelementFunc, 0, 0); |
| 140 | url_initialize(&sURI, "dir"); |
| 141 | |
| 142 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| @@ -555,10 +556,11 @@ | |
| 555 | style_header("Folder Hierarchy"); |
| 556 | }else{ |
| 557 | showDirOnly = 0; |
| 558 | style_header("File Tree"); |
| 559 | } |
| 560 | if( P("expand")!=0 ){ |
| 561 | startExpanded = 1; |
| 562 | url_add_parameter(&sURI, "expand", "1"); |
| 563 | }else{ |
| 564 | startExpanded = 0; |
| @@ -635,10 +637,12 @@ | |
| 635 | } |
| 636 | if( linkTip ){ |
| 637 | style_submenu_element("Tip", "Tip", "%s", |
| 638 | url_render(&sURI, "ci", "tip", 0, 0)); |
| 639 | } |
| 640 | |
| 641 | /* Compute the file hierarchy. |
| 642 | */ |
| 643 | if( zCI ){ |
| 644 | Stmt q; |
| @@ -652,10 +656,13 @@ | |
| 652 | ); |
| 653 | while( db_step(&q)==SQLITE_ROW ){ |
| 654 | const char *zFile = db_column_text(&q,0); |
| 655 | const char *zUuid = db_column_text(&q,1); |
| 656 | double mtime = db_column_double(&q,2); |
| 657 | if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; |
| 658 | tree_add_node(&sTree, zFile, zUuid, mtime); |
| 659 | nFile++; |
| 660 | } |
| 661 | db_finalize(&q); |
| @@ -931,11 +938,11 @@ | |
| 931 | |
| 932 | /* |
| 933 | ** SQL used to compute the age of all files in checkin :ckin whose |
| 934 | ** names match :glob |
| 935 | */ |
| 936 | static const char zComputeFileAgeSetup[] = |
| 937 | @ CREATE TABLE IF NOT EXISTS temp.fileage( |
| 938 | @ fnid INTEGER PRIMARY KEY, |
| 939 | @ fid INTEGER, |
| 940 | @ mid INTEGER, |
| 941 | @ mtime DATETIME, |
| @@ -942,11 +949,11 @@ | |
| 942 | @ pathname TEXT |
| 943 | @ ); |
| 944 | @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; |
| 945 | ; |
| 946 | |
| 947 | static const char zComputeFileAgeRun[] = |
| 948 | @ WITH RECURSIVE |
| 949 | @ ckin(x) AS (VALUES(:ckin) UNION ALL |
| 950 | @ SELECT pid FROM ckin, plink WHERE cid=x AND isprim) |
| 951 | @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname) |
| 952 | @ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name |
| @@ -1068,11 +1075,11 @@ | |
| 1068 | style_header("File Ages"); |
| 1069 | zGlob = P("glob"); |
| 1070 | compute_fileage(rid,zGlob); |
| 1071 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1072 | |
| 1073 | @ <h2>Files in |
| 1074 | @ %z(href("%R/info?name=%T",zUuid))[%S(zUuid)]</a> |
| 1075 | if( zGlob && zGlob[0] ){ |
| 1076 | @ that match "%h(zGlob)" and |
| 1077 | } |
| 1078 | @ ordered by check-in time</h2> |
| 1079 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -133,10 +133,11 @@ | |
| 133 | if( strcmp(PD("type",""),"tree")==0 ){ page_tree(); return; } |
| 134 | login_check_credentials(); |
| 135 | if( !g.perm.Read ){ login_needed(); return; } |
| 136 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 137 | style_header("File List"); |
| 138 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 139 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 140 | pathelementFunc, 0, 0); |
| 141 | url_initialize(&sURI, "dir"); |
| 142 | |
| 143 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| @@ -555,10 +556,11 @@ | |
| 556 | style_header("Folder Hierarchy"); |
| 557 | }else{ |
| 558 | showDirOnly = 0; |
| 559 | style_header("File Tree"); |
| 560 | } |
| 561 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 562 | if( P("expand")!=0 ){ |
| 563 | startExpanded = 1; |
| 564 | url_add_parameter(&sURI, "expand", "1"); |
| 565 | }else{ |
| 566 | startExpanded = 0; |
| @@ -635,10 +637,12 @@ | |
| 637 | } |
| 638 | if( linkTip ){ |
| 639 | style_submenu_element("Tip", "Tip", "%s", |
| 640 | url_render(&sURI, "ci", "tip", 0, 0)); |
| 641 | } |
| 642 | style_submenu_element("Flat-View", "Flat-View", "%s", |
| 643 | url_render(&sURI, "type", "flat", 0, 0)); |
| 644 | |
| 645 | /* Compute the file hierarchy. |
| 646 | */ |
| 647 | if( zCI ){ |
| 648 | Stmt q; |
| @@ -652,10 +656,13 @@ | |
| 656 | ); |
| 657 | while( db_step(&q)==SQLITE_ROW ){ |
| 658 | const char *zFile = db_column_text(&q,0); |
| 659 | const char *zUuid = db_column_text(&q,1); |
| 660 | double mtime = db_column_double(&q,2); |
| 661 | if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){ |
| 662 | continue; |
| 663 | } |
| 664 | if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; |
| 665 | tree_add_node(&sTree, zFile, zUuid, mtime); |
| 666 | nFile++; |
| 667 | } |
| 668 | db_finalize(&q); |
| @@ -931,11 +938,11 @@ | |
| 938 | |
| 939 | /* |
| 940 | ** SQL used to compute the age of all files in checkin :ckin whose |
| 941 | ** names match :glob |
| 942 | */ |
| 943 | static const char zComputeFileAgeSetup[] = |
| 944 | @ CREATE TABLE IF NOT EXISTS temp.fileage( |
| 945 | @ fnid INTEGER PRIMARY KEY, |
| 946 | @ fid INTEGER, |
| 947 | @ mid INTEGER, |
| 948 | @ mtime DATETIME, |
| @@ -942,11 +949,11 @@ | |
| 949 | @ pathname TEXT |
| 950 | @ ); |
| 951 | @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; |
| 952 | ; |
| 953 | |
| 954 | static const char zComputeFileAgeRun[] = |
| 955 | @ WITH RECURSIVE |
| 956 | @ ckin(x) AS (VALUES(:ckin) UNION ALL |
| 957 | @ SELECT pid FROM ckin, plink WHERE cid=x AND isprim) |
| 958 | @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname) |
| 959 | @ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name |
| @@ -1068,11 +1075,11 @@ | |
| 1075 | style_header("File Ages"); |
| 1076 | zGlob = P("glob"); |
| 1077 | compute_fileage(rid,zGlob); |
| 1078 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1079 | |
| 1080 | @ <h2>Files in |
| 1081 | @ %z(href("%R/info?name=%T",zUuid))[%S(zUuid)]</a> |
| 1082 | if( zGlob && zGlob[0] ){ |
| 1083 | @ that match "%h(zGlob)" and |
| 1084 | } |
| 1085 | @ ordered by check-in time</h2> |
| 1086 |
+10
-3
| --- src/browse.c | ||
| +++ src/browse.c | ||
| @@ -133,10 +133,11 @@ | ||
| 133 | 133 | if( strcmp(PD("type",""),"tree")==0 ){ page_tree(); return; } |
| 134 | 134 | login_check_credentials(); |
| 135 | 135 | if( !g.perm.Read ){ login_needed(); return; } |
| 136 | 136 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 137 | 137 | style_header("File List"); |
| 138 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 138 | 139 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 139 | 140 | pathelementFunc, 0, 0); |
| 140 | 141 | url_initialize(&sURI, "dir"); |
| 141 | 142 | |
| 142 | 143 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| @@ -555,10 +556,11 @@ | ||
| 555 | 556 | style_header("Folder Hierarchy"); |
| 556 | 557 | }else{ |
| 557 | 558 | showDirOnly = 0; |
| 558 | 559 | style_header("File Tree"); |
| 559 | 560 | } |
| 561 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 560 | 562 | if( P("expand")!=0 ){ |
| 561 | 563 | startExpanded = 1; |
| 562 | 564 | url_add_parameter(&sURI, "expand", "1"); |
| 563 | 565 | }else{ |
| 564 | 566 | startExpanded = 0; |
| @@ -635,10 +637,12 @@ | ||
| 635 | 637 | } |
| 636 | 638 | if( linkTip ){ |
| 637 | 639 | style_submenu_element("Tip", "Tip", "%s", |
| 638 | 640 | url_render(&sURI, "ci", "tip", 0, 0)); |
| 639 | 641 | } |
| 642 | + style_submenu_element("Flat-View", "Flat-View", "%s", | |
| 643 | + url_render(&sURI, "type", "flat", 0, 0)); | |
| 640 | 644 | |
| 641 | 645 | /* Compute the file hierarchy. |
| 642 | 646 | */ |
| 643 | 647 | if( zCI ){ |
| 644 | 648 | Stmt q; |
| @@ -652,10 +656,13 @@ | ||
| 652 | 656 | ); |
| 653 | 657 | while( db_step(&q)==SQLITE_ROW ){ |
| 654 | 658 | const char *zFile = db_column_text(&q,0); |
| 655 | 659 | const char *zUuid = db_column_text(&q,1); |
| 656 | 660 | double mtime = db_column_double(&q,2); |
| 661 | + if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){ | |
| 662 | + continue; | |
| 663 | + } | |
| 657 | 664 | if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; |
| 658 | 665 | tree_add_node(&sTree, zFile, zUuid, mtime); |
| 659 | 666 | nFile++; |
| 660 | 667 | } |
| 661 | 668 | db_finalize(&q); |
| @@ -931,11 +938,11 @@ | ||
| 931 | 938 | |
| 932 | 939 | /* |
| 933 | 940 | ** SQL used to compute the age of all files in checkin :ckin whose |
| 934 | 941 | ** names match :glob |
| 935 | 942 | */ |
| 936 | -static const char zComputeFileAgeSetup[] = | |
| 943 | +static const char zComputeFileAgeSetup[] = | |
| 937 | 944 | @ CREATE TABLE IF NOT EXISTS temp.fileage( |
| 938 | 945 | @ fnid INTEGER PRIMARY KEY, |
| 939 | 946 | @ fid INTEGER, |
| 940 | 947 | @ mid INTEGER, |
| 941 | 948 | @ mtime DATETIME, |
| @@ -942,11 +949,11 @@ | ||
| 942 | 949 | @ pathname TEXT |
| 943 | 950 | @ ); |
| 944 | 951 | @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; |
| 945 | 952 | ; |
| 946 | 953 | |
| 947 | -static const char zComputeFileAgeRun[] = | |
| 954 | +static const char zComputeFileAgeRun[] = | |
| 948 | 955 | @ WITH RECURSIVE |
| 949 | 956 | @ ckin(x) AS (VALUES(:ckin) UNION ALL |
| 950 | 957 | @ SELECT pid FROM ckin, plink WHERE cid=x AND isprim) |
| 951 | 958 | @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname) |
| 952 | 959 | @ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name |
| @@ -1068,11 +1075,11 @@ | ||
| 1068 | 1075 | style_header("File Ages"); |
| 1069 | 1076 | zGlob = P("glob"); |
| 1070 | 1077 | compute_fileage(rid,zGlob); |
| 1071 | 1078 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1072 | 1079 | |
| 1073 | - @ <h2>Files in | |
| 1080 | + @ <h2>Files in | |
| 1074 | 1081 | @ %z(href("%R/info?name=%T",zUuid))[%S(zUuid)]</a> |
| 1075 | 1082 | if( zGlob && zGlob[0] ){ |
| 1076 | 1083 | @ that match "%h(zGlob)" and |
| 1077 | 1084 | } |
| 1078 | 1085 | @ ordered by check-in time</h2> |
| 1079 | 1086 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -133,10 +133,11 @@ | |
| 133 | if( strcmp(PD("type",""),"tree")==0 ){ page_tree(); return; } |
| 134 | login_check_credentials(); |
| 135 | if( !g.perm.Read ){ login_needed(); return; } |
| 136 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 137 | style_header("File List"); |
| 138 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 139 | pathelementFunc, 0, 0); |
| 140 | url_initialize(&sURI, "dir"); |
| 141 | |
| 142 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| @@ -555,10 +556,11 @@ | |
| 555 | style_header("Folder Hierarchy"); |
| 556 | }else{ |
| 557 | showDirOnly = 0; |
| 558 | style_header("File Tree"); |
| 559 | } |
| 560 | if( P("expand")!=0 ){ |
| 561 | startExpanded = 1; |
| 562 | url_add_parameter(&sURI, "expand", "1"); |
| 563 | }else{ |
| 564 | startExpanded = 0; |
| @@ -635,10 +637,12 @@ | |
| 635 | } |
| 636 | if( linkTip ){ |
| 637 | style_submenu_element("Tip", "Tip", "%s", |
| 638 | url_render(&sURI, "ci", "tip", 0, 0)); |
| 639 | } |
| 640 | |
| 641 | /* Compute the file hierarchy. |
| 642 | */ |
| 643 | if( zCI ){ |
| 644 | Stmt q; |
| @@ -652,10 +656,13 @@ | |
| 652 | ); |
| 653 | while( db_step(&q)==SQLITE_ROW ){ |
| 654 | const char *zFile = db_column_text(&q,0); |
| 655 | const char *zUuid = db_column_text(&q,1); |
| 656 | double mtime = db_column_double(&q,2); |
| 657 | if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; |
| 658 | tree_add_node(&sTree, zFile, zUuid, mtime); |
| 659 | nFile++; |
| 660 | } |
| 661 | db_finalize(&q); |
| @@ -931,11 +938,11 @@ | |
| 931 | |
| 932 | /* |
| 933 | ** SQL used to compute the age of all files in checkin :ckin whose |
| 934 | ** names match :glob |
| 935 | */ |
| 936 | static const char zComputeFileAgeSetup[] = |
| 937 | @ CREATE TABLE IF NOT EXISTS temp.fileage( |
| 938 | @ fnid INTEGER PRIMARY KEY, |
| 939 | @ fid INTEGER, |
| 940 | @ mid INTEGER, |
| 941 | @ mtime DATETIME, |
| @@ -942,11 +949,11 @@ | |
| 942 | @ pathname TEXT |
| 943 | @ ); |
| 944 | @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; |
| 945 | ; |
| 946 | |
| 947 | static const char zComputeFileAgeRun[] = |
| 948 | @ WITH RECURSIVE |
| 949 | @ ckin(x) AS (VALUES(:ckin) UNION ALL |
| 950 | @ SELECT pid FROM ckin, plink WHERE cid=x AND isprim) |
| 951 | @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname) |
| 952 | @ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name |
| @@ -1068,11 +1075,11 @@ | |
| 1068 | style_header("File Ages"); |
| 1069 | zGlob = P("glob"); |
| 1070 | compute_fileage(rid,zGlob); |
| 1071 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1072 | |
| 1073 | @ <h2>Files in |
| 1074 | @ %z(href("%R/info?name=%T",zUuid))[%S(zUuid)]</a> |
| 1075 | if( zGlob && zGlob[0] ){ |
| 1076 | @ that match "%h(zGlob)" and |
| 1077 | } |
| 1078 | @ ordered by check-in time</h2> |
| 1079 |
| --- src/browse.c | |
| +++ src/browse.c | |
| @@ -133,10 +133,11 @@ | |
| 133 | if( strcmp(PD("type",""),"tree")==0 ){ page_tree(); return; } |
| 134 | login_check_credentials(); |
| 135 | if( !g.perm.Read ){ login_needed(); return; } |
| 136 | while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; } |
| 137 | style_header("File List"); |
| 138 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 139 | sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0, |
| 140 | pathelementFunc, 0, 0); |
| 141 | url_initialize(&sURI, "dir"); |
| 142 | |
| 143 | /* If the name= parameter is an empty string, make it a NULL pointer */ |
| @@ -555,10 +556,11 @@ | |
| 556 | style_header("Folder Hierarchy"); |
| 557 | }else{ |
| 558 | showDirOnly = 0; |
| 559 | style_header("File Tree"); |
| 560 | } |
| 561 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 562 | if( P("expand")!=0 ){ |
| 563 | startExpanded = 1; |
| 564 | url_add_parameter(&sURI, "expand", "1"); |
| 565 | }else{ |
| 566 | startExpanded = 0; |
| @@ -635,10 +637,12 @@ | |
| 637 | } |
| 638 | if( linkTip ){ |
| 639 | style_submenu_element("Tip", "Tip", "%s", |
| 640 | url_render(&sURI, "ci", "tip", 0, 0)); |
| 641 | } |
| 642 | style_submenu_element("Flat-View", "Flat-View", "%s", |
| 643 | url_render(&sURI, "type", "flat", 0, 0)); |
| 644 | |
| 645 | /* Compute the file hierarchy. |
| 646 | */ |
| 647 | if( zCI ){ |
| 648 | Stmt q; |
| @@ -652,10 +656,13 @@ | |
| 656 | ); |
| 657 | while( db_step(&q)==SQLITE_ROW ){ |
| 658 | const char *zFile = db_column_text(&q,0); |
| 659 | const char *zUuid = db_column_text(&q,1); |
| 660 | double mtime = db_column_double(&q,2); |
| 661 | if( nD>0 && (fossil_strncmp(zFile, zD, nD-1)!=0 || zFile[nD-1]!='/') ){ |
| 662 | continue; |
| 663 | } |
| 664 | if( pRE && re_match(pRE, (const unsigned char*)zFile, -1)==0 ) continue; |
| 665 | tree_add_node(&sTree, zFile, zUuid, mtime); |
| 666 | nFile++; |
| 667 | } |
| 668 | db_finalize(&q); |
| @@ -931,11 +938,11 @@ | |
| 938 | |
| 939 | /* |
| 940 | ** SQL used to compute the age of all files in checkin :ckin whose |
| 941 | ** names match :glob |
| 942 | */ |
| 943 | static const char zComputeFileAgeSetup[] = |
| 944 | @ CREATE TABLE IF NOT EXISTS temp.fileage( |
| 945 | @ fnid INTEGER PRIMARY KEY, |
| 946 | @ fid INTEGER, |
| 947 | @ mid INTEGER, |
| 948 | @ mtime DATETIME, |
| @@ -942,11 +949,11 @@ | |
| 949 | @ pathname TEXT |
| 950 | @ ); |
| 951 | @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; |
| 952 | ; |
| 953 | |
| 954 | static const char zComputeFileAgeRun[] = |
| 955 | @ WITH RECURSIVE |
| 956 | @ ckin(x) AS (VALUES(:ckin) UNION ALL |
| 957 | @ SELECT pid FROM ckin, plink WHERE cid=x AND isprim) |
| 958 | @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname) |
| 959 | @ SELECT mlink.fnid, mlink.fid, x, event.mtime, filename.name |
| @@ -1068,11 +1075,11 @@ | |
| 1075 | style_header("File Ages"); |
| 1076 | zGlob = P("glob"); |
| 1077 | compute_fileage(rid,zGlob); |
| 1078 | db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);"); |
| 1079 | |
| 1080 | @ <h2>Files in |
| 1081 | @ %z(href("%R/info?name=%T",zUuid))[%S(zUuid)]</a> |
| 1082 | if( zGlob && zGlob[0] ){ |
| 1083 | @ that match "%h(zGlob)" and |
| 1084 | } |
| 1085 | @ ordered by check-in time</h2> |
| 1086 |
+10
-2
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -31,22 +31,30 @@ | ||
| 31 | 31 | |
| 32 | 32 | /* |
| 33 | 33 | ** Return a pointer to built-in content |
| 34 | 34 | */ |
| 35 | 35 | const unsigned char *builtin_file(const char *zFilename, int *piSize){ |
| 36 | - int lwr, upr, i; | |
| 36 | + int lwr, upr, i, c; | |
| 37 | 37 | lwr = 0; |
| 38 | 38 | upr = sizeof(aBuiltinFiles)/sizeof(aBuiltinFiles[0]) - 1; |
| 39 | 39 | while( upr>=lwr ){ |
| 40 | 40 | i = (upr+lwr)/2; |
| 41 | - if( strcmp(aBuiltinFiles[i].zName,zFilename)==0 ){ | |
| 41 | + c = strcmp(aBuiltinFiles[i].zName,zFilename); | |
| 42 | + if( c<0 ){ | |
| 43 | + lwr = i+1; | |
| 44 | + }else if( c>0 ){ | |
| 45 | + upr = i-1; | |
| 46 | + }else{ | |
| 42 | 47 | if( piSize ) *piSize = aBuiltinFiles[i].nByte; |
| 43 | 48 | return aBuiltinFiles[i].pData; |
| 44 | 49 | } |
| 45 | 50 | } |
| 46 | 51 | if( piSize ) *piSize = 0; |
| 47 | 52 | return 0; |
| 53 | +} | |
| 54 | +const char *builtin_text(const char *zFilename){ | |
| 55 | + return (char*)builtin_file(zFilename, 0); | |
| 48 | 56 | } |
| 49 | 57 | |
| 50 | 58 | /* |
| 51 | 59 | ** COMMAND: test-builtin-list |
| 52 | 60 | ** |
| 53 | 61 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -31,22 +31,30 @@ | |
| 31 | |
| 32 | /* |
| 33 | ** Return a pointer to built-in content |
| 34 | */ |
| 35 | const unsigned char *builtin_file(const char *zFilename, int *piSize){ |
| 36 | int lwr, upr, i; |
| 37 | lwr = 0; |
| 38 | upr = sizeof(aBuiltinFiles)/sizeof(aBuiltinFiles[0]) - 1; |
| 39 | while( upr>=lwr ){ |
| 40 | i = (upr+lwr)/2; |
| 41 | if( strcmp(aBuiltinFiles[i].zName,zFilename)==0 ){ |
| 42 | if( piSize ) *piSize = aBuiltinFiles[i].nByte; |
| 43 | return aBuiltinFiles[i].pData; |
| 44 | } |
| 45 | } |
| 46 | if( piSize ) *piSize = 0; |
| 47 | return 0; |
| 48 | } |
| 49 | |
| 50 | /* |
| 51 | ** COMMAND: test-builtin-list |
| 52 | ** |
| 53 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -31,22 +31,30 @@ | |
| 31 | |
| 32 | /* |
| 33 | ** Return a pointer to built-in content |
| 34 | */ |
| 35 | const unsigned char *builtin_file(const char *zFilename, int *piSize){ |
| 36 | int lwr, upr, i, c; |
| 37 | lwr = 0; |
| 38 | upr = sizeof(aBuiltinFiles)/sizeof(aBuiltinFiles[0]) - 1; |
| 39 | while( upr>=lwr ){ |
| 40 | i = (upr+lwr)/2; |
| 41 | c = strcmp(aBuiltinFiles[i].zName,zFilename); |
| 42 | if( c<0 ){ |
| 43 | lwr = i+1; |
| 44 | }else if( c>0 ){ |
| 45 | upr = i-1; |
| 46 | }else{ |
| 47 | if( piSize ) *piSize = aBuiltinFiles[i].nByte; |
| 48 | return aBuiltinFiles[i].pData; |
| 49 | } |
| 50 | } |
| 51 | if( piSize ) *piSize = 0; |
| 52 | return 0; |
| 53 | } |
| 54 | const char *builtin_text(const char *zFilename){ |
| 55 | return (char*)builtin_file(zFilename, 0); |
| 56 | } |
| 57 | |
| 58 | /* |
| 59 | ** COMMAND: test-builtin-list |
| 60 | ** |
| 61 |
+4
-1
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -108,10 +108,13 @@ | ||
| 108 | 108 | ** if it does and false if it does not. |
| 109 | 109 | */ |
| 110 | 110 | int cgi_header_contains(const char *zNeedle){ |
| 111 | 111 | return strstr(blob_str(&cgiContent[0]), zNeedle)!=0; |
| 112 | 112 | } |
| 113 | +int cgi_body_contains(const char *zNeedle){ | |
| 114 | + return strstr(blob_str(&cgiContent[1]), zNeedle)!=0; | |
| 115 | +} | |
| 113 | 116 | |
| 114 | 117 | /* |
| 115 | 118 | ** Append reply content to what already exists. |
| 116 | 119 | */ |
| 117 | 120 | void cgi_append_content(const char *zData, int nAmt){ |
| @@ -1753,11 +1756,11 @@ | ||
| 1753 | 1756 | fd = dup(connection); |
| 1754 | 1757 | if( fd!=0 ) nErr++; |
| 1755 | 1758 | close(1); |
| 1756 | 1759 | fd = dup(connection); |
| 1757 | 1760 | if( fd!=1 ) nErr++; |
| 1758 | - if( !g.fHttpTrace && !g.fSqlTrace ){ | |
| 1761 | + if( !g.fAnyTrace ){ | |
| 1759 | 1762 | close(2); |
| 1760 | 1763 | fd = dup(connection); |
| 1761 | 1764 | if( fd!=2 ) nErr++; |
| 1762 | 1765 | } |
| 1763 | 1766 | close(connection); |
| 1764 | 1767 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -108,10 +108,13 @@ | |
| 108 | ** if it does and false if it does not. |
| 109 | */ |
| 110 | int cgi_header_contains(const char *zNeedle){ |
| 111 | return strstr(blob_str(&cgiContent[0]), zNeedle)!=0; |
| 112 | } |
| 113 | |
| 114 | /* |
| 115 | ** Append reply content to what already exists. |
| 116 | */ |
| 117 | void cgi_append_content(const char *zData, int nAmt){ |
| @@ -1753,11 +1756,11 @@ | |
| 1753 | fd = dup(connection); |
| 1754 | if( fd!=0 ) nErr++; |
| 1755 | close(1); |
| 1756 | fd = dup(connection); |
| 1757 | if( fd!=1 ) nErr++; |
| 1758 | if( !g.fHttpTrace && !g.fSqlTrace ){ |
| 1759 | close(2); |
| 1760 | fd = dup(connection); |
| 1761 | if( fd!=2 ) nErr++; |
| 1762 | } |
| 1763 | close(connection); |
| 1764 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -108,10 +108,13 @@ | |
| 108 | ** if it does and false if it does not. |
| 109 | */ |
| 110 | int cgi_header_contains(const char *zNeedle){ |
| 111 | return strstr(blob_str(&cgiContent[0]), zNeedle)!=0; |
| 112 | } |
| 113 | int cgi_body_contains(const char *zNeedle){ |
| 114 | return strstr(blob_str(&cgiContent[1]), zNeedle)!=0; |
| 115 | } |
| 116 | |
| 117 | /* |
| 118 | ** Append reply content to what already exists. |
| 119 | */ |
| 120 | void cgi_append_content(const char *zData, int nAmt){ |
| @@ -1753,11 +1756,11 @@ | |
| 1756 | fd = dup(connection); |
| 1757 | if( fd!=0 ) nErr++; |
| 1758 | close(1); |
| 1759 | fd = dup(connection); |
| 1760 | if( fd!=1 ) nErr++; |
| 1761 | if( !g.fAnyTrace ){ |
| 1762 | close(2); |
| 1763 | fd = dup(connection); |
| 1764 | if( fd!=2 ) nErr++; |
| 1765 | } |
| 1766 | close(connection); |
| 1767 |
+13
-5
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -213,11 +213,11 @@ | ||
| 213 | 213 | int showHdr = find_option("header",0,0)!=0; |
| 214 | 214 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 215 | 215 | int cwdRelative = 0; |
| 216 | 216 | db_must_be_within_tree(); |
| 217 | 217 | cwdRelative = determine_cwd_relative_option(); |
| 218 | - | |
| 218 | + | |
| 219 | 219 | /* We should be done with options.. */ |
| 220 | 220 | verify_all_options(); |
| 221 | 221 | |
| 222 | 222 | print_changes(useSha1sum, showHdr, verboseFlag, cwdRelative); |
| 223 | 223 | } |
| @@ -249,11 +249,11 @@ | ||
| 249 | 249 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 250 | 250 | int cwdRelative = 0; |
| 251 | 251 | db_must_be_within_tree(); |
| 252 | 252 | /* 012345678901234 */ |
| 253 | 253 | cwdRelative = determine_cwd_relative_option(); |
| 254 | - | |
| 254 | + | |
| 255 | 255 | /* We should be done with options.. */ |
| 256 | 256 | verify_all_options(); |
| 257 | 257 | |
| 258 | 258 | fossil_print("repository: %s\n", db_repository_filename()); |
| 259 | 259 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| @@ -262,11 +262,11 @@ | ||
| 262 | 262 | } |
| 263 | 263 | vid = db_lget_int("checkout", 0); |
| 264 | 264 | if( vid ){ |
| 265 | 265 | show_common_info(vid, "checkout:", 1, 1); |
| 266 | 266 | } |
| 267 | - db_record_repository_filename(0); | |
| 267 | + db_record_repository_filename(0); | |
| 268 | 268 | print_changes(useSha1sum, showHdr, verboseFlag, cwdRelative); |
| 269 | 269 | } |
| 270 | 270 | |
| 271 | 271 | /* |
| 272 | 272 | ** COMMAND: ls |
| @@ -480,11 +480,11 @@ | ||
| 480 | 480 | const char *zPathname, *zDisplayName; |
| 481 | 481 | |
| 482 | 482 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 483 | 483 | db_must_be_within_tree(); |
| 484 | 484 | cwdRelative = determine_cwd_relative_option(); |
| 485 | - | |
| 485 | + | |
| 486 | 486 | /* We should be done with options.. */ |
| 487 | 487 | verify_all_options(); |
| 488 | 488 | |
| 489 | 489 | if( zIgnoreFlag==0 ){ |
| 490 | 490 | zIgnoreFlag = db_get("ignore-glob", 0); |
| @@ -498,10 +498,11 @@ | ||
| 498 | 498 | " ORDER BY 1", |
| 499 | 499 | fossil_all_reserved_names(0) |
| 500 | 500 | ); |
| 501 | 501 | db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); |
| 502 | 502 | blob_zero(&rewrittenPathname); |
| 503 | + g.allowSymlinks = 1; /* Report on symbolic links */ | |
| 503 | 504 | while( db_step(&q)==SQLITE_ROW ){ |
| 504 | 505 | zDisplayName = zPathname = db_column_text(&q, 0); |
| 505 | 506 | if( cwdRelative ) { |
| 506 | 507 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 507 | 508 | file_relative_name(zFullName, &rewrittenPathname, 0); |
| @@ -563,10 +564,11 @@ | ||
| 563 | 564 | ** argument. Matching files, if any, are removed |
| 564 | 565 | ** prior to checking for any empty directories; |
| 565 | 566 | ** therefore, directories that contain only files |
| 566 | 567 | ** that were removed will be removed as well. |
| 567 | 568 | ** -f|--force Remove files without prompting. |
| 569 | +** --verily Shorthand for: -f --emptydirs --dotfiles | |
| 568 | 570 | ** --clean <CSG> Never prompt for files matching this |
| 569 | 571 | ** comma separated list of glob patterns. |
| 570 | 572 | ** --ignore <CSG> Ignore files matching patterns from the |
| 571 | 573 | ** comma separated list of glob patterns. |
| 572 | 574 | ** --keep <CSG> Keep files matching this comma separated |
| @@ -601,10 +603,15 @@ | ||
| 601 | 603 | zIgnoreFlag = find_option("ignore",0,1); |
| 602 | 604 | verboseFlag = find_option("verbose","v",0)!=0; |
| 603 | 605 | zKeepFlag = find_option("keep",0,1); |
| 604 | 606 | zCleanFlag = find_option("clean",0,1); |
| 605 | 607 | db_must_be_within_tree(); |
| 608 | + if( find_option("verily",0,0)!=0 ){ | |
| 609 | + allFileFlag = allDirFlag = 1; | |
| 610 | + emptyDirsFlag = 1; | |
| 611 | + scanFlags |= SCAN_ALL; | |
| 612 | + } | |
| 606 | 613 | if( zIgnoreFlag==0 ){ |
| 607 | 614 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 608 | 615 | } |
| 609 | 616 | if( zKeepFlag==0 ){ |
| 610 | 617 | zKeepFlag = db_get("keep-glob", 0); |
| @@ -615,10 +622,11 @@ | ||
| 615 | 622 | verify_all_options(); |
| 616 | 623 | pIgnore = glob_create(zIgnoreFlag); |
| 617 | 624 | pKeep = glob_create(zKeepFlag); |
| 618 | 625 | pClean = glob_create(zCleanFlag); |
| 619 | 626 | nRoot = (int)strlen(g.zLocalRoot); |
| 627 | + g.allowSymlinks = 1; /* Find symlinks too */ | |
| 620 | 628 | if( !dirsOnlyFlag ){ |
| 621 | 629 | Stmt q; |
| 622 | 630 | Blob repo; |
| 623 | 631 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); |
| 624 | 632 | db_prepare(&q, |
| @@ -1720,11 +1728,11 @@ | ||
| 1720 | 1728 | |
| 1721 | 1729 | /* |
| 1722 | 1730 | ** Do not allow a commit against a closed leaf unless the commit |
| 1723 | 1731 | ** ends up on a different branch. |
| 1724 | 1732 | */ |
| 1725 | - if( | |
| 1733 | + if( | |
| 1726 | 1734 | /* parent checkin has the "closed" tag... */ |
| 1727 | 1735 | db_exists("SELECT 1 FROM tagxref" |
| 1728 | 1736 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 1729 | 1737 | TAG_CLOSED, vid) |
| 1730 | 1738 | /* ... and the new checkin has no --branch option or the --branch |
| 1731 | 1739 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -213,11 +213,11 @@ | |
| 213 | int showHdr = find_option("header",0,0)!=0; |
| 214 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 215 | int cwdRelative = 0; |
| 216 | db_must_be_within_tree(); |
| 217 | cwdRelative = determine_cwd_relative_option(); |
| 218 | |
| 219 | /* We should be done with options.. */ |
| 220 | verify_all_options(); |
| 221 | |
| 222 | print_changes(useSha1sum, showHdr, verboseFlag, cwdRelative); |
| 223 | } |
| @@ -249,11 +249,11 @@ | |
| 249 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 250 | int cwdRelative = 0; |
| 251 | db_must_be_within_tree(); |
| 252 | /* 012345678901234 */ |
| 253 | cwdRelative = determine_cwd_relative_option(); |
| 254 | |
| 255 | /* We should be done with options.. */ |
| 256 | verify_all_options(); |
| 257 | |
| 258 | fossil_print("repository: %s\n", db_repository_filename()); |
| 259 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| @@ -262,11 +262,11 @@ | |
| 262 | } |
| 263 | vid = db_lget_int("checkout", 0); |
| 264 | if( vid ){ |
| 265 | show_common_info(vid, "checkout:", 1, 1); |
| 266 | } |
| 267 | db_record_repository_filename(0); |
| 268 | print_changes(useSha1sum, showHdr, verboseFlag, cwdRelative); |
| 269 | } |
| 270 | |
| 271 | /* |
| 272 | ** COMMAND: ls |
| @@ -480,11 +480,11 @@ | |
| 480 | const char *zPathname, *zDisplayName; |
| 481 | |
| 482 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 483 | db_must_be_within_tree(); |
| 484 | cwdRelative = determine_cwd_relative_option(); |
| 485 | |
| 486 | /* We should be done with options.. */ |
| 487 | verify_all_options(); |
| 488 | |
| 489 | if( zIgnoreFlag==0 ){ |
| 490 | zIgnoreFlag = db_get("ignore-glob", 0); |
| @@ -498,10 +498,11 @@ | |
| 498 | " ORDER BY 1", |
| 499 | fossil_all_reserved_names(0) |
| 500 | ); |
| 501 | db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); |
| 502 | blob_zero(&rewrittenPathname); |
| 503 | while( db_step(&q)==SQLITE_ROW ){ |
| 504 | zDisplayName = zPathname = db_column_text(&q, 0); |
| 505 | if( cwdRelative ) { |
| 506 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 507 | file_relative_name(zFullName, &rewrittenPathname, 0); |
| @@ -563,10 +564,11 @@ | |
| 563 | ** argument. Matching files, if any, are removed |
| 564 | ** prior to checking for any empty directories; |
| 565 | ** therefore, directories that contain only files |
| 566 | ** that were removed will be removed as well. |
| 567 | ** -f|--force Remove files without prompting. |
| 568 | ** --clean <CSG> Never prompt for files matching this |
| 569 | ** comma separated list of glob patterns. |
| 570 | ** --ignore <CSG> Ignore files matching patterns from the |
| 571 | ** comma separated list of glob patterns. |
| 572 | ** --keep <CSG> Keep files matching this comma separated |
| @@ -601,10 +603,15 @@ | |
| 601 | zIgnoreFlag = find_option("ignore",0,1); |
| 602 | verboseFlag = find_option("verbose","v",0)!=0; |
| 603 | zKeepFlag = find_option("keep",0,1); |
| 604 | zCleanFlag = find_option("clean",0,1); |
| 605 | db_must_be_within_tree(); |
| 606 | if( zIgnoreFlag==0 ){ |
| 607 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 608 | } |
| 609 | if( zKeepFlag==0 ){ |
| 610 | zKeepFlag = db_get("keep-glob", 0); |
| @@ -615,10 +622,11 @@ | |
| 615 | verify_all_options(); |
| 616 | pIgnore = glob_create(zIgnoreFlag); |
| 617 | pKeep = glob_create(zKeepFlag); |
| 618 | pClean = glob_create(zCleanFlag); |
| 619 | nRoot = (int)strlen(g.zLocalRoot); |
| 620 | if( !dirsOnlyFlag ){ |
| 621 | Stmt q; |
| 622 | Blob repo; |
| 623 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); |
| 624 | db_prepare(&q, |
| @@ -1720,11 +1728,11 @@ | |
| 1720 | |
| 1721 | /* |
| 1722 | ** Do not allow a commit against a closed leaf unless the commit |
| 1723 | ** ends up on a different branch. |
| 1724 | */ |
| 1725 | if( |
| 1726 | /* parent checkin has the "closed" tag... */ |
| 1727 | db_exists("SELECT 1 FROM tagxref" |
| 1728 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 1729 | TAG_CLOSED, vid) |
| 1730 | /* ... and the new checkin has no --branch option or the --branch |
| 1731 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -213,11 +213,11 @@ | |
| 213 | int showHdr = find_option("header",0,0)!=0; |
| 214 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 215 | int cwdRelative = 0; |
| 216 | db_must_be_within_tree(); |
| 217 | cwdRelative = determine_cwd_relative_option(); |
| 218 | |
| 219 | /* We should be done with options.. */ |
| 220 | verify_all_options(); |
| 221 | |
| 222 | print_changes(useSha1sum, showHdr, verboseFlag, cwdRelative); |
| 223 | } |
| @@ -249,11 +249,11 @@ | |
| 249 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 250 | int cwdRelative = 0; |
| 251 | db_must_be_within_tree(); |
| 252 | /* 012345678901234 */ |
| 253 | cwdRelative = determine_cwd_relative_option(); |
| 254 | |
| 255 | /* We should be done with options.. */ |
| 256 | verify_all_options(); |
| 257 | |
| 258 | fossil_print("repository: %s\n", db_repository_filename()); |
| 259 | fossil_print("local-root: %s\n", g.zLocalRoot); |
| @@ -262,11 +262,11 @@ | |
| 262 | } |
| 263 | vid = db_lget_int("checkout", 0); |
| 264 | if( vid ){ |
| 265 | show_common_info(vid, "checkout:", 1, 1); |
| 266 | } |
| 267 | db_record_repository_filename(0); |
| 268 | print_changes(useSha1sum, showHdr, verboseFlag, cwdRelative); |
| 269 | } |
| 270 | |
| 271 | /* |
| 272 | ** COMMAND: ls |
| @@ -480,11 +480,11 @@ | |
| 480 | const char *zPathname, *zDisplayName; |
| 481 | |
| 482 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 483 | db_must_be_within_tree(); |
| 484 | cwdRelative = determine_cwd_relative_option(); |
| 485 | |
| 486 | /* We should be done with options.. */ |
| 487 | verify_all_options(); |
| 488 | |
| 489 | if( zIgnoreFlag==0 ){ |
| 490 | zIgnoreFlag = db_get("ignore-glob", 0); |
| @@ -498,10 +498,11 @@ | |
| 498 | " ORDER BY 1", |
| 499 | fossil_all_reserved_names(0) |
| 500 | ); |
| 501 | db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)"); |
| 502 | blob_zero(&rewrittenPathname); |
| 503 | g.allowSymlinks = 1; /* Report on symbolic links */ |
| 504 | while( db_step(&q)==SQLITE_ROW ){ |
| 505 | zDisplayName = zPathname = db_column_text(&q, 0); |
| 506 | if( cwdRelative ) { |
| 507 | char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); |
| 508 | file_relative_name(zFullName, &rewrittenPathname, 0); |
| @@ -563,10 +564,11 @@ | |
| 564 | ** argument. Matching files, if any, are removed |
| 565 | ** prior to checking for any empty directories; |
| 566 | ** therefore, directories that contain only files |
| 567 | ** that were removed will be removed as well. |
| 568 | ** -f|--force Remove files without prompting. |
| 569 | ** --verily Shorthand for: -f --emptydirs --dotfiles |
| 570 | ** --clean <CSG> Never prompt for files matching this |
| 571 | ** comma separated list of glob patterns. |
| 572 | ** --ignore <CSG> Ignore files matching patterns from the |
| 573 | ** comma separated list of glob patterns. |
| 574 | ** --keep <CSG> Keep files matching this comma separated |
| @@ -601,10 +603,15 @@ | |
| 603 | zIgnoreFlag = find_option("ignore",0,1); |
| 604 | verboseFlag = find_option("verbose","v",0)!=0; |
| 605 | zKeepFlag = find_option("keep",0,1); |
| 606 | zCleanFlag = find_option("clean",0,1); |
| 607 | db_must_be_within_tree(); |
| 608 | if( find_option("verily",0,0)!=0 ){ |
| 609 | allFileFlag = allDirFlag = 1; |
| 610 | emptyDirsFlag = 1; |
| 611 | scanFlags |= SCAN_ALL; |
| 612 | } |
| 613 | if( zIgnoreFlag==0 ){ |
| 614 | zIgnoreFlag = db_get("ignore-glob", 0); |
| 615 | } |
| 616 | if( zKeepFlag==0 ){ |
| 617 | zKeepFlag = db_get("keep-glob", 0); |
| @@ -615,10 +622,11 @@ | |
| 622 | verify_all_options(); |
| 623 | pIgnore = glob_create(zIgnoreFlag); |
| 624 | pKeep = glob_create(zKeepFlag); |
| 625 | pClean = glob_create(zCleanFlag); |
| 626 | nRoot = (int)strlen(g.zLocalRoot); |
| 627 | g.allowSymlinks = 1; /* Find symlinks too */ |
| 628 | if( !dirsOnlyFlag ){ |
| 629 | Stmt q; |
| 630 | Blob repo; |
| 631 | locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, 0); |
| 632 | db_prepare(&q, |
| @@ -1720,11 +1728,11 @@ | |
| 1728 | |
| 1729 | /* |
| 1730 | ** Do not allow a commit against a closed leaf unless the commit |
| 1731 | ** ends up on a different branch. |
| 1732 | */ |
| 1733 | if( |
| 1734 | /* parent checkin has the "closed" tag... */ |
| 1735 | db_exists("SELECT 1 FROM tagxref" |
| 1736 | " WHERE tagid=%d AND rid=%d AND tagtype>0", |
| 1737 | TAG_CLOSED, vid) |
| 1738 | /* ... and the new checkin has no --branch option or the --branch |
| 1739 |
+14
-11
| --- src/clone.c | ||
| +++ src/clone.c | ||
| @@ -83,11 +83,11 @@ | ||
| 83 | 83 | ** COMMAND: clone |
| 84 | 84 | ** |
| 85 | 85 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 86 | 86 | ** |
| 87 | 87 | ** Make a clone of a repository specified by URL in the local |
| 88 | -** file named FILENAME. | |
| 88 | +** file named FILENAME. | |
| 89 | 89 | ** |
| 90 | 90 | ** URL must be in one of the following form: ([...] mean optional) |
| 91 | 91 | ** HTTP/HTTPS protocol: |
| 92 | 92 | ** http[s]://[userid[:password]@]host[:port][/path] |
| 93 | 93 | ** |
| @@ -96,50 +96,53 @@ | ||
| 96 | 96 | ** [?fossil=path/to/fossil.exe] |
| 97 | 97 | ** |
| 98 | 98 | ** Filesystem: |
| 99 | 99 | ** [file://]path/to/repo.fossil |
| 100 | 100 | ** |
| 101 | -** Note: For ssh and filesystem, path must have an extra leading | |
| 101 | +** Note: For ssh and filesystem, path must have an extra leading | |
| 102 | 102 | ** '/' to use an absolute path. |
| 103 | 103 | ** |
| 104 | 104 | ** By default, your current login name is used to create the default |
| 105 | 105 | ** admin user. This can be overridden using the -A|--admin-user |
| 106 | 106 | ** parameter. |
| 107 | 107 | ** |
| 108 | 108 | ** Options: |
| 109 | 109 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 110 | 110 | ** --once Don't save url. |
| 111 | -** --private Also clone private branches | |
| 111 | +** --private Also clone private branches | |
| 112 | 112 | ** --ssl-identity=filename Use the SSL identity if requested by the server |
| 113 | 113 | ** --ssh-command|-c 'command' Use this SSH command |
| 114 | 114 | ** --httpauth|-B 'user:pass' Add HTTP Basic Authorization to requests |
| 115 | +** --verbose Show more statistics in output | |
| 115 | 116 | ** |
| 116 | 117 | ** See also: init |
| 117 | 118 | */ |
| 118 | 119 | void clone_cmd(void){ |
| 119 | 120 | char *zPassword; |
| 120 | 121 | const char *zDefaultUser; /* Optional name of the default user */ |
| 121 | 122 | const char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 122 | 123 | int nErr = 0; |
| 123 | - int bPrivate = 0; /* Also clone private branches */ | |
| 124 | 124 | int urlFlags = URL_PROMPT_PW | URL_REMEMBER; |
| 125 | + int syncFlags = SYNC_CLONE; | |
| 125 | 126 | |
| 126 | - if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE; | |
| 127 | + /* Also clone private branches */ | |
| 128 | + if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE; | |
| 127 | 129 | if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; |
| 130 | + if( find_option("verbose",0,0)!=0) syncFlags |= SYNC_VERBOSE; | |
| 128 | 131 | zHttpAuth = find_option("httpauth","B",1); |
| 129 | 132 | zDefaultUser = find_option("admin-user","A",1); |
| 130 | 133 | clone_ssh_find_options(); |
| 131 | 134 | url_proxy_options(); |
| 132 | - | |
| 135 | + | |
| 133 | 136 | /* We should be done with options.. */ |
| 134 | 137 | verify_all_options(); |
| 135 | 138 | |
| 136 | 139 | if( g.argc < 4 ){ |
| 137 | 140 | usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); |
| 138 | 141 | } |
| 139 | 142 | db_open_config(0); |
| 140 | - if( file_size(g.argv[3])>0 ){ | |
| 143 | + if( -1 != file_size(g.argv[3]) ){ | |
| 141 | 144 | fossil_fatal("file already exists: %s", g.argv[3]); |
| 142 | 145 | } |
| 143 | 146 | |
| 144 | 147 | url_parse(g.argv[2], urlFlags); |
| 145 | 148 | if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user; |
| @@ -147,11 +150,11 @@ | ||
| 147 | 150 | file_copy(g.url.name, g.argv[3]); |
| 148 | 151 | db_close(1); |
| 149 | 152 | db_open_repository(g.argv[3]); |
| 150 | 153 | db_record_repository_filename(g.argv[3]); |
| 151 | 154 | url_remember(); |
| 152 | - if( !bPrivate ) delete_private_content(); | |
| 155 | + if( !(syncFlags & SYNC_PRIVATE) ) delete_private_content(); | |
| 153 | 156 | shun_artifacts(); |
| 154 | 157 | db_create_default_users(1, zDefaultUser); |
| 155 | 158 | if( zDefaultUser ){ |
| 156 | 159 | g.zLogin = zDefaultUser; |
| 157 | 160 | }else{ |
| @@ -184,11 +187,11 @@ | ||
| 184 | 187 | ); |
| 185 | 188 | url_enable_proxy(0); |
| 186 | 189 | clone_ssh_db_set_options(); |
| 187 | 190 | url_get_password_if_needed(); |
| 188 | 191 | g.xlinkClusterOnly = 1; |
| 189 | - nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0); | |
| 192 | + nErr = client_sync(syncFlags,CONFIGSET_ALL,0); | |
| 190 | 193 | g.xlinkClusterOnly = 0; |
| 191 | 194 | verify_cancel(); |
| 192 | 195 | db_end_transaction(0); |
| 193 | 196 | db_close(1); |
| 194 | 197 | if( nErr ){ |
| @@ -207,11 +210,11 @@ | ||
| 207 | 210 | db_end_transaction(0); |
| 208 | 211 | } |
| 209 | 212 | |
| 210 | 213 | /* |
| 211 | 214 | ** If user chooses to use HTTP Authentication over unencrypted HTTP, |
| 212 | -** remember decision. Otherwise, if the URL is being changed and no | |
| 215 | +** remember decision. Otherwise, if the URL is being changed and no | |
| 213 | 216 | ** preference has been indicated, err on the safe side and revert the |
| 214 | 217 | ** decision. Set the global preference if the URL is not being changed. |
| 215 | 218 | */ |
| 216 | 219 | void remember_or_get_http_auth( |
| 217 | 220 | const char *zHttpAuth, /* Credentials in the form "user:password" */ |
| @@ -266,13 +269,13 @@ | ||
| 266 | 269 | g.zSshCmd = mprintf("%s", zSshCmd); |
| 267 | 270 | } |
| 268 | 271 | } |
| 269 | 272 | |
| 270 | 273 | /* |
| 271 | -** Set SSH options discovered in global variables (set from command line | |
| 274 | +** Set SSH options discovered in global variables (set from command line | |
| 272 | 275 | ** options). |
| 273 | 276 | */ |
| 274 | 277 | void clone_ssh_db_set_options(void){ |
| 275 | 278 | if( g.zSshCmd && g.zSshCmd[0] ){ |
| 276 | 279 | db_set("ssh-command", g.zSshCmd, 0); |
| 277 | 280 | } |
| 278 | 281 | } |
| 279 | 282 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -83,11 +83,11 @@ | |
| 83 | ** COMMAND: clone |
| 84 | ** |
| 85 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 86 | ** |
| 87 | ** Make a clone of a repository specified by URL in the local |
| 88 | ** file named FILENAME. |
| 89 | ** |
| 90 | ** URL must be in one of the following form: ([...] mean optional) |
| 91 | ** HTTP/HTTPS protocol: |
| 92 | ** http[s]://[userid[:password]@]host[:port][/path] |
| 93 | ** |
| @@ -96,50 +96,53 @@ | |
| 96 | ** [?fossil=path/to/fossil.exe] |
| 97 | ** |
| 98 | ** Filesystem: |
| 99 | ** [file://]path/to/repo.fossil |
| 100 | ** |
| 101 | ** Note: For ssh and filesystem, path must have an extra leading |
| 102 | ** '/' to use an absolute path. |
| 103 | ** |
| 104 | ** By default, your current login name is used to create the default |
| 105 | ** admin user. This can be overridden using the -A|--admin-user |
| 106 | ** parameter. |
| 107 | ** |
| 108 | ** Options: |
| 109 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 110 | ** --once Don't save url. |
| 111 | ** --private Also clone private branches |
| 112 | ** --ssl-identity=filename Use the SSL identity if requested by the server |
| 113 | ** --ssh-command|-c 'command' Use this SSH command |
| 114 | ** --httpauth|-B 'user:pass' Add HTTP Basic Authorization to requests |
| 115 | ** |
| 116 | ** See also: init |
| 117 | */ |
| 118 | void clone_cmd(void){ |
| 119 | char *zPassword; |
| 120 | const char *zDefaultUser; /* Optional name of the default user */ |
| 121 | const char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 122 | int nErr = 0; |
| 123 | int bPrivate = 0; /* Also clone private branches */ |
| 124 | int urlFlags = URL_PROMPT_PW | URL_REMEMBER; |
| 125 | |
| 126 | if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE; |
| 127 | if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; |
| 128 | zHttpAuth = find_option("httpauth","B",1); |
| 129 | zDefaultUser = find_option("admin-user","A",1); |
| 130 | clone_ssh_find_options(); |
| 131 | url_proxy_options(); |
| 132 | |
| 133 | /* We should be done with options.. */ |
| 134 | verify_all_options(); |
| 135 | |
| 136 | if( g.argc < 4 ){ |
| 137 | usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); |
| 138 | } |
| 139 | db_open_config(0); |
| 140 | if( file_size(g.argv[3])>0 ){ |
| 141 | fossil_fatal("file already exists: %s", g.argv[3]); |
| 142 | } |
| 143 | |
| 144 | url_parse(g.argv[2], urlFlags); |
| 145 | if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user; |
| @@ -147,11 +150,11 @@ | |
| 147 | file_copy(g.url.name, g.argv[3]); |
| 148 | db_close(1); |
| 149 | db_open_repository(g.argv[3]); |
| 150 | db_record_repository_filename(g.argv[3]); |
| 151 | url_remember(); |
| 152 | if( !bPrivate ) delete_private_content(); |
| 153 | shun_artifacts(); |
| 154 | db_create_default_users(1, zDefaultUser); |
| 155 | if( zDefaultUser ){ |
| 156 | g.zLogin = zDefaultUser; |
| 157 | }else{ |
| @@ -184,11 +187,11 @@ | |
| 184 | ); |
| 185 | url_enable_proxy(0); |
| 186 | clone_ssh_db_set_options(); |
| 187 | url_get_password_if_needed(); |
| 188 | g.xlinkClusterOnly = 1; |
| 189 | nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0); |
| 190 | g.xlinkClusterOnly = 0; |
| 191 | verify_cancel(); |
| 192 | db_end_transaction(0); |
| 193 | db_close(1); |
| 194 | if( nErr ){ |
| @@ -207,11 +210,11 @@ | |
| 207 | db_end_transaction(0); |
| 208 | } |
| 209 | |
| 210 | /* |
| 211 | ** If user chooses to use HTTP Authentication over unencrypted HTTP, |
| 212 | ** remember decision. Otherwise, if the URL is being changed and no |
| 213 | ** preference has been indicated, err on the safe side and revert the |
| 214 | ** decision. Set the global preference if the URL is not being changed. |
| 215 | */ |
| 216 | void remember_or_get_http_auth( |
| 217 | const char *zHttpAuth, /* Credentials in the form "user:password" */ |
| @@ -266,13 +269,13 @@ | |
| 266 | g.zSshCmd = mprintf("%s", zSshCmd); |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | /* |
| 271 | ** Set SSH options discovered in global variables (set from command line |
| 272 | ** options). |
| 273 | */ |
| 274 | void clone_ssh_db_set_options(void){ |
| 275 | if( g.zSshCmd && g.zSshCmd[0] ){ |
| 276 | db_set("ssh-command", g.zSshCmd, 0); |
| 277 | } |
| 278 | } |
| 279 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -83,11 +83,11 @@ | |
| 83 | ** COMMAND: clone |
| 84 | ** |
| 85 | ** Usage: %fossil clone ?OPTIONS? URL FILENAME |
| 86 | ** |
| 87 | ** Make a clone of a repository specified by URL in the local |
| 88 | ** file named FILENAME. |
| 89 | ** |
| 90 | ** URL must be in one of the following form: ([...] mean optional) |
| 91 | ** HTTP/HTTPS protocol: |
| 92 | ** http[s]://[userid[:password]@]host[:port][/path] |
| 93 | ** |
| @@ -96,50 +96,53 @@ | |
| 96 | ** [?fossil=path/to/fossil.exe] |
| 97 | ** |
| 98 | ** Filesystem: |
| 99 | ** [file://]path/to/repo.fossil |
| 100 | ** |
| 101 | ** Note: For ssh and filesystem, path must have an extra leading |
| 102 | ** '/' to use an absolute path. |
| 103 | ** |
| 104 | ** By default, your current login name is used to create the default |
| 105 | ** admin user. This can be overridden using the -A|--admin-user |
| 106 | ** parameter. |
| 107 | ** |
| 108 | ** Options: |
| 109 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 110 | ** --once Don't save url. |
| 111 | ** --private Also clone private branches |
| 112 | ** --ssl-identity=filename Use the SSL identity if requested by the server |
| 113 | ** --ssh-command|-c 'command' Use this SSH command |
| 114 | ** --httpauth|-B 'user:pass' Add HTTP Basic Authorization to requests |
| 115 | ** --verbose Show more statistics in output |
| 116 | ** |
| 117 | ** See also: init |
| 118 | */ |
| 119 | void clone_cmd(void){ |
| 120 | char *zPassword; |
| 121 | const char *zDefaultUser; /* Optional name of the default user */ |
| 122 | const char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 123 | int nErr = 0; |
| 124 | int urlFlags = URL_PROMPT_PW | URL_REMEMBER; |
| 125 | int syncFlags = SYNC_CLONE; |
| 126 | |
| 127 | /* Also clone private branches */ |
| 128 | if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE; |
| 129 | if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; |
| 130 | if( find_option("verbose",0,0)!=0) syncFlags |= SYNC_VERBOSE; |
| 131 | zHttpAuth = find_option("httpauth","B",1); |
| 132 | zDefaultUser = find_option("admin-user","A",1); |
| 133 | clone_ssh_find_options(); |
| 134 | url_proxy_options(); |
| 135 | |
| 136 | /* We should be done with options.. */ |
| 137 | verify_all_options(); |
| 138 | |
| 139 | if( g.argc < 4 ){ |
| 140 | usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); |
| 141 | } |
| 142 | db_open_config(0); |
| 143 | if( -1 != file_size(g.argv[3]) ){ |
| 144 | fossil_fatal("file already exists: %s", g.argv[3]); |
| 145 | } |
| 146 | |
| 147 | url_parse(g.argv[2], urlFlags); |
| 148 | if( zDefaultUser==0 && g.url.user!=0 ) zDefaultUser = g.url.user; |
| @@ -147,11 +150,11 @@ | |
| 150 | file_copy(g.url.name, g.argv[3]); |
| 151 | db_close(1); |
| 152 | db_open_repository(g.argv[3]); |
| 153 | db_record_repository_filename(g.argv[3]); |
| 154 | url_remember(); |
| 155 | if( !(syncFlags & SYNC_PRIVATE) ) delete_private_content(); |
| 156 | shun_artifacts(); |
| 157 | db_create_default_users(1, zDefaultUser); |
| 158 | if( zDefaultUser ){ |
| 159 | g.zLogin = zDefaultUser; |
| 160 | }else{ |
| @@ -184,11 +187,11 @@ | |
| 187 | ); |
| 188 | url_enable_proxy(0); |
| 189 | clone_ssh_db_set_options(); |
| 190 | url_get_password_if_needed(); |
| 191 | g.xlinkClusterOnly = 1; |
| 192 | nErr = client_sync(syncFlags,CONFIGSET_ALL,0); |
| 193 | g.xlinkClusterOnly = 0; |
| 194 | verify_cancel(); |
| 195 | db_end_transaction(0); |
| 196 | db_close(1); |
| 197 | if( nErr ){ |
| @@ -207,11 +210,11 @@ | |
| 210 | db_end_transaction(0); |
| 211 | } |
| 212 | |
| 213 | /* |
| 214 | ** If user chooses to use HTTP Authentication over unencrypted HTTP, |
| 215 | ** remember decision. Otherwise, if the URL is being changed and no |
| 216 | ** preference has been indicated, err on the safe side and revert the |
| 217 | ** decision. Set the global preference if the URL is not being changed. |
| 218 | */ |
| 219 | void remember_or_get_http_auth( |
| 220 | const char *zHttpAuth, /* Credentials in the form "user:password" */ |
| @@ -266,13 +269,13 @@ | |
| 269 | g.zSshCmd = mprintf("%s", zSshCmd); |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | /* |
| 274 | ** Set SSH options discovered in global variables (set from command line |
| 275 | ** options). |
| 276 | */ |
| 277 | void clone_ssh_db_set_options(void){ |
| 278 | if( g.zSshCmd && g.zSshCmd[0] ){ |
| 279 | db_set("ssh-command", g.zSshCmd, 0); |
| 280 | } |
| 281 | } |
| 282 |
+4
-4
| --- src/codecheck1.c | ||
| +++ src/codecheck1.c | ||
| @@ -276,11 +276,11 @@ | ||
| 276 | 276 | |
| 277 | 277 | /* Certain functions are guaranteed to return a string that is safe |
| 278 | 278 | ** for use with %s */ |
| 279 | 279 | z = next_non_whitespace(z, &len, &eType); |
| 280 | 280 | for(i=0; i<sizeof(azSafeFunc)/sizeof(azSafeFunc[0]); i++){ |
| 281 | - if( eType==TK_ID | |
| 281 | + if( eType==TK_ID | |
| 282 | 282 | && strncmp(z, azSafeFunc[i], len)==0 |
| 283 | 283 | && strlen(azSafeFunc[i])==len |
| 284 | 284 | ){ |
| 285 | 285 | return 1; |
| 286 | 286 | } |
| @@ -291,11 +291,11 @@ | ||
| 291 | 291 | if( is_string_expr(z) ) return 1; |
| 292 | 292 | |
| 293 | 293 | /* If the "safe-for-%s" comment appears in the argument, then |
| 294 | 294 | ** let it through */ |
| 295 | 295 | if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1; |
| 296 | - | |
| 296 | + | |
| 297 | 297 | return 0; |
| 298 | 298 | } |
| 299 | 299 | |
| 300 | 300 | /* |
| 301 | 301 | ** Processing flags |
| @@ -460,11 +460,11 @@ | ||
| 460 | 460 | for(i=len-1; i>0 && isspace(z[i]); i--){ z[i] = 0; } |
| 461 | 461 | z += len + 1; |
| 462 | 462 | } |
| 463 | 463 | acType = (char*)&azArg[nArg]; |
| 464 | 464 | if( fmtArg>nArg ){ |
| 465 | - printf("%s:%d: too few arguments to %.*s()\n", | |
| 465 | + printf("%s:%d: too few arguments to %.*s()\n", | |
| 466 | 466 | zFilename, lnFCall, szFName, zFCall); |
| 467 | 467 | nErr++; |
| 468 | 468 | }else{ |
| 469 | 469 | const char *zFmt = azArg[fmtArg-1]; |
| 470 | 470 | const char *zOverride = strstr(zFmt, "/*works-like:"); |
| @@ -537,11 +537,11 @@ | ||
| 537 | 537 | nCurly--; |
| 538 | 538 | }else if( nCurly>0 && z[0]=='(' && ePrev==TK_ID |
| 539 | 539 | && (x = isFormatFunc(zPrev,szPrev,&fmtFlags))>0 ){ |
| 540 | 540 | nErr += checkFormatFunc(zName, zPrev, lnPrev, x, fmtFlags); |
| 541 | 541 | } |
| 542 | - } | |
| 542 | + } | |
| 543 | 543 | zPrev = z; |
| 544 | 544 | ePrev = eToken; |
| 545 | 545 | szPrev = szToken; |
| 546 | 546 | lnPrev = ln; |
| 547 | 547 | } |
| 548 | 548 |
| --- src/codecheck1.c | |
| +++ src/codecheck1.c | |
| @@ -276,11 +276,11 @@ | |
| 276 | |
| 277 | /* Certain functions are guaranteed to return a string that is safe |
| 278 | ** for use with %s */ |
| 279 | z = next_non_whitespace(z, &len, &eType); |
| 280 | for(i=0; i<sizeof(azSafeFunc)/sizeof(azSafeFunc[0]); i++){ |
| 281 | if( eType==TK_ID |
| 282 | && strncmp(z, azSafeFunc[i], len)==0 |
| 283 | && strlen(azSafeFunc[i])==len |
| 284 | ){ |
| 285 | return 1; |
| 286 | } |
| @@ -291,11 +291,11 @@ | |
| 291 | if( is_string_expr(z) ) return 1; |
| 292 | |
| 293 | /* If the "safe-for-%s" comment appears in the argument, then |
| 294 | ** let it through */ |
| 295 | if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1; |
| 296 | |
| 297 | return 0; |
| 298 | } |
| 299 | |
| 300 | /* |
| 301 | ** Processing flags |
| @@ -460,11 +460,11 @@ | |
| 460 | for(i=len-1; i>0 && isspace(z[i]); i--){ z[i] = 0; } |
| 461 | z += len + 1; |
| 462 | } |
| 463 | acType = (char*)&azArg[nArg]; |
| 464 | if( fmtArg>nArg ){ |
| 465 | printf("%s:%d: too few arguments to %.*s()\n", |
| 466 | zFilename, lnFCall, szFName, zFCall); |
| 467 | nErr++; |
| 468 | }else{ |
| 469 | const char *zFmt = azArg[fmtArg-1]; |
| 470 | const char *zOverride = strstr(zFmt, "/*works-like:"); |
| @@ -537,11 +537,11 @@ | |
| 537 | nCurly--; |
| 538 | }else if( nCurly>0 && z[0]=='(' && ePrev==TK_ID |
| 539 | && (x = isFormatFunc(zPrev,szPrev,&fmtFlags))>0 ){ |
| 540 | nErr += checkFormatFunc(zName, zPrev, lnPrev, x, fmtFlags); |
| 541 | } |
| 542 | } |
| 543 | zPrev = z; |
| 544 | ePrev = eToken; |
| 545 | szPrev = szToken; |
| 546 | lnPrev = ln; |
| 547 | } |
| 548 |
| --- src/codecheck1.c | |
| +++ src/codecheck1.c | |
| @@ -276,11 +276,11 @@ | |
| 276 | |
| 277 | /* Certain functions are guaranteed to return a string that is safe |
| 278 | ** for use with %s */ |
| 279 | z = next_non_whitespace(z, &len, &eType); |
| 280 | for(i=0; i<sizeof(azSafeFunc)/sizeof(azSafeFunc[0]); i++){ |
| 281 | if( eType==TK_ID |
| 282 | && strncmp(z, azSafeFunc[i], len)==0 |
| 283 | && strlen(azSafeFunc[i])==len |
| 284 | ){ |
| 285 | return 1; |
| 286 | } |
| @@ -291,11 +291,11 @@ | |
| 291 | if( is_string_expr(z) ) return 1; |
| 292 | |
| 293 | /* If the "safe-for-%s" comment appears in the argument, then |
| 294 | ** let it through */ |
| 295 | if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1; |
| 296 | |
| 297 | return 0; |
| 298 | } |
| 299 | |
| 300 | /* |
| 301 | ** Processing flags |
| @@ -460,11 +460,11 @@ | |
| 460 | for(i=len-1; i>0 && isspace(z[i]); i--){ z[i] = 0; } |
| 461 | z += len + 1; |
| 462 | } |
| 463 | acType = (char*)&azArg[nArg]; |
| 464 | if( fmtArg>nArg ){ |
| 465 | printf("%s:%d: too few arguments to %.*s()\n", |
| 466 | zFilename, lnFCall, szFName, zFCall); |
| 467 | nErr++; |
| 468 | }else{ |
| 469 | const char *zFmt = azArg[fmtArg-1]; |
| 470 | const char *zOverride = strstr(zFmt, "/*works-like:"); |
| @@ -537,11 +537,11 @@ | |
| 537 | nCurly--; |
| 538 | }else if( nCurly>0 && z[0]=='(' && ePrev==TK_ID |
| 539 | && (x = isFormatFunc(zPrev,szPrev,&fmtFlags))>0 ){ |
| 540 | nErr += checkFormatFunc(zName, zPrev, lnPrev, x, fmtFlags); |
| 541 | } |
| 542 | } |
| 543 | zPrev = z; |
| 544 | ePrev = eToken; |
| 545 | szPrev = szToken; |
| 546 | lnPrev = ln; |
| 547 | } |
| 548 |
+192
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -97,10 +97,11 @@ | ||
| 97 | 97 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 98 | 98 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | 99 | { "adunit", CONFIGSET_SKIN }, |
| 100 | 100 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 101 | 101 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 102 | + { "white-foreground", CONFIGSET_SKIN }, | |
| 102 | 103 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 103 | 104 | { "th1-docs", CONFIGSET_TH1 }, |
| 104 | 105 | #endif |
| 105 | 106 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 106 | 107 | { "th1-hooks", CONFIGSET_TH1 }, |
| @@ -989,5 +990,196 @@ | ||
| 989 | 990 | fossil_fatal("METHOD should be one of:" |
| 990 | 991 | " export import merge pull push reset"); |
| 991 | 992 | } |
| 992 | 993 | configure_rebuild(); |
| 993 | 994 | } |
| 995 | + | |
| 996 | + | |
| 997 | +/* | |
| 998 | +** COMMAND: test-var-list | |
| 999 | +** | |
| 1000 | +** Usage: %fossil test-var-list ?PATTERN? ?--unset? ?--mtime? | |
| 1001 | +** | |
| 1002 | +** Show the content of the CONFIG table in a repository. If PATTERN is | |
| 1003 | +** specified, then only show the entries that match that glob pattern. | |
| 1004 | +** Last modification time is shown if the --mtime option is present. | |
| 1005 | +** | |
| 1006 | +** If the --unset option is included, then entries are deleted rather than | |
| 1007 | +** being displayed. WARNING! This cannot be undone. Be sure you know what | |
| 1008 | +** you are doing! The --unset option only works if there is a PATTERN. | |
| 1009 | +** Probably you should run the command once without --unset to make sure | |
| 1010 | +** you know exactly what is being deleted. | |
| 1011 | +** | |
| 1012 | +** If not in an open check-out, use the -R REPO option to specify a | |
| 1013 | +** a repository. | |
| 1014 | +*/ | |
| 1015 | +void test_var_list_cmd(void){ | |
| 1016 | + Stmt q; | |
| 1017 | + int i, j; | |
| 1018 | + const char *zPattern = 0; | |
| 1019 | + int doUnset; | |
| 1020 | + int showMtime; | |
| 1021 | + Blob sql; | |
| 1022 | + Blob ans; | |
| 1023 | + unsigned char zTrans[1000]; | |
| 1024 | + | |
| 1025 | + doUnset = find_option("unset",0,0)!=0; | |
| 1026 | + showMtime = find_option("mtime",0,0)!=0; | |
| 1027 | + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); | |
| 1028 | + verify_all_options(); | |
| 1029 | + if( g.argc>=3 ){ | |
| 1030 | + zPattern = g.argv[2]; | |
| 1031 | + } | |
| 1032 | + blob_init(&sql,0,0); | |
| 1033 | + blob_appendf(&sql, "SELECT name, value, datetime(mtime,'unixepoch')" | |
| 1034 | + " FROM config"); | |
| 1035 | + if( zPattern ){ | |
| 1036 | + blob_appendf(&sql, " WHERE name GLOB %Q", zPattern); | |
| 1037 | + } | |
| 1038 | + if( showMtime ){ | |
| 1039 | + blob_appendf(&sql, " ORDER BY mtime, name"); | |
| 1040 | + }else{ | |
| 1041 | + blob_appendf(&sql, " ORDER BY name"); | |
| 1042 | + } | |
| 1043 | + db_prepare(&q, "%s", blob_str(&sql)/*safe-for-%s*/); | |
| 1044 | + blob_reset(&sql); | |
| 1045 | +#define MX_VAL 40 | |
| 1046 | +#define MX_NM 28 | |
| 1047 | +#define MX_LONGNM 60 | |
| 1048 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 1049 | + const char *zName = db_column_text(&q,0); | |
| 1050 | + int nName = db_column_bytes(&q,0); | |
| 1051 | + const char *zValue = db_column_text(&q,1); | |
| 1052 | + int szValue = db_column_bytes(&q,1); | |
| 1053 | + const char *zMTime = db_column_text(&q,2); | |
| 1054 | + for(i=j=0; j<MX_VAL && zValue[i]; i++){ | |
| 1055 | + unsigned char c = (unsigned char)zValue[i]; | |
| 1056 | + if( c>=' ' && c<='~' ){ | |
| 1057 | + zTrans[j++] = c; | |
| 1058 | + }else{ | |
| 1059 | + zTrans[j++] = '\\'; | |
| 1060 | + if( c=='\n' ){ | |
| 1061 | + zTrans[j++] = 'n'; | |
| 1062 | + }else if( c=='\r' ){ | |
| 1063 | + zTrans[j++] = 'r'; | |
| 1064 | + }else if( c=='\t' ){ | |
| 1065 | + zTrans[j++] = 't'; | |
| 1066 | + }else{ | |
| 1067 | + zTrans[j++] = '0' + ((c>>6)&7); | |
| 1068 | + zTrans[j++] = '0' + ((c>>3)&7); | |
| 1069 | + zTrans[j++] = '0' + (c&7); | |
| 1070 | + } | |
| 1071 | + } | |
| 1072 | + } | |
| 1073 | + zTrans[j] = 0; | |
| 1074 | + if( i<szValue ){ | |
| 1075 | + sqlite3_snprintf(sizeof(zTrans)-j, (char*)zTrans+j, "...+%d", szValue-i); | |
| 1076 | + j += (int)strlen((char*)zTrans+j); | |
| 1077 | + } | |
| 1078 | + if( showMtime ){ | |
| 1079 | + fossil_print("%s:%*s%s\n", zName, 58-nName, "", zMTime); | |
| 1080 | + }else if( nName<MX_NM-2 ){ | |
| 1081 | + fossil_print("%s:%*s%s\n", zName, MX_NM-1-nName, "", zTrans); | |
| 1082 | + }else if( nName<MX_LONGNM-2 && j<10 ){ | |
| 1083 | + fossil_print("%s:%*s%s\n", zName, MX_LONGNM-1-nName, "", zTrans); | |
| 1084 | + }else{ | |
| 1085 | + fossil_print("%s:\n%*s%s\n", zName, MX_NM, "", zTrans); | |
| 1086 | + } | |
| 1087 | + } | |
| 1088 | + db_finalize(&q); | |
| 1089 | + if( zPattern && doUnset ){ | |
| 1090 | + prompt_user("Delete all of the above? (y/N)? ", &ans); | |
| 1091 | + if( blob_str(&ans)[0]=='y' || blob_str(&ans)[0]=='Y' ){ | |
| 1092 | + db_multi_exec("DELETE FROM config WHERE name GLOB %Q", zPattern); | |
| 1093 | + } | |
| 1094 | + blob_reset(&ans); | |
| 1095 | + } | |
| 1096 | +} | |
| 1097 | + | |
| 1098 | +/* | |
| 1099 | +** COMMAND: test-var-get | |
| 1100 | +** | |
| 1101 | +** Usage: %fossil test-var-get VAR ?FILE? | |
| 1102 | +** | |
| 1103 | +** Write the text of the VAR variable into FILE. If FILE is "-" | |
| 1104 | +** or is omitted then output goes to standard output. VAR can be a | |
| 1105 | +** GLOB pattern. | |
| 1106 | +** | |
| 1107 | +** If not in an open check-out, use the -R REPO option to specify a | |
| 1108 | +** a repository. | |
| 1109 | +*/ | |
| 1110 | +void test_var_get_cmd(void){ | |
| 1111 | + const char *zVar; | |
| 1112 | + const char *zFile; | |
| 1113 | + int n; | |
| 1114 | + Blob x; | |
| 1115 | + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); | |
| 1116 | + verify_all_options(); | |
| 1117 | + if( g.argc<3 ){ | |
| 1118 | + usage("VAR ?FILE?"); | |
| 1119 | + } | |
| 1120 | + zVar = g.argv[2]; | |
| 1121 | + zFile = g.argc>=4 ? g.argv[3] : "-"; | |
| 1122 | + n = db_int(0, "SELECT count(*) FROM config WHERE name GLOB %Q", zVar); | |
| 1123 | + if( n==0 ){ | |
| 1124 | + fossil_fatal("no match for %Q", zVar); | |
| 1125 | + } | |
| 1126 | + if( n>1 ){ | |
| 1127 | + fossil_fatal("multiple matches: %s", | |
| 1128 | + db_text(0, "SELECT group_concat(quote(name),', ') FROM (" | |
| 1129 | + " SELECT name FROM config WHERE name GLOB %Q ORDER BY 1)", | |
| 1130 | + zVar)); | |
| 1131 | + } | |
| 1132 | + blob_init(&x,0,0); | |
| 1133 | + db_blob(&x, "SELECT value FROM config WHERE name GLOB %Q", zVar); | |
| 1134 | + blob_write_to_file(&x, zFile); | |
| 1135 | +} | |
| 1136 | + | |
| 1137 | +/* | |
| 1138 | +** COMMAND: test-var-set | |
| 1139 | +** | |
| 1140 | +** Usage: %fossil test-var-set VAR ?VALUE? ?--file FILE? | |
| 1141 | +** | |
| 1142 | +** Store VALUE or the content of FILE (exactly one of which must be | |
| 1143 | +** supplied) into variable VAR. Use a FILE of "-" to read from | |
| 1144 | +** standard input. | |
| 1145 | +** | |
| 1146 | +** WARNING: changing the value of a variable can interfere with the | |
| 1147 | +** operation of Fossil. Be sure you know what you are doing. | |
| 1148 | +** | |
| 1149 | +** Use "--blob FILE" instead of "--file FILE" to load a binary blob | |
| 1150 | +** such as a GIF. | |
| 1151 | +*/ | |
| 1152 | +void test_var_set_cmd(void){ | |
| 1153 | + const char *zVar; | |
| 1154 | + const char *zFile; | |
| 1155 | + const char *zBlob; | |
| 1156 | + Blob x; | |
| 1157 | + Stmt ins; | |
| 1158 | + zFile = find_option("file",0,1); | |
| 1159 | + zBlob = find_option("blob",0,1); | |
| 1160 | + db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); | |
| 1161 | + verify_all_options(); | |
| 1162 | + if( g.argc<3 || (zFile==0 && zBlob==0 && g.argc<4) ){ | |
| 1163 | + usage("VAR ?VALUE? ?--file FILE?"); | |
| 1164 | + } | |
| 1165 | + zVar = g.argv[2]; | |
| 1166 | + if( zFile ){ | |
| 1167 | + if( zBlob ) fossil_fatal("cannot do both --file or --blob"); | |
| 1168 | + blob_read_from_file(&x, zFile); | |
| 1169 | + }else if( zBlob ){ | |
| 1170 | + blob_read_from_file(&x, zBlob); | |
| 1171 | + }else{ | |
| 1172 | + blob_init(&x,g.argv[3],-1); | |
| 1173 | + } | |
| 1174 | + db_prepare(&ins, | |
| 1175 | + "REPLACE INTO config(name,value,mtime)" | |
| 1176 | + "VALUES(%Q,:val,now())", zVar); | |
| 1177 | + if( zBlob ){ | |
| 1178 | + db_bind_blob(&ins, ":val", &x); | |
| 1179 | + }else{ | |
| 1180 | + db_bind_text(&ins, ":val", blob_str(&x)); | |
| 1181 | + } | |
| 1182 | + db_step(&ins); | |
| 1183 | + db_finalize(&ins); | |
| 1184 | + blob_reset(&x); | |
| 1185 | +} | |
| 994 | 1186 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -97,10 +97,11 @@ | |
| 97 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 98 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | { "adunit", CONFIGSET_SKIN }, |
| 100 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 101 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 102 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 103 | { "th1-docs", CONFIGSET_TH1 }, |
| 104 | #endif |
| 105 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 106 | { "th1-hooks", CONFIGSET_TH1 }, |
| @@ -989,5 +990,196 @@ | |
| 989 | fossil_fatal("METHOD should be one of:" |
| 990 | " export import merge pull push reset"); |
| 991 | } |
| 992 | configure_rebuild(); |
| 993 | } |
| 994 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -97,10 +97,11 @@ | |
| 97 | { "timeline-max-comment", CONFIGSET_SKIN }, |
| 98 | { "timeline-plaintext", CONFIGSET_SKIN }, |
| 99 | { "adunit", CONFIGSET_SKIN }, |
| 100 | { "adunit-omit-if-admin", CONFIGSET_SKIN }, |
| 101 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 102 | { "white-foreground", CONFIGSET_SKIN }, |
| 103 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 104 | { "th1-docs", CONFIGSET_TH1 }, |
| 105 | #endif |
| 106 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 107 | { "th1-hooks", CONFIGSET_TH1 }, |
| @@ -989,5 +990,196 @@ | |
| 990 | fossil_fatal("METHOD should be one of:" |
| 991 | " export import merge pull push reset"); |
| 992 | } |
| 993 | configure_rebuild(); |
| 994 | } |
| 995 | |
| 996 | |
| 997 | /* |
| 998 | ** COMMAND: test-var-list |
| 999 | ** |
| 1000 | ** Usage: %fossil test-var-list ?PATTERN? ?--unset? ?--mtime? |
| 1001 | ** |
| 1002 | ** Show the content of the CONFIG table in a repository. If PATTERN is |
| 1003 | ** specified, then only show the entries that match that glob pattern. |
| 1004 | ** Last modification time is shown if the --mtime option is present. |
| 1005 | ** |
| 1006 | ** If the --unset option is included, then entries are deleted rather than |
| 1007 | ** being displayed. WARNING! This cannot be undone. Be sure you know what |
| 1008 | ** you are doing! The --unset option only works if there is a PATTERN. |
| 1009 | ** Probably you should run the command once without --unset to make sure |
| 1010 | ** you know exactly what is being deleted. |
| 1011 | ** |
| 1012 | ** If not in an open check-out, use the -R REPO option to specify a |
| 1013 | ** a repository. |
| 1014 | */ |
| 1015 | void test_var_list_cmd(void){ |
| 1016 | Stmt q; |
| 1017 | int i, j; |
| 1018 | const char *zPattern = 0; |
| 1019 | int doUnset; |
| 1020 | int showMtime; |
| 1021 | Blob sql; |
| 1022 | Blob ans; |
| 1023 | unsigned char zTrans[1000]; |
| 1024 | |
| 1025 | doUnset = find_option("unset",0,0)!=0; |
| 1026 | showMtime = find_option("mtime",0,0)!=0; |
| 1027 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1028 | verify_all_options(); |
| 1029 | if( g.argc>=3 ){ |
| 1030 | zPattern = g.argv[2]; |
| 1031 | } |
| 1032 | blob_init(&sql,0,0); |
| 1033 | blob_appendf(&sql, "SELECT name, value, datetime(mtime,'unixepoch')" |
| 1034 | " FROM config"); |
| 1035 | if( zPattern ){ |
| 1036 | blob_appendf(&sql, " WHERE name GLOB %Q", zPattern); |
| 1037 | } |
| 1038 | if( showMtime ){ |
| 1039 | blob_appendf(&sql, " ORDER BY mtime, name"); |
| 1040 | }else{ |
| 1041 | blob_appendf(&sql, " ORDER BY name"); |
| 1042 | } |
| 1043 | db_prepare(&q, "%s", blob_str(&sql)/*safe-for-%s*/); |
| 1044 | blob_reset(&sql); |
| 1045 | #define MX_VAL 40 |
| 1046 | #define MX_NM 28 |
| 1047 | #define MX_LONGNM 60 |
| 1048 | while( db_step(&q)==SQLITE_ROW ){ |
| 1049 | const char *zName = db_column_text(&q,0); |
| 1050 | int nName = db_column_bytes(&q,0); |
| 1051 | const char *zValue = db_column_text(&q,1); |
| 1052 | int szValue = db_column_bytes(&q,1); |
| 1053 | const char *zMTime = db_column_text(&q,2); |
| 1054 | for(i=j=0; j<MX_VAL && zValue[i]; i++){ |
| 1055 | unsigned char c = (unsigned char)zValue[i]; |
| 1056 | if( c>=' ' && c<='~' ){ |
| 1057 | zTrans[j++] = c; |
| 1058 | }else{ |
| 1059 | zTrans[j++] = '\\'; |
| 1060 | if( c=='\n' ){ |
| 1061 | zTrans[j++] = 'n'; |
| 1062 | }else if( c=='\r' ){ |
| 1063 | zTrans[j++] = 'r'; |
| 1064 | }else if( c=='\t' ){ |
| 1065 | zTrans[j++] = 't'; |
| 1066 | }else{ |
| 1067 | zTrans[j++] = '0' + ((c>>6)&7); |
| 1068 | zTrans[j++] = '0' + ((c>>3)&7); |
| 1069 | zTrans[j++] = '0' + (c&7); |
| 1070 | } |
| 1071 | } |
| 1072 | } |
| 1073 | zTrans[j] = 0; |
| 1074 | if( i<szValue ){ |
| 1075 | sqlite3_snprintf(sizeof(zTrans)-j, (char*)zTrans+j, "...+%d", szValue-i); |
| 1076 | j += (int)strlen((char*)zTrans+j); |
| 1077 | } |
| 1078 | if( showMtime ){ |
| 1079 | fossil_print("%s:%*s%s\n", zName, 58-nName, "", zMTime); |
| 1080 | }else if( nName<MX_NM-2 ){ |
| 1081 | fossil_print("%s:%*s%s\n", zName, MX_NM-1-nName, "", zTrans); |
| 1082 | }else if( nName<MX_LONGNM-2 && j<10 ){ |
| 1083 | fossil_print("%s:%*s%s\n", zName, MX_LONGNM-1-nName, "", zTrans); |
| 1084 | }else{ |
| 1085 | fossil_print("%s:\n%*s%s\n", zName, MX_NM, "", zTrans); |
| 1086 | } |
| 1087 | } |
| 1088 | db_finalize(&q); |
| 1089 | if( zPattern && doUnset ){ |
| 1090 | prompt_user("Delete all of the above? (y/N)? ", &ans); |
| 1091 | if( blob_str(&ans)[0]=='y' || blob_str(&ans)[0]=='Y' ){ |
| 1092 | db_multi_exec("DELETE FROM config WHERE name GLOB %Q", zPattern); |
| 1093 | } |
| 1094 | blob_reset(&ans); |
| 1095 | } |
| 1096 | } |
| 1097 | |
| 1098 | /* |
| 1099 | ** COMMAND: test-var-get |
| 1100 | ** |
| 1101 | ** Usage: %fossil test-var-get VAR ?FILE? |
| 1102 | ** |
| 1103 | ** Write the text of the VAR variable into FILE. If FILE is "-" |
| 1104 | ** or is omitted then output goes to standard output. VAR can be a |
| 1105 | ** GLOB pattern. |
| 1106 | ** |
| 1107 | ** If not in an open check-out, use the -R REPO option to specify a |
| 1108 | ** a repository. |
| 1109 | */ |
| 1110 | void test_var_get_cmd(void){ |
| 1111 | const char *zVar; |
| 1112 | const char *zFile; |
| 1113 | int n; |
| 1114 | Blob x; |
| 1115 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1116 | verify_all_options(); |
| 1117 | if( g.argc<3 ){ |
| 1118 | usage("VAR ?FILE?"); |
| 1119 | } |
| 1120 | zVar = g.argv[2]; |
| 1121 | zFile = g.argc>=4 ? g.argv[3] : "-"; |
| 1122 | n = db_int(0, "SELECT count(*) FROM config WHERE name GLOB %Q", zVar); |
| 1123 | if( n==0 ){ |
| 1124 | fossil_fatal("no match for %Q", zVar); |
| 1125 | } |
| 1126 | if( n>1 ){ |
| 1127 | fossil_fatal("multiple matches: %s", |
| 1128 | db_text(0, "SELECT group_concat(quote(name),', ') FROM (" |
| 1129 | " SELECT name FROM config WHERE name GLOB %Q ORDER BY 1)", |
| 1130 | zVar)); |
| 1131 | } |
| 1132 | blob_init(&x,0,0); |
| 1133 | db_blob(&x, "SELECT value FROM config WHERE name GLOB %Q", zVar); |
| 1134 | blob_write_to_file(&x, zFile); |
| 1135 | } |
| 1136 | |
| 1137 | /* |
| 1138 | ** COMMAND: test-var-set |
| 1139 | ** |
| 1140 | ** Usage: %fossil test-var-set VAR ?VALUE? ?--file FILE? |
| 1141 | ** |
| 1142 | ** Store VALUE or the content of FILE (exactly one of which must be |
| 1143 | ** supplied) into variable VAR. Use a FILE of "-" to read from |
| 1144 | ** standard input. |
| 1145 | ** |
| 1146 | ** WARNING: changing the value of a variable can interfere with the |
| 1147 | ** operation of Fossil. Be sure you know what you are doing. |
| 1148 | ** |
| 1149 | ** Use "--blob FILE" instead of "--file FILE" to load a binary blob |
| 1150 | ** such as a GIF. |
| 1151 | */ |
| 1152 | void test_var_set_cmd(void){ |
| 1153 | const char *zVar; |
| 1154 | const char *zFile; |
| 1155 | const char *zBlob; |
| 1156 | Blob x; |
| 1157 | Stmt ins; |
| 1158 | zFile = find_option("file",0,1); |
| 1159 | zBlob = find_option("blob",0,1); |
| 1160 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 1161 | verify_all_options(); |
| 1162 | if( g.argc<3 || (zFile==0 && zBlob==0 && g.argc<4) ){ |
| 1163 | usage("VAR ?VALUE? ?--file FILE?"); |
| 1164 | } |
| 1165 | zVar = g.argv[2]; |
| 1166 | if( zFile ){ |
| 1167 | if( zBlob ) fossil_fatal("cannot do both --file or --blob"); |
| 1168 | blob_read_from_file(&x, zFile); |
| 1169 | }else if( zBlob ){ |
| 1170 | blob_read_from_file(&x, zBlob); |
| 1171 | }else{ |
| 1172 | blob_init(&x,g.argv[3],-1); |
| 1173 | } |
| 1174 | db_prepare(&ins, |
| 1175 | "REPLACE INTO config(name,value,mtime)" |
| 1176 | "VALUES(%Q,:val,now())", zVar); |
| 1177 | if( zBlob ){ |
| 1178 | db_bind_blob(&ins, ":val", &x); |
| 1179 | }else{ |
| 1180 | db_bind_text(&ins, ":val", blob_str(&x)); |
| 1181 | } |
| 1182 | db_step(&ins); |
| 1183 | db_finalize(&ins); |
| 1184 | blob_reset(&x); |
| 1185 | } |
| 1186 |
M
src/db.c
+102
-33
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -735,10 +735,18 @@ | ||
| 735 | 735 | sqlite3_result_int64(context, time(0)); |
| 736 | 736 | } |
| 737 | 737 | |
| 738 | 738 | /* |
| 739 | 739 | ** Function to return the check-in time for a file. |
| 740 | +** | |
| 741 | +** checkin_mtime(CKINID,RID) | |
| 742 | +** | |
| 743 | +** CKINID: The RID for the manifest for a check-in. | |
| 744 | +** RID: The RID of a file in CKINID for which the check-in time | |
| 745 | +** is desired. | |
| 746 | +** | |
| 747 | +** Returns: The check-in time in seconds since 1970. | |
| 740 | 748 | */ |
| 741 | 749 | void db_checkin_mtime_function( |
| 742 | 750 | sqlite3_context *context, |
| 743 | 751 | int argc, |
| 744 | 752 | sqlite3_value **argv |
| @@ -749,17 +757,25 @@ | ||
| 749 | 757 | if( rc==0 ){ |
| 750 | 758 | sqlite3_result_int64(context, mtime); |
| 751 | 759 | } |
| 752 | 760 | } |
| 753 | 761 | |
| 762 | +/* | |
| 763 | +** SQL wrapper around the symbolic_name_to_rid() C-language API. | |
| 764 | +** Examples: | |
| 765 | +** | |
| 766 | +** symbolic_name_to_rid('trunk'); | |
| 767 | +** symbolic_name_to_rid('trunk','w'); | |
| 768 | +** | |
| 769 | +*/ | |
| 754 | 770 | void db_sym2rid_function( |
| 755 | 771 | sqlite3_context *context, |
| 756 | 772 | int argc, |
| 757 | 773 | sqlite3_value **argv |
| 758 | 774 | ){ |
| 759 | - char const * arg; | |
| 760 | - char const * type; | |
| 775 | + const char *arg; | |
| 776 | + const char *type; | |
| 761 | 777 | if(1 != argc && 2 != argc){ |
| 762 | 778 | sqlite3_result_error(context, "Expecting one or two arguments", -1); |
| 763 | 779 | return; |
| 764 | 780 | } |
| 765 | 781 | arg = (const char*)sqlite3_value_text(argv[0]); |
| @@ -778,10 +794,25 @@ | ||
| 778 | 794 | sqlite3_result_int64(context, rid); |
| 779 | 795 | } |
| 780 | 796 | } |
| 781 | 797 | } |
| 782 | 798 | |
| 799 | +/* | |
| 800 | +** Register the SQL functions that are useful both to the internal | |
| 801 | +** representation and to the "fossil sql" command. | |
| 802 | +*/ | |
| 803 | +void db_add_aux_functions(sqlite3 *db){ | |
| 804 | + sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0, | |
| 805 | + db_checkin_mtime_function, 0, 0); | |
| 806 | + sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, | |
| 807 | + db_sym2rid_function, 0, 0); | |
| 808 | + sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, | |
| 809 | + db_sym2rid_function, 0, 0); | |
| 810 | + sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, | |
| 811 | + db_now_function, 0, 0); | |
| 812 | +} | |
| 813 | + | |
| 783 | 814 | |
| 784 | 815 | /* |
| 785 | 816 | ** Open a database file. Return a pointer to the new database |
| 786 | 817 | ** connection. An error results in process abort. |
| 787 | 818 | */ |
| @@ -798,13 +829,10 @@ | ||
| 798 | 829 | if( rc!=SQLITE_OK ){ |
| 799 | 830 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 800 | 831 | } |
| 801 | 832 | sqlite3_busy_timeout(db, 5000); |
| 802 | 833 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 803 | - sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, db_now_function, 0, 0); | |
| 804 | - sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0, | |
| 805 | - db_checkin_mtime_function, 0, 0); | |
| 806 | 834 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 807 | 835 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 808 | 836 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 809 | 837 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 810 | 838 | sqlite3_create_function( |
| @@ -811,21 +839,14 @@ | ||
| 811 | 839 | db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 812 | 840 | ); |
| 813 | 841 | sqlite3_create_function( |
| 814 | 842 | db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 815 | 843 | ); |
| 816 | - sqlite3_create_function( | |
| 817 | - db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, db_sym2rid_function, | |
| 818 | - 0, 0 | |
| 819 | - ); | |
| 820 | - sqlite3_create_function( | |
| 821 | - db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, db_sym2rid_function, | |
| 822 | - 0, 0 | |
| 823 | - ); | |
| 824 | 844 | if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); |
| 825 | - re_add_sql_func(db); | |
| 826 | - foci_register(db); | |
| 845 | + db_add_aux_functions(db); | |
| 846 | + re_add_sql_func(db); /* The REGEXP operator */ | |
| 847 | + foci_register(db); /* The "files_of_checkin" virtual table */ | |
| 827 | 848 | sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 828 | 849 | return db; |
| 829 | 850 | } |
| 830 | 851 | |
| 831 | 852 | |
| @@ -906,30 +927,35 @@ | ||
| 906 | 927 | char *zHome; |
| 907 | 928 | if( g.zConfigDbName ){ |
| 908 | 929 | if( useAttach==g.useAttach ) return; |
| 909 | 930 | db_close_config(); |
| 910 | 931 | } |
| 911 | -#if defined(_WIN32) || defined(__CYGWIN__) | |
| 912 | - zHome = fossil_getenv("LOCALAPPDATA"); | |
| 913 | - if( zHome==0 ){ | |
| 914 | - zHome = fossil_getenv("APPDATA"); | |
| 915 | - if( zHome==0 ){ | |
| 916 | - char *zDrive = fossil_getenv("HOMEDRIVE"); | |
| 917 | - zHome = fossil_getenv("HOMEPATH"); | |
| 918 | - if( zDrive && zHome ) zHome = mprintf("%s%s", zDrive, zHome); | |
| 932 | + zHome = fossil_getenv("FOSSIL_HOME"); | |
| 933 | +#if defined(_WIN32) || defined(__CYGWIN__) | |
| 934 | + if( zHome==0 ){ | |
| 935 | + zHome = fossil_getenv("LOCALAPPDATA"); | |
| 936 | + if( zHome==0 ){ | |
| 937 | + zHome = fossil_getenv("APPDATA"); | |
| 938 | + if( zHome==0 ){ | |
| 939 | + char *zDrive = fossil_getenv("HOMEDRIVE"); | |
| 940 | + char *zPath = fossil_getenv("HOMEPATH"); | |
| 941 | + if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); | |
| 942 | + } | |
| 919 | 943 | } |
| 920 | 944 | } |
| 921 | 945 | if( zHome==0 ){ |
| 922 | - fossil_fatal("cannot locate home directory - " | |
| 923 | - "please set the LOCALAPPDATA or APPDATA or HOMEPATH " | |
| 924 | - "environment variables"); | |
| 946 | + fossil_fatal("cannot locate home directory - please set the " | |
| 947 | + "FOSSIL_HOME, LOCALAPPDATA, APPDATA, or HOMEPATH " | |
| 948 | + "environment variables"); | |
| 925 | 949 | } |
| 926 | 950 | #else |
| 927 | - zHome = fossil_getenv("HOME"); | |
| 951 | + if( zHome==0 ){ | |
| 952 | + zHome = fossil_getenv("HOME"); | |
| 953 | + } | |
| 928 | 954 | if( zHome==0 ){ |
| 929 | - fossil_fatal("cannot locate home directory - " | |
| 930 | - "please set the HOME environment variable"); | |
| 955 | + fossil_fatal("cannot locate home directory - please set the " | |
| 956 | + "FOSSIL_HOME or HOME environment variables"); | |
| 931 | 957 | } |
| 932 | 958 | #endif |
| 933 | 959 | if( file_isdir(zHome)!=1 ){ |
| 934 | 960 | fossil_fatal("invalid home directory: %s", zHome); |
| 935 | 961 | } |
| @@ -1149,10 +1175,37 @@ | ||
| 1149 | 1175 | g.zRepositoryName = mprintf("%s", zDbName); |
| 1150 | 1176 | db_open_or_attach(g.zRepositoryName, "repository", 0); |
| 1151 | 1177 | g.repositoryOpen = 1; |
| 1152 | 1178 | /* Cache "allow-symlinks" option, because we'll need it on every stat call */ |
| 1153 | 1179 | g.allowSymlinks = db_get_boolean("allow-symlinks", 0); |
| 1180 | + g.zAuxSchema = db_get("aux-schema",""); | |
| 1181 | + | |
| 1182 | + /* Verify that the PLINK table has a new column added by the | |
| 1183 | + ** 2014-11-28 schema change. Create it if necessary. This code | |
| 1184 | + ** can be removed in the future, once all users have upgraded to the | |
| 1185 | + ** 2014-11-28 or later schema. | |
| 1186 | + */ | |
| 1187 | + if( !db_table_has_column("repository","plink","baseid") ){ | |
| 1188 | + db_multi_exec( | |
| 1189 | + "ALTER TABLE %s.plink ADD COLUMN baseid;", db_name("repository") | |
| 1190 | + ); | |
| 1191 | + } | |
| 1192 | + | |
| 1193 | + /* Verify that the MLINK table has the newer columns added by the | |
| 1194 | + ** 2015-01-24 schema change. Create them if necessary. This code | |
| 1195 | + ** can be removed in the future, once all users have upgraded to the | |
| 1196 | + ** 2015-01-24 or later schema. | |
| 1197 | + */ | |
| 1198 | + if( !db_table_has_column("repository","mlink","isaux") ){ | |
| 1199 | + db_begin_transaction(); | |
| 1200 | + db_multi_exec( | |
| 1201 | + "ALTER TABLE %s.mlink ADD COLUMN pmid INTEGER DEFAULT 0;" | |
| 1202 | + "ALTER TABLE %s.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;", | |
| 1203 | + db_name("repository"), db_name("repository") | |
| 1204 | + ); | |
| 1205 | + db_end_transaction(0); | |
| 1206 | + } | |
| 1154 | 1207 | } |
| 1155 | 1208 | |
| 1156 | 1209 | /* |
| 1157 | 1210 | ** Flags for the db_find_and_open_repository() function. |
| 1158 | 1211 | */ |
| @@ -1216,11 +1269,10 @@ | ||
| 1216 | 1269 | |
| 1217 | 1270 | /* |
| 1218 | 1271 | ** Return TRUE if the schema is out-of-date |
| 1219 | 1272 | */ |
| 1220 | 1273 | int db_schema_is_outofdate(void){ |
| 1221 | - if( g.zAuxSchema==0 ) g.zAuxSchema = db_get("aux-schema",""); | |
| 1222 | 1274 | return strcmp(g.zAuxSchema,AUX_SCHEMA_MIN)<0 |
| 1223 | 1275 | || strcmp(g.zAuxSchema,AUX_SCHEMA_MAX)>0; |
| 1224 | 1276 | } |
| 1225 | 1277 | |
| 1226 | 1278 | /* |
| @@ -1334,10 +1386,22 @@ | ||
| 1334 | 1386 | while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){ |
| 1335 | 1387 | fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt)); |
| 1336 | 1388 | } |
| 1337 | 1389 | } |
| 1338 | 1390 | db_close_config(); |
| 1391 | + | |
| 1392 | + /* If the localdb (the check-out database) is open and if it has | |
| 1393 | + ** a lot of unused free space, then VACUUM it as we shut down. | |
| 1394 | + */ | |
| 1395 | + if( g.localOpen && strcmp(db_name("localdb"),"main")==0 ){ | |
| 1396 | + int nFree = db_int(0, "PRAGMA main.freelist_count"); | |
| 1397 | + int nTotal = db_int(0, "PRAGMA main.page_count"); | |
| 1398 | + if( nFree>nTotal/4 ){ | |
| 1399 | + db_multi_exec("VACUUM;"); | |
| 1400 | + } | |
| 1401 | + } | |
| 1402 | + | |
| 1339 | 1403 | if( g.db ){ |
| 1340 | 1404 | sqlite3_wal_checkpoint(g.db, 0); |
| 1341 | 1405 | sqlite3_close(g.db); |
| 1342 | 1406 | g.db = 0; |
| 1343 | 1407 | g.zMainDbType = 0; |
| @@ -1401,13 +1465,13 @@ | ||
| 1401 | 1465 | " WHERE login=%Q", zUser |
| 1402 | 1466 | ); |
| 1403 | 1467 | if( !setupUserOnly ){ |
| 1404 | 1468 | db_multi_exec( |
| 1405 | 1469 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1406 | - " VALUES('anonymous',hex(randomblob(8)),'hmncz','Anon');" | |
| 1470 | + " VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');" | |
| 1407 | 1471 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1408 | - " VALUES('nobody','','gjor','Nobody');" | |
| 1472 | + " VALUES('nobody','','gjorz','Nobody');" | |
| 1409 | 1473 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1410 | 1474 | " VALUES('developer','','dei','Dev');" |
| 1411 | 1475 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1412 | 1476 | " VALUES('reader','','kptw','Reader');" |
| 1413 | 1477 | ); |
| @@ -1618,10 +1682,15 @@ | ||
| 1618 | 1682 | verify_all_options(); |
| 1619 | 1683 | |
| 1620 | 1684 | if( g.argc!=3 ){ |
| 1621 | 1685 | usage("REPOSITORY-NAME"); |
| 1622 | 1686 | } |
| 1687 | + | |
| 1688 | + if( -1 != file_size(g.argv[2]) ){ | |
| 1689 | + fossil_fatal("file already exists: %s", g.argv[2]); | |
| 1690 | + } | |
| 1691 | + | |
| 1623 | 1692 | db_create_repository(g.argv[2]); |
| 1624 | 1693 | db_open_repository(g.argv[2]); |
| 1625 | 1694 | db_open_config(0); |
| 1626 | 1695 | if( zTemplate ) db_attach(zTemplate, "settingSrc"); |
| 1627 | 1696 | db_begin_transaction(); |
| @@ -2637,11 +2706,11 @@ | ||
| 2637 | 2706 | } |
| 2638 | 2707 | } |
| 2639 | 2708 | }else{ |
| 2640 | 2709 | isManifest = 0; |
| 2641 | 2710 | while( ctrlSettings[i].name |
| 2642 | - && strncmp(ctrlSettings[i].name, zName, n)==0 | |
| 2711 | + && strncmp(ctrlSettings[i].name, zName, n)==0 | |
| 2643 | 2712 | ){ |
| 2644 | 2713 | print_setting(&ctrlSettings[i], db_open_local(0)); |
| 2645 | 2714 | i++; |
| 2646 | 2715 | } |
| 2647 | 2716 | } |
| 2648 | 2717 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -735,10 +735,18 @@ | |
| 735 | sqlite3_result_int64(context, time(0)); |
| 736 | } |
| 737 | |
| 738 | /* |
| 739 | ** Function to return the check-in time for a file. |
| 740 | */ |
| 741 | void db_checkin_mtime_function( |
| 742 | sqlite3_context *context, |
| 743 | int argc, |
| 744 | sqlite3_value **argv |
| @@ -749,17 +757,25 @@ | |
| 749 | if( rc==0 ){ |
| 750 | sqlite3_result_int64(context, mtime); |
| 751 | } |
| 752 | } |
| 753 | |
| 754 | void db_sym2rid_function( |
| 755 | sqlite3_context *context, |
| 756 | int argc, |
| 757 | sqlite3_value **argv |
| 758 | ){ |
| 759 | char const * arg; |
| 760 | char const * type; |
| 761 | if(1 != argc && 2 != argc){ |
| 762 | sqlite3_result_error(context, "Expecting one or two arguments", -1); |
| 763 | return; |
| 764 | } |
| 765 | arg = (const char*)sqlite3_value_text(argv[0]); |
| @@ -778,10 +794,25 @@ | |
| 778 | sqlite3_result_int64(context, rid); |
| 779 | } |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | |
| 784 | /* |
| 785 | ** Open a database file. Return a pointer to the new database |
| 786 | ** connection. An error results in process abort. |
| 787 | */ |
| @@ -798,13 +829,10 @@ | |
| 798 | if( rc!=SQLITE_OK ){ |
| 799 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 800 | } |
| 801 | sqlite3_busy_timeout(db, 5000); |
| 802 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 803 | sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, db_now_function, 0, 0); |
| 804 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0, |
| 805 | db_checkin_mtime_function, 0, 0); |
| 806 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 807 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 808 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 809 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 810 | sqlite3_create_function( |
| @@ -811,21 +839,14 @@ | |
| 811 | db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 812 | ); |
| 813 | sqlite3_create_function( |
| 814 | db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 815 | ); |
| 816 | sqlite3_create_function( |
| 817 | db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, db_sym2rid_function, |
| 818 | 0, 0 |
| 819 | ); |
| 820 | sqlite3_create_function( |
| 821 | db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, db_sym2rid_function, |
| 822 | 0, 0 |
| 823 | ); |
| 824 | if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); |
| 825 | re_add_sql_func(db); |
| 826 | foci_register(db); |
| 827 | sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 828 | return db; |
| 829 | } |
| 830 | |
| 831 | |
| @@ -906,30 +927,35 @@ | |
| 906 | char *zHome; |
| 907 | if( g.zConfigDbName ){ |
| 908 | if( useAttach==g.useAttach ) return; |
| 909 | db_close_config(); |
| 910 | } |
| 911 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 912 | zHome = fossil_getenv("LOCALAPPDATA"); |
| 913 | if( zHome==0 ){ |
| 914 | zHome = fossil_getenv("APPDATA"); |
| 915 | if( zHome==0 ){ |
| 916 | char *zDrive = fossil_getenv("HOMEDRIVE"); |
| 917 | zHome = fossil_getenv("HOMEPATH"); |
| 918 | if( zDrive && zHome ) zHome = mprintf("%s%s", zDrive, zHome); |
| 919 | } |
| 920 | } |
| 921 | if( zHome==0 ){ |
| 922 | fossil_fatal("cannot locate home directory - " |
| 923 | "please set the LOCALAPPDATA or APPDATA or HOMEPATH " |
| 924 | "environment variables"); |
| 925 | } |
| 926 | #else |
| 927 | zHome = fossil_getenv("HOME"); |
| 928 | if( zHome==0 ){ |
| 929 | fossil_fatal("cannot locate home directory - " |
| 930 | "please set the HOME environment variable"); |
| 931 | } |
| 932 | #endif |
| 933 | if( file_isdir(zHome)!=1 ){ |
| 934 | fossil_fatal("invalid home directory: %s", zHome); |
| 935 | } |
| @@ -1149,10 +1175,37 @@ | |
| 1149 | g.zRepositoryName = mprintf("%s", zDbName); |
| 1150 | db_open_or_attach(g.zRepositoryName, "repository", 0); |
| 1151 | g.repositoryOpen = 1; |
| 1152 | /* Cache "allow-symlinks" option, because we'll need it on every stat call */ |
| 1153 | g.allowSymlinks = db_get_boolean("allow-symlinks", 0); |
| 1154 | } |
| 1155 | |
| 1156 | /* |
| 1157 | ** Flags for the db_find_and_open_repository() function. |
| 1158 | */ |
| @@ -1216,11 +1269,10 @@ | |
| 1216 | |
| 1217 | /* |
| 1218 | ** Return TRUE if the schema is out-of-date |
| 1219 | */ |
| 1220 | int db_schema_is_outofdate(void){ |
| 1221 | if( g.zAuxSchema==0 ) g.zAuxSchema = db_get("aux-schema",""); |
| 1222 | return strcmp(g.zAuxSchema,AUX_SCHEMA_MIN)<0 |
| 1223 | || strcmp(g.zAuxSchema,AUX_SCHEMA_MAX)>0; |
| 1224 | } |
| 1225 | |
| 1226 | /* |
| @@ -1334,10 +1386,22 @@ | |
| 1334 | while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){ |
| 1335 | fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt)); |
| 1336 | } |
| 1337 | } |
| 1338 | db_close_config(); |
| 1339 | if( g.db ){ |
| 1340 | sqlite3_wal_checkpoint(g.db, 0); |
| 1341 | sqlite3_close(g.db); |
| 1342 | g.db = 0; |
| 1343 | g.zMainDbType = 0; |
| @@ -1401,13 +1465,13 @@ | |
| 1401 | " WHERE login=%Q", zUser |
| 1402 | ); |
| 1403 | if( !setupUserOnly ){ |
| 1404 | db_multi_exec( |
| 1405 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1406 | " VALUES('anonymous',hex(randomblob(8)),'hmncz','Anon');" |
| 1407 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1408 | " VALUES('nobody','','gjor','Nobody');" |
| 1409 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1410 | " VALUES('developer','','dei','Dev');" |
| 1411 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1412 | " VALUES('reader','','kptw','Reader');" |
| 1413 | ); |
| @@ -1618,10 +1682,15 @@ | |
| 1618 | verify_all_options(); |
| 1619 | |
| 1620 | if( g.argc!=3 ){ |
| 1621 | usage("REPOSITORY-NAME"); |
| 1622 | } |
| 1623 | db_create_repository(g.argv[2]); |
| 1624 | db_open_repository(g.argv[2]); |
| 1625 | db_open_config(0); |
| 1626 | if( zTemplate ) db_attach(zTemplate, "settingSrc"); |
| 1627 | db_begin_transaction(); |
| @@ -2637,11 +2706,11 @@ | |
| 2637 | } |
| 2638 | } |
| 2639 | }else{ |
| 2640 | isManifest = 0; |
| 2641 | while( ctrlSettings[i].name |
| 2642 | && strncmp(ctrlSettings[i].name, zName, n)==0 |
| 2643 | ){ |
| 2644 | print_setting(&ctrlSettings[i], db_open_local(0)); |
| 2645 | i++; |
| 2646 | } |
| 2647 | } |
| 2648 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -735,10 +735,18 @@ | |
| 735 | sqlite3_result_int64(context, time(0)); |
| 736 | } |
| 737 | |
| 738 | /* |
| 739 | ** Function to return the check-in time for a file. |
| 740 | ** |
| 741 | ** checkin_mtime(CKINID,RID) |
| 742 | ** |
| 743 | ** CKINID: The RID for the manifest for a check-in. |
| 744 | ** RID: The RID of a file in CKINID for which the check-in time |
| 745 | ** is desired. |
| 746 | ** |
| 747 | ** Returns: The check-in time in seconds since 1970. |
| 748 | */ |
| 749 | void db_checkin_mtime_function( |
| 750 | sqlite3_context *context, |
| 751 | int argc, |
| 752 | sqlite3_value **argv |
| @@ -749,17 +757,25 @@ | |
| 757 | if( rc==0 ){ |
| 758 | sqlite3_result_int64(context, mtime); |
| 759 | } |
| 760 | } |
| 761 | |
| 762 | /* |
| 763 | ** SQL wrapper around the symbolic_name_to_rid() C-language API. |
| 764 | ** Examples: |
| 765 | ** |
| 766 | ** symbolic_name_to_rid('trunk'); |
| 767 | ** symbolic_name_to_rid('trunk','w'); |
| 768 | ** |
| 769 | */ |
| 770 | void db_sym2rid_function( |
| 771 | sqlite3_context *context, |
| 772 | int argc, |
| 773 | sqlite3_value **argv |
| 774 | ){ |
| 775 | const char *arg; |
| 776 | const char *type; |
| 777 | if(1 != argc && 2 != argc){ |
| 778 | sqlite3_result_error(context, "Expecting one or two arguments", -1); |
| 779 | return; |
| 780 | } |
| 781 | arg = (const char*)sqlite3_value_text(argv[0]); |
| @@ -778,10 +794,25 @@ | |
| 794 | sqlite3_result_int64(context, rid); |
| 795 | } |
| 796 | } |
| 797 | } |
| 798 | |
| 799 | /* |
| 800 | ** Register the SQL functions that are useful both to the internal |
| 801 | ** representation and to the "fossil sql" command. |
| 802 | */ |
| 803 | void db_add_aux_functions(sqlite3 *db){ |
| 804 | sqlite3_create_function(db, "checkin_mtime", 2, SQLITE_UTF8, 0, |
| 805 | db_checkin_mtime_function, 0, 0); |
| 806 | sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0, |
| 807 | db_sym2rid_function, 0, 0); |
| 808 | sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0, |
| 809 | db_sym2rid_function, 0, 0); |
| 810 | sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0, |
| 811 | db_now_function, 0, 0); |
| 812 | } |
| 813 | |
| 814 | |
| 815 | /* |
| 816 | ** Open a database file. Return a pointer to the new database |
| 817 | ** connection. An error results in process abort. |
| 818 | */ |
| @@ -798,13 +829,10 @@ | |
| 829 | if( rc!=SQLITE_OK ){ |
| 830 | db_err("[%s]: %s", zDbName, sqlite3_errmsg(db)); |
| 831 | } |
| 832 | sqlite3_busy_timeout(db, 5000); |
| 833 | sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ |
| 834 | sqlite3_create_function(db, "user", 0, SQLITE_UTF8, 0, db_sql_user, 0, 0); |
| 835 | sqlite3_create_function(db, "cgi", 1, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 836 | sqlite3_create_function(db, "cgi", 2, SQLITE_UTF8, 0, db_sql_cgi, 0, 0); |
| 837 | sqlite3_create_function(db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0); |
| 838 | sqlite3_create_function( |
| @@ -811,21 +839,14 @@ | |
| 839 | db, "is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 840 | ); |
| 841 | sqlite3_create_function( |
| 842 | db, "if_selected", 3, SQLITE_UTF8, 0, file_is_selected,0,0 |
| 843 | ); |
| 844 | if( g.fSqlTrace ) sqlite3_trace(db, db_sql_trace, 0); |
| 845 | db_add_aux_functions(db); |
| 846 | re_add_sql_func(db); /* The REGEXP operator */ |
| 847 | foci_register(db); /* The "files_of_checkin" virtual table */ |
| 848 | sqlite3_exec(db, "PRAGMA foreign_keys=OFF;", 0, 0, 0); |
| 849 | return db; |
| 850 | } |
| 851 | |
| 852 | |
| @@ -906,30 +927,35 @@ | |
| 927 | char *zHome; |
| 928 | if( g.zConfigDbName ){ |
| 929 | if( useAttach==g.useAttach ) return; |
| 930 | db_close_config(); |
| 931 | } |
| 932 | zHome = fossil_getenv("FOSSIL_HOME"); |
| 933 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 934 | if( zHome==0 ){ |
| 935 | zHome = fossil_getenv("LOCALAPPDATA"); |
| 936 | if( zHome==0 ){ |
| 937 | zHome = fossil_getenv("APPDATA"); |
| 938 | if( zHome==0 ){ |
| 939 | char *zDrive = fossil_getenv("HOMEDRIVE"); |
| 940 | char *zPath = fossil_getenv("HOMEPATH"); |
| 941 | if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath); |
| 942 | } |
| 943 | } |
| 944 | } |
| 945 | if( zHome==0 ){ |
| 946 | fossil_fatal("cannot locate home directory - please set the " |
| 947 | "FOSSIL_HOME, LOCALAPPDATA, APPDATA, or HOMEPATH " |
| 948 | "environment variables"); |
| 949 | } |
| 950 | #else |
| 951 | if( zHome==0 ){ |
| 952 | zHome = fossil_getenv("HOME"); |
| 953 | } |
| 954 | if( zHome==0 ){ |
| 955 | fossil_fatal("cannot locate home directory - please set the " |
| 956 | "FOSSIL_HOME or HOME environment variables"); |
| 957 | } |
| 958 | #endif |
| 959 | if( file_isdir(zHome)!=1 ){ |
| 960 | fossil_fatal("invalid home directory: %s", zHome); |
| 961 | } |
| @@ -1149,10 +1175,37 @@ | |
| 1175 | g.zRepositoryName = mprintf("%s", zDbName); |
| 1176 | db_open_or_attach(g.zRepositoryName, "repository", 0); |
| 1177 | g.repositoryOpen = 1; |
| 1178 | /* Cache "allow-symlinks" option, because we'll need it on every stat call */ |
| 1179 | g.allowSymlinks = db_get_boolean("allow-symlinks", 0); |
| 1180 | g.zAuxSchema = db_get("aux-schema",""); |
| 1181 | |
| 1182 | /* Verify that the PLINK table has a new column added by the |
| 1183 | ** 2014-11-28 schema change. Create it if necessary. This code |
| 1184 | ** can be removed in the future, once all users have upgraded to the |
| 1185 | ** 2014-11-28 or later schema. |
| 1186 | */ |
| 1187 | if( !db_table_has_column("repository","plink","baseid") ){ |
| 1188 | db_multi_exec( |
| 1189 | "ALTER TABLE %s.plink ADD COLUMN baseid;", db_name("repository") |
| 1190 | ); |
| 1191 | } |
| 1192 | |
| 1193 | /* Verify that the MLINK table has the newer columns added by the |
| 1194 | ** 2015-01-24 schema change. Create them if necessary. This code |
| 1195 | ** can be removed in the future, once all users have upgraded to the |
| 1196 | ** 2015-01-24 or later schema. |
| 1197 | */ |
| 1198 | if( !db_table_has_column("repository","mlink","isaux") ){ |
| 1199 | db_begin_transaction(); |
| 1200 | db_multi_exec( |
| 1201 | "ALTER TABLE %s.mlink ADD COLUMN pmid INTEGER DEFAULT 0;" |
| 1202 | "ALTER TABLE %s.mlink ADD COLUMN isaux BOOLEAN DEFAULT 0;", |
| 1203 | db_name("repository"), db_name("repository") |
| 1204 | ); |
| 1205 | db_end_transaction(0); |
| 1206 | } |
| 1207 | } |
| 1208 | |
| 1209 | /* |
| 1210 | ** Flags for the db_find_and_open_repository() function. |
| 1211 | */ |
| @@ -1216,11 +1269,10 @@ | |
| 1269 | |
| 1270 | /* |
| 1271 | ** Return TRUE if the schema is out-of-date |
| 1272 | */ |
| 1273 | int db_schema_is_outofdate(void){ |
| 1274 | return strcmp(g.zAuxSchema,AUX_SCHEMA_MIN)<0 |
| 1275 | || strcmp(g.zAuxSchema,AUX_SCHEMA_MAX)>0; |
| 1276 | } |
| 1277 | |
| 1278 | /* |
| @@ -1334,10 +1386,22 @@ | |
| 1386 | while( (pStmt = sqlite3_next_stmt(g.db, pStmt))!=0 ){ |
| 1387 | fossil_warning("unfinalized SQL statement: [%s]", sqlite3_sql(pStmt)); |
| 1388 | } |
| 1389 | } |
| 1390 | db_close_config(); |
| 1391 | |
| 1392 | /* If the localdb (the check-out database) is open and if it has |
| 1393 | ** a lot of unused free space, then VACUUM it as we shut down. |
| 1394 | */ |
| 1395 | if( g.localOpen && strcmp(db_name("localdb"),"main")==0 ){ |
| 1396 | int nFree = db_int(0, "PRAGMA main.freelist_count"); |
| 1397 | int nTotal = db_int(0, "PRAGMA main.page_count"); |
| 1398 | if( nFree>nTotal/4 ){ |
| 1399 | db_multi_exec("VACUUM;"); |
| 1400 | } |
| 1401 | } |
| 1402 | |
| 1403 | if( g.db ){ |
| 1404 | sqlite3_wal_checkpoint(g.db, 0); |
| 1405 | sqlite3_close(g.db); |
| 1406 | g.db = 0; |
| 1407 | g.zMainDbType = 0; |
| @@ -1401,13 +1465,13 @@ | |
| 1465 | " WHERE login=%Q", zUser |
| 1466 | ); |
| 1467 | if( !setupUserOnly ){ |
| 1468 | db_multi_exec( |
| 1469 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1470 | " VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');" |
| 1471 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1472 | " VALUES('nobody','','gjorz','Nobody');" |
| 1473 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1474 | " VALUES('developer','','dei','Dev');" |
| 1475 | "INSERT OR IGNORE INTO user(login,pw,cap,info)" |
| 1476 | " VALUES('reader','','kptw','Reader');" |
| 1477 | ); |
| @@ -1618,10 +1682,15 @@ | |
| 1682 | verify_all_options(); |
| 1683 | |
| 1684 | if( g.argc!=3 ){ |
| 1685 | usage("REPOSITORY-NAME"); |
| 1686 | } |
| 1687 | |
| 1688 | if( -1 != file_size(g.argv[2]) ){ |
| 1689 | fossil_fatal("file already exists: %s", g.argv[2]); |
| 1690 | } |
| 1691 | |
| 1692 | db_create_repository(g.argv[2]); |
| 1693 | db_open_repository(g.argv[2]); |
| 1694 | db_open_config(0); |
| 1695 | if( zTemplate ) db_attach(zTemplate, "settingSrc"); |
| 1696 | db_begin_transaction(); |
| @@ -2637,11 +2706,11 @@ | |
| 2706 | } |
| 2707 | } |
| 2708 | }else{ |
| 2709 | isManifest = 0; |
| 2710 | while( ctrlSettings[i].name |
| 2711 | && strncmp(ctrlSettings[i].name, zName, n)==0 |
| 2712 | ){ |
| 2713 | print_setting(&ctrlSettings[i], db_open_local(0)); |
| 2714 | i++; |
| 2715 | } |
| 2716 | } |
| 2717 |
+1
-1
| --- src/deltacmd.c | ||
| +++ src/deltacmd.c | ||
| @@ -17,11 +17,11 @@ | ||
| 17 | 17 | ** |
| 18 | 18 | ** This module implements the interface to the delta generator. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "deltacmd.h" |
| 22 | - | |
| 22 | + | |
| 23 | 23 | /* |
| 24 | 24 | ** Create a delta that describes the change from pOriginal to pTarget |
| 25 | 25 | ** and put that delta in pDelta. The pDelta blob is assumed to be |
| 26 | 26 | ** uninitialized. |
| 27 | 27 | */ |
| 28 | 28 |
| --- src/deltacmd.c | |
| +++ src/deltacmd.c | |
| @@ -17,11 +17,11 @@ | |
| 17 | ** |
| 18 | ** This module implements the interface to the delta generator. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "deltacmd.h" |
| 22 | |
| 23 | /* |
| 24 | ** Create a delta that describes the change from pOriginal to pTarget |
| 25 | ** and put that delta in pDelta. The pDelta blob is assumed to be |
| 26 | ** uninitialized. |
| 27 | */ |
| 28 |
| --- src/deltacmd.c | |
| +++ src/deltacmd.c | |
| @@ -17,11 +17,11 @@ | |
| 17 | ** |
| 18 | ** This module implements the interface to the delta generator. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "deltacmd.h" |
| 22 | |
| 23 | /* |
| 24 | ** Create a delta that describes the change from pOriginal to pTarget |
| 25 | ** and put that delta in pDelta. The pDelta blob is assumed to be |
| 26 | ** uninitialized. |
| 27 | */ |
| 28 |
+9
| --- src/diff.tcl | ||
| +++ src/diff.tcl | ||
| @@ -1,5 +1,14 @@ | ||
| 1 | +# The "diff --tk" command outputs prepends a "set fossilcmd {...}" line | |
| 2 | +# to this file, then runs this file using "tclsh" in order to display the | |
| 3 | +# graphical diff in a separate window. A typical "set fossilcmd" line | |
| 4 | +# looks like this: | |
| 5 | +# | |
| 6 | +# set fossilcmd {| "./fossil" diff --html -y -i -v} | |
| 7 | +# | |
| 8 | +# This header comment is stripped off by the "mkbuiltin.c" program. | |
| 9 | +# | |
| 1 | 10 | set prog { |
| 2 | 11 | package require Tk |
| 3 | 12 | |
| 4 | 13 | array set CFG { |
| 5 | 14 | TITLE {Fossil Diff} |
| 6 | 15 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -1,5 +1,14 @@ | |
| 1 | set prog { |
| 2 | package require Tk |
| 3 | |
| 4 | array set CFG { |
| 5 | TITLE {Fossil Diff} |
| 6 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -1,5 +1,14 @@ | |
| 1 | # The "diff --tk" command outputs prepends a "set fossilcmd {...}" line |
| 2 | # to this file, then runs this file using "tclsh" in order to display the |
| 3 | # graphical diff in a separate window. A typical "set fossilcmd" line |
| 4 | # looks like this: |
| 5 | # |
| 6 | # set fossilcmd {| "./fossil" diff --html -y -i -v} |
| 7 | # |
| 8 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 9 | # |
| 10 | set prog { |
| 11 | package require Tk |
| 12 | |
| 13 | array set CFG { |
| 14 | TITLE {Fossil Diff} |
| 15 |
+130
-126
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -350,150 +350,145 @@ | ||
| 350 | 350 | int i; |
| 351 | 351 | for(i=2; i<g.argc; i++){ |
| 352 | 352 | fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i])); |
| 353 | 353 | } |
| 354 | 354 | } |
| 355 | + | |
| 356 | +/* | |
| 357 | +** Look for a file named zName in the checkin with RID=vid. Load the content | |
| 358 | +** of that file into pContent and return the RID for the file. Or return 0 | |
| 359 | +** if the file is not found or could not be loaded. | |
| 360 | +*/ | |
| 361 | +int doc_load_content(int vid, const char *zName, Blob *pContent){ | |
| 362 | + int rid; /* The RID of the file being loaded */ | |
| 363 | + if( !db_table_exists("repository","vcache") ){ | |
| 364 | + db_multi_exec( | |
| 365 | + "CREATE TABLE IF NOT EXISTS vcache(\n" | |
| 366 | + " vid INTEGER, -- checkin ID\n" | |
| 367 | + " fname TEXT, -- filename\n" | |
| 368 | + " rid INTEGER, -- artifact ID\n" | |
| 369 | + " PRIMARY KEY(vid,fname)\n" | |
| 370 | + ") WITHOUT ROWID" | |
| 371 | + ); | |
| 372 | + } | |
| 373 | + if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ | |
| 374 | + db_multi_exec( | |
| 375 | + "DELETE FROM vcache;\n" | |
| 376 | + "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;\n" | |
| 377 | + "INSERT INTO vcache(vid,fname,rid)" | |
| 378 | + " SELECT checkinID, filename, blob.rid FROM foci, blob" | |
| 379 | + " WHERE blob.uuid=foci.uuid" | |
| 380 | + " AND foci.checkinID=%d;", | |
| 381 | + vid | |
| 382 | + ); | |
| 383 | + } | |
| 384 | + rid = db_int(0, "SELECT rid FROM vcache" | |
| 385 | + " WHERE vid=%d AND fname=%Q", vid, zName); | |
| 386 | + if( rid && content_get(rid, pContent)==0 ){ | |
| 387 | + rid = 0; | |
| 388 | + } | |
| 389 | + return rid; | |
| 390 | +} | |
| 355 | 391 | |
| 356 | 392 | /* |
| 357 | 393 | ** WEBPAGE: doc |
| 358 | -** URL: /doc?name=BASELINE/PATH | |
| 359 | -** URL: /doc/BASELINE/PATH | |
| 360 | -** | |
| 361 | -** BASELINE can be either a baseline uuid prefix or magic words "tip" | |
| 362 | -** to mean the most recently checked in baseline or "ckout" to mean the | |
| 363 | -** content of the local checkout, if any. PATH is the relative pathname | |
| 364 | -** of some file. This method returns the file content. | |
| 365 | -** | |
| 366 | -** If PATH matches the patterns *.wiki or *.txt then formatting content | |
| 367 | -** is added before returning the file. For all other names, the content | |
| 368 | -** is returned straight without any interpretation or processing. | |
| 394 | +** URL: /doc?name=CHECKIN/FILE | |
| 395 | +** URL: /doc/CHECKIN/FILE | |
| 396 | +** | |
| 397 | +** CHECKIN can be either tag or SHA1 hash or timestamp identifying a | |
| 398 | +** particular check, or the name of a branch (meaning the most recent | |
| 399 | +** check-in on that branch) or one of various magic words: | |
| 400 | +** | |
| 401 | +** "tip" means the most recent check-in | |
| 402 | +** | |
| 403 | +** "ckout" means the current check-out, if the server is run from | |
| 404 | +** within a check-out, otherwise it is the same as "tip" | |
| 405 | +** | |
| 406 | +** FILE is the name of a file to delivered up as a webpage. FILE is relative | |
| 407 | +** to the root of the source tree of the repository. The FILE must | |
| 408 | +** be a part of CHECKIN, except when CHECKIN=="ckout" when FILE is read | |
| 409 | +** directly from disk and need not be a managed file. | |
| 410 | +** | |
| 411 | +** The "ckout" CHECKIN is intended for development - to provide a mechanism | |
| 412 | +** for looking at what a file will look like using the /doc webpage after | |
| 413 | +** it gets checked in. | |
| 414 | +** | |
| 415 | +** The file extension is used to decide how to render the file. | |
| 416 | +** | |
| 417 | +** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki", | |
| 418 | +** and "FILE/index.md" are in that order. If none of those are found, | |
| 419 | +** then FILE is completely replaced by "404.md" and tried. If that is | |
| 420 | +** not found, then a default 404 screen is generated. | |
| 369 | 421 | */ |
| 370 | 422 | void doc_page(void){ |
| 371 | 423 | const char *zName; /* Argument to the /doc page */ |
| 424 | + const char *zOrigName = "?"; /* Original document name */ | |
| 372 | 425 | const char *zMime; /* Document MIME type */ |
| 373 | - int vid = 0; /* Artifact of baseline */ | |
| 426 | + char *zCheckin = "tip"; /* The checkin holding the document */ | |
| 427 | + int vid = 0; /* Artifact of checkin */ | |
| 374 | 428 | int rid = 0; /* Artifact of file */ |
| 375 | 429 | int i; /* Loop counter */ |
| 376 | 430 | Blob filebody; /* Content of the documentation file */ |
| 377 | - char zBaseline[UUID_SIZE+1]; /* Baseline UUID */ | |
| 431 | + int nMiss = (-1); /* Failed attempts to find the document */ | |
| 432 | + static const char *const azSuffix[] = { | |
| 433 | + "index.html", "index.wiki", "index.md" | |
| 434 | + }; | |
| 378 | 435 | |
| 379 | 436 | login_check_credentials(); |
| 380 | 437 | if( !g.perm.Read ){ login_needed(); return; } |
| 381 | - zName = PD("name", "tip/index.wiki"); | |
| 382 | - for(i=0; zName[i] && zName[i]!='/'; i++){} | |
| 383 | - if( zName[i]==0 || i>UUID_SIZE ){ | |
| 384 | - zName = "index.html"; | |
| 385 | - goto doc_not_found; | |
| 386 | - } | |
| 387 | - g.zPath = mprintf("%s/%s", g.zPath, zName); | |
| 388 | - memcpy(zBaseline, zName, i); | |
| 389 | - zBaseline[i] = 0; | |
| 390 | - zName += i; | |
| 391 | - while( zName[0]=='/' ){ zName++; } | |
| 392 | - if( !file_is_simple_pathname(zName, 1) ){ | |
| 393 | - int n = strlen(zName); | |
| 394 | - if( n>0 && zName[n-1]=='/' ){ | |
| 395 | - zName = mprintf("%sindex.html", zName); | |
| 396 | - if( !file_is_simple_pathname(zName, 1) ){ | |
| 397 | - goto doc_not_found; | |
| 398 | - } | |
| 399 | - }else{ | |
| 400 | - goto doc_not_found; | |
| 401 | - } | |
| 402 | - } | |
| 403 | - if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local(0)==0 ){ | |
| 404 | - sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); | |
| 405 | - } | |
| 406 | - if( fossil_strcmp(zBaseline,"ckout")==0 ){ | |
| 407 | - /* Read from the local checkout */ | |
| 408 | - char *zFullpath; | |
| 409 | - db_must_be_within_tree(); | |
| 410 | - zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); | |
| 411 | - if( !file_isfile(zFullpath) ){ | |
| 412 | - goto doc_not_found; | |
| 413 | - } | |
| 414 | - if( blob_read_from_file(&filebody, zFullpath)<0 ){ | |
| 415 | - goto doc_not_found; | |
| 416 | - } | |
| 417 | - }else{ | |
| 418 | - db_begin_transaction(); | |
| 419 | - if( fossil_strcmp(zBaseline,"tip")==0 ){ | |
| 420 | - vid = db_int(0, "SELECT objid FROM event WHERE type='ci'" | |
| 421 | - " ORDER BY mtime DESC LIMIT 1"); | |
| 422 | - }else{ | |
| 423 | - vid = name_to_typed_rid(zBaseline, "ci"); | |
| 424 | - } | |
| 425 | - | |
| 426 | - /* Create the baseline cache if it does not already exist */ | |
| 427 | - db_multi_exec( | |
| 428 | - "CREATE TABLE IF NOT EXISTS vcache(\n" | |
| 429 | - " vid INTEGER, -- baseline ID\n" | |
| 430 | - " fname TEXT, -- filename\n" | |
| 431 | - " rid INTEGER, -- artifact ID\n" | |
| 432 | - " UNIQUE(vid,fname,rid)\n" | |
| 433 | - ")" | |
| 434 | - ); | |
| 435 | - | |
| 436 | - | |
| 437 | - | |
| 438 | - /* Check to see if the documentation file artifact ID is contained | |
| 439 | - ** in the baseline cache */ | |
| 440 | - rid = db_int(0, "SELECT rid FROM vcache" | |
| 441 | - " WHERE vid=%d AND fname=%Q", vid, zName); | |
| 442 | - if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ | |
| 443 | - goto doc_not_found; | |
| 444 | - } | |
| 445 | - | |
| 446 | - if( rid==0 ){ | |
| 447 | - Stmt s; | |
| 448 | - Manifest *pM; | |
| 449 | - ManifestFile *pFile; | |
| 450 | - | |
| 451 | - /* Add the vid baseline to the cache */ | |
| 452 | - if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){ | |
| 453 | - db_multi_exec("DELETE FROM vcache"); | |
| 454 | - } | |
| 455 | - pM = manifest_get(vid, CFTYPE_MANIFEST, 0); | |
| 456 | - if( pM==0 ){ | |
| 457 | - goto doc_not_found; | |
| 458 | - } | |
| 459 | - db_prepare(&s, | |
| 460 | - "INSERT INTO vcache(vid,fname,rid)" | |
| 461 | - " SELECT %d, :fname, rid FROM blob" | |
| 462 | - " WHERE uuid=:uuid", | |
| 463 | - vid | |
| 464 | - ); | |
| 465 | - manifest_file_rewind(pM); | |
| 466 | - while( (pFile = manifest_file_next(pM,0))!=0 ){ | |
| 467 | - db_bind_text(&s, ":fname", pFile->zName); | |
| 468 | - db_bind_text(&s, ":uuid", pFile->zUuid); | |
| 469 | - db_step(&s); | |
| 470 | - db_reset(&s); | |
| 471 | - } | |
| 472 | - db_finalize(&s); | |
| 473 | - manifest_destroy(pM); | |
| 474 | - | |
| 475 | - /* Try again to find the file */ | |
| 476 | - rid = db_int(0, "SELECT rid FROM vcache" | |
| 477 | - " WHERE vid=%d AND fname=%Q", vid, zName); | |
| 478 | - } | |
| 479 | - if( rid==0 ){ | |
| 480 | - goto doc_not_found; | |
| 481 | - } | |
| 482 | - | |
| 483 | - /* Get the file content */ | |
| 484 | - if( content_get(rid, &filebody)==0 ){ | |
| 485 | - goto doc_not_found; | |
| 486 | - } | |
| 487 | - db_end_transaction(0); | |
| 488 | - } | |
| 438 | + db_begin_transaction(); | |
| 439 | + while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){ | |
| 440 | + zName = PD("name", "tip/index.wiki"); | |
| 441 | + for(i=0; zName[i] && zName[i]!='/'; i++){} | |
| 442 | + zCheckin = mprintf("%.*s", i, zName); | |
| 443 | + if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ | |
| 444 | + zCheckin = "tip"; | |
| 445 | + } | |
| 446 | + if( nMiss==ArraySize(azSuffix) ){ | |
| 447 | + zName = "404.md"; | |
| 448 | + }else if( zName[i]==0 ){ | |
| 449 | + assert( nMiss>=0 && nMiss<ArraySize(azSuffix) ); | |
| 450 | + zName = azSuffix[nMiss]; | |
| 451 | + }else{ | |
| 452 | + zName += i; | |
| 453 | + } | |
| 454 | + while( zName[0]=='/' ){ zName++; } | |
| 455 | + g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName); | |
| 456 | + if( nMiss==0 ) zOrigName = zName; | |
| 457 | + if( !file_is_simple_pathname(zName, 1) ){ | |
| 458 | + if( sqlite3_strglob("*/", zName)==0 ){ | |
| 459 | + assert( nMiss>=0 && nMiss<ArraySize(azSuffix) ); | |
| 460 | + zName = mprintf("%s%s", zName, azSuffix[nMiss]); | |
| 461 | + if( !file_is_simple_pathname(zName, 1) ){ | |
| 462 | + goto doc_not_found; | |
| 463 | + } | |
| 464 | + }else{ | |
| 465 | + goto doc_not_found; | |
| 466 | + } | |
| 467 | + } | |
| 468 | + if( fossil_strcmp(zCheckin,"ckout")==0 ){ | |
| 469 | + /* Read from the local checkout */ | |
| 470 | + char *zFullpath; | |
| 471 | + db_must_be_within_tree(); | |
| 472 | + zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); | |
| 473 | + if( file_isfile(zFullpath) | |
| 474 | + && blob_read_from_file(&filebody, zFullpath)>0 ){ | |
| 475 | + rid = 1; /* Fake RID just to get the loop to end */ | |
| 476 | + } | |
| 477 | + fossil_free(zFullpath); | |
| 478 | + }else{ | |
| 479 | + vid = name_to_typed_rid(zCheckin, "ci"); | |
| 480 | + rid = doc_load_content(vid, zName, &filebody); | |
| 481 | + } | |
| 482 | + } | |
| 483 | + if( rid==0 ) goto doc_not_found; | |
| 489 | 484 | blob_to_utf8_no_bom(&filebody, 0); |
| 490 | 485 | |
| 491 | 486 | /* The file is now contained in the filebody blob. Deliver the |
| 492 | 487 | ** file to the user |
| 493 | 488 | */ |
| 494 | - zMime = P("mimetype"); | |
| 489 | + zMime = nMiss==0 ? P("mimetype") : 0; | |
| 495 | 490 | if( zMime==0 ){ |
| 496 | 491 | zMime = mimetype_from_name(zName); |
| 497 | 492 | } |
| 498 | 493 | Th_Store("doc_name", zName); |
| 499 | 494 | Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" |
| @@ -500,10 +495,11 @@ | ||
| 500 | 495 | " FROM blob WHERE rid=%d", vid)); |
| 501 | 496 | Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" |
| 502 | 497 | " WHERE objid=%d AND type='ci'", vid)); |
| 503 | 498 | if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ |
| 504 | 499 | Blob title, tail; |
| 500 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 505 | 501 | if( wiki_find_title(&filebody, &title, &tail) ){ |
| 506 | 502 | style_header("%s", blob_str(&title)); |
| 507 | 503 | wiki_convert(&tail, 0, WIKI_BUTTONS); |
| 508 | 504 | }else{ |
| 509 | 505 | style_header("Documentation"); |
| @@ -515,11 +511,12 @@ | ||
| 515 | 511 | Blob tail = BLOB_INITIALIZER; |
| 516 | 512 | markdown_to_html(&filebody, &title, &tail); |
| 517 | 513 | if( blob_size(&title)>0 ){ |
| 518 | 514 | style_header("%s", blob_str(&title)); |
| 519 | 515 | }else{ |
| 520 | - style_header("Documentation"); | |
| 516 | + style_header("%s", nMiss>=ArraySize(azSuffix)? | |
| 517 | + "Not Found" : "Documentation"); | |
| 521 | 518 | } |
| 522 | 519 | blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail)); |
| 523 | 520 | style_footer(); |
| 524 | 521 | }else if( fossil_strcmp(zMime, "text/plain")==0 ){ |
| 525 | 522 | style_header("Documentation"); |
| @@ -536,18 +533,25 @@ | ||
| 536 | 533 | #endif |
| 537 | 534 | }else{ |
| 538 | 535 | cgi_set_content_type(zMime); |
| 539 | 536 | cgi_set_content(&filebody); |
| 540 | 537 | } |
| 538 | + if( nMiss>=ArraySize(azSuffix) ) cgi_set_status(404, "Not Found"); | |
| 539 | + db_end_transaction(0); | |
| 541 | 540 | return; |
| 542 | 541 | |
| 543 | -doc_not_found: | |
| 544 | 542 | /* Jump here when unable to locate the document */ |
| 543 | +doc_not_found: | |
| 545 | 544 | db_end_transaction(0); |
| 546 | - style_header("Document Not Found"); | |
| 547 | - @ <p>No such document: %h(zName)</p> | |
| 545 | + cgi_set_status(404, "Not Found"); | |
| 546 | + style_header("Not Found"); | |
| 547 | + @ <p>Document %h(zOrigName) not found | |
| 548 | + if( fossil_strcmp(zCheckin,"ckout")!=0 ){ | |
| 549 | + @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> | |
| 550 | + } | |
| 548 | 551 | style_footer(); |
| 552 | + db_end_transaction(0); | |
| 549 | 553 | return; |
| 550 | 554 | } |
| 551 | 555 | |
| 552 | 556 | /* |
| 553 | 557 | ** The default logo. |
| 554 | 558 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -350,150 +350,145 @@ | |
| 350 | int i; |
| 351 | for(i=2; i<g.argc; i++){ |
| 352 | fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i])); |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | /* |
| 357 | ** WEBPAGE: doc |
| 358 | ** URL: /doc?name=BASELINE/PATH |
| 359 | ** URL: /doc/BASELINE/PATH |
| 360 | ** |
| 361 | ** BASELINE can be either a baseline uuid prefix or magic words "tip" |
| 362 | ** to mean the most recently checked in baseline or "ckout" to mean the |
| 363 | ** content of the local checkout, if any. PATH is the relative pathname |
| 364 | ** of some file. This method returns the file content. |
| 365 | ** |
| 366 | ** If PATH matches the patterns *.wiki or *.txt then formatting content |
| 367 | ** is added before returning the file. For all other names, the content |
| 368 | ** is returned straight without any interpretation or processing. |
| 369 | */ |
| 370 | void doc_page(void){ |
| 371 | const char *zName; /* Argument to the /doc page */ |
| 372 | const char *zMime; /* Document MIME type */ |
| 373 | int vid = 0; /* Artifact of baseline */ |
| 374 | int rid = 0; /* Artifact of file */ |
| 375 | int i; /* Loop counter */ |
| 376 | Blob filebody; /* Content of the documentation file */ |
| 377 | char zBaseline[UUID_SIZE+1]; /* Baseline UUID */ |
| 378 | |
| 379 | login_check_credentials(); |
| 380 | if( !g.perm.Read ){ login_needed(); return; } |
| 381 | zName = PD("name", "tip/index.wiki"); |
| 382 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 383 | if( zName[i]==0 || i>UUID_SIZE ){ |
| 384 | zName = "index.html"; |
| 385 | goto doc_not_found; |
| 386 | } |
| 387 | g.zPath = mprintf("%s/%s", g.zPath, zName); |
| 388 | memcpy(zBaseline, zName, i); |
| 389 | zBaseline[i] = 0; |
| 390 | zName += i; |
| 391 | while( zName[0]=='/' ){ zName++; } |
| 392 | if( !file_is_simple_pathname(zName, 1) ){ |
| 393 | int n = strlen(zName); |
| 394 | if( n>0 && zName[n-1]=='/' ){ |
| 395 | zName = mprintf("%sindex.html", zName); |
| 396 | if( !file_is_simple_pathname(zName, 1) ){ |
| 397 | goto doc_not_found; |
| 398 | } |
| 399 | }else{ |
| 400 | goto doc_not_found; |
| 401 | } |
| 402 | } |
| 403 | if( fossil_strcmp(zBaseline,"ckout")==0 && db_open_local(0)==0 ){ |
| 404 | sqlite3_snprintf(sizeof(zBaseline), zBaseline, "tip"); |
| 405 | } |
| 406 | if( fossil_strcmp(zBaseline,"ckout")==0 ){ |
| 407 | /* Read from the local checkout */ |
| 408 | char *zFullpath; |
| 409 | db_must_be_within_tree(); |
| 410 | zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 411 | if( !file_isfile(zFullpath) ){ |
| 412 | goto doc_not_found; |
| 413 | } |
| 414 | if( blob_read_from_file(&filebody, zFullpath)<0 ){ |
| 415 | goto doc_not_found; |
| 416 | } |
| 417 | }else{ |
| 418 | db_begin_transaction(); |
| 419 | if( fossil_strcmp(zBaseline,"tip")==0 ){ |
| 420 | vid = db_int(0, "SELECT objid FROM event WHERE type='ci'" |
| 421 | " ORDER BY mtime DESC LIMIT 1"); |
| 422 | }else{ |
| 423 | vid = name_to_typed_rid(zBaseline, "ci"); |
| 424 | } |
| 425 | |
| 426 | /* Create the baseline cache if it does not already exist */ |
| 427 | db_multi_exec( |
| 428 | "CREATE TABLE IF NOT EXISTS vcache(\n" |
| 429 | " vid INTEGER, -- baseline ID\n" |
| 430 | " fname TEXT, -- filename\n" |
| 431 | " rid INTEGER, -- artifact ID\n" |
| 432 | " UNIQUE(vid,fname,rid)\n" |
| 433 | ")" |
| 434 | ); |
| 435 | |
| 436 | |
| 437 | |
| 438 | /* Check to see if the documentation file artifact ID is contained |
| 439 | ** in the baseline cache */ |
| 440 | rid = db_int(0, "SELECT rid FROM vcache" |
| 441 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| 442 | if( rid==0 && db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ |
| 443 | goto doc_not_found; |
| 444 | } |
| 445 | |
| 446 | if( rid==0 ){ |
| 447 | Stmt s; |
| 448 | Manifest *pM; |
| 449 | ManifestFile *pFile; |
| 450 | |
| 451 | /* Add the vid baseline to the cache */ |
| 452 | if( db_int(0, "SELECT count(*) FROM vcache")>10000 ){ |
| 453 | db_multi_exec("DELETE FROM vcache"); |
| 454 | } |
| 455 | pM = manifest_get(vid, CFTYPE_MANIFEST, 0); |
| 456 | if( pM==0 ){ |
| 457 | goto doc_not_found; |
| 458 | } |
| 459 | db_prepare(&s, |
| 460 | "INSERT INTO vcache(vid,fname,rid)" |
| 461 | " SELECT %d, :fname, rid FROM blob" |
| 462 | " WHERE uuid=:uuid", |
| 463 | vid |
| 464 | ); |
| 465 | manifest_file_rewind(pM); |
| 466 | while( (pFile = manifest_file_next(pM,0))!=0 ){ |
| 467 | db_bind_text(&s, ":fname", pFile->zName); |
| 468 | db_bind_text(&s, ":uuid", pFile->zUuid); |
| 469 | db_step(&s); |
| 470 | db_reset(&s); |
| 471 | } |
| 472 | db_finalize(&s); |
| 473 | manifest_destroy(pM); |
| 474 | |
| 475 | /* Try again to find the file */ |
| 476 | rid = db_int(0, "SELECT rid FROM vcache" |
| 477 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| 478 | } |
| 479 | if( rid==0 ){ |
| 480 | goto doc_not_found; |
| 481 | } |
| 482 | |
| 483 | /* Get the file content */ |
| 484 | if( content_get(rid, &filebody)==0 ){ |
| 485 | goto doc_not_found; |
| 486 | } |
| 487 | db_end_transaction(0); |
| 488 | } |
| 489 | blob_to_utf8_no_bom(&filebody, 0); |
| 490 | |
| 491 | /* The file is now contained in the filebody blob. Deliver the |
| 492 | ** file to the user |
| 493 | */ |
| 494 | zMime = P("mimetype"); |
| 495 | if( zMime==0 ){ |
| 496 | zMime = mimetype_from_name(zName); |
| 497 | } |
| 498 | Th_Store("doc_name", zName); |
| 499 | Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" |
| @@ -500,10 +495,11 @@ | |
| 500 | " FROM blob WHERE rid=%d", vid)); |
| 501 | Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" |
| 502 | " WHERE objid=%d AND type='ci'", vid)); |
| 503 | if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ |
| 504 | Blob title, tail; |
| 505 | if( wiki_find_title(&filebody, &title, &tail) ){ |
| 506 | style_header("%s", blob_str(&title)); |
| 507 | wiki_convert(&tail, 0, WIKI_BUTTONS); |
| 508 | }else{ |
| 509 | style_header("Documentation"); |
| @@ -515,11 +511,12 @@ | |
| 515 | Blob tail = BLOB_INITIALIZER; |
| 516 | markdown_to_html(&filebody, &title, &tail); |
| 517 | if( blob_size(&title)>0 ){ |
| 518 | style_header("%s", blob_str(&title)); |
| 519 | }else{ |
| 520 | style_header("Documentation"); |
| 521 | } |
| 522 | blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail)); |
| 523 | style_footer(); |
| 524 | }else if( fossil_strcmp(zMime, "text/plain")==0 ){ |
| 525 | style_header("Documentation"); |
| @@ -536,18 +533,25 @@ | |
| 536 | #endif |
| 537 | }else{ |
| 538 | cgi_set_content_type(zMime); |
| 539 | cgi_set_content(&filebody); |
| 540 | } |
| 541 | return; |
| 542 | |
| 543 | doc_not_found: |
| 544 | /* Jump here when unable to locate the document */ |
| 545 | db_end_transaction(0); |
| 546 | style_header("Document Not Found"); |
| 547 | @ <p>No such document: %h(zName)</p> |
| 548 | style_footer(); |
| 549 | return; |
| 550 | } |
| 551 | |
| 552 | /* |
| 553 | ** The default logo. |
| 554 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -350,150 +350,145 @@ | |
| 350 | int i; |
| 351 | for(i=2; i<g.argc; i++){ |
| 352 | fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i])); |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | /* |
| 357 | ** Look for a file named zName in the checkin with RID=vid. Load the content |
| 358 | ** of that file into pContent and return the RID for the file. Or return 0 |
| 359 | ** if the file is not found or could not be loaded. |
| 360 | */ |
| 361 | int doc_load_content(int vid, const char *zName, Blob *pContent){ |
| 362 | int rid; /* The RID of the file being loaded */ |
| 363 | if( !db_table_exists("repository","vcache") ){ |
| 364 | db_multi_exec( |
| 365 | "CREATE TABLE IF NOT EXISTS vcache(\n" |
| 366 | " vid INTEGER, -- checkin ID\n" |
| 367 | " fname TEXT, -- filename\n" |
| 368 | " rid INTEGER, -- artifact ID\n" |
| 369 | " PRIMARY KEY(vid,fname)\n" |
| 370 | ") WITHOUT ROWID" |
| 371 | ); |
| 372 | } |
| 373 | if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ |
| 374 | db_multi_exec( |
| 375 | "DELETE FROM vcache;\n" |
| 376 | "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;\n" |
| 377 | "INSERT INTO vcache(vid,fname,rid)" |
| 378 | " SELECT checkinID, filename, blob.rid FROM foci, blob" |
| 379 | " WHERE blob.uuid=foci.uuid" |
| 380 | " AND foci.checkinID=%d;", |
| 381 | vid |
| 382 | ); |
| 383 | } |
| 384 | rid = db_int(0, "SELECT rid FROM vcache" |
| 385 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| 386 | if( rid && content_get(rid, pContent)==0 ){ |
| 387 | rid = 0; |
| 388 | } |
| 389 | return rid; |
| 390 | } |
| 391 | |
| 392 | /* |
| 393 | ** WEBPAGE: doc |
| 394 | ** URL: /doc?name=CHECKIN/FILE |
| 395 | ** URL: /doc/CHECKIN/FILE |
| 396 | ** |
| 397 | ** CHECKIN can be either tag or SHA1 hash or timestamp identifying a |
| 398 | ** particular check, or the name of a branch (meaning the most recent |
| 399 | ** check-in on that branch) or one of various magic words: |
| 400 | ** |
| 401 | ** "tip" means the most recent check-in |
| 402 | ** |
| 403 | ** "ckout" means the current check-out, if the server is run from |
| 404 | ** within a check-out, otherwise it is the same as "tip" |
| 405 | ** |
| 406 | ** FILE is the name of a file to delivered up as a webpage. FILE is relative |
| 407 | ** to the root of the source tree of the repository. The FILE must |
| 408 | ** be a part of CHECKIN, except when CHECKIN=="ckout" when FILE is read |
| 409 | ** directly from disk and need not be a managed file. |
| 410 | ** |
| 411 | ** The "ckout" CHECKIN is intended for development - to provide a mechanism |
| 412 | ** for looking at what a file will look like using the /doc webpage after |
| 413 | ** it gets checked in. |
| 414 | ** |
| 415 | ** The file extension is used to decide how to render the file. |
| 416 | ** |
| 417 | ** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki", |
| 418 | ** and "FILE/index.md" are in that order. If none of those are found, |
| 419 | ** then FILE is completely replaced by "404.md" and tried. If that is |
| 420 | ** not found, then a default 404 screen is generated. |
| 421 | */ |
| 422 | void doc_page(void){ |
| 423 | const char *zName; /* Argument to the /doc page */ |
| 424 | const char *zOrigName = "?"; /* Original document name */ |
| 425 | const char *zMime; /* Document MIME type */ |
| 426 | char *zCheckin = "tip"; /* The checkin holding the document */ |
| 427 | int vid = 0; /* Artifact of checkin */ |
| 428 | int rid = 0; /* Artifact of file */ |
| 429 | int i; /* Loop counter */ |
| 430 | Blob filebody; /* Content of the documentation file */ |
| 431 | int nMiss = (-1); /* Failed attempts to find the document */ |
| 432 | static const char *const azSuffix[] = { |
| 433 | "index.html", "index.wiki", "index.md" |
| 434 | }; |
| 435 | |
| 436 | login_check_credentials(); |
| 437 | if( !g.perm.Read ){ login_needed(); return; } |
| 438 | db_begin_transaction(); |
| 439 | while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){ |
| 440 | zName = PD("name", "tip/index.wiki"); |
| 441 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 442 | zCheckin = mprintf("%.*s", i, zName); |
| 443 | if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ |
| 444 | zCheckin = "tip"; |
| 445 | } |
| 446 | if( nMiss==ArraySize(azSuffix) ){ |
| 447 | zName = "404.md"; |
| 448 | }else if( zName[i]==0 ){ |
| 449 | assert( nMiss>=0 && nMiss<ArraySize(azSuffix) ); |
| 450 | zName = azSuffix[nMiss]; |
| 451 | }else{ |
| 452 | zName += i; |
| 453 | } |
| 454 | while( zName[0]=='/' ){ zName++; } |
| 455 | g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName); |
| 456 | if( nMiss==0 ) zOrigName = zName; |
| 457 | if( !file_is_simple_pathname(zName, 1) ){ |
| 458 | if( sqlite3_strglob("*/", zName)==0 ){ |
| 459 | assert( nMiss>=0 && nMiss<ArraySize(azSuffix) ); |
| 460 | zName = mprintf("%s%s", zName, azSuffix[nMiss]); |
| 461 | if( !file_is_simple_pathname(zName, 1) ){ |
| 462 | goto doc_not_found; |
| 463 | } |
| 464 | }else{ |
| 465 | goto doc_not_found; |
| 466 | } |
| 467 | } |
| 468 | if( fossil_strcmp(zCheckin,"ckout")==0 ){ |
| 469 | /* Read from the local checkout */ |
| 470 | char *zFullpath; |
| 471 | db_must_be_within_tree(); |
| 472 | zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 473 | if( file_isfile(zFullpath) |
| 474 | && blob_read_from_file(&filebody, zFullpath)>0 ){ |
| 475 | rid = 1; /* Fake RID just to get the loop to end */ |
| 476 | } |
| 477 | fossil_free(zFullpath); |
| 478 | }else{ |
| 479 | vid = name_to_typed_rid(zCheckin, "ci"); |
| 480 | rid = doc_load_content(vid, zName, &filebody); |
| 481 | } |
| 482 | } |
| 483 | if( rid==0 ) goto doc_not_found; |
| 484 | blob_to_utf8_no_bom(&filebody, 0); |
| 485 | |
| 486 | /* The file is now contained in the filebody blob. Deliver the |
| 487 | ** file to the user |
| 488 | */ |
| 489 | zMime = nMiss==0 ? P("mimetype") : 0; |
| 490 | if( zMime==0 ){ |
| 491 | zMime = mimetype_from_name(zName); |
| 492 | } |
| 493 | Th_Store("doc_name", zName); |
| 494 | Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" |
| @@ -500,10 +495,11 @@ | |
| 495 | " FROM blob WHERE rid=%d", vid)); |
| 496 | Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" |
| 497 | " WHERE objid=%d AND type='ci'", vid)); |
| 498 | if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ |
| 499 | Blob title, tail; |
| 500 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 501 | if( wiki_find_title(&filebody, &title, &tail) ){ |
| 502 | style_header("%s", blob_str(&title)); |
| 503 | wiki_convert(&tail, 0, WIKI_BUTTONS); |
| 504 | }else{ |
| 505 | style_header("Documentation"); |
| @@ -515,11 +511,12 @@ | |
| 511 | Blob tail = BLOB_INITIALIZER; |
| 512 | markdown_to_html(&filebody, &title, &tail); |
| 513 | if( blob_size(&title)>0 ){ |
| 514 | style_header("%s", blob_str(&title)); |
| 515 | }else{ |
| 516 | style_header("%s", nMiss>=ArraySize(azSuffix)? |
| 517 | "Not Found" : "Documentation"); |
| 518 | } |
| 519 | blob_append(cgi_output_blob(), blob_buffer(&tail), blob_size(&tail)); |
| 520 | style_footer(); |
| 521 | }else if( fossil_strcmp(zMime, "text/plain")==0 ){ |
| 522 | style_header("Documentation"); |
| @@ -536,18 +533,25 @@ | |
| 533 | #endif |
| 534 | }else{ |
| 535 | cgi_set_content_type(zMime); |
| 536 | cgi_set_content(&filebody); |
| 537 | } |
| 538 | if( nMiss>=ArraySize(azSuffix) ) cgi_set_status(404, "Not Found"); |
| 539 | db_end_transaction(0); |
| 540 | return; |
| 541 | |
| 542 | /* Jump here when unable to locate the document */ |
| 543 | doc_not_found: |
| 544 | db_end_transaction(0); |
| 545 | cgi_set_status(404, "Not Found"); |
| 546 | style_header("Not Found"); |
| 547 | @ <p>Document %h(zOrigName) not found |
| 548 | if( fossil_strcmp(zCheckin,"ckout")!=0 ){ |
| 549 | @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> |
| 550 | } |
| 551 | style_footer(); |
| 552 | db_end_transaction(0); |
| 553 | return; |
| 554 | } |
| 555 | |
| 556 | /* |
| 557 | ** The default logo. |
| 558 |
+4
-4
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -104,18 +104,18 @@ | ||
| 104 | 104 | ** |
| 105 | 105 | ** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY? |
| 106 | 106 | ** |
| 107 | 107 | ** Write an export of all check-ins to standard output. The export is |
| 108 | 108 | ** written in the git-fast-export file format assuming the --git option is |
| 109 | -** provided. The git-fast-export format is currently the only VCS | |
| 109 | +** provided. The git-fast-export format is currently the only VCS | |
| 110 | 110 | ** interchange format supported, though other formats may be added in |
| 111 | 111 | ** the future. |
| 112 | 112 | ** |
| 113 | 113 | ** Run this command within a checkout. Or use the -R or --repository |
| 114 | 114 | ** option to specify a Fossil repository to be exported. |
| 115 | 115 | ** |
| 116 | -** Only check-ins are exported using --git. Git does not support tickets | |
| 116 | +** Only check-ins are exported using --git. Git does not support tickets | |
| 117 | 117 | ** or wiki or events or attachments, so none of those are exported. |
| 118 | 118 | ** |
| 119 | 119 | ** If the "--import-marks FILE" option is used, it contains a list of |
| 120 | 120 | ** rids to skip. |
| 121 | 121 | ** |
| @@ -124,11 +124,11 @@ | ||
| 124 | 124 | ** |
| 125 | 125 | ** Options: |
| 126 | 126 | ** --export-marks FILE export rids of exported data to FILE |
| 127 | 127 | ** --import-marks FILE read rids of data to ignore from FILE |
| 128 | 128 | ** --repository|-R REPOSITORY export the given REPOSITORY |
| 129 | -** | |
| 129 | +** | |
| 130 | 130 | ** See also: import |
| 131 | 131 | */ |
| 132 | 132 | void export_cmd(void){ |
| 133 | 133 | Stmt q, q2, q3; |
| 134 | 134 | int i; |
| @@ -179,11 +179,11 @@ | ||
| 179 | 179 | db_finalize(&qc); |
| 180 | 180 | fclose(f); |
| 181 | 181 | } |
| 182 | 182 | |
| 183 | 183 | /* Step 1: Generate "blob" records for every artifact that is part |
| 184 | - ** of a check-in | |
| 184 | + ** of a check-in | |
| 185 | 185 | */ |
| 186 | 186 | fossil_binary_mode(stdout); |
| 187 | 187 | db_multi_exec("CREATE TEMP TABLE newblob(rid INTEGER KEY, srcid INTEGER)"); |
| 188 | 188 | db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)"); |
| 189 | 189 | db_multi_exec( |
| 190 | 190 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -104,18 +104,18 @@ | |
| 104 | ** |
| 105 | ** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY? |
| 106 | ** |
| 107 | ** Write an export of all check-ins to standard output. The export is |
| 108 | ** written in the git-fast-export file format assuming the --git option is |
| 109 | ** provided. The git-fast-export format is currently the only VCS |
| 110 | ** interchange format supported, though other formats may be added in |
| 111 | ** the future. |
| 112 | ** |
| 113 | ** Run this command within a checkout. Or use the -R or --repository |
| 114 | ** option to specify a Fossil repository to be exported. |
| 115 | ** |
| 116 | ** Only check-ins are exported using --git. Git does not support tickets |
| 117 | ** or wiki or events or attachments, so none of those are exported. |
| 118 | ** |
| 119 | ** If the "--import-marks FILE" option is used, it contains a list of |
| 120 | ** rids to skip. |
| 121 | ** |
| @@ -124,11 +124,11 @@ | |
| 124 | ** |
| 125 | ** Options: |
| 126 | ** --export-marks FILE export rids of exported data to FILE |
| 127 | ** --import-marks FILE read rids of data to ignore from FILE |
| 128 | ** --repository|-R REPOSITORY export the given REPOSITORY |
| 129 | ** |
| 130 | ** See also: import |
| 131 | */ |
| 132 | void export_cmd(void){ |
| 133 | Stmt q, q2, q3; |
| 134 | int i; |
| @@ -179,11 +179,11 @@ | |
| 179 | db_finalize(&qc); |
| 180 | fclose(f); |
| 181 | } |
| 182 | |
| 183 | /* Step 1: Generate "blob" records for every artifact that is part |
| 184 | ** of a check-in |
| 185 | */ |
| 186 | fossil_binary_mode(stdout); |
| 187 | db_multi_exec("CREATE TEMP TABLE newblob(rid INTEGER KEY, srcid INTEGER)"); |
| 188 | db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)"); |
| 189 | db_multi_exec( |
| 190 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -104,18 +104,18 @@ | |
| 104 | ** |
| 105 | ** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY? |
| 106 | ** |
| 107 | ** Write an export of all check-ins to standard output. The export is |
| 108 | ** written in the git-fast-export file format assuming the --git option is |
| 109 | ** provided. The git-fast-export format is currently the only VCS |
| 110 | ** interchange format supported, though other formats may be added in |
| 111 | ** the future. |
| 112 | ** |
| 113 | ** Run this command within a checkout. Or use the -R or --repository |
| 114 | ** option to specify a Fossil repository to be exported. |
| 115 | ** |
| 116 | ** Only check-ins are exported using --git. Git does not support tickets |
| 117 | ** or wiki or events or attachments, so none of those are exported. |
| 118 | ** |
| 119 | ** If the "--import-marks FILE" option is used, it contains a list of |
| 120 | ** rids to skip. |
| 121 | ** |
| @@ -124,11 +124,11 @@ | |
| 124 | ** |
| 125 | ** Options: |
| 126 | ** --export-marks FILE export rids of exported data to FILE |
| 127 | ** --import-marks FILE read rids of data to ignore from FILE |
| 128 | ** --repository|-R REPOSITORY export the given REPOSITORY |
| 129 | ** |
| 130 | ** See also: import |
| 131 | */ |
| 132 | void export_cmd(void){ |
| 133 | Stmt q, q2, q3; |
| 134 | int i; |
| @@ -179,11 +179,11 @@ | |
| 179 | db_finalize(&qc); |
| 180 | fclose(f); |
| 181 | } |
| 182 | |
| 183 | /* Step 1: Generate "blob" records for every artifact that is part |
| 184 | ** of a check-in |
| 185 | */ |
| 186 | fossil_binary_mode(stdout); |
| 187 | db_multi_exec("CREATE TEMP TABLE newblob(rid INTEGER KEY, srcid INTEGER)"); |
| 188 | db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)"); |
| 189 | db_multi_exec( |
| 190 |
+19
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1262,10 +1262,29 @@ | ||
| 1262 | 1262 | char *zValue = getenv(zName); |
| 1263 | 1263 | #endif |
| 1264 | 1264 | if( zValue ) zValue = fossil_filename_to_utf8(zValue); |
| 1265 | 1265 | return zValue; |
| 1266 | 1266 | } |
| 1267 | + | |
| 1268 | +/* | |
| 1269 | +** Sets the value of an environment variable as UTF8. | |
| 1270 | +*/ | |
| 1271 | +int fossil_setenv(const char *zName, const char *zValue){ | |
| 1272 | + int rc; | |
| 1273 | + char *zString = mprintf("%s=%s", zName, zValue); | |
| 1274 | +#ifdef _WIN32 | |
| 1275 | + wchar_t *uString = fossil_utf8_to_unicode(zString); | |
| 1276 | + rc = _wputenv(uString); | |
| 1277 | + fossil_unicode_free(uString); | |
| 1278 | + fossil_free(zString); | |
| 1279 | +#else | |
| 1280 | + rc = putenv(zString); | |
| 1281 | + /* NOTE: Cannot free the string on POSIX. */ | |
| 1282 | + /* fossil_free(zString); */ | |
| 1283 | +#endif | |
| 1284 | + return rc; | |
| 1285 | +} | |
| 1267 | 1286 | |
| 1268 | 1287 | /* |
| 1269 | 1288 | ** Like fopen() but always takes a UTF8 argument. |
| 1270 | 1289 | */ |
| 1271 | 1290 | FILE *fossil_fopen(const char *zName, const char *zMode){ |
| 1272 | 1291 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1262,10 +1262,29 @@ | |
| 1262 | char *zValue = getenv(zName); |
| 1263 | #endif |
| 1264 | if( zValue ) zValue = fossil_filename_to_utf8(zValue); |
| 1265 | return zValue; |
| 1266 | } |
| 1267 | |
| 1268 | /* |
| 1269 | ** Like fopen() but always takes a UTF8 argument. |
| 1270 | */ |
| 1271 | FILE *fossil_fopen(const char *zName, const char *zMode){ |
| 1272 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1262,10 +1262,29 @@ | |
| 1262 | char *zValue = getenv(zName); |
| 1263 | #endif |
| 1264 | if( zValue ) zValue = fossil_filename_to_utf8(zValue); |
| 1265 | return zValue; |
| 1266 | } |
| 1267 | |
| 1268 | /* |
| 1269 | ** Sets the value of an environment variable as UTF8. |
| 1270 | */ |
| 1271 | int fossil_setenv(const char *zName, const char *zValue){ |
| 1272 | int rc; |
| 1273 | char *zString = mprintf("%s=%s", zName, zValue); |
| 1274 | #ifdef _WIN32 |
| 1275 | wchar_t *uString = fossil_utf8_to_unicode(zString); |
| 1276 | rc = _wputenv(uString); |
| 1277 | fossil_unicode_free(uString); |
| 1278 | fossil_free(zString); |
| 1279 | #else |
| 1280 | rc = putenv(zString); |
| 1281 | /* NOTE: Cannot free the string on POSIX. */ |
| 1282 | /* fossil_free(zString); */ |
| 1283 | #endif |
| 1284 | return rc; |
| 1285 | } |
| 1286 | |
| 1287 | /* |
| 1288 | ** Like fopen() but always takes a UTF8 argument. |
| 1289 | */ |
| 1290 | FILE *fossil_fopen(const char *zName, const char *zMode){ |
| 1291 |
+69
-55
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -250,11 +250,11 @@ | ||
| 250 | 250 | int rc; |
| 251 | 251 | Blob content, fname; |
| 252 | 252 | const char *zRev; |
| 253 | 253 | db_find_and_open_repository(0, 0); |
| 254 | 254 | zRev = find_option("r","r",1); |
| 255 | - | |
| 255 | + | |
| 256 | 256 | /* We should be done with options.. */ |
| 257 | 257 | verify_all_options(); |
| 258 | 258 | |
| 259 | 259 | for(i=2; i<g.argc; i++){ |
| 260 | 260 | file_tree_name(g.argv[i], &fname, 1); |
| @@ -284,110 +284,92 @@ | ||
| 284 | 284 | ** b=DATE Only show changes before DATE |
| 285 | 285 | ** n=NUM Show the first NUM changes only |
| 286 | 286 | ** brbg Background color by branch name |
| 287 | 287 | ** ubg Background color by user name |
| 288 | 288 | ** ci=UUID Ancestors of a particular check-in |
| 289 | -** fco=BOOL Show only first occurrence of each version if true (default) | |
| 290 | 289 | */ |
| 291 | 290 | void finfo_page(void){ |
| 292 | 291 | Stmt q; |
| 293 | 292 | const char *zFilename; |
| 294 | 293 | char zPrevDate[20]; |
| 295 | 294 | const char *zA; |
| 296 | 295 | const char *zB; |
| 297 | 296 | int n; |
| 298 | 297 | int baseCheckin; |
| 299 | - | |
| 298 | + int fnid; | |
| 299 | + Bag ancestor; | |
| 300 | 300 | Blob title; |
| 301 | 301 | Blob sql; |
| 302 | 302 | HQuery url; |
| 303 | 303 | GraphContext *pGraph; |
| 304 | 304 | int brBg = P("brbg")!=0; |
| 305 | 305 | int uBg = P("ubg")!=0; |
| 306 | - int firstChngOnly = atoi(PD("fco","1"))!=0; | |
| 307 | 306 | int fDebug = atoi(PD("debug","0")); |
| 307 | + int fShowId = P("showid")!=0; | |
| 308 | 308 | |
| 309 | 309 | login_check_credentials(); |
| 310 | 310 | if( !g.perm.Read ){ login_needed(); return; } |
| 311 | 311 | style_header("File History"); |
| 312 | 312 | login_anonymous_available(); |
| 313 | 313 | url_initialize(&url, "finfo"); |
| 314 | 314 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 315 | 315 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 316 | 316 | baseCheckin = name_to_rid_www("ci"); |
| 317 | - if( baseCheckin ) firstChngOnly = 1; | |
| 318 | - if( !firstChngOnly ) url_add_parameter(&url, "fco", "0"); | |
| 319 | - | |
| 320 | 317 | zPrevDate[0] = 0; |
| 321 | 318 | zFilename = PD("name",""); |
| 319 | + fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); | |
| 320 | + if( fnid==0 ){ | |
| 321 | + @ No such file: %h(zFilename) | |
| 322 | + style_footer(); | |
| 323 | + return; | |
| 324 | + } | |
| 325 | + if( baseCheckin ){ | |
| 326 | + int baseFid = db_int(0, | |
| 327 | + "SELECT fid FROM mlink WHERE fnid=%d AND mid=%d", | |
| 328 | + fnid, baseCheckin | |
| 329 | + ); | |
| 330 | + bag_init(&ancestor); | |
| 331 | + if( baseFid ) bag_insert(&ancestor, baseFid); | |
| 332 | + } | |
| 322 | 333 | url_add_parameter(&url, "name", zFilename); |
| 323 | 334 | blob_zero(&sql); |
| 324 | 335 | blob_append_sql(&sql, |
| 325 | 336 | "SELECT" |
| 326 | - " datetime(event.mtime%s)," /* Date of change */ | |
| 337 | + " datetime(min(event.mtime)%s)," /* Date of change */ | |
| 327 | 338 | " coalesce(event.ecomment, event.comment)," /* Check-in comment */ |
| 328 | 339 | " coalesce(event.euser, event.user)," /* User who made chng */ |
| 329 | 340 | " mlink.pid," /* Parent file rid */ |
| 330 | 341 | " mlink.fid," /* File rid */ |
| 331 | 342 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */ |
| 332 | 343 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* Current file uuid */ |
| 333 | 344 | " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* Check-in uuid */ |
| 334 | 345 | " event.bgcolor," /* Background color */ |
| 335 | 346 | " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" |
| 336 | - " AND tagxref.rid=mlink.mid)," /* Tags */ | |
| 347 | + " AND tagxref.rid=mlink.mid)," /* Branchname */ | |
| 337 | 348 | " mlink.mid," /* check-in ID */ |
| 338 | - " mlink.pfnid", /* Previous filename */ | |
| 339 | - timeline_utc(), TAG_BRANCH | |
| 340 | - ); | |
| 341 | - if( firstChngOnly ){ | |
| 342 | -#if 0 | |
| 343 | - blob_append_sql(&sql, ", min(event.mtime)"); | |
| 344 | -#else | |
| 345 | - blob_append_sql(&sql, | |
| 346 | - ", min(CASE (SELECT value FROM tagxref" | |
| 347 | - " WHERE tagtype>0 AND tagid=%d" | |
| 348 | - " AND tagxref.rid=mlink.mid)" | |
| 349 | - " WHEN 'trunk' THEN event.mtime-10000 ELSE event.mtime END)", | |
| 350 | - TAG_BRANCH); | |
| 351 | -#endif | |
| 352 | - } | |
| 353 | - blob_append_sql(&sql, | |
| 349 | + " mlink.pfnid" /* Previous filename */ | |
| 354 | 350 | " FROM mlink, event" |
| 355 | - " WHERE mlink.fnid IN (SELECT fnid FROM filename WHERE name=%Q)" | |
| 351 | + " WHERE mlink.fnid=%d" | |
| 356 | 352 | " AND event.objid=mlink.mid", |
| 357 | - zFilename | |
| 353 | + timeline_utc(), TAG_BRANCH, fnid | |
| 358 | 354 | ); |
| 359 | - if( baseCheckin ){ | |
| 360 | - compute_direct_ancestors(baseCheckin, 10000000); | |
| 361 | - blob_append_sql(&sql," AND mlink.mid IN (SELECT rid FROM ancestor)"); | |
| 362 | - } | |
| 363 | 355 | if( (zA = P("a"))!=0 ){ |
| 364 | 356 | blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zA); |
| 365 | 357 | url_add_parameter(&url, "a", zA); |
| 366 | 358 | } |
| 367 | 359 | if( (zB = P("b"))!=0 ){ |
| 368 | 360 | blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zB); |
| 369 | 361 | url_add_parameter(&url, "b", zB); |
| 370 | 362 | } |
| 371 | - if( firstChngOnly ){ | |
| 372 | - blob_append_sql(&sql, " GROUP BY mlink.fid"); | |
| 373 | - } | |
| 374 | - blob_append_sql(&sql," ORDER BY event.mtime DESC /*sort*/"); | |
| 363 | + blob_append_sql(&sql, | |
| 364 | + " GROUP BY mlink.fid" | |
| 365 | + " ORDER BY event.mtime DESC /*sort*/" | |
| 366 | + ); | |
| 375 | 367 | if( (n = atoi(PD("n","0")))>0 ){ |
| 376 | 368 | blob_append_sql(&sql, " LIMIT %d", n); |
| 377 | 369 | url_add_parameter(&url, "n", P("n")); |
| 378 | 370 | } |
| 379 | - if( baseCheckin==0 ){ | |
| 380 | - if( firstChngOnly ){ | |
| 381 | - style_submenu_element("Full", "Show all changes","%s", | |
| 382 | - url_render(&url, "fco", "0", 0, 0)); | |
| 383 | - }else{ | |
| 384 | - style_submenu_element("Simplified", | |
| 385 | - "Show only first use of a change","%s", | |
| 386 | - url_render(&url, "fco", 0, 0, 0)); | |
| 387 | - } | |
| 388 | - } | |
| 389 | 371 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 390 | 372 | if( P("showsql")!=0 ){ |
| 391 | 373 | @ <p>SQL: %h(blob_str(&sql))</p> |
| 392 | 374 | } |
| 393 | 375 | blob_reset(&sql); |
| @@ -395,15 +377,18 @@ | ||
| 395 | 377 | if( baseCheckin ){ |
| 396 | 378 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin); |
| 397 | 379 | char *zLink = href("%R/info/%s", zUuid); |
| 398 | 380 | blob_appendf(&title, "Ancestors of file "); |
| 399 | 381 | hyperlinked_path(zFilename, &title, zUuid, "tree", ""); |
| 382 | + if( fShowId ) blob_appendf(&title, " (%d)", fnid); | |
| 400 | 383 | blob_appendf(&title, " from check-in %z%S</a>", zLink, zUuid); |
| 384 | + if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin); | |
| 401 | 385 | fossil_free(zUuid); |
| 402 | 386 | }else{ |
| 403 | 387 | blob_appendf(&title, "History of files named "); |
| 404 | 388 | hyperlinked_path(zFilename, &title, 0, "tree", ""); |
| 389 | + if( fShowId ) blob_appendf(&title, " (%d)", fnid); | |
| 405 | 390 | } |
| 406 | 391 | @ <h2>%b(&title)</h2> |
| 407 | 392 | blob_reset(&title); |
| 408 | 393 | pGraph = graph_init(); |
| 409 | 394 | @ <div id="canvas" style="position:relative;width:1px;height:1px;" |
| @@ -422,17 +407,36 @@ | ||
| 422 | 407 | const char *zBr = db_column_text(&q, 9); |
| 423 | 408 | int fmid = db_column_int(&q, 10); |
| 424 | 409 | int pfnid = db_column_int(&q, 11); |
| 425 | 410 | int gidx; |
| 426 | 411 | char zTime[10]; |
| 412 | + int nParent = 0; | |
| 413 | + int aParent[GR_MAX_RAIL]; | |
| 414 | + static Stmt qparent; | |
| 415 | + | |
| 416 | + if( baseCheckin && frid && !bag_find(&ancestor, frid) ) continue; | |
| 417 | + db_static_prepare(&qparent, | |
| 418 | + "SELECT DISTINCT pid FROM mlink" | |
| 419 | + " WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid" | |
| 420 | + " ORDER BY isaux /*sort*/" | |
| 421 | + ); | |
| 422 | + db_bind_int(&qparent, ":fid", frid); | |
| 423 | + db_bind_int(&qparent, ":mid", fmid); | |
| 424 | + db_bind_int(&qparent, ":fnid", fnid); | |
| 425 | + while( db_step(&qparent)==SQLITE_ROW && nParent<ArraySize(aParent) ){ | |
| 426 | + aParent[nParent] = db_column_int(&qparent, 0); | |
| 427 | + if( baseCheckin ) bag_insert(&ancestor, aParent[nParent]); | |
| 428 | + nParent++; | |
| 429 | + } | |
| 430 | + db_reset(&qparent); | |
| 427 | 431 | if( zBr==0 ) zBr = "trunk"; |
| 428 | 432 | if( uBg ){ |
| 429 | 433 | zBgClr = hash_color(zUser); |
| 430 | 434 | }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){ |
| 431 | 435 | zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr); |
| 432 | 436 | } |
| 433 | - gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr, | |
| 437 | + gidx = graph_add_row(pGraph, frid, nParent, aParent, zBr, zBgClr, | |
| 434 | 438 | zUuid, 0); |
| 435 | 439 | if( strncmp(zDate, zPrevDate, 10) ){ |
| 436 | 440 | sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate); |
| 437 | 441 | @ <tr><td> |
| 438 | 442 | @ <div class="divider">%s(zPrevDate)</div> |
| @@ -447,19 +451,23 @@ | ||
| 447 | 451 | @ <td class="timelineTableCell" style="background-color: %h(zBgClr);"> |
| 448 | 452 | }else{ |
| 449 | 453 | @ <td class="timelineTableCell"> |
| 450 | 454 | } |
| 451 | 455 | if( zUuid ){ |
| 452 | - if( fpid==0 ){ | |
| 456 | + if( nParent==0 ){ | |
| 453 | 457 | @ <b>Added</b> |
| 454 | 458 | }else if( pfnid ){ |
| 455 | 459 | char *zPrevName = db_text(0, "SELECT name FROM filename WHERE fnid=%d", |
| 456 | 460 | pfnid); |
| 457 | 461 | @ <b>Renamed</b> from |
| 458 | 462 | @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a> |
| 459 | 463 | } |
| 460 | - @ %z(href("%R/artifact/%s",zUuid))[%S(zUuid)]</a> part of check-in | |
| 464 | + @ %z(href("%R/artifact/%s",zUuid))[%S(zUuid)]</a> | |
| 465 | + if( fShowId ){ | |
| 466 | + @ (%d(frid)) | |
| 467 | + } | |
| 468 | + @ part of check-in | |
| 461 | 469 | }else{ |
| 462 | 470 | char *zNewName; |
| 463 | 471 | zNewName = db_text(0, |
| 464 | 472 | "SELECT name FROM filename WHERE fnid = " |
| 465 | 473 | " (SELECT fnid FROM mlink" |
| @@ -473,13 +481,16 @@ | ||
| 473 | 481 | }else{ |
| 474 | 482 | @ <b>Deleted</b> by check-in |
| 475 | 483 | } |
| 476 | 484 | } |
| 477 | 485 | hyperlink_to_uuid(zCkin); |
| 486 | + if( fShowId ){ | |
| 487 | + @ (%d(fmid)) | |
| 488 | + } | |
| 478 | 489 | @ %W(zCom) (user: |
| 479 | 490 | hyperlink_to_user(zUser, zDate, ""); |
| 480 | - @ branch: %h(zBr)) | |
| 491 | + @ branch: %z(href("%R/timeline?t=%T&n=200",zBr))%h(zBr)</a> | |
| 481 | 492 | if( g.perm.Hyperlink && zUuid ){ |
| 482 | 493 | const char *z = zFilename; |
| 483 | 494 | @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin)) |
| 484 | 495 | @ [annotate]</a> |
| 485 | 496 | @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin)) |
| @@ -488,23 +499,26 @@ | ||
| 488 | 499 | if( fpid ){ |
| 489 | 500 | @ %z(href("%R/fdiff?sbs=1&v1=%s&v2=%s",zPUuid,zUuid))[diff]</a> |
| 490 | 501 | } |
| 491 | 502 | } |
| 492 | 503 | if( fDebug & FINFO_DEBUG_MLINK ){ |
| 493 | - int srcid = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", frid); | |
| 494 | - int sz = db_int(0, "SELECT length(content) FROM blob WHERE rid=%d", frid); | |
| 495 | - @ <br>fid=%d(frid) pid=%d(fpid) mid=%d(fmid) sz=%d(sz) | |
| 496 | - if( srcid ){ | |
| 497 | - @ srcid=%d(srcid) | |
| 504 | + int ii; | |
| 505 | + @ <br>fid=%d(frid) pid=%d(fpid) mid=%d(fmid) | |
| 506 | + if( nParent>0 ){ | |
| 507 | + @ parents=%d(aParent[0]) | |
| 508 | + for(ii=1; ii<nParent; ii++){ | |
| 509 | + @ %d(aParent[ii]) | |
| 510 | + } | |
| 498 | 511 | } |
| 512 | + @ %z(href("%R/finfo?name=%T&ci=%s&debug=1",zFilename,zCkin))[ancestry]</a> | |
| 499 | 513 | } |
| 500 | 514 | tag_private_status(frid); |
| 501 | 515 | @ </td></tr> |
| 502 | 516 | } |
| 503 | 517 | db_finalize(&q); |
| 504 | 518 | if( pGraph ){ |
| 505 | - graph_finish(pGraph, 0); | |
| 519 | + graph_finish(pGraph, 1); | |
| 506 | 520 | if( pGraph->nErr ){ |
| 507 | 521 | graph_free(pGraph); |
| 508 | 522 | pGraph = 0; |
| 509 | 523 | }else{ |
| 510 | 524 | int w = (pGraph->mxRail+1)*pGraph->iRailPitch + 10; |
| 511 | 525 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -250,11 +250,11 @@ | |
| 250 | int rc; |
| 251 | Blob content, fname; |
| 252 | const char *zRev; |
| 253 | db_find_and_open_repository(0, 0); |
| 254 | zRev = find_option("r","r",1); |
| 255 | |
| 256 | /* We should be done with options.. */ |
| 257 | verify_all_options(); |
| 258 | |
| 259 | for(i=2; i<g.argc; i++){ |
| 260 | file_tree_name(g.argv[i], &fname, 1); |
| @@ -284,110 +284,92 @@ | |
| 284 | ** b=DATE Only show changes before DATE |
| 285 | ** n=NUM Show the first NUM changes only |
| 286 | ** brbg Background color by branch name |
| 287 | ** ubg Background color by user name |
| 288 | ** ci=UUID Ancestors of a particular check-in |
| 289 | ** fco=BOOL Show only first occurrence of each version if true (default) |
| 290 | */ |
| 291 | void finfo_page(void){ |
| 292 | Stmt q; |
| 293 | const char *zFilename; |
| 294 | char zPrevDate[20]; |
| 295 | const char *zA; |
| 296 | const char *zB; |
| 297 | int n; |
| 298 | int baseCheckin; |
| 299 | |
| 300 | Blob title; |
| 301 | Blob sql; |
| 302 | HQuery url; |
| 303 | GraphContext *pGraph; |
| 304 | int brBg = P("brbg")!=0; |
| 305 | int uBg = P("ubg")!=0; |
| 306 | int firstChngOnly = atoi(PD("fco","1"))!=0; |
| 307 | int fDebug = atoi(PD("debug","0")); |
| 308 | |
| 309 | login_check_credentials(); |
| 310 | if( !g.perm.Read ){ login_needed(); return; } |
| 311 | style_header("File History"); |
| 312 | login_anonymous_available(); |
| 313 | url_initialize(&url, "finfo"); |
| 314 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 315 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 316 | baseCheckin = name_to_rid_www("ci"); |
| 317 | if( baseCheckin ) firstChngOnly = 1; |
| 318 | if( !firstChngOnly ) url_add_parameter(&url, "fco", "0"); |
| 319 | |
| 320 | zPrevDate[0] = 0; |
| 321 | zFilename = PD("name",""); |
| 322 | url_add_parameter(&url, "name", zFilename); |
| 323 | blob_zero(&sql); |
| 324 | blob_append_sql(&sql, |
| 325 | "SELECT" |
| 326 | " datetime(event.mtime%s)," /* Date of change */ |
| 327 | " coalesce(event.ecomment, event.comment)," /* Check-in comment */ |
| 328 | " coalesce(event.euser, event.user)," /* User who made chng */ |
| 329 | " mlink.pid," /* Parent file rid */ |
| 330 | " mlink.fid," /* File rid */ |
| 331 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */ |
| 332 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* Current file uuid */ |
| 333 | " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* Check-in uuid */ |
| 334 | " event.bgcolor," /* Background color */ |
| 335 | " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" |
| 336 | " AND tagxref.rid=mlink.mid)," /* Tags */ |
| 337 | " mlink.mid," /* check-in ID */ |
| 338 | " mlink.pfnid", /* Previous filename */ |
| 339 | timeline_utc(), TAG_BRANCH |
| 340 | ); |
| 341 | if( firstChngOnly ){ |
| 342 | #if 0 |
| 343 | blob_append_sql(&sql, ", min(event.mtime)"); |
| 344 | #else |
| 345 | blob_append_sql(&sql, |
| 346 | ", min(CASE (SELECT value FROM tagxref" |
| 347 | " WHERE tagtype>0 AND tagid=%d" |
| 348 | " AND tagxref.rid=mlink.mid)" |
| 349 | " WHEN 'trunk' THEN event.mtime-10000 ELSE event.mtime END)", |
| 350 | TAG_BRANCH); |
| 351 | #endif |
| 352 | } |
| 353 | blob_append_sql(&sql, |
| 354 | " FROM mlink, event" |
| 355 | " WHERE mlink.fnid IN (SELECT fnid FROM filename WHERE name=%Q)" |
| 356 | " AND event.objid=mlink.mid", |
| 357 | zFilename |
| 358 | ); |
| 359 | if( baseCheckin ){ |
| 360 | compute_direct_ancestors(baseCheckin, 10000000); |
| 361 | blob_append_sql(&sql," AND mlink.mid IN (SELECT rid FROM ancestor)"); |
| 362 | } |
| 363 | if( (zA = P("a"))!=0 ){ |
| 364 | blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zA); |
| 365 | url_add_parameter(&url, "a", zA); |
| 366 | } |
| 367 | if( (zB = P("b"))!=0 ){ |
| 368 | blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zB); |
| 369 | url_add_parameter(&url, "b", zB); |
| 370 | } |
| 371 | if( firstChngOnly ){ |
| 372 | blob_append_sql(&sql, " GROUP BY mlink.fid"); |
| 373 | } |
| 374 | blob_append_sql(&sql," ORDER BY event.mtime DESC /*sort*/"); |
| 375 | if( (n = atoi(PD("n","0")))>0 ){ |
| 376 | blob_append_sql(&sql, " LIMIT %d", n); |
| 377 | url_add_parameter(&url, "n", P("n")); |
| 378 | } |
| 379 | if( baseCheckin==0 ){ |
| 380 | if( firstChngOnly ){ |
| 381 | style_submenu_element("Full", "Show all changes","%s", |
| 382 | url_render(&url, "fco", "0", 0, 0)); |
| 383 | }else{ |
| 384 | style_submenu_element("Simplified", |
| 385 | "Show only first use of a change","%s", |
| 386 | url_render(&url, "fco", 0, 0, 0)); |
| 387 | } |
| 388 | } |
| 389 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 390 | if( P("showsql")!=0 ){ |
| 391 | @ <p>SQL: %h(blob_str(&sql))</p> |
| 392 | } |
| 393 | blob_reset(&sql); |
| @@ -395,15 +377,18 @@ | |
| 395 | if( baseCheckin ){ |
| 396 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin); |
| 397 | char *zLink = href("%R/info/%s", zUuid); |
| 398 | blob_appendf(&title, "Ancestors of file "); |
| 399 | hyperlinked_path(zFilename, &title, zUuid, "tree", ""); |
| 400 | blob_appendf(&title, " from check-in %z%S</a>", zLink, zUuid); |
| 401 | fossil_free(zUuid); |
| 402 | }else{ |
| 403 | blob_appendf(&title, "History of files named "); |
| 404 | hyperlinked_path(zFilename, &title, 0, "tree", ""); |
| 405 | } |
| 406 | @ <h2>%b(&title)</h2> |
| 407 | blob_reset(&title); |
| 408 | pGraph = graph_init(); |
| 409 | @ <div id="canvas" style="position:relative;width:1px;height:1px;" |
| @@ -422,17 +407,36 @@ | |
| 422 | const char *zBr = db_column_text(&q, 9); |
| 423 | int fmid = db_column_int(&q, 10); |
| 424 | int pfnid = db_column_int(&q, 11); |
| 425 | int gidx; |
| 426 | char zTime[10]; |
| 427 | if( zBr==0 ) zBr = "trunk"; |
| 428 | if( uBg ){ |
| 429 | zBgClr = hash_color(zUser); |
| 430 | }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){ |
| 431 | zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr); |
| 432 | } |
| 433 | gidx = graph_add_row(pGraph, frid, fpid>0 ? 1 : 0, &fpid, zBr, zBgClr, |
| 434 | zUuid, 0); |
| 435 | if( strncmp(zDate, zPrevDate, 10) ){ |
| 436 | sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate); |
| 437 | @ <tr><td> |
| 438 | @ <div class="divider">%s(zPrevDate)</div> |
| @@ -447,19 +451,23 @@ | |
| 447 | @ <td class="timelineTableCell" style="background-color: %h(zBgClr);"> |
| 448 | }else{ |
| 449 | @ <td class="timelineTableCell"> |
| 450 | } |
| 451 | if( zUuid ){ |
| 452 | if( fpid==0 ){ |
| 453 | @ <b>Added</b> |
| 454 | }else if( pfnid ){ |
| 455 | char *zPrevName = db_text(0, "SELECT name FROM filename WHERE fnid=%d", |
| 456 | pfnid); |
| 457 | @ <b>Renamed</b> from |
| 458 | @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a> |
| 459 | } |
| 460 | @ %z(href("%R/artifact/%s",zUuid))[%S(zUuid)]</a> part of check-in |
| 461 | }else{ |
| 462 | char *zNewName; |
| 463 | zNewName = db_text(0, |
| 464 | "SELECT name FROM filename WHERE fnid = " |
| 465 | " (SELECT fnid FROM mlink" |
| @@ -473,13 +481,16 @@ | |
| 473 | }else{ |
| 474 | @ <b>Deleted</b> by check-in |
| 475 | } |
| 476 | } |
| 477 | hyperlink_to_uuid(zCkin); |
| 478 | @ %W(zCom) (user: |
| 479 | hyperlink_to_user(zUser, zDate, ""); |
| 480 | @ branch: %h(zBr)) |
| 481 | if( g.perm.Hyperlink && zUuid ){ |
| 482 | const char *z = zFilename; |
| 483 | @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin)) |
| 484 | @ [annotate]</a> |
| 485 | @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin)) |
| @@ -488,23 +499,26 @@ | |
| 488 | if( fpid ){ |
| 489 | @ %z(href("%R/fdiff?sbs=1&v1=%s&v2=%s",zPUuid,zUuid))[diff]</a> |
| 490 | } |
| 491 | } |
| 492 | if( fDebug & FINFO_DEBUG_MLINK ){ |
| 493 | int srcid = db_int(0, "SELECT srcid FROM delta WHERE rid=%d", frid); |
| 494 | int sz = db_int(0, "SELECT length(content) FROM blob WHERE rid=%d", frid); |
| 495 | @ <br>fid=%d(frid) pid=%d(fpid) mid=%d(fmid) sz=%d(sz) |
| 496 | if( srcid ){ |
| 497 | @ srcid=%d(srcid) |
| 498 | } |
| 499 | } |
| 500 | tag_private_status(frid); |
| 501 | @ </td></tr> |
| 502 | } |
| 503 | db_finalize(&q); |
| 504 | if( pGraph ){ |
| 505 | graph_finish(pGraph, 0); |
| 506 | if( pGraph->nErr ){ |
| 507 | graph_free(pGraph); |
| 508 | pGraph = 0; |
| 509 | }else{ |
| 510 | int w = (pGraph->mxRail+1)*pGraph->iRailPitch + 10; |
| 511 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -250,11 +250,11 @@ | |
| 250 | int rc; |
| 251 | Blob content, fname; |
| 252 | const char *zRev; |
| 253 | db_find_and_open_repository(0, 0); |
| 254 | zRev = find_option("r","r",1); |
| 255 | |
| 256 | /* We should be done with options.. */ |
| 257 | verify_all_options(); |
| 258 | |
| 259 | for(i=2; i<g.argc; i++){ |
| 260 | file_tree_name(g.argv[i], &fname, 1); |
| @@ -284,110 +284,92 @@ | |
| 284 | ** b=DATE Only show changes before DATE |
| 285 | ** n=NUM Show the first NUM changes only |
| 286 | ** brbg Background color by branch name |
| 287 | ** ubg Background color by user name |
| 288 | ** ci=UUID Ancestors of a particular check-in |
| 289 | */ |
| 290 | void finfo_page(void){ |
| 291 | Stmt q; |
| 292 | const char *zFilename; |
| 293 | char zPrevDate[20]; |
| 294 | const char *zA; |
| 295 | const char *zB; |
| 296 | int n; |
| 297 | int baseCheckin; |
| 298 | int fnid; |
| 299 | Bag ancestor; |
| 300 | Blob title; |
| 301 | Blob sql; |
| 302 | HQuery url; |
| 303 | GraphContext *pGraph; |
| 304 | int brBg = P("brbg")!=0; |
| 305 | int uBg = P("ubg")!=0; |
| 306 | int fDebug = atoi(PD("debug","0")); |
| 307 | int fShowId = P("showid")!=0; |
| 308 | |
| 309 | login_check_credentials(); |
| 310 | if( !g.perm.Read ){ login_needed(); return; } |
| 311 | style_header("File History"); |
| 312 | login_anonymous_available(); |
| 313 | url_initialize(&url, "finfo"); |
| 314 | if( brBg ) url_add_parameter(&url, "brbg", 0); |
| 315 | if( uBg ) url_add_parameter(&url, "ubg", 0); |
| 316 | baseCheckin = name_to_rid_www("ci"); |
| 317 | zPrevDate[0] = 0; |
| 318 | zFilename = PD("name",""); |
| 319 | fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename); |
| 320 | if( fnid==0 ){ |
| 321 | @ No such file: %h(zFilename) |
| 322 | style_footer(); |
| 323 | return; |
| 324 | } |
| 325 | if( baseCheckin ){ |
| 326 | int baseFid = db_int(0, |
| 327 | "SELECT fid FROM mlink WHERE fnid=%d AND mid=%d", |
| 328 | fnid, baseCheckin |
| 329 | ); |
| 330 | bag_init(&ancestor); |
| 331 | if( baseFid ) bag_insert(&ancestor, baseFid); |
| 332 | } |
| 333 | url_add_parameter(&url, "name", zFilename); |
| 334 | blob_zero(&sql); |
| 335 | blob_append_sql(&sql, |
| 336 | "SELECT" |
| 337 | " datetime(min(event.mtime)%s)," /* Date of change */ |
| 338 | " coalesce(event.ecomment, event.comment)," /* Check-in comment */ |
| 339 | " coalesce(event.euser, event.user)," /* User who made chng */ |
| 340 | " mlink.pid," /* Parent file rid */ |
| 341 | " mlink.fid," /* File rid */ |
| 342 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," /* Parent file uuid */ |
| 343 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* Current file uuid */ |
| 344 | " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* Check-in uuid */ |
| 345 | " event.bgcolor," /* Background color */ |
| 346 | " (SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0" |
| 347 | " AND tagxref.rid=mlink.mid)," /* Branchname */ |
| 348 | " mlink.mid," /* check-in ID */ |
| 349 | " mlink.pfnid" /* Previous filename */ |
| 350 | " FROM mlink, event" |
| 351 | " WHERE mlink.fnid=%d" |
| 352 | " AND event.objid=mlink.mid", |
| 353 | timeline_utc(), TAG_BRANCH, fnid |
| 354 | ); |
| 355 | if( (zA = P("a"))!=0 ){ |
| 356 | blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zA); |
| 357 | url_add_parameter(&url, "a", zA); |
| 358 | } |
| 359 | if( (zB = P("b"))!=0 ){ |
| 360 | blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zB); |
| 361 | url_add_parameter(&url, "b", zB); |
| 362 | } |
| 363 | blob_append_sql(&sql, |
| 364 | " GROUP BY mlink.fid" |
| 365 | " ORDER BY event.mtime DESC /*sort*/" |
| 366 | ); |
| 367 | if( (n = atoi(PD("n","0")))>0 ){ |
| 368 | blob_append_sql(&sql, " LIMIT %d", n); |
| 369 | url_add_parameter(&url, "n", P("n")); |
| 370 | } |
| 371 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 372 | if( P("showsql")!=0 ){ |
| 373 | @ <p>SQL: %h(blob_str(&sql))</p> |
| 374 | } |
| 375 | blob_reset(&sql); |
| @@ -395,15 +377,18 @@ | |
| 377 | if( baseCheckin ){ |
| 378 | char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", baseCheckin); |
| 379 | char *zLink = href("%R/info/%s", zUuid); |
| 380 | blob_appendf(&title, "Ancestors of file "); |
| 381 | hyperlinked_path(zFilename, &title, zUuid, "tree", ""); |
| 382 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 383 | blob_appendf(&title, " from check-in %z%S</a>", zLink, zUuid); |
| 384 | if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin); |
| 385 | fossil_free(zUuid); |
| 386 | }else{ |
| 387 | blob_appendf(&title, "History of files named "); |
| 388 | hyperlinked_path(zFilename, &title, 0, "tree", ""); |
| 389 | if( fShowId ) blob_appendf(&title, " (%d)", fnid); |
| 390 | } |
| 391 | @ <h2>%b(&title)</h2> |
| 392 | blob_reset(&title); |
| 393 | pGraph = graph_init(); |
| 394 | @ <div id="canvas" style="position:relative;width:1px;height:1px;" |
| @@ -422,17 +407,36 @@ | |
| 407 | const char *zBr = db_column_text(&q, 9); |
| 408 | int fmid = db_column_int(&q, 10); |
| 409 | int pfnid = db_column_int(&q, 11); |
| 410 | int gidx; |
| 411 | char zTime[10]; |
| 412 | int nParent = 0; |
| 413 | int aParent[GR_MAX_RAIL]; |
| 414 | static Stmt qparent; |
| 415 | |
| 416 | if( baseCheckin && frid && !bag_find(&ancestor, frid) ) continue; |
| 417 | db_static_prepare(&qparent, |
| 418 | "SELECT DISTINCT pid FROM mlink" |
| 419 | " WHERE fid=:fid AND mid=:mid AND pid>0 AND fnid=:fnid" |
| 420 | " ORDER BY isaux /*sort*/" |
| 421 | ); |
| 422 | db_bind_int(&qparent, ":fid", frid); |
| 423 | db_bind_int(&qparent, ":mid", fmid); |
| 424 | db_bind_int(&qparent, ":fnid", fnid); |
| 425 | while( db_step(&qparent)==SQLITE_ROW && nParent<ArraySize(aParent) ){ |
| 426 | aParent[nParent] = db_column_int(&qparent, 0); |
| 427 | if( baseCheckin ) bag_insert(&ancestor, aParent[nParent]); |
| 428 | nParent++; |
| 429 | } |
| 430 | db_reset(&qparent); |
| 431 | if( zBr==0 ) zBr = "trunk"; |
| 432 | if( uBg ){ |
| 433 | zBgClr = hash_color(zUser); |
| 434 | }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){ |
| 435 | zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr); |
| 436 | } |
| 437 | gidx = graph_add_row(pGraph, frid, nParent, aParent, zBr, zBgClr, |
| 438 | zUuid, 0); |
| 439 | if( strncmp(zDate, zPrevDate, 10) ){ |
| 440 | sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate); |
| 441 | @ <tr><td> |
| 442 | @ <div class="divider">%s(zPrevDate)</div> |
| @@ -447,19 +451,23 @@ | |
| 451 | @ <td class="timelineTableCell" style="background-color: %h(zBgClr);"> |
| 452 | }else{ |
| 453 | @ <td class="timelineTableCell"> |
| 454 | } |
| 455 | if( zUuid ){ |
| 456 | if( nParent==0 ){ |
| 457 | @ <b>Added</b> |
| 458 | }else if( pfnid ){ |
| 459 | char *zPrevName = db_text(0, "SELECT name FROM filename WHERE fnid=%d", |
| 460 | pfnid); |
| 461 | @ <b>Renamed</b> from |
| 462 | @ %z(href("%R/finfo?name=%t", zPrevName))%h(zPrevName)</a> |
| 463 | } |
| 464 | @ %z(href("%R/artifact/%s",zUuid))[%S(zUuid)]</a> |
| 465 | if( fShowId ){ |
| 466 | @ (%d(frid)) |
| 467 | } |
| 468 | @ part of check-in |
| 469 | }else{ |
| 470 | char *zNewName; |
| 471 | zNewName = db_text(0, |
| 472 | "SELECT name FROM filename WHERE fnid = " |
| 473 | " (SELECT fnid FROM mlink" |
| @@ -473,13 +481,16 @@ | |
| 481 | }else{ |
| 482 | @ <b>Deleted</b> by check-in |
| 483 | } |
| 484 | } |
| 485 | hyperlink_to_uuid(zCkin); |
| 486 | if( fShowId ){ |
| 487 | @ (%d(fmid)) |
| 488 | } |
| 489 | @ %W(zCom) (user: |
| 490 | hyperlink_to_user(zUser, zDate, ""); |
| 491 | @ branch: %z(href("%R/timeline?t=%T&n=200",zBr))%h(zBr)</a> |
| 492 | if( g.perm.Hyperlink && zUuid ){ |
| 493 | const char *z = zFilename; |
| 494 | @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin)) |
| 495 | @ [annotate]</a> |
| 496 | @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin)) |
| @@ -488,23 +499,26 @@ | |
| 499 | if( fpid ){ |
| 500 | @ %z(href("%R/fdiff?sbs=1&v1=%s&v2=%s",zPUuid,zUuid))[diff]</a> |
| 501 | } |
| 502 | } |
| 503 | if( fDebug & FINFO_DEBUG_MLINK ){ |
| 504 | int ii; |
| 505 | @ <br>fid=%d(frid) pid=%d(fpid) mid=%d(fmid) |
| 506 | if( nParent>0 ){ |
| 507 | @ parents=%d(aParent[0]) |
| 508 | for(ii=1; ii<nParent; ii++){ |
| 509 | @ %d(aParent[ii]) |
| 510 | } |
| 511 | } |
| 512 | @ %z(href("%R/finfo?name=%T&ci=%s&debug=1",zFilename,zCkin))[ancestry]</a> |
| 513 | } |
| 514 | tag_private_status(frid); |
| 515 | @ </td></tr> |
| 516 | } |
| 517 | db_finalize(&q); |
| 518 | if( pGraph ){ |
| 519 | graph_finish(pGraph, 1); |
| 520 | if( pGraph->nErr ){ |
| 521 | graph_free(pGraph); |
| 522 | pGraph = 0; |
| 523 | }else{ |
| 524 | int w = (pGraph->mxRail+1)*pGraph->iRailPitch + 10; |
| 525 |
+25
-5
| --- src/foci.c | ||
| +++ src/foci.c | ||
| @@ -17,25 +17,45 @@ | ||
| 17 | 17 | ** |
| 18 | 18 | ** This routine implements an SQLite virtual table that gives all of the |
| 19 | 19 | ** files associated with a single checkin. |
| 20 | 20 | ** |
| 21 | 21 | ** The filename "foci" is short for "Files Of CheckIn". |
| 22 | +** | |
| 23 | +** Usage example: | |
| 24 | +** | |
| 25 | +** CREATE VIRTUAL TABLE temp.foci USING files_of_checkin; | |
| 26 | +** -- ^^^^--- important! | |
| 27 | +** SELECT * FROM foci WHERE checkinID=symbolic_name_to_rid('trunk'); | |
| 28 | +** | |
| 29 | +** The symbolic_name_to_rid('trunk') function finds the BLOB.RID value | |
| 30 | +** corresponding to the 'trunk' tag. Then the files_of_checkin virtual table | |
| 31 | +** decodes the manifest defined by that BLOB and returns all files described | |
| 32 | +** by that manifest. The "schema" for the temp.foci table is: | |
| 33 | +** | |
| 34 | +** CREATE TABLE files_of_checkin( | |
| 35 | +** checkinID INTEGER, -- RID for the checkin manifest | |
| 36 | +** filename TEXT, -- Name of a file | |
| 37 | +** uuid TEXT, -- SHA1 hash of the file | |
| 38 | +** previousName TEXT, -- Name of the file in previous checkin | |
| 39 | +** perm TEXT -- Permissions on the file | |
| 40 | +** ); | |
| 41 | +** | |
| 22 | 42 | */ |
| 23 | 43 | #include "config.h" |
| 24 | 44 | #include "foci.h" |
| 25 | 45 | #include <assert.h> |
| 26 | 46 | |
| 27 | 47 | /* |
| 28 | 48 | ** The schema for the virtual table: |
| 29 | 49 | */ |
| 30 | -static const char zFociSchema[] = | |
| 50 | +static const char zFociSchema[] = | |
| 31 | 51 | @ CREATE TABLE files_of_checkin( |
| 32 | 52 | @ checkinID INTEGER, -- RID for the checkin manifest |
| 33 | 53 | @ filename TEXT, -- Name of a file |
| 34 | 54 | @ uuid TEXT, -- SHA1 hash of the file |
| 35 | 55 | @ previousName TEXT, -- Name of the file in previous checkin |
| 36 | -@ prem TEXT -- Permissions on the file | |
| 56 | +@ perm TEXT -- Permissions on the file | |
| 37 | 57 | @ ); |
| 38 | 58 | ; |
| 39 | 59 | |
| 40 | 60 | #if INTERFACE |
| 41 | 61 | /* |
| @@ -139,11 +159,11 @@ | ||
| 139 | 159 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 140 | 160 | return pCsr->pFile==0; |
| 141 | 161 | } |
| 142 | 162 | |
| 143 | 163 | static int fociFilter( |
| 144 | - sqlite3_vtab_cursor *pCursor, | |
| 164 | + sqlite3_vtab_cursor *pCursor, | |
| 145 | 165 | int idxNum, const char *idxStr, |
| 146 | 166 | int argc, sqlite3_value **argv |
| 147 | 167 | ){ |
| 148 | 168 | FociCursor *pCur = (FociCursor *)pCursor; |
| 149 | 169 | manifest_destroy(pCur->pMan); |
| @@ -158,12 +178,12 @@ | ||
| 158 | 178 | } |
| 159 | 179 | return SQLITE_OK; |
| 160 | 180 | } |
| 161 | 181 | |
| 162 | 182 | static int fociColumn( |
| 163 | - sqlite3_vtab_cursor *pCursor, | |
| 164 | - sqlite3_context *ctx, | |
| 183 | + sqlite3_vtab_cursor *pCursor, | |
| 184 | + sqlite3_context *ctx, | |
| 165 | 185 | int i |
| 166 | 186 | ){ |
| 167 | 187 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 168 | 188 | switch( i ){ |
| 169 | 189 | case 0: /* checkinID */ |
| 170 | 190 |
| --- src/foci.c | |
| +++ src/foci.c | |
| @@ -17,25 +17,45 @@ | |
| 17 | ** |
| 18 | ** This routine implements an SQLite virtual table that gives all of the |
| 19 | ** files associated with a single checkin. |
| 20 | ** |
| 21 | ** The filename "foci" is short for "Files Of CheckIn". |
| 22 | */ |
| 23 | #include "config.h" |
| 24 | #include "foci.h" |
| 25 | #include <assert.h> |
| 26 | |
| 27 | /* |
| 28 | ** The schema for the virtual table: |
| 29 | */ |
| 30 | static const char zFociSchema[] = |
| 31 | @ CREATE TABLE files_of_checkin( |
| 32 | @ checkinID INTEGER, -- RID for the checkin manifest |
| 33 | @ filename TEXT, -- Name of a file |
| 34 | @ uuid TEXT, -- SHA1 hash of the file |
| 35 | @ previousName TEXT, -- Name of the file in previous checkin |
| 36 | @ prem TEXT -- Permissions on the file |
| 37 | @ ); |
| 38 | ; |
| 39 | |
| 40 | #if INTERFACE |
| 41 | /* |
| @@ -139,11 +159,11 @@ | |
| 139 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 140 | return pCsr->pFile==0; |
| 141 | } |
| 142 | |
| 143 | static int fociFilter( |
| 144 | sqlite3_vtab_cursor *pCursor, |
| 145 | int idxNum, const char *idxStr, |
| 146 | int argc, sqlite3_value **argv |
| 147 | ){ |
| 148 | FociCursor *pCur = (FociCursor *)pCursor; |
| 149 | manifest_destroy(pCur->pMan); |
| @@ -158,12 +178,12 @@ | |
| 158 | } |
| 159 | return SQLITE_OK; |
| 160 | } |
| 161 | |
| 162 | static int fociColumn( |
| 163 | sqlite3_vtab_cursor *pCursor, |
| 164 | sqlite3_context *ctx, |
| 165 | int i |
| 166 | ){ |
| 167 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 168 | switch( i ){ |
| 169 | case 0: /* checkinID */ |
| 170 |
| --- src/foci.c | |
| +++ src/foci.c | |
| @@ -17,25 +17,45 @@ | |
| 17 | ** |
| 18 | ** This routine implements an SQLite virtual table that gives all of the |
| 19 | ** files associated with a single checkin. |
| 20 | ** |
| 21 | ** The filename "foci" is short for "Files Of CheckIn". |
| 22 | ** |
| 23 | ** Usage example: |
| 24 | ** |
| 25 | ** CREATE VIRTUAL TABLE temp.foci USING files_of_checkin; |
| 26 | ** -- ^^^^--- important! |
| 27 | ** SELECT * FROM foci WHERE checkinID=symbolic_name_to_rid('trunk'); |
| 28 | ** |
| 29 | ** The symbolic_name_to_rid('trunk') function finds the BLOB.RID value |
| 30 | ** corresponding to the 'trunk' tag. Then the files_of_checkin virtual table |
| 31 | ** decodes the manifest defined by that BLOB and returns all files described |
| 32 | ** by that manifest. The "schema" for the temp.foci table is: |
| 33 | ** |
| 34 | ** CREATE TABLE files_of_checkin( |
| 35 | ** checkinID INTEGER, -- RID for the checkin manifest |
| 36 | ** filename TEXT, -- Name of a file |
| 37 | ** uuid TEXT, -- SHA1 hash of the file |
| 38 | ** previousName TEXT, -- Name of the file in previous checkin |
| 39 | ** perm TEXT -- Permissions on the file |
| 40 | ** ); |
| 41 | ** |
| 42 | */ |
| 43 | #include "config.h" |
| 44 | #include "foci.h" |
| 45 | #include <assert.h> |
| 46 | |
| 47 | /* |
| 48 | ** The schema for the virtual table: |
| 49 | */ |
| 50 | static const char zFociSchema[] = |
| 51 | @ CREATE TABLE files_of_checkin( |
| 52 | @ checkinID INTEGER, -- RID for the checkin manifest |
| 53 | @ filename TEXT, -- Name of a file |
| 54 | @ uuid TEXT, -- SHA1 hash of the file |
| 55 | @ previousName TEXT, -- Name of the file in previous checkin |
| 56 | @ perm TEXT -- Permissions on the file |
| 57 | @ ); |
| 58 | ; |
| 59 | |
| 60 | #if INTERFACE |
| 61 | /* |
| @@ -139,11 +159,11 @@ | |
| 159 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 160 | return pCsr->pFile==0; |
| 161 | } |
| 162 | |
| 163 | static int fociFilter( |
| 164 | sqlite3_vtab_cursor *pCursor, |
| 165 | int idxNum, const char *idxStr, |
| 166 | int argc, sqlite3_value **argv |
| 167 | ){ |
| 168 | FociCursor *pCur = (FociCursor *)pCursor; |
| 169 | manifest_destroy(pCur->pMan); |
| @@ -158,12 +178,12 @@ | |
| 178 | } |
| 179 | return SQLITE_OK; |
| 180 | } |
| 181 | |
| 182 | static int fociColumn( |
| 183 | sqlite3_vtab_cursor *pCursor, |
| 184 | sqlite3_context *ctx, |
| 185 | int i |
| 186 | ){ |
| 187 | FociCursor *pCsr = (FociCursor *)pCursor; |
| 188 | switch( i ){ |
| 189 | case 0: /* checkinID */ |
| 190 |
+4
-4
| --- src/fusefs.c | ||
| +++ src/fusefs.c | ||
| @@ -14,11 +14,11 @@ | ||
| 14 | 14 | ** http://www.hwaci.com/drh/ |
| 15 | 15 | ** |
| 16 | 16 | ******************************************************************************* |
| 17 | 17 | ** |
| 18 | 18 | ** This module implements the userspace side of a Fuse Filesystem that |
| 19 | -** contains all check-ins for a fossil repository. | |
| 19 | +** contains all check-ins for a fossil repository. | |
| 20 | 20 | ** |
| 21 | 21 | ** This module is a mostly a no-op unless compiled with -DFOSSIL_HAVE_FUSEFS. |
| 22 | 22 | ** The FOSSIL_HAVE_FUSEFS should be omitted on systems that lack support for |
| 23 | 23 | ** the Fuse Filesystem, of course. |
| 24 | 24 | */ |
| @@ -193,11 +193,11 @@ | ||
| 193 | 193 | int cnt = 0; |
| 194 | 194 | n = fusefs_parse_path(zPath); |
| 195 | 195 | if( n==0 ){ |
| 196 | 196 | filler(buf, ".", NULL, 0); |
| 197 | 197 | filler(buf, "..", NULL, 0); |
| 198 | - filler(buf, "checkins", NULL, 0); | |
| 198 | + filler(buf, "checkins", NULL, 0); | |
| 199 | 199 | return 0; |
| 200 | 200 | } |
| 201 | 201 | if( strcmp(fusefs.az[0],"checkins")!=0 ) return -ENOENT; |
| 202 | 202 | if( n==1 ) return -ENOENT; |
| 203 | 203 | rid = fusefs_name_to_rid(fusefs.az[1]); |
| @@ -275,11 +275,11 @@ | ||
| 275 | 275 | if( offset+size>blob_size(&fusefs.content) ){ |
| 276 | 276 | size = blob_size(&fusefs.content) - offset; |
| 277 | 277 | } |
| 278 | 278 | memcpy(buf, blob_buffer(&fusefs.content)+offset, size); |
| 279 | 279 | return size; |
| 280 | -} | |
| 280 | +} | |
| 281 | 281 | |
| 282 | 282 | static struct fuse_operations fusefs_methods = { |
| 283 | 283 | .getattr = fusefs_getattr, |
| 284 | 284 | .readdir = fusefs_readdir, |
| 285 | 285 | .read = fusefs_read, |
| @@ -293,11 +293,11 @@ | ||
| 293 | 293 | ** |
| 294 | 294 | ** This command uses the Fuse Filesystem to mount a directory at |
| 295 | 295 | ** DIRECTORY that contains the content of all check-ins in the |
| 296 | 296 | ** repository. The names of files are DIRECTORY/checkins/VERSION/PATH |
| 297 | 297 | ** where DIRECTORY is the root of the mount, VERSION is any valid |
| 298 | -** check-in name (examples: "trunk" or "tip" or a tag or any unique | |
| 298 | +** check-in name (examples: "trunk" or "tip" or a tag or any unique | |
| 299 | 299 | ** prefix of a SHA1 hash, etc) and PATH is the pathname of the file |
| 300 | 300 | ** in the checkin. If DIRECTORY does not exist, then an attempt is |
| 301 | 301 | ** made to create it. |
| 302 | 302 | ** |
| 303 | 303 | ** The DIRECTORY/checkins directory is not searchable so one cannot |
| 304 | 304 |
| --- src/fusefs.c | |
| +++ src/fusefs.c | |
| @@ -14,11 +14,11 @@ | |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This module implements the userspace side of a Fuse Filesystem that |
| 19 | ** contains all check-ins for a fossil repository. |
| 20 | ** |
| 21 | ** This module is a mostly a no-op unless compiled with -DFOSSIL_HAVE_FUSEFS. |
| 22 | ** The FOSSIL_HAVE_FUSEFS should be omitted on systems that lack support for |
| 23 | ** the Fuse Filesystem, of course. |
| 24 | */ |
| @@ -193,11 +193,11 @@ | |
| 193 | int cnt = 0; |
| 194 | n = fusefs_parse_path(zPath); |
| 195 | if( n==0 ){ |
| 196 | filler(buf, ".", NULL, 0); |
| 197 | filler(buf, "..", NULL, 0); |
| 198 | filler(buf, "checkins", NULL, 0); |
| 199 | return 0; |
| 200 | } |
| 201 | if( strcmp(fusefs.az[0],"checkins")!=0 ) return -ENOENT; |
| 202 | if( n==1 ) return -ENOENT; |
| 203 | rid = fusefs_name_to_rid(fusefs.az[1]); |
| @@ -275,11 +275,11 @@ | |
| 275 | if( offset+size>blob_size(&fusefs.content) ){ |
| 276 | size = blob_size(&fusefs.content) - offset; |
| 277 | } |
| 278 | memcpy(buf, blob_buffer(&fusefs.content)+offset, size); |
| 279 | return size; |
| 280 | } |
| 281 | |
| 282 | static struct fuse_operations fusefs_methods = { |
| 283 | .getattr = fusefs_getattr, |
| 284 | .readdir = fusefs_readdir, |
| 285 | .read = fusefs_read, |
| @@ -293,11 +293,11 @@ | |
| 293 | ** |
| 294 | ** This command uses the Fuse Filesystem to mount a directory at |
| 295 | ** DIRECTORY that contains the content of all check-ins in the |
| 296 | ** repository. The names of files are DIRECTORY/checkins/VERSION/PATH |
| 297 | ** where DIRECTORY is the root of the mount, VERSION is any valid |
| 298 | ** check-in name (examples: "trunk" or "tip" or a tag or any unique |
| 299 | ** prefix of a SHA1 hash, etc) and PATH is the pathname of the file |
| 300 | ** in the checkin. If DIRECTORY does not exist, then an attempt is |
| 301 | ** made to create it. |
| 302 | ** |
| 303 | ** The DIRECTORY/checkins directory is not searchable so one cannot |
| 304 |
| --- src/fusefs.c | |
| +++ src/fusefs.c | |
| @@ -14,11 +14,11 @@ | |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This module implements the userspace side of a Fuse Filesystem that |
| 19 | ** contains all check-ins for a fossil repository. |
| 20 | ** |
| 21 | ** This module is a mostly a no-op unless compiled with -DFOSSIL_HAVE_FUSEFS. |
| 22 | ** The FOSSIL_HAVE_FUSEFS should be omitted on systems that lack support for |
| 23 | ** the Fuse Filesystem, of course. |
| 24 | */ |
| @@ -193,11 +193,11 @@ | |
| 193 | int cnt = 0; |
| 194 | n = fusefs_parse_path(zPath); |
| 195 | if( n==0 ){ |
| 196 | filler(buf, ".", NULL, 0); |
| 197 | filler(buf, "..", NULL, 0); |
| 198 | filler(buf, "checkins", NULL, 0); |
| 199 | return 0; |
| 200 | } |
| 201 | if( strcmp(fusefs.az[0],"checkins")!=0 ) return -ENOENT; |
| 202 | if( n==1 ) return -ENOENT; |
| 203 | rid = fusefs_name_to_rid(fusefs.az[1]); |
| @@ -275,11 +275,11 @@ | |
| 275 | if( offset+size>blob_size(&fusefs.content) ){ |
| 276 | size = blob_size(&fusefs.content) - offset; |
| 277 | } |
| 278 | memcpy(buf, blob_buffer(&fusefs.content)+offset, size); |
| 279 | return size; |
| 280 | } |
| 281 | |
| 282 | static struct fuse_operations fusefs_methods = { |
| 283 | .getattr = fusefs_getattr, |
| 284 | .readdir = fusefs_readdir, |
| 285 | .read = fusefs_read, |
| @@ -293,11 +293,11 @@ | |
| 293 | ** |
| 294 | ** This command uses the Fuse Filesystem to mount a directory at |
| 295 | ** DIRECTORY that contains the content of all check-ins in the |
| 296 | ** repository. The names of files are DIRECTORY/checkins/VERSION/PATH |
| 297 | ** where DIRECTORY is the root of the mount, VERSION is any valid |
| 298 | ** check-in name (examples: "trunk" or "tip" or a tag or any unique |
| 299 | ** prefix of a SHA1 hash, etc) and PATH is the pathname of the file |
| 300 | ** in the checkin. If DIRECTORY does not exist, then an attempt is |
| 301 | ** made to create it. |
| 302 | ** |
| 303 | ** The DIRECTORY/checkins directory is not searchable so one cannot |
| 304 |
-19
| --- src/glob.c | ||
| +++ src/glob.c | ||
| @@ -138,29 +138,10 @@ | ||
| 138 | 138 | z += i+1; |
| 139 | 139 | } |
| 140 | 140 | return p; |
| 141 | 141 | } |
| 142 | 142 | |
| 143 | -/* | |
| 144 | -** Return non-zero if string z matches glob pattern zGlob and zero if the | |
| 145 | -** pattern does not match. | |
| 146 | -** | |
| 147 | -** Globbing rules: | |
| 148 | -** | |
| 149 | -** '*' Matches any sequence of zero or more characters. | |
| 150 | -** | |
| 151 | -** '?' Matches exactly one character. | |
| 152 | -** | |
| 153 | -** [...] Matches one character from the enclosed list of | |
| 154 | -** characters. | |
| 155 | -** | |
| 156 | -** [^...] Matches one character not in the enclosed list. | |
| 157 | -*/ | |
| 158 | -int strglob(const char *zGlob, const char *z){ | |
| 159 | - return sqlite3_strglob(zGlob, z)==0; | |
| 160 | -} | |
| 161 | - | |
| 162 | 143 | /* |
| 163 | 144 | ** Return true (non-zero) if zString matches any of the patterns in |
| 164 | 145 | ** the Glob. The value returned is actually a 1-based index of the pattern |
| 165 | 146 | ** that matched. Return 0 if none of the patterns match zString. |
| 166 | 147 | ** |
| 167 | 148 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -138,29 +138,10 @@ | |
| 138 | z += i+1; |
| 139 | } |
| 140 | return p; |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | ** Return non-zero if string z matches glob pattern zGlob and zero if the |
| 145 | ** pattern does not match. |
| 146 | ** |
| 147 | ** Globbing rules: |
| 148 | ** |
| 149 | ** '*' Matches any sequence of zero or more characters. |
| 150 | ** |
| 151 | ** '?' Matches exactly one character. |
| 152 | ** |
| 153 | ** [...] Matches one character from the enclosed list of |
| 154 | ** characters. |
| 155 | ** |
| 156 | ** [^...] Matches one character not in the enclosed list. |
| 157 | */ |
| 158 | int strglob(const char *zGlob, const char *z){ |
| 159 | return sqlite3_strglob(zGlob, z)==0; |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | ** Return true (non-zero) if zString matches any of the patterns in |
| 164 | ** the Glob. The value returned is actually a 1-based index of the pattern |
| 165 | ** that matched. Return 0 if none of the patterns match zString. |
| 166 | ** |
| 167 |
| --- src/glob.c | |
| +++ src/glob.c | |
| @@ -138,29 +138,10 @@ | |
| 138 | z += i+1; |
| 139 | } |
| 140 | return p; |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | ** Return true (non-zero) if zString matches any of the patterns in |
| 145 | ** the Glob. The value returned is actually a 1-based index of the pattern |
| 146 | ** that matched. Return 0 if none of the patterns match zString. |
| 147 | ** |
| 148 |
+26
-5
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -38,11 +38,11 @@ | ||
| 38 | 38 | char *zBgClr; /* Background Color */ |
| 39 | 39 | char zUuid[41]; /* Check-in for file ID */ |
| 40 | 40 | |
| 41 | 41 | GraphRow *pNext; /* Next row down in the list of all rows */ |
| 42 | 42 | GraphRow *pPrev; /* Previous row */ |
| 43 | - | |
| 43 | + | |
| 44 | 44 | int idx; /* Row index. First is 1. 0 used for "none" */ |
| 45 | 45 | int idxTop; /* Direct descendent highest up on the graph */ |
| 46 | 46 | GraphRow *pChild; /* Child immediately above this node */ |
| 47 | 47 | u8 isDup; /* True if this is duplicate of a prior entry */ |
| 48 | 48 | u8 isLeaf; /* True if this is a leaf node */ |
| @@ -154,11 +154,11 @@ | ||
| 154 | 154 | ** Return the canonical pointer for a given branch name. |
| 155 | 155 | ** Multiple calls to this routine with equivalent strings |
| 156 | 156 | ** will return the same pointer. |
| 157 | 157 | ** |
| 158 | 158 | ** The returned value is a pointer to a (readonly) string that |
| 159 | -** has the useful property that strings can be checked for | |
| 159 | +** has the useful property that strings can be checked for | |
| 160 | 160 | ** equality by comparing pointers. |
| 161 | 161 | ** |
| 162 | 162 | ** Note: also used for background color names. |
| 163 | 163 | */ |
| 164 | 164 | static char *persistBranchName(GraphContext *p, const char *zBranch){ |
| @@ -214,11 +214,11 @@ | ||
| 214 | 214 | return pRow->idx; |
| 215 | 215 | } |
| 216 | 216 | |
| 217 | 217 | /* |
| 218 | 218 | ** Return the index of a rail currently not in use for any row between |
| 219 | -** top and bottom, inclusive. | |
| 219 | +** top and bottom, inclusive. | |
| 220 | 220 | */ |
| 221 | 221 | static int findFreeRail( |
| 222 | 222 | GraphContext *p, /* The graph context */ |
| 223 | 223 | int top, int btm, /* Span of rows for which the rail is needed */ |
| 224 | 224 | u64 inUseMask, /* Mask or rails already in use */ |
| @@ -379,12 +379,33 @@ | ||
| 379 | 379 | } |
| 380 | 380 | } |
| 381 | 381 | } |
| 382 | 382 | } |
| 383 | 383 | |
| 384 | + /* If the primary parent is in a different branch, but there are | |
| 385 | + ** other parents in the same branch, reorder the parents to make | |
| 386 | + ** the parent from the same branch the primary parent. | |
| 387 | + */ | |
| 388 | + for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ | |
| 389 | + if( pRow->isDup ) continue; | |
| 390 | + if( pRow->nParent<2 ) continue; /* Not a fork */ | |
| 391 | + pParent = hashFind(p, pRow->aParent[0]); | |
| 392 | + if( pParent==0 ) continue; /* Parent off-screen */ | |
| 393 | + if( pParent->zBranch==pRow->zBranch ) continue; /* Same branch */ | |
| 394 | + for(i=1; i<pRow->nParent; i++){ | |
| 395 | + pParent = hashFind(p, pRow->aParent[i]); | |
| 396 | + if( pParent && pParent->zBranch==pRow->zBranch ){ | |
| 397 | + int t = pRow->aParent[0]; | |
| 398 | + pRow->aParent[0] = pRow->aParent[i]; | |
| 399 | + pRow->aParent[i] = t; | |
| 400 | + break; | |
| 401 | + } | |
| 402 | + } | |
| 403 | + } | |
| 404 | + | |
| 384 | 405 | |
| 385 | - /* Find the pChild pointer for each node. | |
| 406 | + /* Find the pChild pointer for each node. | |
| 386 | 407 | ** |
| 387 | 408 | ** The pChild points to the node directly above on the same rail. |
| 388 | 409 | ** The pChild must be in the same branch. Leaf nodes have a NULL |
| 389 | 410 | ** pChild. |
| 390 | 411 | ** |
| @@ -533,11 +554,11 @@ | ||
| 533 | 554 | } |
| 534 | 555 | } |
| 535 | 556 | } |
| 536 | 557 | |
| 537 | 558 | /* |
| 538 | - ** Insert merge rails from primaries to duplicates. | |
| 559 | + ** Insert merge rails from primaries to duplicates. | |
| 539 | 560 | */ |
| 540 | 561 | if( hasDup ){ |
| 541 | 562 | int dupRail; |
| 542 | 563 | int mxRail; |
| 543 | 564 | find_max_rail(p); |
| 544 | 565 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -38,11 +38,11 @@ | |
| 38 | char *zBgClr; /* Background Color */ |
| 39 | char zUuid[41]; /* Check-in for file ID */ |
| 40 | |
| 41 | GraphRow *pNext; /* Next row down in the list of all rows */ |
| 42 | GraphRow *pPrev; /* Previous row */ |
| 43 | |
| 44 | int idx; /* Row index. First is 1. 0 used for "none" */ |
| 45 | int idxTop; /* Direct descendent highest up on the graph */ |
| 46 | GraphRow *pChild; /* Child immediately above this node */ |
| 47 | u8 isDup; /* True if this is duplicate of a prior entry */ |
| 48 | u8 isLeaf; /* True if this is a leaf node */ |
| @@ -154,11 +154,11 @@ | |
| 154 | ** Return the canonical pointer for a given branch name. |
| 155 | ** Multiple calls to this routine with equivalent strings |
| 156 | ** will return the same pointer. |
| 157 | ** |
| 158 | ** The returned value is a pointer to a (readonly) string that |
| 159 | ** has the useful property that strings can be checked for |
| 160 | ** equality by comparing pointers. |
| 161 | ** |
| 162 | ** Note: also used for background color names. |
| 163 | */ |
| 164 | static char *persistBranchName(GraphContext *p, const char *zBranch){ |
| @@ -214,11 +214,11 @@ | |
| 214 | return pRow->idx; |
| 215 | } |
| 216 | |
| 217 | /* |
| 218 | ** Return the index of a rail currently not in use for any row between |
| 219 | ** top and bottom, inclusive. |
| 220 | */ |
| 221 | static int findFreeRail( |
| 222 | GraphContext *p, /* The graph context */ |
| 223 | int top, int btm, /* Span of rows for which the rail is needed */ |
| 224 | u64 inUseMask, /* Mask or rails already in use */ |
| @@ -379,12 +379,33 @@ | |
| 379 | } |
| 380 | } |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | |
| 385 | /* Find the pChild pointer for each node. |
| 386 | ** |
| 387 | ** The pChild points to the node directly above on the same rail. |
| 388 | ** The pChild must be in the same branch. Leaf nodes have a NULL |
| 389 | ** pChild. |
| 390 | ** |
| @@ -533,11 +554,11 @@ | |
| 533 | } |
| 534 | } |
| 535 | } |
| 536 | |
| 537 | /* |
| 538 | ** Insert merge rails from primaries to duplicates. |
| 539 | */ |
| 540 | if( hasDup ){ |
| 541 | int dupRail; |
| 542 | int mxRail; |
| 543 | find_max_rail(p); |
| 544 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -38,11 +38,11 @@ | |
| 38 | char *zBgClr; /* Background Color */ |
| 39 | char zUuid[41]; /* Check-in for file ID */ |
| 40 | |
| 41 | GraphRow *pNext; /* Next row down in the list of all rows */ |
| 42 | GraphRow *pPrev; /* Previous row */ |
| 43 | |
| 44 | int idx; /* Row index. First is 1. 0 used for "none" */ |
| 45 | int idxTop; /* Direct descendent highest up on the graph */ |
| 46 | GraphRow *pChild; /* Child immediately above this node */ |
| 47 | u8 isDup; /* True if this is duplicate of a prior entry */ |
| 48 | u8 isLeaf; /* True if this is a leaf node */ |
| @@ -154,11 +154,11 @@ | |
| 154 | ** Return the canonical pointer for a given branch name. |
| 155 | ** Multiple calls to this routine with equivalent strings |
| 156 | ** will return the same pointer. |
| 157 | ** |
| 158 | ** The returned value is a pointer to a (readonly) string that |
| 159 | ** has the useful property that strings can be checked for |
| 160 | ** equality by comparing pointers. |
| 161 | ** |
| 162 | ** Note: also used for background color names. |
| 163 | */ |
| 164 | static char *persistBranchName(GraphContext *p, const char *zBranch){ |
| @@ -214,11 +214,11 @@ | |
| 214 | return pRow->idx; |
| 215 | } |
| 216 | |
| 217 | /* |
| 218 | ** Return the index of a rail currently not in use for any row between |
| 219 | ** top and bottom, inclusive. |
| 220 | */ |
| 221 | static int findFreeRail( |
| 222 | GraphContext *p, /* The graph context */ |
| 223 | int top, int btm, /* Span of rows for which the rail is needed */ |
| 224 | u64 inUseMask, /* Mask or rails already in use */ |
| @@ -379,12 +379,33 @@ | |
| 379 | } |
| 380 | } |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | /* If the primary parent is in a different branch, but there are |
| 385 | ** other parents in the same branch, reorder the parents to make |
| 386 | ** the parent from the same branch the primary parent. |
| 387 | */ |
| 388 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 389 | if( pRow->isDup ) continue; |
| 390 | if( pRow->nParent<2 ) continue; /* Not a fork */ |
| 391 | pParent = hashFind(p, pRow->aParent[0]); |
| 392 | if( pParent==0 ) continue; /* Parent off-screen */ |
| 393 | if( pParent->zBranch==pRow->zBranch ) continue; /* Same branch */ |
| 394 | for(i=1; i<pRow->nParent; i++){ |
| 395 | pParent = hashFind(p, pRow->aParent[i]); |
| 396 | if( pParent && pParent->zBranch==pRow->zBranch ){ |
| 397 | int t = pRow->aParent[0]; |
| 398 | pRow->aParent[0] = pRow->aParent[i]; |
| 399 | pRow->aParent[i] = t; |
| 400 | break; |
| 401 | } |
| 402 | } |
| 403 | } |
| 404 | |
| 405 | |
| 406 | /* Find the pChild pointer for each node. |
| 407 | ** |
| 408 | ** The pChild points to the node directly above on the same rail. |
| 409 | ** The pChild must be in the same branch. Leaf nodes have a NULL |
| 410 | ** pChild. |
| 411 | ** |
| @@ -533,11 +554,11 @@ | |
| 554 | } |
| 555 | } |
| 556 | } |
| 557 | |
| 558 | /* |
| 559 | ** Insert merge rails from primaries to duplicates. |
| 560 | */ |
| 561 | if( hasDup ){ |
| 562 | int dupRail; |
| 563 | int mxRail; |
| 564 | find_max_rail(p); |
| 565 |
+13
-8
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -155,11 +155,11 @@ | ||
| 155 | 155 | blob_reset(&x); |
| 156 | 156 | return ( c!='n' && c!='N' ); |
| 157 | 157 | } |
| 158 | 158 | |
| 159 | 159 | /* |
| 160 | -** Get the HTTP Basic Authorization credentials from the user | |
| 160 | +** Get the HTTP Basic Authorization credentials from the user | |
| 161 | 161 | ** when 401 is received. |
| 162 | 162 | */ |
| 163 | 163 | char *prompt_for_httpauth_creds(void){ |
| 164 | 164 | Blob x; |
| 165 | 165 | char *zUser; |
| @@ -205,11 +205,12 @@ | ||
| 205 | 205 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){ |
| 206 | 206 | Blob login; /* The login card */ |
| 207 | 207 | Blob payload; /* The complete payload including login card */ |
| 208 | 208 | Blob hdr; /* The HTTP request header */ |
| 209 | 209 | int closeConnection; /* True to close the connection when done */ |
| 210 | - int iLength; /* Length of the reply payload */ | |
| 210 | + int iLength; /* Expected length of the reply payload */ | |
| 211 | + int iRecvLen; /* Received length of the reply payload */ | |
| 211 | 212 | int rc = 0; /* Result code */ |
| 212 | 213 | int iHttpVersion; /* Which version of HTTP protocol server uses */ |
| 213 | 214 | char *zLine; /* A single line of the reply header */ |
| 214 | 215 | int i; /* Loop counter */ |
| 215 | 216 | int isError = 0; /* True if the reply is an error message */ |
| @@ -265,11 +266,11 @@ | ||
| 265 | 266 | transport_send(&g.url, &hdr); |
| 266 | 267 | transport_send(&g.url, &payload); |
| 267 | 268 | blob_reset(&hdr); |
| 268 | 269 | blob_reset(&payload); |
| 269 | 270 | transport_flip(&g.url); |
| 270 | - | |
| 271 | + | |
| 271 | 272 | /* |
| 272 | 273 | ** Read and interpret the server reply |
| 273 | 274 | */ |
| 274 | 275 | closeConnection = 1; |
| 275 | 276 | iLength = -1; |
| @@ -332,11 +333,11 @@ | ||
| 332 | 333 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 333 | 334 | if( zLine[i]==0 ){ |
| 334 | 335 | fossil_warning("malformed redirect: %s", zLine); |
| 335 | 336 | goto write_err; |
| 336 | 337 | } |
| 337 | - j = strlen(zLine) - 1; | |
| 338 | + j = strlen(zLine) - 1; | |
| 338 | 339 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 339 | 340 | j -= 4; |
| 340 | 341 | zLine[j] = 0; |
| 341 | 342 | } |
| 342 | 343 | transport_close(&g.url); |
| @@ -348,11 +349,11 @@ | ||
| 348 | 349 | g.zHttpAuth = get_httpauth(); |
| 349 | 350 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 350 | 351 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 351 | 352 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 352 | 353 | isCompressed = 0; |
| 353 | - }else if( fossil_strnicmp(&zLine[14], | |
| 354 | + }else if( fossil_strnicmp(&zLine[14], | |
| 354 | 355 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 355 | 356 | isCompressed = 0; |
| 356 | 357 | }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){ |
| 357 | 358 | isError = 1; |
| 358 | 359 | } |
| @@ -370,11 +371,15 @@ | ||
| 370 | 371 | /* |
| 371 | 372 | ** Extract the reply payload that follows the header |
| 372 | 373 | */ |
| 373 | 374 | blob_zero(pReply); |
| 374 | 375 | blob_resize(pReply, iLength); |
| 375 | - iLength = transport_receive(&g.url, blob_buffer(pReply), iLength); | |
| 376 | + iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength); | |
| 377 | + if( iRecvLen != iLength ){ | |
| 378 | + fossil_warning("response truncated: got %d bytes of %d", iRecvLen, iLength); | |
| 379 | + goto write_err; | |
| 380 | + } | |
| 376 | 381 | blob_resize(pReply, iLength); |
| 377 | 382 | if( isError ){ |
| 378 | 383 | char *z; |
| 379 | 384 | int i, j; |
| 380 | 385 | z = blob_str(pReply); |
| @@ -406,12 +411,12 @@ | ||
| 406 | 411 | }else{ |
| 407 | 412 | transport_rewind(&g.url); |
| 408 | 413 | } |
| 409 | 414 | return 0; |
| 410 | 415 | |
| 411 | - /* | |
| 416 | + /* | |
| 412 | 417 | ** Jump to here if an error is seen. |
| 413 | 418 | */ |
| 414 | 419 | write_err: |
| 415 | 420 | transport_close(&g.url); |
| 416 | - return 1; | |
| 421 | + return 1; | |
| 417 | 422 | } |
| 418 | 423 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -155,11 +155,11 @@ | |
| 155 | blob_reset(&x); |
| 156 | return ( c!='n' && c!='N' ); |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | ** Get the HTTP Basic Authorization credentials from the user |
| 161 | ** when 401 is received. |
| 162 | */ |
| 163 | char *prompt_for_httpauth_creds(void){ |
| 164 | Blob x; |
| 165 | char *zUser; |
| @@ -205,11 +205,12 @@ | |
| 205 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){ |
| 206 | Blob login; /* The login card */ |
| 207 | Blob payload; /* The complete payload including login card */ |
| 208 | Blob hdr; /* The HTTP request header */ |
| 209 | int closeConnection; /* True to close the connection when done */ |
| 210 | int iLength; /* Length of the reply payload */ |
| 211 | int rc = 0; /* Result code */ |
| 212 | int iHttpVersion; /* Which version of HTTP protocol server uses */ |
| 213 | char *zLine; /* A single line of the reply header */ |
| 214 | int i; /* Loop counter */ |
| 215 | int isError = 0; /* True if the reply is an error message */ |
| @@ -265,11 +266,11 @@ | |
| 265 | transport_send(&g.url, &hdr); |
| 266 | transport_send(&g.url, &payload); |
| 267 | blob_reset(&hdr); |
| 268 | blob_reset(&payload); |
| 269 | transport_flip(&g.url); |
| 270 | |
| 271 | /* |
| 272 | ** Read and interpret the server reply |
| 273 | */ |
| 274 | closeConnection = 1; |
| 275 | iLength = -1; |
| @@ -332,11 +333,11 @@ | |
| 332 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 333 | if( zLine[i]==0 ){ |
| 334 | fossil_warning("malformed redirect: %s", zLine); |
| 335 | goto write_err; |
| 336 | } |
| 337 | j = strlen(zLine) - 1; |
| 338 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 339 | j -= 4; |
| 340 | zLine[j] = 0; |
| 341 | } |
| 342 | transport_close(&g.url); |
| @@ -348,11 +349,11 @@ | |
| 348 | g.zHttpAuth = get_httpauth(); |
| 349 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 350 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 351 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 352 | isCompressed = 0; |
| 353 | }else if( fossil_strnicmp(&zLine[14], |
| 354 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 355 | isCompressed = 0; |
| 356 | }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){ |
| 357 | isError = 1; |
| 358 | } |
| @@ -370,11 +371,15 @@ | |
| 370 | /* |
| 371 | ** Extract the reply payload that follows the header |
| 372 | */ |
| 373 | blob_zero(pReply); |
| 374 | blob_resize(pReply, iLength); |
| 375 | iLength = transport_receive(&g.url, blob_buffer(pReply), iLength); |
| 376 | blob_resize(pReply, iLength); |
| 377 | if( isError ){ |
| 378 | char *z; |
| 379 | int i, j; |
| 380 | z = blob_str(pReply); |
| @@ -406,12 +411,12 @@ | |
| 406 | }else{ |
| 407 | transport_rewind(&g.url); |
| 408 | } |
| 409 | return 0; |
| 410 | |
| 411 | /* |
| 412 | ** Jump to here if an error is seen. |
| 413 | */ |
| 414 | write_err: |
| 415 | transport_close(&g.url); |
| 416 | return 1; |
| 417 | } |
| 418 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -155,11 +155,11 @@ | |
| 155 | blob_reset(&x); |
| 156 | return ( c!='n' && c!='N' ); |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | ** Get the HTTP Basic Authorization credentials from the user |
| 161 | ** when 401 is received. |
| 162 | */ |
| 163 | char *prompt_for_httpauth_creds(void){ |
| 164 | Blob x; |
| 165 | char *zUser; |
| @@ -205,11 +205,12 @@ | |
| 205 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin, int maxRedirect){ |
| 206 | Blob login; /* The login card */ |
| 207 | Blob payload; /* The complete payload including login card */ |
| 208 | Blob hdr; /* The HTTP request header */ |
| 209 | int closeConnection; /* True to close the connection when done */ |
| 210 | int iLength; /* Expected length of the reply payload */ |
| 211 | int iRecvLen; /* Received length of the reply payload */ |
| 212 | int rc = 0; /* Result code */ |
| 213 | int iHttpVersion; /* Which version of HTTP protocol server uses */ |
| 214 | char *zLine; /* A single line of the reply header */ |
| 215 | int i; /* Loop counter */ |
| 216 | int isError = 0; /* True if the reply is an error message */ |
| @@ -265,11 +266,11 @@ | |
| 266 | transport_send(&g.url, &hdr); |
| 267 | transport_send(&g.url, &payload); |
| 268 | blob_reset(&hdr); |
| 269 | blob_reset(&payload); |
| 270 | transport_flip(&g.url); |
| 271 | |
| 272 | /* |
| 273 | ** Read and interpret the server reply |
| 274 | */ |
| 275 | closeConnection = 1; |
| 276 | iLength = -1; |
| @@ -332,11 +333,11 @@ | |
| 333 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 334 | if( zLine[i]==0 ){ |
| 335 | fossil_warning("malformed redirect: %s", zLine); |
| 336 | goto write_err; |
| 337 | } |
| 338 | j = strlen(zLine) - 1; |
| 339 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| 340 | j -= 4; |
| 341 | zLine[j] = 0; |
| 342 | } |
| 343 | transport_close(&g.url); |
| @@ -348,11 +349,11 @@ | |
| 349 | g.zHttpAuth = get_httpauth(); |
| 350 | return http_exchange(pSend, pReply, useLogin, maxRedirect); |
| 351 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 352 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 353 | isCompressed = 0; |
| 354 | }else if( fossil_strnicmp(&zLine[14], |
| 355 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 356 | isCompressed = 0; |
| 357 | }else if( fossil_strnicmp(&zLine[14], "application/x-fossil", -1)!=0 ){ |
| 358 | isError = 1; |
| 359 | } |
| @@ -370,11 +371,15 @@ | |
| 371 | /* |
| 372 | ** Extract the reply payload that follows the header |
| 373 | */ |
| 374 | blob_zero(pReply); |
| 375 | blob_resize(pReply, iLength); |
| 376 | iRecvLen = transport_receive(&g.url, blob_buffer(pReply), iLength); |
| 377 | if( iRecvLen != iLength ){ |
| 378 | fossil_warning("response truncated: got %d bytes of %d", iRecvLen, iLength); |
| 379 | goto write_err; |
| 380 | } |
| 381 | blob_resize(pReply, iLength); |
| 382 | if( isError ){ |
| 383 | char *z; |
| 384 | int i, j; |
| 385 | z = blob_str(pReply); |
| @@ -406,12 +411,12 @@ | |
| 411 | }else{ |
| 412 | transport_rewind(&g.url); |
| 413 | } |
| 414 | return 0; |
| 415 | |
| 416 | /* |
| 417 | ** Jump to here if an error is seen. |
| 418 | */ |
| 419 | write_err: |
| 420 | transport_close(&g.url); |
| 421 | return 1; |
| 422 | } |
| 423 |
+56
-46
| --- src/http_socket.c | ||
| +++ src/http_socket.c | ||
| @@ -27,10 +27,13 @@ | ||
| 27 | 27 | */ |
| 28 | 28 | |
| 29 | 29 | #include "config.h" |
| 30 | 30 | #include "http_socket.h" |
| 31 | 31 | #if defined(_WIN32) |
| 32 | +# if !defined(_WIN32_WINNT) | |
| 33 | +# define _WIN32_WINNT 0x0501 | |
| 34 | +# endif | |
| 32 | 35 | # include <winsock2.h> |
| 33 | 36 | # include <ws2tcpip.h> |
| 34 | 37 | #else |
| 35 | 38 | # include <netinet/in.h> |
| 36 | 39 | # include <arpa/inet.h> |
| @@ -45,11 +48,10 @@ | ||
| 45 | 48 | ** There can only be a single socket connection open at a time. |
| 46 | 49 | ** State information about that socket is stored in the following |
| 47 | 50 | ** local variables: |
| 48 | 51 | */ |
| 49 | 52 | static int socketIsInit = 0; /* True after global initialization */ |
| 50 | -static int addrIsInit = 0; /* True once addr is initialized */ | |
| 51 | 53 | #if defined(_WIN32) |
| 52 | 54 | static WSADATA socketInfo; /* Windows socket initialize data */ |
| 53 | 55 | #endif |
| 54 | 56 | static int iSocket = -1; /* The socket on which we talk to the server */ |
| 55 | 57 | static char *socketErrMsg = 0; /* Text of most recent socket error */ |
| @@ -106,11 +108,10 @@ | ||
| 106 | 108 | WSACleanup(); |
| 107 | 109 | #endif |
| 108 | 110 | socket_clear_errmsg(); |
| 109 | 111 | socketIsInit = 0; |
| 110 | 112 | } |
| 111 | - addrIsInit = 0; | |
| 112 | 113 | } |
| 113 | 114 | |
| 114 | 115 | /* |
| 115 | 116 | ** Close the currently open socket. If no socket is open, this routine |
| 116 | 117 | ** is a no-op. |
| @@ -134,54 +135,56 @@ | ||
| 134 | 135 | ** pUrlDAta->port TCP/IP port to use. Ex: 80 |
| 135 | 136 | ** |
| 136 | 137 | ** Return the number of errors. |
| 137 | 138 | */ |
| 138 | 139 | int socket_open(UrlData *pUrlData){ |
| 139 | - static struct sockaddr_in addr; /* The server address */ | |
| 140 | + int rc = 0; | |
| 141 | + struct addrinfo *ai = 0; | |
| 142 | + struct addrinfo *p; | |
| 143 | + struct addrinfo hints; | |
| 144 | + char zPort[30]; | |
| 145 | + char zRemote[NI_MAXHOST]; | |
| 140 | 146 | |
| 141 | 147 | socket_global_init(); |
| 142 | - if( !addrIsInit ){ | |
| 143 | - memset(&addr, 0, sizeof(addr)); | |
| 144 | - addr.sin_family = AF_INET; | |
| 145 | - addr.sin_port = htons(pUrlData->port); | |
| 146 | - *(int*)&addr.sin_addr = inet_addr(pUrlData->name); | |
| 147 | - if( -1 == *(int*)&addr.sin_addr ){ | |
| 148 | -#ifndef FOSSIL_STATIC_LINK | |
| 149 | - struct hostent *pHost; | |
| 150 | - pHost = gethostbyname(pUrlData->name); | |
| 151 | - if( pHost!=0 ){ | |
| 152 | - memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); | |
| 153 | - }else | |
| 154 | -#endif | |
| 155 | - { | |
| 156 | - socket_set_errmsg("can't resolve host name: %s", pUrlData->name); | |
| 157 | - return 1; | |
| 158 | - } | |
| 159 | - } | |
| 160 | - addrIsInit = 1; | |
| 161 | - | |
| 162 | - /* Set the Global.zIpAddr variable to the server we are talking to. | |
| 163 | - ** This is used to populate the ipaddr column of the rcvfrom table, | |
| 164 | - ** if any files are received from the server. | |
| 165 | - */ | |
| 166 | - g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); | |
| 167 | - } | |
| 168 | - iSocket = socket(AF_INET,SOCK_STREAM,0); | |
| 169 | - if( iSocket<0 ){ | |
| 170 | - socket_set_errmsg("cannot create a socket"); | |
| 171 | - return 1; | |
| 172 | - } | |
| 173 | - if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ | |
| 148 | + memset(&hints, 0, sizeof(struct addrinfo)); | |
| 149 | + assert( iSocket<0 ); | |
| 150 | + hints.ai_family = g.fIPv4 ? AF_INET : AF_UNSPEC; | |
| 151 | + hints.ai_socktype = SOCK_STREAM; | |
| 152 | + hints.ai_protocol = IPPROTO_TCP; | |
| 153 | + sqlite3_snprintf(sizeof(zPort),zPort,"%d", pUrlData->port); | |
| 154 | + rc = getaddrinfo(pUrlData->name, zPort, &hints, &ai); | |
| 155 | + if( rc ){ | |
| 156 | + socket_set_errmsg("getaddrinfo() fails: %s", gai_strerror(rc)); | |
| 157 | + goto end_socket_open; | |
| 158 | + } | |
| 159 | + for(p=ai; p; p=p->ai_next){ | |
| 160 | + iSocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol); | |
| 161 | + if( iSocket<0 ) continue; | |
| 162 | + if( connect(iSocket,p->ai_addr,p->ai_addrlen)<0 ){ | |
| 163 | + socket_close(); | |
| 164 | + continue; | |
| 165 | + } | |
| 166 | + rc = getnameinfo(p->ai_addr, p->ai_addrlen, zRemote, sizeof(zRemote), | |
| 167 | + 0, 0, NI_NUMERICHOST); | |
| 168 | + if( rc ){ | |
| 169 | + socket_set_errmsg("getnameinfo() failed: %s", gai_strerror(rc)); | |
| 170 | + goto end_socket_open; | |
| 171 | + } | |
| 172 | + g.zIpAddr = mprintf("%s", zRemote); | |
| 173 | + break; | |
| 174 | + } | |
| 175 | + if( p==0 ){ | |
| 174 | 176 | socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, |
| 175 | 177 | pUrlData->port); |
| 176 | - socket_close(); | |
| 177 | - return 1; | |
| 178 | 178 | } |
| 179 | 179 | #if !defined(_WIN32) |
| 180 | 180 | signal(SIGPIPE, SIG_IGN); |
| 181 | 181 | #endif |
| 182 | - return 0; | |
| 182 | +end_socket_open: | |
| 183 | + if( rc && iSocket>=0 ) socket_close(); | |
| 184 | + if( ai ) freeaddrinfo(ai); | |
| 185 | + return rc; | |
| 183 | 186 | } |
| 184 | 187 | |
| 185 | 188 | /* |
| 186 | 189 | ** Send content out over the open socket connection. |
| 187 | 190 | */ |
| @@ -220,15 +223,22 @@ | ||
| 220 | 223 | ** so rcvfrom gets populated. For hostnames with more than one IP (or |
| 221 | 224 | ** if overridden in ~/.ssh/config) the rcvfrom may not match the host |
| 222 | 225 | ** to which we connect. |
| 223 | 226 | */ |
| 224 | 227 | void socket_ssh_resolve_addr(UrlData *pUrlData){ |
| 225 | - struct hostent *pHost; /* Used to make best effort for rcvfrom */ | |
| 226 | - struct sockaddr_in addr; | |
| 227 | - | |
| 228 | - memset(&addr, 0, sizeof(addr)); | |
| 229 | - pHost = gethostbyname(pUrlData->name); | |
| 230 | - if( pHost!=0 ){ | |
| 231 | - memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); | |
| 232 | - g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); | |
| 228 | + struct addrinfo *ai = 0; | |
| 229 | + struct addrinfo hints; | |
| 230 | + char zRemote[NI_MAXHOST]; | |
| 231 | + hints.ai_family = AF_UNSPEC; | |
| 232 | + hints.ai_socktype = SOCK_STREAM; | |
| 233 | + hints.ai_protocol = IPPROTO_TCP; | |
| 234 | + if( getaddrinfo(pUrlData->name, NULL, &hints, &ai)==0 | |
| 235 | + && ai!=0 | |
| 236 | + && getnameinfo(ai->ai_addr, ai->ai_addrlen, zRemote, | |
| 237 | + sizeof(zRemote), 0, 0, NI_NUMERICHOST)==0 ){ | |
| 238 | + g.zIpAddr = mprintf("%s (%s)", zRemote, pUrlData->name); | |
| 239 | + } | |
| 240 | + if( ai ) freeaddrinfo(ai); | |
| 241 | + if( g.zIpAddr==0 ){ | |
| 242 | + g.zIpAddr = mprintf("%s", pUrlData->name); | |
| 233 | 243 | } |
| 234 | 244 | } |
| 235 | 245 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -27,10 +27,13 @@ | |
| 27 | */ |
| 28 | |
| 29 | #include "config.h" |
| 30 | #include "http_socket.h" |
| 31 | #if defined(_WIN32) |
| 32 | # include <winsock2.h> |
| 33 | # include <ws2tcpip.h> |
| 34 | #else |
| 35 | # include <netinet/in.h> |
| 36 | # include <arpa/inet.h> |
| @@ -45,11 +48,10 @@ | |
| 45 | ** There can only be a single socket connection open at a time. |
| 46 | ** State information about that socket is stored in the following |
| 47 | ** local variables: |
| 48 | */ |
| 49 | static int socketIsInit = 0; /* True after global initialization */ |
| 50 | static int addrIsInit = 0; /* True once addr is initialized */ |
| 51 | #if defined(_WIN32) |
| 52 | static WSADATA socketInfo; /* Windows socket initialize data */ |
| 53 | #endif |
| 54 | static int iSocket = -1; /* The socket on which we talk to the server */ |
| 55 | static char *socketErrMsg = 0; /* Text of most recent socket error */ |
| @@ -106,11 +108,10 @@ | |
| 106 | WSACleanup(); |
| 107 | #endif |
| 108 | socket_clear_errmsg(); |
| 109 | socketIsInit = 0; |
| 110 | } |
| 111 | addrIsInit = 0; |
| 112 | } |
| 113 | |
| 114 | /* |
| 115 | ** Close the currently open socket. If no socket is open, this routine |
| 116 | ** is a no-op. |
| @@ -134,54 +135,56 @@ | |
| 134 | ** pUrlDAta->port TCP/IP port to use. Ex: 80 |
| 135 | ** |
| 136 | ** Return the number of errors. |
| 137 | */ |
| 138 | int socket_open(UrlData *pUrlData){ |
| 139 | static struct sockaddr_in addr; /* The server address */ |
| 140 | |
| 141 | socket_global_init(); |
| 142 | if( !addrIsInit ){ |
| 143 | memset(&addr, 0, sizeof(addr)); |
| 144 | addr.sin_family = AF_INET; |
| 145 | addr.sin_port = htons(pUrlData->port); |
| 146 | *(int*)&addr.sin_addr = inet_addr(pUrlData->name); |
| 147 | if( -1 == *(int*)&addr.sin_addr ){ |
| 148 | #ifndef FOSSIL_STATIC_LINK |
| 149 | struct hostent *pHost; |
| 150 | pHost = gethostbyname(pUrlData->name); |
| 151 | if( pHost!=0 ){ |
| 152 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 153 | }else |
| 154 | #endif |
| 155 | { |
| 156 | socket_set_errmsg("can't resolve host name: %s", pUrlData->name); |
| 157 | return 1; |
| 158 | } |
| 159 | } |
| 160 | addrIsInit = 1; |
| 161 | |
| 162 | /* Set the Global.zIpAddr variable to the server we are talking to. |
| 163 | ** This is used to populate the ipaddr column of the rcvfrom table, |
| 164 | ** if any files are received from the server. |
| 165 | */ |
| 166 | g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); |
| 167 | } |
| 168 | iSocket = socket(AF_INET,SOCK_STREAM,0); |
| 169 | if( iSocket<0 ){ |
| 170 | socket_set_errmsg("cannot create a socket"); |
| 171 | return 1; |
| 172 | } |
| 173 | if( connect(iSocket,(struct sockaddr*)&addr,sizeof(addr))<0 ){ |
| 174 | socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, |
| 175 | pUrlData->port); |
| 176 | socket_close(); |
| 177 | return 1; |
| 178 | } |
| 179 | #if !defined(_WIN32) |
| 180 | signal(SIGPIPE, SIG_IGN); |
| 181 | #endif |
| 182 | return 0; |
| 183 | } |
| 184 | |
| 185 | /* |
| 186 | ** Send content out over the open socket connection. |
| 187 | */ |
| @@ -220,15 +223,22 @@ | |
| 220 | ** so rcvfrom gets populated. For hostnames with more than one IP (or |
| 221 | ** if overridden in ~/.ssh/config) the rcvfrom may not match the host |
| 222 | ** to which we connect. |
| 223 | */ |
| 224 | void socket_ssh_resolve_addr(UrlData *pUrlData){ |
| 225 | struct hostent *pHost; /* Used to make best effort for rcvfrom */ |
| 226 | struct sockaddr_in addr; |
| 227 | |
| 228 | memset(&addr, 0, sizeof(addr)); |
| 229 | pHost = gethostbyname(pUrlData->name); |
| 230 | if( pHost!=0 ){ |
| 231 | memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); |
| 232 | g.zIpAddr = mprintf("%s", inet_ntoa(addr.sin_addr)); |
| 233 | } |
| 234 | } |
| 235 |
| --- src/http_socket.c | |
| +++ src/http_socket.c | |
| @@ -27,10 +27,13 @@ | |
| 27 | */ |
| 28 | |
| 29 | #include "config.h" |
| 30 | #include "http_socket.h" |
| 31 | #if defined(_WIN32) |
| 32 | # if !defined(_WIN32_WINNT) |
| 33 | # define _WIN32_WINNT 0x0501 |
| 34 | # endif |
| 35 | # include <winsock2.h> |
| 36 | # include <ws2tcpip.h> |
| 37 | #else |
| 38 | # include <netinet/in.h> |
| 39 | # include <arpa/inet.h> |
| @@ -45,11 +48,10 @@ | |
| 48 | ** There can only be a single socket connection open at a time. |
| 49 | ** State information about that socket is stored in the following |
| 50 | ** local variables: |
| 51 | */ |
| 52 | static int socketIsInit = 0; /* True after global initialization */ |
| 53 | #if defined(_WIN32) |
| 54 | static WSADATA socketInfo; /* Windows socket initialize data */ |
| 55 | #endif |
| 56 | static int iSocket = -1; /* The socket on which we talk to the server */ |
| 57 | static char *socketErrMsg = 0; /* Text of most recent socket error */ |
| @@ -106,11 +108,10 @@ | |
| 108 | WSACleanup(); |
| 109 | #endif |
| 110 | socket_clear_errmsg(); |
| 111 | socketIsInit = 0; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | /* |
| 116 | ** Close the currently open socket. If no socket is open, this routine |
| 117 | ** is a no-op. |
| @@ -134,54 +135,56 @@ | |
| 135 | ** pUrlDAta->port TCP/IP port to use. Ex: 80 |
| 136 | ** |
| 137 | ** Return the number of errors. |
| 138 | */ |
| 139 | int socket_open(UrlData *pUrlData){ |
| 140 | int rc = 0; |
| 141 | struct addrinfo *ai = 0; |
| 142 | struct addrinfo *p; |
| 143 | struct addrinfo hints; |
| 144 | char zPort[30]; |
| 145 | char zRemote[NI_MAXHOST]; |
| 146 | |
| 147 | socket_global_init(); |
| 148 | memset(&hints, 0, sizeof(struct addrinfo)); |
| 149 | assert( iSocket<0 ); |
| 150 | hints.ai_family = g.fIPv4 ? AF_INET : AF_UNSPEC; |
| 151 | hints.ai_socktype = SOCK_STREAM; |
| 152 | hints.ai_protocol = IPPROTO_TCP; |
| 153 | sqlite3_snprintf(sizeof(zPort),zPort,"%d", pUrlData->port); |
| 154 | rc = getaddrinfo(pUrlData->name, zPort, &hints, &ai); |
| 155 | if( rc ){ |
| 156 | socket_set_errmsg("getaddrinfo() fails: %s", gai_strerror(rc)); |
| 157 | goto end_socket_open; |
| 158 | } |
| 159 | for(p=ai; p; p=p->ai_next){ |
| 160 | iSocket = socket(p->ai_family, p->ai_socktype, p->ai_protocol); |
| 161 | if( iSocket<0 ) continue; |
| 162 | if( connect(iSocket,p->ai_addr,p->ai_addrlen)<0 ){ |
| 163 | socket_close(); |
| 164 | continue; |
| 165 | } |
| 166 | rc = getnameinfo(p->ai_addr, p->ai_addrlen, zRemote, sizeof(zRemote), |
| 167 | 0, 0, NI_NUMERICHOST); |
| 168 | if( rc ){ |
| 169 | socket_set_errmsg("getnameinfo() failed: %s", gai_strerror(rc)); |
| 170 | goto end_socket_open; |
| 171 | } |
| 172 | g.zIpAddr = mprintf("%s", zRemote); |
| 173 | break; |
| 174 | } |
| 175 | if( p==0 ){ |
| 176 | socket_set_errmsg("cannot connect to host %s:%d", pUrlData->name, |
| 177 | pUrlData->port); |
| 178 | } |
| 179 | #if !defined(_WIN32) |
| 180 | signal(SIGPIPE, SIG_IGN); |
| 181 | #endif |
| 182 | end_socket_open: |
| 183 | if( rc && iSocket>=0 ) socket_close(); |
| 184 | if( ai ) freeaddrinfo(ai); |
| 185 | return rc; |
| 186 | } |
| 187 | |
| 188 | /* |
| 189 | ** Send content out over the open socket connection. |
| 190 | */ |
| @@ -220,15 +223,22 @@ | |
| 223 | ** so rcvfrom gets populated. For hostnames with more than one IP (or |
| 224 | ** if overridden in ~/.ssh/config) the rcvfrom may not match the host |
| 225 | ** to which we connect. |
| 226 | */ |
| 227 | void socket_ssh_resolve_addr(UrlData *pUrlData){ |
| 228 | struct addrinfo *ai = 0; |
| 229 | struct addrinfo hints; |
| 230 | char zRemote[NI_MAXHOST]; |
| 231 | hints.ai_family = AF_UNSPEC; |
| 232 | hints.ai_socktype = SOCK_STREAM; |
| 233 | hints.ai_protocol = IPPROTO_TCP; |
| 234 | if( getaddrinfo(pUrlData->name, NULL, &hints, &ai)==0 |
| 235 | && ai!=0 |
| 236 | && getnameinfo(ai->ai_addr, ai->ai_addrlen, zRemote, |
| 237 | sizeof(zRemote), 0, 0, NI_NUMERICHOST)==0 ){ |
| 238 | g.zIpAddr = mprintf("%s (%s)", zRemote, pUrlData->name); |
| 239 | } |
| 240 | if( ai ) freeaddrinfo(ai); |
| 241 | if( g.zIpAddr==0 ){ |
| 242 | g.zIpAddr = mprintf("%s", pUrlData->name); |
| 243 | } |
| 244 | } |
| 245 |
+1
-1
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -87,11 +87,11 @@ | ||
| 87 | 87 | |
| 88 | 88 | /* |
| 89 | 89 | ** SSH initialization of the transport layer |
| 90 | 90 | */ |
| 91 | 91 | int transport_ssh_open(UrlData *pUrlData){ |
| 92 | - /* For SSH we need to create and run SSH fossil http | |
| 92 | + /* For SSH we need to create and run SSH fossil http | |
| 93 | 93 | ** to talk to the remote machine. |
| 94 | 94 | */ |
| 95 | 95 | const char *zSsh; /* The base SSH command */ |
| 96 | 96 | Blob zCmd; /* The SSH command */ |
| 97 | 97 | char *zHost; /* The host name to contact */ |
| 98 | 98 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -87,11 +87,11 @@ | |
| 87 | |
| 88 | /* |
| 89 | ** SSH initialization of the transport layer |
| 90 | */ |
| 91 | int transport_ssh_open(UrlData *pUrlData){ |
| 92 | /* For SSH we need to create and run SSH fossil http |
| 93 | ** to talk to the remote machine. |
| 94 | */ |
| 95 | const char *zSsh; /* The base SSH command */ |
| 96 | Blob zCmd; /* The SSH command */ |
| 97 | char *zHost; /* The host name to contact */ |
| 98 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -87,11 +87,11 @@ | |
| 87 | |
| 88 | /* |
| 89 | ** SSH initialization of the transport layer |
| 90 | */ |
| 91 | int transport_ssh_open(UrlData *pUrlData){ |
| 92 | /* For SSH we need to create and run SSH fossil http |
| 93 | ** to talk to the remote machine. |
| 94 | */ |
| 95 | const char *zSsh; /* The base SSH command */ |
| 96 | Blob zCmd; /* The SSH command */ |
| 97 | char *zHost; /* The host name to contact */ |
| 98 |
+2
-4
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -198,13 +198,10 @@ | ||
| 198 | 198 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 199 | 199 | if( !verboseFlag ){ |
| 200 | 200 | verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ |
| 201 | 201 | } |
| 202 | 202 | |
| 203 | - /* We should be done with options.. */ | |
| 204 | - verify_all_options(); | |
| 205 | - | |
| 206 | 203 | if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){ |
| 207 | 204 | db_open_config(0); |
| 208 | 205 | db_open_repository(g.argv[2]); |
| 209 | 206 | db_record_repository_filename(g.argv[2]); |
| 210 | 207 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| @@ -211,10 +208,11 @@ | ||
| 211 | 208 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 212 | 209 | extraRepoInfo(); |
| 213 | 210 | return; |
| 214 | 211 | } |
| 215 | 212 | db_find_and_open_repository(0,0); |
| 213 | + verify_all_options(); | |
| 216 | 214 | if( g.argc==2 ){ |
| 217 | 215 | int vid; |
| 218 | 216 | /* 012345678901234 */ |
| 219 | 217 | db_record_repository_filename(0); |
| 220 | 218 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| @@ -716,11 +714,11 @@ | ||
| 716 | 714 | " mperm," |
| 717 | 715 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 718 | 716 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," |
| 719 | 717 | " (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)" |
| 720 | 718 | " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" |
| 721 | - " WHERE mlink.mid=%d" | |
| 719 | + " WHERE mlink.mid=%d AND NOT mlink.isaux" | |
| 722 | 720 | " AND (mlink.fid>0" |
| 723 | 721 | " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))" |
| 724 | 722 | " ORDER BY name /*sort*/", |
| 725 | 723 | rid, rid |
| 726 | 724 | ); |
| 727 | 725 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -198,13 +198,10 @@ | |
| 198 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 199 | if( !verboseFlag ){ |
| 200 | verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ |
| 201 | } |
| 202 | |
| 203 | /* We should be done with options.. */ |
| 204 | verify_all_options(); |
| 205 | |
| 206 | if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){ |
| 207 | db_open_config(0); |
| 208 | db_open_repository(g.argv[2]); |
| 209 | db_record_repository_filename(g.argv[2]); |
| 210 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| @@ -211,10 +208,11 @@ | |
| 211 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 212 | extraRepoInfo(); |
| 213 | return; |
| 214 | } |
| 215 | db_find_and_open_repository(0,0); |
| 216 | if( g.argc==2 ){ |
| 217 | int vid; |
| 218 | /* 012345678901234 */ |
| 219 | db_record_repository_filename(0); |
| 220 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| @@ -716,11 +714,11 @@ | |
| 716 | " mperm," |
| 717 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 718 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," |
| 719 | " (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)" |
| 720 | " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" |
| 721 | " WHERE mlink.mid=%d" |
| 722 | " AND (mlink.fid>0" |
| 723 | " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))" |
| 724 | " ORDER BY name /*sort*/", |
| 725 | rid, rid |
| 726 | ); |
| 727 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -198,13 +198,10 @@ | |
| 198 | int verboseFlag = find_option("verbose","v",0)!=0; |
| 199 | if( !verboseFlag ){ |
| 200 | verboseFlag = find_option("detail","l",0)!=0; /* deprecated */ |
| 201 | } |
| 202 | |
| 203 | if( g.argc==3 && (fsize = file_size(g.argv[2]))>0 && (fsize&0x1ff)==0 ){ |
| 204 | db_open_config(0); |
| 205 | db_open_repository(g.argv[2]); |
| 206 | db_record_repository_filename(g.argv[2]); |
| 207 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| @@ -211,10 +208,11 @@ | |
| 208 | fossil_print("project-code: %s\n", db_get("project-code", "<none>")); |
| 209 | extraRepoInfo(); |
| 210 | return; |
| 211 | } |
| 212 | db_find_and_open_repository(0,0); |
| 213 | verify_all_options(); |
| 214 | if( g.argc==2 ){ |
| 215 | int vid; |
| 216 | /* 012345678901234 */ |
| 217 | db_record_repository_filename(0); |
| 218 | fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>")); |
| @@ -716,11 +714,11 @@ | |
| 714 | " mperm," |
| 715 | " (SELECT uuid FROM blob WHERE rid=mlink.pid)," |
| 716 | " (SELECT uuid FROM blob WHERE rid=mlink.fid)," |
| 717 | " (SELECT name FROM filename WHERE filename.fnid=mlink.pfnid)" |
| 718 | " FROM mlink JOIN filename ON filename.fnid=mlink.fnid" |
| 719 | " WHERE mlink.mid=%d AND NOT mlink.isaux" |
| 720 | " AND (mlink.fid>0" |
| 721 | " OR mlink.fnid NOT IN (SELECT pfnid FROM mlink WHERE mid=%d))" |
| 722 | " ORDER BY name /*sort*/", |
| 723 | rid, rid |
| 724 | ); |
| 725 |
+5
-5
| --- src/json_branch.c | ||
| +++ src/json_branch.c | ||
| @@ -66,11 +66,11 @@ | ||
| 66 | 66 | cson_value * payV; |
| 67 | 67 | cson_object * pay; |
| 68 | 68 | cson_value * listV; |
| 69 | 69 | cson_array * list; |
| 70 | 70 | char const * range = NULL; |
| 71 | - int which = 0; | |
| 71 | + int branchListFlags = BRL_OPEN_ONLY; | |
| 72 | 72 | char * sawConversionError = NULL; |
| 73 | 73 | Stmt q; |
| 74 | 74 | if( !g.perm.Read ){ |
| 75 | 75 | json_set_err(FSL_JSON_E_DENIED, |
| 76 | 76 | "Requires 'o' permissions."); |
| @@ -102,19 +102,19 @@ | ||
| 102 | 102 | } |
| 103 | 103 | /* Normalize range values... */ |
| 104 | 104 | switch(*range){ |
| 105 | 105 | case 'c': |
| 106 | 106 | range = "closed"; |
| 107 | - which = -1; | |
| 107 | + branchListFlags = BRL_CLOSED_ONLY; | |
| 108 | 108 | break; |
| 109 | 109 | case 'a': |
| 110 | 110 | range = "all"; |
| 111 | - which = 1; | |
| 111 | + branchListFlags = BRL_BOTH; | |
| 112 | 112 | break; |
| 113 | 113 | default: |
| 114 | 114 | range = "open"; |
| 115 | - which = 0; | |
| 115 | + branchListFlags = BRL_OPEN_ONLY; | |
| 116 | 116 | break; |
| 117 | 117 | }; |
| 118 | 118 | cson_object_set(pay,"range",json_new_string(range)); |
| 119 | 119 | |
| 120 | 120 | if( g.localOpen ){ /* add "current" property (branch name). */ |
| @@ -128,11 +128,11 @@ | ||
| 128 | 128 | cson_object_set(pay,"current",json_new_string(zCurrent)); |
| 129 | 129 | } |
| 130 | 130 | } |
| 131 | 131 | |
| 132 | 132 | |
| 133 | - branch_prepare_list_query(&q, which); | |
| 133 | + branch_prepare_list_query(&q, branchListFlags); | |
| 134 | 134 | cson_object_set(pay,"branches",listV); |
| 135 | 135 | while((SQLITE_ROW==db_step(&q))){ |
| 136 | 136 | cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0); |
| 137 | 137 | if(v){ |
| 138 | 138 | cson_array_append(list,v); |
| 139 | 139 |
| --- src/json_branch.c | |
| +++ src/json_branch.c | |
| @@ -66,11 +66,11 @@ | |
| 66 | cson_value * payV; |
| 67 | cson_object * pay; |
| 68 | cson_value * listV; |
| 69 | cson_array * list; |
| 70 | char const * range = NULL; |
| 71 | int which = 0; |
| 72 | char * sawConversionError = NULL; |
| 73 | Stmt q; |
| 74 | if( !g.perm.Read ){ |
| 75 | json_set_err(FSL_JSON_E_DENIED, |
| 76 | "Requires 'o' permissions."); |
| @@ -102,19 +102,19 @@ | |
| 102 | } |
| 103 | /* Normalize range values... */ |
| 104 | switch(*range){ |
| 105 | case 'c': |
| 106 | range = "closed"; |
| 107 | which = -1; |
| 108 | break; |
| 109 | case 'a': |
| 110 | range = "all"; |
| 111 | which = 1; |
| 112 | break; |
| 113 | default: |
| 114 | range = "open"; |
| 115 | which = 0; |
| 116 | break; |
| 117 | }; |
| 118 | cson_object_set(pay,"range",json_new_string(range)); |
| 119 | |
| 120 | if( g.localOpen ){ /* add "current" property (branch name). */ |
| @@ -128,11 +128,11 @@ | |
| 128 | cson_object_set(pay,"current",json_new_string(zCurrent)); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | |
| 133 | branch_prepare_list_query(&q, which); |
| 134 | cson_object_set(pay,"branches",listV); |
| 135 | while((SQLITE_ROW==db_step(&q))){ |
| 136 | cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0); |
| 137 | if(v){ |
| 138 | cson_array_append(list,v); |
| 139 |
| --- src/json_branch.c | |
| +++ src/json_branch.c | |
| @@ -66,11 +66,11 @@ | |
| 66 | cson_value * payV; |
| 67 | cson_object * pay; |
| 68 | cson_value * listV; |
| 69 | cson_array * list; |
| 70 | char const * range = NULL; |
| 71 | int branchListFlags = BRL_OPEN_ONLY; |
| 72 | char * sawConversionError = NULL; |
| 73 | Stmt q; |
| 74 | if( !g.perm.Read ){ |
| 75 | json_set_err(FSL_JSON_E_DENIED, |
| 76 | "Requires 'o' permissions."); |
| @@ -102,19 +102,19 @@ | |
| 102 | } |
| 103 | /* Normalize range values... */ |
| 104 | switch(*range){ |
| 105 | case 'c': |
| 106 | range = "closed"; |
| 107 | branchListFlags = BRL_CLOSED_ONLY; |
| 108 | break; |
| 109 | case 'a': |
| 110 | range = "all"; |
| 111 | branchListFlags = BRL_BOTH; |
| 112 | break; |
| 113 | default: |
| 114 | range = "open"; |
| 115 | branchListFlags = BRL_OPEN_ONLY; |
| 116 | break; |
| 117 | }; |
| 118 | cson_object_set(pay,"range",json_new_string(range)); |
| 119 | |
| 120 | if( g.localOpen ){ /* add "current" property (branch name). */ |
| @@ -128,11 +128,11 @@ | |
| 128 | cson_object_set(pay,"current",json_new_string(zCurrent)); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | |
| 133 | branch_prepare_list_query(&q, branchListFlags); |
| 134 | cson_object_set(pay,"branches",listV); |
| 135 | while((SQLITE_ROW==db_step(&q))){ |
| 136 | cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0); |
| 137 | if(v){ |
| 138 | cson_array_append(list,v); |
| 139 |
+5
-5
| --- src/leaf.c | ||
| +++ src/leaf.c | ||
| @@ -27,15 +27,15 @@ | ||
| 27 | 27 | |
| 28 | 28 | |
| 29 | 29 | /* |
| 30 | 30 | ** Return true if the check-in with RID=rid is a leaf. |
| 31 | 31 | ** |
| 32 | -** A leaf has no children in the same branch. | |
| 32 | +** A leaf has no children in the same branch. | |
| 33 | 33 | */ |
| 34 | 34 | int is_a_leaf(int rid){ |
| 35 | 35 | int rc; |
| 36 | - static const char zSql[] = | |
| 36 | + static const char zSql[] = | |
| 37 | 37 | @ SELECT 1 FROM plink |
| 38 | 38 | @ WHERE pid=%d |
| 39 | 39 | @ AND coalesce((SELECT value FROM tagxref |
| 40 | 40 | @ WHERE tagid=%d AND rid=plink.pid), 'trunk') |
| 41 | 41 | @ =coalesce((SELECT value FROM tagxref |
| @@ -56,11 +56,11 @@ | ||
| 56 | 56 | ** A non-branch child is one which is on the same branch as the parent. |
| 57 | 57 | */ |
| 58 | 58 | int count_nonbranch_children(int pid){ |
| 59 | 59 | int nNonBranch = 0; |
| 60 | 60 | static Stmt q; |
| 61 | - static const char zSql[] = | |
| 61 | + static const char zSql[] = | |
| 62 | 62 | @ SELECT count(*) FROM plink |
| 63 | 63 | @ WHERE pid=:pid AND isprim |
| 64 | 64 | @ AND coalesce((SELECT value FROM tagxref |
| 65 | 65 | @ WHERE tagid=%d AND rid=plink.pid), 'trunk') |
| 66 | 66 | @ =coalesce((SELECT value FROM tagxref |
| @@ -75,11 +75,11 @@ | ||
| 75 | 75 | return nNonBranch; |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | 78 | |
| 79 | 79 | /* |
| 80 | -** Recompute the entire LEAF table. | |
| 80 | +** Recompute the entire LEAF table. | |
| 81 | 81 | ** |
| 82 | 82 | ** This can be expensive (5 seconds or so) for a really large repository. |
| 83 | 83 | ** So it is only done for things like a rebuild. |
| 84 | 84 | */ |
| 85 | 85 | void leaf_rebuild(void){ |
| @@ -159,11 +159,11 @@ | ||
| 159 | 159 | ** Schedule a leaf check for "rid" and its parents. |
| 160 | 160 | */ |
| 161 | 161 | void leaf_eventually_check(int rid){ |
| 162 | 162 | static Stmt parentsOf; |
| 163 | 163 | |
| 164 | - db_static_prepare(&parentsOf, | |
| 164 | + db_static_prepare(&parentsOf, | |
| 165 | 165 | "SELECT pid FROM plink WHERE cid=:rid AND pid>0" |
| 166 | 166 | ); |
| 167 | 167 | db_bind_int(&parentsOf, ":rid", rid); |
| 168 | 168 | bag_insert(&needToCheck, rid); |
| 169 | 169 | while( db_step(&parentsOf)==SQLITE_ROW ){ |
| 170 | 170 |
| --- src/leaf.c | |
| +++ src/leaf.c | |
| @@ -27,15 +27,15 @@ | |
| 27 | |
| 28 | |
| 29 | /* |
| 30 | ** Return true if the check-in with RID=rid is a leaf. |
| 31 | ** |
| 32 | ** A leaf has no children in the same branch. |
| 33 | */ |
| 34 | int is_a_leaf(int rid){ |
| 35 | int rc; |
| 36 | static const char zSql[] = |
| 37 | @ SELECT 1 FROM plink |
| 38 | @ WHERE pid=%d |
| 39 | @ AND coalesce((SELECT value FROM tagxref |
| 40 | @ WHERE tagid=%d AND rid=plink.pid), 'trunk') |
| 41 | @ =coalesce((SELECT value FROM tagxref |
| @@ -56,11 +56,11 @@ | |
| 56 | ** A non-branch child is one which is on the same branch as the parent. |
| 57 | */ |
| 58 | int count_nonbranch_children(int pid){ |
| 59 | int nNonBranch = 0; |
| 60 | static Stmt q; |
| 61 | static const char zSql[] = |
| 62 | @ SELECT count(*) FROM plink |
| 63 | @ WHERE pid=:pid AND isprim |
| 64 | @ AND coalesce((SELECT value FROM tagxref |
| 65 | @ WHERE tagid=%d AND rid=plink.pid), 'trunk') |
| 66 | @ =coalesce((SELECT value FROM tagxref |
| @@ -75,11 +75,11 @@ | |
| 75 | return nNonBranch; |
| 76 | } |
| 77 | |
| 78 | |
| 79 | /* |
| 80 | ** Recompute the entire LEAF table. |
| 81 | ** |
| 82 | ** This can be expensive (5 seconds or so) for a really large repository. |
| 83 | ** So it is only done for things like a rebuild. |
| 84 | */ |
| 85 | void leaf_rebuild(void){ |
| @@ -159,11 +159,11 @@ | |
| 159 | ** Schedule a leaf check for "rid" and its parents. |
| 160 | */ |
| 161 | void leaf_eventually_check(int rid){ |
| 162 | static Stmt parentsOf; |
| 163 | |
| 164 | db_static_prepare(&parentsOf, |
| 165 | "SELECT pid FROM plink WHERE cid=:rid AND pid>0" |
| 166 | ); |
| 167 | db_bind_int(&parentsOf, ":rid", rid); |
| 168 | bag_insert(&needToCheck, rid); |
| 169 | while( db_step(&parentsOf)==SQLITE_ROW ){ |
| 170 |
| --- src/leaf.c | |
| +++ src/leaf.c | |
| @@ -27,15 +27,15 @@ | |
| 27 | |
| 28 | |
| 29 | /* |
| 30 | ** Return true if the check-in with RID=rid is a leaf. |
| 31 | ** |
| 32 | ** A leaf has no children in the same branch. |
| 33 | */ |
| 34 | int is_a_leaf(int rid){ |
| 35 | int rc; |
| 36 | static const char zSql[] = |
| 37 | @ SELECT 1 FROM plink |
| 38 | @ WHERE pid=%d |
| 39 | @ AND coalesce((SELECT value FROM tagxref |
| 40 | @ WHERE tagid=%d AND rid=plink.pid), 'trunk') |
| 41 | @ =coalesce((SELECT value FROM tagxref |
| @@ -56,11 +56,11 @@ | |
| 56 | ** A non-branch child is one which is on the same branch as the parent. |
| 57 | */ |
| 58 | int count_nonbranch_children(int pid){ |
| 59 | int nNonBranch = 0; |
| 60 | static Stmt q; |
| 61 | static const char zSql[] = |
| 62 | @ SELECT count(*) FROM plink |
| 63 | @ WHERE pid=:pid AND isprim |
| 64 | @ AND coalesce((SELECT value FROM tagxref |
| 65 | @ WHERE tagid=%d AND rid=plink.pid), 'trunk') |
| 66 | @ =coalesce((SELECT value FROM tagxref |
| @@ -75,11 +75,11 @@ | |
| 75 | return nNonBranch; |
| 76 | } |
| 77 | |
| 78 | |
| 79 | /* |
| 80 | ** Recompute the entire LEAF table. |
| 81 | ** |
| 82 | ** This can be expensive (5 seconds or so) for a really large repository. |
| 83 | ** So it is only done for things like a rebuild. |
| 84 | */ |
| 85 | void leaf_rebuild(void){ |
| @@ -159,11 +159,11 @@ | |
| 159 | ** Schedule a leaf check for "rid" and its parents. |
| 160 | */ |
| 161 | void leaf_eventually_check(int rid){ |
| 162 | static Stmt parentsOf; |
| 163 | |
| 164 | db_static_prepare(&parentsOf, |
| 165 | "SELECT pid FROM plink WHERE cid=:rid AND pid>0" |
| 166 | ); |
| 167 | db_bind_int(&parentsOf, ":rid", rid); |
| 168 | bag_insert(&needToCheck, rid); |
| 169 | while( db_step(&parentsOf)==SQLITE_ROW ){ |
| 170 |
+2
-1
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -483,11 +483,11 @@ | ||
| 483 | 483 | zQS = ""; |
| 484 | 484 | }else if( zQS[0]!=0 ){ |
| 485 | 485 | zQS = mprintf("?%s", zQS); |
| 486 | 486 | } |
| 487 | 487 | cgi_redirectf("%s%s%s", g.zHttpsURL, P("PATH_INFO"), zQS); |
| 488 | - return; | |
| 488 | + return; | |
| 489 | 489 | } |
| 490 | 490 | sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 491 | 491 | constant_time_cmp_function, 0, 0); |
| 492 | 492 | zUsername = P("u"); |
| 493 | 493 | zPasswd = P("p"); |
| @@ -575,10 +575,11 @@ | ||
| 575 | 575 | login_set_user_cookie(zUsername, uid, NULL); |
| 576 | 576 | redirect_to_g(); |
| 577 | 577 | } |
| 578 | 578 | } |
| 579 | 579 | style_header("Login/Logout"); |
| 580 | + style_adunit_config(ADUNIT_OFF); | |
| 580 | 581 | @ %s(zErrMsg) |
| 581 | 582 | if( zGoto && P("anon")==0 ){ |
| 582 | 583 | @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p> |
| 583 | 584 | } |
| 584 | 585 | form_begin(0, "%R/login"); |
| 585 | 586 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -483,11 +483,11 @@ | |
| 483 | zQS = ""; |
| 484 | }else if( zQS[0]!=0 ){ |
| 485 | zQS = mprintf("?%s", zQS); |
| 486 | } |
| 487 | cgi_redirectf("%s%s%s", g.zHttpsURL, P("PATH_INFO"), zQS); |
| 488 | return; |
| 489 | } |
| 490 | sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 491 | constant_time_cmp_function, 0, 0); |
| 492 | zUsername = P("u"); |
| 493 | zPasswd = P("p"); |
| @@ -575,10 +575,11 @@ | |
| 575 | login_set_user_cookie(zUsername, uid, NULL); |
| 576 | redirect_to_g(); |
| 577 | } |
| 578 | } |
| 579 | style_header("Login/Logout"); |
| 580 | @ %s(zErrMsg) |
| 581 | if( zGoto && P("anon")==0 ){ |
| 582 | @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p> |
| 583 | } |
| 584 | form_begin(0, "%R/login"); |
| 585 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -483,11 +483,11 @@ | |
| 483 | zQS = ""; |
| 484 | }else if( zQS[0]!=0 ){ |
| 485 | zQS = mprintf("?%s", zQS); |
| 486 | } |
| 487 | cgi_redirectf("%s%s%s", g.zHttpsURL, P("PATH_INFO"), zQS); |
| 488 | return; |
| 489 | } |
| 490 | sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 491 | constant_time_cmp_function, 0, 0); |
| 492 | zUsername = P("u"); |
| 493 | zPasswd = P("p"); |
| @@ -575,10 +575,11 @@ | |
| 575 | login_set_user_cookie(zUsername, uid, NULL); |
| 576 | redirect_to_g(); |
| 577 | } |
| 578 | } |
| 579 | style_header("Login/Logout"); |
| 580 | style_adunit_config(ADUNIT_OFF); |
| 581 | @ %s(zErrMsg) |
| 582 | if( zGoto && P("anon")==0 ){ |
| 583 | @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p> |
| 584 | } |
| 585 | form_begin(0, "%R/login"); |
| 586 |
+79
-15
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -143,16 +143,18 @@ | ||
| 143 | 143 | int fSqlTrace; /* True if --sqltrace flag is present */ |
| 144 | 144 | int fSqlStats; /* True if --sqltrace or --sqlstats are present */ |
| 145 | 145 | int fSqlPrint; /* True if -sqlprint flag is present */ |
| 146 | 146 | int fQuiet; /* True if -quiet flag is present */ |
| 147 | 147 | int fHttpTrace; /* Trace outbound HTTP requests */ |
| 148 | + int fAnyTrace; /* Any kind of tracing */ | |
| 148 | 149 | char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 149 | 150 | int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ |
| 150 | 151 | int fSshTrace; /* Trace the SSH setup traffic */ |
| 151 | 152 | int fSshClient; /* HTTP client flags for SSH client */ |
| 152 | 153 | char *zSshCmd; /* SSH command string */ |
| 153 | 154 | int fNoSync; /* Do not do an autosync ever. --nosync */ |
| 155 | + int fIPv4; /* Use only IPv4, not IPv6. --ipv4 */ | |
| 154 | 156 | char *zPath; /* Name of webpage being served */ |
| 155 | 157 | char *zExtra; /* Extra path information past the webpage name */ |
| 156 | 158 | char *zBaseURL; /* Full text of the URL being served */ |
| 157 | 159 | char *zHttpsURL; /* zBaseURL translated to https: */ |
| 158 | 160 | char *zTop; /* Parent directory of zPath */ |
| @@ -657,15 +659,15 @@ | ||
| 657 | 659 | g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; |
| 658 | 660 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 659 | 661 | g.fSshClient = 0; |
| 660 | 662 | g.zSshCmd = 0; |
| 661 | 663 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 662 | - g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; | |
| 663 | 664 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 664 | 665 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 665 | 666 | g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; |
| 666 | 667 | #endif |
| 668 | + g.fAnyTrace = g.fSqlTrace|g.fSystemTrace|g.fSshTrace|g.fHttpTrace; | |
| 667 | 669 | g.zHttpAuth = 0; |
| 668 | 670 | g.zLogin = find_option("user", "U", 1); |
| 669 | 671 | g.zSSLIdentity = find_option("ssl-identity", 0, 1); |
| 670 | 672 | g.zErrlog = find_option("errorlog", 0, 1); |
| 671 | 673 | fossil_init_flags_from_options(); |
| @@ -1787,57 +1789,119 @@ | ||
| 1787 | 1789 | g.cgiOutput = 1; |
| 1788 | 1790 | blob_read_from_file(&config, zFile); |
| 1789 | 1791 | while( blob_line(&config, &line) ){ |
| 1790 | 1792 | if( !blob_token(&line, &key) ) continue; |
| 1791 | 1793 | if( blob_buffer(&key)[0]=='#' ) continue; |
| 1792 | - if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){ | |
| 1793 | - g.fDebug = fossil_fopen(blob_str(&value), "ab"); | |
| 1794 | - blob_reset(&value); | |
| 1795 | - continue; | |
| 1796 | - } | |
| 1797 | - if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ | |
| 1798 | - g.zErrlog = mprintf("%s", blob_str(&value)); | |
| 1799 | - continue; | |
| 1800 | - } | |
| 1801 | - if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){ | |
| 1802 | - cgi_setenv("HOME", blob_str(&value)); | |
| 1803 | - blob_reset(&value); | |
| 1804 | - continue; | |
| 1805 | - } | |
| 1806 | 1794 | if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){ |
| 1795 | + /* repository: FILENAME | |
| 1796 | + ** | |
| 1797 | + ** The name of the Fossil repository to be served via CGI. Most | |
| 1798 | + ** fossil CGI scripts have a single non-comment line that contains | |
| 1799 | + ** this one entry. | |
| 1800 | + */ | |
| 1807 | 1801 | blob_trim(&value); |
| 1808 | 1802 | db_open_repository(blob_str(&value)); |
| 1809 | 1803 | blob_reset(&value); |
| 1810 | 1804 | continue; |
| 1811 | 1805 | } |
| 1812 | 1806 | if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){ |
| 1807 | + /* directory: DIRECTORY | |
| 1808 | + ** | |
| 1809 | + ** If repository: is omitted, then terms of the PATH_INFO cgi parameter | |
| 1810 | + ** are appended to DIRECTORY looking for a repository (whose name ends | |
| 1811 | + ** in ".fossil") or a file in "files:". | |
| 1812 | + */ | |
| 1813 | 1813 | db_close(1); |
| 1814 | 1814 | g.zRepositoryName = mprintf("%s", blob_str(&value)); |
| 1815 | 1815 | blob_reset(&value); |
| 1816 | 1816 | continue; |
| 1817 | 1817 | } |
| 1818 | 1818 | if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){ |
| 1819 | + /* notfound: URL | |
| 1820 | + ** | |
| 1821 | + ** If using directory: and no suitable repository or file is found, | |
| 1822 | + ** then redirect to URL. | |
| 1823 | + */ | |
| 1819 | 1824 | zNotFound = mprintf("%s", blob_str(&value)); |
| 1820 | 1825 | blob_reset(&value); |
| 1821 | 1826 | continue; |
| 1822 | 1827 | } |
| 1823 | 1828 | if( blob_eq(&key, "localauth") ){ |
| 1829 | + /* localauth | |
| 1830 | + ** | |
| 1831 | + ** Grant "administrator" privileges to users connecting with HTTP | |
| 1832 | + ** from IP address 127.0.0.1. Do not bother checking credentials. | |
| 1833 | + */ | |
| 1824 | 1834 | g.useLocalauth = 1; |
| 1825 | 1835 | continue; |
| 1826 | 1836 | } |
| 1827 | 1837 | if( blob_eq(&key, "redirect:") && blob_token(&line, &value) |
| 1828 | 1838 | && blob_token(&line, &value2) ){ |
| 1839 | + /* See the header comment on the redirect_web_page() function | |
| 1840 | + ** above for details. */ | |
| 1829 | 1841 | nRedirect++; |
| 1830 | 1842 | azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*)); |
| 1831 | 1843 | azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value)); |
| 1832 | 1844 | azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2)); |
| 1833 | 1845 | blob_reset(&value); |
| 1834 | 1846 | blob_reset(&value2); |
| 1835 | 1847 | continue; |
| 1836 | 1848 | } |
| 1837 | 1849 | if( blob_eq(&key, "files:") && blob_token(&line, &value) ){ |
| 1850 | + /* files: GLOBLIST | |
| 1851 | + ** | |
| 1852 | + ** GLOBLIST is a comma-separated list of filename globs. For | |
| 1853 | + ** example: *.html,*.css,*.js | |
| 1854 | + ** | |
| 1855 | + ** If the repository: line is omitted and then PATH_INFO is searched | |
| 1856 | + ** for files that match any of these GLOBs and if any such file is | |
| 1857 | + ** found it is returned verbatim. This feature allows "fossil server" | |
| 1858 | + ** to function as a primitive web-server delivering arbitrary content. | |
| 1859 | + */ | |
| 1838 | 1860 | pFileGlob = glob_create(blob_str(&value)); |
| 1861 | + blob_reset(&value); | |
| 1862 | + continue; | |
| 1863 | + } | |
| 1864 | + if( blob_eq(&key, "setenv:") && blob_token(&line, &value) | |
| 1865 | + && blob_token(&line, &value2) ){ | |
| 1866 | + /* setenv: NAME VALUE | |
| 1867 | + ** | |
| 1868 | + ** Sets environment variable NAME to VALUE | |
| 1869 | + */ | |
| 1870 | + fossil_setenv(blob_str(&value), blob_str(&value2)); | |
| 1871 | + blob_reset(&value); | |
| 1872 | + blob_reset(&value2); | |
| 1873 | + continue; | |
| 1874 | + } | |
| 1875 | + if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){ | |
| 1876 | + /* debug: FILENAME | |
| 1877 | + ** | |
| 1878 | + ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go | |
| 1879 | + ** into FILENAME. | |
| 1880 | + */ | |
| 1881 | + g.fDebug = fossil_fopen(blob_str(&value), "ab"); | |
| 1882 | + blob_reset(&value); | |
| 1883 | + continue; | |
| 1884 | + } | |
| 1885 | + if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ | |
| 1886 | + /* errorlog: FILENAME | |
| 1887 | + ** | |
| 1888 | + ** Causes messages from warnings, errors, and panics to be appended | |
| 1889 | + ** to FILENAME. | |
| 1890 | + */ | |
| 1891 | + g.zErrlog = mprintf("%s", blob_str(&value)); | |
| 1892 | + blob_reset(&value); | |
| 1893 | + continue; | |
| 1894 | + } | |
| 1895 | + if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){ | |
| 1896 | + /* HOME: VALUE | |
| 1897 | + ** | |
| 1898 | + ** Set CGI parameter "HOME" to VALUE. This is legacy. Use | |
| 1899 | + ** setenv: instead. | |
| 1900 | + */ | |
| 1901 | + cgi_setenv("HOME", blob_str(&value)); | |
| 1902 | + blob_reset(&value); | |
| 1839 | 1903 | continue; |
| 1840 | 1904 | } |
| 1841 | 1905 | } |
| 1842 | 1906 | blob_reset(&config); |
| 1843 | 1907 | if( g.db==0 && g.zRepositoryName==0 && nRedirect==0 ){ |
| 1844 | 1908 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -143,16 +143,18 @@ | |
| 143 | int fSqlTrace; /* True if --sqltrace flag is present */ |
| 144 | int fSqlStats; /* True if --sqltrace or --sqlstats are present */ |
| 145 | int fSqlPrint; /* True if -sqlprint flag is present */ |
| 146 | int fQuiet; /* True if -quiet flag is present */ |
| 147 | int fHttpTrace; /* Trace outbound HTTP requests */ |
| 148 | char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 149 | int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ |
| 150 | int fSshTrace; /* Trace the SSH setup traffic */ |
| 151 | int fSshClient; /* HTTP client flags for SSH client */ |
| 152 | char *zSshCmd; /* SSH command string */ |
| 153 | int fNoSync; /* Do not do an autosync ever. --nosync */ |
| 154 | char *zPath; /* Name of webpage being served */ |
| 155 | char *zExtra; /* Extra path information past the webpage name */ |
| 156 | char *zBaseURL; /* Full text of the URL being served */ |
| 157 | char *zHttpsURL; /* zBaseURL translated to https: */ |
| 158 | char *zTop; /* Parent directory of zPath */ |
| @@ -657,15 +659,15 @@ | |
| 657 | g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; |
| 658 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 659 | g.fSshClient = 0; |
| 660 | g.zSshCmd = 0; |
| 661 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 662 | g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; |
| 663 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 664 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 665 | g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; |
| 666 | #endif |
| 667 | g.zHttpAuth = 0; |
| 668 | g.zLogin = find_option("user", "U", 1); |
| 669 | g.zSSLIdentity = find_option("ssl-identity", 0, 1); |
| 670 | g.zErrlog = find_option("errorlog", 0, 1); |
| 671 | fossil_init_flags_from_options(); |
| @@ -1787,57 +1789,119 @@ | |
| 1787 | g.cgiOutput = 1; |
| 1788 | blob_read_from_file(&config, zFile); |
| 1789 | while( blob_line(&config, &line) ){ |
| 1790 | if( !blob_token(&line, &key) ) continue; |
| 1791 | if( blob_buffer(&key)[0]=='#' ) continue; |
| 1792 | if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){ |
| 1793 | g.fDebug = fossil_fopen(blob_str(&value), "ab"); |
| 1794 | blob_reset(&value); |
| 1795 | continue; |
| 1796 | } |
| 1797 | if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ |
| 1798 | g.zErrlog = mprintf("%s", blob_str(&value)); |
| 1799 | continue; |
| 1800 | } |
| 1801 | if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){ |
| 1802 | cgi_setenv("HOME", blob_str(&value)); |
| 1803 | blob_reset(&value); |
| 1804 | continue; |
| 1805 | } |
| 1806 | if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){ |
| 1807 | blob_trim(&value); |
| 1808 | db_open_repository(blob_str(&value)); |
| 1809 | blob_reset(&value); |
| 1810 | continue; |
| 1811 | } |
| 1812 | if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){ |
| 1813 | db_close(1); |
| 1814 | g.zRepositoryName = mprintf("%s", blob_str(&value)); |
| 1815 | blob_reset(&value); |
| 1816 | continue; |
| 1817 | } |
| 1818 | if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){ |
| 1819 | zNotFound = mprintf("%s", blob_str(&value)); |
| 1820 | blob_reset(&value); |
| 1821 | continue; |
| 1822 | } |
| 1823 | if( blob_eq(&key, "localauth") ){ |
| 1824 | g.useLocalauth = 1; |
| 1825 | continue; |
| 1826 | } |
| 1827 | if( blob_eq(&key, "redirect:") && blob_token(&line, &value) |
| 1828 | && blob_token(&line, &value2) ){ |
| 1829 | nRedirect++; |
| 1830 | azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*)); |
| 1831 | azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value)); |
| 1832 | azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2)); |
| 1833 | blob_reset(&value); |
| 1834 | blob_reset(&value2); |
| 1835 | continue; |
| 1836 | } |
| 1837 | if( blob_eq(&key, "files:") && blob_token(&line, &value) ){ |
| 1838 | pFileGlob = glob_create(blob_str(&value)); |
| 1839 | continue; |
| 1840 | } |
| 1841 | } |
| 1842 | blob_reset(&config); |
| 1843 | if( g.db==0 && g.zRepositoryName==0 && nRedirect==0 ){ |
| 1844 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -143,16 +143,18 @@ | |
| 143 | int fSqlTrace; /* True if --sqltrace flag is present */ |
| 144 | int fSqlStats; /* True if --sqltrace or --sqlstats are present */ |
| 145 | int fSqlPrint; /* True if -sqlprint flag is present */ |
| 146 | int fQuiet; /* True if -quiet flag is present */ |
| 147 | int fHttpTrace; /* Trace outbound HTTP requests */ |
| 148 | int fAnyTrace; /* Any kind of tracing */ |
| 149 | char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 150 | int fSystemTrace; /* Trace calls to fossil_system(), --systemtrace */ |
| 151 | int fSshTrace; /* Trace the SSH setup traffic */ |
| 152 | int fSshClient; /* HTTP client flags for SSH client */ |
| 153 | char *zSshCmd; /* SSH command string */ |
| 154 | int fNoSync; /* Do not do an autosync ever. --nosync */ |
| 155 | int fIPv4; /* Use only IPv4, not IPv6. --ipv4 */ |
| 156 | char *zPath; /* Name of webpage being served */ |
| 157 | char *zExtra; /* Extra path information past the webpage name */ |
| 158 | char *zBaseURL; /* Full text of the URL being served */ |
| 159 | char *zHttpsURL; /* zBaseURL translated to https: */ |
| 160 | char *zTop; /* Parent directory of zPath */ |
| @@ -657,15 +659,15 @@ | |
| 659 | g.fSystemTrace = find_option("systemtrace", 0, 0)!=0; |
| 660 | g.fSshTrace = find_option("sshtrace", 0, 0)!=0; |
| 661 | g.fSshClient = 0; |
| 662 | g.zSshCmd = 0; |
| 663 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 664 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 665 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 666 | g.fNoThHook = find_option("no-th-hook", 0, 0)!=0; |
| 667 | #endif |
| 668 | g.fAnyTrace = g.fSqlTrace|g.fSystemTrace|g.fSshTrace|g.fHttpTrace; |
| 669 | g.zHttpAuth = 0; |
| 670 | g.zLogin = find_option("user", "U", 1); |
| 671 | g.zSSLIdentity = find_option("ssl-identity", 0, 1); |
| 672 | g.zErrlog = find_option("errorlog", 0, 1); |
| 673 | fossil_init_flags_from_options(); |
| @@ -1787,57 +1789,119 @@ | |
| 1789 | g.cgiOutput = 1; |
| 1790 | blob_read_from_file(&config, zFile); |
| 1791 | while( blob_line(&config, &line) ){ |
| 1792 | if( !blob_token(&line, &key) ) continue; |
| 1793 | if( blob_buffer(&key)[0]=='#' ) continue; |
| 1794 | if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){ |
| 1795 | /* repository: FILENAME |
| 1796 | ** |
| 1797 | ** The name of the Fossil repository to be served via CGI. Most |
| 1798 | ** fossil CGI scripts have a single non-comment line that contains |
| 1799 | ** this one entry. |
| 1800 | */ |
| 1801 | blob_trim(&value); |
| 1802 | db_open_repository(blob_str(&value)); |
| 1803 | blob_reset(&value); |
| 1804 | continue; |
| 1805 | } |
| 1806 | if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){ |
| 1807 | /* directory: DIRECTORY |
| 1808 | ** |
| 1809 | ** If repository: is omitted, then terms of the PATH_INFO cgi parameter |
| 1810 | ** are appended to DIRECTORY looking for a repository (whose name ends |
| 1811 | ** in ".fossil") or a file in "files:". |
| 1812 | */ |
| 1813 | db_close(1); |
| 1814 | g.zRepositoryName = mprintf("%s", blob_str(&value)); |
| 1815 | blob_reset(&value); |
| 1816 | continue; |
| 1817 | } |
| 1818 | if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){ |
| 1819 | /* notfound: URL |
| 1820 | ** |
| 1821 | ** If using directory: and no suitable repository or file is found, |
| 1822 | ** then redirect to URL. |
| 1823 | */ |
| 1824 | zNotFound = mprintf("%s", blob_str(&value)); |
| 1825 | blob_reset(&value); |
| 1826 | continue; |
| 1827 | } |
| 1828 | if( blob_eq(&key, "localauth") ){ |
| 1829 | /* localauth |
| 1830 | ** |
| 1831 | ** Grant "administrator" privileges to users connecting with HTTP |
| 1832 | ** from IP address 127.0.0.1. Do not bother checking credentials. |
| 1833 | */ |
| 1834 | g.useLocalauth = 1; |
| 1835 | continue; |
| 1836 | } |
| 1837 | if( blob_eq(&key, "redirect:") && blob_token(&line, &value) |
| 1838 | && blob_token(&line, &value2) ){ |
| 1839 | /* See the header comment on the redirect_web_page() function |
| 1840 | ** above for details. */ |
| 1841 | nRedirect++; |
| 1842 | azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*)); |
| 1843 | azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value)); |
| 1844 | azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2)); |
| 1845 | blob_reset(&value); |
| 1846 | blob_reset(&value2); |
| 1847 | continue; |
| 1848 | } |
| 1849 | if( blob_eq(&key, "files:") && blob_token(&line, &value) ){ |
| 1850 | /* files: GLOBLIST |
| 1851 | ** |
| 1852 | ** GLOBLIST is a comma-separated list of filename globs. For |
| 1853 | ** example: *.html,*.css,*.js |
| 1854 | ** |
| 1855 | ** If the repository: line is omitted and then PATH_INFO is searched |
| 1856 | ** for files that match any of these GLOBs and if any such file is |
| 1857 | ** found it is returned verbatim. This feature allows "fossil server" |
| 1858 | ** to function as a primitive web-server delivering arbitrary content. |
| 1859 | */ |
| 1860 | pFileGlob = glob_create(blob_str(&value)); |
| 1861 | blob_reset(&value); |
| 1862 | continue; |
| 1863 | } |
| 1864 | if( blob_eq(&key, "setenv:") && blob_token(&line, &value) |
| 1865 | && blob_token(&line, &value2) ){ |
| 1866 | /* setenv: NAME VALUE |
| 1867 | ** |
| 1868 | ** Sets environment variable NAME to VALUE |
| 1869 | */ |
| 1870 | fossil_setenv(blob_str(&value), blob_str(&value2)); |
| 1871 | blob_reset(&value); |
| 1872 | blob_reset(&value2); |
| 1873 | continue; |
| 1874 | } |
| 1875 | if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){ |
| 1876 | /* debug: FILENAME |
| 1877 | ** |
| 1878 | ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go |
| 1879 | ** into FILENAME. |
| 1880 | */ |
| 1881 | g.fDebug = fossil_fopen(blob_str(&value), "ab"); |
| 1882 | blob_reset(&value); |
| 1883 | continue; |
| 1884 | } |
| 1885 | if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ |
| 1886 | /* errorlog: FILENAME |
| 1887 | ** |
| 1888 | ** Causes messages from warnings, errors, and panics to be appended |
| 1889 | ** to FILENAME. |
| 1890 | */ |
| 1891 | g.zErrlog = mprintf("%s", blob_str(&value)); |
| 1892 | blob_reset(&value); |
| 1893 | continue; |
| 1894 | } |
| 1895 | if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){ |
| 1896 | /* HOME: VALUE |
| 1897 | ** |
| 1898 | ** Set CGI parameter "HOME" to VALUE. This is legacy. Use |
| 1899 | ** setenv: instead. |
| 1900 | */ |
| 1901 | cgi_setenv("HOME", blob_str(&value)); |
| 1902 | blob_reset(&value); |
| 1903 | continue; |
| 1904 | } |
| 1905 | } |
| 1906 | blob_reset(&config); |
| 1907 | if( g.db==0 && g.zRepositoryName==0 && nRedirect==0 ){ |
| 1908 |
+39
-2
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -104,10 +104,11 @@ | ||
| 104 | 104 | $(SRCDIR)/sitemap.c \ |
| 105 | 105 | $(SRCDIR)/skins.c \ |
| 106 | 106 | $(SRCDIR)/sqlcmd.c \ |
| 107 | 107 | $(SRCDIR)/stash.c \ |
| 108 | 108 | $(SRCDIR)/stat.c \ |
| 109 | + $(SRCDIR)/statrep.c \ | |
| 109 | 110 | $(SRCDIR)/style.c \ |
| 110 | 111 | $(SRCDIR)/sync.c \ |
| 111 | 112 | $(SRCDIR)/tag.c \ |
| 112 | 113 | $(SRCDIR)/tar.c \ |
| 113 | 114 | $(SRCDIR)/th_main.c \ |
| @@ -131,11 +132,36 @@ | ||
| 131 | 132 | $(SRCDIR)/xfer.c \ |
| 132 | 133 | $(SRCDIR)/xfersetup.c \ |
| 133 | 134 | $(SRCDIR)/zip.c |
| 134 | 135 | |
| 135 | 136 | EXTRA_FILES = \ |
| 136 | - $(SRCDIR)/diff.tcl | |
| 137 | + $(SRCDIR)/../skins/black_and_white/css.txt \ | |
| 138 | + $(SRCDIR)/../skins/black_and_white/footer.txt \ | |
| 139 | + $(SRCDIR)/../skins/black_and_white/header.txt \ | |
| 140 | + $(SRCDIR)/../skins/default/css.txt \ | |
| 141 | + $(SRCDIR)/../skins/default/footer.txt \ | |
| 142 | + $(SRCDIR)/../skins/default/header.txt \ | |
| 143 | + $(SRCDIR)/../skins/eagle/css.txt \ | |
| 144 | + $(SRCDIR)/../skins/eagle/footer.txt \ | |
| 145 | + $(SRCDIR)/../skins/eagle/header.txt \ | |
| 146 | + $(SRCDIR)/../skins/enhanced1/css.txt \ | |
| 147 | + $(SRCDIR)/../skins/enhanced1/footer.txt \ | |
| 148 | + $(SRCDIR)/../skins/enhanced1/header.txt \ | |
| 149 | + $(SRCDIR)/../skins/etienne1/css.txt \ | |
| 150 | + $(SRCDIR)/../skins/etienne1/footer.txt \ | |
| 151 | + $(SRCDIR)/../skins/etienne1/header.txt \ | |
| 152 | + $(SRCDIR)/../skins/khaki/css.txt \ | |
| 153 | + $(SRCDIR)/../skins/khaki/footer.txt \ | |
| 154 | + $(SRCDIR)/../skins/khaki/header.txt \ | |
| 155 | + $(SRCDIR)/../skins/plain_gray/css.txt \ | |
| 156 | + $(SRCDIR)/../skins/plain_gray/footer.txt \ | |
| 157 | + $(SRCDIR)/../skins/plain_gray/header.txt \ | |
| 158 | + $(SRCDIR)/../skins/rounded1/css.txt \ | |
| 159 | + $(SRCDIR)/../skins/rounded1/footer.txt \ | |
| 160 | + $(SRCDIR)/../skins/rounded1/header.txt \ | |
| 161 | + $(SRCDIR)/diff.tcl \ | |
| 162 | + $(SRCDIR)/markdown.md | |
| 137 | 163 | |
| 138 | 164 | TRANS_SRC = \ |
| 139 | 165 | $(OBJDIR)/add_.c \ |
| 140 | 166 | $(OBJDIR)/allrepo_.c \ |
| 141 | 167 | $(OBJDIR)/attach_.c \ |
| @@ -226,10 +252,11 @@ | ||
| 226 | 252 | $(OBJDIR)/sitemap_.c \ |
| 227 | 253 | $(OBJDIR)/skins_.c \ |
| 228 | 254 | $(OBJDIR)/sqlcmd_.c \ |
| 229 | 255 | $(OBJDIR)/stash_.c \ |
| 230 | 256 | $(OBJDIR)/stat_.c \ |
| 257 | + $(OBJDIR)/statrep_.c \ | |
| 231 | 258 | $(OBJDIR)/style_.c \ |
| 232 | 259 | $(OBJDIR)/sync_.c \ |
| 233 | 260 | $(OBJDIR)/tag_.c \ |
| 234 | 261 | $(OBJDIR)/tar_.c \ |
| 235 | 262 | $(OBJDIR)/th_main_.c \ |
| @@ -345,10 +372,11 @@ | ||
| 345 | 372 | $(OBJDIR)/sitemap.o \ |
| 346 | 373 | $(OBJDIR)/skins.o \ |
| 347 | 374 | $(OBJDIR)/sqlcmd.o \ |
| 348 | 375 | $(OBJDIR)/stash.o \ |
| 349 | 376 | $(OBJDIR)/stat.o \ |
| 377 | + $(OBJDIR)/statrep.o \ | |
| 350 | 378 | $(OBJDIR)/style.o \ |
| 351 | 379 | $(OBJDIR)/sync.o \ |
| 352 | 380 | $(OBJDIR)/tag.o \ |
| 353 | 381 | $(OBJDIR)/tar.o \ |
| 354 | 382 | $(OBJDIR)/th_main.o \ |
| @@ -478,11 +506,11 @@ | ||
| 478 | 506 | |
| 479 | 507 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex |
| 480 | 508 | $(OBJDIR)/mkindex $(TRANS_SRC) >$@ |
| 481 | 509 | |
| 482 | 510 | $(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES) |
| 483 | - $(OBJDIR)/mkbuiltin $(EXTRA_FILES) >$@ | |
| 511 | + $(OBJDIR)/mkbuiltin --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ | |
| 484 | 512 | |
| 485 | 513 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h |
| 486 | 514 | $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 487 | 515 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 488 | 516 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| @@ -573,10 +601,11 @@ | ||
| 573 | 601 | $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ |
| 574 | 602 | $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ |
| 575 | 603 | $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ |
| 576 | 604 | $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ |
| 577 | 605 | $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ |
| 606 | + $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ | |
| 578 | 607 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 579 | 608 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 580 | 609 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 581 | 610 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 582 | 611 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| @@ -1340,10 +1369,18 @@ | ||
| 1340 | 1369 | |
| 1341 | 1370 | $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h |
| 1342 | 1371 | $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c |
| 1343 | 1372 | |
| 1344 | 1373 | $(OBJDIR)/stat.h: $(OBJDIR)/headers |
| 1374 | + | |
| 1375 | +$(OBJDIR)/statrep_.c: $(SRCDIR)/statrep.c $(OBJDIR)/translate | |
| 1376 | + $(OBJDIR)/translate $(SRCDIR)/statrep.c >$@ | |
| 1377 | + | |
| 1378 | +$(OBJDIR)/statrep.o: $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h $(SRCDIR)/config.h | |
| 1379 | + $(XTCC) -o $(OBJDIR)/statrep.o -c $(OBJDIR)/statrep_.c | |
| 1380 | + | |
| 1381 | +$(OBJDIR)/statrep.h: $(OBJDIR)/headers | |
| 1345 | 1382 | |
| 1346 | 1383 | $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(OBJDIR)/translate |
| 1347 | 1384 | $(OBJDIR)/translate $(SRCDIR)/style.c >$@ |
| 1348 | 1385 | |
| 1349 | 1386 | $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h |
| 1350 | 1387 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -104,10 +104,11 @@ | |
| 104 | $(SRCDIR)/sitemap.c \ |
| 105 | $(SRCDIR)/skins.c \ |
| 106 | $(SRCDIR)/sqlcmd.c \ |
| 107 | $(SRCDIR)/stash.c \ |
| 108 | $(SRCDIR)/stat.c \ |
| 109 | $(SRCDIR)/style.c \ |
| 110 | $(SRCDIR)/sync.c \ |
| 111 | $(SRCDIR)/tag.c \ |
| 112 | $(SRCDIR)/tar.c \ |
| 113 | $(SRCDIR)/th_main.c \ |
| @@ -131,11 +132,36 @@ | |
| 131 | $(SRCDIR)/xfer.c \ |
| 132 | $(SRCDIR)/xfersetup.c \ |
| 133 | $(SRCDIR)/zip.c |
| 134 | |
| 135 | EXTRA_FILES = \ |
| 136 | $(SRCDIR)/diff.tcl |
| 137 | |
| 138 | TRANS_SRC = \ |
| 139 | $(OBJDIR)/add_.c \ |
| 140 | $(OBJDIR)/allrepo_.c \ |
| 141 | $(OBJDIR)/attach_.c \ |
| @@ -226,10 +252,11 @@ | |
| 226 | $(OBJDIR)/sitemap_.c \ |
| 227 | $(OBJDIR)/skins_.c \ |
| 228 | $(OBJDIR)/sqlcmd_.c \ |
| 229 | $(OBJDIR)/stash_.c \ |
| 230 | $(OBJDIR)/stat_.c \ |
| 231 | $(OBJDIR)/style_.c \ |
| 232 | $(OBJDIR)/sync_.c \ |
| 233 | $(OBJDIR)/tag_.c \ |
| 234 | $(OBJDIR)/tar_.c \ |
| 235 | $(OBJDIR)/th_main_.c \ |
| @@ -345,10 +372,11 @@ | |
| 345 | $(OBJDIR)/sitemap.o \ |
| 346 | $(OBJDIR)/skins.o \ |
| 347 | $(OBJDIR)/sqlcmd.o \ |
| 348 | $(OBJDIR)/stash.o \ |
| 349 | $(OBJDIR)/stat.o \ |
| 350 | $(OBJDIR)/style.o \ |
| 351 | $(OBJDIR)/sync.o \ |
| 352 | $(OBJDIR)/tag.o \ |
| 353 | $(OBJDIR)/tar.o \ |
| 354 | $(OBJDIR)/th_main.o \ |
| @@ -478,11 +506,11 @@ | |
| 478 | |
| 479 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex |
| 480 | $(OBJDIR)/mkindex $(TRANS_SRC) >$@ |
| 481 | |
| 482 | $(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES) |
| 483 | $(OBJDIR)/mkbuiltin $(EXTRA_FILES) >$@ |
| 484 | |
| 485 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h |
| 486 | $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 487 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 488 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| @@ -573,10 +601,11 @@ | |
| 573 | $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ |
| 574 | $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ |
| 575 | $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ |
| 576 | $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ |
| 577 | $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ |
| 578 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 579 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 580 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 581 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 582 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| @@ -1340,10 +1369,18 @@ | |
| 1340 | |
| 1341 | $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h |
| 1342 | $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c |
| 1343 | |
| 1344 | $(OBJDIR)/stat.h: $(OBJDIR)/headers |
| 1345 | |
| 1346 | $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(OBJDIR)/translate |
| 1347 | $(OBJDIR)/translate $(SRCDIR)/style.c >$@ |
| 1348 | |
| 1349 | $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h |
| 1350 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -104,10 +104,11 @@ | |
| 104 | $(SRCDIR)/sitemap.c \ |
| 105 | $(SRCDIR)/skins.c \ |
| 106 | $(SRCDIR)/sqlcmd.c \ |
| 107 | $(SRCDIR)/stash.c \ |
| 108 | $(SRCDIR)/stat.c \ |
| 109 | $(SRCDIR)/statrep.c \ |
| 110 | $(SRCDIR)/style.c \ |
| 111 | $(SRCDIR)/sync.c \ |
| 112 | $(SRCDIR)/tag.c \ |
| 113 | $(SRCDIR)/tar.c \ |
| 114 | $(SRCDIR)/th_main.c \ |
| @@ -131,11 +132,36 @@ | |
| 132 | $(SRCDIR)/xfer.c \ |
| 133 | $(SRCDIR)/xfersetup.c \ |
| 134 | $(SRCDIR)/zip.c |
| 135 | |
| 136 | EXTRA_FILES = \ |
| 137 | $(SRCDIR)/../skins/black_and_white/css.txt \ |
| 138 | $(SRCDIR)/../skins/black_and_white/footer.txt \ |
| 139 | $(SRCDIR)/../skins/black_and_white/header.txt \ |
| 140 | $(SRCDIR)/../skins/default/css.txt \ |
| 141 | $(SRCDIR)/../skins/default/footer.txt \ |
| 142 | $(SRCDIR)/../skins/default/header.txt \ |
| 143 | $(SRCDIR)/../skins/eagle/css.txt \ |
| 144 | $(SRCDIR)/../skins/eagle/footer.txt \ |
| 145 | $(SRCDIR)/../skins/eagle/header.txt \ |
| 146 | $(SRCDIR)/../skins/enhanced1/css.txt \ |
| 147 | $(SRCDIR)/../skins/enhanced1/footer.txt \ |
| 148 | $(SRCDIR)/../skins/enhanced1/header.txt \ |
| 149 | $(SRCDIR)/../skins/etienne1/css.txt \ |
| 150 | $(SRCDIR)/../skins/etienne1/footer.txt \ |
| 151 | $(SRCDIR)/../skins/etienne1/header.txt \ |
| 152 | $(SRCDIR)/../skins/khaki/css.txt \ |
| 153 | $(SRCDIR)/../skins/khaki/footer.txt \ |
| 154 | $(SRCDIR)/../skins/khaki/header.txt \ |
| 155 | $(SRCDIR)/../skins/plain_gray/css.txt \ |
| 156 | $(SRCDIR)/../skins/plain_gray/footer.txt \ |
| 157 | $(SRCDIR)/../skins/plain_gray/header.txt \ |
| 158 | $(SRCDIR)/../skins/rounded1/css.txt \ |
| 159 | $(SRCDIR)/../skins/rounded1/footer.txt \ |
| 160 | $(SRCDIR)/../skins/rounded1/header.txt \ |
| 161 | $(SRCDIR)/diff.tcl \ |
| 162 | $(SRCDIR)/markdown.md |
| 163 | |
| 164 | TRANS_SRC = \ |
| 165 | $(OBJDIR)/add_.c \ |
| 166 | $(OBJDIR)/allrepo_.c \ |
| 167 | $(OBJDIR)/attach_.c \ |
| @@ -226,10 +252,11 @@ | |
| 252 | $(OBJDIR)/sitemap_.c \ |
| 253 | $(OBJDIR)/skins_.c \ |
| 254 | $(OBJDIR)/sqlcmd_.c \ |
| 255 | $(OBJDIR)/stash_.c \ |
| 256 | $(OBJDIR)/stat_.c \ |
| 257 | $(OBJDIR)/statrep_.c \ |
| 258 | $(OBJDIR)/style_.c \ |
| 259 | $(OBJDIR)/sync_.c \ |
| 260 | $(OBJDIR)/tag_.c \ |
| 261 | $(OBJDIR)/tar_.c \ |
| 262 | $(OBJDIR)/th_main_.c \ |
| @@ -345,10 +372,11 @@ | |
| 372 | $(OBJDIR)/sitemap.o \ |
| 373 | $(OBJDIR)/skins.o \ |
| 374 | $(OBJDIR)/sqlcmd.o \ |
| 375 | $(OBJDIR)/stash.o \ |
| 376 | $(OBJDIR)/stat.o \ |
| 377 | $(OBJDIR)/statrep.o \ |
| 378 | $(OBJDIR)/style.o \ |
| 379 | $(OBJDIR)/sync.o \ |
| 380 | $(OBJDIR)/tag.o \ |
| 381 | $(OBJDIR)/tar.o \ |
| 382 | $(OBJDIR)/th_main.o \ |
| @@ -478,11 +506,11 @@ | |
| 506 | |
| 507 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(OBJDIR)/mkindex |
| 508 | $(OBJDIR)/mkindex $(TRANS_SRC) >$@ |
| 509 | |
| 510 | $(OBJDIR)/builtin_data.h: $(OBJDIR)/mkbuiltin $(EXTRA_FILES) |
| 511 | $(OBJDIR)/mkbuiltin --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ |
| 512 | |
| 513 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h |
| 514 | $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 515 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 516 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| @@ -573,10 +601,11 @@ | |
| 601 | $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ |
| 602 | $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ |
| 603 | $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ |
| 604 | $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ |
| 605 | $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ |
| 606 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 607 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 608 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 609 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 610 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 611 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| @@ -1340,10 +1369,18 @@ | |
| 1369 | |
| 1370 | $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h |
| 1371 | $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c |
| 1372 | |
| 1373 | $(OBJDIR)/stat.h: $(OBJDIR)/headers |
| 1374 | |
| 1375 | $(OBJDIR)/statrep_.c: $(SRCDIR)/statrep.c $(OBJDIR)/translate |
| 1376 | $(OBJDIR)/translate $(SRCDIR)/statrep.c >$@ |
| 1377 | |
| 1378 | $(OBJDIR)/statrep.o: $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h $(SRCDIR)/config.h |
| 1379 | $(XTCC) -o $(OBJDIR)/statrep.o -c $(OBJDIR)/statrep_.c |
| 1380 | |
| 1381 | $(OBJDIR)/statrep.h: $(OBJDIR)/headers |
| 1382 | |
| 1383 | $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(OBJDIR)/translate |
| 1384 | $(OBJDIR)/translate $(SRCDIR)/style.c >$@ |
| 1385 | |
| 1386 | $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h |
| 1387 |
+59
-59
| --- src/makeheaders.c | ||
| +++ src/makeheaders.c | ||
| @@ -112,19 +112,19 @@ | ||
| 112 | 112 | ** |
| 113 | 113 | ** struct Xyzzy; |
| 114 | 114 | ** |
| 115 | 115 | ** Not every object has a forward declaration. If it does, thought, the |
| 116 | 116 | ** forward declaration will be contained in the zFwd field for C and |
| 117 | -** the zFwdCpp for C++. The zDecl field contains the complete | |
| 118 | -** declaration text. | |
| 117 | +** the zFwdCpp for C++. The zDecl field contains the complete | |
| 118 | +** declaration text. | |
| 119 | 119 | */ |
| 120 | 120 | typedef struct Decl Decl; |
| 121 | 121 | struct Decl { |
| 122 | 122 | char *zName; /* Name of the object being declared. The appearance |
| 123 | 123 | ** of this name is a source file triggers the declaration |
| 124 | 124 | ** to be added to the header for that file. */ |
| 125 | - char *zFile; /* File from which extracted. */ | |
| 125 | + const char *zFile; /* File from which extracted. */ | |
| 126 | 126 | char *zIf; /* Surround the declaration with this #if */ |
| 127 | 127 | char *zFwd; /* A forward declaration. NULL if there is none. */ |
| 128 | 128 | char *zFwdCpp; /* Use this forward declaration for C++. */ |
| 129 | 129 | char *zDecl; /* A full declaration of this object */ |
| 130 | 130 | char *zExtra; /* Extra declaration text inserted into class objects */ |
| @@ -163,11 +163,11 @@ | ||
| 163 | 163 | ** in the output when using the -H option.) |
| 164 | 164 | ** |
| 165 | 165 | ** EXPORT scope The object is visible and usable everywhere. |
| 166 | 166 | ** |
| 167 | 167 | ** The DP_Flag is a temporary use flag that is used during processing to |
| 168 | -** prevent an infinite loop. It's use is localized. | |
| 168 | +** prevent an infinite loop. It's use is localized. | |
| 169 | 169 | ** |
| 170 | 170 | ** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent |
| 171 | 171 | ** and are used to specify what type of declaration the object requires. |
| 172 | 172 | */ |
| 173 | 173 | #define DP_Forward 0x001 /* Has a forward declaration in this file */ |
| @@ -201,11 +201,11 @@ | ||
| 201 | 201 | ** Be careful not to confuse PS_Export with DP_Export or |
| 202 | 202 | ** PS_Local with DP_Local. Their names are similar, but the meanings |
| 203 | 203 | ** of these flags are very different. |
| 204 | 204 | */ |
| 205 | 205 | #define PS_Extern 0x000800 /* "extern" has been seen */ |
| 206 | -#define PS_Export 0x001000 /* If between "#if EXPORT_INTERFACE" | |
| 206 | +#define PS_Export 0x001000 /* If between "#if EXPORT_INTERFACE" | |
| 207 | 207 | ** and "#endif" */ |
| 208 | 208 | #define PS_Export2 0x002000 /* If "EXPORT" seen */ |
| 209 | 209 | #define PS_Typedef 0x004000 /* If "typedef" has been seen */ |
| 210 | 210 | #define PS_Static 0x008000 /* If "static" has been seen */ |
| 211 | 211 | #define PS_Interface 0x010000 /* If within #if INTERFACE..#endif */ |
| @@ -231,11 +231,11 @@ | ||
| 231 | 231 | #define TY_Union 0x04000000 |
| 232 | 232 | #define TY_Enumeration 0x08000000 |
| 233 | 233 | #define TY_Defunct 0x10000000 /* Used to erase a declaration */ |
| 234 | 234 | |
| 235 | 235 | /* |
| 236 | -** Each nested #if (or #ifdef or #ifndef) is stored in a stack of | |
| 236 | +** Each nested #if (or #ifdef or #ifndef) is stored in a stack of | |
| 237 | 237 | ** instances of the following structure. |
| 238 | 238 | */ |
| 239 | 239 | typedef struct Ifmacro Ifmacro; |
| 240 | 240 | struct Ifmacro { |
| 241 | 241 | int nLine; /* Line number where this macro occurs */ |
| @@ -293,11 +293,11 @@ | ||
| 293 | 293 | int flags; /* One or more DP_, PS_ and/or TY_ flags */ |
| 294 | 294 | InFile *pNext; /* Next input file in the list of them all */ |
| 295 | 295 | IdentTable idTable; /* All identifiers in this input file */ |
| 296 | 296 | }; |
| 297 | 297 | |
| 298 | -/* | |
| 298 | +/* | |
| 299 | 299 | ** An unbounded string is able to grow without limit. We use these |
| 300 | 300 | ** to construct large in-memory strings from lots of smaller components. |
| 301 | 301 | */ |
| 302 | 302 | typedef struct String String; |
| 303 | 303 | struct String { |
| @@ -332,19 +332,19 @@ | ||
| 332 | 332 | ** never to read a file that it generated itself. |
| 333 | 333 | ** |
| 334 | 334 | ** The "#undef INTERFACE" part is a hack to work around a name collision |
| 335 | 335 | ** in MSVC 2008. |
| 336 | 336 | */ |
| 337 | -const char zTopLine[] = | |
| 337 | +const char zTopLine[] = | |
| 338 | 338 | "/* \aThis file was automatically generated. Do not edit! */\n" |
| 339 | 339 | "#undef INTERFACE\n"; |
| 340 | 340 | #define nTopLine (sizeof(zTopLine)-1) |
| 341 | 341 | |
| 342 | 342 | /* |
| 343 | 343 | ** The name of the file currently being parsed. |
| 344 | 344 | */ |
| 345 | -static char *zFilename; | |
| 345 | +static const char *zFilename; | |
| 346 | 346 | |
| 347 | 347 | /* |
| 348 | 348 | ** The stack of #if macros for the file currently being parsed. |
| 349 | 349 | */ |
| 350 | 350 | static Ifmacro *ifStack = 0; |
| @@ -702,11 +702,11 @@ | ||
| 702 | 702 | struct stat sStat; |
| 703 | 703 | FILE *pIn; |
| 704 | 704 | char *zBuf; |
| 705 | 705 | int n; |
| 706 | 706 | |
| 707 | - if( stat(zFilename,&sStat)!=0 | |
| 707 | + if( stat(zFilename,&sStat)!=0 | |
| 708 | 708 | #ifndef WIN32 |
| 709 | 709 | || !S_ISREG(sStat.st_mode) |
| 710 | 710 | #endif |
| 711 | 711 | ){ |
| 712 | 712 | return 0; |
| @@ -889,12 +889,12 @@ | ||
| 889 | 889 | } |
| 890 | 890 | } |
| 891 | 891 | } |
| 892 | 892 | i++; |
| 893 | 893 | } |
| 894 | - if( z[i] ){ | |
| 895 | - i += 2; | |
| 894 | + if( z[i] ){ | |
| 895 | + i += 2; | |
| 896 | 896 | }else{ |
| 897 | 897 | isBlockComment = 0; |
| 898 | 898 | fprintf(stderr,"%s:%d: Unterminated comment\n", |
| 899 | 899 | zFilename, startLine); |
| 900 | 900 | nErr++; |
| @@ -906,11 +906,11 @@ | ||
| 906 | 906 | pToken->eType = TT_Other; |
| 907 | 907 | pToken->nText = 1 + (z[i+1]=='+'); |
| 908 | 908 | } |
| 909 | 909 | break; |
| 910 | 910 | |
| 911 | - case '0': | |
| 911 | + case '0': | |
| 912 | 912 | if( z[i+1]=='x' || z[i+1]=='X' ){ |
| 913 | 913 | /* A hex constant */ |
| 914 | 914 | i += 2; |
| 915 | 915 | while( isxdigit(z[i]) ){ i++; } |
| 916 | 916 | }else{ |
| @@ -963,11 +963,11 @@ | ||
| 963 | 963 | while( isalnum(z[i]) || z[i]=='_' ){ i++; }; |
| 964 | 964 | pToken->eType = TT_Id; |
| 965 | 965 | pToken->nText = i - pIn->i; |
| 966 | 966 | break; |
| 967 | 967 | |
| 968 | - case ':': | |
| 968 | + case ':': | |
| 969 | 969 | pToken->eType = TT_Other; |
| 970 | 970 | pToken->nText = 1 + (z[i+1]==':'); |
| 971 | 971 | break; |
| 972 | 972 | |
| 973 | 973 | case '=': |
| @@ -977,11 +977,11 @@ | ||
| 977 | 977 | case '-': |
| 978 | 978 | case '*': |
| 979 | 979 | case '%': |
| 980 | 980 | case '^': |
| 981 | 981 | case '&': |
| 982 | - case '|': | |
| 982 | + case '|': | |
| 983 | 983 | pToken->eType = TT_Other; |
| 984 | 984 | pToken->nText = 1 + (z[i+1]=='='); |
| 985 | 985 | break; |
| 986 | 986 | |
| 987 | 987 | default: |
| @@ -1064,11 +1064,11 @@ | ||
| 1064 | 1064 | } |
| 1065 | 1065 | } |
| 1066 | 1066 | /* NOT REACHED */ |
| 1067 | 1067 | } |
| 1068 | 1068 | |
| 1069 | -/* | |
| 1069 | +/* | |
| 1070 | 1070 | ** This routine looks for identifiers (strings of contiguous alphanumeric |
| 1071 | 1071 | ** characters) within a preprocessor directive and adds every such string |
| 1072 | 1072 | ** found to the given identifier table |
| 1073 | 1073 | */ |
| 1074 | 1074 | static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){ |
| @@ -1157,11 +1157,11 @@ | ||
| 1157 | 1157 | case TT_Id: |
| 1158 | 1158 | if( pTable ){ |
| 1159 | 1159 | IdentTableInsert(pTable,pToken->zText,pToken->nText); |
| 1160 | 1160 | } |
| 1161 | 1161 | break; |
| 1162 | - | |
| 1162 | + | |
| 1163 | 1163 | case TT_Preprocessor: |
| 1164 | 1164 | if( pTable!=0 ){ |
| 1165 | 1165 | FindIdentifiersInMacro(pToken,pTable); |
| 1166 | 1166 | } |
| 1167 | 1167 | break; |
| @@ -1263,11 +1263,11 @@ | ||
| 1263 | 1263 | exit(1); |
| 1264 | 1264 | } |
| 1265 | 1265 | pList = TokenizeFile(zFile,&sTable); |
| 1266 | 1266 | for(p=pList; p; p=p->pNext){ |
| 1267 | 1267 | int j; |
| 1268 | - switch( p->eType ){ | |
| 1268 | + switch( p->eType ){ | |
| 1269 | 1269 | case TT_Space: |
| 1270 | 1270 | printf("%4d: Space\n",p->nLine); |
| 1271 | 1271 | break; |
| 1272 | 1272 | case TT_Id: |
| 1273 | 1273 | printf("%4d: Id %.*s\n",p->nLine,p->nText,p->zText); |
| @@ -1330,11 +1330,11 @@ | ||
| 1330 | 1330 | needSpace = 1; |
| 1331 | 1331 | break; |
| 1332 | 1332 | |
| 1333 | 1333 | default: |
| 1334 | 1334 | c = pFirst->zText[0]; |
| 1335 | - printf("%s%.*s", | |
| 1335 | + printf("%s%.*s", | |
| 1336 | 1336 | (needSpace && (c=='*' || c=='{')) ? " " : "", |
| 1337 | 1337 | pFirst->nText, pFirst->zText); |
| 1338 | 1338 | needSpace = pFirst->zText[0]==','; |
| 1339 | 1339 | break; |
| 1340 | 1340 | } |
| @@ -1371,13 +1371,13 @@ | ||
| 1371 | 1371 | |
| 1372 | 1372 | StringInit(&str); |
| 1373 | 1373 | pLast = pLast->pNext; |
| 1374 | 1374 | while( pFirst!=pLast ){ |
| 1375 | 1375 | if( pFirst==pSkip ){ iSkip = nSkip; } |
| 1376 | - if( iSkip>0 ){ | |
| 1376 | + if( iSkip>0 ){ | |
| 1377 | 1377 | iSkip--; |
| 1378 | - pFirst=pFirst->pNext; | |
| 1378 | + pFirst=pFirst->pNext; | |
| 1379 | 1379 | continue; |
| 1380 | 1380 | } |
| 1381 | 1381 | switch( pFirst->eType ){ |
| 1382 | 1382 | case TT_Preprocessor: |
| 1383 | 1383 | StringAppend(&str,"\n",1); |
| @@ -1384,13 +1384,13 @@ | ||
| 1384 | 1384 | StringAppend(&str,pFirst->zText,pFirst->nText); |
| 1385 | 1385 | StringAppend(&str,"\n",1); |
| 1386 | 1386 | needSpace = 0; |
| 1387 | 1387 | break; |
| 1388 | 1388 | |
| 1389 | - case TT_Id: | |
| 1389 | + case TT_Id: | |
| 1390 | 1390 | switch( pFirst->zText[0] ){ |
| 1391 | - case 'E': | |
| 1391 | + case 'E': | |
| 1392 | 1392 | if( pFirst->nText==6 && strncmp(pFirst->zText,"EXPORT",6)==0 ){ |
| 1393 | 1393 | skipOne = 1; |
| 1394 | 1394 | } |
| 1395 | 1395 | break; |
| 1396 | 1396 | case 'P': |
| @@ -1645,17 +1645,17 @@ | ||
| 1645 | 1645 | pLast = pLast->pNext; |
| 1646 | 1646 | for(p=pFirst; p && p!=pLast; p=p->pNext){ |
| 1647 | 1647 | if( p->eType==TT_Id ){ |
| 1648 | 1648 | static IdentTable sReserved; |
| 1649 | 1649 | static int isInit = 0; |
| 1650 | - static char *aWords[] = { "char", "class", | |
| 1651 | - "const", "double", "enum", "extern", "EXPORT", "ET_PROC", | |
| 1650 | + static const char *aWords[] = { "char", "class", | |
| 1651 | + "const", "double", "enum", "extern", "EXPORT", "ET_PROC", | |
| 1652 | 1652 | "float", "int", "long", |
| 1653 | 1653 | "PRIVATE", "PROTECTED", "PUBLIC", |
| 1654 | - "register", "static", "struct", "sizeof", "signed", "typedef", | |
| 1654 | + "register", "static", "struct", "sizeof", "signed", "typedef", | |
| 1655 | 1655 | "union", "volatile", "virtual", "void", }; |
| 1656 | - | |
| 1656 | + | |
| 1657 | 1657 | if( !isInit ){ |
| 1658 | 1658 | int i; |
| 1659 | 1659 | for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){ |
| 1660 | 1660 | IdentTableInsert(&sReserved,aWords[i],0); |
| 1661 | 1661 | } |
| @@ -1768,11 +1768,11 @@ | ||
| 1768 | 1768 | pCode = pLast; |
| 1769 | 1769 | while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){ |
| 1770 | 1770 | pLast = pLast->pPrev; |
| 1771 | 1771 | } |
| 1772 | 1772 | if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){ |
| 1773 | - fprintf(stderr,"%s:%d: Unrecognized syntax.\n", | |
| 1773 | + fprintf(stderr,"%s:%d: Unrecognized syntax.\n", | |
| 1774 | 1774 | zFilename, pFirst->nLine); |
| 1775 | 1775 | return 1; |
| 1776 | 1776 | } |
| 1777 | 1777 | if( flags & (PS_Interface|PS_Export|PS_Local) ){ |
| 1778 | 1778 | fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n", |
| @@ -1849,11 +1849,11 @@ | ||
| 1849 | 1849 | return 1; |
| 1850 | 1850 | } |
| 1851 | 1851 | |
| 1852 | 1852 | #ifdef DEBUG |
| 1853 | 1853 | if( debugMask & PARSER ){ |
| 1854 | - printf("**** Found inline routine: %.*s on line %d...\n", | |
| 1854 | + printf("**** Found inline routine: %.*s on line %d...\n", | |
| 1855 | 1855 | pName->nText, pName->zText, pFirst->nLine); |
| 1856 | 1856 | PrintTokens(pFirst,pEnd); |
| 1857 | 1857 | printf("\n"); |
| 1858 | 1858 | } |
| 1859 | 1859 | #endif |
| @@ -1888,11 +1888,11 @@ | ||
| 1888 | 1888 | ** to search for an occurrence of an ID followed immediately by '('. |
| 1889 | 1889 | ** If found, we have a prototype. Otherwise we are dealing with a |
| 1890 | 1890 | ** variable definition. |
| 1891 | 1891 | */ |
| 1892 | 1892 | static int isVariableDef(Token *pFirst, Token *pEnd){ |
| 1893 | - if( pEnd && pEnd->zText[0]=='=' && | |
| 1893 | + if( pEnd && pEnd->zText[0]=='=' && | |
| 1894 | 1894 | (pEnd->pPrev->nText!=8 || strncmp(pEnd->pPrev->zText,"operator",8)!=0) |
| 1895 | 1895 | ){ |
| 1896 | 1896 | return 1; |
| 1897 | 1897 | } |
| 1898 | 1898 | while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){ |
| @@ -1949,11 +1949,11 @@ | ||
| 1949 | 1949 | } |
| 1950 | 1950 | while( pFirst!=0 && pFirst->pNext!=pEnd && |
| 1951 | 1951 | ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0) |
| 1952 | 1952 | || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0)) |
| 1953 | 1953 | ){ |
| 1954 | - /* Lose the initial "static" or local from local variables. | |
| 1954 | + /* Lose the initial "static" or local from local variables. | |
| 1955 | 1955 | ** We'll prepend "extern" later. */ |
| 1956 | 1956 | pFirst = pFirst->pNext; |
| 1957 | 1957 | isLocal = 1; |
| 1958 | 1958 | } |
| 1959 | 1959 | if( pFirst==0 || !isLocal ){ |
| @@ -1962,11 +1962,11 @@ | ||
| 1962 | 1962 | }else if( flags & PS_Method ){ |
| 1963 | 1963 | /* Methods are declared by their class. Don't declare separately. */ |
| 1964 | 1964 | return nErr; |
| 1965 | 1965 | } |
| 1966 | 1966 | isVar = (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd); |
| 1967 | - if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0 | |
| 1967 | + if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0 | |
| 1968 | 1968 | && (flags & PS_Extern)==0 ){ |
| 1969 | 1969 | fprintf(stderr,"%s:%d: Can't define a variable in this context\n", |
| 1970 | 1970 | zFilename, pFirst->nLine); |
| 1971 | 1971 | nErr++; |
| 1972 | 1972 | } |
| @@ -2095,11 +2095,11 @@ | ||
| 2095 | 2095 | nCmd++; |
| 2096 | 2096 | } |
| 2097 | 2097 | |
| 2098 | 2098 | if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){ |
| 2099 | 2099 | /* |
| 2100 | - ** Pop the if stack | |
| 2100 | + ** Pop the if stack | |
| 2101 | 2101 | */ |
| 2102 | 2102 | pIf = ifStack; |
| 2103 | 2103 | if( pIf==0 ){ |
| 2104 | 2104 | fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine); |
| 2105 | 2105 | return 1; |
| @@ -2106,11 +2106,11 @@ | ||
| 2106 | 2106 | } |
| 2107 | 2107 | ifStack = pIf->pNext; |
| 2108 | 2108 | SafeFree(pIf); |
| 2109 | 2109 | }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){ |
| 2110 | 2110 | /* |
| 2111 | - ** Record a #define if we are in PS_Interface or PS_Export | |
| 2111 | + ** Record a #define if we are in PS_Interface or PS_Export | |
| 2112 | 2112 | */ |
| 2113 | 2113 | Decl *pDecl; |
| 2114 | 2114 | if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; } |
| 2115 | 2115 | zArg = &zCmd[6]; |
| 2116 | 2116 | while( *zArg && isspace(*zArg) && *zArg!='\n' ){ |
| @@ -2129,11 +2129,11 @@ | ||
| 2129 | 2129 | }else if( flags & PS_Local ){ |
| 2130 | 2130 | DeclSetProperty(pDecl,DP_Local); |
| 2131 | 2131 | } |
| 2132 | 2132 | }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){ |
| 2133 | 2133 | /* |
| 2134 | - ** Record an #include if we are in PS_Interface or PS_Export | |
| 2134 | + ** Record an #include if we are in PS_Interface or PS_Export | |
| 2135 | 2135 | */ |
| 2136 | 2136 | Include *pInclude; |
| 2137 | 2137 | char *zIf; |
| 2138 | 2138 | |
| 2139 | 2139 | if( !(flags & (PS_Interface|PS_Export)) ){ return 0; } |
| @@ -2184,11 +2184,11 @@ | ||
| 2184 | 2184 | PushIfMacro(0,0,0,pToken->nLine,PS_Local); |
| 2185 | 2185 | }else{ |
| 2186 | 2186 | PushIfMacro(0,zArg,nArg,pToken->nLine,0); |
| 2187 | 2187 | } |
| 2188 | 2188 | }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){ |
| 2189 | - /* | |
| 2189 | + /* | |
| 2190 | 2190 | ** Push an #ifdef. |
| 2191 | 2191 | */ |
| 2192 | 2192 | zArg = &zCmd[5]; |
| 2193 | 2193 | while( *zArg && isspace(*zArg) && *zArg!='\n' ){ |
| 2194 | 2194 | zArg++; |
| @@ -2207,11 +2207,11 @@ | ||
| 2207 | 2207 | if( *zArg==0 || *zArg=='\n' ){ return 0; } |
| 2208 | 2208 | nArg = pToken->nText + (int)(pToken->zText - zArg); |
| 2209 | 2209 | PushIfMacro("!defined",zArg,nArg,pToken->nLine,0); |
| 2210 | 2210 | }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){ |
| 2211 | 2211 | /* |
| 2212 | - ** Invert the #if on the top of the stack | |
| 2212 | + ** Invert the #if on the top of the stack | |
| 2213 | 2213 | */ |
| 2214 | 2214 | if( ifStack==0 ){ |
| 2215 | 2215 | fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename, |
| 2216 | 2216 | pToken->nLine); |
| 2217 | 2217 | return 1; |
| @@ -2224,33 +2224,33 @@ | ||
| 2224 | 2224 | }else{ |
| 2225 | 2225 | pIf->flags = 0; |
| 2226 | 2226 | } |
| 2227 | 2227 | }else{ |
| 2228 | 2228 | /* |
| 2229 | - ** This directive can be safely ignored | |
| 2229 | + ** This directive can be safely ignored | |
| 2230 | 2230 | */ |
| 2231 | 2231 | return 0; |
| 2232 | 2232 | } |
| 2233 | 2233 | |
| 2234 | - /* | |
| 2235 | - ** Recompute the preset flags | |
| 2234 | + /* | |
| 2235 | + ** Recompute the preset flags | |
| 2236 | 2236 | */ |
| 2237 | 2237 | *pPresetFlags = 0; |
| 2238 | 2238 | for(pIf = ifStack; pIf; pIf=pIf->pNext){ |
| 2239 | 2239 | *pPresetFlags |= pIf->flags; |
| 2240 | 2240 | } |
| 2241 | - | |
| 2241 | + | |
| 2242 | 2242 | return nErr; |
| 2243 | 2243 | } |
| 2244 | 2244 | |
| 2245 | 2245 | /* |
| 2246 | 2246 | ** Parse an entire file. Return the number of errors. |
| 2247 | 2247 | ** |
| 2248 | 2248 | ** pList is a list of tokens in the file. Whitespace tokens have been |
| 2249 | 2249 | ** eliminated, and text with {...} has been collapsed into a |
| 2250 | 2250 | ** single TT_Brace token. |
| 2251 | -** | |
| 2251 | +** | |
| 2252 | 2252 | ** initFlags are a set of parse flags that should always be set for this |
| 2253 | 2253 | ** file. For .c files this is normally 0. For .h files it is PS_Interface. |
| 2254 | 2254 | */ |
| 2255 | 2255 | static int ParseFile(Token *pList, int initFlags){ |
| 2256 | 2256 | int nErr = 0; |
| @@ -2279,11 +2279,11 @@ | ||
| 2279 | 2279 | pStart = 0; |
| 2280 | 2280 | flags = presetFlags; |
| 2281 | 2281 | break; |
| 2282 | 2282 | |
| 2283 | 2283 | case '=': |
| 2284 | - if( pList->pPrev->nText==8 | |
| 2284 | + if( pList->pPrev->nText==8 | |
| 2285 | 2285 | && strncmp(pList->pPrev->zText,"operator",8)==0 ){ |
| 2286 | 2286 | break; |
| 2287 | 2287 | } |
| 2288 | 2288 | nErr += ProcessDecl(pStart,pList,flags); |
| 2289 | 2289 | pStart = 0; |
| @@ -2471,11 +2471,11 @@ | ||
| 2471 | 2471 | pDecl->zExtra = 0; |
| 2472 | 2472 | } |
| 2473 | 2473 | |
| 2474 | 2474 | /* |
| 2475 | 2475 | ** Reset the DP_Forward and DP_Declared flags on all Decl structures. |
| 2476 | -** Set both flags for anything that is tagged as local and isn't | |
| 2476 | +** Set both flags for anything that is tagged as local and isn't | |
| 2477 | 2477 | ** in the file zFilename so that it won't be printing in other files. |
| 2478 | 2478 | */ |
| 2479 | 2479 | static void ResetDeclFlags(char *zFilename){ |
| 2480 | 2480 | Decl *pDecl; |
| 2481 | 2481 | |
| @@ -2574,11 +2574,11 @@ | ||
| 2574 | 2574 | int flag; |
| 2575 | 2575 | int isCpp; /* True if generating C++ */ |
| 2576 | 2576 | int doneTypedef = 0; /* True if a typedef has been done for this object */ |
| 2577 | 2577 | |
| 2578 | 2578 | /* printf("BEGIN %s of %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/ |
| 2579 | - /* | |
| 2579 | + /* | |
| 2580 | 2580 | ** For any object that has a forward declaration, go ahead and do the |
| 2581 | 2581 | ** forward declaration first. |
| 2582 | 2582 | */ |
| 2583 | 2583 | isCpp = (pState->flags & DP_Cplusplus) != 0; |
| 2584 | 2584 | for(p=pDecl; p; p=p->pSameName){ |
| @@ -2626,12 +2626,12 @@ | ||
| 2626 | 2626 | ** function on a recursive call with the same pDecl. Hence, recursive |
| 2627 | 2627 | ** calls to this function (through ScanText()) can never change the |
| 2628 | 2628 | ** value of DP_Flag out from under us. |
| 2629 | 2629 | */ |
| 2630 | 2630 | for(p=pDecl; p; p=p->pSameName){ |
| 2631 | - if( !DeclHasProperty(p,DP_Declared) | |
| 2632 | - && (p->zFwd==0 || needFullDecl) | |
| 2631 | + if( !DeclHasProperty(p,DP_Declared) | |
| 2632 | + && (p->zFwd==0 || needFullDecl) | |
| 2633 | 2633 | && p->zDecl!=0 |
| 2634 | 2634 | ){ |
| 2635 | 2635 | DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag); |
| 2636 | 2636 | }else{ |
| 2637 | 2637 | DeclClearProperty(p,DP_Flag); |
| @@ -2735,12 +2735,12 @@ | ||
| 2735 | 2735 | ** by sToken. |
| 2736 | 2736 | */ |
| 2737 | 2737 | pDecl = FindDecl(sToken.zText,sToken.nText); |
| 2738 | 2738 | if( pDecl==0 ) continue; |
| 2739 | 2739 | |
| 2740 | - /* | |
| 2741 | - ** If we get this far, we've found an identifier that has a | |
| 2740 | + /* | |
| 2741 | + ** If we get this far, we've found an identifier that has a | |
| 2742 | 2742 | ** declaration in the database. Now see if we the full declaration |
| 2743 | 2743 | ** or just a forward declaration. |
| 2744 | 2744 | */ |
| 2745 | 2745 | GetNonspaceToken(&sIn,&sNext); |
| 2746 | 2746 | if( sNext.zText[0]=='*' ){ |
| @@ -2770,12 +2770,12 @@ | ||
| 2770 | 2770 | int progress; |
| 2771 | 2771 | |
| 2772 | 2772 | do{ |
| 2773 | 2773 | progress = 0; |
| 2774 | 2774 | for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){ |
| 2775 | - if( DeclHasProperty(pDecl,DP_Forward) | |
| 2776 | - && !DeclHasProperty(pDecl,DP_Declared) | |
| 2775 | + if( DeclHasProperty(pDecl,DP_Forward) | |
| 2776 | + && !DeclHasProperty(pDecl,DP_Declared) | |
| 2777 | 2777 | ){ |
| 2778 | 2778 | DeclareObject(pDecl,pState,1); |
| 2779 | 2779 | progress = 1; |
| 2780 | 2780 | assert( DeclHasProperty(pDecl,DP_Declared) ); |
| 2781 | 2781 | } |
| @@ -2842,11 +2842,11 @@ | ||
| 2842 | 2842 | nErr++; |
| 2843 | 2843 | } |
| 2844 | 2844 | }else if( report ){ |
| 2845 | 2845 | fprintf(report,"unchanged\n"); |
| 2846 | 2846 | } |
| 2847 | - SafeFree(zOldVersion); | |
| 2847 | + SafeFree(zOldVersion); | |
| 2848 | 2848 | IdentTableReset(&includeTable); |
| 2849 | 2849 | StringReset(&outStr); |
| 2850 | 2850 | return nErr; |
| 2851 | 2851 | } |
| 2852 | 2852 | |
| @@ -2878,11 +2878,11 @@ | ||
| 2878 | 2878 | } |
| 2879 | 2879 | ChangeIfContext(0,&sState); |
| 2880 | 2880 | printf("%s",StringGet(&outStr)); |
| 2881 | 2881 | IdentTableReset(&includeTable); |
| 2882 | 2882 | StringReset(&outStr); |
| 2883 | - return 0; | |
| 2883 | + return 0; | |
| 2884 | 2884 | } |
| 2885 | 2885 | |
| 2886 | 2886 | #ifdef DEBUG |
| 2887 | 2887 | /* |
| 2888 | 2888 | ** Return the number of characters in the given string prior to the |
| @@ -3040,11 +3040,11 @@ | ||
| 3040 | 3040 | int nSrc; |
| 3041 | 3041 | char *zSrc; |
| 3042 | 3042 | InFile *pFile; |
| 3043 | 3043 | int i; |
| 3044 | 3044 | |
| 3045 | - /* | |
| 3045 | + /* | |
| 3046 | 3046 | ** Get the name of the input file to be scanned. The input file is |
| 3047 | 3047 | ** everything before the first ':' or the whole file if no ':' is seen. |
| 3048 | 3048 | ** |
| 3049 | 3049 | ** Except, on windows, ignore any ':' that occurs as the second character |
| 3050 | 3050 | ** since it might be part of the drive specifier. So really, the ":' has |
| @@ -3099,11 +3099,11 @@ | ||
| 3099 | 3099 | } |
| 3100 | 3100 | } |
| 3101 | 3101 | |
| 3102 | 3102 | /* |
| 3103 | 3103 | ** If pFile->zSrc contains no 'c' or 'C' in its extension, it |
| 3104 | - ** must be a header file. In that case, we need to set the | |
| 3104 | + ** must be a header file. In that case, we need to set the | |
| 3105 | 3105 | ** PS_Interface flag. |
| 3106 | 3106 | */ |
| 3107 | 3107 | pFile->flags |= PS_Interface; |
| 3108 | 3108 | for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){ |
| 3109 | 3109 | if( zSrc[i]=='c' || zSrc[i]=='C' ){ |
| @@ -3110,11 +3110,11 @@ | ||
| 3110 | 3110 | pFile->flags &= ~PS_Interface; |
| 3111 | 3111 | break; |
| 3112 | 3112 | } |
| 3113 | 3113 | } |
| 3114 | 3114 | |
| 3115 | - /* Done! | |
| 3115 | + /* Done! | |
| 3116 | 3116 | */ |
| 3117 | 3117 | return pFile; |
| 3118 | 3118 | } |
| 3119 | 3119 | |
| 3120 | 3120 | /* MS-Windows and MS-DOS both have the following serious OS bug: the |
| @@ -3162,11 +3162,11 @@ | ||
| 3162 | 3162 | while( c!=EOF ){ |
| 3163 | 3163 | while( c!=EOF && isspace(c) ){ |
| 3164 | 3164 | if( c=='\n' ){ |
| 3165 | 3165 | startOfLine = 1; |
| 3166 | 3166 | } |
| 3167 | - c = getc(in); | |
| 3167 | + c = getc(in); | |
| 3168 | 3168 | if( startOfLine && c=='#' ){ |
| 3169 | 3169 | while( c!=EOF && c!='\n' ){ |
| 3170 | 3170 | c = getc(in); |
| 3171 | 3171 | } |
| 3172 | 3172 | } |
| @@ -3184,11 +3184,11 @@ | ||
| 3184 | 3184 | if( nAlloc==0 ){ |
| 3185 | 3185 | nAlloc = 100 + argc; |
| 3186 | 3186 | zNew = malloc( sizeof(char*) * nAlloc ); |
| 3187 | 3187 | }else{ |
| 3188 | 3188 | nAlloc *= 2; |
| 3189 | - zNew = realloc( zNew, sizeof(char*) * nAlloc ); | |
| 3189 | + zNew = realloc( zNew, sizeof(char*) * nAlloc ); | |
| 3190 | 3190 | } |
| 3191 | 3191 | } |
| 3192 | 3192 | if( zNew ){ |
| 3193 | 3193 | int j = nNew + index; |
| 3194 | 3194 | zNew[j] = malloc( n + 1 ); |
| @@ -3254,11 +3254,11 @@ | ||
| 3254 | 3254 | |
| 3255 | 3255 | /* |
| 3256 | 3256 | ** The following text contains a few simple #defines that we want |
| 3257 | 3257 | ** to be available to every file. |
| 3258 | 3258 | */ |
| 3259 | -static char zInit[] = | |
| 3259 | +static const char zInit[] = | |
| 3260 | 3260 | "#define INTERFACE 0\n" |
| 3261 | 3261 | "#define EXPORT_INTERFACE 0\n" |
| 3262 | 3262 | "#define LOCAL_INTERFACE 0\n" |
| 3263 | 3263 | "#define EXPORT\n" |
| 3264 | 3264 | "#define LOCAL static\n" |
| 3265 | 3265 |
| --- src/makeheaders.c | |
| +++ src/makeheaders.c | |
| @@ -112,19 +112,19 @@ | |
| 112 | ** |
| 113 | ** struct Xyzzy; |
| 114 | ** |
| 115 | ** Not every object has a forward declaration. If it does, thought, the |
| 116 | ** forward declaration will be contained in the zFwd field for C and |
| 117 | ** the zFwdCpp for C++. The zDecl field contains the complete |
| 118 | ** declaration text. |
| 119 | */ |
| 120 | typedef struct Decl Decl; |
| 121 | struct Decl { |
| 122 | char *zName; /* Name of the object being declared. The appearance |
| 123 | ** of this name is a source file triggers the declaration |
| 124 | ** to be added to the header for that file. */ |
| 125 | char *zFile; /* File from which extracted. */ |
| 126 | char *zIf; /* Surround the declaration with this #if */ |
| 127 | char *zFwd; /* A forward declaration. NULL if there is none. */ |
| 128 | char *zFwdCpp; /* Use this forward declaration for C++. */ |
| 129 | char *zDecl; /* A full declaration of this object */ |
| 130 | char *zExtra; /* Extra declaration text inserted into class objects */ |
| @@ -163,11 +163,11 @@ | |
| 163 | ** in the output when using the -H option.) |
| 164 | ** |
| 165 | ** EXPORT scope The object is visible and usable everywhere. |
| 166 | ** |
| 167 | ** The DP_Flag is a temporary use flag that is used during processing to |
| 168 | ** prevent an infinite loop. It's use is localized. |
| 169 | ** |
| 170 | ** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent |
| 171 | ** and are used to specify what type of declaration the object requires. |
| 172 | */ |
| 173 | #define DP_Forward 0x001 /* Has a forward declaration in this file */ |
| @@ -201,11 +201,11 @@ | |
| 201 | ** Be careful not to confuse PS_Export with DP_Export or |
| 202 | ** PS_Local with DP_Local. Their names are similar, but the meanings |
| 203 | ** of these flags are very different. |
| 204 | */ |
| 205 | #define PS_Extern 0x000800 /* "extern" has been seen */ |
| 206 | #define PS_Export 0x001000 /* If between "#if EXPORT_INTERFACE" |
| 207 | ** and "#endif" */ |
| 208 | #define PS_Export2 0x002000 /* If "EXPORT" seen */ |
| 209 | #define PS_Typedef 0x004000 /* If "typedef" has been seen */ |
| 210 | #define PS_Static 0x008000 /* If "static" has been seen */ |
| 211 | #define PS_Interface 0x010000 /* If within #if INTERFACE..#endif */ |
| @@ -231,11 +231,11 @@ | |
| 231 | #define TY_Union 0x04000000 |
| 232 | #define TY_Enumeration 0x08000000 |
| 233 | #define TY_Defunct 0x10000000 /* Used to erase a declaration */ |
| 234 | |
| 235 | /* |
| 236 | ** Each nested #if (or #ifdef or #ifndef) is stored in a stack of |
| 237 | ** instances of the following structure. |
| 238 | */ |
| 239 | typedef struct Ifmacro Ifmacro; |
| 240 | struct Ifmacro { |
| 241 | int nLine; /* Line number where this macro occurs */ |
| @@ -293,11 +293,11 @@ | |
| 293 | int flags; /* One or more DP_, PS_ and/or TY_ flags */ |
| 294 | InFile *pNext; /* Next input file in the list of them all */ |
| 295 | IdentTable idTable; /* All identifiers in this input file */ |
| 296 | }; |
| 297 | |
| 298 | /* |
| 299 | ** An unbounded string is able to grow without limit. We use these |
| 300 | ** to construct large in-memory strings from lots of smaller components. |
| 301 | */ |
| 302 | typedef struct String String; |
| 303 | struct String { |
| @@ -332,19 +332,19 @@ | |
| 332 | ** never to read a file that it generated itself. |
| 333 | ** |
| 334 | ** The "#undef INTERFACE" part is a hack to work around a name collision |
| 335 | ** in MSVC 2008. |
| 336 | */ |
| 337 | const char zTopLine[] = |
| 338 | "/* \aThis file was automatically generated. Do not edit! */\n" |
| 339 | "#undef INTERFACE\n"; |
| 340 | #define nTopLine (sizeof(zTopLine)-1) |
| 341 | |
| 342 | /* |
| 343 | ** The name of the file currently being parsed. |
| 344 | */ |
| 345 | static char *zFilename; |
| 346 | |
| 347 | /* |
| 348 | ** The stack of #if macros for the file currently being parsed. |
| 349 | */ |
| 350 | static Ifmacro *ifStack = 0; |
| @@ -702,11 +702,11 @@ | |
| 702 | struct stat sStat; |
| 703 | FILE *pIn; |
| 704 | char *zBuf; |
| 705 | int n; |
| 706 | |
| 707 | if( stat(zFilename,&sStat)!=0 |
| 708 | #ifndef WIN32 |
| 709 | || !S_ISREG(sStat.st_mode) |
| 710 | #endif |
| 711 | ){ |
| 712 | return 0; |
| @@ -889,12 +889,12 @@ | |
| 889 | } |
| 890 | } |
| 891 | } |
| 892 | i++; |
| 893 | } |
| 894 | if( z[i] ){ |
| 895 | i += 2; |
| 896 | }else{ |
| 897 | isBlockComment = 0; |
| 898 | fprintf(stderr,"%s:%d: Unterminated comment\n", |
| 899 | zFilename, startLine); |
| 900 | nErr++; |
| @@ -906,11 +906,11 @@ | |
| 906 | pToken->eType = TT_Other; |
| 907 | pToken->nText = 1 + (z[i+1]=='+'); |
| 908 | } |
| 909 | break; |
| 910 | |
| 911 | case '0': |
| 912 | if( z[i+1]=='x' || z[i+1]=='X' ){ |
| 913 | /* A hex constant */ |
| 914 | i += 2; |
| 915 | while( isxdigit(z[i]) ){ i++; } |
| 916 | }else{ |
| @@ -963,11 +963,11 @@ | |
| 963 | while( isalnum(z[i]) || z[i]=='_' ){ i++; }; |
| 964 | pToken->eType = TT_Id; |
| 965 | pToken->nText = i - pIn->i; |
| 966 | break; |
| 967 | |
| 968 | case ':': |
| 969 | pToken->eType = TT_Other; |
| 970 | pToken->nText = 1 + (z[i+1]==':'); |
| 971 | break; |
| 972 | |
| 973 | case '=': |
| @@ -977,11 +977,11 @@ | |
| 977 | case '-': |
| 978 | case '*': |
| 979 | case '%': |
| 980 | case '^': |
| 981 | case '&': |
| 982 | case '|': |
| 983 | pToken->eType = TT_Other; |
| 984 | pToken->nText = 1 + (z[i+1]=='='); |
| 985 | break; |
| 986 | |
| 987 | default: |
| @@ -1064,11 +1064,11 @@ | |
| 1064 | } |
| 1065 | } |
| 1066 | /* NOT REACHED */ |
| 1067 | } |
| 1068 | |
| 1069 | /* |
| 1070 | ** This routine looks for identifiers (strings of contiguous alphanumeric |
| 1071 | ** characters) within a preprocessor directive and adds every such string |
| 1072 | ** found to the given identifier table |
| 1073 | */ |
| 1074 | static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){ |
| @@ -1157,11 +1157,11 @@ | |
| 1157 | case TT_Id: |
| 1158 | if( pTable ){ |
| 1159 | IdentTableInsert(pTable,pToken->zText,pToken->nText); |
| 1160 | } |
| 1161 | break; |
| 1162 | |
| 1163 | case TT_Preprocessor: |
| 1164 | if( pTable!=0 ){ |
| 1165 | FindIdentifiersInMacro(pToken,pTable); |
| 1166 | } |
| 1167 | break; |
| @@ -1263,11 +1263,11 @@ | |
| 1263 | exit(1); |
| 1264 | } |
| 1265 | pList = TokenizeFile(zFile,&sTable); |
| 1266 | for(p=pList; p; p=p->pNext){ |
| 1267 | int j; |
| 1268 | switch( p->eType ){ |
| 1269 | case TT_Space: |
| 1270 | printf("%4d: Space\n",p->nLine); |
| 1271 | break; |
| 1272 | case TT_Id: |
| 1273 | printf("%4d: Id %.*s\n",p->nLine,p->nText,p->zText); |
| @@ -1330,11 +1330,11 @@ | |
| 1330 | needSpace = 1; |
| 1331 | break; |
| 1332 | |
| 1333 | default: |
| 1334 | c = pFirst->zText[0]; |
| 1335 | printf("%s%.*s", |
| 1336 | (needSpace && (c=='*' || c=='{')) ? " " : "", |
| 1337 | pFirst->nText, pFirst->zText); |
| 1338 | needSpace = pFirst->zText[0]==','; |
| 1339 | break; |
| 1340 | } |
| @@ -1371,13 +1371,13 @@ | |
| 1371 | |
| 1372 | StringInit(&str); |
| 1373 | pLast = pLast->pNext; |
| 1374 | while( pFirst!=pLast ){ |
| 1375 | if( pFirst==pSkip ){ iSkip = nSkip; } |
| 1376 | if( iSkip>0 ){ |
| 1377 | iSkip--; |
| 1378 | pFirst=pFirst->pNext; |
| 1379 | continue; |
| 1380 | } |
| 1381 | switch( pFirst->eType ){ |
| 1382 | case TT_Preprocessor: |
| 1383 | StringAppend(&str,"\n",1); |
| @@ -1384,13 +1384,13 @@ | |
| 1384 | StringAppend(&str,pFirst->zText,pFirst->nText); |
| 1385 | StringAppend(&str,"\n",1); |
| 1386 | needSpace = 0; |
| 1387 | break; |
| 1388 | |
| 1389 | case TT_Id: |
| 1390 | switch( pFirst->zText[0] ){ |
| 1391 | case 'E': |
| 1392 | if( pFirst->nText==6 && strncmp(pFirst->zText,"EXPORT",6)==0 ){ |
| 1393 | skipOne = 1; |
| 1394 | } |
| 1395 | break; |
| 1396 | case 'P': |
| @@ -1645,17 +1645,17 @@ | |
| 1645 | pLast = pLast->pNext; |
| 1646 | for(p=pFirst; p && p!=pLast; p=p->pNext){ |
| 1647 | if( p->eType==TT_Id ){ |
| 1648 | static IdentTable sReserved; |
| 1649 | static int isInit = 0; |
| 1650 | static char *aWords[] = { "char", "class", |
| 1651 | "const", "double", "enum", "extern", "EXPORT", "ET_PROC", |
| 1652 | "float", "int", "long", |
| 1653 | "PRIVATE", "PROTECTED", "PUBLIC", |
| 1654 | "register", "static", "struct", "sizeof", "signed", "typedef", |
| 1655 | "union", "volatile", "virtual", "void", }; |
| 1656 | |
| 1657 | if( !isInit ){ |
| 1658 | int i; |
| 1659 | for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){ |
| 1660 | IdentTableInsert(&sReserved,aWords[i],0); |
| 1661 | } |
| @@ -1768,11 +1768,11 @@ | |
| 1768 | pCode = pLast; |
| 1769 | while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){ |
| 1770 | pLast = pLast->pPrev; |
| 1771 | } |
| 1772 | if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){ |
| 1773 | fprintf(stderr,"%s:%d: Unrecognized syntax.\n", |
| 1774 | zFilename, pFirst->nLine); |
| 1775 | return 1; |
| 1776 | } |
| 1777 | if( flags & (PS_Interface|PS_Export|PS_Local) ){ |
| 1778 | fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n", |
| @@ -1849,11 +1849,11 @@ | |
| 1849 | return 1; |
| 1850 | } |
| 1851 | |
| 1852 | #ifdef DEBUG |
| 1853 | if( debugMask & PARSER ){ |
| 1854 | printf("**** Found inline routine: %.*s on line %d...\n", |
| 1855 | pName->nText, pName->zText, pFirst->nLine); |
| 1856 | PrintTokens(pFirst,pEnd); |
| 1857 | printf("\n"); |
| 1858 | } |
| 1859 | #endif |
| @@ -1888,11 +1888,11 @@ | |
| 1888 | ** to search for an occurrence of an ID followed immediately by '('. |
| 1889 | ** If found, we have a prototype. Otherwise we are dealing with a |
| 1890 | ** variable definition. |
| 1891 | */ |
| 1892 | static int isVariableDef(Token *pFirst, Token *pEnd){ |
| 1893 | if( pEnd && pEnd->zText[0]=='=' && |
| 1894 | (pEnd->pPrev->nText!=8 || strncmp(pEnd->pPrev->zText,"operator",8)!=0) |
| 1895 | ){ |
| 1896 | return 1; |
| 1897 | } |
| 1898 | while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){ |
| @@ -1949,11 +1949,11 @@ | |
| 1949 | } |
| 1950 | while( pFirst!=0 && pFirst->pNext!=pEnd && |
| 1951 | ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0) |
| 1952 | || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0)) |
| 1953 | ){ |
| 1954 | /* Lose the initial "static" or local from local variables. |
| 1955 | ** We'll prepend "extern" later. */ |
| 1956 | pFirst = pFirst->pNext; |
| 1957 | isLocal = 1; |
| 1958 | } |
| 1959 | if( pFirst==0 || !isLocal ){ |
| @@ -1962,11 +1962,11 @@ | |
| 1962 | }else if( flags & PS_Method ){ |
| 1963 | /* Methods are declared by their class. Don't declare separately. */ |
| 1964 | return nErr; |
| 1965 | } |
| 1966 | isVar = (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd); |
| 1967 | if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0 |
| 1968 | && (flags & PS_Extern)==0 ){ |
| 1969 | fprintf(stderr,"%s:%d: Can't define a variable in this context\n", |
| 1970 | zFilename, pFirst->nLine); |
| 1971 | nErr++; |
| 1972 | } |
| @@ -2095,11 +2095,11 @@ | |
| 2095 | nCmd++; |
| 2096 | } |
| 2097 | |
| 2098 | if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){ |
| 2099 | /* |
| 2100 | ** Pop the if stack |
| 2101 | */ |
| 2102 | pIf = ifStack; |
| 2103 | if( pIf==0 ){ |
| 2104 | fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine); |
| 2105 | return 1; |
| @@ -2106,11 +2106,11 @@ | |
| 2106 | } |
| 2107 | ifStack = pIf->pNext; |
| 2108 | SafeFree(pIf); |
| 2109 | }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){ |
| 2110 | /* |
| 2111 | ** Record a #define if we are in PS_Interface or PS_Export |
| 2112 | */ |
| 2113 | Decl *pDecl; |
| 2114 | if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; } |
| 2115 | zArg = &zCmd[6]; |
| 2116 | while( *zArg && isspace(*zArg) && *zArg!='\n' ){ |
| @@ -2129,11 +2129,11 @@ | |
| 2129 | }else if( flags & PS_Local ){ |
| 2130 | DeclSetProperty(pDecl,DP_Local); |
| 2131 | } |
| 2132 | }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){ |
| 2133 | /* |
| 2134 | ** Record an #include if we are in PS_Interface or PS_Export |
| 2135 | */ |
| 2136 | Include *pInclude; |
| 2137 | char *zIf; |
| 2138 | |
| 2139 | if( !(flags & (PS_Interface|PS_Export)) ){ return 0; } |
| @@ -2184,11 +2184,11 @@ | |
| 2184 | PushIfMacro(0,0,0,pToken->nLine,PS_Local); |
| 2185 | }else{ |
| 2186 | PushIfMacro(0,zArg,nArg,pToken->nLine,0); |
| 2187 | } |
| 2188 | }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){ |
| 2189 | /* |
| 2190 | ** Push an #ifdef. |
| 2191 | */ |
| 2192 | zArg = &zCmd[5]; |
| 2193 | while( *zArg && isspace(*zArg) && *zArg!='\n' ){ |
| 2194 | zArg++; |
| @@ -2207,11 +2207,11 @@ | |
| 2207 | if( *zArg==0 || *zArg=='\n' ){ return 0; } |
| 2208 | nArg = pToken->nText + (int)(pToken->zText - zArg); |
| 2209 | PushIfMacro("!defined",zArg,nArg,pToken->nLine,0); |
| 2210 | }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){ |
| 2211 | /* |
| 2212 | ** Invert the #if on the top of the stack |
| 2213 | */ |
| 2214 | if( ifStack==0 ){ |
| 2215 | fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename, |
| 2216 | pToken->nLine); |
| 2217 | return 1; |
| @@ -2224,33 +2224,33 @@ | |
| 2224 | }else{ |
| 2225 | pIf->flags = 0; |
| 2226 | } |
| 2227 | }else{ |
| 2228 | /* |
| 2229 | ** This directive can be safely ignored |
| 2230 | */ |
| 2231 | return 0; |
| 2232 | } |
| 2233 | |
| 2234 | /* |
| 2235 | ** Recompute the preset flags |
| 2236 | */ |
| 2237 | *pPresetFlags = 0; |
| 2238 | for(pIf = ifStack; pIf; pIf=pIf->pNext){ |
| 2239 | *pPresetFlags |= pIf->flags; |
| 2240 | } |
| 2241 | |
| 2242 | return nErr; |
| 2243 | } |
| 2244 | |
| 2245 | /* |
| 2246 | ** Parse an entire file. Return the number of errors. |
| 2247 | ** |
| 2248 | ** pList is a list of tokens in the file. Whitespace tokens have been |
| 2249 | ** eliminated, and text with {...} has been collapsed into a |
| 2250 | ** single TT_Brace token. |
| 2251 | ** |
| 2252 | ** initFlags are a set of parse flags that should always be set for this |
| 2253 | ** file. For .c files this is normally 0. For .h files it is PS_Interface. |
| 2254 | */ |
| 2255 | static int ParseFile(Token *pList, int initFlags){ |
| 2256 | int nErr = 0; |
| @@ -2279,11 +2279,11 @@ | |
| 2279 | pStart = 0; |
| 2280 | flags = presetFlags; |
| 2281 | break; |
| 2282 | |
| 2283 | case '=': |
| 2284 | if( pList->pPrev->nText==8 |
| 2285 | && strncmp(pList->pPrev->zText,"operator",8)==0 ){ |
| 2286 | break; |
| 2287 | } |
| 2288 | nErr += ProcessDecl(pStart,pList,flags); |
| 2289 | pStart = 0; |
| @@ -2471,11 +2471,11 @@ | |
| 2471 | pDecl->zExtra = 0; |
| 2472 | } |
| 2473 | |
| 2474 | /* |
| 2475 | ** Reset the DP_Forward and DP_Declared flags on all Decl structures. |
| 2476 | ** Set both flags for anything that is tagged as local and isn't |
| 2477 | ** in the file zFilename so that it won't be printing in other files. |
| 2478 | */ |
| 2479 | static void ResetDeclFlags(char *zFilename){ |
| 2480 | Decl *pDecl; |
| 2481 | |
| @@ -2574,11 +2574,11 @@ | |
| 2574 | int flag; |
| 2575 | int isCpp; /* True if generating C++ */ |
| 2576 | int doneTypedef = 0; /* True if a typedef has been done for this object */ |
| 2577 | |
| 2578 | /* printf("BEGIN %s of %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/ |
| 2579 | /* |
| 2580 | ** For any object that has a forward declaration, go ahead and do the |
| 2581 | ** forward declaration first. |
| 2582 | */ |
| 2583 | isCpp = (pState->flags & DP_Cplusplus) != 0; |
| 2584 | for(p=pDecl; p; p=p->pSameName){ |
| @@ -2626,12 +2626,12 @@ | |
| 2626 | ** function on a recursive call with the same pDecl. Hence, recursive |
| 2627 | ** calls to this function (through ScanText()) can never change the |
| 2628 | ** value of DP_Flag out from under us. |
| 2629 | */ |
| 2630 | for(p=pDecl; p; p=p->pSameName){ |
| 2631 | if( !DeclHasProperty(p,DP_Declared) |
| 2632 | && (p->zFwd==0 || needFullDecl) |
| 2633 | && p->zDecl!=0 |
| 2634 | ){ |
| 2635 | DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag); |
| 2636 | }else{ |
| 2637 | DeclClearProperty(p,DP_Flag); |
| @@ -2735,12 +2735,12 @@ | |
| 2735 | ** by sToken. |
| 2736 | */ |
| 2737 | pDecl = FindDecl(sToken.zText,sToken.nText); |
| 2738 | if( pDecl==0 ) continue; |
| 2739 | |
| 2740 | /* |
| 2741 | ** If we get this far, we've found an identifier that has a |
| 2742 | ** declaration in the database. Now see if we the full declaration |
| 2743 | ** or just a forward declaration. |
| 2744 | */ |
| 2745 | GetNonspaceToken(&sIn,&sNext); |
| 2746 | if( sNext.zText[0]=='*' ){ |
| @@ -2770,12 +2770,12 @@ | |
| 2770 | int progress; |
| 2771 | |
| 2772 | do{ |
| 2773 | progress = 0; |
| 2774 | for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){ |
| 2775 | if( DeclHasProperty(pDecl,DP_Forward) |
| 2776 | && !DeclHasProperty(pDecl,DP_Declared) |
| 2777 | ){ |
| 2778 | DeclareObject(pDecl,pState,1); |
| 2779 | progress = 1; |
| 2780 | assert( DeclHasProperty(pDecl,DP_Declared) ); |
| 2781 | } |
| @@ -2842,11 +2842,11 @@ | |
| 2842 | nErr++; |
| 2843 | } |
| 2844 | }else if( report ){ |
| 2845 | fprintf(report,"unchanged\n"); |
| 2846 | } |
| 2847 | SafeFree(zOldVersion); |
| 2848 | IdentTableReset(&includeTable); |
| 2849 | StringReset(&outStr); |
| 2850 | return nErr; |
| 2851 | } |
| 2852 | |
| @@ -2878,11 +2878,11 @@ | |
| 2878 | } |
| 2879 | ChangeIfContext(0,&sState); |
| 2880 | printf("%s",StringGet(&outStr)); |
| 2881 | IdentTableReset(&includeTable); |
| 2882 | StringReset(&outStr); |
| 2883 | return 0; |
| 2884 | } |
| 2885 | |
| 2886 | #ifdef DEBUG |
| 2887 | /* |
| 2888 | ** Return the number of characters in the given string prior to the |
| @@ -3040,11 +3040,11 @@ | |
| 3040 | int nSrc; |
| 3041 | char *zSrc; |
| 3042 | InFile *pFile; |
| 3043 | int i; |
| 3044 | |
| 3045 | /* |
| 3046 | ** Get the name of the input file to be scanned. The input file is |
| 3047 | ** everything before the first ':' or the whole file if no ':' is seen. |
| 3048 | ** |
| 3049 | ** Except, on windows, ignore any ':' that occurs as the second character |
| 3050 | ** since it might be part of the drive specifier. So really, the ":' has |
| @@ -3099,11 +3099,11 @@ | |
| 3099 | } |
| 3100 | } |
| 3101 | |
| 3102 | /* |
| 3103 | ** If pFile->zSrc contains no 'c' or 'C' in its extension, it |
| 3104 | ** must be a header file. In that case, we need to set the |
| 3105 | ** PS_Interface flag. |
| 3106 | */ |
| 3107 | pFile->flags |= PS_Interface; |
| 3108 | for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){ |
| 3109 | if( zSrc[i]=='c' || zSrc[i]=='C' ){ |
| @@ -3110,11 +3110,11 @@ | |
| 3110 | pFile->flags &= ~PS_Interface; |
| 3111 | break; |
| 3112 | } |
| 3113 | } |
| 3114 | |
| 3115 | /* Done! |
| 3116 | */ |
| 3117 | return pFile; |
| 3118 | } |
| 3119 | |
| 3120 | /* MS-Windows and MS-DOS both have the following serious OS bug: the |
| @@ -3162,11 +3162,11 @@ | |
| 3162 | while( c!=EOF ){ |
| 3163 | while( c!=EOF && isspace(c) ){ |
| 3164 | if( c=='\n' ){ |
| 3165 | startOfLine = 1; |
| 3166 | } |
| 3167 | c = getc(in); |
| 3168 | if( startOfLine && c=='#' ){ |
| 3169 | while( c!=EOF && c!='\n' ){ |
| 3170 | c = getc(in); |
| 3171 | } |
| 3172 | } |
| @@ -3184,11 +3184,11 @@ | |
| 3184 | if( nAlloc==0 ){ |
| 3185 | nAlloc = 100 + argc; |
| 3186 | zNew = malloc( sizeof(char*) * nAlloc ); |
| 3187 | }else{ |
| 3188 | nAlloc *= 2; |
| 3189 | zNew = realloc( zNew, sizeof(char*) * nAlloc ); |
| 3190 | } |
| 3191 | } |
| 3192 | if( zNew ){ |
| 3193 | int j = nNew + index; |
| 3194 | zNew[j] = malloc( n + 1 ); |
| @@ -3254,11 +3254,11 @@ | |
| 3254 | |
| 3255 | /* |
| 3256 | ** The following text contains a few simple #defines that we want |
| 3257 | ** to be available to every file. |
| 3258 | */ |
| 3259 | static char zInit[] = |
| 3260 | "#define INTERFACE 0\n" |
| 3261 | "#define EXPORT_INTERFACE 0\n" |
| 3262 | "#define LOCAL_INTERFACE 0\n" |
| 3263 | "#define EXPORT\n" |
| 3264 | "#define LOCAL static\n" |
| 3265 |
| --- src/makeheaders.c | |
| +++ src/makeheaders.c | |
| @@ -112,19 +112,19 @@ | |
| 112 | ** |
| 113 | ** struct Xyzzy; |
| 114 | ** |
| 115 | ** Not every object has a forward declaration. If it does, thought, the |
| 116 | ** forward declaration will be contained in the zFwd field for C and |
| 117 | ** the zFwdCpp for C++. The zDecl field contains the complete |
| 118 | ** declaration text. |
| 119 | */ |
| 120 | typedef struct Decl Decl; |
| 121 | struct Decl { |
| 122 | char *zName; /* Name of the object being declared. The appearance |
| 123 | ** of this name is a source file triggers the declaration |
| 124 | ** to be added to the header for that file. */ |
| 125 | const char *zFile; /* File from which extracted. */ |
| 126 | char *zIf; /* Surround the declaration with this #if */ |
| 127 | char *zFwd; /* A forward declaration. NULL if there is none. */ |
| 128 | char *zFwdCpp; /* Use this forward declaration for C++. */ |
| 129 | char *zDecl; /* A full declaration of this object */ |
| 130 | char *zExtra; /* Extra declaration text inserted into class objects */ |
| @@ -163,11 +163,11 @@ | |
| 163 | ** in the output when using the -H option.) |
| 164 | ** |
| 165 | ** EXPORT scope The object is visible and usable everywhere. |
| 166 | ** |
| 167 | ** The DP_Flag is a temporary use flag that is used during processing to |
| 168 | ** prevent an infinite loop. It's use is localized. |
| 169 | ** |
| 170 | ** The DP_Cplusplus, DP_ExternCReqd and DP_ExternReqd flags are permanent |
| 171 | ** and are used to specify what type of declaration the object requires. |
| 172 | */ |
| 173 | #define DP_Forward 0x001 /* Has a forward declaration in this file */ |
| @@ -201,11 +201,11 @@ | |
| 201 | ** Be careful not to confuse PS_Export with DP_Export or |
| 202 | ** PS_Local with DP_Local. Their names are similar, but the meanings |
| 203 | ** of these flags are very different. |
| 204 | */ |
| 205 | #define PS_Extern 0x000800 /* "extern" has been seen */ |
| 206 | #define PS_Export 0x001000 /* If between "#if EXPORT_INTERFACE" |
| 207 | ** and "#endif" */ |
| 208 | #define PS_Export2 0x002000 /* If "EXPORT" seen */ |
| 209 | #define PS_Typedef 0x004000 /* If "typedef" has been seen */ |
| 210 | #define PS_Static 0x008000 /* If "static" has been seen */ |
| 211 | #define PS_Interface 0x010000 /* If within #if INTERFACE..#endif */ |
| @@ -231,11 +231,11 @@ | |
| 231 | #define TY_Union 0x04000000 |
| 232 | #define TY_Enumeration 0x08000000 |
| 233 | #define TY_Defunct 0x10000000 /* Used to erase a declaration */ |
| 234 | |
| 235 | /* |
| 236 | ** Each nested #if (or #ifdef or #ifndef) is stored in a stack of |
| 237 | ** instances of the following structure. |
| 238 | */ |
| 239 | typedef struct Ifmacro Ifmacro; |
| 240 | struct Ifmacro { |
| 241 | int nLine; /* Line number where this macro occurs */ |
| @@ -293,11 +293,11 @@ | |
| 293 | int flags; /* One or more DP_, PS_ and/or TY_ flags */ |
| 294 | InFile *pNext; /* Next input file in the list of them all */ |
| 295 | IdentTable idTable; /* All identifiers in this input file */ |
| 296 | }; |
| 297 | |
| 298 | /* |
| 299 | ** An unbounded string is able to grow without limit. We use these |
| 300 | ** to construct large in-memory strings from lots of smaller components. |
| 301 | */ |
| 302 | typedef struct String String; |
| 303 | struct String { |
| @@ -332,19 +332,19 @@ | |
| 332 | ** never to read a file that it generated itself. |
| 333 | ** |
| 334 | ** The "#undef INTERFACE" part is a hack to work around a name collision |
| 335 | ** in MSVC 2008. |
| 336 | */ |
| 337 | const char zTopLine[] = |
| 338 | "/* \aThis file was automatically generated. Do not edit! */\n" |
| 339 | "#undef INTERFACE\n"; |
| 340 | #define nTopLine (sizeof(zTopLine)-1) |
| 341 | |
| 342 | /* |
| 343 | ** The name of the file currently being parsed. |
| 344 | */ |
| 345 | static const char *zFilename; |
| 346 | |
| 347 | /* |
| 348 | ** The stack of #if macros for the file currently being parsed. |
| 349 | */ |
| 350 | static Ifmacro *ifStack = 0; |
| @@ -702,11 +702,11 @@ | |
| 702 | struct stat sStat; |
| 703 | FILE *pIn; |
| 704 | char *zBuf; |
| 705 | int n; |
| 706 | |
| 707 | if( stat(zFilename,&sStat)!=0 |
| 708 | #ifndef WIN32 |
| 709 | || !S_ISREG(sStat.st_mode) |
| 710 | #endif |
| 711 | ){ |
| 712 | return 0; |
| @@ -889,12 +889,12 @@ | |
| 889 | } |
| 890 | } |
| 891 | } |
| 892 | i++; |
| 893 | } |
| 894 | if( z[i] ){ |
| 895 | i += 2; |
| 896 | }else{ |
| 897 | isBlockComment = 0; |
| 898 | fprintf(stderr,"%s:%d: Unterminated comment\n", |
| 899 | zFilename, startLine); |
| 900 | nErr++; |
| @@ -906,11 +906,11 @@ | |
| 906 | pToken->eType = TT_Other; |
| 907 | pToken->nText = 1 + (z[i+1]=='+'); |
| 908 | } |
| 909 | break; |
| 910 | |
| 911 | case '0': |
| 912 | if( z[i+1]=='x' || z[i+1]=='X' ){ |
| 913 | /* A hex constant */ |
| 914 | i += 2; |
| 915 | while( isxdigit(z[i]) ){ i++; } |
| 916 | }else{ |
| @@ -963,11 +963,11 @@ | |
| 963 | while( isalnum(z[i]) || z[i]=='_' ){ i++; }; |
| 964 | pToken->eType = TT_Id; |
| 965 | pToken->nText = i - pIn->i; |
| 966 | break; |
| 967 | |
| 968 | case ':': |
| 969 | pToken->eType = TT_Other; |
| 970 | pToken->nText = 1 + (z[i+1]==':'); |
| 971 | break; |
| 972 | |
| 973 | case '=': |
| @@ -977,11 +977,11 @@ | |
| 977 | case '-': |
| 978 | case '*': |
| 979 | case '%': |
| 980 | case '^': |
| 981 | case '&': |
| 982 | case '|': |
| 983 | pToken->eType = TT_Other; |
| 984 | pToken->nText = 1 + (z[i+1]=='='); |
| 985 | break; |
| 986 | |
| 987 | default: |
| @@ -1064,11 +1064,11 @@ | |
| 1064 | } |
| 1065 | } |
| 1066 | /* NOT REACHED */ |
| 1067 | } |
| 1068 | |
| 1069 | /* |
| 1070 | ** This routine looks for identifiers (strings of contiguous alphanumeric |
| 1071 | ** characters) within a preprocessor directive and adds every such string |
| 1072 | ** found to the given identifier table |
| 1073 | */ |
| 1074 | static void FindIdentifiersInMacro(Token *pToken, IdentTable *pTable){ |
| @@ -1157,11 +1157,11 @@ | |
| 1157 | case TT_Id: |
| 1158 | if( pTable ){ |
| 1159 | IdentTableInsert(pTable,pToken->zText,pToken->nText); |
| 1160 | } |
| 1161 | break; |
| 1162 | |
| 1163 | case TT_Preprocessor: |
| 1164 | if( pTable!=0 ){ |
| 1165 | FindIdentifiersInMacro(pToken,pTable); |
| 1166 | } |
| 1167 | break; |
| @@ -1263,11 +1263,11 @@ | |
| 1263 | exit(1); |
| 1264 | } |
| 1265 | pList = TokenizeFile(zFile,&sTable); |
| 1266 | for(p=pList; p; p=p->pNext){ |
| 1267 | int j; |
| 1268 | switch( p->eType ){ |
| 1269 | case TT_Space: |
| 1270 | printf("%4d: Space\n",p->nLine); |
| 1271 | break; |
| 1272 | case TT_Id: |
| 1273 | printf("%4d: Id %.*s\n",p->nLine,p->nText,p->zText); |
| @@ -1330,11 +1330,11 @@ | |
| 1330 | needSpace = 1; |
| 1331 | break; |
| 1332 | |
| 1333 | default: |
| 1334 | c = pFirst->zText[0]; |
| 1335 | printf("%s%.*s", |
| 1336 | (needSpace && (c=='*' || c=='{')) ? " " : "", |
| 1337 | pFirst->nText, pFirst->zText); |
| 1338 | needSpace = pFirst->zText[0]==','; |
| 1339 | break; |
| 1340 | } |
| @@ -1371,13 +1371,13 @@ | |
| 1371 | |
| 1372 | StringInit(&str); |
| 1373 | pLast = pLast->pNext; |
| 1374 | while( pFirst!=pLast ){ |
| 1375 | if( pFirst==pSkip ){ iSkip = nSkip; } |
| 1376 | if( iSkip>0 ){ |
| 1377 | iSkip--; |
| 1378 | pFirst=pFirst->pNext; |
| 1379 | continue; |
| 1380 | } |
| 1381 | switch( pFirst->eType ){ |
| 1382 | case TT_Preprocessor: |
| 1383 | StringAppend(&str,"\n",1); |
| @@ -1384,13 +1384,13 @@ | |
| 1384 | StringAppend(&str,pFirst->zText,pFirst->nText); |
| 1385 | StringAppend(&str,"\n",1); |
| 1386 | needSpace = 0; |
| 1387 | break; |
| 1388 | |
| 1389 | case TT_Id: |
| 1390 | switch( pFirst->zText[0] ){ |
| 1391 | case 'E': |
| 1392 | if( pFirst->nText==6 && strncmp(pFirst->zText,"EXPORT",6)==0 ){ |
| 1393 | skipOne = 1; |
| 1394 | } |
| 1395 | break; |
| 1396 | case 'P': |
| @@ -1645,17 +1645,17 @@ | |
| 1645 | pLast = pLast->pNext; |
| 1646 | for(p=pFirst; p && p!=pLast; p=p->pNext){ |
| 1647 | if( p->eType==TT_Id ){ |
| 1648 | static IdentTable sReserved; |
| 1649 | static int isInit = 0; |
| 1650 | static const char *aWords[] = { "char", "class", |
| 1651 | "const", "double", "enum", "extern", "EXPORT", "ET_PROC", |
| 1652 | "float", "int", "long", |
| 1653 | "PRIVATE", "PROTECTED", "PUBLIC", |
| 1654 | "register", "static", "struct", "sizeof", "signed", "typedef", |
| 1655 | "union", "volatile", "virtual", "void", }; |
| 1656 | |
| 1657 | if( !isInit ){ |
| 1658 | int i; |
| 1659 | for(i=0; i<sizeof(aWords)/sizeof(aWords[0]); i++){ |
| 1660 | IdentTableInsert(&sReserved,aWords[i],0); |
| 1661 | } |
| @@ -1768,11 +1768,11 @@ | |
| 1768 | pCode = pLast; |
| 1769 | while( pLast && pLast!=pFirst && pLast->zText[0]!=')' ){ |
| 1770 | pLast = pLast->pPrev; |
| 1771 | } |
| 1772 | if( pLast==0 || pLast==pFirst || pFirst->pNext==pLast ){ |
| 1773 | fprintf(stderr,"%s:%d: Unrecognized syntax.\n", |
| 1774 | zFilename, pFirst->nLine); |
| 1775 | return 1; |
| 1776 | } |
| 1777 | if( flags & (PS_Interface|PS_Export|PS_Local) ){ |
| 1778 | fprintf(stderr,"%s:%d: Missing \"inline\" on function or procedure.\n", |
| @@ -1849,11 +1849,11 @@ | |
| 1849 | return 1; |
| 1850 | } |
| 1851 | |
| 1852 | #ifdef DEBUG |
| 1853 | if( debugMask & PARSER ){ |
| 1854 | printf("**** Found inline routine: %.*s on line %d...\n", |
| 1855 | pName->nText, pName->zText, pFirst->nLine); |
| 1856 | PrintTokens(pFirst,pEnd); |
| 1857 | printf("\n"); |
| 1858 | } |
| 1859 | #endif |
| @@ -1888,11 +1888,11 @@ | |
| 1888 | ** to search for an occurrence of an ID followed immediately by '('. |
| 1889 | ** If found, we have a prototype. Otherwise we are dealing with a |
| 1890 | ** variable definition. |
| 1891 | */ |
| 1892 | static int isVariableDef(Token *pFirst, Token *pEnd){ |
| 1893 | if( pEnd && pEnd->zText[0]=='=' && |
| 1894 | (pEnd->pPrev->nText!=8 || strncmp(pEnd->pPrev->zText,"operator",8)!=0) |
| 1895 | ){ |
| 1896 | return 1; |
| 1897 | } |
| 1898 | while( pFirst && pFirst!=pEnd && pFirst->pNext && pFirst->pNext!=pEnd ){ |
| @@ -1949,11 +1949,11 @@ | |
| 1949 | } |
| 1950 | while( pFirst!=0 && pFirst->pNext!=pEnd && |
| 1951 | ((pFirst->nText==6 && strncmp(pFirst->zText,"static",6)==0) |
| 1952 | || (pFirst->nText==5 && strncmp(pFirst->zText,"LOCAL",6)==0)) |
| 1953 | ){ |
| 1954 | /* Lose the initial "static" or local from local variables. |
| 1955 | ** We'll prepend "extern" later. */ |
| 1956 | pFirst = pFirst->pNext; |
| 1957 | isLocal = 1; |
| 1958 | } |
| 1959 | if( pFirst==0 || !isLocal ){ |
| @@ -1962,11 +1962,11 @@ | |
| 1962 | }else if( flags & PS_Method ){ |
| 1963 | /* Methods are declared by their class. Don't declare separately. */ |
| 1964 | return nErr; |
| 1965 | } |
| 1966 | isVar = (flags & (PS_Typedef|PS_Method))==0 && isVariableDef(pFirst,pEnd); |
| 1967 | if( isVar && (flags & (PS_Interface|PS_Export|PS_Local))!=0 |
| 1968 | && (flags & PS_Extern)==0 ){ |
| 1969 | fprintf(stderr,"%s:%d: Can't define a variable in this context\n", |
| 1970 | zFilename, pFirst->nLine); |
| 1971 | nErr++; |
| 1972 | } |
| @@ -2095,11 +2095,11 @@ | |
| 2095 | nCmd++; |
| 2096 | } |
| 2097 | |
| 2098 | if( nCmd==5 && strncmp(zCmd,"endif",5)==0 ){ |
| 2099 | /* |
| 2100 | ** Pop the if stack |
| 2101 | */ |
| 2102 | pIf = ifStack; |
| 2103 | if( pIf==0 ){ |
| 2104 | fprintf(stderr,"%s:%d: extra '#endif'.\n",zFilename,pToken->nLine); |
| 2105 | return 1; |
| @@ -2106,11 +2106,11 @@ | |
| 2106 | } |
| 2107 | ifStack = pIf->pNext; |
| 2108 | SafeFree(pIf); |
| 2109 | }else if( nCmd==6 && strncmp(zCmd,"define",6)==0 ){ |
| 2110 | /* |
| 2111 | ** Record a #define if we are in PS_Interface or PS_Export |
| 2112 | */ |
| 2113 | Decl *pDecl; |
| 2114 | if( !(flags & (PS_Local|PS_Interface|PS_Export)) ){ return 0; } |
| 2115 | zArg = &zCmd[6]; |
| 2116 | while( *zArg && isspace(*zArg) && *zArg!='\n' ){ |
| @@ -2129,11 +2129,11 @@ | |
| 2129 | }else if( flags & PS_Local ){ |
| 2130 | DeclSetProperty(pDecl,DP_Local); |
| 2131 | } |
| 2132 | }else if( nCmd==7 && strncmp(zCmd,"include",7)==0 ){ |
| 2133 | /* |
| 2134 | ** Record an #include if we are in PS_Interface or PS_Export |
| 2135 | */ |
| 2136 | Include *pInclude; |
| 2137 | char *zIf; |
| 2138 | |
| 2139 | if( !(flags & (PS_Interface|PS_Export)) ){ return 0; } |
| @@ -2184,11 +2184,11 @@ | |
| 2184 | PushIfMacro(0,0,0,pToken->nLine,PS_Local); |
| 2185 | }else{ |
| 2186 | PushIfMacro(0,zArg,nArg,pToken->nLine,0); |
| 2187 | } |
| 2188 | }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){ |
| 2189 | /* |
| 2190 | ** Push an #ifdef. |
| 2191 | */ |
| 2192 | zArg = &zCmd[5]; |
| 2193 | while( *zArg && isspace(*zArg) && *zArg!='\n' ){ |
| 2194 | zArg++; |
| @@ -2207,11 +2207,11 @@ | |
| 2207 | if( *zArg==0 || *zArg=='\n' ){ return 0; } |
| 2208 | nArg = pToken->nText + (int)(pToken->zText - zArg); |
| 2209 | PushIfMacro("!defined",zArg,nArg,pToken->nLine,0); |
| 2210 | }else if( nCmd==4 && strncmp(zCmd,"else",4)==0 ){ |
| 2211 | /* |
| 2212 | ** Invert the #if on the top of the stack |
| 2213 | */ |
| 2214 | if( ifStack==0 ){ |
| 2215 | fprintf(stderr,"%s:%d: '#else' without an '#if'\n",zFilename, |
| 2216 | pToken->nLine); |
| 2217 | return 1; |
| @@ -2224,33 +2224,33 @@ | |
| 2224 | }else{ |
| 2225 | pIf->flags = 0; |
| 2226 | } |
| 2227 | }else{ |
| 2228 | /* |
| 2229 | ** This directive can be safely ignored |
| 2230 | */ |
| 2231 | return 0; |
| 2232 | } |
| 2233 | |
| 2234 | /* |
| 2235 | ** Recompute the preset flags |
| 2236 | */ |
| 2237 | *pPresetFlags = 0; |
| 2238 | for(pIf = ifStack; pIf; pIf=pIf->pNext){ |
| 2239 | *pPresetFlags |= pIf->flags; |
| 2240 | } |
| 2241 | |
| 2242 | return nErr; |
| 2243 | } |
| 2244 | |
| 2245 | /* |
| 2246 | ** Parse an entire file. Return the number of errors. |
| 2247 | ** |
| 2248 | ** pList is a list of tokens in the file. Whitespace tokens have been |
| 2249 | ** eliminated, and text with {...} has been collapsed into a |
| 2250 | ** single TT_Brace token. |
| 2251 | ** |
| 2252 | ** initFlags are a set of parse flags that should always be set for this |
| 2253 | ** file. For .c files this is normally 0. For .h files it is PS_Interface. |
| 2254 | */ |
| 2255 | static int ParseFile(Token *pList, int initFlags){ |
| 2256 | int nErr = 0; |
| @@ -2279,11 +2279,11 @@ | |
| 2279 | pStart = 0; |
| 2280 | flags = presetFlags; |
| 2281 | break; |
| 2282 | |
| 2283 | case '=': |
| 2284 | if( pList->pPrev->nText==8 |
| 2285 | && strncmp(pList->pPrev->zText,"operator",8)==0 ){ |
| 2286 | break; |
| 2287 | } |
| 2288 | nErr += ProcessDecl(pStart,pList,flags); |
| 2289 | pStart = 0; |
| @@ -2471,11 +2471,11 @@ | |
| 2471 | pDecl->zExtra = 0; |
| 2472 | } |
| 2473 | |
| 2474 | /* |
| 2475 | ** Reset the DP_Forward and DP_Declared flags on all Decl structures. |
| 2476 | ** Set both flags for anything that is tagged as local and isn't |
| 2477 | ** in the file zFilename so that it won't be printing in other files. |
| 2478 | */ |
| 2479 | static void ResetDeclFlags(char *zFilename){ |
| 2480 | Decl *pDecl; |
| 2481 | |
| @@ -2574,11 +2574,11 @@ | |
| 2574 | int flag; |
| 2575 | int isCpp; /* True if generating C++ */ |
| 2576 | int doneTypedef = 0; /* True if a typedef has been done for this object */ |
| 2577 | |
| 2578 | /* printf("BEGIN %s of %s\n",needFullDecl?"FULL":"PROTOTYPE",pDecl->zName);*/ |
| 2579 | /* |
| 2580 | ** For any object that has a forward declaration, go ahead and do the |
| 2581 | ** forward declaration first. |
| 2582 | */ |
| 2583 | isCpp = (pState->flags & DP_Cplusplus) != 0; |
| 2584 | for(p=pDecl; p; p=p->pSameName){ |
| @@ -2626,12 +2626,12 @@ | |
| 2626 | ** function on a recursive call with the same pDecl. Hence, recursive |
| 2627 | ** calls to this function (through ScanText()) can never change the |
| 2628 | ** value of DP_Flag out from under us. |
| 2629 | */ |
| 2630 | for(p=pDecl; p; p=p->pSameName){ |
| 2631 | if( !DeclHasProperty(p,DP_Declared) |
| 2632 | && (p->zFwd==0 || needFullDecl) |
| 2633 | && p->zDecl!=0 |
| 2634 | ){ |
| 2635 | DeclSetProperty(p,DP_Forward|DP_Declared|DP_Flag); |
| 2636 | }else{ |
| 2637 | DeclClearProperty(p,DP_Flag); |
| @@ -2735,12 +2735,12 @@ | |
| 2735 | ** by sToken. |
| 2736 | */ |
| 2737 | pDecl = FindDecl(sToken.zText,sToken.nText); |
| 2738 | if( pDecl==0 ) continue; |
| 2739 | |
| 2740 | /* |
| 2741 | ** If we get this far, we've found an identifier that has a |
| 2742 | ** declaration in the database. Now see if we the full declaration |
| 2743 | ** or just a forward declaration. |
| 2744 | */ |
| 2745 | GetNonspaceToken(&sIn,&sNext); |
| 2746 | if( sNext.zText[0]=='*' ){ |
| @@ -2770,12 +2770,12 @@ | |
| 2770 | int progress; |
| 2771 | |
| 2772 | do{ |
| 2773 | progress = 0; |
| 2774 | for(pDecl=pDeclFirst; pDecl; pDecl=pDecl->pNext){ |
| 2775 | if( DeclHasProperty(pDecl,DP_Forward) |
| 2776 | && !DeclHasProperty(pDecl,DP_Declared) |
| 2777 | ){ |
| 2778 | DeclareObject(pDecl,pState,1); |
| 2779 | progress = 1; |
| 2780 | assert( DeclHasProperty(pDecl,DP_Declared) ); |
| 2781 | } |
| @@ -2842,11 +2842,11 @@ | |
| 2842 | nErr++; |
| 2843 | } |
| 2844 | }else if( report ){ |
| 2845 | fprintf(report,"unchanged\n"); |
| 2846 | } |
| 2847 | SafeFree(zOldVersion); |
| 2848 | IdentTableReset(&includeTable); |
| 2849 | StringReset(&outStr); |
| 2850 | return nErr; |
| 2851 | } |
| 2852 | |
| @@ -2878,11 +2878,11 @@ | |
| 2878 | } |
| 2879 | ChangeIfContext(0,&sState); |
| 2880 | printf("%s",StringGet(&outStr)); |
| 2881 | IdentTableReset(&includeTable); |
| 2882 | StringReset(&outStr); |
| 2883 | return 0; |
| 2884 | } |
| 2885 | |
| 2886 | #ifdef DEBUG |
| 2887 | /* |
| 2888 | ** Return the number of characters in the given string prior to the |
| @@ -3040,11 +3040,11 @@ | |
| 3040 | int nSrc; |
| 3041 | char *zSrc; |
| 3042 | InFile *pFile; |
| 3043 | int i; |
| 3044 | |
| 3045 | /* |
| 3046 | ** Get the name of the input file to be scanned. The input file is |
| 3047 | ** everything before the first ':' or the whole file if no ':' is seen. |
| 3048 | ** |
| 3049 | ** Except, on windows, ignore any ':' that occurs as the second character |
| 3050 | ** since it might be part of the drive specifier. So really, the ":' has |
| @@ -3099,11 +3099,11 @@ | |
| 3099 | } |
| 3100 | } |
| 3101 | |
| 3102 | /* |
| 3103 | ** If pFile->zSrc contains no 'c' or 'C' in its extension, it |
| 3104 | ** must be a header file. In that case, we need to set the |
| 3105 | ** PS_Interface flag. |
| 3106 | */ |
| 3107 | pFile->flags |= PS_Interface; |
| 3108 | for(i=nSrc-1; i>0 && zSrc[i]!='.'; i--){ |
| 3109 | if( zSrc[i]=='c' || zSrc[i]=='C' ){ |
| @@ -3110,11 +3110,11 @@ | |
| 3110 | pFile->flags &= ~PS_Interface; |
| 3111 | break; |
| 3112 | } |
| 3113 | } |
| 3114 | |
| 3115 | /* Done! |
| 3116 | */ |
| 3117 | return pFile; |
| 3118 | } |
| 3119 | |
| 3120 | /* MS-Windows and MS-DOS both have the following serious OS bug: the |
| @@ -3162,11 +3162,11 @@ | |
| 3162 | while( c!=EOF ){ |
| 3163 | while( c!=EOF && isspace(c) ){ |
| 3164 | if( c=='\n' ){ |
| 3165 | startOfLine = 1; |
| 3166 | } |
| 3167 | c = getc(in); |
| 3168 | if( startOfLine && c=='#' ){ |
| 3169 | while( c!=EOF && c!='\n' ){ |
| 3170 | c = getc(in); |
| 3171 | } |
| 3172 | } |
| @@ -3184,11 +3184,11 @@ | |
| 3184 | if( nAlloc==0 ){ |
| 3185 | nAlloc = 100 + argc; |
| 3186 | zNew = malloc( sizeof(char*) * nAlloc ); |
| 3187 | }else{ |
| 3188 | nAlloc *= 2; |
| 3189 | zNew = realloc( zNew, sizeof(char*) * nAlloc ); |
| 3190 | } |
| 3191 | } |
| 3192 | if( zNew ){ |
| 3193 | int j = nNew + index; |
| 3194 | zNew[j] = malloc( n + 1 ); |
| @@ -3254,11 +3254,11 @@ | |
| 3254 | |
| 3255 | /* |
| 3256 | ** The following text contains a few simple #defines that we want |
| 3257 | ** to be available to every file. |
| 3258 | */ |
| 3259 | static const char zInit[] = |
| 3260 | "#define INTERFACE 0\n" |
| 3261 | "#define EXPORT_INTERFACE 0\n" |
| 3262 | "#define LOCAL_INTERFACE 0\n" |
| 3263 | "#define EXPORT\n" |
| 3264 | "#define LOCAL static\n" |
| 3265 |
+22
-9
| --- src/makemake.tcl | ||
| +++ src/makemake.tcl | ||
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | #!/usr/bin/tclsh |
| 2 | 2 | # |
| 3 | -# Run this TCL script to generate the various makefiles for a variety | |
| 3 | +# Run this Tcl script to generate the various makefiles for a variety | |
| 4 | 4 | # of platforms. Files generated include: |
| 5 | 5 | # |
| 6 | 6 | # src/main.mk # makefile for all unix systems |
| 7 | 7 | # win/Makefile.mingw # makefile for mingw on windows |
| 8 | 8 | # win/Makefile.* # makefiles for other windows compilers |
| @@ -110,10 +110,11 @@ | ||
| 110 | 110 | sitemap |
| 111 | 111 | skins |
| 112 | 112 | sqlcmd |
| 113 | 113 | stash |
| 114 | 114 | stat |
| 115 | + statrep | |
| 115 | 116 | style |
| 116 | 117 | sync |
| 117 | 118 | tag |
| 118 | 119 | tar |
| 119 | 120 | th_main |
| @@ -142,10 +143,12 @@ | ||
| 142 | 143 | |
| 143 | 144 | # Additional resource files that get built into the executable. |
| 144 | 145 | # |
| 145 | 146 | set extra_files { |
| 146 | 147 | diff.tcl |
| 148 | + markdown.md | |
| 149 | + ../skins/*/*.txt | |
| 147 | 150 | } |
| 148 | 151 | |
| 149 | 152 | # Options used to compile the included SQLite library. |
| 150 | 153 | # |
| 151 | 154 | set SQLITE_OPTIONS { |
| @@ -203,10 +206,19 @@ | ||
| 203 | 206 | } |
| 204 | 207 | |
| 205 | 208 | # STOP HERE. |
| 206 | 209 | # Unless the build procedures changes, you should not have to edit anything |
| 207 | 210 | # below this line. |
| 211 | + | |
| 212 | +# Expand any wildcards in "extra_files" | |
| 213 | +set new_extra_files {} | |
| 214 | +foreach file $extra_files { | |
| 215 | + foreach x [glob -nocomplain $file] { | |
| 216 | + lappend new_extra_files $x | |
| 217 | + } | |
| 218 | +} | |
| 219 | +set extra_files $new_extra_files | |
| 208 | 220 | |
| 209 | 221 | ############################################################################## |
| 210 | 222 | ############################################################################## |
| 211 | 223 | ############################################################################## |
| 212 | 224 | # Start by generating the "main.mk" makefile used for all unix systems. |
| @@ -362,11 +374,11 @@ | ||
| 362 | 374 | set mhargs [string map [list <<<NEXT_LINE>>> \\\n\t] $mhargs] |
| 363 | 375 | writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" |
| 364 | 376 | writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >\$@\n" |
| 365 | 377 | |
| 366 | 378 | writeln "\$(OBJDIR)/builtin_data.h: \$(OBJDIR)/mkbuiltin \$(EXTRA_FILES)" |
| 367 | -writeln "\t\$(OBJDIR)/mkbuiltin \$(EXTRA_FILES) >\$@\n" | |
| 379 | +writeln "\t\$(OBJDIR)/mkbuiltin --prefix \$(SRCDIR)/ \$(EXTRA_FILES) >\$@\n" | |
| 368 | 380 | |
| 369 | 381 | writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h" |
| 370 | 382 | writeln "\t\$(OBJDIR)/makeheaders $mhargs" |
| 371 | 383 | writeln "\ttouch \$(OBJDIR)/headers" |
| 372 | 384 | writeln "\$(OBJDIR)/headers: Makefile" |
| @@ -573,12 +585,13 @@ | ||
| 573 | 585 | #### The directories where the OpenSSL include and library files are located. |
| 574 | 586 | # The recommended usage here is to use the Sysinternals junction tool |
| 575 | 587 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 576 | 588 | # Fossil source code directory and the target OpenSSL source directory. |
| 577 | 589 | # |
| 578 | -OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1j/include | |
| 579 | -OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1j | |
| 590 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2 | |
| 591 | +OPENSSLINCDIR = $(OPENSSLDIR)/include | |
| 592 | +OPENSSLLIBDIR = $(OPENSSLDIR) | |
| 580 | 593 | |
| 581 | 594 | #### Either the directory where the Tcl library is installed or the Tcl |
| 582 | 595 | # source code directory resides (depending on the value of the macro |
| 583 | 596 | # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, |
| 584 | 597 | # this directory must have "include" and "lib" sub-directories. If |
| @@ -1014,11 +1027,11 @@ | ||
| 1014 | 1027 | append mhargs " \\\n\t\t\$(OBJDIR)/VERSION.h" |
| 1015 | 1028 | writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(MKINDEX)" |
| 1016 | 1029 | writeln "\t\$(MKINDEX) \$(TRANS_SRC) >\$@\n" |
| 1017 | 1030 | |
| 1018 | 1031 | writeln "\$(OBJDIR)/builtin_data.h:\t\$(MKBUILTIN) \$(EXTRA_FILES)" |
| 1019 | -writeln "\t\$(MKBUILTIN) \$(EXTRA_FILES) >\$@\n" | |
| 1032 | +writeln "\t\$(MKBUILTIN) --prefix \$(SRCDIR)/ \$(EXTRA_FILES) >\$@\n" | |
| 1020 | 1033 | |
| 1021 | 1034 | writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(MAKEHEADERS) \$(OBJDIR)/VERSION.h" |
| 1022 | 1035 | writeln "\t\$(MAKEHEADERS) $mhargs" |
| 1023 | 1036 | writeln "\techo Done >\$(OBJDIR)/headers\n" |
| 1024 | 1037 | writeln "\$(OBJDIR)/headers: Makefile\n" |
| @@ -1193,11 +1206,11 @@ | ||
| 1193 | 1206 | |
| 1194 | 1207 | page_index.h: mkindex$E $(SRC) |
| 1195 | 1208 | +$** > $@ |
| 1196 | 1209 | |
| 1197 | 1210 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 1198 | - +$** > $@ | |
| 1211 | + mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ | |
| 1199 | 1212 | |
| 1200 | 1213 | clean: |
| 1201 | 1214 | -del $(OBJDIR)\*.obj |
| 1202 | 1215 | -del *.obj *_.c *.h *.map |
| 1203 | 1216 | |
| @@ -1305,11 +1318,11 @@ | ||
| 1305 | 1318 | |
| 1306 | 1319 | # Uncomment to enable Tcl support |
| 1307 | 1320 | # FOSSIL_ENABLE_TCL = 1 |
| 1308 | 1321 | |
| 1309 | 1322 | !ifdef FOSSIL_ENABLE_SSL |
| 1310 | -SSLDIR = $(B)\compat\openssl-1.0.1j | |
| 1323 | +SSLDIR = $(B)\compat\openssl-1.0.2 | |
| 1311 | 1324 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1312 | 1325 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1313 | 1326 | SSLLFLAGS = /nologo /opt:ref /debug |
| 1314 | 1327 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 1315 | 1328 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| @@ -1587,11 +1600,11 @@ | ||
| 1587 | 1600 | |
| 1588 | 1601 | page_index.h: mkindex$E $(SRC) |
| 1589 | 1602 | $** > $@ |
| 1590 | 1603 | |
| 1591 | 1604 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 1592 | - $** > $@ | |
| 1605 | + mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ | |
| 1593 | 1606 | |
| 1594 | 1607 | clean: |
| 1595 | 1608 | -del $(OX)\*.obj |
| 1596 | 1609 | -del *.obj |
| 1597 | 1610 | -del *_.c |
| @@ -1819,11 +1832,11 @@ | ||
| 1819 | 1832 | # generate the index source, containing all web references,.. |
| 1820 | 1833 | page_index.h: $(TRANSLATEDSRC) mkindex.exe |
| 1821 | 1834 | mkindex.exe $(TRANSLATEDSRC) >$@ |
| 1822 | 1835 | |
| 1823 | 1836 | builtin_data.h: $(EXTRA_FILES) mkbuiltin.exe |
| 1824 | - mkbuiltin.exe $(EXTRA_FILES) >$@ | |
| 1837 | + mkbuiltin.exe --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ | |
| 1825 | 1838 | |
| 1826 | 1839 | # extracting version info from manifest |
| 1827 | 1840 | VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION |
| 1828 | 1841 | version.exe ..\manifest.uuid ..\manifest ..\VERSION >$@ |
| 1829 | 1842 | |
| 1830 | 1843 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -1,8 +1,8 @@ | |
| 1 | #!/usr/bin/tclsh |
| 2 | # |
| 3 | # Run this TCL script to generate the various makefiles for a variety |
| 4 | # of platforms. Files generated include: |
| 5 | # |
| 6 | # src/main.mk # makefile for all unix systems |
| 7 | # win/Makefile.mingw # makefile for mingw on windows |
| 8 | # win/Makefile.* # makefiles for other windows compilers |
| @@ -110,10 +110,11 @@ | |
| 110 | sitemap |
| 111 | skins |
| 112 | sqlcmd |
| 113 | stash |
| 114 | stat |
| 115 | style |
| 116 | sync |
| 117 | tag |
| 118 | tar |
| 119 | th_main |
| @@ -142,10 +143,12 @@ | |
| 142 | |
| 143 | # Additional resource files that get built into the executable. |
| 144 | # |
| 145 | set extra_files { |
| 146 | diff.tcl |
| 147 | } |
| 148 | |
| 149 | # Options used to compile the included SQLite library. |
| 150 | # |
| 151 | set SQLITE_OPTIONS { |
| @@ -203,10 +206,19 @@ | |
| 203 | } |
| 204 | |
| 205 | # STOP HERE. |
| 206 | # Unless the build procedures changes, you should not have to edit anything |
| 207 | # below this line. |
| 208 | |
| 209 | ############################################################################## |
| 210 | ############################################################################## |
| 211 | ############################################################################## |
| 212 | # Start by generating the "main.mk" makefile used for all unix systems. |
| @@ -362,11 +374,11 @@ | |
| 362 | set mhargs [string map [list <<<NEXT_LINE>>> \\\n\t] $mhargs] |
| 363 | writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" |
| 364 | writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >\$@\n" |
| 365 | |
| 366 | writeln "\$(OBJDIR)/builtin_data.h: \$(OBJDIR)/mkbuiltin \$(EXTRA_FILES)" |
| 367 | writeln "\t\$(OBJDIR)/mkbuiltin \$(EXTRA_FILES) >\$@\n" |
| 368 | |
| 369 | writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h" |
| 370 | writeln "\t\$(OBJDIR)/makeheaders $mhargs" |
| 371 | writeln "\ttouch \$(OBJDIR)/headers" |
| 372 | writeln "\$(OBJDIR)/headers: Makefile" |
| @@ -573,12 +585,13 @@ | |
| 573 | #### The directories where the OpenSSL include and library files are located. |
| 574 | # The recommended usage here is to use the Sysinternals junction tool |
| 575 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 576 | # Fossil source code directory and the target OpenSSL source directory. |
| 577 | # |
| 578 | OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1j/include |
| 579 | OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1j |
| 580 | |
| 581 | #### Either the directory where the Tcl library is installed or the Tcl |
| 582 | # source code directory resides (depending on the value of the macro |
| 583 | # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, |
| 584 | # this directory must have "include" and "lib" sub-directories. If |
| @@ -1014,11 +1027,11 @@ | |
| 1014 | append mhargs " \\\n\t\t\$(OBJDIR)/VERSION.h" |
| 1015 | writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(MKINDEX)" |
| 1016 | writeln "\t\$(MKINDEX) \$(TRANS_SRC) >\$@\n" |
| 1017 | |
| 1018 | writeln "\$(OBJDIR)/builtin_data.h:\t\$(MKBUILTIN) \$(EXTRA_FILES)" |
| 1019 | writeln "\t\$(MKBUILTIN) \$(EXTRA_FILES) >\$@\n" |
| 1020 | |
| 1021 | writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(MAKEHEADERS) \$(OBJDIR)/VERSION.h" |
| 1022 | writeln "\t\$(MAKEHEADERS) $mhargs" |
| 1023 | writeln "\techo Done >\$(OBJDIR)/headers\n" |
| 1024 | writeln "\$(OBJDIR)/headers: Makefile\n" |
| @@ -1193,11 +1206,11 @@ | |
| 1193 | |
| 1194 | page_index.h: mkindex$E $(SRC) |
| 1195 | +$** > $@ |
| 1196 | |
| 1197 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 1198 | +$** > $@ |
| 1199 | |
| 1200 | clean: |
| 1201 | -del $(OBJDIR)\*.obj |
| 1202 | -del *.obj *_.c *.h *.map |
| 1203 | |
| @@ -1305,11 +1318,11 @@ | |
| 1305 | |
| 1306 | # Uncomment to enable Tcl support |
| 1307 | # FOSSIL_ENABLE_TCL = 1 |
| 1308 | |
| 1309 | !ifdef FOSSIL_ENABLE_SSL |
| 1310 | SSLDIR = $(B)\compat\openssl-1.0.1j |
| 1311 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1312 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1313 | SSLLFLAGS = /nologo /opt:ref /debug |
| 1314 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 1315 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| @@ -1587,11 +1600,11 @@ | |
| 1587 | |
| 1588 | page_index.h: mkindex$E $(SRC) |
| 1589 | $** > $@ |
| 1590 | |
| 1591 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 1592 | $** > $@ |
| 1593 | |
| 1594 | clean: |
| 1595 | -del $(OX)\*.obj |
| 1596 | -del *.obj |
| 1597 | -del *_.c |
| @@ -1819,11 +1832,11 @@ | |
| 1819 | # generate the index source, containing all web references,.. |
| 1820 | page_index.h: $(TRANSLATEDSRC) mkindex.exe |
| 1821 | mkindex.exe $(TRANSLATEDSRC) >$@ |
| 1822 | |
| 1823 | builtin_data.h: $(EXTRA_FILES) mkbuiltin.exe |
| 1824 | mkbuiltin.exe $(EXTRA_FILES) >$@ |
| 1825 | |
| 1826 | # extracting version info from manifest |
| 1827 | VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION |
| 1828 | version.exe ..\manifest.uuid ..\manifest ..\VERSION >$@ |
| 1829 | |
| 1830 |
| --- src/makemake.tcl | |
| +++ src/makemake.tcl | |
| @@ -1,8 +1,8 @@ | |
| 1 | #!/usr/bin/tclsh |
| 2 | # |
| 3 | # Run this Tcl script to generate the various makefiles for a variety |
| 4 | # of platforms. Files generated include: |
| 5 | # |
| 6 | # src/main.mk # makefile for all unix systems |
| 7 | # win/Makefile.mingw # makefile for mingw on windows |
| 8 | # win/Makefile.* # makefiles for other windows compilers |
| @@ -110,10 +110,11 @@ | |
| 110 | sitemap |
| 111 | skins |
| 112 | sqlcmd |
| 113 | stash |
| 114 | stat |
| 115 | statrep |
| 116 | style |
| 117 | sync |
| 118 | tag |
| 119 | tar |
| 120 | th_main |
| @@ -142,10 +143,12 @@ | |
| 143 | |
| 144 | # Additional resource files that get built into the executable. |
| 145 | # |
| 146 | set extra_files { |
| 147 | diff.tcl |
| 148 | markdown.md |
| 149 | ../skins/*/*.txt |
| 150 | } |
| 151 | |
| 152 | # Options used to compile the included SQLite library. |
| 153 | # |
| 154 | set SQLITE_OPTIONS { |
| @@ -203,10 +206,19 @@ | |
| 206 | } |
| 207 | |
| 208 | # STOP HERE. |
| 209 | # Unless the build procedures changes, you should not have to edit anything |
| 210 | # below this line. |
| 211 | |
| 212 | # Expand any wildcards in "extra_files" |
| 213 | set new_extra_files {} |
| 214 | foreach file $extra_files { |
| 215 | foreach x [glob -nocomplain $file] { |
| 216 | lappend new_extra_files $x |
| 217 | } |
| 218 | } |
| 219 | set extra_files $new_extra_files |
| 220 | |
| 221 | ############################################################################## |
| 222 | ############################################################################## |
| 223 | ############################################################################## |
| 224 | # Start by generating the "main.mk" makefile used for all unix systems. |
| @@ -362,11 +374,11 @@ | |
| 374 | set mhargs [string map [list <<<NEXT_LINE>>> \\\n\t] $mhargs] |
| 375 | writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(OBJDIR)/mkindex" |
| 376 | writeln "\t\$(OBJDIR)/mkindex \$(TRANS_SRC) >\$@\n" |
| 377 | |
| 378 | writeln "\$(OBJDIR)/builtin_data.h: \$(OBJDIR)/mkbuiltin \$(EXTRA_FILES)" |
| 379 | writeln "\t\$(OBJDIR)/mkbuiltin --prefix \$(SRCDIR)/ \$(EXTRA_FILES) >\$@\n" |
| 380 | |
| 381 | writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(OBJDIR)/makeheaders \$(OBJDIR)/VERSION.h" |
| 382 | writeln "\t\$(OBJDIR)/makeheaders $mhargs" |
| 383 | writeln "\ttouch \$(OBJDIR)/headers" |
| 384 | writeln "\$(OBJDIR)/headers: Makefile" |
| @@ -573,12 +585,13 @@ | |
| 585 | #### The directories where the OpenSSL include and library files are located. |
| 586 | # The recommended usage here is to use the Sysinternals junction tool |
| 587 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 588 | # Fossil source code directory and the target OpenSSL source directory. |
| 589 | # |
| 590 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2 |
| 591 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 592 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 593 | |
| 594 | #### Either the directory where the Tcl library is installed or the Tcl |
| 595 | # source code directory resides (depending on the value of the macro |
| 596 | # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, |
| 597 | # this directory must have "include" and "lib" sub-directories. If |
| @@ -1014,11 +1027,11 @@ | |
| 1027 | append mhargs " \\\n\t\t\$(OBJDIR)/VERSION.h" |
| 1028 | writeln "\$(OBJDIR)/page_index.h: \$(TRANS_SRC) \$(MKINDEX)" |
| 1029 | writeln "\t\$(MKINDEX) \$(TRANS_SRC) >\$@\n" |
| 1030 | |
| 1031 | writeln "\$(OBJDIR)/builtin_data.h:\t\$(MKBUILTIN) \$(EXTRA_FILES)" |
| 1032 | writeln "\t\$(MKBUILTIN) --prefix \$(SRCDIR)/ \$(EXTRA_FILES) >\$@\n" |
| 1033 | |
| 1034 | writeln "\$(OBJDIR)/headers:\t\$(OBJDIR)/page_index.h \$(OBJDIR)/builtin_data.h \$(MAKEHEADERS) \$(OBJDIR)/VERSION.h" |
| 1035 | writeln "\t\$(MAKEHEADERS) $mhargs" |
| 1036 | writeln "\techo Done >\$(OBJDIR)/headers\n" |
| 1037 | writeln "\$(OBJDIR)/headers: Makefile\n" |
| @@ -1193,11 +1206,11 @@ | |
| 1206 | |
| 1207 | page_index.h: mkindex$E $(SRC) |
| 1208 | +$** > $@ |
| 1209 | |
| 1210 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 1211 | mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ |
| 1212 | |
| 1213 | clean: |
| 1214 | -del $(OBJDIR)\*.obj |
| 1215 | -del *.obj *_.c *.h *.map |
| 1216 | |
| @@ -1305,11 +1318,11 @@ | |
| 1318 | |
| 1319 | # Uncomment to enable Tcl support |
| 1320 | # FOSSIL_ENABLE_TCL = 1 |
| 1321 | |
| 1322 | !ifdef FOSSIL_ENABLE_SSL |
| 1323 | SSLDIR = $(B)\compat\openssl-1.0.2 |
| 1324 | SSLINCDIR = $(SSLDIR)\inc32 |
| 1325 | SSLLIBDIR = $(SSLDIR)\out32 |
| 1326 | SSLLFLAGS = /nologo /opt:ref /debug |
| 1327 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 1328 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| @@ -1587,11 +1600,11 @@ | |
| 1600 | |
| 1601 | page_index.h: mkindex$E $(SRC) |
| 1602 | $** > $@ |
| 1603 | |
| 1604 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 1605 | mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ |
| 1606 | |
| 1607 | clean: |
| 1608 | -del $(OX)\*.obj |
| 1609 | -del *.obj |
| 1610 | -del *_.c |
| @@ -1819,11 +1832,11 @@ | |
| 1832 | # generate the index source, containing all web references,.. |
| 1833 | page_index.h: $(TRANSLATEDSRC) mkindex.exe |
| 1834 | mkindex.exe $(TRANSLATEDSRC) >$@ |
| 1835 | |
| 1836 | builtin_data.h: $(EXTRA_FILES) mkbuiltin.exe |
| 1837 | mkbuiltin.exe --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ |
| 1838 | |
| 1839 | # extracting version info from manifest |
| 1840 | VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION |
| 1841 | version.exe ..\manifest.uuid ..\manifest ..\VERSION >$@ |
| 1842 | |
| 1843 |
+64
-53
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -1187,16 +1187,18 @@ | ||
| 1187 | 1187 | /* |
| 1188 | 1188 | ** Add a single entry to the mlink table. Also add the filename to |
| 1189 | 1189 | ** the filename table if it is not there already. |
| 1190 | 1190 | */ |
| 1191 | 1191 | static void add_one_mlink( |
| 1192 | + int pmid, /* The parent manifest */ | |
| 1193 | + const char *zFromUuid, /* UUID for content in parent */ | |
| 1192 | 1194 | int mid, /* The record ID of the manifest */ |
| 1193 | - const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */ | |
| 1194 | - const char *zToUuid, /* UUID for the mlink.fid. "" to delete */ | |
| 1195 | + const char *zToUuid, /* UUID for content in child */ | |
| 1195 | 1196 | const char *zFilename, /* Filename */ |
| 1196 | 1197 | const char *zPrior, /* Previous filename. NULL if unchanged */ |
| 1197 | 1198 | int isPublic, /* True if mid is not a private manifest */ |
| 1199 | + int isPrimary, /* pmid is the primary parent of mid */ | |
| 1198 | 1200 | int mperm /* 1: exec, 2: symlink */ |
| 1199 | 1201 | ){ |
| 1200 | 1202 | int fnid, pfnid, pid, fid; |
| 1201 | 1203 | static Stmt s1; |
| 1202 | 1204 | |
| @@ -1216,19 +1218,21 @@ | ||
| 1216 | 1218 | }else{ |
| 1217 | 1219 | fid = uuid_to_rid(zToUuid, 1); |
| 1218 | 1220 | if( isPublic ) content_make_public(fid); |
| 1219 | 1221 | } |
| 1220 | 1222 | db_static_prepare(&s1, |
| 1221 | - "INSERT INTO mlink(mid,pid,fid,fnid,pfnid,mperm)" | |
| 1222 | - "VALUES(:m,:p,:f,:n,:pfn,:mp)" | |
| 1223 | + "INSERT INTO mlink(mid,fid,pmid,pid,fnid,pfnid,mperm,isaux)" | |
| 1224 | + "VALUES(:m,:f,:pm,:p,:n,:pfn,:mp,:isaux)" | |
| 1223 | 1225 | ); |
| 1224 | 1226 | db_bind_int(&s1, ":m", mid); |
| 1225 | - db_bind_int(&s1, ":p", pid); | |
| 1226 | 1227 | db_bind_int(&s1, ":f", fid); |
| 1228 | + db_bind_int(&s1, ":pm", pmid); | |
| 1229 | + db_bind_int(&s1, ":p", pid); | |
| 1227 | 1230 | db_bind_int(&s1, ":n", fnid); |
| 1228 | 1231 | db_bind_int(&s1, ":pfn", pfnid); |
| 1229 | 1232 | db_bind_int(&s1, ":mp", mperm); |
| 1233 | + db_bind_int(&s1, ":isaux", isPrimary==0); | |
| 1230 | 1234 | db_exec(&s1); |
| 1231 | 1235 | if( pid && fid ){ |
| 1232 | 1236 | content_deltify(pid, fid, 0); |
| 1233 | 1237 | } |
| 1234 | 1238 | } |
| @@ -1342,24 +1346,29 @@ | ||
| 1342 | 1346 | ** |
| 1343 | 1347 | ** Deleted files have mlink.fid=0. |
| 1344 | 1348 | ** Added files have mlink.pid=0. |
| 1345 | 1349 | ** Edited files have both mlink.pid!=0 and mlink.fid!=0 |
| 1346 | 1350 | */ |
| 1347 | -static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){ | |
| 1351 | +static void add_mlink( | |
| 1352 | + int pmid, Manifest *pParent, /* Parent check-in */ | |
| 1353 | + int mid, Manifest *pChild, /* The child check-in */ | |
| 1354 | + int isPrim /* TRUE if pmid is the primary parent of mid */ | |
| 1355 | +){ | |
| 1348 | 1356 | Blob otherContent; |
| 1349 | 1357 | int otherRid; |
| 1350 | 1358 | int i, rc; |
| 1351 | 1359 | ManifestFile *pChildFile, *pParentFile; |
| 1352 | 1360 | Manifest **ppOther; |
| 1353 | 1361 | static Stmt eq; |
| 1354 | 1362 | int isPublic; /* True if pChild is non-private */ |
| 1355 | 1363 | |
| 1356 | - /* If mlink table entires are already set for cid, then abort early | |
| 1357 | - ** doing no work. | |
| 1364 | + /* If mlink table entires are already exist for the pmid-to-mid transition, | |
| 1365 | + ** then abort early doing no work. | |
| 1358 | 1366 | */ |
| 1359 | - db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid"); | |
| 1360 | - db_bind_int(&eq, ":mid", cid); | |
| 1367 | + db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid AND pmid=:pmid"); | |
| 1368 | + db_bind_int(&eq, ":mid", mid); | |
| 1369 | + db_bind_int(&eq, ":pmid", pmid); | |
| 1361 | 1370 | rc = db_step(&eq); |
| 1362 | 1371 | db_reset(&eq); |
| 1363 | 1372 | if( rc==SQLITE_ROW ) return; |
| 1364 | 1373 | |
| 1365 | 1374 | /* Compute the value of the missing pParent or pChild parameter. |
| @@ -1366,14 +1375,14 @@ | ||
| 1366 | 1375 | ** Fetch the baseline checkins for both. |
| 1367 | 1376 | */ |
| 1368 | 1377 | assert( pParent==0 || pChild==0 ); |
| 1369 | 1378 | if( pParent==0 ){ |
| 1370 | 1379 | ppOther = &pParent; |
| 1371 | - otherRid = pid; | |
| 1380 | + otherRid = pmid; | |
| 1372 | 1381 | }else{ |
| 1373 | 1382 | ppOther = &pChild; |
| 1374 | - otherRid = cid; | |
| 1383 | + otherRid = mid; | |
| 1375 | 1384 | } |
| 1376 | 1385 | if( (*ppOther = manifest_cache_find(otherRid))==0 ){ |
| 1377 | 1386 | content_get(otherRid, &otherContent); |
| 1378 | 1387 | if( blob_size(&otherContent)==0 ) return; |
| 1379 | 1388 | *ppOther = manifest_parse(&otherContent, otherRid, 0); |
| @@ -1381,20 +1390,20 @@ | ||
| 1381 | 1390 | } |
| 1382 | 1391 | if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ |
| 1383 | 1392 | manifest_destroy(*ppOther); |
| 1384 | 1393 | return; |
| 1385 | 1394 | } |
| 1386 | - isPublic = !content_is_private(cid); | |
| 1395 | + isPublic = !content_is_private(mid); | |
| 1387 | 1396 | |
| 1388 | 1397 | /* Try to make the parent manifest a delta from the child, if that |
| 1389 | 1398 | ** is an appropriate thing to do. For a new baseline, make the |
| 1390 | 1399 | ** previous baseline a delta from the current baseline. |
| 1391 | 1400 | */ |
| 1392 | 1401 | if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){ |
| 1393 | - content_deltify(pid, cid, 0); | |
| 1402 | + content_deltify(pmid, mid, 0); | |
| 1394 | 1403 | }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ |
| 1395 | - content_deltify(pParent->pBaseline->rid, cid, 0); | |
| 1404 | + content_deltify(pParent->pBaseline->rid, mid, 0); | |
| 1396 | 1405 | } |
| 1397 | 1406 | |
| 1398 | 1407 | /* Remember all children less than a few seconds younger than their parent, |
| 1399 | 1408 | ** as we might want to fudge the times for those children. |
| 1400 | 1409 | */ |
| @@ -1414,31 +1423,32 @@ | ||
| 1414 | 1423 | int mperm = manifest_file_mperm(pChildFile); |
| 1415 | 1424 | if( pChildFile->zPrior ){ |
| 1416 | 1425 | pParentFile = manifest_file_seek(pParent, pChildFile->zPrior, 0); |
| 1417 | 1426 | if( pParentFile ){ |
| 1418 | 1427 | /* File with name change */ |
| 1419 | - add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, | |
| 1420 | - pChildFile->zName, pChildFile->zPrior, isPublic, mperm); | |
| 1428 | + add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid, | |
| 1429 | + pChildFile->zName, pChildFile->zPrior, | |
| 1430 | + isPublic, isPrim, mperm); | |
| 1421 | 1431 | }else{ |
| 1422 | 1432 | /* File name changed, but the old name is not found in the parent! |
| 1423 | 1433 | ** Treat this like a new file. */ |
| 1424 | - add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1425 | - isPublic, mperm); | |
| 1434 | + add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1435 | + isPublic, isPrim, mperm); | |
| 1426 | 1436 | } |
| 1427 | 1437 | }else{ |
| 1428 | 1438 | pParentFile = manifest_file_seek(pParent, pChildFile->zName, 0); |
| 1429 | 1439 | if( pParentFile==0 ){ |
| 1430 | 1440 | if( pChildFile->zUuid ){ |
| 1431 | 1441 | /* A new file */ |
| 1432 | - add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1433 | - isPublic, mperm); | |
| 1442 | + add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1443 | + isPublic, isPrim, mperm); | |
| 1434 | 1444 | } |
| 1435 | 1445 | }else if( fossil_strcmp(pChildFile->zUuid, pParentFile->zUuid)!=0 |
| 1436 | 1446 | || manifest_file_mperm(pParentFile)!=mperm ){ |
| 1437 | 1447 | /* Changes in file content or permissions */ |
| 1438 | - add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, | |
| 1439 | - pChildFile->zName, 0, isPublic, mperm); | |
| 1448 | + add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid, | |
| 1449 | + pChildFile->zName, 0, isPublic, isPrim, mperm); | |
| 1440 | 1450 | } |
| 1441 | 1451 | } |
| 1442 | 1452 | } |
| 1443 | 1453 | if( pParent->zBaseline && pChild->zBaseline ){ |
| 1444 | 1454 | /* Both parent and child are delta manifests. Look for files that |
| @@ -1450,22 +1460,22 @@ | ||
| 1450 | 1460 | pChildFile = manifest_file_seek_base(pChild, pParentFile->zName, 0); |
| 1451 | 1461 | if( pChildFile==0 ){ |
| 1452 | 1462 | /* The child file reverts to baseline. Show this as a change */ |
| 1453 | 1463 | pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); |
| 1454 | 1464 | if( pChildFile ){ |
| 1455 | - add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, | |
| 1456 | - pChildFile->zName, 0, isPublic, | |
| 1465 | + add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid, | |
| 1466 | + pChildFile->zName, 0, isPublic, isPrim, | |
| 1457 | 1467 | manifest_file_mperm(pChildFile)); |
| 1458 | 1468 | } |
| 1459 | 1469 | } |
| 1460 | 1470 | }else{ |
| 1461 | 1471 | pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); |
| 1462 | 1472 | if( pChildFile ){ |
| 1463 | 1473 | /* File resurrected in the child after having been deleted in |
| 1464 | 1474 | ** the parent. Show this as an added file. */ |
| 1465 | - add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1466 | - isPublic, manifest_file_mperm(pChildFile)); | |
| 1475 | + add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0, | |
| 1476 | + isPublic, isPrim, manifest_file_mperm(pChildFile)); | |
| 1467 | 1477 | } |
| 1468 | 1478 | } |
| 1469 | 1479 | } |
| 1470 | 1480 | }else if( pChild->zBaseline==0 ){ |
| 1471 | 1481 | /* pChild is a baseline. Look for files that are present in pParent |
| @@ -1472,12 +1482,12 @@ | ||
| 1472 | 1482 | ** but are missing from pChild and mark them as having been deleted. */ |
| 1473 | 1483 | manifest_file_rewind(pParent); |
| 1474 | 1484 | while( (pParentFile = manifest_file_next(pParent,0))!=0 ){ |
| 1475 | 1485 | pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); |
| 1476 | 1486 | if( pChildFile==0 && pParentFile->zUuid!=0 ){ |
| 1477 | - add_one_mlink(cid, pParentFile->zUuid, 0, pParentFile->zName, 0, | |
| 1478 | - isPublic, 0); | |
| 1487 | + add_one_mlink(pmid, pParentFile->zUuid, mid, 0, pParentFile->zName, 0, | |
| 1488 | + isPublic, isPrim, 0); | |
| 1479 | 1489 | } |
| 1480 | 1490 | } |
| 1481 | 1491 | } |
| 1482 | 1492 | manifest_cache_insert(*ppOther); |
| 1483 | 1493 | } |
| @@ -1781,45 +1791,46 @@ | ||
| 1781 | 1791 | sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d", |
| 1782 | 1792 | uuid_to_rid(p->zBaseline,1)); |
| 1783 | 1793 | }else{ |
| 1784 | 1794 | sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL"); |
| 1785 | 1795 | } |
| 1786 | - (void)db_schema_is_outofdate(); /* Make sure g.zAuxSchema is initialized */ | |
| 1787 | 1796 | for(i=0; i<p->nParent; i++){ |
| 1788 | 1797 | int pid = uuid_to_rid(p->azParent[i], 1); |
| 1789 | - if( strcmp(g.zAuxSchema,"2014-11-24 20:35")>=0 ){ | |
| 1790 | - /* Support for PLINK.BASEID added on 2014-11-24 */ | |
| 1791 | - db_multi_exec( | |
| 1792 | - "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)" | |
| 1793 | - "VALUES(%d, %d, %d, %.17g, %s)", | |
| 1794 | - pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/); | |
| 1795 | - }else{ | |
| 1796 | - /* Continue to work with older schema to avoid an unnecessary | |
| 1797 | - ** rebuild */ | |
| 1798 | - db_multi_exec( | |
| 1799 | - "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" | |
| 1800 | - "VALUES(%d, %d, %d, %.17g)", | |
| 1801 | - pid, rid, i==0, p->rDate); | |
| 1802 | - } | |
| 1803 | - if( i==0 ){ | |
| 1804 | - add_mlink(pid, 0, rid, p); | |
| 1805 | - parentid = pid; | |
| 1806 | - } | |
| 1807 | - } | |
| 1808 | - db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid); | |
| 1798 | + db_multi_exec( | |
| 1799 | + "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)" | |
| 1800 | + "VALUES(%d, %d, %d, %.17g, %s)", | |
| 1801 | + pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/); | |
| 1802 | + add_mlink(pid, 0, rid, p, i==0); | |
| 1803 | + if( i==0 ) parentid = pid; | |
| 1804 | + } | |
| 1805 | + if( p->nParent>1 ){ | |
| 1806 | + /* Remove incorrect MLINK create-file entries that arise when a | |
| 1807 | + ** file is added by merge. */ | |
| 1808 | + db_multi_exec( | |
| 1809 | + "DELETE FROM mlink" | |
| 1810 | + " WHERE mid=%d" | |
| 1811 | + " AND pid=0" | |
| 1812 | + " AND fnid IN " | |
| 1813 | + " (SELECT fnid FROM mlink WHERE mid=%d GROUP BY fnid" | |
| 1814 | + " HAVING count(*)<%d)", | |
| 1815 | + rid, rid, p->nParent | |
| 1816 | + ); | |
| 1817 | + } | |
| 1818 | + db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid); | |
| 1809 | 1819 | while( db_step(&q)==SQLITE_ROW ){ |
| 1810 | 1820 | int cid = db_column_int(&q, 0); |
| 1811 | - add_mlink(rid, p, cid, 0); | |
| 1821 | + int isprim = db_column_int(&q, 1); | |
| 1822 | + add_mlink(rid, p, cid, 0, isprim); | |
| 1812 | 1823 | } |
| 1813 | 1824 | db_finalize(&q); |
| 1814 | 1825 | if( p->nParent==0 ){ |
| 1815 | 1826 | /* For root files (files without parents) add mlink entries |
| 1816 | 1827 | ** showing all content as new. */ |
| 1817 | 1828 | int isPublic = !content_is_private(rid); |
| 1818 | 1829 | for(i=0; i<p->nFile; i++){ |
| 1819 | - add_one_mlink(rid, 0, p->aFile[i].zUuid, p->aFile[i].zName, 0, | |
| 1820 | - isPublic, manifest_file_mperm(&p->aFile[i])); | |
| 1830 | + add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0, | |
| 1831 | + isPublic, 1, manifest_file_mperm(&p->aFile[i])); | |
| 1821 | 1832 | } |
| 1822 | 1833 | } |
| 1823 | 1834 | db_multi_exec( |
| 1824 | 1835 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1825 | 1836 | "bgcolor,euser,ecomment,omtime)" |
| 1826 | 1837 | |
| 1827 | 1838 | ADDED src/markdown.md |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -1187,16 +1187,18 @@ | |
| 1187 | /* |
| 1188 | ** Add a single entry to the mlink table. Also add the filename to |
| 1189 | ** the filename table if it is not there already. |
| 1190 | */ |
| 1191 | static void add_one_mlink( |
| 1192 | int mid, /* The record ID of the manifest */ |
| 1193 | const char *zFromUuid, /* UUID for the mlink.pid. "" to add file */ |
| 1194 | const char *zToUuid, /* UUID for the mlink.fid. "" to delete */ |
| 1195 | const char *zFilename, /* Filename */ |
| 1196 | const char *zPrior, /* Previous filename. NULL if unchanged */ |
| 1197 | int isPublic, /* True if mid is not a private manifest */ |
| 1198 | int mperm /* 1: exec, 2: symlink */ |
| 1199 | ){ |
| 1200 | int fnid, pfnid, pid, fid; |
| 1201 | static Stmt s1; |
| 1202 | |
| @@ -1216,19 +1218,21 @@ | |
| 1216 | }else{ |
| 1217 | fid = uuid_to_rid(zToUuid, 1); |
| 1218 | if( isPublic ) content_make_public(fid); |
| 1219 | } |
| 1220 | db_static_prepare(&s1, |
| 1221 | "INSERT INTO mlink(mid,pid,fid,fnid,pfnid,mperm)" |
| 1222 | "VALUES(:m,:p,:f,:n,:pfn,:mp)" |
| 1223 | ); |
| 1224 | db_bind_int(&s1, ":m", mid); |
| 1225 | db_bind_int(&s1, ":p", pid); |
| 1226 | db_bind_int(&s1, ":f", fid); |
| 1227 | db_bind_int(&s1, ":n", fnid); |
| 1228 | db_bind_int(&s1, ":pfn", pfnid); |
| 1229 | db_bind_int(&s1, ":mp", mperm); |
| 1230 | db_exec(&s1); |
| 1231 | if( pid && fid ){ |
| 1232 | content_deltify(pid, fid, 0); |
| 1233 | } |
| 1234 | } |
| @@ -1342,24 +1346,29 @@ | |
| 1342 | ** |
| 1343 | ** Deleted files have mlink.fid=0. |
| 1344 | ** Added files have mlink.pid=0. |
| 1345 | ** Edited files have both mlink.pid!=0 and mlink.fid!=0 |
| 1346 | */ |
| 1347 | static void add_mlink(int pid, Manifest *pParent, int cid, Manifest *pChild){ |
| 1348 | Blob otherContent; |
| 1349 | int otherRid; |
| 1350 | int i, rc; |
| 1351 | ManifestFile *pChildFile, *pParentFile; |
| 1352 | Manifest **ppOther; |
| 1353 | static Stmt eq; |
| 1354 | int isPublic; /* True if pChild is non-private */ |
| 1355 | |
| 1356 | /* If mlink table entires are already set for cid, then abort early |
| 1357 | ** doing no work. |
| 1358 | */ |
| 1359 | db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid"); |
| 1360 | db_bind_int(&eq, ":mid", cid); |
| 1361 | rc = db_step(&eq); |
| 1362 | db_reset(&eq); |
| 1363 | if( rc==SQLITE_ROW ) return; |
| 1364 | |
| 1365 | /* Compute the value of the missing pParent or pChild parameter. |
| @@ -1366,14 +1375,14 @@ | |
| 1366 | ** Fetch the baseline checkins for both. |
| 1367 | */ |
| 1368 | assert( pParent==0 || pChild==0 ); |
| 1369 | if( pParent==0 ){ |
| 1370 | ppOther = &pParent; |
| 1371 | otherRid = pid; |
| 1372 | }else{ |
| 1373 | ppOther = &pChild; |
| 1374 | otherRid = cid; |
| 1375 | } |
| 1376 | if( (*ppOther = manifest_cache_find(otherRid))==0 ){ |
| 1377 | content_get(otherRid, &otherContent); |
| 1378 | if( blob_size(&otherContent)==0 ) return; |
| 1379 | *ppOther = manifest_parse(&otherContent, otherRid, 0); |
| @@ -1381,20 +1390,20 @@ | |
| 1381 | } |
| 1382 | if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ |
| 1383 | manifest_destroy(*ppOther); |
| 1384 | return; |
| 1385 | } |
| 1386 | isPublic = !content_is_private(cid); |
| 1387 | |
| 1388 | /* Try to make the parent manifest a delta from the child, if that |
| 1389 | ** is an appropriate thing to do. For a new baseline, make the |
| 1390 | ** previous baseline a delta from the current baseline. |
| 1391 | */ |
| 1392 | if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){ |
| 1393 | content_deltify(pid, cid, 0); |
| 1394 | }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ |
| 1395 | content_deltify(pParent->pBaseline->rid, cid, 0); |
| 1396 | } |
| 1397 | |
| 1398 | /* Remember all children less than a few seconds younger than their parent, |
| 1399 | ** as we might want to fudge the times for those children. |
| 1400 | */ |
| @@ -1414,31 +1423,32 @@ | |
| 1414 | int mperm = manifest_file_mperm(pChildFile); |
| 1415 | if( pChildFile->zPrior ){ |
| 1416 | pParentFile = manifest_file_seek(pParent, pChildFile->zPrior, 0); |
| 1417 | if( pParentFile ){ |
| 1418 | /* File with name change */ |
| 1419 | add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, |
| 1420 | pChildFile->zName, pChildFile->zPrior, isPublic, mperm); |
| 1421 | }else{ |
| 1422 | /* File name changed, but the old name is not found in the parent! |
| 1423 | ** Treat this like a new file. */ |
| 1424 | add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, |
| 1425 | isPublic, mperm); |
| 1426 | } |
| 1427 | }else{ |
| 1428 | pParentFile = manifest_file_seek(pParent, pChildFile->zName, 0); |
| 1429 | if( pParentFile==0 ){ |
| 1430 | if( pChildFile->zUuid ){ |
| 1431 | /* A new file */ |
| 1432 | add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, |
| 1433 | isPublic, mperm); |
| 1434 | } |
| 1435 | }else if( fossil_strcmp(pChildFile->zUuid, pParentFile->zUuid)!=0 |
| 1436 | || manifest_file_mperm(pParentFile)!=mperm ){ |
| 1437 | /* Changes in file content or permissions */ |
| 1438 | add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, |
| 1439 | pChildFile->zName, 0, isPublic, mperm); |
| 1440 | } |
| 1441 | } |
| 1442 | } |
| 1443 | if( pParent->zBaseline && pChild->zBaseline ){ |
| 1444 | /* Both parent and child are delta manifests. Look for files that |
| @@ -1450,22 +1460,22 @@ | |
| 1450 | pChildFile = manifest_file_seek_base(pChild, pParentFile->zName, 0); |
| 1451 | if( pChildFile==0 ){ |
| 1452 | /* The child file reverts to baseline. Show this as a change */ |
| 1453 | pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); |
| 1454 | if( pChildFile ){ |
| 1455 | add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, |
| 1456 | pChildFile->zName, 0, isPublic, |
| 1457 | manifest_file_mperm(pChildFile)); |
| 1458 | } |
| 1459 | } |
| 1460 | }else{ |
| 1461 | pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); |
| 1462 | if( pChildFile ){ |
| 1463 | /* File resurrected in the child after having been deleted in |
| 1464 | ** the parent. Show this as an added file. */ |
| 1465 | add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0, |
| 1466 | isPublic, manifest_file_mperm(pChildFile)); |
| 1467 | } |
| 1468 | } |
| 1469 | } |
| 1470 | }else if( pChild->zBaseline==0 ){ |
| 1471 | /* pChild is a baseline. Look for files that are present in pParent |
| @@ -1472,12 +1482,12 @@ | |
| 1472 | ** but are missing from pChild and mark them as having been deleted. */ |
| 1473 | manifest_file_rewind(pParent); |
| 1474 | while( (pParentFile = manifest_file_next(pParent,0))!=0 ){ |
| 1475 | pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); |
| 1476 | if( pChildFile==0 && pParentFile->zUuid!=0 ){ |
| 1477 | add_one_mlink(cid, pParentFile->zUuid, 0, pParentFile->zName, 0, |
| 1478 | isPublic, 0); |
| 1479 | } |
| 1480 | } |
| 1481 | } |
| 1482 | manifest_cache_insert(*ppOther); |
| 1483 | } |
| @@ -1781,45 +1791,46 @@ | |
| 1781 | sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d", |
| 1782 | uuid_to_rid(p->zBaseline,1)); |
| 1783 | }else{ |
| 1784 | sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL"); |
| 1785 | } |
| 1786 | (void)db_schema_is_outofdate(); /* Make sure g.zAuxSchema is initialized */ |
| 1787 | for(i=0; i<p->nParent; i++){ |
| 1788 | int pid = uuid_to_rid(p->azParent[i], 1); |
| 1789 | if( strcmp(g.zAuxSchema,"2014-11-24 20:35")>=0 ){ |
| 1790 | /* Support for PLINK.BASEID added on 2014-11-24 */ |
| 1791 | db_multi_exec( |
| 1792 | "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)" |
| 1793 | "VALUES(%d, %d, %d, %.17g, %s)", |
| 1794 | pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/); |
| 1795 | }else{ |
| 1796 | /* Continue to work with older schema to avoid an unnecessary |
| 1797 | ** rebuild */ |
| 1798 | db_multi_exec( |
| 1799 | "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime)" |
| 1800 | "VALUES(%d, %d, %d, %.17g)", |
| 1801 | pid, rid, i==0, p->rDate); |
| 1802 | } |
| 1803 | if( i==0 ){ |
| 1804 | add_mlink(pid, 0, rid, p); |
| 1805 | parentid = pid; |
| 1806 | } |
| 1807 | } |
| 1808 | db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid); |
| 1809 | while( db_step(&q)==SQLITE_ROW ){ |
| 1810 | int cid = db_column_int(&q, 0); |
| 1811 | add_mlink(rid, p, cid, 0); |
| 1812 | } |
| 1813 | db_finalize(&q); |
| 1814 | if( p->nParent==0 ){ |
| 1815 | /* For root files (files without parents) add mlink entries |
| 1816 | ** showing all content as new. */ |
| 1817 | int isPublic = !content_is_private(rid); |
| 1818 | for(i=0; i<p->nFile; i++){ |
| 1819 | add_one_mlink(rid, 0, p->aFile[i].zUuid, p->aFile[i].zName, 0, |
| 1820 | isPublic, manifest_file_mperm(&p->aFile[i])); |
| 1821 | } |
| 1822 | } |
| 1823 | db_multi_exec( |
| 1824 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1825 | "bgcolor,euser,ecomment,omtime)" |
| 1826 | |
| 1827 | DDED src/markdown.md |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -1187,16 +1187,18 @@ | |
| 1187 | /* |
| 1188 | ** Add a single entry to the mlink table. Also add the filename to |
| 1189 | ** the filename table if it is not there already. |
| 1190 | */ |
| 1191 | static void add_one_mlink( |
| 1192 | int pmid, /* The parent manifest */ |
| 1193 | const char *zFromUuid, /* UUID for content in parent */ |
| 1194 | int mid, /* The record ID of the manifest */ |
| 1195 | const char *zToUuid, /* UUID for content in child */ |
| 1196 | const char *zFilename, /* Filename */ |
| 1197 | const char *zPrior, /* Previous filename. NULL if unchanged */ |
| 1198 | int isPublic, /* True if mid is not a private manifest */ |
| 1199 | int isPrimary, /* pmid is the primary parent of mid */ |
| 1200 | int mperm /* 1: exec, 2: symlink */ |
| 1201 | ){ |
| 1202 | int fnid, pfnid, pid, fid; |
| 1203 | static Stmt s1; |
| 1204 | |
| @@ -1216,19 +1218,21 @@ | |
| 1218 | }else{ |
| 1219 | fid = uuid_to_rid(zToUuid, 1); |
| 1220 | if( isPublic ) content_make_public(fid); |
| 1221 | } |
| 1222 | db_static_prepare(&s1, |
| 1223 | "INSERT INTO mlink(mid,fid,pmid,pid,fnid,pfnid,mperm,isaux)" |
| 1224 | "VALUES(:m,:f,:pm,:p,:n,:pfn,:mp,:isaux)" |
| 1225 | ); |
| 1226 | db_bind_int(&s1, ":m", mid); |
| 1227 | db_bind_int(&s1, ":f", fid); |
| 1228 | db_bind_int(&s1, ":pm", pmid); |
| 1229 | db_bind_int(&s1, ":p", pid); |
| 1230 | db_bind_int(&s1, ":n", fnid); |
| 1231 | db_bind_int(&s1, ":pfn", pfnid); |
| 1232 | db_bind_int(&s1, ":mp", mperm); |
| 1233 | db_bind_int(&s1, ":isaux", isPrimary==0); |
| 1234 | db_exec(&s1); |
| 1235 | if( pid && fid ){ |
| 1236 | content_deltify(pid, fid, 0); |
| 1237 | } |
| 1238 | } |
| @@ -1342,24 +1346,29 @@ | |
| 1346 | ** |
| 1347 | ** Deleted files have mlink.fid=0. |
| 1348 | ** Added files have mlink.pid=0. |
| 1349 | ** Edited files have both mlink.pid!=0 and mlink.fid!=0 |
| 1350 | */ |
| 1351 | static void add_mlink( |
| 1352 | int pmid, Manifest *pParent, /* Parent check-in */ |
| 1353 | int mid, Manifest *pChild, /* The child check-in */ |
| 1354 | int isPrim /* TRUE if pmid is the primary parent of mid */ |
| 1355 | ){ |
| 1356 | Blob otherContent; |
| 1357 | int otherRid; |
| 1358 | int i, rc; |
| 1359 | ManifestFile *pChildFile, *pParentFile; |
| 1360 | Manifest **ppOther; |
| 1361 | static Stmt eq; |
| 1362 | int isPublic; /* True if pChild is non-private */ |
| 1363 | |
| 1364 | /* If mlink table entires are already exist for the pmid-to-mid transition, |
| 1365 | ** then abort early doing no work. |
| 1366 | */ |
| 1367 | db_static_prepare(&eq, "SELECT 1 FROM mlink WHERE mid=:mid AND pmid=:pmid"); |
| 1368 | db_bind_int(&eq, ":mid", mid); |
| 1369 | db_bind_int(&eq, ":pmid", pmid); |
| 1370 | rc = db_step(&eq); |
| 1371 | db_reset(&eq); |
| 1372 | if( rc==SQLITE_ROW ) return; |
| 1373 | |
| 1374 | /* Compute the value of the missing pParent or pChild parameter. |
| @@ -1366,14 +1375,14 @@ | |
| 1375 | ** Fetch the baseline checkins for both. |
| 1376 | */ |
| 1377 | assert( pParent==0 || pChild==0 ); |
| 1378 | if( pParent==0 ){ |
| 1379 | ppOther = &pParent; |
| 1380 | otherRid = pmid; |
| 1381 | }else{ |
| 1382 | ppOther = &pChild; |
| 1383 | otherRid = mid; |
| 1384 | } |
| 1385 | if( (*ppOther = manifest_cache_find(otherRid))==0 ){ |
| 1386 | content_get(otherRid, &otherContent); |
| 1387 | if( blob_size(&otherContent)==0 ) return; |
| 1388 | *ppOther = manifest_parse(&otherContent, otherRid, 0); |
| @@ -1381,20 +1390,20 @@ | |
| 1390 | } |
| 1391 | if( fetch_baseline(pParent, 0) || fetch_baseline(pChild, 0) ){ |
| 1392 | manifest_destroy(*ppOther); |
| 1393 | return; |
| 1394 | } |
| 1395 | isPublic = !content_is_private(mid); |
| 1396 | |
| 1397 | /* Try to make the parent manifest a delta from the child, if that |
| 1398 | ** is an appropriate thing to do. For a new baseline, make the |
| 1399 | ** previous baseline a delta from the current baseline. |
| 1400 | */ |
| 1401 | if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){ |
| 1402 | content_deltify(pmid, mid, 0); |
| 1403 | }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ |
| 1404 | content_deltify(pParent->pBaseline->rid, mid, 0); |
| 1405 | } |
| 1406 | |
| 1407 | /* Remember all children less than a few seconds younger than their parent, |
| 1408 | ** as we might want to fudge the times for those children. |
| 1409 | */ |
| @@ -1414,31 +1423,32 @@ | |
| 1423 | int mperm = manifest_file_mperm(pChildFile); |
| 1424 | if( pChildFile->zPrior ){ |
| 1425 | pParentFile = manifest_file_seek(pParent, pChildFile->zPrior, 0); |
| 1426 | if( pParentFile ){ |
| 1427 | /* File with name change */ |
| 1428 | add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid, |
| 1429 | pChildFile->zName, pChildFile->zPrior, |
| 1430 | isPublic, isPrim, mperm); |
| 1431 | }else{ |
| 1432 | /* File name changed, but the old name is not found in the parent! |
| 1433 | ** Treat this like a new file. */ |
| 1434 | add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0, |
| 1435 | isPublic, isPrim, mperm); |
| 1436 | } |
| 1437 | }else{ |
| 1438 | pParentFile = manifest_file_seek(pParent, pChildFile->zName, 0); |
| 1439 | if( pParentFile==0 ){ |
| 1440 | if( pChildFile->zUuid ){ |
| 1441 | /* A new file */ |
| 1442 | add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0, |
| 1443 | isPublic, isPrim, mperm); |
| 1444 | } |
| 1445 | }else if( fossil_strcmp(pChildFile->zUuid, pParentFile->zUuid)!=0 |
| 1446 | || manifest_file_mperm(pParentFile)!=mperm ){ |
| 1447 | /* Changes in file content or permissions */ |
| 1448 | add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid, |
| 1449 | pChildFile->zName, 0, isPublic, isPrim, mperm); |
| 1450 | } |
| 1451 | } |
| 1452 | } |
| 1453 | if( pParent->zBaseline && pChild->zBaseline ){ |
| 1454 | /* Both parent and child are delta manifests. Look for files that |
| @@ -1450,22 +1460,22 @@ | |
| 1460 | pChildFile = manifest_file_seek_base(pChild, pParentFile->zName, 0); |
| 1461 | if( pChildFile==0 ){ |
| 1462 | /* The child file reverts to baseline. Show this as a change */ |
| 1463 | pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); |
| 1464 | if( pChildFile ){ |
| 1465 | add_one_mlink(pmid, pParentFile->zUuid, mid, pChildFile->zUuid, |
| 1466 | pChildFile->zName, 0, isPublic, isPrim, |
| 1467 | manifest_file_mperm(pChildFile)); |
| 1468 | } |
| 1469 | } |
| 1470 | }else{ |
| 1471 | pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); |
| 1472 | if( pChildFile ){ |
| 1473 | /* File resurrected in the child after having been deleted in |
| 1474 | ** the parent. Show this as an added file. */ |
| 1475 | add_one_mlink(pmid, 0, mid, pChildFile->zUuid, pChildFile->zName, 0, |
| 1476 | isPublic, isPrim, manifest_file_mperm(pChildFile)); |
| 1477 | } |
| 1478 | } |
| 1479 | } |
| 1480 | }else if( pChild->zBaseline==0 ){ |
| 1481 | /* pChild is a baseline. Look for files that are present in pParent |
| @@ -1472,12 +1482,12 @@ | |
| 1482 | ** but are missing from pChild and mark them as having been deleted. */ |
| 1483 | manifest_file_rewind(pParent); |
| 1484 | while( (pParentFile = manifest_file_next(pParent,0))!=0 ){ |
| 1485 | pChildFile = manifest_file_seek(pChild, pParentFile->zName, 0); |
| 1486 | if( pChildFile==0 && pParentFile->zUuid!=0 ){ |
| 1487 | add_one_mlink(pmid, pParentFile->zUuid, mid, 0, pParentFile->zName, 0, |
| 1488 | isPublic, isPrim, 0); |
| 1489 | } |
| 1490 | } |
| 1491 | } |
| 1492 | manifest_cache_insert(*ppOther); |
| 1493 | } |
| @@ -1781,45 +1791,46 @@ | |
| 1791 | sqlite3_snprintf(sizeof(zBaseId), zBaseId, "%d", |
| 1792 | uuid_to_rid(p->zBaseline,1)); |
| 1793 | }else{ |
| 1794 | sqlite3_snprintf(sizeof(zBaseId), zBaseId, "NULL"); |
| 1795 | } |
| 1796 | for(i=0; i<p->nParent; i++){ |
| 1797 | int pid = uuid_to_rid(p->azParent[i], 1); |
| 1798 | db_multi_exec( |
| 1799 | "INSERT OR IGNORE INTO plink(pid, cid, isprim, mtime, baseid)" |
| 1800 | "VALUES(%d, %d, %d, %.17g, %s)", |
| 1801 | pid, rid, i==0, p->rDate, zBaseId/*safe-for-%s*/); |
| 1802 | add_mlink(pid, 0, rid, p, i==0); |
| 1803 | if( i==0 ) parentid = pid; |
| 1804 | } |
| 1805 | if( p->nParent>1 ){ |
| 1806 | /* Remove incorrect MLINK create-file entries that arise when a |
| 1807 | ** file is added by merge. */ |
| 1808 | db_multi_exec( |
| 1809 | "DELETE FROM mlink" |
| 1810 | " WHERE mid=%d" |
| 1811 | " AND pid=0" |
| 1812 | " AND fnid IN " |
| 1813 | " (SELECT fnid FROM mlink WHERE mid=%d GROUP BY fnid" |
| 1814 | " HAVING count(*)<%d)", |
| 1815 | rid, rid, p->nParent |
| 1816 | ); |
| 1817 | } |
| 1818 | db_prepare(&q, "SELECT cid, isprim FROM plink WHERE pid=%d", rid); |
| 1819 | while( db_step(&q)==SQLITE_ROW ){ |
| 1820 | int cid = db_column_int(&q, 0); |
| 1821 | int isprim = db_column_int(&q, 1); |
| 1822 | add_mlink(rid, p, cid, 0, isprim); |
| 1823 | } |
| 1824 | db_finalize(&q); |
| 1825 | if( p->nParent==0 ){ |
| 1826 | /* For root files (files without parents) add mlink entries |
| 1827 | ** showing all content as new. */ |
| 1828 | int isPublic = !content_is_private(rid); |
| 1829 | for(i=0; i<p->nFile; i++){ |
| 1830 | add_one_mlink(0, 0, rid, p->aFile[i].zUuid, p->aFile[i].zName, 0, |
| 1831 | isPublic, 1, manifest_file_mperm(&p->aFile[i])); |
| 1832 | } |
| 1833 | } |
| 1834 | db_multi_exec( |
| 1835 | "REPLACE INTO event(type,mtime,objid,user,comment," |
| 1836 | "bgcolor,euser,ecomment,omtime)" |
| 1837 | |
| 1838 | DDED src/markdown.md |
+224
| --- a/src/markdown.md | ||
| +++ b/src/markdown.md | ||
| @@ -0,0 +1,224 @@ | ||
| 1 | +# Markdown formatting rules | |
| 2 | + | |
| 3 | +In addition to its native Wiki formatting syntax, Fossil supports Mark down syntax as specified by | |
| 4 | +[John Gruber's original Markdown implementation](http://daringfireba | |
| 5 | +[syntax description](http://daringfireball.net/projects/markdown/syntax), of which the page you | |
| 6 | +are reading is an extract. | |
| 7 | + | |
| 8 | +This page itself uses Markdown formatting. | |
| 9 | + | |
| 10 | +## Summary | |
| 11 | + | |
| 12 | + - Block elements | |
| 13 | + | |
| 14 | + * A **paragraph** is a group of consecutive lines. Paragraphs are separated by blank lines. | |
| 15 | + | |
| 16 | + * A **Header** is a line of text underlined with equal signs or hyphens, or prefixed by a | |
| 17 | + in s, or prefixed by a | |
| 18 | + number of hash marks. | |
| 19 | + | |
| 20 | + * **Block quotes** are blocks of text prefixed by '>'. | |
| 21 | + | |
| 22 | + * **Ordered list** items are prefixed by a number and a period. **Unordered list** items | |
| 23 | + are prefixed by a hyphen, asterisk or plus sign. Pre | |
| 24 | + | |
| 25 | + * **Code blocks** are formed by lines of text (possibly including empty lines) prefixed by | |
| 26 | + at least 4 spaces or a tab. | |
| 27 | + | |
| 28 | + * A **horizontal rule** is a line consisting of 3 or more asterisks, hyphens or underscores, | |
| 29 | + with optional whitespace between them. | |
| 30 | + | |
| 31 | + - Span elements | |
| 32 | + | |
| 33 | + * 3 types of **links** exist: | |
| 34 | + | |
| 35 | + - **automatic links** are URLs or email addresses enclosed in angle brackets | |
| 36 | + ('<' and '>'), and are displayed as such. | |
| 37 | + | |
| 38 | + - **inline links** consist of the displayed link text in squ | |
| 39 | + followed by the | |
| 40 | + | |
| 41 | + - **reference links** separate _link instance_ from _link definition_. A link instance efinition_. A link instance | |
| 42 | + consists of the displayed link text | |
| 43 | + in square brackets. in square brackets. | |
| 44 | + The corresponding link definition can occur anywhere on the page, and consists | |
| 45 | + of the link definition name in s a colon, whitespace and the | |
| 46 | + link target. | |
| 47 | + | |
| 48 | + * **Emphasis** can be given by wrapping text in one or two asterisks or underscores - use | |
| 49 | + one for HTML `<em>`, and two for `<strong>` emphasis. | |
| 50 | + | |
| 51 | + * A **code span** is text wrapped in backticks ('`'). | |
| 52 | + | |
| 53 | + * **Images** use a syntax much like inline or reference links, # Markdown formatting rules | |
| 54 | + | |
| 55 | +In addition to its native Wiki formatting syntax, Fossil supports Markdown syntax as specified by | |
| 56 | +[John Gruber's original Markdown implementation](http://daringfireball.net/projects/markdown/). | |
| 57 | +For lots of examples - not repeated here - please refer to its | |
| 58 | +[syntax description](http://daringfireball.net/projects/markdown/syntax), of which the page you | |
| 59 | +are reading is an extract. | |
| 60 | + | |
| 61 | +This page itself uses Markdown formatting. | |
| 62 | + | |
| 63 | +## Summary | |
| 64 | + | |
| 65 | + - Block elements | |
| 66 | + | |
| 67 | + * A **paragraph** is a group of consecutive lines. Paragraphs are separated by blank lines. | |
| 68 | + | |
| 69 | + * A **Header** is a line of text underlined with equal signs or hyphens, or prefixed by a | |
| 70 | + number of hash marks. | |
| 71 | + | |
| 72 | + * **Block quotes** are blocks of text prefixed by '>'. | |
| 73 | + | |
| 74 | + * **Ordered list** items are prefixed by a number and a period. **Unordered list** items | |
| 75 | + are prefixed by a hyphen, asterisk or plus sign. Prefix and item text are separated by | |
| 76 | + whitespace. | |
| 77 | + | |
| 78 | + * **Code blocks** are formed by lines of text (possibly including empty lines) prefixed by | |
| 79 | + at least 4 spaces or a tab. | |
| 80 | + | |
| 81 | + * A **horizontal rule** is a line consisting of 3 or more asterisks, hyphens or underscores, | |
| 82 | + with optional whitespace between them. | |
| 83 | + | |
| 84 | + - Span elements | |
| 85 | + | |
| 86 | + * 3 types of **links** exist: | |
| 87 | + | |
| 88 | + - **automatic links** are URLs or email addresses enclosed in angle brackets | |
| 89 | + a * **Emphasis** can be given by wrapping text in one or two asterisks or underscores - use | |
| 90 | + one for HTML `<em>`, and two for `<strong>` emphasis. | |
| 91 | + | |
| 92 | + * A **code span** is text wrapped in backticks ('`'). | |
| 93 | + | |
| 94 | + * **Images** use a syntax much like inline or reference links, but with alt attribute text | |
| 95 | + ('img alt=...') instead of link text, and the first pair of square | |
| 96 | + brackets m prefix; the items will be | |
| 97 | +renumbered during rendering. However, future implementations may | |
| 98 | +for the first item in a list indicates an offset to be used for subsequent items. | |
| 99 | + | |
| 100 | +For list items spanning multiple lines, subsequent lines can be indented using an arbitrary amount | |
| 101 | +of whitespace. | |
| 102 | + | |
| 103 | +List items will be wrapped in HTML `<p>` tags if they are separated by blank lines. | |
| 104 | + | |
| 105 | +A list item may span multiple paragraphs. At least the first line | |
| 106 | +be indented using at least 4 spaces or a tab character. | |
| 107 | + | |
| 108 | +Block quotes within list items must have their '>' delimiters indented using 4 up to 7 spaces. | |
| 109 | + | |
| 110 | +Code blocks within list items need to be indented _twice_, that is, using 8 spaces or 2 tab | |
| 111 | +characters. | |
| 112 | + | |
| 113 | +### Code blocks | |
| 114 | + | |
| 115 | +Lines within a code block are rendered verbatim using HTML `<pre>` and `<code>` tags, except that | |
| 116 | +HTML punctuation characters like '<' and '&' are automatically converted to HTML entities. Thus, | |
| 117 | +there is no need to explicitly escape HTML syntax within a code block. | |
| 118 | + | |
| 119 | +A code block runs until the first non blank line with indent less than 4 spaces or 1 tab character. | |
| 120 | + | |
| 121 | + | |
| 122 | +Regular Markdown syntax is not processed within code blocks. | |
| 123 | + | |
| 124 | +### Links | |
| 125 | + | |
| 126 | +#### Automatic links | |
| 127 | + | |
| 128 | +When rendering automatic links to email addresses, HTML enco | |
| 129 | +prevent some spambots from harvesting. | |
| 130 | + | |
| 131 | +#### Inline links | |
| 132 | + | |
| 133 | +Links to resources on the same server can use relative paths (i.e. can start with a '/'). | |
| 134 | + | |
| 135 | +An optional title for the link (e.g. to have mouseover text in the b rowser) may be given behind | |
| 136 | +the link target but within the parentheses, in single and double quo | |
| 137 | +link target by whitespace. | |
| 138 | +link target by whitespace. | |
| 139 | + | |
| 140 | +# reference link consists of | |
| 141 | +> | |
| 142 | +> - one or more _link instances_ at appropriate locations in the page text | |
| 143 | +> - a single _link definition_ at an arbit | |
| 144 | +> During rendering, each link instance is resolved, and the corresponding definition is | |
| 145 | +> filled in. No separate link definition clauses occu r in the rendered output. | |
| 146 | +> | |
| 147 | +> There are 3 fields involved in link instances and definitions: | |
| 148 | +> | |
| 149 | +> - link text (i.e. the text that is displayed at the resulting link) | |
| 150 | +> - link definition name (i.e. an unique ID binding link instances to link definition) | |
| 151 | +> - link target (a target URL for the link) | |
| 152 | + | |
| 153 | +Multiple link instances may reference the same link definition using its link definition | |
| 154 | +name. | |
| 155 | + | |
| 156 | +Link definition names are case insensitive, and may contain letters, numbers, spaces and | |
| 157 | +punctuation. | |
| 158 | + | |
| 159 | +##### Link instance | |
| 160 | + | |
| 161 | +A space may be inserted between the bracket pairs for link text and link definition name. | |
| 162 | + | |
| 163 | +A link instance can use an _implicit link definition name_ shortcut, in which case the link | |
| 164 | +text is used as the link definition name. The second set of brackets then remains empty, e.g. | |
| 165 | +'[Google][]' ('Google' being used as both link text and link definition name). | |
| 166 | + | |
| 167 | +##### Link definition | |
| 168 | + | |
| 169 | +The first bracket pair containing the link definition name may be indented using up to 3 spaces. | |
| 170 | + | |
| 171 | +The link target may optionally be surrounded by angle brackets ('<' and '>'). | |
| 172 | + | |
| 173 | +A link target may be followed by an optional title (e.g. to have mouseover text in the browser). | |
| 174 | +This title may be enclosed in parentheses, single or double quotes. | |
| 175 | + | |
| 176 | +Link definitions may be split into 2 lines, with the title on the second line, arbitrarily | |
| 177 | +indented. This may be more visually pleasing when using long link targets. | |
| 178 | + | |
| 179 | +### Emphasis | |
| 180 | + | |
| 181 | +The same character(s) used for starting the emphasis must be used to end it; don't mix | |
| 182 | +asterisks and underscores. | |
| 183 | + | |
| 184 | +Emphasis can be used in the middle of a word. That is, there need not be whitespace on either | |
| 185 | +side of emphasis start or end punctuation characters. | |
| 186 | + | |
| 187 | +### Code spans | |
| 188 | + | |
| 189 | +To include a literal backtick character in a code span, use multiple backticks as opening and | |
| 190 | +closing delimiters. | |
| 191 | + | |
| 192 | +Whitespace may exist immediately after the opening delimiter and b Markdown formatting rules | |
| 193 | + | |
| 194 | +In addition to its native Wiki formatting syntax, Fossil supports Markdown syntax as specified by | |
| 195 | +[John Gruber's original Markdown implementation](http://daringfireball.net/projects/markdown/). | |
| 196 | +For lots of examples - not repeated here - please refer to its | |
| 197 | +[syntax description](http://daringfireball.net/projects/markdownr | |
| 198 | +a HTML block level construct (`<div>`, `<table>` etc) must be separated from surrounding | |
| 199 | +context using blank lines, and must both occur at the start of a line. | |
| 200 | + | |
| 201 | +No extra unwanted `<p>` HTML tags are added around HTML block level tags. | |
| 202 | + | |
| 203 | +Markdown formatting within HTML block level tags is not processed; however, formatting within | |
| 204 | +span level tags (e.g. `<mark>`) is processed normally. | |
| 205 | + | |
| 206 | +### Escaping Markdown punctuation | |
| 207 | + | |
| 208 | +The following punctuation characters can be escaped using backslash: | |
| 209 | + | |
| 210 | + - \\ backslash | |
| 211 | + - ` backtick | |
| 212 | + - * asterisk | |
| 213 | + - _ underscore | |
| 214 | + - {} curly braces | |
| 215 | + - [] square brackets | |
| 216 | + - () parentheses | |
| 217 | + - # hash mark | |
| 218 | + - + plus sign | |
| 219 | + - - minus sign (hyphen) | |
| 220 | + - . dot | |
| 221 | + - ! exclamation mark | |
| 222 | + | |
| 223 | +To render a literal backslash, use 2 backslashes ('\\\\'). | |
| 224 | + |
| --- a/src/markdown.md | |
| +++ b/src/markdown.md | |
| @@ -0,0 +1,224 @@ | |
| --- a/src/markdown.md | |
| +++ b/src/markdown.md | |
| @@ -0,0 +1,224 @@ | |
| 1 | # Markdown formatting rules |
| 2 | |
| 3 | In addition to its native Wiki formatting syntax, Fossil supports Mark down syntax as specified by |
| 4 | [John Gruber's original Markdown implementation](http://daringfireba |
| 5 | [syntax description](http://daringfireball.net/projects/markdown/syntax), of which the page you |
| 6 | are reading is an extract. |
| 7 | |
| 8 | This page itself uses Markdown formatting. |
| 9 | |
| 10 | ## Summary |
| 11 | |
| 12 | - Block elements |
| 13 | |
| 14 | * A **paragraph** is a group of consecutive lines. Paragraphs are separated by blank lines. |
| 15 | |
| 16 | * A **Header** is a line of text underlined with equal signs or hyphens, or prefixed by a |
| 17 | in s, or prefixed by a |
| 18 | number of hash marks. |
| 19 | |
| 20 | * **Block quotes** are blocks of text prefixed by '>'. |
| 21 | |
| 22 | * **Ordered list** items are prefixed by a number and a period. **Unordered list** items |
| 23 | are prefixed by a hyphen, asterisk or plus sign. Pre |
| 24 | |
| 25 | * **Code blocks** are formed by lines of text (possibly including empty lines) prefixed by |
| 26 | at least 4 spaces or a tab. |
| 27 | |
| 28 | * A **horizontal rule** is a line consisting of 3 or more asterisks, hyphens or underscores, |
| 29 | with optional whitespace between them. |
| 30 | |
| 31 | - Span elements |
| 32 | |
| 33 | * 3 types of **links** exist: |
| 34 | |
| 35 | - **automatic links** are URLs or email addresses enclosed in angle brackets |
| 36 | ('<' and '>'), and are displayed as such. |
| 37 | |
| 38 | - **inline links** consist of the displayed link text in squ |
| 39 | followed by the |
| 40 | |
| 41 | - **reference links** separate _link instance_ from _link definition_. A link instance efinition_. A link instance |
| 42 | consists of the displayed link text |
| 43 | in square brackets. in square brackets. |
| 44 | The corresponding link definition can occur anywhere on the page, and consists |
| 45 | of the link definition name in s a colon, whitespace and the |
| 46 | link target. |
| 47 | |
| 48 | * **Emphasis** can be given by wrapping text in one or two asterisks or underscores - use |
| 49 | one for HTML `<em>`, and two for `<strong>` emphasis. |
| 50 | |
| 51 | * A **code span** is text wrapped in backticks ('`'). |
| 52 | |
| 53 | * **Images** use a syntax much like inline or reference links, # Markdown formatting rules |
| 54 | |
| 55 | In addition to its native Wiki formatting syntax, Fossil supports Markdown syntax as specified by |
| 56 | [John Gruber's original Markdown implementation](http://daringfireball.net/projects/markdown/). |
| 57 | For lots of examples - not repeated here - please refer to its |
| 58 | [syntax description](http://daringfireball.net/projects/markdown/syntax), of which the page you |
| 59 | are reading is an extract. |
| 60 | |
| 61 | This page itself uses Markdown formatting. |
| 62 | |
| 63 | ## Summary |
| 64 | |
| 65 | - Block elements |
| 66 | |
| 67 | * A **paragraph** is a group of consecutive lines. Paragraphs are separated by blank lines. |
| 68 | |
| 69 | * A **Header** is a line of text underlined with equal signs or hyphens, or prefixed by a |
| 70 | number of hash marks. |
| 71 | |
| 72 | * **Block quotes** are blocks of text prefixed by '>'. |
| 73 | |
| 74 | * **Ordered list** items are prefixed by a number and a period. **Unordered list** items |
| 75 | are prefixed by a hyphen, asterisk or plus sign. Prefix and item text are separated by |
| 76 | whitespace. |
| 77 | |
| 78 | * **Code blocks** are formed by lines of text (possibly including empty lines) prefixed by |
| 79 | at least 4 spaces or a tab. |
| 80 | |
| 81 | * A **horizontal rule** is a line consisting of 3 or more asterisks, hyphens or underscores, |
| 82 | with optional whitespace between them. |
| 83 | |
| 84 | - Span elements |
| 85 | |
| 86 | * 3 types of **links** exist: |
| 87 | |
| 88 | - **automatic links** are URLs or email addresses enclosed in angle brackets |
| 89 | a * **Emphasis** can be given by wrapping text in one or two asterisks or underscores - use |
| 90 | one for HTML `<em>`, and two for `<strong>` emphasis. |
| 91 | |
| 92 | * A **code span** is text wrapped in backticks ('`'). |
| 93 | |
| 94 | * **Images** use a syntax much like inline or reference links, but with alt attribute text |
| 95 | ('img alt=...') instead of link text, and the first pair of square |
| 96 | brackets m prefix; the items will be |
| 97 | renumbered during rendering. However, future implementations may |
| 98 | for the first item in a list indicates an offset to be used for subsequent items. |
| 99 | |
| 100 | For list items spanning multiple lines, subsequent lines can be indented using an arbitrary amount |
| 101 | of whitespace. |
| 102 | |
| 103 | List items will be wrapped in HTML `<p>` tags if they are separated by blank lines. |
| 104 | |
| 105 | A list item may span multiple paragraphs. At least the first line |
| 106 | be indented using at least 4 spaces or a tab character. |
| 107 | |
| 108 | Block quotes within list items must have their '>' delimiters indented using 4 up to 7 spaces. |
| 109 | |
| 110 | Code blocks within list items need to be indented _twice_, that is, using 8 spaces or 2 tab |
| 111 | characters. |
| 112 | |
| 113 | ### Code blocks |
| 114 | |
| 115 | Lines within a code block are rendered verbatim using HTML `<pre>` and `<code>` tags, except that |
| 116 | HTML punctuation characters like '<' and '&' are automatically converted to HTML entities. Thus, |
| 117 | there is no need to explicitly escape HTML syntax within a code block. |
| 118 | |
| 119 | A code block runs until the first non blank line with indent less than 4 spaces or 1 tab character. |
| 120 | |
| 121 | |
| 122 | Regular Markdown syntax is not processed within code blocks. |
| 123 | |
| 124 | ### Links |
| 125 | |
| 126 | #### Automatic links |
| 127 | |
| 128 | When rendering automatic links to email addresses, HTML enco |
| 129 | prevent some spambots from harvesting. |
| 130 | |
| 131 | #### Inline links |
| 132 | |
| 133 | Links to resources on the same server can use relative paths (i.e. can start with a '/'). |
| 134 | |
| 135 | An optional title for the link (e.g. to have mouseover text in the b rowser) may be given behind |
| 136 | the link target but within the parentheses, in single and double quo |
| 137 | link target by whitespace. |
| 138 | link target by whitespace. |
| 139 | |
| 140 | # reference link consists of |
| 141 | > |
| 142 | > - one or more _link instances_ at appropriate locations in the page text |
| 143 | > - a single _link definition_ at an arbit |
| 144 | > During rendering, each link instance is resolved, and the corresponding definition is |
| 145 | > filled in. No separate link definition clauses occu r in the rendered output. |
| 146 | > |
| 147 | > There are 3 fields involved in link instances and definitions: |
| 148 | > |
| 149 | > - link text (i.e. the text that is displayed at the resulting link) |
| 150 | > - link definition name (i.e. an unique ID binding link instances to link definition) |
| 151 | > - link target (a target URL for the link) |
| 152 | |
| 153 | Multiple link instances may reference the same link definition using its link definition |
| 154 | name. |
| 155 | |
| 156 | Link definition names are case insensitive, and may contain letters, numbers, spaces and |
| 157 | punctuation. |
| 158 | |
| 159 | ##### Link instance |
| 160 | |
| 161 | A space may be inserted between the bracket pairs for link text and link definition name. |
| 162 | |
| 163 | A link instance can use an _implicit link definition name_ shortcut, in which case the link |
| 164 | text is used as the link definition name. The second set of brackets then remains empty, e.g. |
| 165 | '[Google][]' ('Google' being used as both link text and link definition name). |
| 166 | |
| 167 | ##### Link definition |
| 168 | |
| 169 | The first bracket pair containing the link definition name may be indented using up to 3 spaces. |
| 170 | |
| 171 | The link target may optionally be surrounded by angle brackets ('<' and '>'). |
| 172 | |
| 173 | A link target may be followed by an optional title (e.g. to have mouseover text in the browser). |
| 174 | This title may be enclosed in parentheses, single or double quotes. |
| 175 | |
| 176 | Link definitions may be split into 2 lines, with the title on the second line, arbitrarily |
| 177 | indented. This may be more visually pleasing when using long link targets. |
| 178 | |
| 179 | ### Emphasis |
| 180 | |
| 181 | The same character(s) used for starting the emphasis must be used to end it; don't mix |
| 182 | asterisks and underscores. |
| 183 | |
| 184 | Emphasis can be used in the middle of a word. That is, there need not be whitespace on either |
| 185 | side of emphasis start or end punctuation characters. |
| 186 | |
| 187 | ### Code spans |
| 188 | |
| 189 | To include a literal backtick character in a code span, use multiple backticks as opening and |
| 190 | closing delimiters. |
| 191 | |
| 192 | Whitespace may exist immediately after the opening delimiter and b Markdown formatting rules |
| 193 | |
| 194 | In addition to its native Wiki formatting syntax, Fossil supports Markdown syntax as specified by |
| 195 | [John Gruber's original Markdown implementation](http://daringfireball.net/projects/markdown/). |
| 196 | For lots of examples - not repeated here - please refer to its |
| 197 | [syntax description](http://daringfireball.net/projects/markdownr |
| 198 | a HTML block level construct (`<div>`, `<table>` etc) must be separated from surrounding |
| 199 | context using blank lines, and must both occur at the start of a line. |
| 200 | |
| 201 | No extra unwanted `<p>` HTML tags are added around HTML block level tags. |
| 202 | |
| 203 | Markdown formatting within HTML block level tags is not processed; however, formatting within |
| 204 | span level tags (e.g. `<mark>`) is processed normally. |
| 205 | |
| 206 | ### Escaping Markdown punctuation |
| 207 | |
| 208 | The following punctuation characters can be escaped using backslash: |
| 209 | |
| 210 | - \\ backslash |
| 211 | - ` backtick |
| 212 | - * asterisk |
| 213 | - _ underscore |
| 214 | - {} curly braces |
| 215 | - [] square brackets |
| 216 | - () parentheses |
| 217 | - # hash mark |
| 218 | - + plus sign |
| 219 | - - minus sign (hyphen) |
| 220 | - . dot |
| 221 | - ! exclamation mark |
| 222 | |
| 223 | To render a literal backslash, use 2 backslashes ('\\\\'). |
| 224 |
+5
-5
| --- src/md5.c | ||
| +++ src/md5.c | ||
| @@ -177,11 +177,11 @@ | ||
| 177 | 177 | |
| 178 | 178 | /* |
| 179 | 179 | * Update context to reflect the concatenation of another buffer full |
| 180 | 180 | * of bytes. |
| 181 | 181 | */ |
| 182 | -static | |
| 182 | +static | |
| 183 | 183 | void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){ |
| 184 | 184 | struct Context *ctx = (struct Context *)pCtx; |
| 185 | 185 | uint32 t; |
| 186 | 186 | |
| 187 | 187 | /* Update bitcount */ |
| @@ -224,11 +224,11 @@ | ||
| 224 | 224 | |
| 225 | 225 | memcpy(ctx->in, buf, len); |
| 226 | 226 | } |
| 227 | 227 | |
| 228 | 228 | /* |
| 229 | - * Final wrapup - pad to 64-byte boundary with the bit pattern | |
| 229 | + * Final wrapup - pad to 64-byte boundary with the bit pattern | |
| 230 | 230 | * 1 0* (64-bit count of bits processed, MSB-first) |
| 231 | 231 | */ |
| 232 | 232 | static void MD5Final(unsigned char digest[16], MD5Context *pCtx){ |
| 233 | 233 | struct Context *ctx = (struct Context *)pCtx; |
| 234 | 234 | unsigned count; |
| @@ -274,11 +274,11 @@ | ||
| 274 | 274 | ** "unsigned char digest[16]" in the calling function. The MD5 |
| 275 | 275 | ** digest is stored in the first 16 bytes. zBuf should |
| 276 | 276 | ** be "char zBuf[33]". |
| 277 | 277 | */ |
| 278 | 278 | static void DigestToBase16(unsigned char *digest, char *zBuf){ |
| 279 | - static char const zEncode[] = "0123456789abcdef"; | |
| 279 | + static const char zEncode[] = "0123456789abcdef"; | |
| 280 | 280 | int i, j; |
| 281 | 281 | |
| 282 | 282 | for(j=i=0; i<16; i++){ |
| 283 | 283 | int a = digest[i]; |
| 284 | 284 | zBuf[j++] = zEncode[(a>>4)&0xf]; |
| @@ -343,11 +343,11 @@ | ||
| 343 | 343 | return zResult; |
| 344 | 344 | } |
| 345 | 345 | |
| 346 | 346 | /* |
| 347 | 347 | ** Finish the incremental MD5 checksum. Store the result in blob pOut |
| 348 | -** if pOut!=0. Also return a pointer to the result. | |
| 348 | +** if pOut!=0. Also return a pointer to the result. | |
| 349 | 349 | ** |
| 350 | 350 | ** This resets the incremental checksum preparing for the next round |
| 351 | 351 | ** of computation. The return pointer points to a static buffer that |
| 352 | 352 | ** is overwritten by subsequent calls to this function. |
| 353 | 353 | */ |
| @@ -431,11 +431,11 @@ | ||
| 431 | 431 | */ |
| 432 | 432 | void md5sum_test(void){ |
| 433 | 433 | int i; |
| 434 | 434 | Blob in; |
| 435 | 435 | Blob cksum; |
| 436 | - | |
| 436 | + | |
| 437 | 437 | for(i=2; i<g.argc; i++){ |
| 438 | 438 | blob_init(&cksum, "********** not found ***********", -1); |
| 439 | 439 | if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ |
| 440 | 440 | blob_read_from_channel(&in, stdin, -1); |
| 441 | 441 | md5sum_blob(&in, &cksum); |
| 442 | 442 |
| --- src/md5.c | |
| +++ src/md5.c | |
| @@ -177,11 +177,11 @@ | |
| 177 | |
| 178 | /* |
| 179 | * Update context to reflect the concatenation of another buffer full |
| 180 | * of bytes. |
| 181 | */ |
| 182 | static |
| 183 | void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){ |
| 184 | struct Context *ctx = (struct Context *)pCtx; |
| 185 | uint32 t; |
| 186 | |
| 187 | /* Update bitcount */ |
| @@ -224,11 +224,11 @@ | |
| 224 | |
| 225 | memcpy(ctx->in, buf, len); |
| 226 | } |
| 227 | |
| 228 | /* |
| 229 | * Final wrapup - pad to 64-byte boundary with the bit pattern |
| 230 | * 1 0* (64-bit count of bits processed, MSB-first) |
| 231 | */ |
| 232 | static void MD5Final(unsigned char digest[16], MD5Context *pCtx){ |
| 233 | struct Context *ctx = (struct Context *)pCtx; |
| 234 | unsigned count; |
| @@ -274,11 +274,11 @@ | |
| 274 | ** "unsigned char digest[16]" in the calling function. The MD5 |
| 275 | ** digest is stored in the first 16 bytes. zBuf should |
| 276 | ** be "char zBuf[33]". |
| 277 | */ |
| 278 | static void DigestToBase16(unsigned char *digest, char *zBuf){ |
| 279 | static char const zEncode[] = "0123456789abcdef"; |
| 280 | int i, j; |
| 281 | |
| 282 | for(j=i=0; i<16; i++){ |
| 283 | int a = digest[i]; |
| 284 | zBuf[j++] = zEncode[(a>>4)&0xf]; |
| @@ -343,11 +343,11 @@ | |
| 343 | return zResult; |
| 344 | } |
| 345 | |
| 346 | /* |
| 347 | ** Finish the incremental MD5 checksum. Store the result in blob pOut |
| 348 | ** if pOut!=0. Also return a pointer to the result. |
| 349 | ** |
| 350 | ** This resets the incremental checksum preparing for the next round |
| 351 | ** of computation. The return pointer points to a static buffer that |
| 352 | ** is overwritten by subsequent calls to this function. |
| 353 | */ |
| @@ -431,11 +431,11 @@ | |
| 431 | */ |
| 432 | void md5sum_test(void){ |
| 433 | int i; |
| 434 | Blob in; |
| 435 | Blob cksum; |
| 436 | |
| 437 | for(i=2; i<g.argc; i++){ |
| 438 | blob_init(&cksum, "********** not found ***********", -1); |
| 439 | if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ |
| 440 | blob_read_from_channel(&in, stdin, -1); |
| 441 | md5sum_blob(&in, &cksum); |
| 442 |
| --- src/md5.c | |
| +++ src/md5.c | |
| @@ -177,11 +177,11 @@ | |
| 177 | |
| 178 | /* |
| 179 | * Update context to reflect the concatenation of another buffer full |
| 180 | * of bytes. |
| 181 | */ |
| 182 | static |
| 183 | void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){ |
| 184 | struct Context *ctx = (struct Context *)pCtx; |
| 185 | uint32 t; |
| 186 | |
| 187 | /* Update bitcount */ |
| @@ -224,11 +224,11 @@ | |
| 224 | |
| 225 | memcpy(ctx->in, buf, len); |
| 226 | } |
| 227 | |
| 228 | /* |
| 229 | * Final wrapup - pad to 64-byte boundary with the bit pattern |
| 230 | * 1 0* (64-bit count of bits processed, MSB-first) |
| 231 | */ |
| 232 | static void MD5Final(unsigned char digest[16], MD5Context *pCtx){ |
| 233 | struct Context *ctx = (struct Context *)pCtx; |
| 234 | unsigned count; |
| @@ -274,11 +274,11 @@ | |
| 274 | ** "unsigned char digest[16]" in the calling function. The MD5 |
| 275 | ** digest is stored in the first 16 bytes. zBuf should |
| 276 | ** be "char zBuf[33]". |
| 277 | */ |
| 278 | static void DigestToBase16(unsigned char *digest, char *zBuf){ |
| 279 | static const char zEncode[] = "0123456789abcdef"; |
| 280 | int i, j; |
| 281 | |
| 282 | for(j=i=0; i<16; i++){ |
| 283 | int a = digest[i]; |
| 284 | zBuf[j++] = zEncode[(a>>4)&0xf]; |
| @@ -343,11 +343,11 @@ | |
| 343 | return zResult; |
| 344 | } |
| 345 | |
| 346 | /* |
| 347 | ** Finish the incremental MD5 checksum. Store the result in blob pOut |
| 348 | ** if pOut!=0. Also return a pointer to the result. |
| 349 | ** |
| 350 | ** This resets the incremental checksum preparing for the next round |
| 351 | ** of computation. The return pointer points to a static buffer that |
| 352 | ** is overwritten by subsequent calls to this function. |
| 353 | */ |
| @@ -431,11 +431,11 @@ | |
| 431 | */ |
| 432 | void md5sum_test(void){ |
| 433 | int i; |
| 434 | Blob in; |
| 435 | Blob cksum; |
| 436 | |
| 437 | for(i=2; i<g.argc; i++){ |
| 438 | blob_init(&cksum, "********** not found ***********", -1); |
| 439 | if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ |
| 440 | blob_read_from_channel(&in, stdin, -1); |
| 441 | md5sum_blob(&in, &cksum); |
| 442 |
+6
-6
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -41,11 +41,11 @@ | ||
| 41 | 41 | if( zTagList && zTagList[0] ){ |
| 42 | 42 | zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList); |
| 43 | 43 | }else{ |
| 44 | 44 | zCom = mprintf("%s", db_column_text(&q,2)); |
| 45 | 45 | } |
| 46 | - fossil_print("%-*s [%S] by %s on %s\n%*s", | |
| 46 | + fossil_print("%-*s [%S] by %s on %s\n%*s", | |
| 47 | 47 | indent-1, zLabel, |
| 48 | 48 | db_column_text(&q, 3), |
| 49 | 49 | db_column_text(&q, 1), |
| 50 | 50 | db_column_text(&q, 0), |
| 51 | 51 | indent, ""); |
| @@ -166,11 +166,11 @@ | ||
| 166 | 166 | }else if( g.argc==2 ){ |
| 167 | 167 | /* No version specified on the command-line so pick the most recent |
| 168 | 168 | ** leaf that is (1) not the version currently checked out and (2) |
| 169 | 169 | ** has not already been merged into the current checkout and (3) |
| 170 | 170 | ** the leaf is not closed and (4) the leaf is in the same branch |
| 171 | - ** as the current checkout. | |
| 171 | + ** as the current checkout. | |
| 172 | 172 | */ |
| 173 | 173 | Stmt q; |
| 174 | 174 | if( pickFlag || backoutFlag || integrateFlag){ |
| 175 | 175 | fossil_fatal("cannot use --backout, --cherrypick or --integrate with a fork merge"); |
| 176 | 176 | } |
| @@ -472,13 +472,13 @@ | ||
| 472 | 472 | undo_save(zName); |
| 473 | 473 | vfile_to_disk(0, idm, 0, 0); |
| 474 | 474 | } |
| 475 | 475 | } |
| 476 | 476 | db_finalize(&q); |
| 477 | - | |
| 477 | + | |
| 478 | 478 | /* |
| 479 | - ** Find files that have changed from P->M but not P->V. | |
| 479 | + ** Find files that have changed from P->M but not P->V. | |
| 480 | 480 | ** Copy the M content over into V. |
| 481 | 481 | */ |
| 482 | 482 | db_prepare(&q, |
| 483 | 483 | "SELECT idv, ridm, fn, islinkm FROM fv" |
| 484 | 484 | " WHERE idp>0 AND idv>0 AND idm>0" |
| @@ -524,18 +524,18 @@ | ||
| 524 | 524 | int rc; |
| 525 | 525 | char *zFullPath; |
| 526 | 526 | Blob m, p, r; |
| 527 | 527 | /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ |
| 528 | 528 | if( verboseFlag ){ |
| 529 | - fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n", | |
| 529 | + fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n", | |
| 530 | 530 | zName, ridp, ridm, ridv); |
| 531 | 531 | }else{ |
| 532 | 532 | fossil_print("MERGE %s\n", zName); |
| 533 | 533 | } |
| 534 | 534 | if( islinkv || islinkm /* || file_wd_islink(zFullPath) */ ){ |
| 535 | 535 | fossil_print("***** Cannot merge symlink %s\n", zName); |
| 536 | - nConflict++; | |
| 536 | + nConflict++; | |
| 537 | 537 | }else{ |
| 538 | 538 | undo_save(zName); |
| 539 | 539 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 540 | 540 | content_get(ridp, &p); |
| 541 | 541 | content_get(ridm, &m); |
| 542 | 542 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -41,11 +41,11 @@ | |
| 41 | if( zTagList && zTagList[0] ){ |
| 42 | zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList); |
| 43 | }else{ |
| 44 | zCom = mprintf("%s", db_column_text(&q,2)); |
| 45 | } |
| 46 | fossil_print("%-*s [%S] by %s on %s\n%*s", |
| 47 | indent-1, zLabel, |
| 48 | db_column_text(&q, 3), |
| 49 | db_column_text(&q, 1), |
| 50 | db_column_text(&q, 0), |
| 51 | indent, ""); |
| @@ -166,11 +166,11 @@ | |
| 166 | }else if( g.argc==2 ){ |
| 167 | /* No version specified on the command-line so pick the most recent |
| 168 | ** leaf that is (1) not the version currently checked out and (2) |
| 169 | ** has not already been merged into the current checkout and (3) |
| 170 | ** the leaf is not closed and (4) the leaf is in the same branch |
| 171 | ** as the current checkout. |
| 172 | */ |
| 173 | Stmt q; |
| 174 | if( pickFlag || backoutFlag || integrateFlag){ |
| 175 | fossil_fatal("cannot use --backout, --cherrypick or --integrate with a fork merge"); |
| 176 | } |
| @@ -472,13 +472,13 @@ | |
| 472 | undo_save(zName); |
| 473 | vfile_to_disk(0, idm, 0, 0); |
| 474 | } |
| 475 | } |
| 476 | db_finalize(&q); |
| 477 | |
| 478 | /* |
| 479 | ** Find files that have changed from P->M but not P->V. |
| 480 | ** Copy the M content over into V. |
| 481 | */ |
| 482 | db_prepare(&q, |
| 483 | "SELECT idv, ridm, fn, islinkm FROM fv" |
| 484 | " WHERE idp>0 AND idv>0 AND idm>0" |
| @@ -524,18 +524,18 @@ | |
| 524 | int rc; |
| 525 | char *zFullPath; |
| 526 | Blob m, p, r; |
| 527 | /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ |
| 528 | if( verboseFlag ){ |
| 529 | fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n", |
| 530 | zName, ridp, ridm, ridv); |
| 531 | }else{ |
| 532 | fossil_print("MERGE %s\n", zName); |
| 533 | } |
| 534 | if( islinkv || islinkm /* || file_wd_islink(zFullPath) */ ){ |
| 535 | fossil_print("***** Cannot merge symlink %s\n", zName); |
| 536 | nConflict++; |
| 537 | }else{ |
| 538 | undo_save(zName); |
| 539 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 540 | content_get(ridp, &p); |
| 541 | content_get(ridm, &m); |
| 542 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -41,11 +41,11 @@ | |
| 41 | if( zTagList && zTagList[0] ){ |
| 42 | zCom = mprintf("%s (%s)", db_column_text(&q, 2), zTagList); |
| 43 | }else{ |
| 44 | zCom = mprintf("%s", db_column_text(&q,2)); |
| 45 | } |
| 46 | fossil_print("%-*s [%S] by %s on %s\n%*s", |
| 47 | indent-1, zLabel, |
| 48 | db_column_text(&q, 3), |
| 49 | db_column_text(&q, 1), |
| 50 | db_column_text(&q, 0), |
| 51 | indent, ""); |
| @@ -166,11 +166,11 @@ | |
| 166 | }else if( g.argc==2 ){ |
| 167 | /* No version specified on the command-line so pick the most recent |
| 168 | ** leaf that is (1) not the version currently checked out and (2) |
| 169 | ** has not already been merged into the current checkout and (3) |
| 170 | ** the leaf is not closed and (4) the leaf is in the same branch |
| 171 | ** as the current checkout. |
| 172 | */ |
| 173 | Stmt q; |
| 174 | if( pickFlag || backoutFlag || integrateFlag){ |
| 175 | fossil_fatal("cannot use --backout, --cherrypick or --integrate with a fork merge"); |
| 176 | } |
| @@ -472,13 +472,13 @@ | |
| 472 | undo_save(zName); |
| 473 | vfile_to_disk(0, idm, 0, 0); |
| 474 | } |
| 475 | } |
| 476 | db_finalize(&q); |
| 477 | |
| 478 | /* |
| 479 | ** Find files that have changed from P->M but not P->V. |
| 480 | ** Copy the M content over into V. |
| 481 | */ |
| 482 | db_prepare(&q, |
| 483 | "SELECT idv, ridm, fn, islinkm FROM fv" |
| 484 | " WHERE idp>0 AND idv>0 AND idm>0" |
| @@ -524,18 +524,18 @@ | |
| 524 | int rc; |
| 525 | char *zFullPath; |
| 526 | Blob m, p, r; |
| 527 | /* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */ |
| 528 | if( verboseFlag ){ |
| 529 | fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n", |
| 530 | zName, ridp, ridm, ridv); |
| 531 | }else{ |
| 532 | fossil_print("MERGE %s\n", zName); |
| 533 | } |
| 534 | if( islinkv || islinkm /* || file_wd_islink(zFullPath) */ ){ |
| 535 | fossil_print("***** Cannot merge symlink %s\n", zName); |
| 536 | nConflict++; |
| 537 | }else{ |
| 538 | undo_save(zName); |
| 539 | zFullPath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 540 | content_get(ridp, &p); |
| 541 | content_get(ridm, &m); |
| 542 |
+32
-13
| --- src/mkbuiltin.c | ||
| +++ src/mkbuiltin.c | ||
| @@ -29,11 +29,11 @@ | ||
| 29 | 29 | #include <string.h> |
| 30 | 30 | |
| 31 | 31 | |
| 32 | 32 | /* |
| 33 | 33 | ** Read the entire content of the file named zFilename into memory obtained |
| 34 | -** from malloc() and retur a pointer to that memory. Write the size of the | |
| 34 | +** from malloc() and return a pointer to that memory. Write the size of the | |
| 35 | 35 | ** file into *pnByte. |
| 36 | 36 | */ |
| 37 | 37 | static unsigned char *read_file(const char *zFilename, int *pnByte){ |
| 38 | 38 | FILE *in; |
| 39 | 39 | unsigned char *z; |
| @@ -62,10 +62,11 @@ | ||
| 62 | 62 | */ |
| 63 | 63 | typedef struct Resource Resource; |
| 64 | 64 | struct Resource { |
| 65 | 65 | const char *zName; |
| 66 | 66 | int nByte; |
| 67 | + int idx; | |
| 67 | 68 | }; |
| 68 | 69 | |
| 69 | 70 | /* |
| 70 | 71 | ** Compare two Resource objects for sorting purposes. They sort |
| 71 | 72 | ** in zName order so that Fossil can search for resources using |
| @@ -79,14 +80,22 @@ | ||
| 79 | 80 | |
| 80 | 81 | int main(int argc, char **argv){ |
| 81 | 82 | int i, sz; |
| 82 | 83 | int j, n; |
| 83 | 84 | Resource *aRes; |
| 84 | - int nRes = argc-1; | |
| 85 | + int nRes; | |
| 85 | 86 | unsigned char *pData; |
| 86 | 87 | int nErr = 0; |
| 87 | - | |
| 88 | + int nSkip; | |
| 89 | + int nPrefix = 0; | |
| 90 | + | |
| 91 | + if( argc>3 && strcmp(argv[1],"--prefix")==0 ){ | |
| 92 | + nPrefix = (int)strlen(argv[2]); | |
| 93 | + argc -= 2; | |
| 94 | + argv += 2; | |
| 95 | + } | |
| 96 | + nRes = argc - 1; | |
| 88 | 97 | aRes = malloc( nRes*sizeof(aRes[0]) ); |
| 89 | 98 | if( aRes==0 ){ |
| 90 | 99 | fprintf(stderr, "malloc failed\n"); |
| 91 | 100 | return 1; |
| 92 | 101 | } |
| @@ -103,15 +112,24 @@ | ||
| 103 | 112 | if( pData==0 ){ |
| 104 | 113 | fprintf(stderr, "Cannot open file [%s]\n", aRes[i].zName); |
| 105 | 114 | nErr++; |
| 106 | 115 | continue; |
| 107 | 116 | } |
| 108 | - aRes[i].nByte = sz; | |
| 117 | + | |
| 118 | + /* Skip initial lines beginning with # */ | |
| 119 | + nSkip = 0; | |
| 120 | + while( pData[nSkip]=='#' ){ | |
| 121 | + while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; } | |
| 122 | + if( pData[nSkip]=='\n' ) nSkip++; | |
| 123 | + } | |
| 124 | + | |
| 125 | + aRes[i].nByte = sz - nSkip; | |
| 126 | + aRes[i].idx = i; | |
| 109 | 127 | printf("/* Content of file %s */\n", aRes[i].zName); |
| 110 | 128 | printf("static const unsigned char bidata%d[%d] = {\n ", |
| 111 | - i, sz+1); | |
| 112 | - for(j=n=0; j<=sz; j++){ | |
| 129 | + i, sz+1-nSkip); | |
| 130 | + for(j=nSkip, n=0; j<=sz; j++){ | |
| 113 | 131 | printf("%3d", pData[j]); |
| 114 | 132 | if( j==sz ){ |
| 115 | 133 | printf(" };\n"); |
| 116 | 134 | }else if( n==14 ){ |
| 117 | 135 | printf(",\n "); |
| @@ -129,17 +147,18 @@ | ||
| 129 | 147 | printf(" const unsigned char *pData;\n"); |
| 130 | 148 | printf(" int nByte;\n"); |
| 131 | 149 | printf("};\n"); |
| 132 | 150 | printf("static const BuiltinFileTable aBuiltinFiles[] = {\n"); |
| 133 | 151 | for(i=0; i<nRes; i++){ |
| 134 | - const char *zTail; | |
| 135 | 152 | const char *z = aRes[i].zName; |
| 136 | - zTail = z; | |
| 137 | - while( z && z[0] ){ | |
| 138 | - if( z[0]=='/' || z[0]=='\\' ) zTail = &z[1]; | |
| 139 | - z++; | |
| 140 | - } | |
| 141 | - printf(" { \"%s\", bidata%d, %d },\n", zTail, i, aRes[i].nByte); | |
| 153 | + if( strlen(z)>=nPrefix ) z += nPrefix; | |
| 154 | + while( z[0]=='.' || z[0]=='/' ){ z++; } | |
| 155 | + aRes[i].zName = z; | |
| 156 | + } | |
| 157 | + qsort(aRes, nRes, sizeof(aRes[0]), compareResource); | |
| 158 | + for(i=0; i<nRes; i++){ | |
| 159 | + printf(" { \"%s\", bidata%d, %d },\n", | |
| 160 | + aRes[i].zName, aRes[i].idx, aRes[i].nByte); | |
| 142 | 161 | } |
| 143 | 162 | printf("};\n"); |
| 144 | 163 | return nErr; |
| 145 | 164 | } |
| 146 | 165 |
| --- src/mkbuiltin.c | |
| +++ src/mkbuiltin.c | |
| @@ -29,11 +29,11 @@ | |
| 29 | #include <string.h> |
| 30 | |
| 31 | |
| 32 | /* |
| 33 | ** Read the entire content of the file named zFilename into memory obtained |
| 34 | ** from malloc() and retur a pointer to that memory. Write the size of the |
| 35 | ** file into *pnByte. |
| 36 | */ |
| 37 | static unsigned char *read_file(const char *zFilename, int *pnByte){ |
| 38 | FILE *in; |
| 39 | unsigned char *z; |
| @@ -62,10 +62,11 @@ | |
| 62 | */ |
| 63 | typedef struct Resource Resource; |
| 64 | struct Resource { |
| 65 | const char *zName; |
| 66 | int nByte; |
| 67 | }; |
| 68 | |
| 69 | /* |
| 70 | ** Compare two Resource objects for sorting purposes. They sort |
| 71 | ** in zName order so that Fossil can search for resources using |
| @@ -79,14 +80,22 @@ | |
| 79 | |
| 80 | int main(int argc, char **argv){ |
| 81 | int i, sz; |
| 82 | int j, n; |
| 83 | Resource *aRes; |
| 84 | int nRes = argc-1; |
| 85 | unsigned char *pData; |
| 86 | int nErr = 0; |
| 87 | |
| 88 | aRes = malloc( nRes*sizeof(aRes[0]) ); |
| 89 | if( aRes==0 ){ |
| 90 | fprintf(stderr, "malloc failed\n"); |
| 91 | return 1; |
| 92 | } |
| @@ -103,15 +112,24 @@ | |
| 103 | if( pData==0 ){ |
| 104 | fprintf(stderr, "Cannot open file [%s]\n", aRes[i].zName); |
| 105 | nErr++; |
| 106 | continue; |
| 107 | } |
| 108 | aRes[i].nByte = sz; |
| 109 | printf("/* Content of file %s */\n", aRes[i].zName); |
| 110 | printf("static const unsigned char bidata%d[%d] = {\n ", |
| 111 | i, sz+1); |
| 112 | for(j=n=0; j<=sz; j++){ |
| 113 | printf("%3d", pData[j]); |
| 114 | if( j==sz ){ |
| 115 | printf(" };\n"); |
| 116 | }else if( n==14 ){ |
| 117 | printf(",\n "); |
| @@ -129,17 +147,18 @@ | |
| 129 | printf(" const unsigned char *pData;\n"); |
| 130 | printf(" int nByte;\n"); |
| 131 | printf("};\n"); |
| 132 | printf("static const BuiltinFileTable aBuiltinFiles[] = {\n"); |
| 133 | for(i=0; i<nRes; i++){ |
| 134 | const char *zTail; |
| 135 | const char *z = aRes[i].zName; |
| 136 | zTail = z; |
| 137 | while( z && z[0] ){ |
| 138 | if( z[0]=='/' || z[0]=='\\' ) zTail = &z[1]; |
| 139 | z++; |
| 140 | } |
| 141 | printf(" { \"%s\", bidata%d, %d },\n", zTail, i, aRes[i].nByte); |
| 142 | } |
| 143 | printf("};\n"); |
| 144 | return nErr; |
| 145 | } |
| 146 |
| --- src/mkbuiltin.c | |
| +++ src/mkbuiltin.c | |
| @@ -29,11 +29,11 @@ | |
| 29 | #include <string.h> |
| 30 | |
| 31 | |
| 32 | /* |
| 33 | ** Read the entire content of the file named zFilename into memory obtained |
| 34 | ** from malloc() and return a pointer to that memory. Write the size of the |
| 35 | ** file into *pnByte. |
| 36 | */ |
| 37 | static unsigned char *read_file(const char *zFilename, int *pnByte){ |
| 38 | FILE *in; |
| 39 | unsigned char *z; |
| @@ -62,10 +62,11 @@ | |
| 62 | */ |
| 63 | typedef struct Resource Resource; |
| 64 | struct Resource { |
| 65 | const char *zName; |
| 66 | int nByte; |
| 67 | int idx; |
| 68 | }; |
| 69 | |
| 70 | /* |
| 71 | ** Compare two Resource objects for sorting purposes. They sort |
| 72 | ** in zName order so that Fossil can search for resources using |
| @@ -79,14 +80,22 @@ | |
| 80 | |
| 81 | int main(int argc, char **argv){ |
| 82 | int i, sz; |
| 83 | int j, n; |
| 84 | Resource *aRes; |
| 85 | int nRes; |
| 86 | unsigned char *pData; |
| 87 | int nErr = 0; |
| 88 | int nSkip; |
| 89 | int nPrefix = 0; |
| 90 | |
| 91 | if( argc>3 && strcmp(argv[1],"--prefix")==0 ){ |
| 92 | nPrefix = (int)strlen(argv[2]); |
| 93 | argc -= 2; |
| 94 | argv += 2; |
| 95 | } |
| 96 | nRes = argc - 1; |
| 97 | aRes = malloc( nRes*sizeof(aRes[0]) ); |
| 98 | if( aRes==0 ){ |
| 99 | fprintf(stderr, "malloc failed\n"); |
| 100 | return 1; |
| 101 | } |
| @@ -103,15 +112,24 @@ | |
| 112 | if( pData==0 ){ |
| 113 | fprintf(stderr, "Cannot open file [%s]\n", aRes[i].zName); |
| 114 | nErr++; |
| 115 | continue; |
| 116 | } |
| 117 | |
| 118 | /* Skip initial lines beginning with # */ |
| 119 | nSkip = 0; |
| 120 | while( pData[nSkip]=='#' ){ |
| 121 | while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; } |
| 122 | if( pData[nSkip]=='\n' ) nSkip++; |
| 123 | } |
| 124 | |
| 125 | aRes[i].nByte = sz - nSkip; |
| 126 | aRes[i].idx = i; |
| 127 | printf("/* Content of file %s */\n", aRes[i].zName); |
| 128 | printf("static const unsigned char bidata%d[%d] = {\n ", |
| 129 | i, sz+1-nSkip); |
| 130 | for(j=nSkip, n=0; j<=sz; j++){ |
| 131 | printf("%3d", pData[j]); |
| 132 | if( j==sz ){ |
| 133 | printf(" };\n"); |
| 134 | }else if( n==14 ){ |
| 135 | printf(",\n "); |
| @@ -129,17 +147,18 @@ | |
| 147 | printf(" const unsigned char *pData;\n"); |
| 148 | printf(" int nByte;\n"); |
| 149 | printf("};\n"); |
| 150 | printf("static const BuiltinFileTable aBuiltinFiles[] = {\n"); |
| 151 | for(i=0; i<nRes; i++){ |
| 152 | const char *z = aRes[i].zName; |
| 153 | if( strlen(z)>=nPrefix ) z += nPrefix; |
| 154 | while( z[0]=='.' || z[0]=='/' ){ z++; } |
| 155 | aRes[i].zName = z; |
| 156 | } |
| 157 | qsort(aRes, nRes, sizeof(aRes[0]), compareResource); |
| 158 | for(i=0; i<nRes; i++){ |
| 159 | printf(" { \"%s\", bidata%d, %d },\n", |
| 160 | aRes[i].zName, aRes[i].idx, aRes[i].nByte); |
| 161 | } |
| 162 | printf("};\n"); |
| 163 | return nErr; |
| 164 | } |
| 165 |
+3
-3
| --- src/mkindex.c | ||
| +++ src/mkindex.c | ||
| @@ -217,11 +217,11 @@ | ||
| 217 | 217 | if( zLine[i]!='(' ) goto page_skip; |
| 218 | 218 | nFixed = nUsed; |
| 219 | 219 | nHelp = 0; |
| 220 | 220 | return; |
| 221 | 221 | |
| 222 | -page_skip: | |
| 222 | +page_skip: | |
| 223 | 223 | for(i=nFixed; i<nUsed; i++){ |
| 224 | 224 | fprintf(stderr,"%s:%d: skipping page \"%s\"\n", |
| 225 | 225 | zFile, nLine, aEntry[i].zPath); |
| 226 | 226 | } |
| 227 | 227 | nUsed = nFixed; |
| @@ -327,11 +327,11 @@ | ||
| 327 | 327 | aEntry[i].zHelp[0] = 0; |
| 328 | 328 | } |
| 329 | 329 | } |
| 330 | 330 | puts("struct CmdHelp {" |
| 331 | 331 | "int eType; " |
| 332 | - "char const * zText;" | |
| 332 | + "const char *zText;" | |
| 333 | 333 | "};"); |
| 334 | 334 | puts("static struct CmdHelp aCmdHelp[] = {"); |
| 335 | 335 | for(i=0; i<nFixed; i++){ |
| 336 | 336 | if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); |
| 337 | 337 | if( aEntry[i].zHelp==0 ){ |
| @@ -361,11 +361,11 @@ | ||
| 361 | 361 | scan_for_label("WEBPAGE:",zLine,0); |
| 362 | 362 | scan_for_label("COMMAND:",zLine,1); |
| 363 | 363 | scan_for_func(zLine); |
| 364 | 364 | } |
| 365 | 365 | fclose(in); |
| 366 | - nUsed = nFixed; | |
| 366 | + nUsed = nFixed; | |
| 367 | 367 | } |
| 368 | 368 | |
| 369 | 369 | int main(int argc, char **argv){ |
| 370 | 370 | int i; |
| 371 | 371 | for(i=1; i<argc; i++){ |
| 372 | 372 |
| --- src/mkindex.c | |
| +++ src/mkindex.c | |
| @@ -217,11 +217,11 @@ | |
| 217 | if( zLine[i]!='(' ) goto page_skip; |
| 218 | nFixed = nUsed; |
| 219 | nHelp = 0; |
| 220 | return; |
| 221 | |
| 222 | page_skip: |
| 223 | for(i=nFixed; i<nUsed; i++){ |
| 224 | fprintf(stderr,"%s:%d: skipping page \"%s\"\n", |
| 225 | zFile, nLine, aEntry[i].zPath); |
| 226 | } |
| 227 | nUsed = nFixed; |
| @@ -327,11 +327,11 @@ | |
| 327 | aEntry[i].zHelp[0] = 0; |
| 328 | } |
| 329 | } |
| 330 | puts("struct CmdHelp {" |
| 331 | "int eType; " |
| 332 | "char const * zText;" |
| 333 | "};"); |
| 334 | puts("static struct CmdHelp aCmdHelp[] = {"); |
| 335 | for(i=0; i<nFixed; i++){ |
| 336 | if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); |
| 337 | if( aEntry[i].zHelp==0 ){ |
| @@ -361,11 +361,11 @@ | |
| 361 | scan_for_label("WEBPAGE:",zLine,0); |
| 362 | scan_for_label("COMMAND:",zLine,1); |
| 363 | scan_for_func(zLine); |
| 364 | } |
| 365 | fclose(in); |
| 366 | nUsed = nFixed; |
| 367 | } |
| 368 | |
| 369 | int main(int argc, char **argv){ |
| 370 | int i; |
| 371 | for(i=1; i<argc; i++){ |
| 372 |
| --- src/mkindex.c | |
| +++ src/mkindex.c | |
| @@ -217,11 +217,11 @@ | |
| 217 | if( zLine[i]!='(' ) goto page_skip; |
| 218 | nFixed = nUsed; |
| 219 | nHelp = 0; |
| 220 | return; |
| 221 | |
| 222 | page_skip: |
| 223 | for(i=nFixed; i<nUsed; i++){ |
| 224 | fprintf(stderr,"%s:%d: skipping page \"%s\"\n", |
| 225 | zFile, nLine, aEntry[i].zPath); |
| 226 | } |
| 227 | nUsed = nFixed; |
| @@ -327,11 +327,11 @@ | |
| 327 | aEntry[i].zHelp[0] = 0; |
| 328 | } |
| 329 | } |
| 330 | puts("struct CmdHelp {" |
| 331 | "int eType; " |
| 332 | "const char *zText;" |
| 333 | "};"); |
| 334 | puts("static struct CmdHelp aCmdHelp[] = {"); |
| 335 | for(i=0; i<nFixed; i++){ |
| 336 | if( aEntry[i].zIf ) printf("%s", aEntry[i].zIf); |
| 337 | if( aEntry[i].zHelp==0 ){ |
| @@ -361,11 +361,11 @@ | |
| 361 | scan_for_label("WEBPAGE:",zLine,0); |
| 362 | scan_for_label("COMMAND:",zLine,1); |
| 363 | scan_for_func(zLine); |
| 364 | } |
| 365 | fclose(in); |
| 366 | nUsed = nFixed; |
| 367 | } |
| 368 | |
| 369 | int main(int argc, char **argv){ |
| 370 | int i; |
| 371 | for(i=1; i<argc; i++){ |
| 372 |
+1
-1
| --- src/mkversion.c | ||
| +++ src/mkversion.c | ||
| @@ -43,11 +43,11 @@ | ||
| 43 | 43 | exit(1); |
| 44 | 44 | } |
| 45 | 45 | fclose(v); |
| 46 | 46 | for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} |
| 47 | 47 | *z = 0; |
| 48 | - printf("#define RELEASE_VERSION \"%s\"\n", b); | |
| 48 | + printf("#define RELEASE_VERSION \"%s\"\n", b); | |
| 49 | 49 | x=0; |
| 50 | 50 | i=0; |
| 51 | 51 | z=b; |
| 52 | 52 | while(1){ |
| 53 | 53 | if( z[0]>='0' && z[0]<='9' ){ |
| 54 | 54 |
| --- src/mkversion.c | |
| +++ src/mkversion.c | |
| @@ -43,11 +43,11 @@ | |
| 43 | exit(1); |
| 44 | } |
| 45 | fclose(v); |
| 46 | for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} |
| 47 | *z = 0; |
| 48 | printf("#define RELEASE_VERSION \"%s\"\n", b); |
| 49 | x=0; |
| 50 | i=0; |
| 51 | z=b; |
| 52 | while(1){ |
| 53 | if( z[0]>='0' && z[0]<='9' ){ |
| 54 |
| --- src/mkversion.c | |
| +++ src/mkversion.c | |
| @@ -43,11 +43,11 @@ | |
| 43 | exit(1); |
| 44 | } |
| 45 | fclose(v); |
| 46 | for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){} |
| 47 | *z = 0; |
| 48 | printf("#define RELEASE_VERSION \"%s\"\n", b); |
| 49 | x=0; |
| 50 | i=0; |
| 51 | z=b; |
| 52 | while(1){ |
| 53 | if( z[0]>='0' && z[0]<='9' ){ |
| 54 |
+1
-1
| --- src/publish.c | ||
| +++ src/publish.c | ||
| @@ -58,11 +58,11 @@ | ||
| 58 | 58 | ** |
| 59 | 59 | ** Cause artifacts identified by TAGS... to be published (made non-private). |
| 60 | 60 | ** This can be used (for example) to convert a private branch into a public |
| 61 | 61 | ** branch, or to publish a bundle that was imported privately. |
| 62 | 62 | ** |
| 63 | -** If any of TAGS names a branch, then all checkins on that most recent | |
| 63 | +** If any of TAGS names a branch, then all checkins on the most recent | |
| 64 | 64 | ** instance of that branch are included, not just the most recent checkin. |
| 65 | 65 | ** |
| 66 | 66 | ** If any of TAGS name checkins then all files and tags associated with |
| 67 | 67 | ** those checkins are also published automatically. Except if the --only |
| 68 | 68 | ** option is used, then only the specific artifacts identified by TAGS |
| 69 | 69 |
| --- src/publish.c | |
| +++ src/publish.c | |
| @@ -58,11 +58,11 @@ | |
| 58 | ** |
| 59 | ** Cause artifacts identified by TAGS... to be published (made non-private). |
| 60 | ** This can be used (for example) to convert a private branch into a public |
| 61 | ** branch, or to publish a bundle that was imported privately. |
| 62 | ** |
| 63 | ** If any of TAGS names a branch, then all checkins on that most recent |
| 64 | ** instance of that branch are included, not just the most recent checkin. |
| 65 | ** |
| 66 | ** If any of TAGS name checkins then all files and tags associated with |
| 67 | ** those checkins are also published automatically. Except if the --only |
| 68 | ** option is used, then only the specific artifacts identified by TAGS |
| 69 |
| --- src/publish.c | |
| +++ src/publish.c | |
| @@ -58,11 +58,11 @@ | |
| 58 | ** |
| 59 | ** Cause artifacts identified by TAGS... to be published (made non-private). |
| 60 | ** This can be used (for example) to convert a private branch into a public |
| 61 | ** branch, or to publish a bundle that was imported privately. |
| 62 | ** |
| 63 | ** If any of TAGS names a branch, then all checkins on the most recent |
| 64 | ** instance of that branch are included, not just the most recent checkin. |
| 65 | ** |
| 66 | ** If any of TAGS name checkins then all files and tags associated with |
| 67 | ** those checkins are also published automatically. Except if the --only |
| 68 | ** option is used, then only the specific artifacts identified by TAGS |
| 69 |
+9
-9
| --- src/regexp.c | ||
| +++ src/regexp.c | ||
| @@ -127,11 +127,11 @@ | ||
| 127 | 127 | pSet->aState[pSet->nState++] = newState; |
| 128 | 128 | } |
| 129 | 129 | |
| 130 | 130 | /* Extract the next unicode character from *pzIn and return it. Advance |
| 131 | 131 | ** *pzIn to the first byte past the end of the character returned. To |
| 132 | -** be clear: this routine converts utf8 to unicode. This routine is | |
| 132 | +** be clear: this routine converts utf8 to unicode. This routine is | |
| 133 | 133 | ** optimized for the common case where the next character is a single byte. |
| 134 | 134 | */ |
| 135 | 135 | static unsigned re_next_char(ReInput *p){ |
| 136 | 136 | unsigned c; |
| 137 | 137 | if( p->i>=p->mx ) return 0; |
| @@ -191,16 +191,16 @@ | ||
| 191 | 191 | int rc = 0; |
| 192 | 192 | ReInput in; |
| 193 | 193 | |
| 194 | 194 | in.z = zIn; |
| 195 | 195 | in.i = 0; |
| 196 | - in.mx = nIn>=0 ? nIn : strlen((char const*)zIn); | |
| 196 | + in.mx = nIn>=0 ? nIn : strlen((const char*)zIn); | |
| 197 | 197 | |
| 198 | 198 | /* Look for the initial prefix match, if there is one. */ |
| 199 | 199 | if( pRe->nInit ){ |
| 200 | 200 | unsigned char x = pRe->zInit[0]; |
| 201 | - while( in.i+pRe->nInit<=in.mx | |
| 201 | + while( in.i+pRe->nInit<=in.mx | |
| 202 | 202 | && (zIn[in.i]!=x || |
| 203 | 203 | strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0) |
| 204 | 204 | ){ |
| 205 | 205 | in.i++; |
| 206 | 206 | } |
| @@ -303,11 +303,11 @@ | ||
| 303 | 303 | } |
| 304 | 304 | } |
| 305 | 305 | } |
| 306 | 306 | if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; |
| 307 | 307 | if( hit ) re_add_state(pNext, x+n); |
| 308 | - break; | |
| 308 | + break; | |
| 309 | 309 | } |
| 310 | 310 | } |
| 311 | 311 | } |
| 312 | 312 | } |
| 313 | 313 | for(i=0; i<pNext->nState; i++){ |
| @@ -464,11 +464,11 @@ | ||
| 464 | 464 | const char *zErr; |
| 465 | 465 | while( (c = p->xNextChar(&p->sIn))!=0 ){ |
| 466 | 466 | iStart = p->nState; |
| 467 | 467 | switch( c ){ |
| 468 | 468 | case '|': |
| 469 | - case '$': | |
| 469 | + case '$': | |
| 470 | 470 | case ')': { |
| 471 | 471 | p->sIn.i--; |
| 472 | 472 | return 0; |
| 473 | 473 | } |
| 474 | 474 | case '(': { |
| @@ -480,11 +480,11 @@ | ||
| 480 | 480 | } |
| 481 | 481 | case '.': { |
| 482 | 482 | if( rePeek(p)=='*' ){ |
| 483 | 483 | re_append(p, RE_OP_ANYSTAR, 0); |
| 484 | 484 | p->sIn.i++; |
| 485 | - }else{ | |
| 485 | + }else{ | |
| 486 | 486 | re_append(p, RE_OP_ANY, 0); |
| 487 | 487 | } |
| 488 | 488 | break; |
| 489 | 489 | } |
| 490 | 490 | case '*': { |
| @@ -652,11 +652,11 @@ | ||
| 652 | 652 | } |
| 653 | 653 | |
| 654 | 654 | /* The following is a performance optimization. If the regex begins with |
| 655 | 655 | ** ".*" (if the input regex lacks an initial "^") and afterwards there are |
| 656 | 656 | ** one or more matching characters, enter those matching characters into |
| 657 | - ** zInit[]. The re_match() routine can then search ahead in the input | |
| 657 | + ** zInit[]. The re_match() routine can then search ahead in the input | |
| 658 | 658 | ** string looking for the initial match without having to run the whole |
| 659 | 659 | ** regex engine over the string. Do not worry able trying to match |
| 660 | 660 | ** unicode characters beyond plane 0 - those are very rare and this is |
| 661 | 661 | ** just an optimization. */ |
| 662 | 662 | if( pRe->aOp[0]==RE_OP_ANYSTAR ){ |
| @@ -689,12 +689,12 @@ | ||
| 689 | 689 | ** A REGEXP B |
| 690 | 690 | ** |
| 691 | 691 | ** is implemented as regexp(B,A). |
| 692 | 692 | */ |
| 693 | 693 | static void re_sql_func( |
| 694 | - sqlite3_context *context, | |
| 695 | - int argc, | |
| 694 | + sqlite3_context *context, | |
| 695 | + int argc, | |
| 696 | 696 | sqlite3_value **argv |
| 697 | 697 | ){ |
| 698 | 698 | ReCompiled *pRe; /* Compiled regular expression */ |
| 699 | 699 | const char *zPattern; /* The regular expression */ |
| 700 | 700 | const unsigned char *zStr;/* String being searched */ |
| 701 | 701 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -127,11 +127,11 @@ | |
| 127 | pSet->aState[pSet->nState++] = newState; |
| 128 | } |
| 129 | |
| 130 | /* Extract the next unicode character from *pzIn and return it. Advance |
| 131 | ** *pzIn to the first byte past the end of the character returned. To |
| 132 | ** be clear: this routine converts utf8 to unicode. This routine is |
| 133 | ** optimized for the common case where the next character is a single byte. |
| 134 | */ |
| 135 | static unsigned re_next_char(ReInput *p){ |
| 136 | unsigned c; |
| 137 | if( p->i>=p->mx ) return 0; |
| @@ -191,16 +191,16 @@ | |
| 191 | int rc = 0; |
| 192 | ReInput in; |
| 193 | |
| 194 | in.z = zIn; |
| 195 | in.i = 0; |
| 196 | in.mx = nIn>=0 ? nIn : strlen((char const*)zIn); |
| 197 | |
| 198 | /* Look for the initial prefix match, if there is one. */ |
| 199 | if( pRe->nInit ){ |
| 200 | unsigned char x = pRe->zInit[0]; |
| 201 | while( in.i+pRe->nInit<=in.mx |
| 202 | && (zIn[in.i]!=x || |
| 203 | strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0) |
| 204 | ){ |
| 205 | in.i++; |
| 206 | } |
| @@ -303,11 +303,11 @@ | |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; |
| 307 | if( hit ) re_add_state(pNext, x+n); |
| 308 | break; |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | } |
| 313 | for(i=0; i<pNext->nState; i++){ |
| @@ -464,11 +464,11 @@ | |
| 464 | const char *zErr; |
| 465 | while( (c = p->xNextChar(&p->sIn))!=0 ){ |
| 466 | iStart = p->nState; |
| 467 | switch( c ){ |
| 468 | case '|': |
| 469 | case '$': |
| 470 | case ')': { |
| 471 | p->sIn.i--; |
| 472 | return 0; |
| 473 | } |
| 474 | case '(': { |
| @@ -480,11 +480,11 @@ | |
| 480 | } |
| 481 | case '.': { |
| 482 | if( rePeek(p)=='*' ){ |
| 483 | re_append(p, RE_OP_ANYSTAR, 0); |
| 484 | p->sIn.i++; |
| 485 | }else{ |
| 486 | re_append(p, RE_OP_ANY, 0); |
| 487 | } |
| 488 | break; |
| 489 | } |
| 490 | case '*': { |
| @@ -652,11 +652,11 @@ | |
| 652 | } |
| 653 | |
| 654 | /* The following is a performance optimization. If the regex begins with |
| 655 | ** ".*" (if the input regex lacks an initial "^") and afterwards there are |
| 656 | ** one or more matching characters, enter those matching characters into |
| 657 | ** zInit[]. The re_match() routine can then search ahead in the input |
| 658 | ** string looking for the initial match without having to run the whole |
| 659 | ** regex engine over the string. Do not worry able trying to match |
| 660 | ** unicode characters beyond plane 0 - those are very rare and this is |
| 661 | ** just an optimization. */ |
| 662 | if( pRe->aOp[0]==RE_OP_ANYSTAR ){ |
| @@ -689,12 +689,12 @@ | |
| 689 | ** A REGEXP B |
| 690 | ** |
| 691 | ** is implemented as regexp(B,A). |
| 692 | */ |
| 693 | static void re_sql_func( |
| 694 | sqlite3_context *context, |
| 695 | int argc, |
| 696 | sqlite3_value **argv |
| 697 | ){ |
| 698 | ReCompiled *pRe; /* Compiled regular expression */ |
| 699 | const char *zPattern; /* The regular expression */ |
| 700 | const unsigned char *zStr;/* String being searched */ |
| 701 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -127,11 +127,11 @@ | |
| 127 | pSet->aState[pSet->nState++] = newState; |
| 128 | } |
| 129 | |
| 130 | /* Extract the next unicode character from *pzIn and return it. Advance |
| 131 | ** *pzIn to the first byte past the end of the character returned. To |
| 132 | ** be clear: this routine converts utf8 to unicode. This routine is |
| 133 | ** optimized for the common case where the next character is a single byte. |
| 134 | */ |
| 135 | static unsigned re_next_char(ReInput *p){ |
| 136 | unsigned c; |
| 137 | if( p->i>=p->mx ) return 0; |
| @@ -191,16 +191,16 @@ | |
| 191 | int rc = 0; |
| 192 | ReInput in; |
| 193 | |
| 194 | in.z = zIn; |
| 195 | in.i = 0; |
| 196 | in.mx = nIn>=0 ? nIn : strlen((const char*)zIn); |
| 197 | |
| 198 | /* Look for the initial prefix match, if there is one. */ |
| 199 | if( pRe->nInit ){ |
| 200 | unsigned char x = pRe->zInit[0]; |
| 201 | while( in.i+pRe->nInit<=in.mx |
| 202 | && (zIn[in.i]!=x || |
| 203 | strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0) |
| 204 | ){ |
| 205 | in.i++; |
| 206 | } |
| @@ -303,11 +303,11 @@ | |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | if( pRe->aOp[x]==RE_OP_CC_EXC ) hit = !hit; |
| 307 | if( hit ) re_add_state(pNext, x+n); |
| 308 | break; |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | } |
| 313 | for(i=0; i<pNext->nState; i++){ |
| @@ -464,11 +464,11 @@ | |
| 464 | const char *zErr; |
| 465 | while( (c = p->xNextChar(&p->sIn))!=0 ){ |
| 466 | iStart = p->nState; |
| 467 | switch( c ){ |
| 468 | case '|': |
| 469 | case '$': |
| 470 | case ')': { |
| 471 | p->sIn.i--; |
| 472 | return 0; |
| 473 | } |
| 474 | case '(': { |
| @@ -480,11 +480,11 @@ | |
| 480 | } |
| 481 | case '.': { |
| 482 | if( rePeek(p)=='*' ){ |
| 483 | re_append(p, RE_OP_ANYSTAR, 0); |
| 484 | p->sIn.i++; |
| 485 | }else{ |
| 486 | re_append(p, RE_OP_ANY, 0); |
| 487 | } |
| 488 | break; |
| 489 | } |
| 490 | case '*': { |
| @@ -652,11 +652,11 @@ | |
| 652 | } |
| 653 | |
| 654 | /* The following is a performance optimization. If the regex begins with |
| 655 | ** ".*" (if the input regex lacks an initial "^") and afterwards there are |
| 656 | ** one or more matching characters, enter those matching characters into |
| 657 | ** zInit[]. The re_match() routine can then search ahead in the input |
| 658 | ** string looking for the initial match without having to run the whole |
| 659 | ** regex engine over the string. Do not worry able trying to match |
| 660 | ** unicode characters beyond plane 0 - those are very rare and this is |
| 661 | ** just an optimization. */ |
| 662 | if( pRe->aOp[0]==RE_OP_ANYSTAR ){ |
| @@ -689,12 +689,12 @@ | |
| 689 | ** A REGEXP B |
| 690 | ** |
| 691 | ** is implemented as regexp(B,A). |
| 692 | */ |
| 693 | static void re_sql_func( |
| 694 | sqlite3_context *context, |
| 695 | int argc, |
| 696 | sqlite3_value **argv |
| 697 | ){ |
| 698 | ReCompiled *pRe; /* Compiled regular expression */ |
| 699 | const char *zPattern; /* The regular expression */ |
| 700 | const unsigned char *zStr;/* String being searched */ |
| 701 |
+89
-30
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -171,10 +171,11 @@ | ||
| 171 | 171 | /* We've already seen an error. No need to continue. */ |
| 172 | 172 | return SQLITE_OK; |
| 173 | 173 | } |
| 174 | 174 | switch( code ){ |
| 175 | 175 | case SQLITE_SELECT: |
| 176 | + case SQLITE_RECURSIVE: | |
| 176 | 177 | case SQLITE_FUNCTION: { |
| 177 | 178 | break; |
| 178 | 179 | } |
| 179 | 180 | case SQLITE_READ: { |
| 180 | 181 | static const char *const azAllowed[] = { |
| @@ -201,15 +202,10 @@ | ||
| 201 | 202 | }else if( !g.perm.RdAddr && strncmp(zArg2, "private_", 8)==0 ){ |
| 202 | 203 | rc = SQLITE_IGNORE; |
| 203 | 204 | } |
| 204 | 205 | break; |
| 205 | 206 | } |
| 206 | - case SQLITE_RECURSIVE: { | |
| 207 | - *(char**)pError = mprintf("recursive queries are not allowed"); | |
| 208 | - rc = SQLITE_DENY; | |
| 209 | - break; | |
| 210 | - } | |
| 211 | 207 | default: { |
| 212 | 208 | *(char**)pError = mprintf("only SELECT statements are allowed"); |
| 213 | 209 | rc = SQLITE_DENY; |
| 214 | 210 | break; |
| 215 | 211 | } |
| @@ -240,15 +236,17 @@ | ||
| 240 | 236 | const char *zTail; |
| 241 | 237 | sqlite3_stmt *pStmt; |
| 242 | 238 | int rc; |
| 243 | 239 | |
| 244 | 240 | /* First make sure the SQL is a single query command by verifying that |
| 245 | - ** the first token is "SELECT" and that there are no unquoted semicolons. | |
| 241 | + ** the first token is "SELECT" or "WITH" and that there are no unquoted | |
| 242 | + ** semicolons. | |
| 246 | 243 | */ |
| 247 | 244 | for(i=0; fossil_isspace(zSql[i]); i++){} |
| 248 | - if( fossil_strnicmp(&zSql[i],"select",6)!=0 ){ | |
| 249 | - return mprintf("The SQL must be a SELECT statement"); | |
| 245 | + if( fossil_strnicmp(&zSql[i], "select", 6)!=0 | |
| 246 | + && fossil_strnicmp(&zSql[i], "with", 4)!=0 ){ | |
| 247 | + return mprintf("The SQL must be a SELECT or WITH statement"); | |
| 250 | 248 | } |
| 251 | 249 | for(i=0; zSql[i]; i++){ |
| 252 | 250 | if( zSql[i]==';' ){ |
| 253 | 251 | int bad; |
| 254 | 252 | int c = zSql[i+1]; |
| @@ -922,79 +920,140 @@ | ||
| 922 | 920 | |
| 923 | 921 | /* |
| 924 | 922 | ** Output Javascript code that will enables sorting of the table with |
| 925 | 923 | ** the id zTableId by clicking. |
| 926 | 924 | ** |
| 927 | -** The javascript is derived from: | |
| 925 | +** The javascript was originally derived from: | |
| 928 | 926 | ** |
| 929 | 927 | ** http://www.webtoolkit.info/sortable-html-table.html |
| 928 | +** | |
| 929 | +** But there have been extensive modifications. | |
| 930 | 930 | ** |
| 931 | 931 | ** This variation allows column types to be expressed using the second |
| 932 | 932 | ** argument. Each character of the second argument represent a column. |
| 933 | -** "t" means sort as text. "n" means sort numerically. "x" means do not | |
| 934 | -** sort on this column. If there are fewer characters in zColumnTypes[] than | |
| 935 | -** their are columns, the all extra columns assume type "t" (text). | |
| 933 | +** | |
| 934 | +** t Sort by text | |
| 935 | +** n Sort numerically | |
| 936 | +** k Sort by the data-sortkey property | |
| 937 | +** x This column is not sortable | |
| 938 | +** | |
| 939 | +** Capital letters mean sort in reverse order. | |
| 940 | +** If there are fewer characters in zColumnTypes[] than their are columns, | |
| 941 | +** the all extra columns assume type "t" (text). | |
| 942 | +** | |
| 943 | +** The third parameter is the column that was initially sorted (using 1-based | |
| 944 | +** column numbers, like SQL). Make this value 0 if none of the columns are | |
| 945 | +** initially sorted. Make the value negative if the column is initially sorted | |
| 946 | +** in reverse order. | |
| 947 | +** | |
| 948 | +** Clicking on the same column header twice in a row inverts the sort. | |
| 936 | 949 | */ |
| 937 | -void output_table_sorting_javascript(const char *zTableId, const char *zColumnTypes){ | |
| 950 | +void output_table_sorting_javascript( | |
| 951 | + const char *zTableId, /* ID of table to sort */ | |
| 952 | + const char *zColumnTypes, /* String for column types */ | |
| 953 | + int iInitSort /* Initially sorted column. Leftmost is 1. 0 for NONE */ | |
| 954 | +){ | |
| 938 | 955 | @ <script> |
| 939 | - @ function SortableTable(tableEl,columnTypes){ | |
| 956 | + @ function SortableTable(tableEl,columnTypes,initSort){ | |
| 940 | 957 | @ this.tbody = tableEl.getElementsByTagName('tbody'); |
| 958 | + @ this.columnTypes = columnTypes; | |
| 941 | 959 | @ this.sort = function (cell) { |
| 942 | 960 | @ var column = cell.cellIndex; |
| 943 | - @ var sortFn = cell.sortType=="n" ? this.sortNumeric : this.sortText; | |
| 961 | + @ var sortFn; | |
| 962 | + @ switch( cell.sortType ){ | |
| 963 | + @ case "N": case "n": sortFn = this.sortNumeric; break; | |
| 964 | + @ case "T": case "t": sortFn = this.sortText; break; | |
| 965 | + @ case "K": case "k": sortFn = this.sortKey; break; | |
| 966 | + @ default: return; | |
| 967 | + @ } | |
| 944 | 968 | @ this.sortIndex = column; |
| 945 | 969 | @ var newRows = new Array(); |
| 946 | 970 | @ for (j = 0; j < this.tbody[0].rows.length; j++) { |
| 947 | 971 | @ newRows[j] = this.tbody[0].rows[j]; |
| 948 | 972 | @ } |
| 949 | - @ newRows.sort(sortFn); | |
| 950 | - @ if (cell.getAttribute("sortdir") == 'down') { | |
| 951 | - @ newRows.reverse(); | |
| 952 | - @ cell.setAttribute('sortdir','up'); | |
| 953 | - @ } else { | |
| 954 | - @ cell.setAttribute('sortdir','down'); | |
| 973 | + @ if( this.sortIndex==Math.abs(this.prevColumn)-1 ){ | |
| 974 | + @ newRows.reverse(); | |
| 975 | + @ this.prevColumn = -this.prevColumn; | |
| 976 | + @ }else{ | |
| 977 | + @ newRows.sort(sortFn); | |
| 978 | + @ this.prevColumn = this.sortIndex+1; | |
| 979 | + @ if( cell.sortType>="A" && cell.sortType<="Z" ){ | |
| 980 | + @ newRows.reverse(); | |
| 981 | + @ } | |
| 955 | 982 | @ } |
| 956 | 983 | @ for (i=0;i<newRows.length;i++) { |
| 957 | 984 | @ this.tbody[0].appendChild(newRows[i]); |
| 958 | 985 | @ } |
| 986 | + @ this.setHdrIcons(); | |
| 987 | + @ } | |
| 988 | + @ this.setHdrIcons = function() { | |
| 989 | + @ for (var i=0; i<this.hdrRow.cells.length; i++) { | |
| 990 | + @ if( this.columnTypes[i]=='x' ) continue; | |
| 991 | + @ var sortType; | |
| 992 | + @ if( this.prevColumn==i+1 ){ | |
| 993 | + @ sortType = 'asc'; | |
| 994 | + @ }else if( this.prevColumn==(-1-i) ){ | |
| 995 | + @ sortType = 'desc' | |
| 996 | + @ }else{ | |
| 997 | + @ sortType = 'none'; | |
| 998 | + @ } | |
| 999 | + @ var hdrCell = this.hdrRow.cells[i]; | |
| 1000 | + @ var clsName = hdrCell.className.replace(/\s*\bsort\s*\w+/, ''); | |
| 1001 | + @ clsName += ' sort ' + sortType; | |
| 1002 | + @ hdrCell.className = clsName; | |
| 1003 | + @ } | |
| 959 | 1004 | @ } |
| 960 | 1005 | @ this.sortText = function(a,b) { |
| 961 | 1006 | @ var i = thisObject.sortIndex; |
| 962 | 1007 | @ aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); |
| 963 | 1008 | @ bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); |
| 964 | - @ if(aa==bb) return 0; | |
| 1009 | + @ if(aa==bb) return a.rowIndex-b.rowIndex; | |
| 965 | 1010 | @ if(aa<bb) return -1; |
| 966 | 1011 | @ return 1; |
| 967 | 1012 | @ } |
| 968 | 1013 | @ this.sortNumeric = function(a,b) { |
| 969 | 1014 | @ var i = thisObject.sortIndex; |
| 970 | 1015 | @ aa = parseFloat(a.cells[i].textContent); |
| 971 | 1016 | @ if (isNaN(aa)) aa = 0; |
| 972 | 1017 | @ bb = parseFloat(b.cells[i].textContent); |
| 973 | 1018 | @ if (isNaN(bb)) bb = 0; |
| 1019 | + @ if(aa==bb) return a.rowIndex-b.rowIndex; | |
| 974 | 1020 | @ return aa-bb; |
| 975 | 1021 | @ } |
| 976 | - @ var thisObject = this; | |
| 1022 | + @ this.sortKey = function(a,b) { | |
| 1023 | + @ var i = thisObject.sortIndex; | |
| 1024 | + @ aa = a.cells[i].getAttribute("data-sortkey"); | |
| 1025 | + @ bb = b.cells[i].getAttribute("data-sortkey"); | |
| 1026 | + @ if(aa==bb) return a.rowIndex-b.rowIndex; | |
| 1027 | + @ if(aa<bb) return -1; | |
| 1028 | + @ return 1; | |
| 1029 | + @ } | |
| 977 | 1030 | @ var x = tableEl.getElementsByTagName('thead'); |
| 978 | 1031 | @ if(!(this.tbody && this.tbody[0].rows && this.tbody[0].rows.length>0)){ |
| 979 | 1032 | @ return; |
| 980 | 1033 | @ } |
| 981 | 1034 | @ if(x && x[0].rows && x[0].rows.length > 0) { |
| 982 | - @ var sortRow = x[0].rows[0]; | |
| 1035 | + @ this.hdrRow = x[0].rows[0]; | |
| 983 | 1036 | @ } else { |
| 984 | 1037 | @ return; |
| 985 | 1038 | @ } |
| 986 | - @ for (var i=0; i<sortRow.cells.length; i++) { | |
| 987 | - @ sortRow.cells[i].sTable = this; | |
| 988 | - @ sortRow.cells[i].sortType = columnTypes[i] || 't'; | |
| 989 | - @ sortRow.cells[i].onclick = function () { | |
| 1039 | + @ var thisObject = this; | |
| 1040 | + @ this.prevColumn = initSort; | |
| 1041 | + @ for (var i=0; i<this.hdrRow.cells.length; i++) { | |
| 1042 | + @ if( columnTypes[i]=='x' ) continue; | |
| 1043 | + @ var hdrcell = this.hdrRow.cells[i]; | |
| 1044 | + @ hdrcell.sTable = this; | |
| 1045 | + @ hdrcell.style.cursor = "pointer"; | |
| 1046 | + @ hdrcell.sortType = columnTypes[i] || 't'; | |
| 1047 | + @ hdrcell.onclick = function () { | |
| 990 | 1048 | @ this.sTable.sort(this); |
| 991 | 1049 | @ return false; |
| 992 | 1050 | @ } |
| 993 | 1051 | @ } |
| 1052 | + @ this.setHdrIcons() | |
| 994 | 1053 | @ } |
| 995 | - @ var t = new SortableTable(gebi("%s(zTableId)"),"%s(zColumnTypes)"); | |
| 1054 | + @ var t = new SortableTable(gebi("%s(zTableId)"),"%s(zColumnTypes)",%d(iInitSort)); | |
| 996 | 1055 | @ </script> |
| 997 | 1056 | } |
| 998 | 1057 | |
| 999 | 1058 | |
| 1000 | 1059 | /* |
| @@ -1086,11 +1145,11 @@ | ||
| 1086 | 1145 | if( zErr1 ){ |
| 1087 | 1146 | @ <p class="reportError">Error: %h(zErr1)</p> |
| 1088 | 1147 | }else if( zErr2 ){ |
| 1089 | 1148 | @ <p class="reportError">Error: %h(zErr2)</p> |
| 1090 | 1149 | } |
| 1091 | - output_table_sorting_javascript("reportTable",""); | |
| 1150 | + output_table_sorting_javascript("reportTable","",0); | |
| 1092 | 1151 | style_footer(); |
| 1093 | 1152 | }else{ |
| 1094 | 1153 | report_restrict_sql(&zErr1); |
| 1095 | 1154 | db_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2); |
| 1096 | 1155 | report_unrestrict_sql(); |
| 1097 | 1156 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -171,10 +171,11 @@ | |
| 171 | /* We've already seen an error. No need to continue. */ |
| 172 | return SQLITE_OK; |
| 173 | } |
| 174 | switch( code ){ |
| 175 | case SQLITE_SELECT: |
| 176 | case SQLITE_FUNCTION: { |
| 177 | break; |
| 178 | } |
| 179 | case SQLITE_READ: { |
| 180 | static const char *const azAllowed[] = { |
| @@ -201,15 +202,10 @@ | |
| 201 | }else if( !g.perm.RdAddr && strncmp(zArg2, "private_", 8)==0 ){ |
| 202 | rc = SQLITE_IGNORE; |
| 203 | } |
| 204 | break; |
| 205 | } |
| 206 | case SQLITE_RECURSIVE: { |
| 207 | *(char**)pError = mprintf("recursive queries are not allowed"); |
| 208 | rc = SQLITE_DENY; |
| 209 | break; |
| 210 | } |
| 211 | default: { |
| 212 | *(char**)pError = mprintf("only SELECT statements are allowed"); |
| 213 | rc = SQLITE_DENY; |
| 214 | break; |
| 215 | } |
| @@ -240,15 +236,17 @@ | |
| 240 | const char *zTail; |
| 241 | sqlite3_stmt *pStmt; |
| 242 | int rc; |
| 243 | |
| 244 | /* First make sure the SQL is a single query command by verifying that |
| 245 | ** the first token is "SELECT" and that there are no unquoted semicolons. |
| 246 | */ |
| 247 | for(i=0; fossil_isspace(zSql[i]); i++){} |
| 248 | if( fossil_strnicmp(&zSql[i],"select",6)!=0 ){ |
| 249 | return mprintf("The SQL must be a SELECT statement"); |
| 250 | } |
| 251 | for(i=0; zSql[i]; i++){ |
| 252 | if( zSql[i]==';' ){ |
| 253 | int bad; |
| 254 | int c = zSql[i+1]; |
| @@ -922,79 +920,140 @@ | |
| 922 | |
| 923 | /* |
| 924 | ** Output Javascript code that will enables sorting of the table with |
| 925 | ** the id zTableId by clicking. |
| 926 | ** |
| 927 | ** The javascript is derived from: |
| 928 | ** |
| 929 | ** http://www.webtoolkit.info/sortable-html-table.html |
| 930 | ** |
| 931 | ** This variation allows column types to be expressed using the second |
| 932 | ** argument. Each character of the second argument represent a column. |
| 933 | ** "t" means sort as text. "n" means sort numerically. "x" means do not |
| 934 | ** sort on this column. If there are fewer characters in zColumnTypes[] than |
| 935 | ** their are columns, the all extra columns assume type "t" (text). |
| 936 | */ |
| 937 | void output_table_sorting_javascript(const char *zTableId, const char *zColumnTypes){ |
| 938 | @ <script> |
| 939 | @ function SortableTable(tableEl,columnTypes){ |
| 940 | @ this.tbody = tableEl.getElementsByTagName('tbody'); |
| 941 | @ this.sort = function (cell) { |
| 942 | @ var column = cell.cellIndex; |
| 943 | @ var sortFn = cell.sortType=="n" ? this.sortNumeric : this.sortText; |
| 944 | @ this.sortIndex = column; |
| 945 | @ var newRows = new Array(); |
| 946 | @ for (j = 0; j < this.tbody[0].rows.length; j++) { |
| 947 | @ newRows[j] = this.tbody[0].rows[j]; |
| 948 | @ } |
| 949 | @ newRows.sort(sortFn); |
| 950 | @ if (cell.getAttribute("sortdir") == 'down') { |
| 951 | @ newRows.reverse(); |
| 952 | @ cell.setAttribute('sortdir','up'); |
| 953 | @ } else { |
| 954 | @ cell.setAttribute('sortdir','down'); |
| 955 | @ } |
| 956 | @ for (i=0;i<newRows.length;i++) { |
| 957 | @ this.tbody[0].appendChild(newRows[i]); |
| 958 | @ } |
| 959 | @ } |
| 960 | @ this.sortText = function(a,b) { |
| 961 | @ var i = thisObject.sortIndex; |
| 962 | @ aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); |
| 963 | @ bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); |
| 964 | @ if(aa==bb) return 0; |
| 965 | @ if(aa<bb) return -1; |
| 966 | @ return 1; |
| 967 | @ } |
| 968 | @ this.sortNumeric = function(a,b) { |
| 969 | @ var i = thisObject.sortIndex; |
| 970 | @ aa = parseFloat(a.cells[i].textContent); |
| 971 | @ if (isNaN(aa)) aa = 0; |
| 972 | @ bb = parseFloat(b.cells[i].textContent); |
| 973 | @ if (isNaN(bb)) bb = 0; |
| 974 | @ return aa-bb; |
| 975 | @ } |
| 976 | @ var thisObject = this; |
| 977 | @ var x = tableEl.getElementsByTagName('thead'); |
| 978 | @ if(!(this.tbody && this.tbody[0].rows && this.tbody[0].rows.length>0)){ |
| 979 | @ return; |
| 980 | @ } |
| 981 | @ if(x && x[0].rows && x[0].rows.length > 0) { |
| 982 | @ var sortRow = x[0].rows[0]; |
| 983 | @ } else { |
| 984 | @ return; |
| 985 | @ } |
| 986 | @ for (var i=0; i<sortRow.cells.length; i++) { |
| 987 | @ sortRow.cells[i].sTable = this; |
| 988 | @ sortRow.cells[i].sortType = columnTypes[i] || 't'; |
| 989 | @ sortRow.cells[i].onclick = function () { |
| 990 | @ this.sTable.sort(this); |
| 991 | @ return false; |
| 992 | @ } |
| 993 | @ } |
| 994 | @ } |
| 995 | @ var t = new SortableTable(gebi("%s(zTableId)"),"%s(zColumnTypes)"); |
| 996 | @ </script> |
| 997 | } |
| 998 | |
| 999 | |
| 1000 | /* |
| @@ -1086,11 +1145,11 @@ | |
| 1086 | if( zErr1 ){ |
| 1087 | @ <p class="reportError">Error: %h(zErr1)</p> |
| 1088 | }else if( zErr2 ){ |
| 1089 | @ <p class="reportError">Error: %h(zErr2)</p> |
| 1090 | } |
| 1091 | output_table_sorting_javascript("reportTable",""); |
| 1092 | style_footer(); |
| 1093 | }else{ |
| 1094 | report_restrict_sql(&zErr1); |
| 1095 | db_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2); |
| 1096 | report_unrestrict_sql(); |
| 1097 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -171,10 +171,11 @@ | |
| 171 | /* We've already seen an error. No need to continue. */ |
| 172 | return SQLITE_OK; |
| 173 | } |
| 174 | switch( code ){ |
| 175 | case SQLITE_SELECT: |
| 176 | case SQLITE_RECURSIVE: |
| 177 | case SQLITE_FUNCTION: { |
| 178 | break; |
| 179 | } |
| 180 | case SQLITE_READ: { |
| 181 | static const char *const azAllowed[] = { |
| @@ -201,15 +202,10 @@ | |
| 202 | }else if( !g.perm.RdAddr && strncmp(zArg2, "private_", 8)==0 ){ |
| 203 | rc = SQLITE_IGNORE; |
| 204 | } |
| 205 | break; |
| 206 | } |
| 207 | default: { |
| 208 | *(char**)pError = mprintf("only SELECT statements are allowed"); |
| 209 | rc = SQLITE_DENY; |
| 210 | break; |
| 211 | } |
| @@ -240,15 +236,17 @@ | |
| 236 | const char *zTail; |
| 237 | sqlite3_stmt *pStmt; |
| 238 | int rc; |
| 239 | |
| 240 | /* First make sure the SQL is a single query command by verifying that |
| 241 | ** the first token is "SELECT" or "WITH" and that there are no unquoted |
| 242 | ** semicolons. |
| 243 | */ |
| 244 | for(i=0; fossil_isspace(zSql[i]); i++){} |
| 245 | if( fossil_strnicmp(&zSql[i], "select", 6)!=0 |
| 246 | && fossil_strnicmp(&zSql[i], "with", 4)!=0 ){ |
| 247 | return mprintf("The SQL must be a SELECT or WITH statement"); |
| 248 | } |
| 249 | for(i=0; zSql[i]; i++){ |
| 250 | if( zSql[i]==';' ){ |
| 251 | int bad; |
| 252 | int c = zSql[i+1]; |
| @@ -922,79 +920,140 @@ | |
| 920 | |
| 921 | /* |
| 922 | ** Output Javascript code that will enables sorting of the table with |
| 923 | ** the id zTableId by clicking. |
| 924 | ** |
| 925 | ** The javascript was originally derived from: |
| 926 | ** |
| 927 | ** http://www.webtoolkit.info/sortable-html-table.html |
| 928 | ** |
| 929 | ** But there have been extensive modifications. |
| 930 | ** |
| 931 | ** This variation allows column types to be expressed using the second |
| 932 | ** argument. Each character of the second argument represent a column. |
| 933 | ** |
| 934 | ** t Sort by text |
| 935 | ** n Sort numerically |
| 936 | ** k Sort by the data-sortkey property |
| 937 | ** x This column is not sortable |
| 938 | ** |
| 939 | ** Capital letters mean sort in reverse order. |
| 940 | ** If there are fewer characters in zColumnTypes[] than their are columns, |
| 941 | ** the all extra columns assume type "t" (text). |
| 942 | ** |
| 943 | ** The third parameter is the column that was initially sorted (using 1-based |
| 944 | ** column numbers, like SQL). Make this value 0 if none of the columns are |
| 945 | ** initially sorted. Make the value negative if the column is initially sorted |
| 946 | ** in reverse order. |
| 947 | ** |
| 948 | ** Clicking on the same column header twice in a row inverts the sort. |
| 949 | */ |
| 950 | void output_table_sorting_javascript( |
| 951 | const char *zTableId, /* ID of table to sort */ |
| 952 | const char *zColumnTypes, /* String for column types */ |
| 953 | int iInitSort /* Initially sorted column. Leftmost is 1. 0 for NONE */ |
| 954 | ){ |
| 955 | @ <script> |
| 956 | @ function SortableTable(tableEl,columnTypes,initSort){ |
| 957 | @ this.tbody = tableEl.getElementsByTagName('tbody'); |
| 958 | @ this.columnTypes = columnTypes; |
| 959 | @ this.sort = function (cell) { |
| 960 | @ var column = cell.cellIndex; |
| 961 | @ var sortFn; |
| 962 | @ switch( cell.sortType ){ |
| 963 | @ case "N": case "n": sortFn = this.sortNumeric; break; |
| 964 | @ case "T": case "t": sortFn = this.sortText; break; |
| 965 | @ case "K": case "k": sortFn = this.sortKey; break; |
| 966 | @ default: return; |
| 967 | @ } |
| 968 | @ this.sortIndex = column; |
| 969 | @ var newRows = new Array(); |
| 970 | @ for (j = 0; j < this.tbody[0].rows.length; j++) { |
| 971 | @ newRows[j] = this.tbody[0].rows[j]; |
| 972 | @ } |
| 973 | @ if( this.sortIndex==Math.abs(this.prevColumn)-1 ){ |
| 974 | @ newRows.reverse(); |
| 975 | @ this.prevColumn = -this.prevColumn; |
| 976 | @ }else{ |
| 977 | @ newRows.sort(sortFn); |
| 978 | @ this.prevColumn = this.sortIndex+1; |
| 979 | @ if( cell.sortType>="A" && cell.sortType<="Z" ){ |
| 980 | @ newRows.reverse(); |
| 981 | @ } |
| 982 | @ } |
| 983 | @ for (i=0;i<newRows.length;i++) { |
| 984 | @ this.tbody[0].appendChild(newRows[i]); |
| 985 | @ } |
| 986 | @ this.setHdrIcons(); |
| 987 | @ } |
| 988 | @ this.setHdrIcons = function() { |
| 989 | @ for (var i=0; i<this.hdrRow.cells.length; i++) { |
| 990 | @ if( this.columnTypes[i]=='x' ) continue; |
| 991 | @ var sortType; |
| 992 | @ if( this.prevColumn==i+1 ){ |
| 993 | @ sortType = 'asc'; |
| 994 | @ }else if( this.prevColumn==(-1-i) ){ |
| 995 | @ sortType = 'desc' |
| 996 | @ }else{ |
| 997 | @ sortType = 'none'; |
| 998 | @ } |
| 999 | @ var hdrCell = this.hdrRow.cells[i]; |
| 1000 | @ var clsName = hdrCell.className.replace(/\s*\bsort\s*\w+/, ''); |
| 1001 | @ clsName += ' sort ' + sortType; |
| 1002 | @ hdrCell.className = clsName; |
| 1003 | @ } |
| 1004 | @ } |
| 1005 | @ this.sortText = function(a,b) { |
| 1006 | @ var i = thisObject.sortIndex; |
| 1007 | @ aa = a.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); |
| 1008 | @ bb = b.cells[i].textContent.replace(/^\W+/,'').toLowerCase(); |
| 1009 | @ if(aa==bb) return a.rowIndex-b.rowIndex; |
| 1010 | @ if(aa<bb) return -1; |
| 1011 | @ return 1; |
| 1012 | @ } |
| 1013 | @ this.sortNumeric = function(a,b) { |
| 1014 | @ var i = thisObject.sortIndex; |
| 1015 | @ aa = parseFloat(a.cells[i].textContent); |
| 1016 | @ if (isNaN(aa)) aa = 0; |
| 1017 | @ bb = parseFloat(b.cells[i].textContent); |
| 1018 | @ if (isNaN(bb)) bb = 0; |
| 1019 | @ if(aa==bb) return a.rowIndex-b.rowIndex; |
| 1020 | @ return aa-bb; |
| 1021 | @ } |
| 1022 | @ this.sortKey = function(a,b) { |
| 1023 | @ var i = thisObject.sortIndex; |
| 1024 | @ aa = a.cells[i].getAttribute("data-sortkey"); |
| 1025 | @ bb = b.cells[i].getAttribute("data-sortkey"); |
| 1026 | @ if(aa==bb) return a.rowIndex-b.rowIndex; |
| 1027 | @ if(aa<bb) return -1; |
| 1028 | @ return 1; |
| 1029 | @ } |
| 1030 | @ var x = tableEl.getElementsByTagName('thead'); |
| 1031 | @ if(!(this.tbody && this.tbody[0].rows && this.tbody[0].rows.length>0)){ |
| 1032 | @ return; |
| 1033 | @ } |
| 1034 | @ if(x && x[0].rows && x[0].rows.length > 0) { |
| 1035 | @ this.hdrRow = x[0].rows[0]; |
| 1036 | @ } else { |
| 1037 | @ return; |
| 1038 | @ } |
| 1039 | @ var thisObject = this; |
| 1040 | @ this.prevColumn = initSort; |
| 1041 | @ for (var i=0; i<this.hdrRow.cells.length; i++) { |
| 1042 | @ if( columnTypes[i]=='x' ) continue; |
| 1043 | @ var hdrcell = this.hdrRow.cells[i]; |
| 1044 | @ hdrcell.sTable = this; |
| 1045 | @ hdrcell.style.cursor = "pointer"; |
| 1046 | @ hdrcell.sortType = columnTypes[i] || 't'; |
| 1047 | @ hdrcell.onclick = function () { |
| 1048 | @ this.sTable.sort(this); |
| 1049 | @ return false; |
| 1050 | @ } |
| 1051 | @ } |
| 1052 | @ this.setHdrIcons() |
| 1053 | @ } |
| 1054 | @ var t = new SortableTable(gebi("%s(zTableId)"),"%s(zColumnTypes)",%d(iInitSort)); |
| 1055 | @ </script> |
| 1056 | } |
| 1057 | |
| 1058 | |
| 1059 | /* |
| @@ -1086,11 +1145,11 @@ | |
| 1145 | if( zErr1 ){ |
| 1146 | @ <p class="reportError">Error: %h(zErr1)</p> |
| 1147 | }else if( zErr2 ){ |
| 1148 | @ <p class="reportError">Error: %h(zErr2)</p> |
| 1149 | } |
| 1150 | output_table_sorting_javascript("reportTable","",0); |
| 1151 | style_footer(); |
| 1152 | }else{ |
| 1153 | report_restrict_sql(&zErr1); |
| 1154 | db_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2); |
| 1155 | report_unrestrict_sql(); |
| 1156 |
+35
-12
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -12,11 +12,11 @@ | ||
| 12 | 12 | ** Author contact information: |
| 13 | 13 | ** [email protected] |
| 14 | 14 | ** http://www.hwaci.com/drh/ |
| 15 | 15 | ** |
| 16 | 16 | ******************************************************************************* |
| 17 | -** | |
| 17 | +** | |
| 18 | 18 | ** This file contains string constants that implement the database schema. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "schema.h" |
| 22 | 22 | |
| @@ -45,11 +45,14 @@ | ||
| 45 | 45 | ** we have to execute special procedures to update the schema. When |
| 46 | 46 | ** the aux schema changes, all we need to do is rebuild the database. |
| 47 | 47 | */ |
| 48 | 48 | #define CONTENT_SCHEMA "2" |
| 49 | 49 | #define AUX_SCHEMA_MIN "2011-04-25 19:50" |
| 50 | -#define AUX_SCHEMA_MAX "2014-11-24 20:35" | |
| 50 | +#define AUX_SCHEMA_MAX "2015-01-24" | |
| 51 | +/* NB: Some features require the latest schema. Warning or error messages | |
| 52 | +** will appear if an older schema is used. However, the older schemas are | |
| 53 | +** adequate for many common functions. */ | |
| 51 | 54 | |
| 52 | 55 | #endif /* INTERFACE */ |
| 53 | 56 | |
| 54 | 57 | |
| 55 | 58 | /* |
| @@ -81,11 +84,11 @@ | ||
| 81 | 84 | @ uuid TEXT UNIQUE NOT NULL, -- SHA1 hash of the content |
| 82 | 85 | @ content BLOB, -- Compressed content of this record |
| 83 | 86 | @ CHECK( length(uuid)==40 AND rid>0 ) |
| 84 | 87 | @ ); |
| 85 | 88 | @ CREATE TABLE delta( |
| 86 | -@ rid INTEGER PRIMARY KEY, -- BLOB that is delta-compressed | |
| 89 | +@ rid INTEGER PRIMARY KEY, -- BLOB that is delta-compressed | |
| 87 | 90 | @ srcid INTEGER NOT NULL REFERENCES blob -- Baseline for delta-compression |
| 88 | 91 | @ ); |
| 89 | 92 | @ CREATE INDEX delta_i1 ON delta(srcid); |
| 90 | 93 | @ |
| 91 | 94 | @ ------------------------------------------------------------------------- |
| @@ -126,11 +129,11 @@ | ||
| 126 | 129 | @ info TEXT, -- contact information |
| 127 | 130 | @ mtime DATE, -- last change. seconds since 1970 |
| 128 | 131 | @ photo BLOB -- JPEG image of this user |
| 129 | 132 | @ ); |
| 130 | 133 | @ |
| 131 | -@ -- The VAR table holds miscellanous information about the repository. | |
| 134 | +@ -- The config table holds miscellanous information about the repository. | |
| 132 | 135 | @ -- in the form of name-value pairs. |
| 133 | 136 | @ -- |
| 134 | 137 | @ CREATE TABLE config( |
| 135 | 138 | @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry |
| 136 | 139 | @ value CLOB, -- Content of the named parameter |
| @@ -227,21 +230,41 @@ | ||
| 227 | 230 | @ name TEXT UNIQUE -- Name of file page |
| 228 | 231 | @ ); |
| 229 | 232 | @ |
| 230 | 233 | @ -- Linkages between checkins, files created by each checkin, and |
| 231 | 234 | @ -- the names of those files. |
| 235 | +@ -- | |
| 236 | +@ -- Each entry represents a file that changed content from pid to fid | |
| 237 | +@ -- due to the check-in that goes from pmid to mid. fnid is the name | |
| 238 | +@ -- of the file in the mid check-in. If the file was renamed as part | |
| 239 | +@ -- of the mid check-in, then pfnid is the previous filename. | |
| 240 | +@ | |
| 241 | +@ -- There can be multiple entries for (mid,fid) if the mid checkin was | |
| 242 | +@ -- a merge. Entries with isaux==0 are from the primary parent. Merge | |
| 243 | +@ -- parents have isaux set to true. | |
| 244 | +@ -- | |
| 245 | +@ -- Field name mnemonics: | |
| 246 | +@ -- mid = Manifest ID. (Each check-in is stored as a "Manifest") | |
| 247 | +@ -- fid = File ID. | |
| 248 | +@ -- pmid = Parent Manifest ID. | |
| 249 | +@ -- pid = Parent file ID. | |
| 250 | +@ -- fnid = File Name ID. | |
| 251 | +@ -- pfnid = Parent File Name ID. | |
| 252 | +@ -- isaux = pmid IS AUXiliary parent, not primary parent | |
| 232 | 253 | @ -- |
| 233 | 254 | @ -- pid==0 if the file is added by checkin mid. |
| 234 | 255 | @ -- fid==0 if the file is removed by checkin mid. |
| 235 | 256 | @ -- |
| 236 | 257 | @ CREATE TABLE mlink( |
| 237 | -@ mid INTEGER REFERENCES blob, -- Manifest ID where change occurs | |
| 238 | -@ pid INTEGER REFERENCES blob, -- File ID in parent manifest | |
| 239 | -@ fid INTEGER REFERENCES blob, -- Changed file ID in this manifest | |
| 258 | +@ mid INTEGER REFERENCES plink(cid), -- Checkin that contains fid | |
| 259 | +@ fid INTEGER REFERENCES blob, -- New file content. 0 if deleted | |
| 260 | +@ pmid INTEGER REFERENCES plink(cid), -- Checkin that contains pid | |
| 261 | +@ pid INTEGER REFERENCES blob, -- Prev file content. 0 if new | |
| 240 | 262 | @ fnid INTEGER REFERENCES filename, -- Name of the file |
| 241 | 263 | @ pfnid INTEGER REFERENCES filename, -- Previous name. 0 if unchanged |
| 242 | -@ mperm INTEGER -- File permissions. 1==exec | |
| 264 | +@ mperm INTEGER, -- File permissions. 1==exec | |
| 265 | +@ isaux BOOLEAN DEFAULT 0 -- TRUE if pmid is the primary | |
| 243 | 266 | @ ); |
| 244 | 267 | @ CREATE INDEX mlink_i1 ON mlink(mid); |
| 245 | 268 | @ CREATE INDEX mlink_i2 ON mlink(fnid); |
| 246 | 269 | @ CREATE INDEX mlink_i3 ON mlink(fid); |
| 247 | 270 | @ CREATE INDEX mlink_i4 ON mlink(pid); |
| @@ -251,11 +274,11 @@ | ||
| 251 | 274 | @ CREATE TABLE plink( |
| 252 | 275 | @ pid INTEGER REFERENCES blob, -- Parent manifest |
| 253 | 276 | @ cid INTEGER REFERENCES blob, -- Child manifest |
| 254 | 277 | @ isprim BOOLEAN, -- pid is the primary parent of cid |
| 255 | 278 | @ mtime DATETIME, -- the date/time stamp on cid. Julian day. |
| 256 | -@ baseid INTEGER REFERENCES blob, -- Baseline if child is a delta manifest | |
| 279 | +@ baseid INTEGER REFERENCES blob, -- Baseline if cid is a delta manifest. | |
| 257 | 280 | @ UNIQUE(pid, cid) |
| 258 | 281 | @ ); |
| 259 | 282 | @ CREATE INDEX plink_i2 ON plink(cid,pid); |
| 260 | 283 | @ |
| 261 | 284 | @ -- A "leaf" checkin is a checkin that has no children in the same |
| @@ -481,18 +504,18 @@ | ||
| 481 | 504 | @ -- Vfile.chnged is 0 for unmodified files, 1 for files that have |
| 482 | 505 | @ -- been edited or which have been subjected to a 3-way merge. |
| 483 | 506 | @ -- Vfile.chnged is 2 if the file has been replaced from a different |
| 484 | 507 | @ -- version by the merge and 3 if the file has been added by a merge. |
| 485 | 508 | @ -- Vfile.chnged is 4|5 is the same as 2|3, but the operation has been |
| 486 | -@ -- done by an --integrate merge. The difference between vfile.chnged==2|4 | |
| 487 | -@ -- and a regular add is that with vfile.chnged==2|4 we know that the | |
| 509 | +@ -- done by an --integrate merge. The difference between vfile.chnged==3|5 | |
| 510 | +@ -- and a regular add is that with vfile.chnged==3|5 we know that the | |
| 488 | 511 | @ -- current version of the file is already in the repository. |
| 489 | 512 | @ -- |
| 490 | 513 | @ CREATE TABLE vfile( |
| 491 | 514 | @ id INTEGER PRIMARY KEY, -- ID of the checked out file |
| 492 | 515 | @ vid INTEGER REFERENCES blob, -- The baseline this file is part of. |
| 493 | -@ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add 4:i-chng 5:i-add | |
| 516 | +@ chnged INT DEFAULT 0, -- 0:unchng 1:edit 2:m-chng 3:m-add 4:i-chng 5:i-add | |
| 494 | 517 | @ deleted BOOLEAN DEFAULT 0, -- True if deleted |
| 495 | 518 | @ isexe BOOLEAN, -- True if file should be executable |
| 496 | 519 | @ islink BOOLEAN, -- True if file should be symlink |
| 497 | 520 | @ rid INTEGER, -- Originally from this repository record |
| 498 | 521 | @ mrid INTEGER, -- Based on this record due to a merge |
| 499 | 522 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -12,11 +12,11 @@ | |
| 12 | ** Author contact information: |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file contains string constants that implement the database schema. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "schema.h" |
| 22 | |
| @@ -45,11 +45,14 @@ | |
| 45 | ** we have to execute special procedures to update the schema. When |
| 46 | ** the aux schema changes, all we need to do is rebuild the database. |
| 47 | */ |
| 48 | #define CONTENT_SCHEMA "2" |
| 49 | #define AUX_SCHEMA_MIN "2011-04-25 19:50" |
| 50 | #define AUX_SCHEMA_MAX "2014-11-24 20:35" |
| 51 | |
| 52 | #endif /* INTERFACE */ |
| 53 | |
| 54 | |
| 55 | /* |
| @@ -81,11 +84,11 @@ | |
| 81 | @ uuid TEXT UNIQUE NOT NULL, -- SHA1 hash of the content |
| 82 | @ content BLOB, -- Compressed content of this record |
| 83 | @ CHECK( length(uuid)==40 AND rid>0 ) |
| 84 | @ ); |
| 85 | @ CREATE TABLE delta( |
| 86 | @ rid INTEGER PRIMARY KEY, -- BLOB that is delta-compressed |
| 87 | @ srcid INTEGER NOT NULL REFERENCES blob -- Baseline for delta-compression |
| 88 | @ ); |
| 89 | @ CREATE INDEX delta_i1 ON delta(srcid); |
| 90 | @ |
| 91 | @ ------------------------------------------------------------------------- |
| @@ -126,11 +129,11 @@ | |
| 126 | @ info TEXT, -- contact information |
| 127 | @ mtime DATE, -- last change. seconds since 1970 |
| 128 | @ photo BLOB -- JPEG image of this user |
| 129 | @ ); |
| 130 | @ |
| 131 | @ -- The VAR table holds miscellanous information about the repository. |
| 132 | @ -- in the form of name-value pairs. |
| 133 | @ -- |
| 134 | @ CREATE TABLE config( |
| 135 | @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry |
| 136 | @ value CLOB, -- Content of the named parameter |
| @@ -227,21 +230,41 @@ | |
| 227 | @ name TEXT UNIQUE -- Name of file page |
| 228 | @ ); |
| 229 | @ |
| 230 | @ -- Linkages between checkins, files created by each checkin, and |
| 231 | @ -- the names of those files. |
| 232 | @ -- |
| 233 | @ -- pid==0 if the file is added by checkin mid. |
| 234 | @ -- fid==0 if the file is removed by checkin mid. |
| 235 | @ -- |
| 236 | @ CREATE TABLE mlink( |
| 237 | @ mid INTEGER REFERENCES blob, -- Manifest ID where change occurs |
| 238 | @ pid INTEGER REFERENCES blob, -- File ID in parent manifest |
| 239 | @ fid INTEGER REFERENCES blob, -- Changed file ID in this manifest |
| 240 | @ fnid INTEGER REFERENCES filename, -- Name of the file |
| 241 | @ pfnid INTEGER REFERENCES filename, -- Previous name. 0 if unchanged |
| 242 | @ mperm INTEGER -- File permissions. 1==exec |
| 243 | @ ); |
| 244 | @ CREATE INDEX mlink_i1 ON mlink(mid); |
| 245 | @ CREATE INDEX mlink_i2 ON mlink(fnid); |
| 246 | @ CREATE INDEX mlink_i3 ON mlink(fid); |
| 247 | @ CREATE INDEX mlink_i4 ON mlink(pid); |
| @@ -251,11 +274,11 @@ | |
| 251 | @ CREATE TABLE plink( |
| 252 | @ pid INTEGER REFERENCES blob, -- Parent manifest |
| 253 | @ cid INTEGER REFERENCES blob, -- Child manifest |
| 254 | @ isprim BOOLEAN, -- pid is the primary parent of cid |
| 255 | @ mtime DATETIME, -- the date/time stamp on cid. Julian day. |
| 256 | @ baseid INTEGER REFERENCES blob, -- Baseline if child is a delta manifest |
| 257 | @ UNIQUE(pid, cid) |
| 258 | @ ); |
| 259 | @ CREATE INDEX plink_i2 ON plink(cid,pid); |
| 260 | @ |
| 261 | @ -- A "leaf" checkin is a checkin that has no children in the same |
| @@ -481,18 +504,18 @@ | |
| 481 | @ -- Vfile.chnged is 0 for unmodified files, 1 for files that have |
| 482 | @ -- been edited or which have been subjected to a 3-way merge. |
| 483 | @ -- Vfile.chnged is 2 if the file has been replaced from a different |
| 484 | @ -- version by the merge and 3 if the file has been added by a merge. |
| 485 | @ -- Vfile.chnged is 4|5 is the same as 2|3, but the operation has been |
| 486 | @ -- done by an --integrate merge. The difference between vfile.chnged==2|4 |
| 487 | @ -- and a regular add is that with vfile.chnged==2|4 we know that the |
| 488 | @ -- current version of the file is already in the repository. |
| 489 | @ -- |
| 490 | @ CREATE TABLE vfile( |
| 491 | @ id INTEGER PRIMARY KEY, -- ID of the checked out file |
| 492 | @ vid INTEGER REFERENCES blob, -- The baseline this file is part of. |
| 493 | @ chnged INT DEFAULT 0, -- 0:unchnged 1:edited 2:m-chng 3:m-add 4:i-chng 5:i-add |
| 494 | @ deleted BOOLEAN DEFAULT 0, -- True if deleted |
| 495 | @ isexe BOOLEAN, -- True if file should be executable |
| 496 | @ islink BOOLEAN, -- True if file should be symlink |
| 497 | @ rid INTEGER, -- Originally from this repository record |
| 498 | @ mrid INTEGER, -- Based on this record due to a merge |
| 499 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -12,11 +12,11 @@ | |
| 12 | ** Author contact information: |
| 13 | ** [email protected] |
| 14 | ** http://www.hwaci.com/drh/ |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file contains string constants that implement the database schema. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "schema.h" |
| 22 | |
| @@ -45,11 +45,14 @@ | |
| 45 | ** we have to execute special procedures to update the schema. When |
| 46 | ** the aux schema changes, all we need to do is rebuild the database. |
| 47 | */ |
| 48 | #define CONTENT_SCHEMA "2" |
| 49 | #define AUX_SCHEMA_MIN "2011-04-25 19:50" |
| 50 | #define AUX_SCHEMA_MAX "2015-01-24" |
| 51 | /* NB: Some features require the latest schema. Warning or error messages |
| 52 | ** will appear if an older schema is used. However, the older schemas are |
| 53 | ** adequate for many common functions. */ |
| 54 | |
| 55 | #endif /* INTERFACE */ |
| 56 | |
| 57 | |
| 58 | /* |
| @@ -81,11 +84,11 @@ | |
| 84 | @ uuid TEXT UNIQUE NOT NULL, -- SHA1 hash of the content |
| 85 | @ content BLOB, -- Compressed content of this record |
| 86 | @ CHECK( length(uuid)==40 AND rid>0 ) |
| 87 | @ ); |
| 88 | @ CREATE TABLE delta( |
| 89 | @ rid INTEGER PRIMARY KEY, -- BLOB that is delta-compressed |
| 90 | @ srcid INTEGER NOT NULL REFERENCES blob -- Baseline for delta-compression |
| 91 | @ ); |
| 92 | @ CREATE INDEX delta_i1 ON delta(srcid); |
| 93 | @ |
| 94 | @ ------------------------------------------------------------------------- |
| @@ -126,11 +129,11 @@ | |
| 129 | @ info TEXT, -- contact information |
| 130 | @ mtime DATE, -- last change. seconds since 1970 |
| 131 | @ photo BLOB -- JPEG image of this user |
| 132 | @ ); |
| 133 | @ |
| 134 | @ -- The config table holds miscellanous information about the repository. |
| 135 | @ -- in the form of name-value pairs. |
| 136 | @ -- |
| 137 | @ CREATE TABLE config( |
| 138 | @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry |
| 139 | @ value CLOB, -- Content of the named parameter |
| @@ -227,21 +230,41 @@ | |
| 230 | @ name TEXT UNIQUE -- Name of file page |
| 231 | @ ); |
| 232 | @ |
| 233 | @ -- Linkages between checkins, files created by each checkin, and |
| 234 | @ -- the names of those files. |
| 235 | @ -- |
| 236 | @ -- Each entry represents a file that changed content from pid to fid |
| 237 | @ -- due to the check-in that goes from pmid to mid. fnid is the name |
| 238 | @ -- of the file in the mid check-in. If the file was renamed as part |
| 239 | @ -- of the mid check-in, then pfnid is the previous filename. |
| 240 | @ |
| 241 | @ -- There can be multiple entries for (mid,fid) if the mid checkin was |
| 242 | @ -- a merge. Entries with isaux==0 are from the primary parent. Merge |
| 243 | @ -- parents have isaux set to true. |
| 244 | @ -- |
| 245 | @ -- Field name mnemonics: |
| 246 | @ -- mid = Manifest ID. (Each check-in is stored as a "Manifest") |
| 247 | @ -- fid = File ID. |
| 248 | @ -- pmid = Parent Manifest ID. |
| 249 | @ -- pid = Parent file ID. |
| 250 | @ -- fnid = File Name ID. |
| 251 | @ -- pfnid = Parent File Name ID. |
| 252 | @ -- isaux = pmid IS AUXiliary parent, not primary parent |
| 253 | @ -- |
| 254 | @ -- pid==0 if the file is added by checkin mid. |
| 255 | @ -- fid==0 if the file is removed by checkin mid. |
| 256 | @ -- |
| 257 | @ CREATE TABLE mlink( |
| 258 | @ mid INTEGER REFERENCES plink(cid), -- Checkin that contains fid |
| 259 | @ fid INTEGER REFERENCES blob, -- New file content. 0 if deleted |
| 260 | @ pmid INTEGER REFERENCES plink(cid), -- Checkin that contains pid |
| 261 | @ pid INTEGER REFERENCES blob, -- Prev file content. 0 if new |
| 262 | @ fnid INTEGER REFERENCES filename, -- Name of the file |
| 263 | @ pfnid INTEGER REFERENCES filename, -- Previous name. 0 if unchanged |
| 264 | @ mperm INTEGER, -- File permissions. 1==exec |
| 265 | @ isaux BOOLEAN DEFAULT 0 -- TRUE if pmid is the primary |
| 266 | @ ); |
| 267 | @ CREATE INDEX mlink_i1 ON mlink(mid); |
| 268 | @ CREATE INDEX mlink_i2 ON mlink(fnid); |
| 269 | @ CREATE INDEX mlink_i3 ON mlink(fid); |
| 270 | @ CREATE INDEX mlink_i4 ON mlink(pid); |
| @@ -251,11 +274,11 @@ | |
| 274 | @ CREATE TABLE plink( |
| 275 | @ pid INTEGER REFERENCES blob, -- Parent manifest |
| 276 | @ cid INTEGER REFERENCES blob, -- Child manifest |
| 277 | @ isprim BOOLEAN, -- pid is the primary parent of cid |
| 278 | @ mtime DATETIME, -- the date/time stamp on cid. Julian day. |
| 279 | @ baseid INTEGER REFERENCES blob, -- Baseline if cid is a delta manifest. |
| 280 | @ UNIQUE(pid, cid) |
| 281 | @ ); |
| 282 | @ CREATE INDEX plink_i2 ON plink(cid,pid); |
| 283 | @ |
| 284 | @ -- A "leaf" checkin is a checkin that has no children in the same |
| @@ -481,18 +504,18 @@ | |
| 504 | @ -- Vfile.chnged is 0 for unmodified files, 1 for files that have |
| 505 | @ -- been edited or which have been subjected to a 3-way merge. |
| 506 | @ -- Vfile.chnged is 2 if the file has been replaced from a different |
| 507 | @ -- version by the merge and 3 if the file has been added by a merge. |
| 508 | @ -- Vfile.chnged is 4|5 is the same as 2|3, but the operation has been |
| 509 | @ -- done by an --integrate merge. The difference between vfile.chnged==3|5 |
| 510 | @ -- and a regular add is that with vfile.chnged==3|5 we know that the |
| 511 | @ -- current version of the file is already in the repository. |
| 512 | @ -- |
| 513 | @ CREATE TABLE vfile( |
| 514 | @ id INTEGER PRIMARY KEY, -- ID of the checked out file |
| 515 | @ vid INTEGER REFERENCES blob, -- The baseline this file is part of. |
| 516 | @ chnged INT DEFAULT 0, -- 0:unchng 1:edit 2:m-chng 3:m-add 4:i-chng 5:i-add |
| 517 | @ deleted BOOLEAN DEFAULT 0, -- True if deleted |
| 518 | @ isexe BOOLEAN, -- True if file should be executable |
| 519 | @ islink BOOLEAN, -- True if file should be symlink |
| 520 | @ rid INTEGER, -- Originally from this repository record |
| 521 | @ mrid INTEGER, -- Based on this record due to a merge |
| 522 |
+621
-83
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -26,54 +26,42 @@ | ||
| 26 | 26 | #include "config.h" |
| 27 | 27 | #include "search.h" |
| 28 | 28 | #include <assert.h> |
| 29 | 29 | |
| 30 | 30 | #if INTERFACE |
| 31 | + | |
| 32 | +/* Maximum number of search terms */ | |
| 33 | +#define SEARCH_MAX_TERM 8 | |
| 34 | + | |
| 31 | 35 | /* |
| 32 | 36 | ** A compiled search pattern |
| 33 | 37 | */ |
| 34 | 38 | struct Search { |
| 35 | 39 | int nTerm; /* Number of search terms */ |
| 36 | 40 | struct srchTerm { /* For each search term */ |
| 37 | 41 | char *z; /* Text */ |
| 38 | 42 | int n; /* length */ |
| 39 | - } a[8]; | |
| 43 | + } a[SEARCH_MAX_TERM]; | |
| 44 | + /* Snippet controls */ | |
| 45 | + char *zPattern; /* The search pattern */ | |
| 46 | + char *zMarkBegin; /* Start of a match */ | |
| 47 | + char *zMarkEnd; /* End of a match */ | |
| 48 | + char *zMarkGap; /* A gap between two matches */ | |
| 49 | + unsigned fSrchFlg; /* Flags */ | |
| 40 | 50 | }; |
| 51 | + | |
| 52 | +#define SRCHFLG_HTML 0x01 /* Escape snippet text for HTML */ | |
| 53 | +#define SRCHFLG_SCORE 0x02 /* Prepend the score to each snippet */ | |
| 54 | +#define SRCHFLG_STATIC 0x04 /* The static gSearch object */ | |
| 55 | + | |
| 41 | 56 | #endif |
| 42 | 57 | |
| 43 | 58 | /* |
| 44 | -** Compile a search pattern | |
| 45 | -*/ | |
| 46 | -Search *search_init(const char *zPattern){ | |
| 47 | - int nPattern = strlen(zPattern); | |
| 48 | - Search *p; | |
| 49 | - char *z; | |
| 50 | - int i; | |
| 51 | - | |
| 52 | - p = fossil_malloc( nPattern + sizeof(*p) + 1); | |
| 53 | - z = (char*)&p[1]; | |
| 54 | - memcpy(z, zPattern, nPattern+1); | |
| 55 | - memset(p, 0, sizeof(*p)); | |
| 56 | - while( *z && p->nTerm<sizeof(p->a)/sizeof(p->a[0]) ){ | |
| 57 | - while( !fossil_isalnum(*z) && *z ){ z++; } | |
| 58 | - if( *z==0 ) break; | |
| 59 | - p->a[p->nTerm].z = z; | |
| 60 | - for(i=1; fossil_isalnum(z[i]) || z[i]=='_'; i++){} | |
| 61 | - p->a[p->nTerm].n = i; | |
| 62 | - z += i; | |
| 63 | - p->nTerm++; | |
| 64 | - } | |
| 65 | - return p; | |
| 66 | -} | |
| 67 | - | |
| 68 | - | |
| 69 | -/* | |
| 70 | -** Destroy a search context. | |
| 71 | -*/ | |
| 72 | -void search_end(Search *p){ | |
| 73 | - free(p); | |
| 74 | -} | |
| 59 | +** There is a single global Search object: | |
| 60 | +*/ | |
| 61 | +static Search gSearch; | |
| 62 | + | |
| 75 | 63 | |
| 76 | 64 | /* |
| 77 | 65 | ** Theses characters constitute a word boundary |
| 78 | 66 | */ |
| 79 | 67 | static const char isBoundary[] = { |
| @@ -92,95 +80,388 @@ | ||
| 92 | 80 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 93 | 81 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 94 | 82 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 95 | 83 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 96 | 84 | }; |
| 85 | +#define ISALNUM(x) (!isBoundary[(x)&0xff]) | |
| 86 | + | |
| 87 | + | |
| 88 | +/* | |
| 89 | +** Destroy a search context. | |
| 90 | +*/ | |
| 91 | +void search_end(Search *p){ | |
| 92 | + if( p ){ | |
| 93 | + fossil_free(p->zPattern); | |
| 94 | + fossil_free(p->zMarkBegin); | |
| 95 | + fossil_free(p->zMarkEnd); | |
| 96 | + fossil_free(p->zMarkGap); | |
| 97 | + memset(p, 0, sizeof(*p)); | |
| 98 | + if( p!=&gSearch ) fossil_free(p); | |
| 99 | + } | |
| 100 | +} | |
| 101 | + | |
| 102 | +/* | |
| 103 | +** Compile a search pattern | |
| 104 | +*/ | |
| 105 | +Search *search_init( | |
| 106 | + const char *zPattern, /* The search pattern */ | |
| 107 | + const char *zMarkBegin, /* Start of a match */ | |
| 108 | + const char *zMarkEnd, /* End of a match */ | |
| 109 | + const char *zMarkGap, /* A gap between two matches */ | |
| 110 | + unsigned fSrchFlg /* Flags */ | |
| 111 | +){ | |
| 112 | + Search *p; | |
| 113 | + char *z; | |
| 114 | + int i; | |
| 115 | + | |
| 116 | + if( fSrchFlg & SRCHFLG_STATIC ){ | |
| 117 | + p = &gSearch; | |
| 118 | + search_end(p); | |
| 119 | + }else{ | |
| 120 | + p = fossil_malloc(sizeof(*p)); | |
| 121 | + memset(p, 0, sizeof(*p)); | |
| 122 | + } | |
| 123 | + p->zPattern = z = mprintf("%s", zPattern); | |
| 124 | + p->zMarkBegin = mprintf("%s", zMarkBegin); | |
| 125 | + p->zMarkEnd = mprintf("%s", zMarkEnd); | |
| 126 | + p->zMarkGap = mprintf("%s", zMarkGap); | |
| 127 | + p->fSrchFlg = fSrchFlg; | |
| 128 | + while( *z && p->nTerm<SEARCH_MAX_TERM ){ | |
| 129 | + while( *z && !ISALNUM(*z) ){ z++; } | |
| 130 | + if( *z==0 ) break; | |
| 131 | + p->a[p->nTerm].z = z; | |
| 132 | + for(i=1; ISALNUM(z[i]); i++){} | |
| 133 | + p->a[p->nTerm].n = i; | |
| 134 | + z += i; | |
| 135 | + p->nTerm++; | |
| 136 | + } | |
| 137 | + return p; | |
| 138 | +} | |
| 139 | + | |
| 140 | + | |
| 141 | +/* | |
| 142 | +** Append n bytes of text to snippet zTxt. Encode the text appropriately. | |
| 143 | +*/ | |
| 144 | +static void snippet_text_append( | |
| 145 | + Search *p, /* The search context */ | |
| 146 | + Blob *pSnip, /* Append to this snippet */ | |
| 147 | + const char *zTxt, /* Text to append */ | |
| 148 | + int n /* How many bytes to append */ | |
| 149 | +){ | |
| 150 | + if( n>0 ){ | |
| 151 | + if( p->fSrchFlg & SRCHFLG_HTML ){ | |
| 152 | + blob_appendf(pSnip, "%#h", n, zTxt); | |
| 153 | + }else{ | |
| 154 | + blob_append(pSnip, zTxt, n); | |
| 155 | + } | |
| 156 | + } | |
| 157 | +} | |
| 97 | 158 | |
| 98 | 159 | /* |
| 99 | -** Compare a search pattern against an input string and return a score. | |
| 160 | +** Compare a search pattern against one or more input strings which | |
| 161 | +** collectively comprise a document. Return a match score. Optionally | |
| 162 | +** also return a "snippet". | |
| 100 | 163 | ** |
| 101 | 164 | ** Scoring: |
| 102 | 165 | ** * All terms must match at least once or the score is zero |
| 103 | -** * 10 bonus points if the first occurrence is an exact match | |
| 104 | -** * 1 additional point for each subsequent match of the same word | |
| 105 | -** * Extra points of two consecutive words of the pattern are consecutive | |
| 166 | +** * One point for each matching term | |
| 167 | +** * Extra points if consecutive words of the pattern are consecutive | |
| 106 | 168 | ** in the document |
| 107 | 169 | */ |
| 108 | -int search_score(Search *p, int nDoc, const char **azDoc){ | |
| 109 | - int iPrev = 999; | |
| 110 | - int score = 10; | |
| 111 | - int iBonus = 0; | |
| 112 | - int i, j, k; | |
| 113 | - const char *zDoc; | |
| 114 | - unsigned char seen[8]; | |
| 115 | - | |
| 116 | - memset(seen, 0, sizeof(seen)); | |
| 117 | - for(k=0; k<nDoc; k++){ | |
| 118 | - zDoc = azDoc[k]; | |
| 170 | +static int search_score( | |
| 171 | + Search *p, /* Search pattern and flags */ | |
| 172 | + int nDoc, /* Number of strings in this document */ | |
| 173 | + const char **azDoc, /* Text of each string */ | |
| 174 | + Blob *pSnip /* If not NULL: Write a snippet here */ | |
| 175 | +){ | |
| 176 | + int score; /* Final score */ | |
| 177 | + int i; /* Offset into current document */ | |
| 178 | + int ii; /* Loop counter */ | |
| 179 | + int j; /* Loop over search terms */ | |
| 180 | + int k; /* Loop over prior terms */ | |
| 181 | + int iWord = 0; /* Current word number */ | |
| 182 | + int iDoc; /* Current document number */ | |
| 183 | + int wantGap = 0; /* True if a zMarkGap is wanted */ | |
| 184 | + const char *zDoc; /* Current document text */ | |
| 185 | + const int CTX = 50; /* Amount of snippet context */ | |
| 186 | + int anMatch[SEARCH_MAX_TERM]; /* Number of terms in best match */ | |
| 187 | + int aiBestDoc[SEARCH_MAX_TERM]; /* Document containing best match */ | |
| 188 | + int aiBestOfst[SEARCH_MAX_TERM]; /* Byte offset to start of best match */ | |
| 189 | + int aiLastDoc[SEARCH_MAX_TERM]; /* Document containing most recent match */ | |
| 190 | + int aiLastOfst[SEARCH_MAX_TERM]; /* Byte offset to the most recent match */ | |
| 191 | + int aiWordIdx[SEARCH_MAX_TERM]; /* Word index of most recent match */ | |
| 192 | + | |
| 193 | + memset(anMatch, 0, sizeof(anMatch)); | |
| 194 | + memset(aiWordIdx, 0xff, sizeof(aiWordIdx)); | |
| 195 | + for(iDoc=0; iDoc<nDoc; iDoc++){ | |
| 196 | + zDoc = azDoc[iDoc]; | |
| 119 | 197 | if( zDoc==0 ) continue; |
| 198 | + iWord++; | |
| 120 | 199 | for(i=0; zDoc[i]; i++){ |
| 121 | - char c = zDoc[i]; | |
| 122 | - if( isBoundary[c&0xff] ) continue; | |
| 200 | + if( !ISALNUM(zDoc[i]) ) continue; | |
| 201 | + iWord++; | |
| 202 | + for(j=0; j<p->nTerm; j++){ | |
| 203 | + int n = p->a[j].n; | |
| 204 | + if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 | |
| 205 | + && (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*') | |
| 206 | + ){ | |
| 207 | + aiWordIdx[j] = iWord; | |
| 208 | + aiLastDoc[j] = iDoc; | |
| 209 | + aiLastOfst[j] = i; | |
| 210 | + for(k=1; j-k>=0 && anMatch[j-k] && aiWordIdx[j-k]==iWord-k; k++){} | |
| 211 | + for(ii=0; ii<k; ii++){ | |
| 212 | + if( anMatch[j-ii]<k ){ | |
| 213 | + anMatch[j-ii] = k; | |
| 214 | + aiBestDoc[j-ii] = aiLastDoc[j-ii]; | |
| 215 | + aiBestOfst[j-ii] = aiLastOfst[j-ii]; | |
| 216 | + } | |
| 217 | + } | |
| 218 | + break; | |
| 219 | + } | |
| 220 | + } | |
| 221 | + while( ISALNUM(zDoc[i]) ){ i++; } | |
| 222 | + } | |
| 223 | + } | |
| 224 | + | |
| 225 | + /* Finished search all documents. | |
| 226 | + ** Every term must be seen or else the score is zero | |
| 227 | + */ | |
| 228 | + score = 1; | |
| 229 | + for(j=0; j<p->nTerm; j++) score *= anMatch[j]; | |
| 230 | + if( score==0 || pSnip==0 ) return score; | |
| 231 | + | |
| 232 | + | |
| 233 | + /* Prepare a snippet that describes the matching text. | |
| 234 | + */ | |
| 235 | + blob_init(pSnip, 0, 0); | |
| 236 | + if( p->fSrchFlg & SRCHFLG_SCORE ) blob_appendf(pSnip, "%08x", score); | |
| 237 | + | |
| 238 | + while(1){ | |
| 239 | + int iOfst; | |
| 240 | + int iTail; | |
| 241 | + int iBest; | |
| 242 | + for(ii=0; ii<p->nTerm && anMatch[ii]==0; ii++){} | |
| 243 | + if( ii>=p->nTerm ) break; /* This is where the loop exits */ | |
| 244 | + iBest = ii; | |
| 245 | + iDoc = aiBestDoc[ii]; | |
| 246 | + iOfst = aiBestOfst[ii]; | |
| 247 | + for(; ii<p->nTerm; ii++){ | |
| 248 | + if( anMatch[ii]==0 ) continue; | |
| 249 | + if( aiBestDoc[ii]>iDoc ) continue; | |
| 250 | + if( aiBestOfst[ii]>iOfst ) continue; | |
| 251 | + iDoc = aiBestDoc[ii]; | |
| 252 | + iOfst = aiBestOfst[ii]; | |
| 253 | + iBest = ii; | |
| 254 | + } | |
| 255 | + iTail = iOfst + p->a[iBest].n; | |
| 256 | + anMatch[iBest] = 0; | |
| 257 | + for(ii=0; ii<p->nTerm; ii++){ | |
| 258 | + if( anMatch[ii]==0 ) continue; | |
| 259 | + if( aiBestDoc[ii]!=iDoc ) continue; | |
| 260 | + if( aiBestOfst[ii]<=iTail+CTX*2 ){ | |
| 261 | + if( iTail<aiBestOfst[ii]+p->a[ii].n ){ | |
| 262 | + iTail = aiBestOfst[ii]+p->a[ii].n; | |
| 263 | + } | |
| 264 | + anMatch[ii] = 0; | |
| 265 | + ii = -1; | |
| 266 | + continue; | |
| 267 | + } | |
| 268 | + } | |
| 269 | + zDoc = azDoc[iDoc]; | |
| 270 | + iOfst -= CTX; | |
| 271 | + if( iOfst<0 ) iOfst = 0; | |
| 272 | + while( iOfst>0 && ISALNUM(zDoc[iOfst-1]) ) iOfst--; | |
| 273 | + while( zDoc[iOfst] && !ISALNUM(zDoc[iOfst]) ) iOfst++; | |
| 274 | + for(ii=0; ii<CTX && zDoc[iTail]; ii++, iTail++){} | |
| 275 | + while( ISALNUM(zDoc[iTail]) ) iTail++; | |
| 276 | + if( iOfst>0 || wantGap ) blob_append(pSnip, p->zMarkGap, -1); | |
| 277 | + wantGap = zDoc[iTail]!=0; | |
| 278 | + zDoc += iOfst; | |
| 279 | + iTail -= iOfst; | |
| 280 | + | |
| 281 | + /* Add a snippet segment using characters iOfst..iOfst+iTail from zDoc */ | |
| 282 | + for(i=0; i<iTail; i++){ | |
| 283 | + if( !ISALNUM(zDoc[i]) ) continue; | |
| 123 | 284 | for(j=0; j<p->nTerm; j++){ |
| 124 | 285 | int n = p->a[j].n; |
| 125 | - if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 ){ | |
| 126 | - score += 1; | |
| 127 | - if( !seen[j] ){ | |
| 128 | - if( isBoundary[zDoc[i+n]&0xff] ) score += 10; | |
| 129 | - seen[j] = 1; | |
| 130 | - } | |
| 131 | - if( j==iPrev+1 ){ | |
| 132 | - score += iBonus; | |
| 133 | - } | |
| 134 | - i += n-1; | |
| 135 | - iPrev = j; | |
| 136 | - iBonus = 50; | |
| 286 | + if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 | |
| 287 | + && (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*') | |
| 288 | + ){ | |
| 289 | + snippet_text_append(p, pSnip, zDoc, i); | |
| 290 | + zDoc += i; | |
| 291 | + iTail -= i; | |
| 292 | + blob_append(pSnip, p->zMarkBegin, -1); | |
| 293 | + if( p->a[j].z[n]=='*' ){ | |
| 294 | + while( ISALNUM(zDoc[n]) ) n++; | |
| 295 | + } | |
| 296 | + snippet_text_append(p, pSnip, zDoc, n); | |
| 297 | + zDoc += n; | |
| 298 | + iTail -= n; | |
| 299 | + blob_append(pSnip, p->zMarkEnd, -1); | |
| 300 | + i = -1; | |
| 137 | 301 | break; |
| 138 | - } | |
| 139 | - } | |
| 140 | - iBonus /= 2; | |
| 141 | - while( !isBoundary[zDoc[i]&0xff] ){ i++; } | |
| 142 | - } | |
| 143 | - } | |
| 144 | - | |
| 145 | - /* Every term must be seen or else the score is zero */ | |
| 146 | - for(j=0; j<p->nTerm; j++){ | |
| 147 | - if( !seen[j] ) return 0; | |
| 148 | - } | |
| 149 | - | |
| 302 | + } /* end-if */ | |
| 303 | + } /* end for(j) */ | |
| 304 | + if( j<p->nTerm ){ | |
| 305 | + while( ISALNUM(zDoc[i]) && i<iTail ){ i++; } | |
| 306 | + } | |
| 307 | + } /* end for(i) */ | |
| 308 | + snippet_text_append(p, pSnip, zDoc, iTail); | |
| 309 | + } | |
| 310 | + if( wantGap ) blob_append(pSnip, p->zMarkGap, -1); | |
| 150 | 311 | return score; |
| 151 | 312 | } |
| 313 | + | |
| 314 | +/* | |
| 315 | +** COMMAND: test-snippet | |
| 316 | +** | |
| 317 | +** Usage: fossil test-snippet SEARCHSTRING FILE1 FILE2 ... | |
| 318 | +*/ | |
| 319 | +void test_snippet_cmd(void){ | |
| 320 | + Search *p; | |
| 321 | + int i; | |
| 322 | + Blob x; | |
| 323 | + Blob snip; | |
| 324 | + int score; | |
| 325 | + char *zDoc; | |
| 326 | + int flg = 0; | |
| 327 | + char *zBegin = (char*)find_option("begin",0,1); | |
| 328 | + char *zEnd = (char*)find_option("end",0,1); | |
| 329 | + char *zGap = (char*)find_option("gap",0,1); | |
| 330 | + if( find_option("html",0,0)!=0 ) flg |= SRCHFLG_HTML; | |
| 331 | + if( find_option("score",0,0)!=0 ) flg |= SRCHFLG_SCORE; | |
| 332 | + if( find_option("static",0,0)!=0 ) flg |= SRCHFLG_STATIC; | |
| 333 | + verify_all_options(); | |
| 334 | + if( g.argc<4 ) usage("SEARCHSTRING FILE1..."); | |
| 335 | + if( zBegin==0 ) zBegin = "[["; | |
| 336 | + if( zEnd==0 ) zEnd = "]]"; | |
| 337 | + if( zGap==0 ) zGap = " ... "; | |
| 338 | + p = search_init(g.argv[2], zBegin, zEnd, zGap, flg); | |
| 339 | + for(i=3; i<g.argc; i++){ | |
| 340 | + blob_read_from_file(&x, g.argv[i]); | |
| 341 | + zDoc = blob_str(&x); | |
| 342 | + score = search_score(p, 1, (const char**)&zDoc, &snip); | |
| 343 | + fossil_print("%s: %d\n", g.argv[i], score); | |
| 344 | + blob_reset(&x); | |
| 345 | + if( score ){ | |
| 346 | + fossil_print("%.78c\n%s\n%.78c\n\n", '=', blob_str(&snip), '='); | |
| 347 | + blob_reset(&snip); | |
| 348 | + } | |
| 349 | + } | |
| 350 | +} | |
| 351 | + | |
| 352 | +/* | |
| 353 | +** An SQL function to initialize the global search pattern: | |
| 354 | +** | |
| 355 | +** search_init(PATTERN,BEGIN,END,GAP,FLAGS) | |
| 356 | +** | |
| 357 | +** All arguments are optional. | |
| 358 | +*/ | |
| 359 | +static void search_init_sqlfunc( | |
| 360 | + sqlite3_context *context, | |
| 361 | + int argc, | |
| 362 | + sqlite3_value **argv | |
| 363 | +){ | |
| 364 | + const char *zPattern = 0; | |
| 365 | + const char *zBegin = "<b>"; | |
| 366 | + const char *zEnd = "</b>"; | |
| 367 | + const char *zGap = " ... "; | |
| 368 | + unsigned int flg = SRCHFLG_HTML; | |
| 369 | + switch( argc ){ | |
| 370 | + default: | |
| 371 | + flg = (unsigned int)sqlite3_value_int(argv[4]); | |
| 372 | + case 4: | |
| 373 | + zGap = (const char*)sqlite3_value_text(argv[3]); | |
| 374 | + case 3: | |
| 375 | + zEnd = (const char*)sqlite3_value_text(argv[2]); | |
| 376 | + case 2: | |
| 377 | + zBegin = (const char*)sqlite3_value_text(argv[1]); | |
| 378 | + case 1: | |
| 379 | + zPattern = (const char*)sqlite3_value_text(argv[0]); | |
| 380 | + } | |
| 381 | + if( zPattern && zPattern[0] ){ | |
| 382 | + search_init(zPattern, zBegin, zEnd, zGap, flg | SRCHFLG_STATIC); | |
| 383 | + }else{ | |
| 384 | + search_end(&gSearch); | |
| 385 | + } | |
| 386 | +} | |
| 152 | 387 | |
| 153 | 388 | /* |
| 154 | 389 | ** This is an SQLite function that scores its input using |
| 155 | -** a pre-computed pattern. | |
| 390 | +** the pattern from the previous call to search_init(). | |
| 156 | 391 | */ |
| 157 | 392 | static void search_score_sqlfunc( |
| 158 | 393 | sqlite3_context *context, |
| 159 | 394 | int argc, |
| 160 | 395 | sqlite3_value **argv |
| 161 | 396 | ){ |
| 162 | - Search *p = (Search*)sqlite3_user_data(context); | |
| 397 | + int isSnippet = sqlite3_user_data(context)!=0; | |
| 163 | 398 | const char **azDoc; |
| 164 | 399 | int score; |
| 165 | 400 | int i; |
| 401 | + Blob snip; | |
| 166 | 402 | |
| 403 | + if( gSearch.nTerm==0 ) return; | |
| 167 | 404 | azDoc = fossil_malloc( sizeof(const char*)*(argc+1) ); |
| 168 | 405 | for(i=0; i<argc; i++) azDoc[i] = (const char*)sqlite3_value_text(argv[i]); |
| 169 | - score = search_score(p, argc, azDoc); | |
| 170 | - fossil_free(azDoc); | |
| 171 | - sqlite3_result_int(context, score); | |
| 406 | + score = search_score(&gSearch, argc, azDoc, isSnippet ? &snip : 0); | |
| 407 | + fossil_free((void *)azDoc); | |
| 408 | + if( isSnippet ){ | |
| 409 | + if( score ){ | |
| 410 | + sqlite3_result_text(context, blob_materialize(&snip), -1, fossil_free); | |
| 411 | + } | |
| 412 | + }else{ | |
| 413 | + sqlite3_result_int(context, score); | |
| 414 | + } | |
| 415 | +} | |
| 416 | + | |
| 417 | +/* | |
| 418 | +** This is an SQLite function that computes the searchable text. | |
| 419 | +** It is a wrapper around the search_stext() routine. See the | |
| 420 | +** search_stext() routine for further detail. | |
| 421 | +*/ | |
| 422 | +static void search_stext_sqlfunc( | |
| 423 | + sqlite3_context *context, | |
| 424 | + int argc, | |
| 425 | + sqlite3_value **argv | |
| 426 | +){ | |
| 427 | + Blob txt; | |
| 428 | + const char *zType = (const char*)sqlite3_value_text(argv[0]); | |
| 429 | + const char *zArg1 = (const char*)sqlite3_value_text(argv[1]); | |
| 430 | + const char *zArg2 = (const char*)sqlite3_value_text(argv[2]); | |
| 431 | + search_stext(zType[0], zArg1, zArg2, &txt); | |
| 432 | + sqlite3_result_text(context, blob_materialize(&txt), -1, fossil_free); | |
| 433 | +} | |
| 434 | + | |
| 435 | +/* | |
| 436 | +** Encode a string for use as a query parameter in a URL | |
| 437 | +*/ | |
| 438 | +static void search_urlencode_sqlfunc( | |
| 439 | + sqlite3_context *context, | |
| 440 | + int argc, | |
| 441 | + sqlite3_value **argv | |
| 442 | +){ | |
| 443 | + char *z = mprintf("%T",sqlite3_value_text(argv[0])); | |
| 444 | + sqlite3_result_text(context, z, -1, fossil_free); | |
| 172 | 445 | } |
| 173 | 446 | |
| 174 | 447 | /* |
| 175 | 448 | ** Register the "score()" SQL function to score its input text |
| 176 | 449 | ** using the given Search object. Once this function is registered, |
| 177 | 450 | ** do not delete the Search object. |
| 178 | 451 | */ |
| 179 | -void search_sql_setup(Search *p){ | |
| 180 | - sqlite3_create_function(g.db, "score", -1, SQLITE_UTF8, p, | |
| 452 | +void search_sql_setup(sqlite3 *db){ | |
| 453 | + sqlite3_create_function(db, "score", -1, SQLITE_UTF8, 0, | |
| 454 | + search_score_sqlfunc, 0, 0); | |
| 455 | + sqlite3_create_function(db, "snippet", -1, SQLITE_UTF8, &gSearch, | |
| 181 | 456 | search_score_sqlfunc, 0, 0); |
| 457 | + sqlite3_create_function(db, "search_init", -1, SQLITE_UTF8, 0, | |
| 458 | + search_init_sqlfunc, 0, 0); | |
| 459 | + sqlite3_create_function(db, "stext", 3, SQLITE_UTF8, 0, | |
| 460 | + search_stext_sqlfunc, 0, 0); | |
| 461 | + sqlite3_create_function(db, "urlencode", 1, SQLITE_UTF8, 0, | |
| 462 | + search_urlencode_sqlfunc, 0, 0); | |
| 182 | 463 | } |
| 183 | 464 | |
| 184 | 465 | /* |
| 185 | 466 | ** Testing the search function. |
| 186 | 467 | ** |
| @@ -198,11 +479,10 @@ | ||
| 198 | 479 | ** of entries returned. The -width option can be |
| 199 | 480 | ** used to set the output width used when printing |
| 200 | 481 | ** matches. |
| 201 | 482 | */ |
| 202 | 483 | void search_cmd(void){ |
| 203 | - Search *p; | |
| 204 | 484 | Blob pattern; |
| 205 | 485 | int i; |
| 206 | 486 | Blob sql = empty_blob; |
| 207 | 487 | Stmt q; |
| 208 | 488 | int iBest; |
| @@ -227,13 +507,13 @@ | ||
| 227 | 507 | if( g.argc<2 ) return; |
| 228 | 508 | blob_init(&pattern, g.argv[2], -1); |
| 229 | 509 | for(i=3; i<g.argc; i++){ |
| 230 | 510 | blob_appendf(&pattern, " %s", g.argv[i]); |
| 231 | 511 | } |
| 232 | - p = search_init(blob_str(&pattern)); | |
| 512 | + (void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC); | |
| 233 | 513 | blob_reset(&pattern); |
| 234 | - search_sql_setup(p); | |
| 514 | + search_sql_setup(g.db); | |
| 235 | 515 | |
| 236 | 516 | db_multi_exec( |
| 237 | 517 | "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);" |
| 238 | 518 | "CREATE INDEX srch_idx1 ON srch(x);" |
| 239 | 519 | "INSERT INTO srch(rid,uuid,date,comment,x)" |
| @@ -255,5 +535,263 @@ | ||
| 255 | 535 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 256 | 536 | blob_reset(&sql); |
| 257 | 537 | print_timeline(&q, nLimit, width, 0); |
| 258 | 538 | db_finalize(&q); |
| 259 | 539 | } |
| 540 | + | |
| 541 | +/* | |
| 542 | +** WEBPAGE: /search | |
| 543 | +** | |
| 544 | +** This is an EXPERIMENTAL page for doing search across a repository. | |
| 545 | +** | |
| 546 | +** The current implementation does a full text search over embedded | |
| 547 | +** documentation files on the tip of the "trunk" branch. Only files | |
| 548 | +** ending in ".wiki", ".md", ".html", and ".txt" are searched. | |
| 549 | +** | |
| 550 | +** The entire text is scanned. There is no full-text index. This is | |
| 551 | +** experimental. We may change to using a full-text index depending | |
| 552 | +** on performance. | |
| 553 | +** | |
| 554 | +** Other pending enhancements: | |
| 555 | +** * Search tickets | |
| 556 | +** * Search wiki | |
| 557 | +*/ | |
| 558 | +void search_page(void){ | |
| 559 | + const char *zPattern = PD("s",""); | |
| 560 | + Stmt q; | |
| 561 | + const char *zSrchEnable = "dwc"; | |
| 562 | + | |
| 563 | + login_check_credentials(); | |
| 564 | + if( !g.perm.Read ){ login_needed(); return; } | |
| 565 | + style_header("Search"); | |
| 566 | + @ <form method="GET" action="search"><center> | |
| 567 | + @ <input type="text" name="s" size="40" value="%h(zPattern)"> | |
| 568 | + @ <input type="submit" value="Search"> | |
| 569 | + @ </center></form> | |
| 570 | + while( fossil_isspace(zPattern[0]) ) zPattern++; | |
| 571 | + if( zPattern[0] ){ | |
| 572 | + search_sql_setup(g.db); | |
| 573 | + add_content_sql_commands(g.db); | |
| 574 | + search_init(zPattern, "<b>", "</b>", " ... ", | |
| 575 | + SRCHFLG_STATIC|SRCHFLG_HTML|SRCHFLG_SCORE); | |
| 576 | + db_multi_exec( | |
| 577 | + "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" | |
| 578 | + "CREATE TEMP TABLE x(label TEXT,url TEXT,date TEXT,snip TEXT);" | |
| 579 | + ); | |
| 580 | + if( strchr(zSrchEnable, 'd') ){ | |
| 581 | + db_multi_exec( | |
| 582 | + "INSERT INTO x(label,url,date,snip)" | |
| 583 | + " SELECT printf('Document: %%s',foci.filename)," | |
| 584 | + " printf('%R/doc/trunk/%%s',foci.filename)," | |
| 585 | + " (SELECT datetime(event.mtime) FROM event" | |
| 586 | + " WHERE objid=symbolic_name_to_rid('trunk'))," | |
| 587 | + " snippet(stext('d',blob.rid,foci.filename))" | |
| 588 | + " FROM foci CROSS JOIN blob" | |
| 589 | + " WHERE checkinID=symbolic_name_to_rid('trunk')" | |
| 590 | + " AND blob.uuid=foci.uuid" | |
| 591 | + " AND (filename GLOB '*.wiki' OR" | |
| 592 | + " filename GLOB '*.md' OR" | |
| 593 | + " filename GLOB '*.txt' OR" | |
| 594 | + " filename GLOB '*.html');" | |
| 595 | + ); | |
| 596 | + } | |
| 597 | + if( strchr(zSrchEnable, 'w') ){ | |
| 598 | + db_multi_exec( | |
| 599 | + "WITH wiki(name,rid,mtime) AS (" | |
| 600 | + " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)" | |
| 601 | + " FROM tag, tagxref" | |
| 602 | + " WHERE tag.tagname GLOB 'wiki-*'" | |
| 603 | + " AND tagxref.tagid=tag.tagid" | |
| 604 | + " GROUP BY 1" | |
| 605 | + ")" | |
| 606 | + "INSERT INTO x(label,url,date,snip)" | |
| 607 | + " SELECT printf('Wiki: %%s',name)," | |
| 608 | + " printf('%R/wiki?name=%%s',urlencode(name))," | |
| 609 | + " datetime(mtime)," | |
| 610 | + " snippet(stext('w',rid,name))" | |
| 611 | + " FROM wiki;" | |
| 612 | + ); | |
| 613 | + } | |
| 614 | + if( strchr(zSrchEnable, 'c') ){ | |
| 615 | + db_multi_exec( | |
| 616 | + "WITH ckin(uuid,rid,mtime) AS (" | |
| 617 | + " SELECT blob.uuid, event.objid, event.mtime" | |
| 618 | + " FROM event, blob" | |
| 619 | + " WHERE event.type='ci'" | |
| 620 | + " AND blob.rid=event.objid" | |
| 621 | + ")" | |
| 622 | + "INSERT INTO x(label,url,date,snip)" | |
| 623 | + " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime))," | |
| 624 | + " printf('%R/timeline?c=%%s&n=8&y=ci',uuid)," | |
| 625 | + " datetime(mtime)," | |
| 626 | + " snippet(stext('c',rid,NULL))" | |
| 627 | + " FROM ckin;" | |
| 628 | + ); | |
| 629 | + } | |
| 630 | + db_prepare(&q, "SELECT url, substr(snip,9), label" | |
| 631 | + " FROM x WHERE snip IS NOT NULL" | |
| 632 | + " ORDER BY substr(snip,1,9) DESC, date DESC;"); | |
| 633 | + @ <ol> | |
| 634 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 635 | + const char *zUrl = db_column_text(&q, 0); | |
| 636 | + const char *zSnippet = db_column_text(&q, 1); | |
| 637 | + const char *zLabel = db_column_text(&q, 2); | |
| 638 | + @ <li><p>%s(href("%s",zUrl))%h(zLabel)</a><br>%s(zSnippet)</li> | |
| 639 | + } | |
| 640 | + db_finalize(&q); | |
| 641 | + @ </ol> | |
| 642 | + } | |
| 643 | + style_footer(); | |
| 644 | +} | |
| 645 | + | |
| 646 | + | |
| 647 | +/* | |
| 648 | +** This is a helper function for search_stext(). Writing into pOut | |
| 649 | +** the search text obtained from pIn according to zMimetype. | |
| 650 | +*/ | |
| 651 | +static void get_stext_by_mimetype( | |
| 652 | + Blob *pIn, | |
| 653 | + const char *zMimetype, | |
| 654 | + Blob *pOut | |
| 655 | +){ | |
| 656 | + Blob html, title; | |
| 657 | + blob_init(&html, 0, 0); | |
| 658 | + blob_init(&title, 0, 0); | |
| 659 | + if( zMimetype==0 ) zMimetype = "text/plain"; | |
| 660 | + if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){ | |
| 661 | + wiki_convert(pIn, &html, 0); | |
| 662 | + html_to_plaintext(blob_str(&html), pOut); | |
| 663 | + }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){ | |
| 664 | + markdown_to_html(pIn, &title, &html); | |
| 665 | + html_to_plaintext(blob_str(&html), pOut); | |
| 666 | + }else if( fossil_strcmp(zMimetype,"text/html")==0 ){ | |
| 667 | + html_to_plaintext(blob_str(pIn), pOut); | |
| 668 | + }else{ | |
| 669 | + *pOut = *pIn; | |
| 670 | + blob_init(pIn, 0, 0); | |
| 671 | + } | |
| 672 | + blob_reset(&html); | |
| 673 | + blob_reset(&title); | |
| 674 | +} | |
| 675 | + | |
| 676 | +/* | |
| 677 | +** Return "search text" - a reduced version of a document appropriate for | |
| 678 | +** full text search and/or for constructing a search result snippet. | |
| 679 | +** | |
| 680 | +** cType: d Embedded documentation | |
| 681 | +** s Source code listing | |
| 682 | +** w Wiki page | |
| 683 | +** c Check-in comment | |
| 684 | +** t Ticket text | |
| 685 | +** e Event/Blog text | |
| 686 | +** k Diff of a wiki | |
| 687 | +** f Diff of a checkin | |
| 688 | +** | |
| 689 | +** zArg1, zArg2: Description of the document, depending on cType. | |
| 690 | +*/ | |
| 691 | +void search_stext( | |
| 692 | + char cType, /* Type of document */ | |
| 693 | + const char *zArg1, /* First parameter */ | |
| 694 | + const char *zArg2, /* Second parameter */ | |
| 695 | + Blob *pOut /* OUT: Initialize to the search text */ | |
| 696 | +){ | |
| 697 | + blob_init(pOut, 0, 0); | |
| 698 | + switch( cType ){ | |
| 699 | + case 'd': /* Doc. zArg1: RID of the file. zArg2: Filename */ | |
| 700 | + case 's': { /* Source. zArg1: RID of the file. zArg2: Filename */ | |
| 701 | + int rid = atoi(zArg1); | |
| 702 | + Blob doc; | |
| 703 | + content_get(rid, &doc); | |
| 704 | + blob_to_utf8_no_bom(&doc, 0); | |
| 705 | + get_stext_by_mimetype(&doc, mimetype_from_name(zArg2), pOut); | |
| 706 | + blob_reset(&doc); | |
| 707 | + break; | |
| 708 | + } | |
| 709 | + case 'w': { /* Wiki. zArg1: RID of the page. zArg2: Page name */ | |
| 710 | + int rid = atoi(zArg1); | |
| 711 | + Manifest *pWiki = manifest_get(rid, CFTYPE_WIKI,0); | |
| 712 | + Blob wiki; | |
| 713 | + if( pWiki==0 ) break; | |
| 714 | + blob_init(&wiki, pWiki->zWiki, -1); | |
| 715 | + get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype), | |
| 716 | + pOut); | |
| 717 | + blob_reset(&wiki); | |
| 718 | + manifest_destroy(pWiki); | |
| 719 | + break; | |
| 720 | + } | |
| 721 | + case 'c': { /* Ckeckin: zArg1: RID of the checkin. zArg2: Not used */ | |
| 722 | + int rid = atoi(zArg1); | |
| 723 | + static Stmt q; | |
| 724 | + db_static_prepare(&q, | |
| 725 | + "SELECT coalesce(ecomment,comment)" | |
| 726 | + " ||' (user: '||coalesce(euser,user,'?')" | |
| 727 | + " ||', tags: '||" | |
| 728 | + " (SELECT group_concat(substr(tag.tagname,5),',')" | |
| 729 | + " FROM tag, tagxref" | |
| 730 | + " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" | |
| 731 | + " AND tagxref.rid=event.objid AND tagxref.tagtype>0)" | |
| 732 | + " ||')'" | |
| 733 | + " FROM event WHERE objid=:x AND type='ci'"); | |
| 734 | + db_bind_int(&q, ":x", rid); | |
| 735 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 736 | + db_column_blob(&q, 0, pOut); | |
| 737 | + blob_append(pOut, "\n", 1); | |
| 738 | + } | |
| 739 | + db_reset(&q); | |
| 740 | + break; | |
| 741 | + } | |
| 742 | + } | |
| 743 | +} | |
| 744 | + | |
| 745 | +/* | |
| 746 | +** The arguments cType,zArg1,zArg2 define an object that can be searched | |
| 747 | +** for. Return a URL (relative to the root of the Fossil project) that | |
| 748 | +** will jump to that document. | |
| 749 | +** | |
| 750 | +** Space to hold the returned string is obtained from mprintf() and should | |
| 751 | +** be freed by the caller using fossil_free() or the equivalent. | |
| 752 | +*/ | |
| 753 | +char *search_url( | |
| 754 | + char cType, /* Type of document */ | |
| 755 | + const char *zArg1, /* First parameter */ | |
| 756 | + const char *zArg2 /* Second parameter */ | |
| 757 | +){ | |
| 758 | + char *zUrl = 0; | |
| 759 | + switch( cType ){ | |
| 760 | + case 'd': { /* Doc. zArg1: RID of the file. zArg2: Filename */ | |
| 761 | + case 's': /* Source. zArg1: RID of the file. zArg2: Filename */ | |
| 762 | + zUrl = db_text(0, | |
| 763 | + "SELECT printf('/doc/%%s%%s', substr(blob.uuid,20), %Q)" | |
| 764 | + " FROM mlink, blob" | |
| 765 | + " WHERE mlink.fid=%d AND mlink.mid=blob.rid", | |
| 766 | + zArg2, atoi(zArg1)); | |
| 767 | + break; | |
| 768 | + } | |
| 769 | + case 'w': { /* Wiki. zArg1: RID of the page. zArg2: Page name */ | |
| 770 | + char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",atoi(zArg1)); | |
| 771 | + zUrl = mprintf("/wiki?id=%z&name=%t", zId, zArg2); | |
| 772 | + break; | |
| 773 | + } | |
| 774 | + case 'c': { /* Ckeckin: zArg1: RID of the checkin. zArg2: Not used */ | |
| 775 | + char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",atoi(zArg1)); | |
| 776 | + zUrl = mprintf("/info/%z", zId); | |
| 777 | + break; | |
| 778 | + } | |
| 779 | + } | |
| 780 | + return zUrl; | |
| 781 | +} | |
| 782 | + | |
| 783 | +/* | |
| 784 | +** COMMAND: test-search-stext | |
| 785 | +** | |
| 786 | +** Usage: fossil test-search-stext TYPE ARG1 ARG2 | |
| 787 | +*/ | |
| 788 | +void test_search_stext(void){ | |
| 789 | + Blob out; | |
| 790 | + char *zUrl; | |
| 791 | + db_find_and_open_repository(0,0); | |
| 792 | + if( g.argc!=5 ) usage("TYPE ARG1 ARG2"); | |
| 793 | + search_stext(g.argv[2][0], g.argv[3], g.argv[4], &out); | |
| 794 | + zUrl = search_url(g.argv[2][0], g.argv[3], g.argv[4]); | |
| 795 | + fossil_print("%s\n%z\n",blob_str(&out),zUrl); | |
| 796 | + blob_reset(&out); | |
| 797 | +} | |
| 260 | 798 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -26,54 +26,42 @@ | |
| 26 | #include "config.h" |
| 27 | #include "search.h" |
| 28 | #include <assert.h> |
| 29 | |
| 30 | #if INTERFACE |
| 31 | /* |
| 32 | ** A compiled search pattern |
| 33 | */ |
| 34 | struct Search { |
| 35 | int nTerm; /* Number of search terms */ |
| 36 | struct srchTerm { /* For each search term */ |
| 37 | char *z; /* Text */ |
| 38 | int n; /* length */ |
| 39 | } a[8]; |
| 40 | }; |
| 41 | #endif |
| 42 | |
| 43 | /* |
| 44 | ** Compile a search pattern |
| 45 | */ |
| 46 | Search *search_init(const char *zPattern){ |
| 47 | int nPattern = strlen(zPattern); |
| 48 | Search *p; |
| 49 | char *z; |
| 50 | int i; |
| 51 | |
| 52 | p = fossil_malloc( nPattern + sizeof(*p) + 1); |
| 53 | z = (char*)&p[1]; |
| 54 | memcpy(z, zPattern, nPattern+1); |
| 55 | memset(p, 0, sizeof(*p)); |
| 56 | while( *z && p->nTerm<sizeof(p->a)/sizeof(p->a[0]) ){ |
| 57 | while( !fossil_isalnum(*z) && *z ){ z++; } |
| 58 | if( *z==0 ) break; |
| 59 | p->a[p->nTerm].z = z; |
| 60 | for(i=1; fossil_isalnum(z[i]) || z[i]=='_'; i++){} |
| 61 | p->a[p->nTerm].n = i; |
| 62 | z += i; |
| 63 | p->nTerm++; |
| 64 | } |
| 65 | return p; |
| 66 | } |
| 67 | |
| 68 | |
| 69 | /* |
| 70 | ** Destroy a search context. |
| 71 | */ |
| 72 | void search_end(Search *p){ |
| 73 | free(p); |
| 74 | } |
| 75 | |
| 76 | /* |
| 77 | ** Theses characters constitute a word boundary |
| 78 | */ |
| 79 | static const char isBoundary[] = { |
| @@ -92,95 +80,388 @@ | |
| 92 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 93 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 94 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 95 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 96 | }; |
| 97 | |
| 98 | /* |
| 99 | ** Compare a search pattern against an input string and return a score. |
| 100 | ** |
| 101 | ** Scoring: |
| 102 | ** * All terms must match at least once or the score is zero |
| 103 | ** * 10 bonus points if the first occurrence is an exact match |
| 104 | ** * 1 additional point for each subsequent match of the same word |
| 105 | ** * Extra points of two consecutive words of the pattern are consecutive |
| 106 | ** in the document |
| 107 | */ |
| 108 | int search_score(Search *p, int nDoc, const char **azDoc){ |
| 109 | int iPrev = 999; |
| 110 | int score = 10; |
| 111 | int iBonus = 0; |
| 112 | int i, j, k; |
| 113 | const char *zDoc; |
| 114 | unsigned char seen[8]; |
| 115 | |
| 116 | memset(seen, 0, sizeof(seen)); |
| 117 | for(k=0; k<nDoc; k++){ |
| 118 | zDoc = azDoc[k]; |
| 119 | if( zDoc==0 ) continue; |
| 120 | for(i=0; zDoc[i]; i++){ |
| 121 | char c = zDoc[i]; |
| 122 | if( isBoundary[c&0xff] ) continue; |
| 123 | for(j=0; j<p->nTerm; j++){ |
| 124 | int n = p->a[j].n; |
| 125 | if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 ){ |
| 126 | score += 1; |
| 127 | if( !seen[j] ){ |
| 128 | if( isBoundary[zDoc[i+n]&0xff] ) score += 10; |
| 129 | seen[j] = 1; |
| 130 | } |
| 131 | if( j==iPrev+1 ){ |
| 132 | score += iBonus; |
| 133 | } |
| 134 | i += n-1; |
| 135 | iPrev = j; |
| 136 | iBonus = 50; |
| 137 | break; |
| 138 | } |
| 139 | } |
| 140 | iBonus /= 2; |
| 141 | while( !isBoundary[zDoc[i]&0xff] ){ i++; } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /* Every term must be seen or else the score is zero */ |
| 146 | for(j=0; j<p->nTerm; j++){ |
| 147 | if( !seen[j] ) return 0; |
| 148 | } |
| 149 | |
| 150 | return score; |
| 151 | } |
| 152 | |
| 153 | /* |
| 154 | ** This is an SQLite function that scores its input using |
| 155 | ** a pre-computed pattern. |
| 156 | */ |
| 157 | static void search_score_sqlfunc( |
| 158 | sqlite3_context *context, |
| 159 | int argc, |
| 160 | sqlite3_value **argv |
| 161 | ){ |
| 162 | Search *p = (Search*)sqlite3_user_data(context); |
| 163 | const char **azDoc; |
| 164 | int score; |
| 165 | int i; |
| 166 | |
| 167 | azDoc = fossil_malloc( sizeof(const char*)*(argc+1) ); |
| 168 | for(i=0; i<argc; i++) azDoc[i] = (const char*)sqlite3_value_text(argv[i]); |
| 169 | score = search_score(p, argc, azDoc); |
| 170 | fossil_free(azDoc); |
| 171 | sqlite3_result_int(context, score); |
| 172 | } |
| 173 | |
| 174 | /* |
| 175 | ** Register the "score()" SQL function to score its input text |
| 176 | ** using the given Search object. Once this function is registered, |
| 177 | ** do not delete the Search object. |
| 178 | */ |
| 179 | void search_sql_setup(Search *p){ |
| 180 | sqlite3_create_function(g.db, "score", -1, SQLITE_UTF8, p, |
| 181 | search_score_sqlfunc, 0, 0); |
| 182 | } |
| 183 | |
| 184 | /* |
| 185 | ** Testing the search function. |
| 186 | ** |
| @@ -198,11 +479,10 @@ | |
| 198 | ** of entries returned. The -width option can be |
| 199 | ** used to set the output width used when printing |
| 200 | ** matches. |
| 201 | */ |
| 202 | void search_cmd(void){ |
| 203 | Search *p; |
| 204 | Blob pattern; |
| 205 | int i; |
| 206 | Blob sql = empty_blob; |
| 207 | Stmt q; |
| 208 | int iBest; |
| @@ -227,13 +507,13 @@ | |
| 227 | if( g.argc<2 ) return; |
| 228 | blob_init(&pattern, g.argv[2], -1); |
| 229 | for(i=3; i<g.argc; i++){ |
| 230 | blob_appendf(&pattern, " %s", g.argv[i]); |
| 231 | } |
| 232 | p = search_init(blob_str(&pattern)); |
| 233 | blob_reset(&pattern); |
| 234 | search_sql_setup(p); |
| 235 | |
| 236 | db_multi_exec( |
| 237 | "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);" |
| 238 | "CREATE INDEX srch_idx1 ON srch(x);" |
| 239 | "INSERT INTO srch(rid,uuid,date,comment,x)" |
| @@ -255,5 +535,263 @@ | |
| 255 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 256 | blob_reset(&sql); |
| 257 | print_timeline(&q, nLimit, width, 0); |
| 258 | db_finalize(&q); |
| 259 | } |
| 260 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -26,54 +26,42 @@ | |
| 26 | #include "config.h" |
| 27 | #include "search.h" |
| 28 | #include <assert.h> |
| 29 | |
| 30 | #if INTERFACE |
| 31 | |
| 32 | /* Maximum number of search terms */ |
| 33 | #define SEARCH_MAX_TERM 8 |
| 34 | |
| 35 | /* |
| 36 | ** A compiled search pattern |
| 37 | */ |
| 38 | struct Search { |
| 39 | int nTerm; /* Number of search terms */ |
| 40 | struct srchTerm { /* For each search term */ |
| 41 | char *z; /* Text */ |
| 42 | int n; /* length */ |
| 43 | } a[SEARCH_MAX_TERM]; |
| 44 | /* Snippet controls */ |
| 45 | char *zPattern; /* The search pattern */ |
| 46 | char *zMarkBegin; /* Start of a match */ |
| 47 | char *zMarkEnd; /* End of a match */ |
| 48 | char *zMarkGap; /* A gap between two matches */ |
| 49 | unsigned fSrchFlg; /* Flags */ |
| 50 | }; |
| 51 | |
| 52 | #define SRCHFLG_HTML 0x01 /* Escape snippet text for HTML */ |
| 53 | #define SRCHFLG_SCORE 0x02 /* Prepend the score to each snippet */ |
| 54 | #define SRCHFLG_STATIC 0x04 /* The static gSearch object */ |
| 55 | |
| 56 | #endif |
| 57 | |
| 58 | /* |
| 59 | ** There is a single global Search object: |
| 60 | */ |
| 61 | static Search gSearch; |
| 62 | |
| 63 | |
| 64 | /* |
| 65 | ** Theses characters constitute a word boundary |
| 66 | */ |
| 67 | static const char isBoundary[] = { |
| @@ -92,95 +80,388 @@ | |
| 80 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 81 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 82 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 83 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 84 | }; |
| 85 | #define ISALNUM(x) (!isBoundary[(x)&0xff]) |
| 86 | |
| 87 | |
| 88 | /* |
| 89 | ** Destroy a search context. |
| 90 | */ |
| 91 | void search_end(Search *p){ |
| 92 | if( p ){ |
| 93 | fossil_free(p->zPattern); |
| 94 | fossil_free(p->zMarkBegin); |
| 95 | fossil_free(p->zMarkEnd); |
| 96 | fossil_free(p->zMarkGap); |
| 97 | memset(p, 0, sizeof(*p)); |
| 98 | if( p!=&gSearch ) fossil_free(p); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | /* |
| 103 | ** Compile a search pattern |
| 104 | */ |
| 105 | Search *search_init( |
| 106 | const char *zPattern, /* The search pattern */ |
| 107 | const char *zMarkBegin, /* Start of a match */ |
| 108 | const char *zMarkEnd, /* End of a match */ |
| 109 | const char *zMarkGap, /* A gap between two matches */ |
| 110 | unsigned fSrchFlg /* Flags */ |
| 111 | ){ |
| 112 | Search *p; |
| 113 | char *z; |
| 114 | int i; |
| 115 | |
| 116 | if( fSrchFlg & SRCHFLG_STATIC ){ |
| 117 | p = &gSearch; |
| 118 | search_end(p); |
| 119 | }else{ |
| 120 | p = fossil_malloc(sizeof(*p)); |
| 121 | memset(p, 0, sizeof(*p)); |
| 122 | } |
| 123 | p->zPattern = z = mprintf("%s", zPattern); |
| 124 | p->zMarkBegin = mprintf("%s", zMarkBegin); |
| 125 | p->zMarkEnd = mprintf("%s", zMarkEnd); |
| 126 | p->zMarkGap = mprintf("%s", zMarkGap); |
| 127 | p->fSrchFlg = fSrchFlg; |
| 128 | while( *z && p->nTerm<SEARCH_MAX_TERM ){ |
| 129 | while( *z && !ISALNUM(*z) ){ z++; } |
| 130 | if( *z==0 ) break; |
| 131 | p->a[p->nTerm].z = z; |
| 132 | for(i=1; ISALNUM(z[i]); i++){} |
| 133 | p->a[p->nTerm].n = i; |
| 134 | z += i; |
| 135 | p->nTerm++; |
| 136 | } |
| 137 | return p; |
| 138 | } |
| 139 | |
| 140 | |
| 141 | /* |
| 142 | ** Append n bytes of text to snippet zTxt. Encode the text appropriately. |
| 143 | */ |
| 144 | static void snippet_text_append( |
| 145 | Search *p, /* The search context */ |
| 146 | Blob *pSnip, /* Append to this snippet */ |
| 147 | const char *zTxt, /* Text to append */ |
| 148 | int n /* How many bytes to append */ |
| 149 | ){ |
| 150 | if( n>0 ){ |
| 151 | if( p->fSrchFlg & SRCHFLG_HTML ){ |
| 152 | blob_appendf(pSnip, "%#h", n, zTxt); |
| 153 | }else{ |
| 154 | blob_append(pSnip, zTxt, n); |
| 155 | } |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | ** Compare a search pattern against one or more input strings which |
| 161 | ** collectively comprise a document. Return a match score. Optionally |
| 162 | ** also return a "snippet". |
| 163 | ** |
| 164 | ** Scoring: |
| 165 | ** * All terms must match at least once or the score is zero |
| 166 | ** * One point for each matching term |
| 167 | ** * Extra points if consecutive words of the pattern are consecutive |
| 168 | ** in the document |
| 169 | */ |
| 170 | static int search_score( |
| 171 | Search *p, /* Search pattern and flags */ |
| 172 | int nDoc, /* Number of strings in this document */ |
| 173 | const char **azDoc, /* Text of each string */ |
| 174 | Blob *pSnip /* If not NULL: Write a snippet here */ |
| 175 | ){ |
| 176 | int score; /* Final score */ |
| 177 | int i; /* Offset into current document */ |
| 178 | int ii; /* Loop counter */ |
| 179 | int j; /* Loop over search terms */ |
| 180 | int k; /* Loop over prior terms */ |
| 181 | int iWord = 0; /* Current word number */ |
| 182 | int iDoc; /* Current document number */ |
| 183 | int wantGap = 0; /* True if a zMarkGap is wanted */ |
| 184 | const char *zDoc; /* Current document text */ |
| 185 | const int CTX = 50; /* Amount of snippet context */ |
| 186 | int anMatch[SEARCH_MAX_TERM]; /* Number of terms in best match */ |
| 187 | int aiBestDoc[SEARCH_MAX_TERM]; /* Document containing best match */ |
| 188 | int aiBestOfst[SEARCH_MAX_TERM]; /* Byte offset to start of best match */ |
| 189 | int aiLastDoc[SEARCH_MAX_TERM]; /* Document containing most recent match */ |
| 190 | int aiLastOfst[SEARCH_MAX_TERM]; /* Byte offset to the most recent match */ |
| 191 | int aiWordIdx[SEARCH_MAX_TERM]; /* Word index of most recent match */ |
| 192 | |
| 193 | memset(anMatch, 0, sizeof(anMatch)); |
| 194 | memset(aiWordIdx, 0xff, sizeof(aiWordIdx)); |
| 195 | for(iDoc=0; iDoc<nDoc; iDoc++){ |
| 196 | zDoc = azDoc[iDoc]; |
| 197 | if( zDoc==0 ) continue; |
| 198 | iWord++; |
| 199 | for(i=0; zDoc[i]; i++){ |
| 200 | if( !ISALNUM(zDoc[i]) ) continue; |
| 201 | iWord++; |
| 202 | for(j=0; j<p->nTerm; j++){ |
| 203 | int n = p->a[j].n; |
| 204 | if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 |
| 205 | && (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*') |
| 206 | ){ |
| 207 | aiWordIdx[j] = iWord; |
| 208 | aiLastDoc[j] = iDoc; |
| 209 | aiLastOfst[j] = i; |
| 210 | for(k=1; j-k>=0 && anMatch[j-k] && aiWordIdx[j-k]==iWord-k; k++){} |
| 211 | for(ii=0; ii<k; ii++){ |
| 212 | if( anMatch[j-ii]<k ){ |
| 213 | anMatch[j-ii] = k; |
| 214 | aiBestDoc[j-ii] = aiLastDoc[j-ii]; |
| 215 | aiBestOfst[j-ii] = aiLastOfst[j-ii]; |
| 216 | } |
| 217 | } |
| 218 | break; |
| 219 | } |
| 220 | } |
| 221 | while( ISALNUM(zDoc[i]) ){ i++; } |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | /* Finished search all documents. |
| 226 | ** Every term must be seen or else the score is zero |
| 227 | */ |
| 228 | score = 1; |
| 229 | for(j=0; j<p->nTerm; j++) score *= anMatch[j]; |
| 230 | if( score==0 || pSnip==0 ) return score; |
| 231 | |
| 232 | |
| 233 | /* Prepare a snippet that describes the matching text. |
| 234 | */ |
| 235 | blob_init(pSnip, 0, 0); |
| 236 | if( p->fSrchFlg & SRCHFLG_SCORE ) blob_appendf(pSnip, "%08x", score); |
| 237 | |
| 238 | while(1){ |
| 239 | int iOfst; |
| 240 | int iTail; |
| 241 | int iBest; |
| 242 | for(ii=0; ii<p->nTerm && anMatch[ii]==0; ii++){} |
| 243 | if( ii>=p->nTerm ) break; /* This is where the loop exits */ |
| 244 | iBest = ii; |
| 245 | iDoc = aiBestDoc[ii]; |
| 246 | iOfst = aiBestOfst[ii]; |
| 247 | for(; ii<p->nTerm; ii++){ |
| 248 | if( anMatch[ii]==0 ) continue; |
| 249 | if( aiBestDoc[ii]>iDoc ) continue; |
| 250 | if( aiBestOfst[ii]>iOfst ) continue; |
| 251 | iDoc = aiBestDoc[ii]; |
| 252 | iOfst = aiBestOfst[ii]; |
| 253 | iBest = ii; |
| 254 | } |
| 255 | iTail = iOfst + p->a[iBest].n; |
| 256 | anMatch[iBest] = 0; |
| 257 | for(ii=0; ii<p->nTerm; ii++){ |
| 258 | if( anMatch[ii]==0 ) continue; |
| 259 | if( aiBestDoc[ii]!=iDoc ) continue; |
| 260 | if( aiBestOfst[ii]<=iTail+CTX*2 ){ |
| 261 | if( iTail<aiBestOfst[ii]+p->a[ii].n ){ |
| 262 | iTail = aiBestOfst[ii]+p->a[ii].n; |
| 263 | } |
| 264 | anMatch[ii] = 0; |
| 265 | ii = -1; |
| 266 | continue; |
| 267 | } |
| 268 | } |
| 269 | zDoc = azDoc[iDoc]; |
| 270 | iOfst -= CTX; |
| 271 | if( iOfst<0 ) iOfst = 0; |
| 272 | while( iOfst>0 && ISALNUM(zDoc[iOfst-1]) ) iOfst--; |
| 273 | while( zDoc[iOfst] && !ISALNUM(zDoc[iOfst]) ) iOfst++; |
| 274 | for(ii=0; ii<CTX && zDoc[iTail]; ii++, iTail++){} |
| 275 | while( ISALNUM(zDoc[iTail]) ) iTail++; |
| 276 | if( iOfst>0 || wantGap ) blob_append(pSnip, p->zMarkGap, -1); |
| 277 | wantGap = zDoc[iTail]!=0; |
| 278 | zDoc += iOfst; |
| 279 | iTail -= iOfst; |
| 280 | |
| 281 | /* Add a snippet segment using characters iOfst..iOfst+iTail from zDoc */ |
| 282 | for(i=0; i<iTail; i++){ |
| 283 | if( !ISALNUM(zDoc[i]) ) continue; |
| 284 | for(j=0; j<p->nTerm; j++){ |
| 285 | int n = p->a[j].n; |
| 286 | if( sqlite3_strnicmp(p->a[j].z, &zDoc[i], n)==0 |
| 287 | && (!ISALNUM(zDoc[i+n]) || p->a[j].z[n]=='*') |
| 288 | ){ |
| 289 | snippet_text_append(p, pSnip, zDoc, i); |
| 290 | zDoc += i; |
| 291 | iTail -= i; |
| 292 | blob_append(pSnip, p->zMarkBegin, -1); |
| 293 | if( p->a[j].z[n]=='*' ){ |
| 294 | while( ISALNUM(zDoc[n]) ) n++; |
| 295 | } |
| 296 | snippet_text_append(p, pSnip, zDoc, n); |
| 297 | zDoc += n; |
| 298 | iTail -= n; |
| 299 | blob_append(pSnip, p->zMarkEnd, -1); |
| 300 | i = -1; |
| 301 | break; |
| 302 | } /* end-if */ |
| 303 | } /* end for(j) */ |
| 304 | if( j<p->nTerm ){ |
| 305 | while( ISALNUM(zDoc[i]) && i<iTail ){ i++; } |
| 306 | } |
| 307 | } /* end for(i) */ |
| 308 | snippet_text_append(p, pSnip, zDoc, iTail); |
| 309 | } |
| 310 | if( wantGap ) blob_append(pSnip, p->zMarkGap, -1); |
| 311 | return score; |
| 312 | } |
| 313 | |
| 314 | /* |
| 315 | ** COMMAND: test-snippet |
| 316 | ** |
| 317 | ** Usage: fossil test-snippet SEARCHSTRING FILE1 FILE2 ... |
| 318 | */ |
| 319 | void test_snippet_cmd(void){ |
| 320 | Search *p; |
| 321 | int i; |
| 322 | Blob x; |
| 323 | Blob snip; |
| 324 | int score; |
| 325 | char *zDoc; |
| 326 | int flg = 0; |
| 327 | char *zBegin = (char*)find_option("begin",0,1); |
| 328 | char *zEnd = (char*)find_option("end",0,1); |
| 329 | char *zGap = (char*)find_option("gap",0,1); |
| 330 | if( find_option("html",0,0)!=0 ) flg |= SRCHFLG_HTML; |
| 331 | if( find_option("score",0,0)!=0 ) flg |= SRCHFLG_SCORE; |
| 332 | if( find_option("static",0,0)!=0 ) flg |= SRCHFLG_STATIC; |
| 333 | verify_all_options(); |
| 334 | if( g.argc<4 ) usage("SEARCHSTRING FILE1..."); |
| 335 | if( zBegin==0 ) zBegin = "[["; |
| 336 | if( zEnd==0 ) zEnd = "]]"; |
| 337 | if( zGap==0 ) zGap = " ... "; |
| 338 | p = search_init(g.argv[2], zBegin, zEnd, zGap, flg); |
| 339 | for(i=3; i<g.argc; i++){ |
| 340 | blob_read_from_file(&x, g.argv[i]); |
| 341 | zDoc = blob_str(&x); |
| 342 | score = search_score(p, 1, (const char**)&zDoc, &snip); |
| 343 | fossil_print("%s: %d\n", g.argv[i], score); |
| 344 | blob_reset(&x); |
| 345 | if( score ){ |
| 346 | fossil_print("%.78c\n%s\n%.78c\n\n", '=', blob_str(&snip), '='); |
| 347 | blob_reset(&snip); |
| 348 | } |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | /* |
| 353 | ** An SQL function to initialize the global search pattern: |
| 354 | ** |
| 355 | ** search_init(PATTERN,BEGIN,END,GAP,FLAGS) |
| 356 | ** |
| 357 | ** All arguments are optional. |
| 358 | */ |
| 359 | static void search_init_sqlfunc( |
| 360 | sqlite3_context *context, |
| 361 | int argc, |
| 362 | sqlite3_value **argv |
| 363 | ){ |
| 364 | const char *zPattern = 0; |
| 365 | const char *zBegin = "<b>"; |
| 366 | const char *zEnd = "</b>"; |
| 367 | const char *zGap = " ... "; |
| 368 | unsigned int flg = SRCHFLG_HTML; |
| 369 | switch( argc ){ |
| 370 | default: |
| 371 | flg = (unsigned int)sqlite3_value_int(argv[4]); |
| 372 | case 4: |
| 373 | zGap = (const char*)sqlite3_value_text(argv[3]); |
| 374 | case 3: |
| 375 | zEnd = (const char*)sqlite3_value_text(argv[2]); |
| 376 | case 2: |
| 377 | zBegin = (const char*)sqlite3_value_text(argv[1]); |
| 378 | case 1: |
| 379 | zPattern = (const char*)sqlite3_value_text(argv[0]); |
| 380 | } |
| 381 | if( zPattern && zPattern[0] ){ |
| 382 | search_init(zPattern, zBegin, zEnd, zGap, flg | SRCHFLG_STATIC); |
| 383 | }else{ |
| 384 | search_end(&gSearch); |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | /* |
| 389 | ** This is an SQLite function that scores its input using |
| 390 | ** the pattern from the previous call to search_init(). |
| 391 | */ |
| 392 | static void search_score_sqlfunc( |
| 393 | sqlite3_context *context, |
| 394 | int argc, |
| 395 | sqlite3_value **argv |
| 396 | ){ |
| 397 | int isSnippet = sqlite3_user_data(context)!=0; |
| 398 | const char **azDoc; |
| 399 | int score; |
| 400 | int i; |
| 401 | Blob snip; |
| 402 | |
| 403 | if( gSearch.nTerm==0 ) return; |
| 404 | azDoc = fossil_malloc( sizeof(const char*)*(argc+1) ); |
| 405 | for(i=0; i<argc; i++) azDoc[i] = (const char*)sqlite3_value_text(argv[i]); |
| 406 | score = search_score(&gSearch, argc, azDoc, isSnippet ? &snip : 0); |
| 407 | fossil_free((void *)azDoc); |
| 408 | if( isSnippet ){ |
| 409 | if( score ){ |
| 410 | sqlite3_result_text(context, blob_materialize(&snip), -1, fossil_free); |
| 411 | } |
| 412 | }else{ |
| 413 | sqlite3_result_int(context, score); |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | ** This is an SQLite function that computes the searchable text. |
| 419 | ** It is a wrapper around the search_stext() routine. See the |
| 420 | ** search_stext() routine for further detail. |
| 421 | */ |
| 422 | static void search_stext_sqlfunc( |
| 423 | sqlite3_context *context, |
| 424 | int argc, |
| 425 | sqlite3_value **argv |
| 426 | ){ |
| 427 | Blob txt; |
| 428 | const char *zType = (const char*)sqlite3_value_text(argv[0]); |
| 429 | const char *zArg1 = (const char*)sqlite3_value_text(argv[1]); |
| 430 | const char *zArg2 = (const char*)sqlite3_value_text(argv[2]); |
| 431 | search_stext(zType[0], zArg1, zArg2, &txt); |
| 432 | sqlite3_result_text(context, blob_materialize(&txt), -1, fossil_free); |
| 433 | } |
| 434 | |
| 435 | /* |
| 436 | ** Encode a string for use as a query parameter in a URL |
| 437 | */ |
| 438 | static void search_urlencode_sqlfunc( |
| 439 | sqlite3_context *context, |
| 440 | int argc, |
| 441 | sqlite3_value **argv |
| 442 | ){ |
| 443 | char *z = mprintf("%T",sqlite3_value_text(argv[0])); |
| 444 | sqlite3_result_text(context, z, -1, fossil_free); |
| 445 | } |
| 446 | |
| 447 | /* |
| 448 | ** Register the "score()" SQL function to score its input text |
| 449 | ** using the given Search object. Once this function is registered, |
| 450 | ** do not delete the Search object. |
| 451 | */ |
| 452 | void search_sql_setup(sqlite3 *db){ |
| 453 | sqlite3_create_function(db, "score", -1, SQLITE_UTF8, 0, |
| 454 | search_score_sqlfunc, 0, 0); |
| 455 | sqlite3_create_function(db, "snippet", -1, SQLITE_UTF8, &gSearch, |
| 456 | search_score_sqlfunc, 0, 0); |
| 457 | sqlite3_create_function(db, "search_init", -1, SQLITE_UTF8, 0, |
| 458 | search_init_sqlfunc, 0, 0); |
| 459 | sqlite3_create_function(db, "stext", 3, SQLITE_UTF8, 0, |
| 460 | search_stext_sqlfunc, 0, 0); |
| 461 | sqlite3_create_function(db, "urlencode", 1, SQLITE_UTF8, 0, |
| 462 | search_urlencode_sqlfunc, 0, 0); |
| 463 | } |
| 464 | |
| 465 | /* |
| 466 | ** Testing the search function. |
| 467 | ** |
| @@ -198,11 +479,10 @@ | |
| 479 | ** of entries returned. The -width option can be |
| 480 | ** used to set the output width used when printing |
| 481 | ** matches. |
| 482 | */ |
| 483 | void search_cmd(void){ |
| 484 | Blob pattern; |
| 485 | int i; |
| 486 | Blob sql = empty_blob; |
| 487 | Stmt q; |
| 488 | int iBest; |
| @@ -227,13 +507,13 @@ | |
| 507 | if( g.argc<2 ) return; |
| 508 | blob_init(&pattern, g.argv[2], -1); |
| 509 | for(i=3; i<g.argc; i++){ |
| 510 | blob_appendf(&pattern, " %s", g.argv[i]); |
| 511 | } |
| 512 | (void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC); |
| 513 | blob_reset(&pattern); |
| 514 | search_sql_setup(g.db); |
| 515 | |
| 516 | db_multi_exec( |
| 517 | "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);" |
| 518 | "CREATE INDEX srch_idx1 ON srch(x);" |
| 519 | "INSERT INTO srch(rid,uuid,date,comment,x)" |
| @@ -255,5 +535,263 @@ | |
| 535 | db_prepare(&q, "%s", blob_sql_text(&sql)); |
| 536 | blob_reset(&sql); |
| 537 | print_timeline(&q, nLimit, width, 0); |
| 538 | db_finalize(&q); |
| 539 | } |
| 540 | |
| 541 | /* |
| 542 | ** WEBPAGE: /search |
| 543 | ** |
| 544 | ** This is an EXPERIMENTAL page for doing search across a repository. |
| 545 | ** |
| 546 | ** The current implementation does a full text search over embedded |
| 547 | ** documentation files on the tip of the "trunk" branch. Only files |
| 548 | ** ending in ".wiki", ".md", ".html", and ".txt" are searched. |
| 549 | ** |
| 550 | ** The entire text is scanned. There is no full-text index. This is |
| 551 | ** experimental. We may change to using a full-text index depending |
| 552 | ** on performance. |
| 553 | ** |
| 554 | ** Other pending enhancements: |
| 555 | ** * Search tickets |
| 556 | ** * Search wiki |
| 557 | */ |
| 558 | void search_page(void){ |
| 559 | const char *zPattern = PD("s",""); |
| 560 | Stmt q; |
| 561 | const char *zSrchEnable = "dwc"; |
| 562 | |
| 563 | login_check_credentials(); |
| 564 | if( !g.perm.Read ){ login_needed(); return; } |
| 565 | style_header("Search"); |
| 566 | @ <form method="GET" action="search"><center> |
| 567 | @ <input type="text" name="s" size="40" value="%h(zPattern)"> |
| 568 | @ <input type="submit" value="Search"> |
| 569 | @ </center></form> |
| 570 | while( fossil_isspace(zPattern[0]) ) zPattern++; |
| 571 | if( zPattern[0] ){ |
| 572 | search_sql_setup(g.db); |
| 573 | add_content_sql_commands(g.db); |
| 574 | search_init(zPattern, "<b>", "</b>", " ... ", |
| 575 | SRCHFLG_STATIC|SRCHFLG_HTML|SRCHFLG_SCORE); |
| 576 | db_multi_exec( |
| 577 | "CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;" |
| 578 | "CREATE TEMP TABLE x(label TEXT,url TEXT,date TEXT,snip TEXT);" |
| 579 | ); |
| 580 | if( strchr(zSrchEnable, 'd') ){ |
| 581 | db_multi_exec( |
| 582 | "INSERT INTO x(label,url,date,snip)" |
| 583 | " SELECT printf('Document: %%s',foci.filename)," |
| 584 | " printf('%R/doc/trunk/%%s',foci.filename)," |
| 585 | " (SELECT datetime(event.mtime) FROM event" |
| 586 | " WHERE objid=symbolic_name_to_rid('trunk'))," |
| 587 | " snippet(stext('d',blob.rid,foci.filename))" |
| 588 | " FROM foci CROSS JOIN blob" |
| 589 | " WHERE checkinID=symbolic_name_to_rid('trunk')" |
| 590 | " AND blob.uuid=foci.uuid" |
| 591 | " AND (filename GLOB '*.wiki' OR" |
| 592 | " filename GLOB '*.md' OR" |
| 593 | " filename GLOB '*.txt' OR" |
| 594 | " filename GLOB '*.html');" |
| 595 | ); |
| 596 | } |
| 597 | if( strchr(zSrchEnable, 'w') ){ |
| 598 | db_multi_exec( |
| 599 | "WITH wiki(name,rid,mtime) AS (" |
| 600 | " SELECT substr(tagname,6), tagxref.rid, max(tagxref.mtime)" |
| 601 | " FROM tag, tagxref" |
| 602 | " WHERE tag.tagname GLOB 'wiki-*'" |
| 603 | " AND tagxref.tagid=tag.tagid" |
| 604 | " GROUP BY 1" |
| 605 | ")" |
| 606 | "INSERT INTO x(label,url,date,snip)" |
| 607 | " SELECT printf('Wiki: %%s',name)," |
| 608 | " printf('%R/wiki?name=%%s',urlencode(name))," |
| 609 | " datetime(mtime)," |
| 610 | " snippet(stext('w',rid,name))" |
| 611 | " FROM wiki;" |
| 612 | ); |
| 613 | } |
| 614 | if( strchr(zSrchEnable, 'c') ){ |
| 615 | db_multi_exec( |
| 616 | "WITH ckin(uuid,rid,mtime) AS (" |
| 617 | " SELECT blob.uuid, event.objid, event.mtime" |
| 618 | " FROM event, blob" |
| 619 | " WHERE event.type='ci'" |
| 620 | " AND blob.rid=event.objid" |
| 621 | ")" |
| 622 | "INSERT INTO x(label,url,date,snip)" |
| 623 | " SELECT printf('Check-in [%%.10s] on %%s',uuid,datetime(mtime))," |
| 624 | " printf('%R/timeline?c=%%s&n=8&y=ci',uuid)," |
| 625 | " datetime(mtime)," |
| 626 | " snippet(stext('c',rid,NULL))" |
| 627 | " FROM ckin;" |
| 628 | ); |
| 629 | } |
| 630 | db_prepare(&q, "SELECT url, substr(snip,9), label" |
| 631 | " FROM x WHERE snip IS NOT NULL" |
| 632 | " ORDER BY substr(snip,1,9) DESC, date DESC;"); |
| 633 | @ <ol> |
| 634 | while( db_step(&q)==SQLITE_ROW ){ |
| 635 | const char *zUrl = db_column_text(&q, 0); |
| 636 | const char *zSnippet = db_column_text(&q, 1); |
| 637 | const char *zLabel = db_column_text(&q, 2); |
| 638 | @ <li><p>%s(href("%s",zUrl))%h(zLabel)</a><br>%s(zSnippet)</li> |
| 639 | } |
| 640 | db_finalize(&q); |
| 641 | @ </ol> |
| 642 | } |
| 643 | style_footer(); |
| 644 | } |
| 645 | |
| 646 | |
| 647 | /* |
| 648 | ** This is a helper function for search_stext(). Writing into pOut |
| 649 | ** the search text obtained from pIn according to zMimetype. |
| 650 | */ |
| 651 | static void get_stext_by_mimetype( |
| 652 | Blob *pIn, |
| 653 | const char *zMimetype, |
| 654 | Blob *pOut |
| 655 | ){ |
| 656 | Blob html, title; |
| 657 | blob_init(&html, 0, 0); |
| 658 | blob_init(&title, 0, 0); |
| 659 | if( zMimetype==0 ) zMimetype = "text/plain"; |
| 660 | if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){ |
| 661 | wiki_convert(pIn, &html, 0); |
| 662 | html_to_plaintext(blob_str(&html), pOut); |
| 663 | }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){ |
| 664 | markdown_to_html(pIn, &title, &html); |
| 665 | html_to_plaintext(blob_str(&html), pOut); |
| 666 | }else if( fossil_strcmp(zMimetype,"text/html")==0 ){ |
| 667 | html_to_plaintext(blob_str(pIn), pOut); |
| 668 | }else{ |
| 669 | *pOut = *pIn; |
| 670 | blob_init(pIn, 0, 0); |
| 671 | } |
| 672 | blob_reset(&html); |
| 673 | blob_reset(&title); |
| 674 | } |
| 675 | |
| 676 | /* |
| 677 | ** Return "search text" - a reduced version of a document appropriate for |
| 678 | ** full text search and/or for constructing a search result snippet. |
| 679 | ** |
| 680 | ** cType: d Embedded documentation |
| 681 | ** s Source code listing |
| 682 | ** w Wiki page |
| 683 | ** c Check-in comment |
| 684 | ** t Ticket text |
| 685 | ** e Event/Blog text |
| 686 | ** k Diff of a wiki |
| 687 | ** f Diff of a checkin |
| 688 | ** |
| 689 | ** zArg1, zArg2: Description of the document, depending on cType. |
| 690 | */ |
| 691 | void search_stext( |
| 692 | char cType, /* Type of document */ |
| 693 | const char *zArg1, /* First parameter */ |
| 694 | const char *zArg2, /* Second parameter */ |
| 695 | Blob *pOut /* OUT: Initialize to the search text */ |
| 696 | ){ |
| 697 | blob_init(pOut, 0, 0); |
| 698 | switch( cType ){ |
| 699 | case 'd': /* Doc. zArg1: RID of the file. zArg2: Filename */ |
| 700 | case 's': { /* Source. zArg1: RID of the file. zArg2: Filename */ |
| 701 | int rid = atoi(zArg1); |
| 702 | Blob doc; |
| 703 | content_get(rid, &doc); |
| 704 | blob_to_utf8_no_bom(&doc, 0); |
| 705 | get_stext_by_mimetype(&doc, mimetype_from_name(zArg2), pOut); |
| 706 | blob_reset(&doc); |
| 707 | break; |
| 708 | } |
| 709 | case 'w': { /* Wiki. zArg1: RID of the page. zArg2: Page name */ |
| 710 | int rid = atoi(zArg1); |
| 711 | Manifest *pWiki = manifest_get(rid, CFTYPE_WIKI,0); |
| 712 | Blob wiki; |
| 713 | if( pWiki==0 ) break; |
| 714 | blob_init(&wiki, pWiki->zWiki, -1); |
| 715 | get_stext_by_mimetype(&wiki, wiki_filter_mimetypes(pWiki->zMimetype), |
| 716 | pOut); |
| 717 | blob_reset(&wiki); |
| 718 | manifest_destroy(pWiki); |
| 719 | break; |
| 720 | } |
| 721 | case 'c': { /* Ckeckin: zArg1: RID of the checkin. zArg2: Not used */ |
| 722 | int rid = atoi(zArg1); |
| 723 | static Stmt q; |
| 724 | db_static_prepare(&q, |
| 725 | "SELECT coalesce(ecomment,comment)" |
| 726 | " ||' (user: '||coalesce(euser,user,'?')" |
| 727 | " ||', tags: '||" |
| 728 | " (SELECT group_concat(substr(tag.tagname,5),',')" |
| 729 | " FROM tag, tagxref" |
| 730 | " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" |
| 731 | " AND tagxref.rid=event.objid AND tagxref.tagtype>0)" |
| 732 | " ||')'" |
| 733 | " FROM event WHERE objid=:x AND type='ci'"); |
| 734 | db_bind_int(&q, ":x", rid); |
| 735 | if( db_step(&q)==SQLITE_ROW ){ |
| 736 | db_column_blob(&q, 0, pOut); |
| 737 | blob_append(pOut, "\n", 1); |
| 738 | } |
| 739 | db_reset(&q); |
| 740 | break; |
| 741 | } |
| 742 | } |
| 743 | } |
| 744 | |
| 745 | /* |
| 746 | ** The arguments cType,zArg1,zArg2 define an object that can be searched |
| 747 | ** for. Return a URL (relative to the root of the Fossil project) that |
| 748 | ** will jump to that document. |
| 749 | ** |
| 750 | ** Space to hold the returned string is obtained from mprintf() and should |
| 751 | ** be freed by the caller using fossil_free() or the equivalent. |
| 752 | */ |
| 753 | char *search_url( |
| 754 | char cType, /* Type of document */ |
| 755 | const char *zArg1, /* First parameter */ |
| 756 | const char *zArg2 /* Second parameter */ |
| 757 | ){ |
| 758 | char *zUrl = 0; |
| 759 | switch( cType ){ |
| 760 | case 'd': { /* Doc. zArg1: RID of the file. zArg2: Filename */ |
| 761 | case 's': /* Source. zArg1: RID of the file. zArg2: Filename */ |
| 762 | zUrl = db_text(0, |
| 763 | "SELECT printf('/doc/%%s%%s', substr(blob.uuid,20), %Q)" |
| 764 | " FROM mlink, blob" |
| 765 | " WHERE mlink.fid=%d AND mlink.mid=blob.rid", |
| 766 | zArg2, atoi(zArg1)); |
| 767 | break; |
| 768 | } |
| 769 | case 'w': { /* Wiki. zArg1: RID of the page. zArg2: Page name */ |
| 770 | char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",atoi(zArg1)); |
| 771 | zUrl = mprintf("/wiki?id=%z&name=%t", zId, zArg2); |
| 772 | break; |
| 773 | } |
| 774 | case 'c': { /* Ckeckin: zArg1: RID of the checkin. zArg2: Not used */ |
| 775 | char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",atoi(zArg1)); |
| 776 | zUrl = mprintf("/info/%z", zId); |
| 777 | break; |
| 778 | } |
| 779 | } |
| 780 | return zUrl; |
| 781 | } |
| 782 | |
| 783 | /* |
| 784 | ** COMMAND: test-search-stext |
| 785 | ** |
| 786 | ** Usage: fossil test-search-stext TYPE ARG1 ARG2 |
| 787 | */ |
| 788 | void test_search_stext(void){ |
| 789 | Blob out; |
| 790 | char *zUrl; |
| 791 | db_find_and_open_repository(0,0); |
| 792 | if( g.argc!=5 ) usage("TYPE ARG1 ARG2"); |
| 793 | search_stext(g.argv[2][0], g.argv[3], g.argv[4], &out); |
| 794 | zUrl = search_url(g.argv[2][0], g.argv[3], g.argv[4]); |
| 795 | fossil_print("%s\n%z\n",blob_str(&out),zUrl); |
| 796 | blob_reset(&out); |
| 797 | } |
| 798 |
+62
-27
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -151,11 +151,11 @@ | ||
| 151 | 151 | "SELECT uid, login, cap, info, 1 FROM user" |
| 152 | 152 | " WHERE login IN ('anonymous','nobody','developer','reader') " |
| 153 | 153 | " UNION ALL " |
| 154 | 154 | "SELECT uid, login, cap, info, 2 FROM user" |
| 155 | 155 | " WHERE login NOT IN ('anonymous','nobody','developer','reader') " |
| 156 | - "ORDER BY 5, 2" | |
| 156 | + "ORDER BY 5, 2 COLLATE nocase" | |
| 157 | 157 | ); |
| 158 | 158 | while( db_step(&s)==SQLITE_ROW ){ |
| 159 | 159 | int iLevel = db_column_int(&s, 4); |
| 160 | 160 | const char *zCap = db_column_text(&s, 2); |
| 161 | 161 | const char *zLogin = db_column_text(&s, 1); |
| @@ -813,17 +813,13 @@ | ||
| 813 | 813 | @ <ul> |
| 814 | 814 | @ <li><p> |
| 815 | 815 | @ No login is required for user <span class="usertype">nobody</span>. The |
| 816 | 816 | @ capabilities of the <span class="usertype">nobody</span> user are |
| 817 | 817 | @ inherited by all users, regardless of whether or not they are logged in. |
| 818 | - @ To disable universal access to the repository, make sure no user named | |
| 819 | - @ <span class="usertype">nobody</span> exists or that the | |
| 818 | + @ To disable universal access to the repository, make sure that the | |
| 820 | 819 | @ <span class="usertype">nobody</span> user has no capabilities |
| 821 | 820 | @ enabled. The password for <span class="usertype">nobody</span> is ignored. |
| 822 | - @ To avoid problems with spiders overloading the server, it is recommended | |
| 823 | - @ that the <span class="capability">h</span> (Hyperlinks) capability be | |
| 824 | - @ turned off for the <span class="usertype">nobody</span> user. | |
| 825 | 821 | @ </p></li> |
| 826 | 822 | @ |
| 827 | 823 | @ <li><p> |
| 828 | 824 | @ Login is required for user <span class="usertype">anonymous</span> but the |
| 829 | 825 | @ password is displayed on the login screen beside the password entry box |
| @@ -995,11 +991,11 @@ | ||
| 995 | 991 | login_insert_csrf_secret(); |
| 996 | 992 | @ <hr /> |
| 997 | 993 | onoff_attribute("Redirect to HTTPS on the Login page", |
| 998 | 994 | "redirect-to-https", "redirhttps", 0, 0); |
| 999 | 995 | @ <p>When selected, force the use of HTTPS for the Login page. |
| 1000 | - @ <p>Details: When enabled, this option causes the $secureurl TH1 | |
| 996 | + @ <p>Details: When enabled, this option causes the $secureurl TH1 | |
| 1001 | 997 | @ variable is set to an "https:" variant of $baseurl. Otherwise, |
| 1002 | 998 | @ $secureurl is just an alias for $baseurl. Also when enabled, the |
| 1003 | 999 | @ Login page redirects to https if accessed via http. |
| 1004 | 1000 | @ <hr /> |
| 1005 | 1001 | onoff_attribute("Require password for local access", |
| @@ -1525,24 +1521,26 @@ | ||
| 1525 | 1521 | login_needed(); |
| 1526 | 1522 | } |
| 1527 | 1523 | db_begin_transaction(); |
| 1528 | 1524 | if( P("clear")!=0 ){ |
| 1529 | 1525 | db_multi_exec("DELETE FROM config WHERE name='css'"); |
| 1530 | - cgi_replace_parameter("css", zDefaultCSS); | |
| 1526 | + cgi_replace_parameter("css", builtin_text("skins/default/css.txt")); | |
| 1531 | 1527 | db_end_transaction(0); |
| 1532 | 1528 | cgi_redirect("setup_editcss"); |
| 1533 | 1529 | } |
| 1534 | 1530 | if( P("submit")!=0 ){ |
| 1535 | - textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS, 0); | |
| 1531 | + textarea_attribute(0, 0, 0, "css", "css", | |
| 1532 | + builtin_text("skins/default/css.txt"), 0); | |
| 1536 | 1533 | db_end_transaction(0); |
| 1537 | 1534 | cgi_redirect("setup_editcss"); |
| 1538 | 1535 | } |
| 1539 | 1536 | style_header("Edit CSS"); |
| 1540 | 1537 | @ <form action="%s(g.zTop)/setup_editcss" method="post"><div> |
| 1541 | 1538 | login_insert_csrf_secret(); |
| 1542 | 1539 | @ Edit the CSS below:<br /> |
| 1543 | - textarea_attribute("", 35, 80, "css", "css", zDefaultCSS, 0); | |
| 1540 | + textarea_attribute("", 35, 80, "css", "css", | |
| 1541 | + builtin_text("skins/default/css.txt"), 0); | |
| 1544 | 1542 | @ <br /> |
| 1545 | 1543 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1546 | 1544 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 1547 | 1545 | @ </div></form> |
| 1548 | 1546 | @ <p><span class="note">Note:</span> Press your browser Reload button after |
| @@ -1568,15 +1566,17 @@ | ||
| 1568 | 1566 | login_needed(); |
| 1569 | 1567 | } |
| 1570 | 1568 | db_begin_transaction(); |
| 1571 | 1569 | if( P("clear")!=0 ){ |
| 1572 | 1570 | db_multi_exec("DELETE FROM config WHERE name='header'"); |
| 1573 | - cgi_replace_parameter("header", zDefaultHeader); | |
| 1571 | + cgi_replace_parameter("header", builtin_text("skins/default/header.txt")); | |
| 1574 | 1572 | }else if( P("submit")!=0 ){ |
| 1575 | - textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader, 0); | |
| 1573 | + textarea_attribute(0, 0, 0, "header", "header", | |
| 1574 | + builtin_text("skins/default/header.txt"), 0); | |
| 1576 | 1575 | }else if( P("fixbase")!=0 ){ |
| 1577 | - const char *z = db_get("header", (char*)zDefaultHeader); | |
| 1576 | + const char *z = db_get("header", | |
| 1577 | + (char*)builtin_text("skins/default/header.txt")); | |
| 1578 | 1578 | char *zHead = strstr(z, "<head>"); |
| 1579 | 1579 | if( strstr(z, "<base href=")==0 && zHead!=0 ){ |
| 1580 | 1580 | char *zNew; |
| 1581 | 1581 | char *zTail = &zHead[6]; |
| 1582 | 1582 | while( fossil_isspace(zTail[0]) ) zTail++; |
| @@ -1598,14 +1598,15 @@ | ||
| 1598 | 1598 | @ <tt><head></tt> in the header! |
| 1599 | 1599 | @ <input type="submit" name="fixbase" value="Add <base> Now"></p> |
| 1600 | 1600 | } |
| 1601 | 1601 | |
| 1602 | 1602 | login_insert_csrf_secret(); |
| 1603 | - @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to | |
| 1603 | + @ <p>Edit HTML text with embedded TH1 (a Tcl dialect) that will be used to | |
| 1604 | 1604 | @ generate the beginning of every page through start of the main |
| 1605 | 1605 | @ menu.</p> |
| 1606 | - textarea_attribute("", 35, 80, "header", "header", zDefaultHeader, 0); | |
| 1606 | + textarea_attribute("", 35, 80, "header", "header", | |
| 1607 | + builtin_text("skins/default/header.txt"), 0); | |
| 1607 | 1608 | @ <br /> |
| 1608 | 1609 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1609 | 1610 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 1610 | 1611 | @ </div></form> |
| 1611 | 1612 | @ <hr /> |
| @@ -1612,11 +1613,11 @@ | ||
| 1612 | 1613 | @ The default header is shown below for reference. Other examples |
| 1613 | 1614 | @ of headers can be seen on the <a href="setup_skin">skins page</a>. |
| 1614 | 1615 | @ See also the <a href="setup_editcss">CSS</a> and |
| 1615 | 1616 | @ <a href="setup_footer">footer</a> editing screens. |
| 1616 | 1617 | @ <blockquote><pre> |
| 1617 | - @ %h(zDefaultHeader) | |
| 1618 | + @ %h(builtin_text("skins/default/header.txt")) | |
| 1618 | 1619 | @ </pre></blockquote> |
| 1619 | 1620 | style_footer(); |
| 1620 | 1621 | db_end_transaction(0); |
| 1621 | 1622 | } |
| 1622 | 1623 | |
| @@ -1629,19 +1630,20 @@ | ||
| 1629 | 1630 | login_needed(); |
| 1630 | 1631 | } |
| 1631 | 1632 | db_begin_transaction(); |
| 1632 | 1633 | if( P("clear")!=0 ){ |
| 1633 | 1634 | db_multi_exec("DELETE FROM config WHERE name='footer'"); |
| 1634 | - cgi_replace_parameter("footer", zDefaultFooter); | |
| 1635 | + cgi_replace_parameter("footer", builtin_text("skins/default/footer.txt")); | |
| 1635 | 1636 | } |
| 1636 | 1637 | |
| 1637 | 1638 | style_header("Edit Page Footer"); |
| 1638 | 1639 | @ <form action="%s(g.zTop)/setup_footer" method="post"><div> |
| 1639 | 1640 | login_insert_csrf_secret(); |
| 1640 | - @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to | |
| 1641 | + @ <p>Edit HTML text with embedded TH1 (a Tcl dialect) that will be used to | |
| 1641 | 1642 | @ generate the end of every page.</p> |
| 1642 | - textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter, 0); | |
| 1643 | + textarea_attribute("", 20, 80, "footer", "footer", | |
| 1644 | + builtin_text("skins/default/footer.txt"), 0); | |
| 1643 | 1645 | @ <br /> |
| 1644 | 1646 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1645 | 1647 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 1646 | 1648 | @ </div></form> |
| 1647 | 1649 | @ <hr /> |
| @@ -1648,11 +1650,11 @@ | ||
| 1648 | 1650 | @ The default footer is shown below for reference. Other examples |
| 1649 | 1651 | @ of footers can be seen on the <a href="setup_skin">skins page</a>. |
| 1650 | 1652 | @ See also the <a href="setup_editcss">CSS</a> and |
| 1651 | 1653 | @ <a href="setup_header">header</a> editing screens. |
| 1652 | 1654 | @ <blockquote><pre> |
| 1653 | - @ %h(zDefaultFooter) | |
| 1655 | + @ %h(builtin_text("skins/default/footer.txt")) | |
| 1654 | 1656 | @ </pre></blockquote> |
| 1655 | 1657 | style_footer(); |
| 1656 | 1658 | db_end_transaction(0); |
| 1657 | 1659 | } |
| 1658 | 1660 | |
| @@ -1715,13 +1717,15 @@ | ||
| 1715 | 1717 | } |
| 1716 | 1718 | |
| 1717 | 1719 | style_header("Edit Ad Unit"); |
| 1718 | 1720 | @ <form action="%s(g.zTop)/setup_adunit" method="post"><div> |
| 1719 | 1721 | login_insert_csrf_secret(); |
| 1720 | - @ <p>Edit HTML text for an ad unit that will be inserted after the | |
| 1721 | - @ menu bar and above the content of every page.</p> | |
| 1722 | - textarea_attribute("", 20, 80, "adunit", "adunit", "", 0); | |
| 1722 | + @ <b>Banner Ad-Unit:</b><br /> | |
| 1723 | + textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); | |
| 1724 | + @ <br /> | |
| 1725 | + @ <b>Right-Column Ad-Unit:</b><br /> | |
| 1726 | + textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); | |
| 1723 | 1727 | @ <br /> |
| 1724 | 1728 | onoff_attribute("Omit ads to administrator", |
| 1725 | 1729 | "adunit-omit-if-admin", "oia", 0, 0); |
| 1726 | 1730 | @ <br /> |
| 1727 | 1731 | onoff_attribute("Omit ads to logged-in users", |
| @@ -1728,10 +1732,41 @@ | ||
| 1728 | 1732 | "adunit-omit-if-user", "oiu", 0, 0); |
| 1729 | 1733 | @ <br /> |
| 1730 | 1734 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1731 | 1735 | @ <input type="submit" name="clear" value="Delete Ad-Unit" /> |
| 1732 | 1736 | @ </div></form> |
| 1737 | + @ <hr /> | |
| 1738 | + @ <b>Ad-Unit Notes:</b><ul> | |
| 1739 | + @ <li>Leave both Ad-Units blank to disable all advertising. | |
| 1740 | + @ <li>The "Banner Ad-Unit" is used for wide pages. | |
| 1741 | + @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. | |
| 1742 | + @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is used on all pages. | |
| 1743 | + @ <li>Suggested <a href="setup_editcss">CSS</a> changes: | |
| 1744 | + @ <blockquote><pre> | |
| 1745 | + @ div.adunit_banner { | |
| 1746 | + @ margin: auto; | |
| 1747 | + @ width: 100%; | |
| 1748 | + @ } | |
| 1749 | + @ div.adunit_right { | |
| 1750 | + @ float: right; | |
| 1751 | + @ } | |
| 1752 | + @ div.adunit_right_container { | |
| 1753 | + @ min-height: <i>height-of-right-column-ad-unit</i>; | |
| 1754 | + @ } | |
| 1755 | + @ </pre></blockquote> | |
| 1756 | + @ <li>For a place-holder Ad-Unit for testing, Copy/Paste the following | |
| 1757 | + @ with appropriate adjustments to "width:" and "height:". | |
| 1758 | + @ <blockquote><pre> | |
| 1759 | + @ <div style=' | |
| 1760 | + @ margin: 0 auto; | |
| 1761 | + @ width: 600px; | |
| 1762 | + @ height: 90px; | |
| 1763 | + @ border: 1px solid #f11; | |
| 1764 | + @ background-color: #fcc; | |
| 1765 | + @ '>Demo Ad</div> | |
| 1766 | + @ </pre></blockquote> | |
| 1767 | + @ </li> | |
| 1733 | 1768 | style_footer(); |
| 1734 | 1769 | db_end_transaction(0); |
| 1735 | 1770 | } |
| 1736 | 1771 | |
| 1737 | 1772 | /* |
| @@ -2103,14 +2138,14 @@ | ||
| 2103 | 2138 | @ <th>User</th> |
| 2104 | 2139 | @ <th>Page</th> |
| 2105 | 2140 | @ <th width="60%%">Message</th> |
| 2106 | 2141 | @ </thead><tbody> |
| 2107 | 2142 | while( SQLITE_ROW == db_step(&stLog) ){ |
| 2108 | - char const * zTime = db_column_text(&stLog, 0); | |
| 2109 | - char const * zUser = db_column_text(&stLog, 1); | |
| 2110 | - char const * zPage = db_column_text(&stLog, 2); | |
| 2111 | - char const * zMessage = db_column_text(&stLog, 3); | |
| 2143 | + const char *zTime = db_column_text(&stLog, 0); | |
| 2144 | + const char *zUser = db_column_text(&stLog, 1); | |
| 2145 | + const char *zPage = db_column_text(&stLog, 2); | |
| 2146 | + const char *zMessage = db_column_text(&stLog, 3); | |
| 2112 | 2147 | @ <tr class="row%d(counter++%2)"> |
| 2113 | 2148 | @ <td class="adminTime">%s(zTime)</td> |
| 2114 | 2149 | @ <td>%s(zUser)</td> |
| 2115 | 2150 | @ <td>%s(zPage)</td> |
| 2116 | 2151 | @ <td>%h(zMessage)</td> |
| 2117 | 2152 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -151,11 +151,11 @@ | |
| 151 | "SELECT uid, login, cap, info, 1 FROM user" |
| 152 | " WHERE login IN ('anonymous','nobody','developer','reader') " |
| 153 | " UNION ALL " |
| 154 | "SELECT uid, login, cap, info, 2 FROM user" |
| 155 | " WHERE login NOT IN ('anonymous','nobody','developer','reader') " |
| 156 | "ORDER BY 5, 2" |
| 157 | ); |
| 158 | while( db_step(&s)==SQLITE_ROW ){ |
| 159 | int iLevel = db_column_int(&s, 4); |
| 160 | const char *zCap = db_column_text(&s, 2); |
| 161 | const char *zLogin = db_column_text(&s, 1); |
| @@ -813,17 +813,13 @@ | |
| 813 | @ <ul> |
| 814 | @ <li><p> |
| 815 | @ No login is required for user <span class="usertype">nobody</span>. The |
| 816 | @ capabilities of the <span class="usertype">nobody</span> user are |
| 817 | @ inherited by all users, regardless of whether or not they are logged in. |
| 818 | @ To disable universal access to the repository, make sure no user named |
| 819 | @ <span class="usertype">nobody</span> exists or that the |
| 820 | @ <span class="usertype">nobody</span> user has no capabilities |
| 821 | @ enabled. The password for <span class="usertype">nobody</span> is ignored. |
| 822 | @ To avoid problems with spiders overloading the server, it is recommended |
| 823 | @ that the <span class="capability">h</span> (Hyperlinks) capability be |
| 824 | @ turned off for the <span class="usertype">nobody</span> user. |
| 825 | @ </p></li> |
| 826 | @ |
| 827 | @ <li><p> |
| 828 | @ Login is required for user <span class="usertype">anonymous</span> but the |
| 829 | @ password is displayed on the login screen beside the password entry box |
| @@ -995,11 +991,11 @@ | |
| 995 | login_insert_csrf_secret(); |
| 996 | @ <hr /> |
| 997 | onoff_attribute("Redirect to HTTPS on the Login page", |
| 998 | "redirect-to-https", "redirhttps", 0, 0); |
| 999 | @ <p>When selected, force the use of HTTPS for the Login page. |
| 1000 | @ <p>Details: When enabled, this option causes the $secureurl TH1 |
| 1001 | @ variable is set to an "https:" variant of $baseurl. Otherwise, |
| 1002 | @ $secureurl is just an alias for $baseurl. Also when enabled, the |
| 1003 | @ Login page redirects to https if accessed via http. |
| 1004 | @ <hr /> |
| 1005 | onoff_attribute("Require password for local access", |
| @@ -1525,24 +1521,26 @@ | |
| 1525 | login_needed(); |
| 1526 | } |
| 1527 | db_begin_transaction(); |
| 1528 | if( P("clear")!=0 ){ |
| 1529 | db_multi_exec("DELETE FROM config WHERE name='css'"); |
| 1530 | cgi_replace_parameter("css", zDefaultCSS); |
| 1531 | db_end_transaction(0); |
| 1532 | cgi_redirect("setup_editcss"); |
| 1533 | } |
| 1534 | if( P("submit")!=0 ){ |
| 1535 | textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS, 0); |
| 1536 | db_end_transaction(0); |
| 1537 | cgi_redirect("setup_editcss"); |
| 1538 | } |
| 1539 | style_header("Edit CSS"); |
| 1540 | @ <form action="%s(g.zTop)/setup_editcss" method="post"><div> |
| 1541 | login_insert_csrf_secret(); |
| 1542 | @ Edit the CSS below:<br /> |
| 1543 | textarea_attribute("", 35, 80, "css", "css", zDefaultCSS, 0); |
| 1544 | @ <br /> |
| 1545 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1546 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 1547 | @ </div></form> |
| 1548 | @ <p><span class="note">Note:</span> Press your browser Reload button after |
| @@ -1568,15 +1566,17 @@ | |
| 1568 | login_needed(); |
| 1569 | } |
| 1570 | db_begin_transaction(); |
| 1571 | if( P("clear")!=0 ){ |
| 1572 | db_multi_exec("DELETE FROM config WHERE name='header'"); |
| 1573 | cgi_replace_parameter("header", zDefaultHeader); |
| 1574 | }else if( P("submit")!=0 ){ |
| 1575 | textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader, 0); |
| 1576 | }else if( P("fixbase")!=0 ){ |
| 1577 | const char *z = db_get("header", (char*)zDefaultHeader); |
| 1578 | char *zHead = strstr(z, "<head>"); |
| 1579 | if( strstr(z, "<base href=")==0 && zHead!=0 ){ |
| 1580 | char *zNew; |
| 1581 | char *zTail = &zHead[6]; |
| 1582 | while( fossil_isspace(zTail[0]) ) zTail++; |
| @@ -1598,14 +1598,15 @@ | |
| 1598 | @ <tt><head></tt> in the header! |
| 1599 | @ <input type="submit" name="fixbase" value="Add <base> Now"></p> |
| 1600 | } |
| 1601 | |
| 1602 | login_insert_csrf_secret(); |
| 1603 | @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to |
| 1604 | @ generate the beginning of every page through start of the main |
| 1605 | @ menu.</p> |
| 1606 | textarea_attribute("", 35, 80, "header", "header", zDefaultHeader, 0); |
| 1607 | @ <br /> |
| 1608 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1609 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 1610 | @ </div></form> |
| 1611 | @ <hr /> |
| @@ -1612,11 +1613,11 @@ | |
| 1612 | @ The default header is shown below for reference. Other examples |
| 1613 | @ of headers can be seen on the <a href="setup_skin">skins page</a>. |
| 1614 | @ See also the <a href="setup_editcss">CSS</a> and |
| 1615 | @ <a href="setup_footer">footer</a> editing screens. |
| 1616 | @ <blockquote><pre> |
| 1617 | @ %h(zDefaultHeader) |
| 1618 | @ </pre></blockquote> |
| 1619 | style_footer(); |
| 1620 | db_end_transaction(0); |
| 1621 | } |
| 1622 | |
| @@ -1629,19 +1630,20 @@ | |
| 1629 | login_needed(); |
| 1630 | } |
| 1631 | db_begin_transaction(); |
| 1632 | if( P("clear")!=0 ){ |
| 1633 | db_multi_exec("DELETE FROM config WHERE name='footer'"); |
| 1634 | cgi_replace_parameter("footer", zDefaultFooter); |
| 1635 | } |
| 1636 | |
| 1637 | style_header("Edit Page Footer"); |
| 1638 | @ <form action="%s(g.zTop)/setup_footer" method="post"><div> |
| 1639 | login_insert_csrf_secret(); |
| 1640 | @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to |
| 1641 | @ generate the end of every page.</p> |
| 1642 | textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter, 0); |
| 1643 | @ <br /> |
| 1644 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1645 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 1646 | @ </div></form> |
| 1647 | @ <hr /> |
| @@ -1648,11 +1650,11 @@ | |
| 1648 | @ The default footer is shown below for reference. Other examples |
| 1649 | @ of footers can be seen on the <a href="setup_skin">skins page</a>. |
| 1650 | @ See also the <a href="setup_editcss">CSS</a> and |
| 1651 | @ <a href="setup_header">header</a> editing screens. |
| 1652 | @ <blockquote><pre> |
| 1653 | @ %h(zDefaultFooter) |
| 1654 | @ </pre></blockquote> |
| 1655 | style_footer(); |
| 1656 | db_end_transaction(0); |
| 1657 | } |
| 1658 | |
| @@ -1715,13 +1717,15 @@ | |
| 1715 | } |
| 1716 | |
| 1717 | style_header("Edit Ad Unit"); |
| 1718 | @ <form action="%s(g.zTop)/setup_adunit" method="post"><div> |
| 1719 | login_insert_csrf_secret(); |
| 1720 | @ <p>Edit HTML text for an ad unit that will be inserted after the |
| 1721 | @ menu bar and above the content of every page.</p> |
| 1722 | textarea_attribute("", 20, 80, "adunit", "adunit", "", 0); |
| 1723 | @ <br /> |
| 1724 | onoff_attribute("Omit ads to administrator", |
| 1725 | "adunit-omit-if-admin", "oia", 0, 0); |
| 1726 | @ <br /> |
| 1727 | onoff_attribute("Omit ads to logged-in users", |
| @@ -1728,10 +1732,41 @@ | |
| 1728 | "adunit-omit-if-user", "oiu", 0, 0); |
| 1729 | @ <br /> |
| 1730 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1731 | @ <input type="submit" name="clear" value="Delete Ad-Unit" /> |
| 1732 | @ </div></form> |
| 1733 | style_footer(); |
| 1734 | db_end_transaction(0); |
| 1735 | } |
| 1736 | |
| 1737 | /* |
| @@ -2103,14 +2138,14 @@ | |
| 2103 | @ <th>User</th> |
| 2104 | @ <th>Page</th> |
| 2105 | @ <th width="60%%">Message</th> |
| 2106 | @ </thead><tbody> |
| 2107 | while( SQLITE_ROW == db_step(&stLog) ){ |
| 2108 | char const * zTime = db_column_text(&stLog, 0); |
| 2109 | char const * zUser = db_column_text(&stLog, 1); |
| 2110 | char const * zPage = db_column_text(&stLog, 2); |
| 2111 | char const * zMessage = db_column_text(&stLog, 3); |
| 2112 | @ <tr class="row%d(counter++%2)"> |
| 2113 | @ <td class="adminTime">%s(zTime)</td> |
| 2114 | @ <td>%s(zUser)</td> |
| 2115 | @ <td>%s(zPage)</td> |
| 2116 | @ <td>%h(zMessage)</td> |
| 2117 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -151,11 +151,11 @@ | |
| 151 | "SELECT uid, login, cap, info, 1 FROM user" |
| 152 | " WHERE login IN ('anonymous','nobody','developer','reader') " |
| 153 | " UNION ALL " |
| 154 | "SELECT uid, login, cap, info, 2 FROM user" |
| 155 | " WHERE login NOT IN ('anonymous','nobody','developer','reader') " |
| 156 | "ORDER BY 5, 2 COLLATE nocase" |
| 157 | ); |
| 158 | while( db_step(&s)==SQLITE_ROW ){ |
| 159 | int iLevel = db_column_int(&s, 4); |
| 160 | const char *zCap = db_column_text(&s, 2); |
| 161 | const char *zLogin = db_column_text(&s, 1); |
| @@ -813,17 +813,13 @@ | |
| 813 | @ <ul> |
| 814 | @ <li><p> |
| 815 | @ No login is required for user <span class="usertype">nobody</span>. The |
| 816 | @ capabilities of the <span class="usertype">nobody</span> user are |
| 817 | @ inherited by all users, regardless of whether or not they are logged in. |
| 818 | @ To disable universal access to the repository, make sure that the |
| 819 | @ <span class="usertype">nobody</span> user has no capabilities |
| 820 | @ enabled. The password for <span class="usertype">nobody</span> is ignored. |
| 821 | @ </p></li> |
| 822 | @ |
| 823 | @ <li><p> |
| 824 | @ Login is required for user <span class="usertype">anonymous</span> but the |
| 825 | @ password is displayed on the login screen beside the password entry box |
| @@ -995,11 +991,11 @@ | |
| 991 | login_insert_csrf_secret(); |
| 992 | @ <hr /> |
| 993 | onoff_attribute("Redirect to HTTPS on the Login page", |
| 994 | "redirect-to-https", "redirhttps", 0, 0); |
| 995 | @ <p>When selected, force the use of HTTPS for the Login page. |
| 996 | @ <p>Details: When enabled, this option causes the $secureurl TH1 |
| 997 | @ variable is set to an "https:" variant of $baseurl. Otherwise, |
| 998 | @ $secureurl is just an alias for $baseurl. Also when enabled, the |
| 999 | @ Login page redirects to https if accessed via http. |
| 1000 | @ <hr /> |
| 1001 | onoff_attribute("Require password for local access", |
| @@ -1525,24 +1521,26 @@ | |
| 1521 | login_needed(); |
| 1522 | } |
| 1523 | db_begin_transaction(); |
| 1524 | if( P("clear")!=0 ){ |
| 1525 | db_multi_exec("DELETE FROM config WHERE name='css'"); |
| 1526 | cgi_replace_parameter("css", builtin_text("skins/default/css.txt")); |
| 1527 | db_end_transaction(0); |
| 1528 | cgi_redirect("setup_editcss"); |
| 1529 | } |
| 1530 | if( P("submit")!=0 ){ |
| 1531 | textarea_attribute(0, 0, 0, "css", "css", |
| 1532 | builtin_text("skins/default/css.txt"), 0); |
| 1533 | db_end_transaction(0); |
| 1534 | cgi_redirect("setup_editcss"); |
| 1535 | } |
| 1536 | style_header("Edit CSS"); |
| 1537 | @ <form action="%s(g.zTop)/setup_editcss" method="post"><div> |
| 1538 | login_insert_csrf_secret(); |
| 1539 | @ Edit the CSS below:<br /> |
| 1540 | textarea_attribute("", 35, 80, "css", "css", |
| 1541 | builtin_text("skins/default/css.txt"), 0); |
| 1542 | @ <br /> |
| 1543 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1544 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 1545 | @ </div></form> |
| 1546 | @ <p><span class="note">Note:</span> Press your browser Reload button after |
| @@ -1568,15 +1566,17 @@ | |
| 1566 | login_needed(); |
| 1567 | } |
| 1568 | db_begin_transaction(); |
| 1569 | if( P("clear")!=0 ){ |
| 1570 | db_multi_exec("DELETE FROM config WHERE name='header'"); |
| 1571 | cgi_replace_parameter("header", builtin_text("skins/default/header.txt")); |
| 1572 | }else if( P("submit")!=0 ){ |
| 1573 | textarea_attribute(0, 0, 0, "header", "header", |
| 1574 | builtin_text("skins/default/header.txt"), 0); |
| 1575 | }else if( P("fixbase")!=0 ){ |
| 1576 | const char *z = db_get("header", |
| 1577 | (char*)builtin_text("skins/default/header.txt")); |
| 1578 | char *zHead = strstr(z, "<head>"); |
| 1579 | if( strstr(z, "<base href=")==0 && zHead!=0 ){ |
| 1580 | char *zNew; |
| 1581 | char *zTail = &zHead[6]; |
| 1582 | while( fossil_isspace(zTail[0]) ) zTail++; |
| @@ -1598,14 +1598,15 @@ | |
| 1598 | @ <tt><head></tt> in the header! |
| 1599 | @ <input type="submit" name="fixbase" value="Add <base> Now"></p> |
| 1600 | } |
| 1601 | |
| 1602 | login_insert_csrf_secret(); |
| 1603 | @ <p>Edit HTML text with embedded TH1 (a Tcl dialect) that will be used to |
| 1604 | @ generate the beginning of every page through start of the main |
| 1605 | @ menu.</p> |
| 1606 | textarea_attribute("", 35, 80, "header", "header", |
| 1607 | builtin_text("skins/default/header.txt"), 0); |
| 1608 | @ <br /> |
| 1609 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1610 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 1611 | @ </div></form> |
| 1612 | @ <hr /> |
| @@ -1612,11 +1613,11 @@ | |
| 1613 | @ The default header is shown below for reference. Other examples |
| 1614 | @ of headers can be seen on the <a href="setup_skin">skins page</a>. |
| 1615 | @ See also the <a href="setup_editcss">CSS</a> and |
| 1616 | @ <a href="setup_footer">footer</a> editing screens. |
| 1617 | @ <blockquote><pre> |
| 1618 | @ %h(builtin_text("skins/default/header.txt")) |
| 1619 | @ </pre></blockquote> |
| 1620 | style_footer(); |
| 1621 | db_end_transaction(0); |
| 1622 | } |
| 1623 | |
| @@ -1629,19 +1630,20 @@ | |
| 1630 | login_needed(); |
| 1631 | } |
| 1632 | db_begin_transaction(); |
| 1633 | if( P("clear")!=0 ){ |
| 1634 | db_multi_exec("DELETE FROM config WHERE name='footer'"); |
| 1635 | cgi_replace_parameter("footer", builtin_text("skins/default/footer.txt")); |
| 1636 | } |
| 1637 | |
| 1638 | style_header("Edit Page Footer"); |
| 1639 | @ <form action="%s(g.zTop)/setup_footer" method="post"><div> |
| 1640 | login_insert_csrf_secret(); |
| 1641 | @ <p>Edit HTML text with embedded TH1 (a Tcl dialect) that will be used to |
| 1642 | @ generate the end of every page.</p> |
| 1643 | textarea_attribute("", 20, 80, "footer", "footer", |
| 1644 | builtin_text("skins/default/footer.txt"), 0); |
| 1645 | @ <br /> |
| 1646 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1647 | @ <input type="submit" name="clear" value="Revert To Default" /> |
| 1648 | @ </div></form> |
| 1649 | @ <hr /> |
| @@ -1648,11 +1650,11 @@ | |
| 1650 | @ The default footer is shown below for reference. Other examples |
| 1651 | @ of footers can be seen on the <a href="setup_skin">skins page</a>. |
| 1652 | @ See also the <a href="setup_editcss">CSS</a> and |
| 1653 | @ <a href="setup_header">header</a> editing screens. |
| 1654 | @ <blockquote><pre> |
| 1655 | @ %h(builtin_text("skins/default/footer.txt")) |
| 1656 | @ </pre></blockquote> |
| 1657 | style_footer(); |
| 1658 | db_end_transaction(0); |
| 1659 | } |
| 1660 | |
| @@ -1715,13 +1717,15 @@ | |
| 1717 | } |
| 1718 | |
| 1719 | style_header("Edit Ad Unit"); |
| 1720 | @ <form action="%s(g.zTop)/setup_adunit" method="post"><div> |
| 1721 | login_insert_csrf_secret(); |
| 1722 | @ <b>Banner Ad-Unit:</b><br /> |
| 1723 | textarea_attribute("", 6, 80, "adunit", "adunit", "", 0); |
| 1724 | @ <br /> |
| 1725 | @ <b>Right-Column Ad-Unit:</b><br /> |
| 1726 | textarea_attribute("", 6, 80, "adunit-right", "adright", "", 0); |
| 1727 | @ <br /> |
| 1728 | onoff_attribute("Omit ads to administrator", |
| 1729 | "adunit-omit-if-admin", "oia", 0, 0); |
| 1730 | @ <br /> |
| 1731 | onoff_attribute("Omit ads to logged-in users", |
| @@ -1728,10 +1732,41 @@ | |
| 1732 | "adunit-omit-if-user", "oiu", 0, 0); |
| 1733 | @ <br /> |
| 1734 | @ <input type="submit" name="submit" value="Apply Changes" /> |
| 1735 | @ <input type="submit" name="clear" value="Delete Ad-Unit" /> |
| 1736 | @ </div></form> |
| 1737 | @ <hr /> |
| 1738 | @ <b>Ad-Unit Notes:</b><ul> |
| 1739 | @ <li>Leave both Ad-Units blank to disable all advertising. |
| 1740 | @ <li>The "Banner Ad-Unit" is used for wide pages. |
| 1741 | @ <li>The "Right-Column Ad-Unit" is used on pages with tall, narrow content. |
| 1742 | @ <li>If the "Right-Column Ad-Unit" is blank, the "Banner Ad-Unit" is used on all pages. |
| 1743 | @ <li>Suggested <a href="setup_editcss">CSS</a> changes: |
| 1744 | @ <blockquote><pre> |
| 1745 | @ div.adunit_banner { |
| 1746 | @ margin: auto; |
| 1747 | @ width: 100%; |
| 1748 | @ } |
| 1749 | @ div.adunit_right { |
| 1750 | @ float: right; |
| 1751 | @ } |
| 1752 | @ div.adunit_right_container { |
| 1753 | @ min-height: <i>height-of-right-column-ad-unit</i>; |
| 1754 | @ } |
| 1755 | @ </pre></blockquote> |
| 1756 | @ <li>For a place-holder Ad-Unit for testing, Copy/Paste the following |
| 1757 | @ with appropriate adjustments to "width:" and "height:". |
| 1758 | @ <blockquote><pre> |
| 1759 | @ <div style=' |
| 1760 | @ margin: 0 auto; |
| 1761 | @ width: 600px; |
| 1762 | @ height: 90px; |
| 1763 | @ border: 1px solid #f11; |
| 1764 | @ background-color: #fcc; |
| 1765 | @ '>Demo Ad</div> |
| 1766 | @ </pre></blockquote> |
| 1767 | @ </li> |
| 1768 | style_footer(); |
| 1769 | db_end_transaction(0); |
| 1770 | } |
| 1771 | |
| 1772 | /* |
| @@ -2103,14 +2138,14 @@ | |
| 2138 | @ <th>User</th> |
| 2139 | @ <th>Page</th> |
| 2140 | @ <th width="60%%">Message</th> |
| 2141 | @ </thead><tbody> |
| 2142 | while( SQLITE_ROW == db_step(&stLog) ){ |
| 2143 | const char *zTime = db_column_text(&stLog, 0); |
| 2144 | const char *zUser = db_column_text(&stLog, 1); |
| 2145 | const char *zPage = db_column_text(&stLog, 2); |
| 2146 | const char *zMessage = db_column_text(&stLog, 3); |
| 2147 | @ <tr class="row%d(counter++%2)"> |
| 2148 | @ <td class="adminTime">%s(zTime)</td> |
| 2149 | @ <td>%s(zUser)</td> |
| 2150 | @ <td>%s(zPage)</td> |
| 2151 | @ <td>%h(zMessage)</td> |
| 2152 |
+7
-7
| --- src/sha1.c | ||
| +++ src/sha1.c | ||
| @@ -64,11 +64,11 @@ | ||
| 64 | 64 | ^block[(i+2)&15]^block[i&15],1)) |
| 65 | 65 | |
| 66 | 66 | /* |
| 67 | 67 | * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 |
| 68 | 68 | * |
| 69 | - * Rl0() for little-endian and Rb0() for big-endian. Endianness is | |
| 69 | + * Rl0() for little-endian and Rb0() for big-endian. Endianness is | |
| 70 | 70 | * determined at run-time. |
| 71 | 71 | */ |
| 72 | 72 | #define Rl0(v,w,x,y,z,i) \ |
| 73 | 73 | z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); |
| 74 | 74 | #define Rb0(v,w,x,y,z,i) \ |
| @@ -217,11 +217,11 @@ | ||
| 217 | 217 | ** "unsigned char digest[20]" in the calling function. The SHA1 |
| 218 | 218 | ** digest is stored in the first 20 bytes. zBuf should |
| 219 | 219 | ** be "char zBuf[41]". |
| 220 | 220 | */ |
| 221 | 221 | static void DigestToBase16(unsigned char *digest, char *zBuf){ |
| 222 | - static char const zEncode[] = "0123456789abcdef"; | |
| 222 | + static const char zEncode[] = "0123456789abcdef"; | |
| 223 | 223 | int ix; |
| 224 | 224 | |
| 225 | 225 | for(ix=0; ix<20; ix++){ |
| 226 | 226 | *zBuf++ = zEncode[(*digest>>4)&0xf]; |
| 227 | 227 | *zBuf++ = zEncode[*digest++ & 0xf]; |
| @@ -258,11 +258,11 @@ | ||
| 258 | 258 | sha1sum_step_text(blob_buffer(p), blob_size(p)); |
| 259 | 259 | } |
| 260 | 260 | |
| 261 | 261 | /* |
| 262 | 262 | ** Finish the incremental SHA1 checksum. Store the result in blob pOut |
| 263 | -** if pOut!=0. Also return a pointer to the result. | |
| 263 | +** if pOut!=0. Also return a pointer to the result. | |
| 264 | 264 | ** |
| 265 | 265 | ** This resets the incremental checksum preparing for the next round |
| 266 | 266 | ** of computation. The return pointer points to a static buffer that |
| 267 | 267 | ** is overwritten by subsequent calls to this function. |
| 268 | 268 | */ |
| @@ -295,11 +295,11 @@ | ||
| 295 | 295 | |
| 296 | 296 | if( file_wd_islink(zFilename) ){ |
| 297 | 297 | /* Instead of file content, return sha1 of link destination path */ |
| 298 | 298 | Blob destinationPath; |
| 299 | 299 | int rc; |
| 300 | - | |
| 300 | + | |
| 301 | 301 | blob_read_link(&destinationPath, zFilename); |
| 302 | 302 | rc = sha1sum_blob(&destinationPath, pCksum); |
| 303 | 303 | blob_reset(&destinationPath); |
| 304 | 304 | return rc; |
| 305 | 305 | } |
| @@ -363,11 +363,11 @@ | ||
| 363 | 363 | return mprintf("%s", zDigest); |
| 364 | 364 | } |
| 365 | 365 | |
| 366 | 366 | /* |
| 367 | 367 | ** Convert a cleartext password for a specific user into a SHA1 hash. |
| 368 | -** | |
| 368 | +** | |
| 369 | 369 | ** The algorithm here is: |
| 370 | 370 | ** |
| 371 | 371 | ** SHA1( project-code + "/" + login + "/" + password ) |
| 372 | 372 | ** |
| 373 | 373 | ** In words: The users login name and password are appended to the |
| @@ -375,11 +375,11 @@ | ||
| 375 | 375 | ** |
| 376 | 376 | ** The result of this function is the shared secret used by a client |
| 377 | 377 | ** to authenticate to a server for the sync protocol. It is also the |
| 378 | 378 | ** value stored in the USER.PW field of the database. By mixing in the |
| 379 | 379 | ** login name and the project id with the hash, different shared secrets |
| 380 | -** are obtained even if two users select the same password, or if a | |
| 380 | +** are obtained even if two users select the same password, or if a | |
| 381 | 381 | ** single user selects the same password for multiple projects. |
| 382 | 382 | */ |
| 383 | 383 | char *sha1_shared_secret( |
| 384 | 384 | const char *zPw, /* The password to encrypt */ |
| 385 | 385 | const char *zLogin, /* Username */ |
| @@ -457,11 +457,11 @@ | ||
| 457 | 457 | */ |
| 458 | 458 | void sha1sum_test(void){ |
| 459 | 459 | int i; |
| 460 | 460 | Blob in; |
| 461 | 461 | Blob cksum; |
| 462 | - | |
| 462 | + | |
| 463 | 463 | for(i=2; i<g.argc; i++){ |
| 464 | 464 | blob_init(&cksum, "************** not found ***************", -1); |
| 465 | 465 | if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ |
| 466 | 466 | blob_read_from_channel(&in, stdin, -1); |
| 467 | 467 | sha1sum_blob(&in, &cksum); |
| 468 | 468 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -64,11 +64,11 @@ | |
| 64 | ^block[(i+2)&15]^block[i&15],1)) |
| 65 | |
| 66 | /* |
| 67 | * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 |
| 68 | * |
| 69 | * Rl0() for little-endian and Rb0() for big-endian. Endianness is |
| 70 | * determined at run-time. |
| 71 | */ |
| 72 | #define Rl0(v,w,x,y,z,i) \ |
| 73 | z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); |
| 74 | #define Rb0(v,w,x,y,z,i) \ |
| @@ -217,11 +217,11 @@ | |
| 217 | ** "unsigned char digest[20]" in the calling function. The SHA1 |
| 218 | ** digest is stored in the first 20 bytes. zBuf should |
| 219 | ** be "char zBuf[41]". |
| 220 | */ |
| 221 | static void DigestToBase16(unsigned char *digest, char *zBuf){ |
| 222 | static char const zEncode[] = "0123456789abcdef"; |
| 223 | int ix; |
| 224 | |
| 225 | for(ix=0; ix<20; ix++){ |
| 226 | *zBuf++ = zEncode[(*digest>>4)&0xf]; |
| 227 | *zBuf++ = zEncode[*digest++ & 0xf]; |
| @@ -258,11 +258,11 @@ | |
| 258 | sha1sum_step_text(blob_buffer(p), blob_size(p)); |
| 259 | } |
| 260 | |
| 261 | /* |
| 262 | ** Finish the incremental SHA1 checksum. Store the result in blob pOut |
| 263 | ** if pOut!=0. Also return a pointer to the result. |
| 264 | ** |
| 265 | ** This resets the incremental checksum preparing for the next round |
| 266 | ** of computation. The return pointer points to a static buffer that |
| 267 | ** is overwritten by subsequent calls to this function. |
| 268 | */ |
| @@ -295,11 +295,11 @@ | |
| 295 | |
| 296 | if( file_wd_islink(zFilename) ){ |
| 297 | /* Instead of file content, return sha1 of link destination path */ |
| 298 | Blob destinationPath; |
| 299 | int rc; |
| 300 | |
| 301 | blob_read_link(&destinationPath, zFilename); |
| 302 | rc = sha1sum_blob(&destinationPath, pCksum); |
| 303 | blob_reset(&destinationPath); |
| 304 | return rc; |
| 305 | } |
| @@ -363,11 +363,11 @@ | |
| 363 | return mprintf("%s", zDigest); |
| 364 | } |
| 365 | |
| 366 | /* |
| 367 | ** Convert a cleartext password for a specific user into a SHA1 hash. |
| 368 | ** |
| 369 | ** The algorithm here is: |
| 370 | ** |
| 371 | ** SHA1( project-code + "/" + login + "/" + password ) |
| 372 | ** |
| 373 | ** In words: The users login name and password are appended to the |
| @@ -375,11 +375,11 @@ | |
| 375 | ** |
| 376 | ** The result of this function is the shared secret used by a client |
| 377 | ** to authenticate to a server for the sync protocol. It is also the |
| 378 | ** value stored in the USER.PW field of the database. By mixing in the |
| 379 | ** login name and the project id with the hash, different shared secrets |
| 380 | ** are obtained even if two users select the same password, or if a |
| 381 | ** single user selects the same password for multiple projects. |
| 382 | */ |
| 383 | char *sha1_shared_secret( |
| 384 | const char *zPw, /* The password to encrypt */ |
| 385 | const char *zLogin, /* Username */ |
| @@ -457,11 +457,11 @@ | |
| 457 | */ |
| 458 | void sha1sum_test(void){ |
| 459 | int i; |
| 460 | Blob in; |
| 461 | Blob cksum; |
| 462 | |
| 463 | for(i=2; i<g.argc; i++){ |
| 464 | blob_init(&cksum, "************** not found ***************", -1); |
| 465 | if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ |
| 466 | blob_read_from_channel(&in, stdin, -1); |
| 467 | sha1sum_blob(&in, &cksum); |
| 468 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -64,11 +64,11 @@ | |
| 64 | ^block[(i+2)&15]^block[i&15],1)) |
| 65 | |
| 66 | /* |
| 67 | * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 |
| 68 | * |
| 69 | * Rl0() for little-endian and Rb0() for big-endian. Endianness is |
| 70 | * determined at run-time. |
| 71 | */ |
| 72 | #define Rl0(v,w,x,y,z,i) \ |
| 73 | z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2); |
| 74 | #define Rb0(v,w,x,y,z,i) \ |
| @@ -217,11 +217,11 @@ | |
| 217 | ** "unsigned char digest[20]" in the calling function. The SHA1 |
| 218 | ** digest is stored in the first 20 bytes. zBuf should |
| 219 | ** be "char zBuf[41]". |
| 220 | */ |
| 221 | static void DigestToBase16(unsigned char *digest, char *zBuf){ |
| 222 | static const char zEncode[] = "0123456789abcdef"; |
| 223 | int ix; |
| 224 | |
| 225 | for(ix=0; ix<20; ix++){ |
| 226 | *zBuf++ = zEncode[(*digest>>4)&0xf]; |
| 227 | *zBuf++ = zEncode[*digest++ & 0xf]; |
| @@ -258,11 +258,11 @@ | |
| 258 | sha1sum_step_text(blob_buffer(p), blob_size(p)); |
| 259 | } |
| 260 | |
| 261 | /* |
| 262 | ** Finish the incremental SHA1 checksum. Store the result in blob pOut |
| 263 | ** if pOut!=0. Also return a pointer to the result. |
| 264 | ** |
| 265 | ** This resets the incremental checksum preparing for the next round |
| 266 | ** of computation. The return pointer points to a static buffer that |
| 267 | ** is overwritten by subsequent calls to this function. |
| 268 | */ |
| @@ -295,11 +295,11 @@ | |
| 295 | |
| 296 | if( file_wd_islink(zFilename) ){ |
| 297 | /* Instead of file content, return sha1 of link destination path */ |
| 298 | Blob destinationPath; |
| 299 | int rc; |
| 300 | |
| 301 | blob_read_link(&destinationPath, zFilename); |
| 302 | rc = sha1sum_blob(&destinationPath, pCksum); |
| 303 | blob_reset(&destinationPath); |
| 304 | return rc; |
| 305 | } |
| @@ -363,11 +363,11 @@ | |
| 363 | return mprintf("%s", zDigest); |
| 364 | } |
| 365 | |
| 366 | /* |
| 367 | ** Convert a cleartext password for a specific user into a SHA1 hash. |
| 368 | ** |
| 369 | ** The algorithm here is: |
| 370 | ** |
| 371 | ** SHA1( project-code + "/" + login + "/" + password ) |
| 372 | ** |
| 373 | ** In words: The users login name and password are appended to the |
| @@ -375,11 +375,11 @@ | |
| 375 | ** |
| 376 | ** The result of this function is the shared secret used by a client |
| 377 | ** to authenticate to a server for the sync protocol. It is also the |
| 378 | ** value stored in the USER.PW field of the database. By mixing in the |
| 379 | ** login name and the project id with the hash, different shared secrets |
| 380 | ** are obtained even if two users select the same password, or if a |
| 381 | ** single user selects the same password for multiple projects. |
| 382 | */ |
| 383 | char *sha1_shared_secret( |
| 384 | const char *zPw, /* The password to encrypt */ |
| 385 | const char *zLogin, /* Username */ |
| @@ -457,11 +457,11 @@ | |
| 457 | */ |
| 458 | void sha1sum_test(void){ |
| 459 | int i; |
| 460 | Blob in; |
| 461 | Blob cksum; |
| 462 | |
| 463 | for(i=2; i<g.argc; i++){ |
| 464 | blob_init(&cksum, "************** not found ***************", -1); |
| 465 | if( g.argv[i][0]=='-' && g.argv[i][1]==0 ){ |
| 466 | blob_read_from_channel(&in, stdin, -1); |
| 467 | sha1sum_blob(&in, &cksum); |
| 468 |
+300
-139
| --- src/shell.c | ||
| +++ src/shell.c | ||
| @@ -15,10 +15,17 @@ | ||
| 15 | 15 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 16 | 16 | /* This needs to come before any includes for MSVC compiler */ |
| 17 | 17 | #define _CRT_SECURE_NO_WARNINGS |
| 18 | 18 | #endif |
| 19 | 19 | |
| 20 | +/* | |
| 21 | +** If requested, include the SQLite compiler options file for MSVC. | |
| 22 | +*/ | |
| 23 | +#if defined(INCLUDE_MSVC_H) | |
| 24 | +#include "msvc.h" | |
| 25 | +#endif | |
| 26 | + | |
| 20 | 27 | /* |
| 21 | 28 | ** Enable large-file support for fopen() and friends on unix. |
| 22 | 29 | */ |
| 23 | 30 | #ifndef SQLITE_DISABLE_LFS |
| 24 | 31 | # define _LARGE_FILE 1 |
| @@ -46,21 +53,20 @@ | ||
| 46 | 53 | # endif |
| 47 | 54 | # include <unistd.h> |
| 48 | 55 | # include <sys/types.h> |
| 49 | 56 | #endif |
| 50 | 57 | |
| 51 | -#if defined(HAVE_READLINE) && HAVE_READLINE!=0 | |
| 58 | +#if HAVE_READLINE | |
| 52 | 59 | # include <readline/readline.h> |
| 53 | 60 | # include <readline/history.h> |
| 54 | -#else | |
| 55 | -# undef HAVE_READLINE | |
| 56 | 61 | #endif |
| 57 | -#if defined(HAVE_EDITLINE) && !defined(HAVE_READLINE) | |
| 62 | +#if HAVE_EDITLINE | |
| 63 | +# undef HAVE_READLINE | |
| 58 | 64 | # define HAVE_READLINE 1 |
| 59 | 65 | # include <editline/readline.h> |
| 60 | 66 | #endif |
| 61 | -#if !defined(HAVE_READLINE) | |
| 67 | +#if !HAVE_READLINE | |
| 62 | 68 | # define add_history(X) |
| 63 | 69 | # define read_history(X) |
| 64 | 70 | # define write_history(X) |
| 65 | 71 | # define stifle_history(X) |
| 66 | 72 | #endif |
| @@ -97,10 +103,30 @@ | ||
| 97 | 103 | |
| 98 | 104 | /* ctype macros that work with signed characters */ |
| 99 | 105 | #define IsSpace(X) isspace((unsigned char)X) |
| 100 | 106 | #define IsDigit(X) isdigit((unsigned char)X) |
| 101 | 107 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 108 | + | |
| 109 | +/* On Windows, we normally run with output mode of TEXT so that \n characters | |
| 110 | +** are automatically translated into \r\n. However, this behavior needs | |
| 111 | +** to be disabled in some cases (ex: when generating CSV output and when | |
| 112 | +** rendering quoted strings that contain \n characters). The following | |
| 113 | +** routines take care of that. | |
| 114 | +*/ | |
| 115 | +#if defined(_WIN32) || defined(WIN32) | |
| 116 | +static void setBinaryMode(FILE *out){ | |
| 117 | + fflush(out); | |
| 118 | + _setmode(_fileno(out), _O_BINARY); | |
| 119 | +} | |
| 120 | +static void setTextMode(FILE *out){ | |
| 121 | + fflush(out); | |
| 122 | + _setmode(_fileno(out), _O_TEXT); | |
| 123 | +} | |
| 124 | +#else | |
| 125 | +# define setBinaryMode(X) | |
| 126 | +# define setTextMode(X) | |
| 127 | +#endif | |
| 102 | 128 | |
| 103 | 129 | |
| 104 | 130 | /* True if the timer is enabled */ |
| 105 | 131 | static int enableTimer = 0; |
| 106 | 132 | |
| @@ -423,11 +449,11 @@ | ||
| 423 | 449 | char *zResult; |
| 424 | 450 | if( in!=0 ){ |
| 425 | 451 | zResult = local_getline(zPrior, in); |
| 426 | 452 | }else{ |
| 427 | 453 | zPrompt = isContinuation ? continuePrompt : mainPrompt; |
| 428 | -#if defined(HAVE_READLINE) | |
| 454 | +#if HAVE_READLINE | |
| 429 | 455 | free(zPrior); |
| 430 | 456 | zResult = readline(zPrompt); |
| 431 | 457 | if( zResult && *zResult ) add_history(zResult); |
| 432 | 458 | #else |
| 433 | 459 | printf("%s", zPrompt); |
| @@ -469,15 +495,15 @@ | ||
| 469 | 495 | int mode; /* An output mode setting */ |
| 470 | 496 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 471 | 497 | int showHeader; /* True to show column names in List or Column mode */ |
| 472 | 498 | unsigned shellFlgs; /* Various flags */ |
| 473 | 499 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 474 | - char separator[20]; /* Separator character for MODE_List */ | |
| 475 | - char newline[20]; /* Record separator in MODE_Csv */ | |
| 500 | + char colSeparator[20]; /* Column separator character for several modes */ | |
| 501 | + char rowSeparator[20]; /* Row separator character for MODE_Ascii */ | |
| 476 | 502 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 477 | 503 | int actualWidth[100]; /* Actual width of each column */ |
| 478 | - char nullvalue[20]; /* The text to print when a NULL comes back from | |
| 504 | + char nullValue[20]; /* The text to print when a NULL comes back from | |
| 479 | 505 | ** the database */ |
| 480 | 506 | SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ |
| 481 | 507 | char outfile[FILENAME_MAX]; /* Filename for *out */ |
| 482 | 508 | const char *zDbFilename; /* name of the database file */ |
| 483 | 509 | char *zFreeOnClose; /* Filename to free when closing */ |
| @@ -506,10 +532,11 @@ | ||
| 506 | 532 | #define MODE_Html 4 /* Generate an XHTML table */ |
| 507 | 533 | #define MODE_Insert 5 /* Generate SQL "insert" statements */ |
| 508 | 534 | #define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ |
| 509 | 535 | #define MODE_Csv 7 /* Quote strings, numbers are plain */ |
| 510 | 536 | #define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ |
| 537 | +#define MODE_Ascii 9 /* Use ASCII unit and record separators (0x1F/0x1E) */ | |
| 511 | 538 | |
| 512 | 539 | static const char *modeDescr[] = { |
| 513 | 540 | "line", |
| 514 | 541 | "column", |
| 515 | 542 | "list", |
| @@ -517,12 +544,26 @@ | ||
| 517 | 544 | "html", |
| 518 | 545 | "insert", |
| 519 | 546 | "tcl", |
| 520 | 547 | "csv", |
| 521 | 548 | "explain", |
| 549 | + "ascii", | |
| 522 | 550 | }; |
| 523 | 551 | |
| 552 | +/* | |
| 553 | +** These are the column/row/line separators used by the various | |
| 554 | +** import/export modes. | |
| 555 | +*/ | |
| 556 | +#define SEP_Column "|" | |
| 557 | +#define SEP_Row "\n" | |
| 558 | +#define SEP_Tab "\t" | |
| 559 | +#define SEP_Space " " | |
| 560 | +#define SEP_Comma "," | |
| 561 | +#define SEP_CrLf "\r\n" | |
| 562 | +#define SEP_Unit "\x1F" | |
| 563 | +#define SEP_Record "\x1E" | |
| 564 | + | |
| 524 | 565 | /* |
| 525 | 566 | ** Number of elements in an array |
| 526 | 567 | */ |
| 527 | 568 | #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) |
| 528 | 569 | |
| @@ -561,10 +602,11 @@ | ||
| 561 | 602 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 562 | 603 | */ |
| 563 | 604 | static void output_quoted_string(FILE *out, const char *z){ |
| 564 | 605 | int i; |
| 565 | 606 | int nSingle = 0; |
| 607 | + setBinaryMode(out); | |
| 566 | 608 | for(i=0; z[i]; i++){ |
| 567 | 609 | if( z[i]=='\'' ) nSingle++; |
| 568 | 610 | } |
| 569 | 611 | if( nSingle==0 ){ |
| 570 | 612 | fprintf(out,"'%s'",z); |
| @@ -583,10 +625,11 @@ | ||
| 583 | 625 | break; |
| 584 | 626 | } |
| 585 | 627 | } |
| 586 | 628 | fprintf(out,"'"); |
| 587 | 629 | } |
| 630 | + setTextMode(out); | |
| 588 | 631 | } |
| 589 | 632 | |
| 590 | 633 | /* |
| 591 | 634 | ** Output the given string as a quoted according to C or TCL quoting rules. |
| 592 | 635 | */ |
| @@ -675,26 +718,26 @@ | ||
| 675 | 718 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 676 | 719 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 677 | 720 | }; |
| 678 | 721 | |
| 679 | 722 | /* |
| 680 | -** Output a single term of CSV. Actually, p->separator is used for | |
| 681 | -** the separator, which may or may not be a comma. p->nullvalue is | |
| 723 | +** Output a single term of CSV. Actually, p->colSeparator is used for | |
| 724 | +** the separator, which may or may not be a comma. p->nullValue is | |
| 682 | 725 | ** the null value. Strings are quoted if necessary. The separator |
| 683 | 726 | ** is only issued if bSep is true. |
| 684 | 727 | */ |
| 685 | 728 | static void output_csv(ShellState *p, const char *z, int bSep){ |
| 686 | 729 | FILE *out = p->out; |
| 687 | 730 | if( z==0 ){ |
| 688 | - fprintf(out,"%s",p->nullvalue); | |
| 731 | + fprintf(out,"%s",p->nullValue); | |
| 689 | 732 | }else{ |
| 690 | 733 | int i; |
| 691 | - int nSep = strlen30(p->separator); | |
| 734 | + int nSep = strlen30(p->colSeparator); | |
| 692 | 735 | for(i=0; z[i]; i++){ |
| 693 | 736 | if( needCsvQuote[((unsigned char*)z)[i]] |
| 694 | - || (z[i]==p->separator[0] && | |
| 695 | - (nSep==1 || memcmp(z, p->separator, nSep)==0)) ){ | |
| 737 | + || (z[i]==p->colSeparator[0] && | |
| 738 | + (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ | |
| 696 | 739 | i = 0; |
| 697 | 740 | break; |
| 698 | 741 | } |
| 699 | 742 | } |
| 700 | 743 | if( i==0 ){ |
| @@ -707,11 +750,11 @@ | ||
| 707 | 750 | }else{ |
| 708 | 751 | fprintf(out, "%s", z); |
| 709 | 752 | } |
| 710 | 753 | } |
| 711 | 754 | if( bSep ){ |
| 712 | - fprintf(p->out, "%s", p->separator); | |
| 755 | + fprintf(p->out, "%s", p->colSeparator); | |
| 713 | 756 | } |
| 714 | 757 | } |
| 715 | 758 | |
| 716 | 759 | #ifdef SIGINT |
| 717 | 760 | /* |
| @@ -745,14 +788,14 @@ | ||
| 745 | 788 | if( azArg==0 ) break; |
| 746 | 789 | for(i=0; i<nArg; i++){ |
| 747 | 790 | int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 748 | 791 | if( len>w ) w = len; |
| 749 | 792 | } |
| 750 | - if( p->cnt++>0 ) fprintf(p->out,"\n"); | |
| 793 | + if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator); | |
| 751 | 794 | for(i=0; i<nArg; i++){ |
| 752 | - fprintf(p->out,"%*s = %s\n", w, azCol[i], | |
| 753 | - azArg[i] ? azArg[i] : p->nullvalue); | |
| 795 | + fprintf(p->out,"%*s = %s%s", w, azCol[i], | |
| 796 | + azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); | |
| 754 | 797 | } |
| 755 | 798 | break; |
| 756 | 799 | } |
| 757 | 800 | case MODE_Explain: |
| 758 | 801 | case MODE_Column: { |
| @@ -765,21 +808,23 @@ | ||
| 765 | 808 | w = 0; |
| 766 | 809 | } |
| 767 | 810 | if( w==0 ){ |
| 768 | 811 | w = strlen30(azCol[i] ? azCol[i] : ""); |
| 769 | 812 | if( w<10 ) w = 10; |
| 770 | - n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue); | |
| 813 | + n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullValue); | |
| 771 | 814 | if( w<n ) w = n; |
| 772 | 815 | } |
| 773 | 816 | if( i<ArraySize(p->actualWidth) ){ |
| 774 | 817 | p->actualWidth[i] = w; |
| 775 | 818 | } |
| 776 | 819 | if( p->showHeader ){ |
| 777 | 820 | if( w<0 ){ |
| 778 | - fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": " "); | |
| 821 | + fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], | |
| 822 | + i==nArg-1 ? p->rowSeparator : " "); | |
| 779 | 823 | }else{ |
| 780 | - fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); | |
| 824 | + fprintf(p->out,"%-*.*s%s",w,w,azCol[i], | |
| 825 | + i==nArg-1 ? p->rowSeparator : " "); | |
| 781 | 826 | } |
| 782 | 827 | } |
| 783 | 828 | } |
| 784 | 829 | if( p->showHeader ){ |
| 785 | 830 | for(i=0; i<nArg; i++){ |
| @@ -790,11 +835,11 @@ | ||
| 790 | 835 | }else{ |
| 791 | 836 | w = 10; |
| 792 | 837 | } |
| 793 | 838 | fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" |
| 794 | 839 | "----------------------------------------------------------", |
| 795 | - i==nArg-1 ? "\n": " "); | |
| 840 | + i==nArg-1 ? p->rowSeparator : " "); | |
| 796 | 841 | } |
| 797 | 842 | } |
| 798 | 843 | } |
| 799 | 844 | if( azArg==0 ) break; |
| 800 | 845 | for(i=0; i<nArg; i++){ |
| @@ -813,36 +858,39 @@ | ||
| 813 | 858 | } |
| 814 | 859 | p->iIndent++; |
| 815 | 860 | } |
| 816 | 861 | if( w<0 ){ |
| 817 | 862 | fprintf(p->out,"%*.*s%s",-w,-w, |
| 818 | - azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); | |
| 863 | + azArg[i] ? azArg[i] : p->nullValue, | |
| 864 | + i==nArg-1 ? p->rowSeparator : " "); | |
| 819 | 865 | }else{ |
| 820 | 866 | fprintf(p->out,"%-*.*s%s",w,w, |
| 821 | - azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); | |
| 867 | + azArg[i] ? azArg[i] : p->nullValue, | |
| 868 | + i==nArg-1 ? p->rowSeparator : " "); | |
| 822 | 869 | } |
| 823 | 870 | } |
| 824 | 871 | break; |
| 825 | 872 | } |
| 826 | 873 | case MODE_Semi: |
| 827 | 874 | case MODE_List: { |
| 828 | 875 | if( p->cnt++==0 && p->showHeader ){ |
| 829 | 876 | for(i=0; i<nArg; i++){ |
| 830 | - fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); | |
| 877 | + fprintf(p->out,"%s%s",azCol[i], | |
| 878 | + i==nArg-1 ? p->rowSeparator : p->colSeparator); | |
| 831 | 879 | } |
| 832 | 880 | } |
| 833 | 881 | if( azArg==0 ) break; |
| 834 | 882 | for(i=0; i<nArg; i++){ |
| 835 | 883 | char *z = azArg[i]; |
| 836 | - if( z==0 ) z = p->nullvalue; | |
| 884 | + if( z==0 ) z = p->nullValue; | |
| 837 | 885 | fprintf(p->out, "%s", z); |
| 838 | 886 | if( i<nArg-1 ){ |
| 839 | - fprintf(p->out, "%s", p->separator); | |
| 887 | + fprintf(p->out, "%s", p->colSeparator); | |
| 840 | 888 | }else if( p->mode==MODE_Semi ){ |
| 841 | - fprintf(p->out, ";\n"); | |
| 889 | + fprintf(p->out, ";%s", p->rowSeparator); | |
| 842 | 890 | }else{ |
| 843 | - fprintf(p->out, "\n"); | |
| 891 | + fprintf(p->out, "%s", p->rowSeparator); | |
| 844 | 892 | } |
| 845 | 893 | } |
| 846 | 894 | break; |
| 847 | 895 | } |
| 848 | 896 | case MODE_Html: { |
| @@ -857,53 +905,47 @@ | ||
| 857 | 905 | } |
| 858 | 906 | if( azArg==0 ) break; |
| 859 | 907 | fprintf(p->out,"<TR>"); |
| 860 | 908 | for(i=0; i<nArg; i++){ |
| 861 | 909 | fprintf(p->out,"<TD>"); |
| 862 | - output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); | |
| 910 | + output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); | |
| 863 | 911 | fprintf(p->out,"</TD>\n"); |
| 864 | 912 | } |
| 865 | 913 | fprintf(p->out,"</TR>\n"); |
| 866 | 914 | break; |
| 867 | 915 | } |
| 868 | 916 | case MODE_Tcl: { |
| 869 | 917 | if( p->cnt++==0 && p->showHeader ){ |
| 870 | 918 | for(i=0; i<nArg; i++){ |
| 871 | 919 | output_c_string(p->out,azCol[i] ? azCol[i] : ""); |
| 872 | - if(i<nArg-1) fprintf(p->out, "%s", p->separator); | |
| 920 | + if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator); | |
| 873 | 921 | } |
| 874 | - fprintf(p->out,"\n"); | |
| 922 | + fprintf(p->out, "%s", p->rowSeparator); | |
| 875 | 923 | } |
| 876 | 924 | if( azArg==0 ) break; |
| 877 | 925 | for(i=0; i<nArg; i++){ |
| 878 | - output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); | |
| 879 | - if(i<nArg-1) fprintf(p->out, "%s", p->separator); | |
| 926 | + output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); | |
| 927 | + if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator); | |
| 880 | 928 | } |
| 881 | - fprintf(p->out,"\n"); | |
| 929 | + fprintf(p->out, "%s", p->rowSeparator); | |
| 882 | 930 | break; |
| 883 | 931 | } |
| 884 | 932 | case MODE_Csv: { |
| 885 | -#if defined(WIN32) || defined(_WIN32) | |
| 886 | - fflush(p->out); | |
| 887 | - _setmode(_fileno(p->out), _O_BINARY); | |
| 888 | -#endif | |
| 933 | + setBinaryMode(p->out); | |
| 889 | 934 | if( p->cnt++==0 && p->showHeader ){ |
| 890 | 935 | for(i=0; i<nArg; i++){ |
| 891 | 936 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 892 | 937 | } |
| 893 | - fprintf(p->out,"%s",p->newline); | |
| 938 | + fprintf(p->out, "%s", p->rowSeparator); | |
| 894 | 939 | } |
| 895 | 940 | if( nArg>0 ){ |
| 896 | 941 | for(i=0; i<nArg; i++){ |
| 897 | 942 | output_csv(p, azArg[i], i<nArg-1); |
| 898 | 943 | } |
| 899 | - fprintf(p->out,"%s",p->newline); | |
| 944 | + fprintf(p->out, "%s", p->rowSeparator); | |
| 900 | 945 | } |
| 901 | -#if defined(WIN32) || defined(_WIN32) | |
| 902 | - fflush(p->out); | |
| 903 | - _setmode(_fileno(p->out), _O_TEXT); | |
| 904 | -#endif | |
| 946 | + setTextMode(p->out); | |
| 905 | 947 | break; |
| 906 | 948 | } |
| 907 | 949 | case MODE_Insert: { |
| 908 | 950 | p->cnt++; |
| 909 | 951 | if( azArg==0 ) break; |
| @@ -930,10 +972,26 @@ | ||
| 930 | 972 | output_quoted_string(p->out, azArg[i]); |
| 931 | 973 | } |
| 932 | 974 | } |
| 933 | 975 | fprintf(p->out,");\n"); |
| 934 | 976 | break; |
| 977 | + } | |
| 978 | + case MODE_Ascii: { | |
| 979 | + if( p->cnt++==0 && p->showHeader ){ | |
| 980 | + for(i=0; i<nArg; i++){ | |
| 981 | + if( i>0 ) fprintf(p->out, "%s", p->colSeparator); | |
| 982 | + fprintf(p->out,"%s",azCol[i] ? azCol[i] : ""); | |
| 983 | + } | |
| 984 | + fprintf(p->out, "%s", p->rowSeparator); | |
| 985 | + } | |
| 986 | + if( azArg==0 ) break; | |
| 987 | + for(i=0; i<nArg; i++){ | |
| 988 | + if( i>0 ) fprintf(p->out, "%s", p->colSeparator); | |
| 989 | + fprintf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); | |
| 990 | + } | |
| 991 | + fprintf(p->out, "%s", p->rowSeparator); | |
| 992 | + break; | |
| 935 | 993 | } |
| 936 | 994 | } |
| 937 | 995 | return 0; |
| 938 | 996 | } |
| 939 | 997 | |
| @@ -1428,10 +1486,21 @@ | ||
| 1428 | 1486 | } |
| 1429 | 1487 | } |
| 1430 | 1488 | sqlite3_finalize(pExplain); |
| 1431 | 1489 | sqlite3_free(zEQP); |
| 1432 | 1490 | } |
| 1491 | + | |
| 1492 | +#if USE_SYSTEM_SQLITE+0==1 | |
| 1493 | + /* Output TESTCTRL_EXPLAIN text of requested */ | |
| 1494 | + if( pArg && pArg->mode==MODE_Explain && sqlite3_libversion_number()<3008007 ){ | |
| 1495 | + const char *zExplain = 0; | |
| 1496 | + sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, pStmt, &zExplain); | |
| 1497 | + if( zExplain && zExplain[0] ){ | |
| 1498 | + fprintf(pArg->out, "%s", zExplain); | |
| 1499 | + } | |
| 1500 | + } | |
| 1501 | +#endif | |
| 1433 | 1502 | |
| 1434 | 1503 | /* If the shell is currently in ".explain" mode, gather the extra |
| 1435 | 1504 | ** data required to add indents to the output.*/ |
| 1436 | 1505 | if( pArg && pArg->mode==MODE_Explain ){ |
| 1437 | 1506 | explain_data_prepare(pArg, pStmt); |
| @@ -1696,16 +1765,17 @@ | ||
| 1696 | 1765 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1697 | 1766 | ".load FILE ?ENTRY? Load an extension library\n" |
| 1698 | 1767 | #endif |
| 1699 | 1768 | ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" |
| 1700 | 1769 | ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" |
| 1770 | + " ascii Columns/rows delimited by 0x1F and 0x1E\n" | |
| 1701 | 1771 | " csv Comma-separated values\n" |
| 1702 | 1772 | " column Left-aligned columns. (See .width)\n" |
| 1703 | 1773 | " html HTML <table> code\n" |
| 1704 | 1774 | " insert SQL insert statements for TABLE\n" |
| 1705 | 1775 | " line One value per line\n" |
| 1706 | - " list Values delimited by .separator string\n" | |
| 1776 | + " list Values delimited by .separator strings\n" | |
| 1707 | 1777 | " tabs Tab-separated values\n" |
| 1708 | 1778 | " tcl TCL list elements\n" |
| 1709 | 1779 | ".nullvalue STRING Use STRING in place of NULL values\n" |
| 1710 | 1780 | ".once FILENAME Output for the next SQL command only to FILENAME\n" |
| 1711 | 1781 | ".open ?FILENAME? Close existing database and reopen FILENAME\n" |
| @@ -1718,12 +1788,12 @@ | ||
| 1718 | 1788 | ".save FILE Write in-memory database into FILE\n" |
| 1719 | 1789 | ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n" |
| 1720 | 1790 | ".schema ?TABLE? Show the CREATE statements\n" |
| 1721 | 1791 | " If TABLE specified, only show tables matching\n" |
| 1722 | 1792 | " LIKE pattern TABLE.\n" |
| 1723 | - ".separator STRING ?NL? Change separator used by output mode and .import\n" | |
| 1724 | - " NL is the end-of-line mark for CSV\n" | |
| 1793 | + ".separator COL ?ROW? Change the column separator and optionally the row\n" | |
| 1794 | + " separator for both the output mode and .import\n" | |
| 1725 | 1795 | ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1726 | 1796 | ".show Show the current values for various settings\n" |
| 1727 | 1797 | ".stats on|off Turn stats on or off\n" |
| 1728 | 1798 | ".system CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1729 | 1799 | ".tables ?TABLE? List names of tables\n" |
| @@ -2000,26 +2070,27 @@ | ||
| 2000 | 2070 | static int nCall = 0; |
| 2001 | 2071 | nCall++; |
| 2002 | 2072 | } |
| 2003 | 2073 | |
| 2004 | 2074 | /* |
| 2005 | -** An object used to read a CSV file | |
| 2075 | +** An object used to read a CSV and other files for import. | |
| 2006 | 2076 | */ |
| 2007 | -typedef struct CSVReader CSVReader; | |
| 2008 | -struct CSVReader { | |
| 2077 | +typedef struct ImportCtx ImportCtx; | |
| 2078 | +struct ImportCtx { | |
| 2009 | 2079 | const char *zFile; /* Name of the input file */ |
| 2010 | 2080 | FILE *in; /* Read the CSV text from this input stream */ |
| 2011 | 2081 | char *z; /* Accumulated text for a field */ |
| 2012 | 2082 | int n; /* Number of bytes in z */ |
| 2013 | 2083 | int nAlloc; /* Space allocated for z[] */ |
| 2014 | 2084 | int nLine; /* Current line number */ |
| 2015 | 2085 | int cTerm; /* Character that terminated the most recent field */ |
| 2016 | - int cSeparator; /* The separator character. (Usually ",") */ | |
| 2086 | + int cColSep; /* The column separator character. (Usually ",") */ | |
| 2087 | + int cRowSep; /* The row separator character. (Usually "\n") */ | |
| 2017 | 2088 | }; |
| 2018 | 2089 | |
| 2019 | 2090 | /* Append a single byte to z[] */ |
| 2020 | -static void csv_append_char(CSVReader *p, int c){ | |
| 2091 | +static void import_append_char(ImportCtx *p, int c){ | |
| 2021 | 2092 | if( p->n+1>=p->nAlloc ){ |
| 2022 | 2093 | p->nAlloc += p->nAlloc + 100; |
| 2023 | 2094 | p->z = sqlite3_realloc(p->z, p->nAlloc); |
| 2024 | 2095 | if( p->z==0 ){ |
| 2025 | 2096 | fprintf(stderr, "out of memory\n"); |
| @@ -2033,41 +2104,44 @@ | ||
| 2033 | 2104 | ** with the option of having a separator other than ",". |
| 2034 | 2105 | ** |
| 2035 | 2106 | ** + Input comes from p->in. |
| 2036 | 2107 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 2037 | 2108 | ** from sqlite3_malloc(). |
| 2038 | -** + Use p->cSep as the separator. The default is ",". | |
| 2109 | +** + Use p->cSep as the column separator. The default is ",". | |
| 2110 | +** + Use p->rSep as the row separator. The default is "\n". | |
| 2039 | 2111 | ** + Keep track of the line number in p->nLine. |
| 2040 | 2112 | ** + Store the character that terminates the field in p->cTerm. Store |
| 2041 | 2113 | ** EOF on end-of-file. |
| 2042 | 2114 | ** + Report syntax errors on stderr |
| 2043 | 2115 | */ |
| 2044 | -static char *csv_read_one_field(CSVReader *p){ | |
| 2045 | - int c, pc, ppc; | |
| 2046 | - int cSep = p->cSeparator; | |
| 2116 | +static char *csv_read_one_field(ImportCtx *p){ | |
| 2117 | + int c; | |
| 2118 | + int cSep = p->cColSep; | |
| 2119 | + int rSep = p->cRowSep; | |
| 2047 | 2120 | p->n = 0; |
| 2048 | 2121 | c = fgetc(p->in); |
| 2049 | 2122 | if( c==EOF || seenInterrupt ){ |
| 2050 | 2123 | p->cTerm = EOF; |
| 2051 | 2124 | return 0; |
| 2052 | 2125 | } |
| 2053 | 2126 | if( c=='"' ){ |
| 2127 | + int pc, ppc; | |
| 2054 | 2128 | int startLine = p->nLine; |
| 2055 | 2129 | int cQuote = c; |
| 2056 | 2130 | pc = ppc = 0; |
| 2057 | 2131 | while( 1 ){ |
| 2058 | 2132 | c = fgetc(p->in); |
| 2059 | - if( c=='\n' ) p->nLine++; | |
| 2133 | + if( c==rSep ) p->nLine++; | |
| 2060 | 2134 | if( c==cQuote ){ |
| 2061 | 2135 | if( pc==cQuote ){ |
| 2062 | 2136 | pc = 0; |
| 2063 | 2137 | continue; |
| 2064 | 2138 | } |
| 2065 | 2139 | } |
| 2066 | 2140 | if( (c==cSep && pc==cQuote) |
| 2067 | - || (c=='\n' && pc==cQuote) | |
| 2068 | - || (c=='\n' && pc=='\r' && ppc==cQuote) | |
| 2141 | + || (c==rSep && pc==cQuote) | |
| 2142 | + || (c==rSep && pc=='\r' && ppc==cQuote) | |
| 2069 | 2143 | || (c==EOF && pc==cQuote) |
| 2070 | 2144 | ){ |
| 2071 | 2145 | do{ p->n--; }while( p->z[p->n]!=cQuote ); |
| 2072 | 2146 | p->cTerm = c; |
| 2073 | 2147 | break; |
| @@ -2077,31 +2151,65 @@ | ||
| 2077 | 2151 | p->zFile, p->nLine, cQuote); |
| 2078 | 2152 | } |
| 2079 | 2153 | if( c==EOF ){ |
| 2080 | 2154 | fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", |
| 2081 | 2155 | p->zFile, startLine, cQuote); |
| 2082 | - p->cTerm = EOF; | |
| 2156 | + p->cTerm = c; | |
| 2083 | 2157 | break; |
| 2084 | 2158 | } |
| 2085 | - csv_append_char(p, c); | |
| 2159 | + import_append_char(p, c); | |
| 2086 | 2160 | ppc = pc; |
| 2087 | 2161 | pc = c; |
| 2088 | 2162 | } |
| 2089 | 2163 | }else{ |
| 2090 | - while( c!=EOF && c!=cSep && c!='\n' ){ | |
| 2091 | - csv_append_char(p, c); | |
| 2164 | + while( c!=EOF && c!=cSep && c!=rSep ){ | |
| 2165 | + import_append_char(p, c); | |
| 2092 | 2166 | c = fgetc(p->in); |
| 2093 | 2167 | } |
| 2094 | - if( c=='\n' ){ | |
| 2168 | + if( c==rSep ){ | |
| 2095 | 2169 | p->nLine++; |
| 2096 | 2170 | if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; |
| 2097 | 2171 | } |
| 2098 | 2172 | p->cTerm = c; |
| 2099 | 2173 | } |
| 2100 | 2174 | if( p->z ) p->z[p->n] = 0; |
| 2101 | 2175 | return p->z; |
| 2102 | 2176 | } |
| 2177 | + | |
| 2178 | +/* Read a single field of ASCII delimited text. | |
| 2179 | +** | |
| 2180 | +** + Input comes from p->in. | |
| 2181 | +** + Store results in p->z of length p->n. Space to hold p->z comes | |
| 2182 | +** from sqlite3_malloc(). | |
| 2183 | +** + Use p->cSep as the column separator. The default is "\x1F". | |
| 2184 | +** + Use p->rSep as the row separator. The default is "\x1E". | |
| 2185 | +** + Keep track of the row number in p->nLine. | |
| 2186 | +** + Store the character that terminates the field in p->cTerm. Store | |
| 2187 | +** EOF on end-of-file. | |
| 2188 | +** + Report syntax errors on stderr | |
| 2189 | +*/ | |
| 2190 | +static char *ascii_read_one_field(ImportCtx *p){ | |
| 2191 | + int c; | |
| 2192 | + int cSep = p->cColSep; | |
| 2193 | + int rSep = p->cRowSep; | |
| 2194 | + p->n = 0; | |
| 2195 | + c = fgetc(p->in); | |
| 2196 | + if( c==EOF || seenInterrupt ){ | |
| 2197 | + p->cTerm = EOF; | |
| 2198 | + return 0; | |
| 2199 | + } | |
| 2200 | + while( c!=EOF && c!=cSep && c!=rSep ){ | |
| 2201 | + import_append_char(p, c); | |
| 2202 | + c = fgetc(p->in); | |
| 2203 | + } | |
| 2204 | + if( c==rSep ){ | |
| 2205 | + p->nLine++; | |
| 2206 | + } | |
| 2207 | + p->cTerm = c; | |
| 2208 | + if( p->z ) p->z[p->n] = 0; | |
| 2209 | + return p->z; | |
| 2210 | +} | |
| 2103 | 2211 | |
| 2104 | 2212 | /* |
| 2105 | 2213 | ** Try to transfer data for table zTable. If an error is seen while |
| 2106 | 2214 | ** moving forward, try to go backwards. The backwards movement won't |
| 2107 | 2215 | ** work for WITHOUT ROWID tables. |
| @@ -2653,100 +2761,125 @@ | ||
| 2653 | 2761 | sqlite3_stmt *pStmt = NULL; /* A statement */ |
| 2654 | 2762 | int nCol; /* Number of columns in the table */ |
| 2655 | 2763 | int nByte; /* Number of bytes in an SQL string */ |
| 2656 | 2764 | int i, j; /* Loop counters */ |
| 2657 | 2765 | int needCommit; /* True to COMMIT or ROLLBACK at end */ |
| 2658 | - int nSep; /* Number of bytes in p->separator[] */ | |
| 2766 | + int nSep; /* Number of bytes in p->colSeparator[] */ | |
| 2659 | 2767 | char *zSql; /* An SQL statement */ |
| 2660 | - CSVReader sCsv; /* Reader context */ | |
| 2768 | + ImportCtx sCtx; /* Reader context */ | |
| 2769 | + char *(*xRead)(ImportCtx*); /* Procedure to read one value */ | |
| 2661 | 2770 | int (*xCloser)(FILE*); /* Procedure to close th3 connection */ |
| 2662 | 2771 | |
| 2663 | 2772 | if( nArg!=3 ){ |
| 2664 | 2773 | fprintf(stderr, "Usage: .import FILE TABLE\n"); |
| 2665 | 2774 | goto meta_command_exit; |
| 2666 | 2775 | } |
| 2667 | 2776 | zFile = azArg[1]; |
| 2668 | 2777 | zTable = azArg[2]; |
| 2669 | 2778 | seenInterrupt = 0; |
| 2670 | - memset(&sCsv, 0, sizeof(sCsv)); | |
| 2779 | + memset(&sCtx, 0, sizeof(sCtx)); | |
| 2671 | 2780 | open_db(p, 0); |
| 2672 | - nSep = strlen30(p->separator); | |
| 2781 | + nSep = strlen30(p->colSeparator); | |
| 2782 | + if( nSep==0 ){ | |
| 2783 | + fprintf(stderr, "Error: non-null column separator required for import\n"); | |
| 2784 | + return 1; | |
| 2785 | + } | |
| 2786 | + if( nSep>1 ){ | |
| 2787 | + fprintf(stderr, "Error: multi-character column separators not allowed" | |
| 2788 | + " for import\n"); | |
| 2789 | + return 1; | |
| 2790 | + } | |
| 2791 | + nSep = strlen30(p->rowSeparator); | |
| 2673 | 2792 | if( nSep==0 ){ |
| 2674 | - fprintf(stderr, "Error: non-null separator required for import\n"); | |
| 2793 | + fprintf(stderr, "Error: non-null row separator required for import\n"); | |
| 2675 | 2794 | return 1; |
| 2795 | + } | |
| 2796 | + if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator, SEP_CrLf)==0 ){ | |
| 2797 | + /* When importing CSV (only), if the row separator is set to the | |
| 2798 | + ** default output row separator, change it to the default input | |
| 2799 | + ** row separator. This avoids having to maintain different input | |
| 2800 | + ** and output row separators. */ | |
| 2801 | + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); | |
| 2802 | + nSep = strlen30(p->rowSeparator); | |
| 2676 | 2803 | } |
| 2677 | 2804 | if( nSep>1 ){ |
| 2678 | - fprintf(stderr, "Error: multi-character separators not allowed" | |
| 2805 | + fprintf(stderr, "Error: multi-character row separators not allowed" | |
| 2679 | 2806 | " for import\n"); |
| 2680 | 2807 | return 1; |
| 2681 | 2808 | } |
| 2682 | - sCsv.zFile = zFile; | |
| 2683 | - sCsv.nLine = 1; | |
| 2684 | - if( sCsv.zFile[0]=='|' ){ | |
| 2685 | - sCsv.in = popen(sCsv.zFile+1, "r"); | |
| 2686 | - sCsv.zFile = "<pipe>"; | |
| 2809 | + sCtx.zFile = zFile; | |
| 2810 | + sCtx.nLine = 1; | |
| 2811 | + if( sCtx.zFile[0]=='|' ){ | |
| 2812 | + sCtx.in = popen(sCtx.zFile+1, "r"); | |
| 2813 | + sCtx.zFile = "<pipe>"; | |
| 2687 | 2814 | xCloser = pclose; |
| 2688 | 2815 | }else{ |
| 2689 | - sCsv.in = fopen(sCsv.zFile, "rb"); | |
| 2816 | + sCtx.in = fopen(sCtx.zFile, "rb"); | |
| 2690 | 2817 | xCloser = fclose; |
| 2691 | 2818 | } |
| 2692 | - if( sCsv.in==0 ){ | |
| 2819 | + if( p->mode==MODE_Ascii ){ | |
| 2820 | + xRead = ascii_read_one_field; | |
| 2821 | + }else{ | |
| 2822 | + xRead = csv_read_one_field; | |
| 2823 | + } | |
| 2824 | + if( sCtx.in==0 ){ | |
| 2693 | 2825 | fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); |
| 2694 | 2826 | return 1; |
| 2695 | 2827 | } |
| 2696 | - sCsv.cSeparator = p->separator[0]; | |
| 2828 | + sCtx.cColSep = p->colSeparator[0]; | |
| 2829 | + sCtx.cRowSep = p->rowSeparator[0]; | |
| 2697 | 2830 | zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); |
| 2698 | 2831 | if( zSql==0 ){ |
| 2699 | 2832 | fprintf(stderr, "Error: out of memory\n"); |
| 2700 | - xCloser(sCsv.in); | |
| 2833 | + xCloser(sCtx.in); | |
| 2701 | 2834 | return 1; |
| 2702 | 2835 | } |
| 2703 | 2836 | nByte = strlen30(zSql); |
| 2704 | 2837 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 2705 | - csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */ | |
| 2838 | + import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ | |
| 2706 | 2839 | if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ |
| 2707 | 2840 | char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); |
| 2708 | 2841 | char cSep = '('; |
| 2709 | - while( csv_read_one_field(&sCsv) ){ | |
| 2710 | - zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z); | |
| 2842 | + while( xRead(&sCtx) ){ | |
| 2843 | + zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z); | |
| 2711 | 2844 | cSep = ','; |
| 2712 | - if( sCsv.cTerm!=sCsv.cSeparator ) break; | |
| 2845 | + if( sCtx.cTerm!=sCtx.cColSep ) break; | |
| 2713 | 2846 | } |
| 2714 | 2847 | if( cSep=='(' ){ |
| 2715 | 2848 | sqlite3_free(zCreate); |
| 2716 | - sqlite3_free(sCsv.z); | |
| 2717 | - xCloser(sCsv.in); | |
| 2718 | - fprintf(stderr,"%s: empty file\n", sCsv.zFile); | |
| 2849 | + sqlite3_free(sCtx.z); | |
| 2850 | + xCloser(sCtx.in); | |
| 2851 | + fprintf(stderr,"%s: empty file\n", sCtx.zFile); | |
| 2719 | 2852 | return 1; |
| 2720 | 2853 | } |
| 2721 | 2854 | zCreate = sqlite3_mprintf("%z\n)", zCreate); |
| 2722 | 2855 | rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
| 2723 | 2856 | sqlite3_free(zCreate); |
| 2724 | 2857 | if( rc ){ |
| 2725 | 2858 | fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, |
| 2726 | 2859 | sqlite3_errmsg(db)); |
| 2727 | - sqlite3_free(sCsv.z); | |
| 2728 | - xCloser(sCsv.in); | |
| 2860 | + sqlite3_free(sCtx.z); | |
| 2861 | + xCloser(sCtx.in); | |
| 2729 | 2862 | return 1; |
| 2730 | 2863 | } |
| 2731 | 2864 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 2732 | 2865 | } |
| 2733 | 2866 | sqlite3_free(zSql); |
| 2734 | 2867 | if( rc ){ |
| 2735 | 2868 | if (pStmt) sqlite3_finalize(pStmt); |
| 2736 | 2869 | fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); |
| 2737 | - xCloser(sCsv.in); | |
| 2870 | + xCloser(sCtx.in); | |
| 2738 | 2871 | return 1; |
| 2739 | 2872 | } |
| 2740 | 2873 | nCol = sqlite3_column_count(pStmt); |
| 2741 | 2874 | sqlite3_finalize(pStmt); |
| 2742 | 2875 | pStmt = 0; |
| 2743 | 2876 | if( nCol==0 ) return 0; /* no columns, no error */ |
| 2744 | 2877 | zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); |
| 2745 | 2878 | if( zSql==0 ){ |
| 2746 | 2879 | fprintf(stderr, "Error: out of memory\n"); |
| 2747 | - xCloser(sCsv.in); | |
| 2880 | + xCloser(sCtx.in); | |
| 2748 | 2881 | return 1; |
| 2749 | 2882 | } |
| 2750 | 2883 | sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); |
| 2751 | 2884 | j = strlen30(zSql); |
| 2752 | 2885 | for(i=1; i<nCol; i++){ |
| @@ -2758,50 +2891,60 @@ | ||
| 2758 | 2891 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 2759 | 2892 | sqlite3_free(zSql); |
| 2760 | 2893 | if( rc ){ |
| 2761 | 2894 | fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); |
| 2762 | 2895 | if (pStmt) sqlite3_finalize(pStmt); |
| 2763 | - xCloser(sCsv.in); | |
| 2896 | + xCloser(sCtx.in); | |
| 2764 | 2897 | return 1; |
| 2765 | 2898 | } |
| 2766 | 2899 | needCommit = sqlite3_get_autocommit(db); |
| 2767 | 2900 | if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); |
| 2768 | 2901 | do{ |
| 2769 | - int startLine = sCsv.nLine; | |
| 2902 | + int startLine = sCtx.nLine; | |
| 2770 | 2903 | for(i=0; i<nCol; i++){ |
| 2771 | - char *z = csv_read_one_field(&sCsv); | |
| 2904 | + char *z = xRead(&sCtx); | |
| 2905 | + /* | |
| 2906 | + ** Did we reach end-of-file before finding any columns? | |
| 2907 | + ** If so, stop instead of NULL filling the remaining columns. | |
| 2908 | + */ | |
| 2772 | 2909 | if( z==0 && i==0 ) break; |
| 2910 | + /* | |
| 2911 | + ** Did we reach end-of-file OR end-of-line before finding any | |
| 2912 | + ** columns in ASCII mode? If so, stop instead of NULL filling | |
| 2913 | + ** the remaining columns. | |
| 2914 | + */ | |
| 2915 | + if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; | |
| 2773 | 2916 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 2774 | - if( i<nCol-1 && sCsv.cTerm!=sCsv.cSeparator ){ | |
| 2917 | + if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ | |
| 2775 | 2918 | fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
| 2776 | 2919 | "filling the rest with NULL\n", |
| 2777 | - sCsv.zFile, startLine, nCol, i+1); | |
| 2920 | + sCtx.zFile, startLine, nCol, i+1); | |
| 2778 | 2921 | i++; |
| 2779 | 2922 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| 2780 | 2923 | } |
| 2781 | 2924 | } |
| 2782 | - if( sCsv.cTerm==sCsv.cSeparator ){ | |
| 2925 | + if( sCtx.cTerm==sCtx.cColSep ){ | |
| 2783 | 2926 | do{ |
| 2784 | - csv_read_one_field(&sCsv); | |
| 2927 | + xRead(&sCtx); | |
| 2785 | 2928 | i++; |
| 2786 | - }while( sCsv.cTerm==sCsv.cSeparator ); | |
| 2929 | + }while( sCtx.cTerm==sCtx.cColSep ); | |
| 2787 | 2930 | fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
| 2788 | 2931 | "extras ignored\n", |
| 2789 | - sCsv.zFile, startLine, nCol, i); | |
| 2932 | + sCtx.zFile, startLine, nCol, i); | |
| 2790 | 2933 | } |
| 2791 | 2934 | if( i>=nCol ){ |
| 2792 | 2935 | sqlite3_step(pStmt); |
| 2793 | 2936 | rc = sqlite3_reset(pStmt); |
| 2794 | 2937 | if( rc!=SQLITE_OK ){ |
| 2795 | - fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine, | |
| 2938 | + fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, | |
| 2796 | 2939 | sqlite3_errmsg(db)); |
| 2797 | 2940 | } |
| 2798 | 2941 | } |
| 2799 | - }while( sCsv.cTerm!=EOF ); | |
| 2942 | + }while( sCtx.cTerm!=EOF ); | |
| 2800 | 2943 | |
| 2801 | - xCloser(sCsv.in); | |
| 2802 | - sqlite3_free(sCsv.z); | |
| 2944 | + xCloser(sCtx.in); | |
| 2945 | + sqlite3_free(sCtx.z); | |
| 2803 | 2946 | sqlite3_finalize(pStmt); |
| 2804 | 2947 | if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); |
| 2805 | 2948 | }else |
| 2806 | 2949 | |
| 2807 | 2950 | if( c=='i' && strncmp(azArg[0], "indices", n)==0 ){ |
| @@ -2915,32 +3058,36 @@ | ||
| 2915 | 3058 | p->mode = MODE_List; |
| 2916 | 3059 | }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ |
| 2917 | 3060 | p->mode = MODE_Html; |
| 2918 | 3061 | }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ |
| 2919 | 3062 | p->mode = MODE_Tcl; |
| 2920 | - sqlite3_snprintf(sizeof(p->separator), p->separator, " "); | |
| 3063 | + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); | |
| 2921 | 3064 | }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
| 2922 | 3065 | p->mode = MODE_Csv; |
| 2923 | - sqlite3_snprintf(sizeof(p->separator), p->separator, ","); | |
| 2924 | - sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); | |
| 3066 | + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); | |
| 3067 | + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); | |
| 2925 | 3068 | }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
| 2926 | 3069 | p->mode = MODE_List; |
| 2927 | - sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); | |
| 3070 | + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); | |
| 2928 | 3071 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 2929 | 3072 | p->mode = MODE_Insert; |
| 2930 | 3073 | set_table_name(p, nArg>=3 ? azArg[2] : "table"); |
| 3074 | + }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ | |
| 3075 | + p->mode = MODE_Ascii; | |
| 3076 | + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); | |
| 3077 | + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); | |
| 2931 | 3078 | }else { |
| 2932 | 3079 | fprintf(stderr,"Error: mode should be one of: " |
| 2933 | - "column csv html insert line list tabs tcl\n"); | |
| 3080 | + "ascii column csv html insert line list tabs tcl\n"); | |
| 2934 | 3081 | rc = 1; |
| 2935 | 3082 | } |
| 2936 | 3083 | }else |
| 2937 | 3084 | |
| 2938 | 3085 | if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ |
| 2939 | 3086 | if( nArg==2 ){ |
| 2940 | - sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue, | |
| 2941 | - "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]); | |
| 3087 | + sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, | |
| 3088 | + "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); | |
| 2942 | 3089 | }else{ |
| 2943 | 3090 | fprintf(stderr, "Usage: .nullvalue STRING\n"); |
| 2944 | 3091 | rc = 1; |
| 2945 | 3092 | } |
| 2946 | 3093 | }else |
| @@ -3191,11 +3338,11 @@ | ||
| 3191 | 3338 | |
| 3192 | 3339 | |
| 3193 | 3340 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 3194 | 3341 | if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ |
| 3195 | 3342 | extern int sqlite3SelectTrace; |
| 3196 | - sqlite3SelectTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff; | |
| 3343 | + sqlite3SelectTrace = integerValue(azArg[1]); | |
| 3197 | 3344 | }else |
| 3198 | 3345 | #endif |
| 3199 | 3346 | |
| 3200 | 3347 | |
| 3201 | 3348 | #ifdef SQLITE_DEBUG |
| @@ -3221,18 +3368,20 @@ | ||
| 3221 | 3368 | }else |
| 3222 | 3369 | #endif |
| 3223 | 3370 | |
| 3224 | 3371 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
| 3225 | 3372 | if( nArg<2 || nArg>3 ){ |
| 3226 | - fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); | |
| 3373 | + fprintf(stderr, "Usage: .separator COL ?ROW?\n"); | |
| 3227 | 3374 | rc = 1; |
| 3228 | 3375 | } |
| 3229 | 3376 | if( nArg>=2 ){ |
| 3230 | - sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); | |
| 3377 | + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, | |
| 3378 | + "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); | |
| 3231 | 3379 | } |
| 3232 | 3380 | if( nArg>=3 ){ |
| 3233 | - sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); | |
| 3381 | + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, | |
| 3382 | + "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); | |
| 3234 | 3383 | } |
| 3235 | 3384 | }else |
| 3236 | 3385 | |
| 3237 | 3386 | if( c=='s' |
| 3238 | 3387 | && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) |
| @@ -3259,27 +3408,28 @@ | ||
| 3259 | 3408 | if( nArg!=1 ){ |
| 3260 | 3409 | fprintf(stderr, "Usage: .show\n"); |
| 3261 | 3410 | rc = 1; |
| 3262 | 3411 | goto meta_command_exit; |
| 3263 | 3412 | } |
| 3264 | - fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); | |
| 3265 | - fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off"); | |
| 3413 | + fprintf(p->out,"%12.12s: %s\n","echo", p->echoOn ? "on" : "off"); | |
| 3414 | + fprintf(p->out,"%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off"); | |
| 3266 | 3415 | fprintf(p->out,"%9.9s: %s\n","explain", p->normalMode.valid ? "on" :"off"); |
| 3267 | - fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off"); | |
| 3268 | - fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); | |
| 3269 | - fprintf(p->out,"%9.9s: ", "nullvalue"); | |
| 3270 | - output_c_string(p->out, p->nullvalue); | |
| 3416 | + fprintf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off"); | |
| 3417 | + fprintf(p->out,"%12.12s: %s\n","mode", modeDescr[p->mode]); | |
| 3418 | + fprintf(p->out,"%12.12s: ", "nullvalue"); | |
| 3419 | + output_c_string(p->out, p->nullValue); | |
| 3271 | 3420 | fprintf(p->out, "\n"); |
| 3272 | - fprintf(p->out,"%9.9s: %s\n","output", | |
| 3421 | + fprintf(p->out,"%12.12s: %s\n","output", | |
| 3273 | 3422 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 3274 | - fprintf(p->out,"%9.9s: ", "separator"); | |
| 3275 | - output_c_string(p->out, p->separator); | |
| 3276 | - fprintf(p->out," "); | |
| 3277 | - output_c_string(p->out, p->newline); | |
| 3423 | + fprintf(p->out,"%12.12s: ", "colseparator"); | |
| 3424 | + output_c_string(p->out, p->colSeparator); | |
| 3425 | + fprintf(p->out, "\n"); | |
| 3426 | + fprintf(p->out,"%12.12s: ", "rowseparator"); | |
| 3427 | + output_c_string(p->out, p->rowSeparator); | |
| 3278 | 3428 | fprintf(p->out, "\n"); |
| 3279 | - fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); | |
| 3280 | - fprintf(p->out,"%9.9s: ","width"); | |
| 3429 | + fprintf(p->out,"%12.12s: %s\n","stats", p->statsOn ? "on" : "off"); | |
| 3430 | + fprintf(p->out,"%12.12s: ","width"); | |
| 3281 | 3431 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 3282 | 3432 | fprintf(p->out,"%d ",p->colWidth[i]); |
| 3283 | 3433 | } |
| 3284 | 3434 | fprintf(p->out,"\n"); |
| 3285 | 3435 | }else |
| @@ -3394,10 +3544,11 @@ | ||
| 3394 | 3544 | { "reserve", SQLITE_TESTCTRL_RESERVE }, |
| 3395 | 3545 | { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, |
| 3396 | 3546 | { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, |
| 3397 | 3547 | { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, |
| 3398 | 3548 | { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, |
| 3549 | + { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT }, | |
| 3399 | 3550 | }; |
| 3400 | 3551 | int testctrl = -1; |
| 3401 | 3552 | int rc = 0; |
| 3402 | 3553 | int i, n; |
| 3403 | 3554 | open_db(p, 0); |
| @@ -3460,11 +3611,12 @@ | ||
| 3460 | 3611 | } |
| 3461 | 3612 | break; |
| 3462 | 3613 | |
| 3463 | 3614 | /* sqlite3_test_control(int, int) */ |
| 3464 | 3615 | case SQLITE_TESTCTRL_ASSERT: |
| 3465 | - case SQLITE_TESTCTRL_ALWAYS: | |
| 3616 | + case SQLITE_TESTCTRL_ALWAYS: | |
| 3617 | + case SQLITE_TESTCTRL_NEVER_CORRUPT: | |
| 3466 | 3618 | if( nArg==3 ){ |
| 3467 | 3619 | int opt = booleanValue(azArg[2]); |
| 3468 | 3620 | rc = sqlite3_test_control(testctrl, opt); |
| 3469 | 3621 | fprintf(p->out, "%d (0x%08x)\n", rc, rc); |
| 3470 | 3622 | } else { |
| @@ -3936,10 +4088,11 @@ | ||
| 3936 | 4088 | |
| 3937 | 4089 | /* |
| 3938 | 4090 | ** Show available command line options |
| 3939 | 4091 | */ |
| 3940 | 4092 | static const char zOptions[] = |
| 4093 | + " -ascii set output mode to 'ascii'\n" | |
| 3941 | 4094 | " -bail stop after hitting an error\n" |
| 3942 | 4095 | " -batch force batch I/O\n" |
| 3943 | 4096 | " -column set output mode to 'column'\n" |
| 3944 | 4097 | " -cmd COMMAND run \"COMMAND\" before reading stdin\n" |
| 3945 | 4098 | " -csv set output mode to 'csv'\n" |
| @@ -3957,15 +4110,15 @@ | ||
| 3957 | 4110 | " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" |
| 3958 | 4111 | " -mmap N default mmap size set to N\n" |
| 3959 | 4112 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 3960 | 4113 | " -multiplex enable the multiplexor VFS\n" |
| 3961 | 4114 | #endif |
| 3962 | - " -newline SEP set newline character(s) for CSV\n" | |
| 4115 | + " -newline SEP set output row separator. Default: '\\n'\n" | |
| 3963 | 4116 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 3964 | 4117 | " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" |
| 3965 | 4118 | " -scratch SIZE N use N slots of SZ bytes each for scratch memory\n" |
| 3966 | - " -separator SEP set output field separator. Default: '|'\n" | |
| 4119 | + " -separator SEP set output column separator. Default: '|'\n" | |
| 3967 | 4120 | " -stats print memory stats before each finalize\n" |
| 3968 | 4121 | " -version show SQLite version\n" |
| 3969 | 4122 | " -vfs NAME use NAME as the default VFS\n" |
| 3970 | 4123 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 3971 | 4124 | " -vfstrace enable tracing of all VFS calls\n" |
| @@ -3988,12 +4141,12 @@ | ||
| 3988 | 4141 | ** Initialize the state information in data |
| 3989 | 4142 | */ |
| 3990 | 4143 | static void main_init(ShellState *data) { |
| 3991 | 4144 | memset(data, 0, sizeof(*data)); |
| 3992 | 4145 | data->mode = MODE_List; |
| 3993 | - memcpy(data->separator,"|", 2); | |
| 3994 | - memcpy(data->newline,"\r\n", 3); | |
| 4146 | + memcpy(data->colSeparator,SEP_Column, 2); | |
| 4147 | + memcpy(data->rowSeparator,SEP_Row, 2); | |
| 3995 | 4148 | data->showHeader = 0; |
| 3996 | 4149 | data->shellFlgs = SHFLG_Lookaside; |
| 3997 | 4150 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 3998 | 4151 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 3999 | 4152 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); |
| @@ -4050,10 +4203,12 @@ | ||
| 4050 | 4203 | fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", |
| 4051 | 4204 | sqlite3_sourceid(), SQLITE_SOURCE_ID); |
| 4052 | 4205 | exit(1); |
| 4053 | 4206 | } |
| 4054 | 4207 | #endif |
| 4208 | + setBinaryMode(stdin); | |
| 4209 | + setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ | |
| 4055 | 4210 | Argv0 = argv[0]; |
| 4056 | 4211 | main_init(&data); |
| 4057 | 4212 | stdin_is_interactive = isatty(0); |
| 4058 | 4213 | |
| 4059 | 4214 | /* Make sure we have a valid signal handler early, before anything |
| @@ -4228,19 +4383,25 @@ | ||
| 4228 | 4383 | data.mode = MODE_Line; |
| 4229 | 4384 | }else if( strcmp(z,"-column")==0 ){ |
| 4230 | 4385 | data.mode = MODE_Column; |
| 4231 | 4386 | }else if( strcmp(z,"-csv")==0 ){ |
| 4232 | 4387 | data.mode = MODE_Csv; |
| 4233 | - memcpy(data.separator,",",2); | |
| 4388 | + memcpy(data.colSeparator,",",2); | |
| 4389 | + }else if( strcmp(z,"-ascii")==0 ){ | |
| 4390 | + data.mode = MODE_Ascii; | |
| 4391 | + sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, | |
| 4392 | + SEP_Unit); | |
| 4393 | + sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, | |
| 4394 | + SEP_Record); | |
| 4234 | 4395 | }else if( strcmp(z,"-separator")==0 ){ |
| 4235 | - sqlite3_snprintf(sizeof(data.separator), data.separator, | |
| 4396 | + sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, | |
| 4236 | 4397 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4237 | 4398 | }else if( strcmp(z,"-newline")==0 ){ |
| 4238 | - sqlite3_snprintf(sizeof(data.newline), data.newline, | |
| 4399 | + sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, | |
| 4239 | 4400 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4240 | 4401 | }else if( strcmp(z,"-nullvalue")==0 ){ |
| 4241 | - sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, | |
| 4402 | + sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, | |
| 4242 | 4403 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4243 | 4404 | }else if( strcmp(z,"-header")==0 ){ |
| 4244 | 4405 | data.showHeader = 1; |
| 4245 | 4406 | }else if( strcmp(z,"-noheader")==0 ){ |
| 4246 | 4407 | data.showHeader = 0; |
| @@ -4356,11 +4517,11 @@ | ||
| 4356 | 4517 | nHistory = strlen30(zHome) + 20; |
| 4357 | 4518 | if( (zHistory = malloc(nHistory))!=0 ){ |
| 4358 | 4519 | sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); |
| 4359 | 4520 | } |
| 4360 | 4521 | } |
| 4361 | -#if defined(HAVE_READLINE) | |
| 4522 | +#if HAVE_READLINE | |
| 4362 | 4523 | if( zHistory ) read_history(zHistory); |
| 4363 | 4524 | #endif |
| 4364 | 4525 | rc = process_input(&data, 0); |
| 4365 | 4526 | if( zHistory ){ |
| 4366 | 4527 | stifle_history(100); |
| 4367 | 4528 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -15,10 +15,17 @@ | |
| 15 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 16 | /* This needs to come before any includes for MSVC compiler */ |
| 17 | #define _CRT_SECURE_NO_WARNINGS |
| 18 | #endif |
| 19 | |
| 20 | /* |
| 21 | ** Enable large-file support for fopen() and friends on unix. |
| 22 | */ |
| 23 | #ifndef SQLITE_DISABLE_LFS |
| 24 | # define _LARGE_FILE 1 |
| @@ -46,21 +53,20 @@ | |
| 46 | # endif |
| 47 | # include <unistd.h> |
| 48 | # include <sys/types.h> |
| 49 | #endif |
| 50 | |
| 51 | #if defined(HAVE_READLINE) && HAVE_READLINE!=0 |
| 52 | # include <readline/readline.h> |
| 53 | # include <readline/history.h> |
| 54 | #else |
| 55 | # undef HAVE_READLINE |
| 56 | #endif |
| 57 | #if defined(HAVE_EDITLINE) && !defined(HAVE_READLINE) |
| 58 | # define HAVE_READLINE 1 |
| 59 | # include <editline/readline.h> |
| 60 | #endif |
| 61 | #if !defined(HAVE_READLINE) |
| 62 | # define add_history(X) |
| 63 | # define read_history(X) |
| 64 | # define write_history(X) |
| 65 | # define stifle_history(X) |
| 66 | #endif |
| @@ -97,10 +103,30 @@ | |
| 97 | |
| 98 | /* ctype macros that work with signed characters */ |
| 99 | #define IsSpace(X) isspace((unsigned char)X) |
| 100 | #define IsDigit(X) isdigit((unsigned char)X) |
| 101 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 102 | |
| 103 | |
| 104 | /* True if the timer is enabled */ |
| 105 | static int enableTimer = 0; |
| 106 | |
| @@ -423,11 +449,11 @@ | |
| 423 | char *zResult; |
| 424 | if( in!=0 ){ |
| 425 | zResult = local_getline(zPrior, in); |
| 426 | }else{ |
| 427 | zPrompt = isContinuation ? continuePrompt : mainPrompt; |
| 428 | #if defined(HAVE_READLINE) |
| 429 | free(zPrior); |
| 430 | zResult = readline(zPrompt); |
| 431 | if( zResult && *zResult ) add_history(zResult); |
| 432 | #else |
| 433 | printf("%s", zPrompt); |
| @@ -469,15 +495,15 @@ | |
| 469 | int mode; /* An output mode setting */ |
| 470 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 471 | int showHeader; /* True to show column names in List or Column mode */ |
| 472 | unsigned shellFlgs; /* Various flags */ |
| 473 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 474 | char separator[20]; /* Separator character for MODE_List */ |
| 475 | char newline[20]; /* Record separator in MODE_Csv */ |
| 476 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 477 | int actualWidth[100]; /* Actual width of each column */ |
| 478 | char nullvalue[20]; /* The text to print when a NULL comes back from |
| 479 | ** the database */ |
| 480 | SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ |
| 481 | char outfile[FILENAME_MAX]; /* Filename for *out */ |
| 482 | const char *zDbFilename; /* name of the database file */ |
| 483 | char *zFreeOnClose; /* Filename to free when closing */ |
| @@ -506,10 +532,11 @@ | |
| 506 | #define MODE_Html 4 /* Generate an XHTML table */ |
| 507 | #define MODE_Insert 5 /* Generate SQL "insert" statements */ |
| 508 | #define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ |
| 509 | #define MODE_Csv 7 /* Quote strings, numbers are plain */ |
| 510 | #define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ |
| 511 | |
| 512 | static const char *modeDescr[] = { |
| 513 | "line", |
| 514 | "column", |
| 515 | "list", |
| @@ -517,12 +544,26 @@ | |
| 517 | "html", |
| 518 | "insert", |
| 519 | "tcl", |
| 520 | "csv", |
| 521 | "explain", |
| 522 | }; |
| 523 | |
| 524 | /* |
| 525 | ** Number of elements in an array |
| 526 | */ |
| 527 | #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) |
| 528 | |
| @@ -561,10 +602,11 @@ | |
| 561 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 562 | */ |
| 563 | static void output_quoted_string(FILE *out, const char *z){ |
| 564 | int i; |
| 565 | int nSingle = 0; |
| 566 | for(i=0; z[i]; i++){ |
| 567 | if( z[i]=='\'' ) nSingle++; |
| 568 | } |
| 569 | if( nSingle==0 ){ |
| 570 | fprintf(out,"'%s'",z); |
| @@ -583,10 +625,11 @@ | |
| 583 | break; |
| 584 | } |
| 585 | } |
| 586 | fprintf(out,"'"); |
| 587 | } |
| 588 | } |
| 589 | |
| 590 | /* |
| 591 | ** Output the given string as a quoted according to C or TCL quoting rules. |
| 592 | */ |
| @@ -675,26 +718,26 @@ | |
| 675 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 676 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 677 | }; |
| 678 | |
| 679 | /* |
| 680 | ** Output a single term of CSV. Actually, p->separator is used for |
| 681 | ** the separator, which may or may not be a comma. p->nullvalue is |
| 682 | ** the null value. Strings are quoted if necessary. The separator |
| 683 | ** is only issued if bSep is true. |
| 684 | */ |
| 685 | static void output_csv(ShellState *p, const char *z, int bSep){ |
| 686 | FILE *out = p->out; |
| 687 | if( z==0 ){ |
| 688 | fprintf(out,"%s",p->nullvalue); |
| 689 | }else{ |
| 690 | int i; |
| 691 | int nSep = strlen30(p->separator); |
| 692 | for(i=0; z[i]; i++){ |
| 693 | if( needCsvQuote[((unsigned char*)z)[i]] |
| 694 | || (z[i]==p->separator[0] && |
| 695 | (nSep==1 || memcmp(z, p->separator, nSep)==0)) ){ |
| 696 | i = 0; |
| 697 | break; |
| 698 | } |
| 699 | } |
| 700 | if( i==0 ){ |
| @@ -707,11 +750,11 @@ | |
| 707 | }else{ |
| 708 | fprintf(out, "%s", z); |
| 709 | } |
| 710 | } |
| 711 | if( bSep ){ |
| 712 | fprintf(p->out, "%s", p->separator); |
| 713 | } |
| 714 | } |
| 715 | |
| 716 | #ifdef SIGINT |
| 717 | /* |
| @@ -745,14 +788,14 @@ | |
| 745 | if( azArg==0 ) break; |
| 746 | for(i=0; i<nArg; i++){ |
| 747 | int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 748 | if( len>w ) w = len; |
| 749 | } |
| 750 | if( p->cnt++>0 ) fprintf(p->out,"\n"); |
| 751 | for(i=0; i<nArg; i++){ |
| 752 | fprintf(p->out,"%*s = %s\n", w, azCol[i], |
| 753 | azArg[i] ? azArg[i] : p->nullvalue); |
| 754 | } |
| 755 | break; |
| 756 | } |
| 757 | case MODE_Explain: |
| 758 | case MODE_Column: { |
| @@ -765,21 +808,23 @@ | |
| 765 | w = 0; |
| 766 | } |
| 767 | if( w==0 ){ |
| 768 | w = strlen30(azCol[i] ? azCol[i] : ""); |
| 769 | if( w<10 ) w = 10; |
| 770 | n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue); |
| 771 | if( w<n ) w = n; |
| 772 | } |
| 773 | if( i<ArraySize(p->actualWidth) ){ |
| 774 | p->actualWidth[i] = w; |
| 775 | } |
| 776 | if( p->showHeader ){ |
| 777 | if( w<0 ){ |
| 778 | fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": " "); |
| 779 | }else{ |
| 780 | fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); |
| 781 | } |
| 782 | } |
| 783 | } |
| 784 | if( p->showHeader ){ |
| 785 | for(i=0; i<nArg; i++){ |
| @@ -790,11 +835,11 @@ | |
| 790 | }else{ |
| 791 | w = 10; |
| 792 | } |
| 793 | fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" |
| 794 | "----------------------------------------------------------", |
| 795 | i==nArg-1 ? "\n": " "); |
| 796 | } |
| 797 | } |
| 798 | } |
| 799 | if( azArg==0 ) break; |
| 800 | for(i=0; i<nArg; i++){ |
| @@ -813,36 +858,39 @@ | |
| 813 | } |
| 814 | p->iIndent++; |
| 815 | } |
| 816 | if( w<0 ){ |
| 817 | fprintf(p->out,"%*.*s%s",-w,-w, |
| 818 | azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); |
| 819 | }else{ |
| 820 | fprintf(p->out,"%-*.*s%s",w,w, |
| 821 | azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); |
| 822 | } |
| 823 | } |
| 824 | break; |
| 825 | } |
| 826 | case MODE_Semi: |
| 827 | case MODE_List: { |
| 828 | if( p->cnt++==0 && p->showHeader ){ |
| 829 | for(i=0; i<nArg; i++){ |
| 830 | fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator); |
| 831 | } |
| 832 | } |
| 833 | if( azArg==0 ) break; |
| 834 | for(i=0; i<nArg; i++){ |
| 835 | char *z = azArg[i]; |
| 836 | if( z==0 ) z = p->nullvalue; |
| 837 | fprintf(p->out, "%s", z); |
| 838 | if( i<nArg-1 ){ |
| 839 | fprintf(p->out, "%s", p->separator); |
| 840 | }else if( p->mode==MODE_Semi ){ |
| 841 | fprintf(p->out, ";\n"); |
| 842 | }else{ |
| 843 | fprintf(p->out, "\n"); |
| 844 | } |
| 845 | } |
| 846 | break; |
| 847 | } |
| 848 | case MODE_Html: { |
| @@ -857,53 +905,47 @@ | |
| 857 | } |
| 858 | if( azArg==0 ) break; |
| 859 | fprintf(p->out,"<TR>"); |
| 860 | for(i=0; i<nArg; i++){ |
| 861 | fprintf(p->out,"<TD>"); |
| 862 | output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); |
| 863 | fprintf(p->out,"</TD>\n"); |
| 864 | } |
| 865 | fprintf(p->out,"</TR>\n"); |
| 866 | break; |
| 867 | } |
| 868 | case MODE_Tcl: { |
| 869 | if( p->cnt++==0 && p->showHeader ){ |
| 870 | for(i=0; i<nArg; i++){ |
| 871 | output_c_string(p->out,azCol[i] ? azCol[i] : ""); |
| 872 | if(i<nArg-1) fprintf(p->out, "%s", p->separator); |
| 873 | } |
| 874 | fprintf(p->out,"\n"); |
| 875 | } |
| 876 | if( azArg==0 ) break; |
| 877 | for(i=0; i<nArg; i++){ |
| 878 | output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue); |
| 879 | if(i<nArg-1) fprintf(p->out, "%s", p->separator); |
| 880 | } |
| 881 | fprintf(p->out,"\n"); |
| 882 | break; |
| 883 | } |
| 884 | case MODE_Csv: { |
| 885 | #if defined(WIN32) || defined(_WIN32) |
| 886 | fflush(p->out); |
| 887 | _setmode(_fileno(p->out), _O_BINARY); |
| 888 | #endif |
| 889 | if( p->cnt++==0 && p->showHeader ){ |
| 890 | for(i=0; i<nArg; i++){ |
| 891 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 892 | } |
| 893 | fprintf(p->out,"%s",p->newline); |
| 894 | } |
| 895 | if( nArg>0 ){ |
| 896 | for(i=0; i<nArg; i++){ |
| 897 | output_csv(p, azArg[i], i<nArg-1); |
| 898 | } |
| 899 | fprintf(p->out,"%s",p->newline); |
| 900 | } |
| 901 | #if defined(WIN32) || defined(_WIN32) |
| 902 | fflush(p->out); |
| 903 | _setmode(_fileno(p->out), _O_TEXT); |
| 904 | #endif |
| 905 | break; |
| 906 | } |
| 907 | case MODE_Insert: { |
| 908 | p->cnt++; |
| 909 | if( azArg==0 ) break; |
| @@ -930,10 +972,26 @@ | |
| 930 | output_quoted_string(p->out, azArg[i]); |
| 931 | } |
| 932 | } |
| 933 | fprintf(p->out,");\n"); |
| 934 | break; |
| 935 | } |
| 936 | } |
| 937 | return 0; |
| 938 | } |
| 939 | |
| @@ -1428,10 +1486,21 @@ | |
| 1428 | } |
| 1429 | } |
| 1430 | sqlite3_finalize(pExplain); |
| 1431 | sqlite3_free(zEQP); |
| 1432 | } |
| 1433 | |
| 1434 | /* If the shell is currently in ".explain" mode, gather the extra |
| 1435 | ** data required to add indents to the output.*/ |
| 1436 | if( pArg && pArg->mode==MODE_Explain ){ |
| 1437 | explain_data_prepare(pArg, pStmt); |
| @@ -1696,16 +1765,17 @@ | |
| 1696 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1697 | ".load FILE ?ENTRY? Load an extension library\n" |
| 1698 | #endif |
| 1699 | ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" |
| 1700 | ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" |
| 1701 | " csv Comma-separated values\n" |
| 1702 | " column Left-aligned columns. (See .width)\n" |
| 1703 | " html HTML <table> code\n" |
| 1704 | " insert SQL insert statements for TABLE\n" |
| 1705 | " line One value per line\n" |
| 1706 | " list Values delimited by .separator string\n" |
| 1707 | " tabs Tab-separated values\n" |
| 1708 | " tcl TCL list elements\n" |
| 1709 | ".nullvalue STRING Use STRING in place of NULL values\n" |
| 1710 | ".once FILENAME Output for the next SQL command only to FILENAME\n" |
| 1711 | ".open ?FILENAME? Close existing database and reopen FILENAME\n" |
| @@ -1718,12 +1788,12 @@ | |
| 1718 | ".save FILE Write in-memory database into FILE\n" |
| 1719 | ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n" |
| 1720 | ".schema ?TABLE? Show the CREATE statements\n" |
| 1721 | " If TABLE specified, only show tables matching\n" |
| 1722 | " LIKE pattern TABLE.\n" |
| 1723 | ".separator STRING ?NL? Change separator used by output mode and .import\n" |
| 1724 | " NL is the end-of-line mark for CSV\n" |
| 1725 | ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1726 | ".show Show the current values for various settings\n" |
| 1727 | ".stats on|off Turn stats on or off\n" |
| 1728 | ".system CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1729 | ".tables ?TABLE? List names of tables\n" |
| @@ -2000,26 +2070,27 @@ | |
| 2000 | static int nCall = 0; |
| 2001 | nCall++; |
| 2002 | } |
| 2003 | |
| 2004 | /* |
| 2005 | ** An object used to read a CSV file |
| 2006 | */ |
| 2007 | typedef struct CSVReader CSVReader; |
| 2008 | struct CSVReader { |
| 2009 | const char *zFile; /* Name of the input file */ |
| 2010 | FILE *in; /* Read the CSV text from this input stream */ |
| 2011 | char *z; /* Accumulated text for a field */ |
| 2012 | int n; /* Number of bytes in z */ |
| 2013 | int nAlloc; /* Space allocated for z[] */ |
| 2014 | int nLine; /* Current line number */ |
| 2015 | int cTerm; /* Character that terminated the most recent field */ |
| 2016 | int cSeparator; /* The separator character. (Usually ",") */ |
| 2017 | }; |
| 2018 | |
| 2019 | /* Append a single byte to z[] */ |
| 2020 | static void csv_append_char(CSVReader *p, int c){ |
| 2021 | if( p->n+1>=p->nAlloc ){ |
| 2022 | p->nAlloc += p->nAlloc + 100; |
| 2023 | p->z = sqlite3_realloc(p->z, p->nAlloc); |
| 2024 | if( p->z==0 ){ |
| 2025 | fprintf(stderr, "out of memory\n"); |
| @@ -2033,41 +2104,44 @@ | |
| 2033 | ** with the option of having a separator other than ",". |
| 2034 | ** |
| 2035 | ** + Input comes from p->in. |
| 2036 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 2037 | ** from sqlite3_malloc(). |
| 2038 | ** + Use p->cSep as the separator. The default is ",". |
| 2039 | ** + Keep track of the line number in p->nLine. |
| 2040 | ** + Store the character that terminates the field in p->cTerm. Store |
| 2041 | ** EOF on end-of-file. |
| 2042 | ** + Report syntax errors on stderr |
| 2043 | */ |
| 2044 | static char *csv_read_one_field(CSVReader *p){ |
| 2045 | int c, pc, ppc; |
| 2046 | int cSep = p->cSeparator; |
| 2047 | p->n = 0; |
| 2048 | c = fgetc(p->in); |
| 2049 | if( c==EOF || seenInterrupt ){ |
| 2050 | p->cTerm = EOF; |
| 2051 | return 0; |
| 2052 | } |
| 2053 | if( c=='"' ){ |
| 2054 | int startLine = p->nLine; |
| 2055 | int cQuote = c; |
| 2056 | pc = ppc = 0; |
| 2057 | while( 1 ){ |
| 2058 | c = fgetc(p->in); |
| 2059 | if( c=='\n' ) p->nLine++; |
| 2060 | if( c==cQuote ){ |
| 2061 | if( pc==cQuote ){ |
| 2062 | pc = 0; |
| 2063 | continue; |
| 2064 | } |
| 2065 | } |
| 2066 | if( (c==cSep && pc==cQuote) |
| 2067 | || (c=='\n' && pc==cQuote) |
| 2068 | || (c=='\n' && pc=='\r' && ppc==cQuote) |
| 2069 | || (c==EOF && pc==cQuote) |
| 2070 | ){ |
| 2071 | do{ p->n--; }while( p->z[p->n]!=cQuote ); |
| 2072 | p->cTerm = c; |
| 2073 | break; |
| @@ -2077,31 +2151,65 @@ | |
| 2077 | p->zFile, p->nLine, cQuote); |
| 2078 | } |
| 2079 | if( c==EOF ){ |
| 2080 | fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", |
| 2081 | p->zFile, startLine, cQuote); |
| 2082 | p->cTerm = EOF; |
| 2083 | break; |
| 2084 | } |
| 2085 | csv_append_char(p, c); |
| 2086 | ppc = pc; |
| 2087 | pc = c; |
| 2088 | } |
| 2089 | }else{ |
| 2090 | while( c!=EOF && c!=cSep && c!='\n' ){ |
| 2091 | csv_append_char(p, c); |
| 2092 | c = fgetc(p->in); |
| 2093 | } |
| 2094 | if( c=='\n' ){ |
| 2095 | p->nLine++; |
| 2096 | if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; |
| 2097 | } |
| 2098 | p->cTerm = c; |
| 2099 | } |
| 2100 | if( p->z ) p->z[p->n] = 0; |
| 2101 | return p->z; |
| 2102 | } |
| 2103 | |
| 2104 | /* |
| 2105 | ** Try to transfer data for table zTable. If an error is seen while |
| 2106 | ** moving forward, try to go backwards. The backwards movement won't |
| 2107 | ** work for WITHOUT ROWID tables. |
| @@ -2653,100 +2761,125 @@ | |
| 2653 | sqlite3_stmt *pStmt = NULL; /* A statement */ |
| 2654 | int nCol; /* Number of columns in the table */ |
| 2655 | int nByte; /* Number of bytes in an SQL string */ |
| 2656 | int i, j; /* Loop counters */ |
| 2657 | int needCommit; /* True to COMMIT or ROLLBACK at end */ |
| 2658 | int nSep; /* Number of bytes in p->separator[] */ |
| 2659 | char *zSql; /* An SQL statement */ |
| 2660 | CSVReader sCsv; /* Reader context */ |
| 2661 | int (*xCloser)(FILE*); /* Procedure to close th3 connection */ |
| 2662 | |
| 2663 | if( nArg!=3 ){ |
| 2664 | fprintf(stderr, "Usage: .import FILE TABLE\n"); |
| 2665 | goto meta_command_exit; |
| 2666 | } |
| 2667 | zFile = azArg[1]; |
| 2668 | zTable = azArg[2]; |
| 2669 | seenInterrupt = 0; |
| 2670 | memset(&sCsv, 0, sizeof(sCsv)); |
| 2671 | open_db(p, 0); |
| 2672 | nSep = strlen30(p->separator); |
| 2673 | if( nSep==0 ){ |
| 2674 | fprintf(stderr, "Error: non-null separator required for import\n"); |
| 2675 | return 1; |
| 2676 | } |
| 2677 | if( nSep>1 ){ |
| 2678 | fprintf(stderr, "Error: multi-character separators not allowed" |
| 2679 | " for import\n"); |
| 2680 | return 1; |
| 2681 | } |
| 2682 | sCsv.zFile = zFile; |
| 2683 | sCsv.nLine = 1; |
| 2684 | if( sCsv.zFile[0]=='|' ){ |
| 2685 | sCsv.in = popen(sCsv.zFile+1, "r"); |
| 2686 | sCsv.zFile = "<pipe>"; |
| 2687 | xCloser = pclose; |
| 2688 | }else{ |
| 2689 | sCsv.in = fopen(sCsv.zFile, "rb"); |
| 2690 | xCloser = fclose; |
| 2691 | } |
| 2692 | if( sCsv.in==0 ){ |
| 2693 | fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); |
| 2694 | return 1; |
| 2695 | } |
| 2696 | sCsv.cSeparator = p->separator[0]; |
| 2697 | zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); |
| 2698 | if( zSql==0 ){ |
| 2699 | fprintf(stderr, "Error: out of memory\n"); |
| 2700 | xCloser(sCsv.in); |
| 2701 | return 1; |
| 2702 | } |
| 2703 | nByte = strlen30(zSql); |
| 2704 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 2705 | csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */ |
| 2706 | if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ |
| 2707 | char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); |
| 2708 | char cSep = '('; |
| 2709 | while( csv_read_one_field(&sCsv) ){ |
| 2710 | zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z); |
| 2711 | cSep = ','; |
| 2712 | if( sCsv.cTerm!=sCsv.cSeparator ) break; |
| 2713 | } |
| 2714 | if( cSep=='(' ){ |
| 2715 | sqlite3_free(zCreate); |
| 2716 | sqlite3_free(sCsv.z); |
| 2717 | xCloser(sCsv.in); |
| 2718 | fprintf(stderr,"%s: empty file\n", sCsv.zFile); |
| 2719 | return 1; |
| 2720 | } |
| 2721 | zCreate = sqlite3_mprintf("%z\n)", zCreate); |
| 2722 | rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
| 2723 | sqlite3_free(zCreate); |
| 2724 | if( rc ){ |
| 2725 | fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, |
| 2726 | sqlite3_errmsg(db)); |
| 2727 | sqlite3_free(sCsv.z); |
| 2728 | xCloser(sCsv.in); |
| 2729 | return 1; |
| 2730 | } |
| 2731 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 2732 | } |
| 2733 | sqlite3_free(zSql); |
| 2734 | if( rc ){ |
| 2735 | if (pStmt) sqlite3_finalize(pStmt); |
| 2736 | fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); |
| 2737 | xCloser(sCsv.in); |
| 2738 | return 1; |
| 2739 | } |
| 2740 | nCol = sqlite3_column_count(pStmt); |
| 2741 | sqlite3_finalize(pStmt); |
| 2742 | pStmt = 0; |
| 2743 | if( nCol==0 ) return 0; /* no columns, no error */ |
| 2744 | zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); |
| 2745 | if( zSql==0 ){ |
| 2746 | fprintf(stderr, "Error: out of memory\n"); |
| 2747 | xCloser(sCsv.in); |
| 2748 | return 1; |
| 2749 | } |
| 2750 | sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); |
| 2751 | j = strlen30(zSql); |
| 2752 | for(i=1; i<nCol; i++){ |
| @@ -2758,50 +2891,60 @@ | |
| 2758 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 2759 | sqlite3_free(zSql); |
| 2760 | if( rc ){ |
| 2761 | fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); |
| 2762 | if (pStmt) sqlite3_finalize(pStmt); |
| 2763 | xCloser(sCsv.in); |
| 2764 | return 1; |
| 2765 | } |
| 2766 | needCommit = sqlite3_get_autocommit(db); |
| 2767 | if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); |
| 2768 | do{ |
| 2769 | int startLine = sCsv.nLine; |
| 2770 | for(i=0; i<nCol; i++){ |
| 2771 | char *z = csv_read_one_field(&sCsv); |
| 2772 | if( z==0 && i==0 ) break; |
| 2773 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 2774 | if( i<nCol-1 && sCsv.cTerm!=sCsv.cSeparator ){ |
| 2775 | fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
| 2776 | "filling the rest with NULL\n", |
| 2777 | sCsv.zFile, startLine, nCol, i+1); |
| 2778 | i++; |
| 2779 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| 2780 | } |
| 2781 | } |
| 2782 | if( sCsv.cTerm==sCsv.cSeparator ){ |
| 2783 | do{ |
| 2784 | csv_read_one_field(&sCsv); |
| 2785 | i++; |
| 2786 | }while( sCsv.cTerm==sCsv.cSeparator ); |
| 2787 | fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
| 2788 | "extras ignored\n", |
| 2789 | sCsv.zFile, startLine, nCol, i); |
| 2790 | } |
| 2791 | if( i>=nCol ){ |
| 2792 | sqlite3_step(pStmt); |
| 2793 | rc = sqlite3_reset(pStmt); |
| 2794 | if( rc!=SQLITE_OK ){ |
| 2795 | fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine, |
| 2796 | sqlite3_errmsg(db)); |
| 2797 | } |
| 2798 | } |
| 2799 | }while( sCsv.cTerm!=EOF ); |
| 2800 | |
| 2801 | xCloser(sCsv.in); |
| 2802 | sqlite3_free(sCsv.z); |
| 2803 | sqlite3_finalize(pStmt); |
| 2804 | if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); |
| 2805 | }else |
| 2806 | |
| 2807 | if( c=='i' && strncmp(azArg[0], "indices", n)==0 ){ |
| @@ -2915,32 +3058,36 @@ | |
| 2915 | p->mode = MODE_List; |
| 2916 | }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ |
| 2917 | p->mode = MODE_Html; |
| 2918 | }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ |
| 2919 | p->mode = MODE_Tcl; |
| 2920 | sqlite3_snprintf(sizeof(p->separator), p->separator, " "); |
| 2921 | }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
| 2922 | p->mode = MODE_Csv; |
| 2923 | sqlite3_snprintf(sizeof(p->separator), p->separator, ","); |
| 2924 | sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n"); |
| 2925 | }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
| 2926 | p->mode = MODE_List; |
| 2927 | sqlite3_snprintf(sizeof(p->separator), p->separator, "\t"); |
| 2928 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 2929 | p->mode = MODE_Insert; |
| 2930 | set_table_name(p, nArg>=3 ? azArg[2] : "table"); |
| 2931 | }else { |
| 2932 | fprintf(stderr,"Error: mode should be one of: " |
| 2933 | "column csv html insert line list tabs tcl\n"); |
| 2934 | rc = 1; |
| 2935 | } |
| 2936 | }else |
| 2937 | |
| 2938 | if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ |
| 2939 | if( nArg==2 ){ |
| 2940 | sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue, |
| 2941 | "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]); |
| 2942 | }else{ |
| 2943 | fprintf(stderr, "Usage: .nullvalue STRING\n"); |
| 2944 | rc = 1; |
| 2945 | } |
| 2946 | }else |
| @@ -3191,11 +3338,11 @@ | |
| 3191 | |
| 3192 | |
| 3193 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 3194 | if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ |
| 3195 | extern int sqlite3SelectTrace; |
| 3196 | sqlite3SelectTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff; |
| 3197 | }else |
| 3198 | #endif |
| 3199 | |
| 3200 | |
| 3201 | #ifdef SQLITE_DEBUG |
| @@ -3221,18 +3368,20 @@ | |
| 3221 | }else |
| 3222 | #endif |
| 3223 | |
| 3224 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
| 3225 | if( nArg<2 || nArg>3 ){ |
| 3226 | fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n"); |
| 3227 | rc = 1; |
| 3228 | } |
| 3229 | if( nArg>=2 ){ |
| 3230 | sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]); |
| 3231 | } |
| 3232 | if( nArg>=3 ){ |
| 3233 | sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]); |
| 3234 | } |
| 3235 | }else |
| 3236 | |
| 3237 | if( c=='s' |
| 3238 | && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) |
| @@ -3259,27 +3408,28 @@ | |
| 3259 | if( nArg!=1 ){ |
| 3260 | fprintf(stderr, "Usage: .show\n"); |
| 3261 | rc = 1; |
| 3262 | goto meta_command_exit; |
| 3263 | } |
| 3264 | fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off"); |
| 3265 | fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off"); |
| 3266 | fprintf(p->out,"%9.9s: %s\n","explain", p->normalMode.valid ? "on" :"off"); |
| 3267 | fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off"); |
| 3268 | fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]); |
| 3269 | fprintf(p->out,"%9.9s: ", "nullvalue"); |
| 3270 | output_c_string(p->out, p->nullvalue); |
| 3271 | fprintf(p->out, "\n"); |
| 3272 | fprintf(p->out,"%9.9s: %s\n","output", |
| 3273 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 3274 | fprintf(p->out,"%9.9s: ", "separator"); |
| 3275 | output_c_string(p->out, p->separator); |
| 3276 | fprintf(p->out," "); |
| 3277 | output_c_string(p->out, p->newline); |
| 3278 | fprintf(p->out, "\n"); |
| 3279 | fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off"); |
| 3280 | fprintf(p->out,"%9.9s: ","width"); |
| 3281 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 3282 | fprintf(p->out,"%d ",p->colWidth[i]); |
| 3283 | } |
| 3284 | fprintf(p->out,"\n"); |
| 3285 | }else |
| @@ -3394,10 +3544,11 @@ | |
| 3394 | { "reserve", SQLITE_TESTCTRL_RESERVE }, |
| 3395 | { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, |
| 3396 | { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, |
| 3397 | { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, |
| 3398 | { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, |
| 3399 | }; |
| 3400 | int testctrl = -1; |
| 3401 | int rc = 0; |
| 3402 | int i, n; |
| 3403 | open_db(p, 0); |
| @@ -3460,11 +3611,12 @@ | |
| 3460 | } |
| 3461 | break; |
| 3462 | |
| 3463 | /* sqlite3_test_control(int, int) */ |
| 3464 | case SQLITE_TESTCTRL_ASSERT: |
| 3465 | case SQLITE_TESTCTRL_ALWAYS: |
| 3466 | if( nArg==3 ){ |
| 3467 | int opt = booleanValue(azArg[2]); |
| 3468 | rc = sqlite3_test_control(testctrl, opt); |
| 3469 | fprintf(p->out, "%d (0x%08x)\n", rc, rc); |
| 3470 | } else { |
| @@ -3936,10 +4088,11 @@ | |
| 3936 | |
| 3937 | /* |
| 3938 | ** Show available command line options |
| 3939 | */ |
| 3940 | static const char zOptions[] = |
| 3941 | " -bail stop after hitting an error\n" |
| 3942 | " -batch force batch I/O\n" |
| 3943 | " -column set output mode to 'column'\n" |
| 3944 | " -cmd COMMAND run \"COMMAND\" before reading stdin\n" |
| 3945 | " -csv set output mode to 'csv'\n" |
| @@ -3957,15 +4110,15 @@ | |
| 3957 | " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" |
| 3958 | " -mmap N default mmap size set to N\n" |
| 3959 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 3960 | " -multiplex enable the multiplexor VFS\n" |
| 3961 | #endif |
| 3962 | " -newline SEP set newline character(s) for CSV\n" |
| 3963 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 3964 | " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" |
| 3965 | " -scratch SIZE N use N slots of SZ bytes each for scratch memory\n" |
| 3966 | " -separator SEP set output field separator. Default: '|'\n" |
| 3967 | " -stats print memory stats before each finalize\n" |
| 3968 | " -version show SQLite version\n" |
| 3969 | " -vfs NAME use NAME as the default VFS\n" |
| 3970 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 3971 | " -vfstrace enable tracing of all VFS calls\n" |
| @@ -3988,12 +4141,12 @@ | |
| 3988 | ** Initialize the state information in data |
| 3989 | */ |
| 3990 | static void main_init(ShellState *data) { |
| 3991 | memset(data, 0, sizeof(*data)); |
| 3992 | data->mode = MODE_List; |
| 3993 | memcpy(data->separator,"|", 2); |
| 3994 | memcpy(data->newline,"\r\n", 3); |
| 3995 | data->showHeader = 0; |
| 3996 | data->shellFlgs = SHFLG_Lookaside; |
| 3997 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 3998 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 3999 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); |
| @@ -4050,10 +4203,12 @@ | |
| 4050 | fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", |
| 4051 | sqlite3_sourceid(), SQLITE_SOURCE_ID); |
| 4052 | exit(1); |
| 4053 | } |
| 4054 | #endif |
| 4055 | Argv0 = argv[0]; |
| 4056 | main_init(&data); |
| 4057 | stdin_is_interactive = isatty(0); |
| 4058 | |
| 4059 | /* Make sure we have a valid signal handler early, before anything |
| @@ -4228,19 +4383,25 @@ | |
| 4228 | data.mode = MODE_Line; |
| 4229 | }else if( strcmp(z,"-column")==0 ){ |
| 4230 | data.mode = MODE_Column; |
| 4231 | }else if( strcmp(z,"-csv")==0 ){ |
| 4232 | data.mode = MODE_Csv; |
| 4233 | memcpy(data.separator,",",2); |
| 4234 | }else if( strcmp(z,"-separator")==0 ){ |
| 4235 | sqlite3_snprintf(sizeof(data.separator), data.separator, |
| 4236 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4237 | }else if( strcmp(z,"-newline")==0 ){ |
| 4238 | sqlite3_snprintf(sizeof(data.newline), data.newline, |
| 4239 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4240 | }else if( strcmp(z,"-nullvalue")==0 ){ |
| 4241 | sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, |
| 4242 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4243 | }else if( strcmp(z,"-header")==0 ){ |
| 4244 | data.showHeader = 1; |
| 4245 | }else if( strcmp(z,"-noheader")==0 ){ |
| 4246 | data.showHeader = 0; |
| @@ -4356,11 +4517,11 @@ | |
| 4356 | nHistory = strlen30(zHome) + 20; |
| 4357 | if( (zHistory = malloc(nHistory))!=0 ){ |
| 4358 | sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); |
| 4359 | } |
| 4360 | } |
| 4361 | #if defined(HAVE_READLINE) |
| 4362 | if( zHistory ) read_history(zHistory); |
| 4363 | #endif |
| 4364 | rc = process_input(&data, 0); |
| 4365 | if( zHistory ){ |
| 4366 | stifle_history(100); |
| 4367 |
| --- src/shell.c | |
| +++ src/shell.c | |
| @@ -15,10 +15,17 @@ | |
| 15 | #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS) |
| 16 | /* This needs to come before any includes for MSVC compiler */ |
| 17 | #define _CRT_SECURE_NO_WARNINGS |
| 18 | #endif |
| 19 | |
| 20 | /* |
| 21 | ** If requested, include the SQLite compiler options file for MSVC. |
| 22 | */ |
| 23 | #if defined(INCLUDE_MSVC_H) |
| 24 | #include "msvc.h" |
| 25 | #endif |
| 26 | |
| 27 | /* |
| 28 | ** Enable large-file support for fopen() and friends on unix. |
| 29 | */ |
| 30 | #ifndef SQLITE_DISABLE_LFS |
| 31 | # define _LARGE_FILE 1 |
| @@ -46,21 +53,20 @@ | |
| 53 | # endif |
| 54 | # include <unistd.h> |
| 55 | # include <sys/types.h> |
| 56 | #endif |
| 57 | |
| 58 | #if HAVE_READLINE |
| 59 | # include <readline/readline.h> |
| 60 | # include <readline/history.h> |
| 61 | #endif |
| 62 | #if HAVE_EDITLINE |
| 63 | # undef HAVE_READLINE |
| 64 | # define HAVE_READLINE 1 |
| 65 | # include <editline/readline.h> |
| 66 | #endif |
| 67 | #if !HAVE_READLINE |
| 68 | # define add_history(X) |
| 69 | # define read_history(X) |
| 70 | # define write_history(X) |
| 71 | # define stifle_history(X) |
| 72 | #endif |
| @@ -97,10 +103,30 @@ | |
| 103 | |
| 104 | /* ctype macros that work with signed characters */ |
| 105 | #define IsSpace(X) isspace((unsigned char)X) |
| 106 | #define IsDigit(X) isdigit((unsigned char)X) |
| 107 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 108 | |
| 109 | /* On Windows, we normally run with output mode of TEXT so that \n characters |
| 110 | ** are automatically translated into \r\n. However, this behavior needs |
| 111 | ** to be disabled in some cases (ex: when generating CSV output and when |
| 112 | ** rendering quoted strings that contain \n characters). The following |
| 113 | ** routines take care of that. |
| 114 | */ |
| 115 | #if defined(_WIN32) || defined(WIN32) |
| 116 | static void setBinaryMode(FILE *out){ |
| 117 | fflush(out); |
| 118 | _setmode(_fileno(out), _O_BINARY); |
| 119 | } |
| 120 | static void setTextMode(FILE *out){ |
| 121 | fflush(out); |
| 122 | _setmode(_fileno(out), _O_TEXT); |
| 123 | } |
| 124 | #else |
| 125 | # define setBinaryMode(X) |
| 126 | # define setTextMode(X) |
| 127 | #endif |
| 128 | |
| 129 | |
| 130 | /* True if the timer is enabled */ |
| 131 | static int enableTimer = 0; |
| 132 | |
| @@ -423,11 +449,11 @@ | |
| 449 | char *zResult; |
| 450 | if( in!=0 ){ |
| 451 | zResult = local_getline(zPrior, in); |
| 452 | }else{ |
| 453 | zPrompt = isContinuation ? continuePrompt : mainPrompt; |
| 454 | #if HAVE_READLINE |
| 455 | free(zPrior); |
| 456 | zResult = readline(zPrompt); |
| 457 | if( zResult && *zResult ) add_history(zResult); |
| 458 | #else |
| 459 | printf("%s", zPrompt); |
| @@ -469,15 +495,15 @@ | |
| 495 | int mode; /* An output mode setting */ |
| 496 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 497 | int showHeader; /* True to show column names in List or Column mode */ |
| 498 | unsigned shellFlgs; /* Various flags */ |
| 499 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 500 | char colSeparator[20]; /* Column separator character for several modes */ |
| 501 | char rowSeparator[20]; /* Row separator character for MODE_Ascii */ |
| 502 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 503 | int actualWidth[100]; /* Actual width of each column */ |
| 504 | char nullValue[20]; /* The text to print when a NULL comes back from |
| 505 | ** the database */ |
| 506 | SavedModeInfo normalMode;/* Holds the mode just before .explain ON */ |
| 507 | char outfile[FILENAME_MAX]; /* Filename for *out */ |
| 508 | const char *zDbFilename; /* name of the database file */ |
| 509 | char *zFreeOnClose; /* Filename to free when closing */ |
| @@ -506,10 +532,11 @@ | |
| 532 | #define MODE_Html 4 /* Generate an XHTML table */ |
| 533 | #define MODE_Insert 5 /* Generate SQL "insert" statements */ |
| 534 | #define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ |
| 535 | #define MODE_Csv 7 /* Quote strings, numbers are plain */ |
| 536 | #define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ |
| 537 | #define MODE_Ascii 9 /* Use ASCII unit and record separators (0x1F/0x1E) */ |
| 538 | |
| 539 | static const char *modeDescr[] = { |
| 540 | "line", |
| 541 | "column", |
| 542 | "list", |
| @@ -517,12 +544,26 @@ | |
| 544 | "html", |
| 545 | "insert", |
| 546 | "tcl", |
| 547 | "csv", |
| 548 | "explain", |
| 549 | "ascii", |
| 550 | }; |
| 551 | |
| 552 | /* |
| 553 | ** These are the column/row/line separators used by the various |
| 554 | ** import/export modes. |
| 555 | */ |
| 556 | #define SEP_Column "|" |
| 557 | #define SEP_Row "\n" |
| 558 | #define SEP_Tab "\t" |
| 559 | #define SEP_Space " " |
| 560 | #define SEP_Comma "," |
| 561 | #define SEP_CrLf "\r\n" |
| 562 | #define SEP_Unit "\x1F" |
| 563 | #define SEP_Record "\x1E" |
| 564 | |
| 565 | /* |
| 566 | ** Number of elements in an array |
| 567 | */ |
| 568 | #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) |
| 569 | |
| @@ -561,10 +602,11 @@ | |
| 602 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 603 | */ |
| 604 | static void output_quoted_string(FILE *out, const char *z){ |
| 605 | int i; |
| 606 | int nSingle = 0; |
| 607 | setBinaryMode(out); |
| 608 | for(i=0; z[i]; i++){ |
| 609 | if( z[i]=='\'' ) nSingle++; |
| 610 | } |
| 611 | if( nSingle==0 ){ |
| 612 | fprintf(out,"'%s'",z); |
| @@ -583,10 +625,11 @@ | |
| 625 | break; |
| 626 | } |
| 627 | } |
| 628 | fprintf(out,"'"); |
| 629 | } |
| 630 | setTextMode(out); |
| 631 | } |
| 632 | |
| 633 | /* |
| 634 | ** Output the given string as a quoted according to C or TCL quoting rules. |
| 635 | */ |
| @@ -675,26 +718,26 @@ | |
| 718 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 719 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 720 | }; |
| 721 | |
| 722 | /* |
| 723 | ** Output a single term of CSV. Actually, p->colSeparator is used for |
| 724 | ** the separator, which may or may not be a comma. p->nullValue is |
| 725 | ** the null value. Strings are quoted if necessary. The separator |
| 726 | ** is only issued if bSep is true. |
| 727 | */ |
| 728 | static void output_csv(ShellState *p, const char *z, int bSep){ |
| 729 | FILE *out = p->out; |
| 730 | if( z==0 ){ |
| 731 | fprintf(out,"%s",p->nullValue); |
| 732 | }else{ |
| 733 | int i; |
| 734 | int nSep = strlen30(p->colSeparator); |
| 735 | for(i=0; z[i]; i++){ |
| 736 | if( needCsvQuote[((unsigned char*)z)[i]] |
| 737 | || (z[i]==p->colSeparator[0] && |
| 738 | (nSep==1 || memcmp(z, p->colSeparator, nSep)==0)) ){ |
| 739 | i = 0; |
| 740 | break; |
| 741 | } |
| 742 | } |
| 743 | if( i==0 ){ |
| @@ -707,11 +750,11 @@ | |
| 750 | }else{ |
| 751 | fprintf(out, "%s", z); |
| 752 | } |
| 753 | } |
| 754 | if( bSep ){ |
| 755 | fprintf(p->out, "%s", p->colSeparator); |
| 756 | } |
| 757 | } |
| 758 | |
| 759 | #ifdef SIGINT |
| 760 | /* |
| @@ -745,14 +788,14 @@ | |
| 788 | if( azArg==0 ) break; |
| 789 | for(i=0; i<nArg; i++){ |
| 790 | int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 791 | if( len>w ) w = len; |
| 792 | } |
| 793 | if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator); |
| 794 | for(i=0; i<nArg; i++){ |
| 795 | fprintf(p->out,"%*s = %s%s", w, azCol[i], |
| 796 | azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); |
| 797 | } |
| 798 | break; |
| 799 | } |
| 800 | case MODE_Explain: |
| 801 | case MODE_Column: { |
| @@ -765,21 +808,23 @@ | |
| 808 | w = 0; |
| 809 | } |
| 810 | if( w==0 ){ |
| 811 | w = strlen30(azCol[i] ? azCol[i] : ""); |
| 812 | if( w<10 ) w = 10; |
| 813 | n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullValue); |
| 814 | if( w<n ) w = n; |
| 815 | } |
| 816 | if( i<ArraySize(p->actualWidth) ){ |
| 817 | p->actualWidth[i] = w; |
| 818 | } |
| 819 | if( p->showHeader ){ |
| 820 | if( w<0 ){ |
| 821 | fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], |
| 822 | i==nArg-1 ? p->rowSeparator : " "); |
| 823 | }else{ |
| 824 | fprintf(p->out,"%-*.*s%s",w,w,azCol[i], |
| 825 | i==nArg-1 ? p->rowSeparator : " "); |
| 826 | } |
| 827 | } |
| 828 | } |
| 829 | if( p->showHeader ){ |
| 830 | for(i=0; i<nArg; i++){ |
| @@ -790,11 +835,11 @@ | |
| 835 | }else{ |
| 836 | w = 10; |
| 837 | } |
| 838 | fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" |
| 839 | "----------------------------------------------------------", |
| 840 | i==nArg-1 ? p->rowSeparator : " "); |
| 841 | } |
| 842 | } |
| 843 | } |
| 844 | if( azArg==0 ) break; |
| 845 | for(i=0; i<nArg; i++){ |
| @@ -813,36 +858,39 @@ | |
| 858 | } |
| 859 | p->iIndent++; |
| 860 | } |
| 861 | if( w<0 ){ |
| 862 | fprintf(p->out,"%*.*s%s",-w,-w, |
| 863 | azArg[i] ? azArg[i] : p->nullValue, |
| 864 | i==nArg-1 ? p->rowSeparator : " "); |
| 865 | }else{ |
| 866 | fprintf(p->out,"%-*.*s%s",w,w, |
| 867 | azArg[i] ? azArg[i] : p->nullValue, |
| 868 | i==nArg-1 ? p->rowSeparator : " "); |
| 869 | } |
| 870 | } |
| 871 | break; |
| 872 | } |
| 873 | case MODE_Semi: |
| 874 | case MODE_List: { |
| 875 | if( p->cnt++==0 && p->showHeader ){ |
| 876 | for(i=0; i<nArg; i++){ |
| 877 | fprintf(p->out,"%s%s",azCol[i], |
| 878 | i==nArg-1 ? p->rowSeparator : p->colSeparator); |
| 879 | } |
| 880 | } |
| 881 | if( azArg==0 ) break; |
| 882 | for(i=0; i<nArg; i++){ |
| 883 | char *z = azArg[i]; |
| 884 | if( z==0 ) z = p->nullValue; |
| 885 | fprintf(p->out, "%s", z); |
| 886 | if( i<nArg-1 ){ |
| 887 | fprintf(p->out, "%s", p->colSeparator); |
| 888 | }else if( p->mode==MODE_Semi ){ |
| 889 | fprintf(p->out, ";%s", p->rowSeparator); |
| 890 | }else{ |
| 891 | fprintf(p->out, "%s", p->rowSeparator); |
| 892 | } |
| 893 | } |
| 894 | break; |
| 895 | } |
| 896 | case MODE_Html: { |
| @@ -857,53 +905,47 @@ | |
| 905 | } |
| 906 | if( azArg==0 ) break; |
| 907 | fprintf(p->out,"<TR>"); |
| 908 | for(i=0; i<nArg; i++){ |
| 909 | fprintf(p->out,"<TD>"); |
| 910 | output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); |
| 911 | fprintf(p->out,"</TD>\n"); |
| 912 | } |
| 913 | fprintf(p->out,"</TR>\n"); |
| 914 | break; |
| 915 | } |
| 916 | case MODE_Tcl: { |
| 917 | if( p->cnt++==0 && p->showHeader ){ |
| 918 | for(i=0; i<nArg; i++){ |
| 919 | output_c_string(p->out,azCol[i] ? azCol[i] : ""); |
| 920 | if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator); |
| 921 | } |
| 922 | fprintf(p->out, "%s", p->rowSeparator); |
| 923 | } |
| 924 | if( azArg==0 ) break; |
| 925 | for(i=0; i<nArg; i++){ |
| 926 | output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); |
| 927 | if(i<nArg-1) fprintf(p->out, "%s", p->colSeparator); |
| 928 | } |
| 929 | fprintf(p->out, "%s", p->rowSeparator); |
| 930 | break; |
| 931 | } |
| 932 | case MODE_Csv: { |
| 933 | setBinaryMode(p->out); |
| 934 | if( p->cnt++==0 && p->showHeader ){ |
| 935 | for(i=0; i<nArg; i++){ |
| 936 | output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); |
| 937 | } |
| 938 | fprintf(p->out, "%s", p->rowSeparator); |
| 939 | } |
| 940 | if( nArg>0 ){ |
| 941 | for(i=0; i<nArg; i++){ |
| 942 | output_csv(p, azArg[i], i<nArg-1); |
| 943 | } |
| 944 | fprintf(p->out, "%s", p->rowSeparator); |
| 945 | } |
| 946 | setTextMode(p->out); |
| 947 | break; |
| 948 | } |
| 949 | case MODE_Insert: { |
| 950 | p->cnt++; |
| 951 | if( azArg==0 ) break; |
| @@ -930,10 +972,26 @@ | |
| 972 | output_quoted_string(p->out, azArg[i]); |
| 973 | } |
| 974 | } |
| 975 | fprintf(p->out,");\n"); |
| 976 | break; |
| 977 | } |
| 978 | case MODE_Ascii: { |
| 979 | if( p->cnt++==0 && p->showHeader ){ |
| 980 | for(i=0; i<nArg; i++){ |
| 981 | if( i>0 ) fprintf(p->out, "%s", p->colSeparator); |
| 982 | fprintf(p->out,"%s",azCol[i] ? azCol[i] : ""); |
| 983 | } |
| 984 | fprintf(p->out, "%s", p->rowSeparator); |
| 985 | } |
| 986 | if( azArg==0 ) break; |
| 987 | for(i=0; i<nArg; i++){ |
| 988 | if( i>0 ) fprintf(p->out, "%s", p->colSeparator); |
| 989 | fprintf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); |
| 990 | } |
| 991 | fprintf(p->out, "%s", p->rowSeparator); |
| 992 | break; |
| 993 | } |
| 994 | } |
| 995 | return 0; |
| 996 | } |
| 997 | |
| @@ -1428,10 +1486,21 @@ | |
| 1486 | } |
| 1487 | } |
| 1488 | sqlite3_finalize(pExplain); |
| 1489 | sqlite3_free(zEQP); |
| 1490 | } |
| 1491 | |
| 1492 | #if USE_SYSTEM_SQLITE+0==1 |
| 1493 | /* Output TESTCTRL_EXPLAIN text of requested */ |
| 1494 | if( pArg && pArg->mode==MODE_Explain && sqlite3_libversion_number()<3008007 ){ |
| 1495 | const char *zExplain = 0; |
| 1496 | sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, pStmt, &zExplain); |
| 1497 | if( zExplain && zExplain[0] ){ |
| 1498 | fprintf(pArg->out, "%s", zExplain); |
| 1499 | } |
| 1500 | } |
| 1501 | #endif |
| 1502 | |
| 1503 | /* If the shell is currently in ".explain" mode, gather the extra |
| 1504 | ** data required to add indents to the output.*/ |
| 1505 | if( pArg && pArg->mode==MODE_Explain ){ |
| 1506 | explain_data_prepare(pArg, pStmt); |
| @@ -1696,16 +1765,17 @@ | |
| 1765 | #ifndef SQLITE_OMIT_LOAD_EXTENSION |
| 1766 | ".load FILE ?ENTRY? Load an extension library\n" |
| 1767 | #endif |
| 1768 | ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" |
| 1769 | ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" |
| 1770 | " ascii Columns/rows delimited by 0x1F and 0x1E\n" |
| 1771 | " csv Comma-separated values\n" |
| 1772 | " column Left-aligned columns. (See .width)\n" |
| 1773 | " html HTML <table> code\n" |
| 1774 | " insert SQL insert statements for TABLE\n" |
| 1775 | " line One value per line\n" |
| 1776 | " list Values delimited by .separator strings\n" |
| 1777 | " tabs Tab-separated values\n" |
| 1778 | " tcl TCL list elements\n" |
| 1779 | ".nullvalue STRING Use STRING in place of NULL values\n" |
| 1780 | ".once FILENAME Output for the next SQL command only to FILENAME\n" |
| 1781 | ".open ?FILENAME? Close existing database and reopen FILENAME\n" |
| @@ -1718,12 +1788,12 @@ | |
| 1788 | ".save FILE Write in-memory database into FILE\n" |
| 1789 | ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off\n" |
| 1790 | ".schema ?TABLE? Show the CREATE statements\n" |
| 1791 | " If TABLE specified, only show tables matching\n" |
| 1792 | " LIKE pattern TABLE.\n" |
| 1793 | ".separator COL ?ROW? Change the column separator and optionally the row\n" |
| 1794 | " separator for both the output mode and .import\n" |
| 1795 | ".shell CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1796 | ".show Show the current values for various settings\n" |
| 1797 | ".stats on|off Turn stats on or off\n" |
| 1798 | ".system CMD ARGS... Run CMD ARGS... in a system shell\n" |
| 1799 | ".tables ?TABLE? List names of tables\n" |
| @@ -2000,26 +2070,27 @@ | |
| 2070 | static int nCall = 0; |
| 2071 | nCall++; |
| 2072 | } |
| 2073 | |
| 2074 | /* |
| 2075 | ** An object used to read a CSV and other files for import. |
| 2076 | */ |
| 2077 | typedef struct ImportCtx ImportCtx; |
| 2078 | struct ImportCtx { |
| 2079 | const char *zFile; /* Name of the input file */ |
| 2080 | FILE *in; /* Read the CSV text from this input stream */ |
| 2081 | char *z; /* Accumulated text for a field */ |
| 2082 | int n; /* Number of bytes in z */ |
| 2083 | int nAlloc; /* Space allocated for z[] */ |
| 2084 | int nLine; /* Current line number */ |
| 2085 | int cTerm; /* Character that terminated the most recent field */ |
| 2086 | int cColSep; /* The column separator character. (Usually ",") */ |
| 2087 | int cRowSep; /* The row separator character. (Usually "\n") */ |
| 2088 | }; |
| 2089 | |
| 2090 | /* Append a single byte to z[] */ |
| 2091 | static void import_append_char(ImportCtx *p, int c){ |
| 2092 | if( p->n+1>=p->nAlloc ){ |
| 2093 | p->nAlloc += p->nAlloc + 100; |
| 2094 | p->z = sqlite3_realloc(p->z, p->nAlloc); |
| 2095 | if( p->z==0 ){ |
| 2096 | fprintf(stderr, "out of memory\n"); |
| @@ -2033,41 +2104,44 @@ | |
| 2104 | ** with the option of having a separator other than ",". |
| 2105 | ** |
| 2106 | ** + Input comes from p->in. |
| 2107 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 2108 | ** from sqlite3_malloc(). |
| 2109 | ** + Use p->cSep as the column separator. The default is ",". |
| 2110 | ** + Use p->rSep as the row separator. The default is "\n". |
| 2111 | ** + Keep track of the line number in p->nLine. |
| 2112 | ** + Store the character that terminates the field in p->cTerm. Store |
| 2113 | ** EOF on end-of-file. |
| 2114 | ** + Report syntax errors on stderr |
| 2115 | */ |
| 2116 | static char *csv_read_one_field(ImportCtx *p){ |
| 2117 | int c; |
| 2118 | int cSep = p->cColSep; |
| 2119 | int rSep = p->cRowSep; |
| 2120 | p->n = 0; |
| 2121 | c = fgetc(p->in); |
| 2122 | if( c==EOF || seenInterrupt ){ |
| 2123 | p->cTerm = EOF; |
| 2124 | return 0; |
| 2125 | } |
| 2126 | if( c=='"' ){ |
| 2127 | int pc, ppc; |
| 2128 | int startLine = p->nLine; |
| 2129 | int cQuote = c; |
| 2130 | pc = ppc = 0; |
| 2131 | while( 1 ){ |
| 2132 | c = fgetc(p->in); |
| 2133 | if( c==rSep ) p->nLine++; |
| 2134 | if( c==cQuote ){ |
| 2135 | if( pc==cQuote ){ |
| 2136 | pc = 0; |
| 2137 | continue; |
| 2138 | } |
| 2139 | } |
| 2140 | if( (c==cSep && pc==cQuote) |
| 2141 | || (c==rSep && pc==cQuote) |
| 2142 | || (c==rSep && pc=='\r' && ppc==cQuote) |
| 2143 | || (c==EOF && pc==cQuote) |
| 2144 | ){ |
| 2145 | do{ p->n--; }while( p->z[p->n]!=cQuote ); |
| 2146 | p->cTerm = c; |
| 2147 | break; |
| @@ -2077,31 +2151,65 @@ | |
| 2151 | p->zFile, p->nLine, cQuote); |
| 2152 | } |
| 2153 | if( c==EOF ){ |
| 2154 | fprintf(stderr, "%s:%d: unterminated %c-quoted field\n", |
| 2155 | p->zFile, startLine, cQuote); |
| 2156 | p->cTerm = c; |
| 2157 | break; |
| 2158 | } |
| 2159 | import_append_char(p, c); |
| 2160 | ppc = pc; |
| 2161 | pc = c; |
| 2162 | } |
| 2163 | }else{ |
| 2164 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 2165 | import_append_char(p, c); |
| 2166 | c = fgetc(p->in); |
| 2167 | } |
| 2168 | if( c==rSep ){ |
| 2169 | p->nLine++; |
| 2170 | if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--; |
| 2171 | } |
| 2172 | p->cTerm = c; |
| 2173 | } |
| 2174 | if( p->z ) p->z[p->n] = 0; |
| 2175 | return p->z; |
| 2176 | } |
| 2177 | |
| 2178 | /* Read a single field of ASCII delimited text. |
| 2179 | ** |
| 2180 | ** + Input comes from p->in. |
| 2181 | ** + Store results in p->z of length p->n. Space to hold p->z comes |
| 2182 | ** from sqlite3_malloc(). |
| 2183 | ** + Use p->cSep as the column separator. The default is "\x1F". |
| 2184 | ** + Use p->rSep as the row separator. The default is "\x1E". |
| 2185 | ** + Keep track of the row number in p->nLine. |
| 2186 | ** + Store the character that terminates the field in p->cTerm. Store |
| 2187 | ** EOF on end-of-file. |
| 2188 | ** + Report syntax errors on stderr |
| 2189 | */ |
| 2190 | static char *ascii_read_one_field(ImportCtx *p){ |
| 2191 | int c; |
| 2192 | int cSep = p->cColSep; |
| 2193 | int rSep = p->cRowSep; |
| 2194 | p->n = 0; |
| 2195 | c = fgetc(p->in); |
| 2196 | if( c==EOF || seenInterrupt ){ |
| 2197 | p->cTerm = EOF; |
| 2198 | return 0; |
| 2199 | } |
| 2200 | while( c!=EOF && c!=cSep && c!=rSep ){ |
| 2201 | import_append_char(p, c); |
| 2202 | c = fgetc(p->in); |
| 2203 | } |
| 2204 | if( c==rSep ){ |
| 2205 | p->nLine++; |
| 2206 | } |
| 2207 | p->cTerm = c; |
| 2208 | if( p->z ) p->z[p->n] = 0; |
| 2209 | return p->z; |
| 2210 | } |
| 2211 | |
| 2212 | /* |
| 2213 | ** Try to transfer data for table zTable. If an error is seen while |
| 2214 | ** moving forward, try to go backwards. The backwards movement won't |
| 2215 | ** work for WITHOUT ROWID tables. |
| @@ -2653,100 +2761,125 @@ | |
| 2761 | sqlite3_stmt *pStmt = NULL; /* A statement */ |
| 2762 | int nCol; /* Number of columns in the table */ |
| 2763 | int nByte; /* Number of bytes in an SQL string */ |
| 2764 | int i, j; /* Loop counters */ |
| 2765 | int needCommit; /* True to COMMIT or ROLLBACK at end */ |
| 2766 | int nSep; /* Number of bytes in p->colSeparator[] */ |
| 2767 | char *zSql; /* An SQL statement */ |
| 2768 | ImportCtx sCtx; /* Reader context */ |
| 2769 | char *(*xRead)(ImportCtx*); /* Procedure to read one value */ |
| 2770 | int (*xCloser)(FILE*); /* Procedure to close th3 connection */ |
| 2771 | |
| 2772 | if( nArg!=3 ){ |
| 2773 | fprintf(stderr, "Usage: .import FILE TABLE\n"); |
| 2774 | goto meta_command_exit; |
| 2775 | } |
| 2776 | zFile = azArg[1]; |
| 2777 | zTable = azArg[2]; |
| 2778 | seenInterrupt = 0; |
| 2779 | memset(&sCtx, 0, sizeof(sCtx)); |
| 2780 | open_db(p, 0); |
| 2781 | nSep = strlen30(p->colSeparator); |
| 2782 | if( nSep==0 ){ |
| 2783 | fprintf(stderr, "Error: non-null column separator required for import\n"); |
| 2784 | return 1; |
| 2785 | } |
| 2786 | if( nSep>1 ){ |
| 2787 | fprintf(stderr, "Error: multi-character column separators not allowed" |
| 2788 | " for import\n"); |
| 2789 | return 1; |
| 2790 | } |
| 2791 | nSep = strlen30(p->rowSeparator); |
| 2792 | if( nSep==0 ){ |
| 2793 | fprintf(stderr, "Error: non-null row separator required for import\n"); |
| 2794 | return 1; |
| 2795 | } |
| 2796 | if( nSep==2 && p->mode==MODE_Csv && strcmp(p->rowSeparator, SEP_CrLf)==0 ){ |
| 2797 | /* When importing CSV (only), if the row separator is set to the |
| 2798 | ** default output row separator, change it to the default input |
| 2799 | ** row separator. This avoids having to maintain different input |
| 2800 | ** and output row separators. */ |
| 2801 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 2802 | nSep = strlen30(p->rowSeparator); |
| 2803 | } |
| 2804 | if( nSep>1 ){ |
| 2805 | fprintf(stderr, "Error: multi-character row separators not allowed" |
| 2806 | " for import\n"); |
| 2807 | return 1; |
| 2808 | } |
| 2809 | sCtx.zFile = zFile; |
| 2810 | sCtx.nLine = 1; |
| 2811 | if( sCtx.zFile[0]=='|' ){ |
| 2812 | sCtx.in = popen(sCtx.zFile+1, "r"); |
| 2813 | sCtx.zFile = "<pipe>"; |
| 2814 | xCloser = pclose; |
| 2815 | }else{ |
| 2816 | sCtx.in = fopen(sCtx.zFile, "rb"); |
| 2817 | xCloser = fclose; |
| 2818 | } |
| 2819 | if( p->mode==MODE_Ascii ){ |
| 2820 | xRead = ascii_read_one_field; |
| 2821 | }else{ |
| 2822 | xRead = csv_read_one_field; |
| 2823 | } |
| 2824 | if( sCtx.in==0 ){ |
| 2825 | fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); |
| 2826 | return 1; |
| 2827 | } |
| 2828 | sCtx.cColSep = p->colSeparator[0]; |
| 2829 | sCtx.cRowSep = p->rowSeparator[0]; |
| 2830 | zSql = sqlite3_mprintf("SELECT * FROM %s", zTable); |
| 2831 | if( zSql==0 ){ |
| 2832 | fprintf(stderr, "Error: out of memory\n"); |
| 2833 | xCloser(sCtx.in); |
| 2834 | return 1; |
| 2835 | } |
| 2836 | nByte = strlen30(zSql); |
| 2837 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 2838 | import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ |
| 2839 | if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){ |
| 2840 | char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable); |
| 2841 | char cSep = '('; |
| 2842 | while( xRead(&sCtx) ){ |
| 2843 | zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCtx.z); |
| 2844 | cSep = ','; |
| 2845 | if( sCtx.cTerm!=sCtx.cColSep ) break; |
| 2846 | } |
| 2847 | if( cSep=='(' ){ |
| 2848 | sqlite3_free(zCreate); |
| 2849 | sqlite3_free(sCtx.z); |
| 2850 | xCloser(sCtx.in); |
| 2851 | fprintf(stderr,"%s: empty file\n", sCtx.zFile); |
| 2852 | return 1; |
| 2853 | } |
| 2854 | zCreate = sqlite3_mprintf("%z\n)", zCreate); |
| 2855 | rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
| 2856 | sqlite3_free(zCreate); |
| 2857 | if( rc ){ |
| 2858 | fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable, |
| 2859 | sqlite3_errmsg(db)); |
| 2860 | sqlite3_free(sCtx.z); |
| 2861 | xCloser(sCtx.in); |
| 2862 | return 1; |
| 2863 | } |
| 2864 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 2865 | } |
| 2866 | sqlite3_free(zSql); |
| 2867 | if( rc ){ |
| 2868 | if (pStmt) sqlite3_finalize(pStmt); |
| 2869 | fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db)); |
| 2870 | xCloser(sCtx.in); |
| 2871 | return 1; |
| 2872 | } |
| 2873 | nCol = sqlite3_column_count(pStmt); |
| 2874 | sqlite3_finalize(pStmt); |
| 2875 | pStmt = 0; |
| 2876 | if( nCol==0 ) return 0; /* no columns, no error */ |
| 2877 | zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 ); |
| 2878 | if( zSql==0 ){ |
| 2879 | fprintf(stderr, "Error: out of memory\n"); |
| 2880 | xCloser(sCtx.in); |
| 2881 | return 1; |
| 2882 | } |
| 2883 | sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); |
| 2884 | j = strlen30(zSql); |
| 2885 | for(i=1; i<nCol; i++){ |
| @@ -2758,50 +2891,60 @@ | |
| 2891 | rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
| 2892 | sqlite3_free(zSql); |
| 2893 | if( rc ){ |
| 2894 | fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db)); |
| 2895 | if (pStmt) sqlite3_finalize(pStmt); |
| 2896 | xCloser(sCtx.in); |
| 2897 | return 1; |
| 2898 | } |
| 2899 | needCommit = sqlite3_get_autocommit(db); |
| 2900 | if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0); |
| 2901 | do{ |
| 2902 | int startLine = sCtx.nLine; |
| 2903 | for(i=0; i<nCol; i++){ |
| 2904 | char *z = xRead(&sCtx); |
| 2905 | /* |
| 2906 | ** Did we reach end-of-file before finding any columns? |
| 2907 | ** If so, stop instead of NULL filling the remaining columns. |
| 2908 | */ |
| 2909 | if( z==0 && i==0 ) break; |
| 2910 | /* |
| 2911 | ** Did we reach end-of-file OR end-of-line before finding any |
| 2912 | ** columns in ASCII mode? If so, stop instead of NULL filling |
| 2913 | ** the remaining columns. |
| 2914 | */ |
| 2915 | if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; |
| 2916 | sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
| 2917 | if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
| 2918 | fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
| 2919 | "filling the rest with NULL\n", |
| 2920 | sCtx.zFile, startLine, nCol, i+1); |
| 2921 | i++; |
| 2922 | while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
| 2923 | } |
| 2924 | } |
| 2925 | if( sCtx.cTerm==sCtx.cColSep ){ |
| 2926 | do{ |
| 2927 | xRead(&sCtx); |
| 2928 | i++; |
| 2929 | }while( sCtx.cTerm==sCtx.cColSep ); |
| 2930 | fprintf(stderr, "%s:%d: expected %d columns but found %d - " |
| 2931 | "extras ignored\n", |
| 2932 | sCtx.zFile, startLine, nCol, i); |
| 2933 | } |
| 2934 | if( i>=nCol ){ |
| 2935 | sqlite3_step(pStmt); |
| 2936 | rc = sqlite3_reset(pStmt); |
| 2937 | if( rc!=SQLITE_OK ){ |
| 2938 | fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, |
| 2939 | sqlite3_errmsg(db)); |
| 2940 | } |
| 2941 | } |
| 2942 | }while( sCtx.cTerm!=EOF ); |
| 2943 | |
| 2944 | xCloser(sCtx.in); |
| 2945 | sqlite3_free(sCtx.z); |
| 2946 | sqlite3_finalize(pStmt); |
| 2947 | if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0); |
| 2948 | }else |
| 2949 | |
| 2950 | if( c=='i' && strncmp(azArg[0], "indices", n)==0 ){ |
| @@ -2915,32 +3058,36 @@ | |
| 3058 | p->mode = MODE_List; |
| 3059 | }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){ |
| 3060 | p->mode = MODE_Html; |
| 3061 | }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){ |
| 3062 | p->mode = MODE_Tcl; |
| 3063 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space); |
| 3064 | }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){ |
| 3065 | p->mode = MODE_Csv; |
| 3066 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 3067 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 3068 | }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){ |
| 3069 | p->mode = MODE_List; |
| 3070 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); |
| 3071 | }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ |
| 3072 | p->mode = MODE_Insert; |
| 3073 | set_table_name(p, nArg>=3 ? azArg[2] : "table"); |
| 3074 | }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ |
| 3075 | p->mode = MODE_Ascii; |
| 3076 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); |
| 3077 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); |
| 3078 | }else { |
| 3079 | fprintf(stderr,"Error: mode should be one of: " |
| 3080 | "ascii column csv html insert line list tabs tcl\n"); |
| 3081 | rc = 1; |
| 3082 | } |
| 3083 | }else |
| 3084 | |
| 3085 | if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){ |
| 3086 | if( nArg==2 ){ |
| 3087 | sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, |
| 3088 | "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); |
| 3089 | }else{ |
| 3090 | fprintf(stderr, "Usage: .nullvalue STRING\n"); |
| 3091 | rc = 1; |
| 3092 | } |
| 3093 | }else |
| @@ -3191,11 +3338,11 @@ | |
| 3338 | |
| 3339 | |
| 3340 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) |
| 3341 | if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){ |
| 3342 | extern int sqlite3SelectTrace; |
| 3343 | sqlite3SelectTrace = integerValue(azArg[1]); |
| 3344 | }else |
| 3345 | #endif |
| 3346 | |
| 3347 | |
| 3348 | #ifdef SQLITE_DEBUG |
| @@ -3221,18 +3368,20 @@ | |
| 3368 | }else |
| 3369 | #endif |
| 3370 | |
| 3371 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){ |
| 3372 | if( nArg<2 || nArg>3 ){ |
| 3373 | fprintf(stderr, "Usage: .separator COL ?ROW?\n"); |
| 3374 | rc = 1; |
| 3375 | } |
| 3376 | if( nArg>=2 ){ |
| 3377 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, |
| 3378 | "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]); |
| 3379 | } |
| 3380 | if( nArg>=3 ){ |
| 3381 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, |
| 3382 | "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]); |
| 3383 | } |
| 3384 | }else |
| 3385 | |
| 3386 | if( c=='s' |
| 3387 | && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0) |
| @@ -3259,27 +3408,28 @@ | |
| 3408 | if( nArg!=1 ){ |
| 3409 | fprintf(stderr, "Usage: .show\n"); |
| 3410 | rc = 1; |
| 3411 | goto meta_command_exit; |
| 3412 | } |
| 3413 | fprintf(p->out,"%12.12s: %s\n","echo", p->echoOn ? "on" : "off"); |
| 3414 | fprintf(p->out,"%12.12s: %s\n","eqp", p->autoEQP ? "on" : "off"); |
| 3415 | fprintf(p->out,"%9.9s: %s\n","explain", p->normalMode.valid ? "on" :"off"); |
| 3416 | fprintf(p->out,"%12.12s: %s\n","headers", p->showHeader ? "on" : "off"); |
| 3417 | fprintf(p->out,"%12.12s: %s\n","mode", modeDescr[p->mode]); |
| 3418 | fprintf(p->out,"%12.12s: ", "nullvalue"); |
| 3419 | output_c_string(p->out, p->nullValue); |
| 3420 | fprintf(p->out, "\n"); |
| 3421 | fprintf(p->out,"%12.12s: %s\n","output", |
| 3422 | strlen30(p->outfile) ? p->outfile : "stdout"); |
| 3423 | fprintf(p->out,"%12.12s: ", "colseparator"); |
| 3424 | output_c_string(p->out, p->colSeparator); |
| 3425 | fprintf(p->out, "\n"); |
| 3426 | fprintf(p->out,"%12.12s: ", "rowseparator"); |
| 3427 | output_c_string(p->out, p->rowSeparator); |
| 3428 | fprintf(p->out, "\n"); |
| 3429 | fprintf(p->out,"%12.12s: %s\n","stats", p->statsOn ? "on" : "off"); |
| 3430 | fprintf(p->out,"%12.12s: ","width"); |
| 3431 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) { |
| 3432 | fprintf(p->out,"%d ",p->colWidth[i]); |
| 3433 | } |
| 3434 | fprintf(p->out,"\n"); |
| 3435 | }else |
| @@ -3394,10 +3544,11 @@ | |
| 3544 | { "reserve", SQLITE_TESTCTRL_RESERVE }, |
| 3545 | { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, |
| 3546 | { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, |
| 3547 | { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, |
| 3548 | { "byteorder", SQLITE_TESTCTRL_BYTEORDER }, |
| 3549 | { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT }, |
| 3550 | }; |
| 3551 | int testctrl = -1; |
| 3552 | int rc = 0; |
| 3553 | int i, n; |
| 3554 | open_db(p, 0); |
| @@ -3460,11 +3611,12 @@ | |
| 3611 | } |
| 3612 | break; |
| 3613 | |
| 3614 | /* sqlite3_test_control(int, int) */ |
| 3615 | case SQLITE_TESTCTRL_ASSERT: |
| 3616 | case SQLITE_TESTCTRL_ALWAYS: |
| 3617 | case SQLITE_TESTCTRL_NEVER_CORRUPT: |
| 3618 | if( nArg==3 ){ |
| 3619 | int opt = booleanValue(azArg[2]); |
| 3620 | rc = sqlite3_test_control(testctrl, opt); |
| 3621 | fprintf(p->out, "%d (0x%08x)\n", rc, rc); |
| 3622 | } else { |
| @@ -3936,10 +4088,11 @@ | |
| 4088 | |
| 4089 | /* |
| 4090 | ** Show available command line options |
| 4091 | */ |
| 4092 | static const char zOptions[] = |
| 4093 | " -ascii set output mode to 'ascii'\n" |
| 4094 | " -bail stop after hitting an error\n" |
| 4095 | " -batch force batch I/O\n" |
| 4096 | " -column set output mode to 'column'\n" |
| 4097 | " -cmd COMMAND run \"COMMAND\" before reading stdin\n" |
| 4098 | " -csv set output mode to 'csv'\n" |
| @@ -3957,15 +4110,15 @@ | |
| 4110 | " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" |
| 4111 | " -mmap N default mmap size set to N\n" |
| 4112 | #ifdef SQLITE_ENABLE_MULTIPLEX |
| 4113 | " -multiplex enable the multiplexor VFS\n" |
| 4114 | #endif |
| 4115 | " -newline SEP set output row separator. Default: '\\n'\n" |
| 4116 | " -nullvalue TEXT set text string for NULL values. Default ''\n" |
| 4117 | " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" |
| 4118 | " -scratch SIZE N use N slots of SZ bytes each for scratch memory\n" |
| 4119 | " -separator SEP set output column separator. Default: '|'\n" |
| 4120 | " -stats print memory stats before each finalize\n" |
| 4121 | " -version show SQLite version\n" |
| 4122 | " -vfs NAME use NAME as the default VFS\n" |
| 4123 | #ifdef SQLITE_ENABLE_VFSTRACE |
| 4124 | " -vfstrace enable tracing of all VFS calls\n" |
| @@ -3988,12 +4141,12 @@ | |
| 4141 | ** Initialize the state information in data |
| 4142 | */ |
| 4143 | static void main_init(ShellState *data) { |
| 4144 | memset(data, 0, sizeof(*data)); |
| 4145 | data->mode = MODE_List; |
| 4146 | memcpy(data->colSeparator,SEP_Column, 2); |
| 4147 | memcpy(data->rowSeparator,SEP_Row, 2); |
| 4148 | data->showHeader = 0; |
| 4149 | data->shellFlgs = SHFLG_Lookaside; |
| 4150 | sqlite3_config(SQLITE_CONFIG_URI, 1); |
| 4151 | sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); |
| 4152 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); |
| @@ -4050,10 +4203,12 @@ | |
| 4203 | fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", |
| 4204 | sqlite3_sourceid(), SQLITE_SOURCE_ID); |
| 4205 | exit(1); |
| 4206 | } |
| 4207 | #endif |
| 4208 | setBinaryMode(stdin); |
| 4209 | setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ |
| 4210 | Argv0 = argv[0]; |
| 4211 | main_init(&data); |
| 4212 | stdin_is_interactive = isatty(0); |
| 4213 | |
| 4214 | /* Make sure we have a valid signal handler early, before anything |
| @@ -4228,19 +4383,25 @@ | |
| 4383 | data.mode = MODE_Line; |
| 4384 | }else if( strcmp(z,"-column")==0 ){ |
| 4385 | data.mode = MODE_Column; |
| 4386 | }else if( strcmp(z,"-csv")==0 ){ |
| 4387 | data.mode = MODE_Csv; |
| 4388 | memcpy(data.colSeparator,",",2); |
| 4389 | }else if( strcmp(z,"-ascii")==0 ){ |
| 4390 | data.mode = MODE_Ascii; |
| 4391 | sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, |
| 4392 | SEP_Unit); |
| 4393 | sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, |
| 4394 | SEP_Record); |
| 4395 | }else if( strcmp(z,"-separator")==0 ){ |
| 4396 | sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, |
| 4397 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4398 | }else if( strcmp(z,"-newline")==0 ){ |
| 4399 | sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, |
| 4400 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4401 | }else if( strcmp(z,"-nullvalue")==0 ){ |
| 4402 | sqlite3_snprintf(sizeof(data.nullValue), data.nullValue, |
| 4403 | "%s",cmdline_option_value(argc,argv,++i)); |
| 4404 | }else if( strcmp(z,"-header")==0 ){ |
| 4405 | data.showHeader = 1; |
| 4406 | }else if( strcmp(z,"-noheader")==0 ){ |
| 4407 | data.showHeader = 0; |
| @@ -4356,11 +4517,11 @@ | |
| 4517 | nHistory = strlen30(zHome) + 20; |
| 4518 | if( (zHistory = malloc(nHistory))!=0 ){ |
| 4519 | sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome); |
| 4520 | } |
| 4521 | } |
| 4522 | #if HAVE_READLINE |
| 4523 | if( zHistory ) read_history(zHistory); |
| 4524 | #endif |
| 4525 | rc = process_input(&data, 0); |
| 4526 | if( zHistory ){ |
| 4527 | stifle_history(100); |
| 4528 |
+1
| --- src/sitemap.c | ||
| +++ src/sitemap.c | ||
| @@ -27,10 +27,11 @@ | ||
| 27 | 27 | ** Show an incomplete list of web pages offered by the Fossil web engine. |
| 28 | 28 | */ |
| 29 | 29 | void sitemap_page(void){ |
| 30 | 30 | login_check_credentials(); |
| 31 | 31 | style_header("Site Map"); |
| 32 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 32 | 33 | @ <p> |
| 33 | 34 | @ The following links are just a few of the many web-pages available for |
| 34 | 35 | @ this Fossil repository: |
| 35 | 36 | @ </p> |
| 36 | 37 | @ |
| 37 | 38 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -27,10 +27,11 @@ | |
| 27 | ** Show an incomplete list of web pages offered by the Fossil web engine. |
| 28 | */ |
| 29 | void sitemap_page(void){ |
| 30 | login_check_credentials(); |
| 31 | style_header("Site Map"); |
| 32 | @ <p> |
| 33 | @ The following links are just a few of the many web-pages available for |
| 34 | @ this Fossil repository: |
| 35 | @ </p> |
| 36 | @ |
| 37 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -27,10 +27,11 @@ | |
| 27 | ** Show an incomplete list of web pages offered by the Fossil web engine. |
| 28 | */ |
| 29 | void sitemap_page(void){ |
| 30 | login_check_credentials(); |
| 31 | style_header("Site Map"); |
| 32 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 33 | @ <p> |
| 34 | @ The following links are just a few of the many web-pages available for |
| 35 | @ this Fossil repository: |
| 36 | @ </p> |
| 37 | @ |
| 38 |
+196
-1310
| --- src/skins.c | ||
| +++ src/skins.c | ||
| @@ -19,1255 +19,38 @@ | ||
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include <assert.h> |
| 22 | 22 | #include "skins.h" |
| 23 | 23 | |
| 24 | -/* @-comment: ## */ | |
| 25 | -/* | |
| 26 | -** A black-and-white theme with the project title in a bar across the top | |
| 27 | -** and no logo image. | |
| 28 | -*/ | |
| 29 | -static const char zBuiltinSkin1[] = | |
| 30 | -@ REPLACE INTO config(name,mtime,value) | |
| 31 | -@ VALUES('css',now(),'/* General settings for the entire page */ | |
| 32 | -@ body { | |
| 33 | -@ margin: 0ex 1ex; | |
| 34 | -@ padding: 0px; | |
| 35 | -@ background-color: white; | |
| 36 | -@ font-family: sans-serif; | |
| 37 | -@ } | |
| 38 | -@ | |
| 39 | -@ /* The project logo in the upper left-hand corner of each page */ | |
| 40 | -@ div.logo { | |
| 41 | -@ display: table-row; | |
| 42 | -@ text-align: center; | |
| 43 | -@ /* vertical-align: bottom;*/ | |
| 44 | -@ font-size: 2em; | |
| 45 | -@ font-weight: bold; | |
| 46 | -@ background-color: #707070; | |
| 47 | -@ color: #ffffff; | |
| 48 | -@ min-width: 200px; | |
| 49 | -@ white-space: nowrap; | |
| 50 | -@ } | |
| 51 | -@ | |
| 52 | -@ /* The page title centered at the top of each page */ | |
| 53 | -@ div.title { | |
| 54 | -@ display: table-cell; | |
| 55 | -@ font-size: 1.5em; | |
| 56 | -@ font-weight: bold; | |
| 57 | -@ text-align: center; | |
| 58 | -@ padding: 0 0 0 10px; | |
| 59 | -@ color: #404040; | |
| 60 | -@ vertical-align: bottom; | |
| 61 | -@ width: 100%; | |
| 62 | -@ } | |
| 63 | -@ | |
| 64 | -@ /* The login status message in the top right-hand corner */ | |
| 65 | -@ div.status { | |
| 66 | -@ display: table-cell; | |
| 67 | -@ text-align: right; | |
| 68 | -@ vertical-align: bottom; | |
| 69 | -@ color: #404040; | |
| 70 | -@ font-size: 0.8em; | |
| 71 | -@ font-weight: bold; | |
| 72 | -@ min-width: 200px; | |
| 73 | -@ white-space: nowrap; | |
| 74 | -@ } | |
| 75 | -@ | |
| 76 | -@ /* The header across the top of the page */ | |
| 77 | -@ div.header { | |
| 78 | -@ display: table; | |
| 79 | -@ width: 100%; | |
| 80 | -@ } | |
| 81 | -@ | |
| 82 | -@ /* The main menu bar that appears at the top of the page beneath | |
| 83 | -@ ** the header */ | |
| 84 | -@ div.mainmenu { | |
| 85 | -@ padding: 5px 10px 5px 10px; | |
| 86 | -@ font-size: 0.9em; | |
| 87 | -@ font-weight: bold; | |
| 88 | -@ text-align: center; | |
| 89 | -@ letter-spacing: 1px; | |
| 90 | -@ background-color: #404040; | |
| 91 | -@ color: white; | |
| 92 | -@ } | |
| 93 | -@ | |
| 94 | -@ /* The submenu bar that *sometimes* appears below the main menu */ | |
| 95 | -@ div.submenu, div.sectionmenu { | |
| 96 | -@ padding: 3px 10px 3px 0px; | |
| 97 | -@ font-size: 0.9em; | |
| 98 | -@ text-align: center; | |
| 99 | -@ background-color: #606060; | |
| 100 | -@ color: white; | |
| 101 | -@ } | |
| 102 | -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, | |
| 103 | -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { | |
| 104 | -@ padding: 3px 10px 3px 10px; | |
| 105 | -@ color: white; | |
| 106 | -@ text-decoration: none; | |
| 107 | -@ } | |
| 108 | -@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 109 | -@ color: #404040; | |
| 110 | -@ background-color: white; | |
| 111 | -@ } | |
| 112 | -@ | |
| 113 | -@ /* All page content from the bottom of the menu or submenu down to | |
| 114 | -@ ** the footer */ | |
| 115 | -@ div.content { | |
| 116 | -@ padding: 0ex 0ex 0ex 0ex; | |
| 117 | -@ } | |
| 118 | -@ /* Hyperlink colors */ | |
| 119 | -@ div.content a { color: #604000; } | |
| 120 | -@ div.content a:link { color: #604000;} | |
| 121 | -@ div.content a:visited { color: #600000; } | |
| 122 | -@ | |
| 123 | -@ /* <verbatim> blocks */ | |
| 124 | -@ pre.verbatim { | |
| 125 | -@ background-color: #ffffff; | |
| 126 | -@ padding: 0.5em; | |
| 127 | -@ white-space: pre-wrap; | |
| 128 | -@ } | |
| 129 | -@ | |
| 130 | -@ /* Some pages have section dividers */ | |
| 131 | -@ div.section { | |
| 132 | -@ margin-bottom: 0px; | |
| 133 | -@ margin-top: 1em; | |
| 134 | -@ padding: 1px 1px 1px 1px; | |
| 135 | -@ font-size: 1.2em; | |
| 136 | -@ font-weight: bold; | |
| 137 | -@ background-color: #404040; | |
| 138 | -@ color: white; | |
| 139 | -@ white-space: nowrap; | |
| 140 | -@ } | |
| 141 | -@ | |
| 142 | -@ /* The "Date" that occurs on the left hand side of timelines */ | |
| 143 | -@ div.divider { | |
| 144 | -@ background: #a0a0a0; | |
| 145 | -@ border: 2px #505050 solid; | |
| 146 | -@ font-size: 1em; font-weight: normal; | |
| 147 | -@ padding: .25em; | |
| 148 | -@ margin: .2em 0 .2em 0; | |
| 149 | -@ float: left; | |
| 150 | -@ clear: left; | |
| 151 | -@ white-space: nowrap; | |
| 152 | -@ } | |
| 153 | -@ | |
| 154 | -@ /* The footer at the very bottom of the page */ | |
| 155 | -@ div.footer { | |
| 156 | -@ font-size: 0.8em; | |
| 157 | -@ margin-top: 12px; | |
| 158 | -@ padding: 5px 10px 5px 10px; | |
| 159 | -@ text-align: right; | |
| 160 | -@ background-color: #404040; | |
| 161 | -@ color: white; | |
| 162 | -@ } | |
| 163 | -@ | |
| 164 | -@ /* The label/value pairs on (for example) the vinfo page */ | |
| 165 | -@ table.label-value th { | |
| 166 | -@ vertical-align: top; | |
| 167 | -@ text-align: right; | |
| 168 | -@ padding: 0.2ex 2ex; | |
| 169 | -@ }'); | |
| 170 | -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> | |
| 171 | -@ <head> | |
| 172 | -@ <base href="$baseurl/$current_page" /> | |
| 173 | -@ <title>$<project_name>: $<title></title> | |
| 174 | -@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 175 | -@ href="$home/timeline.rss"> | |
| 176 | -@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" | |
| 177 | -@ media="screen"> | |
| 178 | -@ </head> | |
| 179 | -@ <body> | |
| 180 | -@ <div class="header"> | |
| 181 | -@ <div class="title"><small>$<project_name></small><br />$<title></div> | |
| 182 | -@ <div class="status"><th1> | |
| 183 | -@ if {[info exists login]} { | |
| 184 | -@ puts "Logged in as $login" | |
| 185 | -@ } else { | |
| 186 | -@ puts "Not logged in" | |
| 187 | -@ } | |
| 188 | -@ </th1></div> | |
| 189 | -@ </div> | |
| 190 | -@ <div class="mainmenu"> | |
| 191 | -@ <th1> | |
| 192 | -@ html "<a href=''$home$index_page''>Home</a>\n" | |
| 193 | -@ if {[anycap jor]} { | |
| 194 | -@ html "<a href=''$home/timeline''>Timeline</a>\n" | |
| 195 | -@ } | |
| 196 | -@ if {[hascap oh]} { | |
| 197 | -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" | |
| 198 | -@ } | |
| 199 | -@ if {[hascap o]} { | |
| 200 | -@ html "<a href=''$home/brlist''>Branches</a>\n" | |
| 201 | -@ html "<a href=''$home/taglist''>Tags</a>\n" | |
| 202 | -@ } | |
| 203 | -@ if {[hascap r]} { | |
| 204 | -@ html "<a href=''$home/reportlist''>Tickets</a>\n" | |
| 205 | -@ } | |
| 206 | -@ if {[hascap j]} { | |
| 207 | -@ html "<a href=''$home/wiki''>Wiki</a>\n" | |
| 208 | -@ } | |
| 209 | -@ if {[hascap s]} { | |
| 210 | -@ html "<a href=''$home/setup''>Admin</a>\n" | |
| 211 | -@ } elseif {[hascap a]} { | |
| 212 | -@ html "<a href=''$home/setup_ulist''>Users</a>\n" | |
| 213 | -@ } | |
| 214 | -@ if {[info exists login]} { | |
| 215 | -@ html "<a href=''$home/login''>Logout</a>\n" | |
| 216 | -@ } else { | |
| 217 | -@ html "<a href=''$home/login''>Login</a>\n" | |
| 218 | -@ } | |
| 219 | -@ </th1></div> | |
| 220 | -@ '); | |
| 221 | -@ REPLACE INTO config(name,mtime,value) | |
| 222 | -@ VALUES('footer',now(),'<div class="footer"> | |
| 223 | -@ Fossil version $manifest_version $manifest_date | |
| 224 | -@ </div> | |
| 225 | -@ </body></html> | |
| 226 | -@ '); | |
| 227 | -; | |
| 228 | - | |
| 229 | -/* | |
| 230 | -** A tan theme with the project title above the user identification | |
| 231 | -** and no logo image. | |
| 232 | -*/ | |
| 233 | -static const char zBuiltinSkin2[] = | |
| 234 | -@ REPLACE INTO config(name,mtime,value) | |
| 235 | -@ VALUES('css',now(),'/* General settings for the entire page */ | |
| 236 | -@ body { | |
| 237 | -@ margin: 0ex 0ex; | |
| 238 | -@ padding: 0px; | |
| 239 | -@ background-color: #fef3bc; | |
| 240 | -@ font-family: sans-serif; | |
| 241 | -@ } | |
| 242 | -@ | |
| 243 | -@ /* The project logo in the upper left-hand corner of each page */ | |
| 244 | -@ div.logo { | |
| 245 | -@ display: inline; | |
| 246 | -@ text-align: center; | |
| 247 | -@ vertical-align: bottom; | |
| 248 | -@ font-weight: bold; | |
| 249 | -@ font-size: 2.5em; | |
| 250 | -@ color: #a09048; | |
| 251 | -@ white-space: nowrap; | |
| 252 | -@ } | |
| 253 | -@ | |
| 254 | -@ /* The page title centered at the top of each page */ | |
| 255 | -@ div.title { | |
| 256 | -@ display: table-cell; | |
| 257 | -@ font-size: 2em; | |
| 258 | -@ font-weight: bold; | |
| 259 | -@ text-align: left; | |
| 260 | -@ padding: 0 0 0 5px; | |
| 261 | -@ color: #a09048; | |
| 262 | -@ vertical-align: bottom; | |
| 263 | -@ width: 100%; | |
| 264 | -@ } | |
| 265 | -@ | |
| 266 | -@ /* The login status message in the top right-hand corner */ | |
| 267 | -@ div.status { | |
| 268 | -@ display: table-cell; | |
| 269 | -@ text-align: right; | |
| 270 | -@ vertical-align: bottom; | |
| 271 | -@ color: #a09048; | |
| 272 | -@ padding: 5px 5px 0 0; | |
| 273 | -@ font-size: 0.8em; | |
| 274 | -@ font-weight: bold; | |
| 275 | -@ white-space: nowrap; | |
| 276 | -@ } | |
| 277 | -@ | |
| 278 | -@ /* The header across the top of the page */ | |
| 279 | -@ div.header { | |
| 280 | -@ display: table; | |
| 281 | -@ width: 100%; | |
| 282 | -@ } | |
| 283 | -@ | |
| 284 | -@ /* The main menu bar that appears at the top of the page beneath | |
| 285 | -@ ** the header */ | |
| 286 | -@ div.mainmenu { | |
| 287 | -@ padding: 5px 10px 5px 10px; | |
| 288 | -@ font-size: 0.9em; | |
| 289 | -@ font-weight: bold; | |
| 290 | -@ text-align: center; | |
| 291 | -@ letter-spacing: 1px; | |
| 292 | -@ background-color: #a09048; | |
| 293 | -@ color: black; | |
| 294 | -@ } | |
| 295 | -@ | |
| 296 | -@ /* The submenu bar that *sometimes* appears below the main menu */ | |
| 297 | -@ div.submenu, div.sectionmenu { | |
| 298 | -@ padding: 3px 10px 3px 0px; | |
| 299 | -@ font-size: 0.9em; | |
| 300 | -@ text-align: center; | |
| 301 | -@ background-color: #c0af58; | |
| 302 | -@ color: white; | |
| 303 | -@ } | |
| 304 | -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, | |
| 305 | -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { | |
| 306 | -@ padding: 3px 10px 3px 10px; | |
| 307 | -@ color: white; | |
| 308 | -@ text-decoration: none; | |
| 309 | -@ } | |
| 310 | -@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 311 | -@ color: #a09048; | |
| 312 | -@ background-color: white; | |
| 313 | -@ } | |
| 314 | -@ | |
| 315 | -@ /* All page content from the bottom of the menu or submenu down to | |
| 316 | -@ ** the footer */ | |
| 317 | -@ div.content { | |
| 318 | -@ padding: 1ex 5px; | |
| 319 | -@ } | |
| 320 | -@ div.content a { color: #706532; } | |
| 321 | -@ div.content a:link { color: #706532; } | |
| 322 | -@ div.content a:visited { color: #704032; } | |
| 323 | -@ div.content a:hover { background-color: white; color: #706532; } | |
| 324 | -@ | |
| 325 | -@ /* Some pages have section dividers */ | |
| 326 | -@ div.section { | |
| 327 | -@ margin-bottom: 0px; | |
| 328 | -@ margin-top: 1em; | |
| 329 | -@ padding: 3px 3px 0 3px; | |
| 330 | -@ font-size: 1.2em; | |
| 331 | -@ font-weight: bold; | |
| 332 | -@ background-color: #a09048; | |
| 333 | -@ color: white; | |
| 334 | -@ white-space: nowrap; | |
| 335 | -@ } | |
| 336 | -@ | |
| 337 | -@ /* The "Date" that occurs on the left hand side of timelines */ | |
| 338 | -@ div.divider { | |
| 339 | -@ background: #e1d498; | |
| 340 | -@ border: 2px #a09048 solid; | |
| 341 | -@ font-size: 1em; font-weight: normal; | |
| 342 | -@ padding: .25em; | |
| 343 | -@ margin: .2em 0 .2em 0; | |
| 344 | -@ float: left; | |
| 345 | -@ clear: left; | |
| 346 | -@ white-space: nowrap; | |
| 347 | -@ } | |
| 348 | -@ | |
| 349 | -@ /* The footer at the very bottom of the page */ | |
| 350 | -@ div.footer { | |
| 351 | -@ font-size: 0.8em; | |
| 352 | -@ margin-top: 12px; | |
| 353 | -@ padding: 5px 10px 5px 10px; | |
| 354 | -@ text-align: right; | |
| 355 | -@ background-color: #a09048; | |
| 356 | -@ color: white; | |
| 357 | -@ } | |
| 358 | -@ | |
| 359 | -@ /* Hyperlink colors */ | |
| 360 | -@ div.footer a { color: white; } | |
| 361 | -@ div.footer a:link { color: white; } | |
| 362 | -@ div.footer a:visited { color: white; } | |
| 363 | -@ div.footer a:hover { background-color: white; color: #558195; } | |
| 364 | -@ | |
| 365 | -@ /* <verbatim> blocks */ | |
| 366 | -@ pre.verbatim { | |
| 367 | -@ background-color: #f5f5f5; | |
| 368 | -@ padding: 0.5em; | |
| 369 | -@ white-space: pre-wrap; | |
| 370 | -@ } | |
| 371 | -@ | |
| 372 | -@ /* The label/value pairs on (for example) the ci page */ | |
| 373 | -@ table.label-value th { | |
| 374 | -@ vertical-align: top; | |
| 375 | -@ text-align: right; | |
| 376 | -@ padding: 0.2ex 2ex; | |
| 377 | -@ }'); | |
| 378 | -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> | |
| 379 | -@ <head> | |
| 380 | -@ <base href="$baseurl/$current_page" /> | |
| 381 | -@ <title>$<project_name>: $<title></title> | |
| 382 | -@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 383 | -@ href="$home/timeline.rss"> | |
| 384 | -@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" | |
| 385 | -@ media="screen"> | |
| 386 | -@ </head> | |
| 387 | -@ <body> | |
| 388 | -@ <div class="header"> | |
| 389 | -@ <div class="title">$<title></div> | |
| 390 | -@ <div class="status"> | |
| 391 | -@ <div class="logo">$<project_name></div><br/> | |
| 392 | -@ <th1> | |
| 393 | -@ if {[info exists login]} { | |
| 394 | -@ puts "Logged in as $login" | |
| 395 | -@ } else { | |
| 396 | -@ puts "Not logged in" | |
| 397 | -@ } | |
| 398 | -@ </th1></div> | |
| 399 | -@ </div> | |
| 400 | -@ <div class="mainmenu"> | |
| 401 | -@ <th1> | |
| 402 | -@ html "<a href=''$home$index_page''>Home</a>\n" | |
| 403 | -@ if {[anycap jor]} { | |
| 404 | -@ html "<a href=''$home/timeline''>Timeline</a>\n" | |
| 405 | -@ } | |
| 406 | -@ if {[hascap oh]} { | |
| 407 | -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" | |
| 408 | -@ } | |
| 409 | -@ if {[hascap o]} { | |
| 410 | -@ html "<a href=''$home/brlist''>Branches</a>\n" | |
| 411 | -@ html "<a href=''$home/taglist''>Tags</a>\n" | |
| 412 | -@ } | |
| 413 | -@ if {[hascap r]} { | |
| 414 | -@ html "<a href=''$home/reportlist''>Tickets</a>\n" | |
| 415 | -@ } | |
| 416 | -@ if {[hascap j]} { | |
| 417 | -@ html "<a href=''$home/wiki''>Wiki</a>\n" | |
| 418 | -@ } | |
| 419 | -@ if {[hascap s]} { | |
| 420 | -@ html "<a href=''$home/setup''>Admin</a>\n" | |
| 421 | -@ } elseif {[hascap a]} { | |
| 422 | -@ html "<a href=''$home/setup_ulist''>Users</a>\n" | |
| 423 | -@ } | |
| 424 | -@ if {[info exists login]} { | |
| 425 | -@ html "<a href=''$home/login''>Logout</a>\n" | |
| 426 | -@ } else { | |
| 427 | -@ html "<a href=''$home/login''>Login</a>\n" | |
| 428 | -@ } | |
| 429 | -@ </th1></div> | |
| 430 | -@ '); | |
| 431 | -@ REPLACE INTO config(name,mtime,value) | |
| 432 | -@ VALUES('footer',now(),'<div class="footer"> | |
| 433 | -@ Fossil version $manifest_version $manifest_date | |
| 434 | -@ </div> | |
| 435 | -@ </body></html> | |
| 436 | -@ '); | |
| 437 | -; | |
| 438 | - | |
| 439 | -/* | |
| 440 | -** Black letters on a white or cream background with the main menu | |
| 441 | -** stuck on the left-hand side. | |
| 442 | -*/ | |
| 443 | -static const char zBuiltinSkin3[] = | |
| 444 | -@ REPLACE INTO config(name,mtime,value) | |
| 445 | -@ VALUES('css',now(),'/* General settings for the entire page */ | |
| 446 | -@ body { | |
| 447 | -@ margin:0px 0px 0px 0px; | |
| 448 | -@ padding:0px; | |
| 449 | -@ font-family:verdana, arial, helvetica, "sans serif"; | |
| 450 | -@ color:#333; | |
| 451 | -@ background-color:white; | |
| 452 | -@ } | |
| 453 | -@ | |
| 454 | -@ /* consistent colours */ | |
| 455 | -@ h2 { | |
| 456 | -@ color: #333; | |
| 457 | -@ } | |
| 458 | -@ h3 { | |
| 459 | -@ color: #333; | |
| 460 | -@ } | |
| 461 | -@ | |
| 462 | -@ /* The project logo in the upper left-hand corner of each page */ | |
| 463 | -@ div.logo { | |
| 464 | -@ display: table-cell; | |
| 465 | -@ text-align: left; | |
| 466 | -@ vertical-align: bottom; | |
| 467 | -@ font-weight: bold; | |
| 468 | -@ color: #333; | |
| 469 | -@ white-space: nowrap; | |
| 470 | -@ } | |
| 471 | -@ | |
| 472 | -@ /* The page title centered at the top of each page */ | |
| 473 | -@ div.title { | |
| 474 | -@ display: table-cell; | |
| 475 | -@ font-size: 2em; | |
| 476 | -@ font-weight: bold; | |
| 477 | -@ text-align: center; | |
| 478 | -@ color: #333; | |
| 479 | -@ vertical-align: bottom; | |
| 480 | -@ width: 100%; | |
| 481 | -@ } | |
| 482 | -@ | |
| 483 | -@ /* The login status message in the top right-hand corner */ | |
| 484 | -@ div.status { | |
| 485 | -@ display: table-cell; | |
| 486 | -@ padding-right: 10px; | |
| 487 | -@ text-align: right; | |
| 488 | -@ vertical-align: bottom; | |
| 489 | -@ padding-bottom: 5px; | |
| 490 | -@ color: #333; | |
| 491 | -@ font-size: 0.8em; | |
| 492 | -@ font-weight: bold; | |
| 493 | -@ white-space: nowrap; | |
| 494 | -@ } | |
| 495 | -@ | |
| 496 | -@ /* The header across the top of the page */ | |
| 497 | -@ div.header { | |
| 498 | -@ margin:10px 0px 10px 0px; | |
| 499 | -@ padding:1px 0px 0px 20px; | |
| 500 | -@ border-style:solid; | |
| 501 | -@ border-color:black; | |
| 502 | -@ border-width:1px 0px; | |
| 503 | -@ background-color:#eee; | |
| 504 | -@ } | |
| 505 | -@ | |
| 506 | -@ /* The main menu bar that appears at the top left of the page beneath | |
| 507 | -@ ** the header. Width must be co-ordinated with the container below */ | |
| 508 | -@ div.mainmenu { | |
| 509 | -@ float: left; | |
| 510 | -@ margin-left: 10px; | |
| 511 | -@ margin-right: 10px; | |
| 512 | -@ font-size: 0.9em; | |
| 513 | -@ font-weight: bold; | |
| 514 | -@ padding:5px; | |
| 515 | -@ background-color:#eee; | |
| 516 | -@ border:1px solid #999; | |
| 517 | -@ width:8em; | |
| 518 | -@ } | |
| 519 | -@ | |
| 520 | -@ /* Main menu is now a list */ | |
| 521 | -@ div.mainmenu ul { | |
| 522 | -@ padding: 0; | |
| 523 | -@ list-style:none; | |
| 524 | -@ } | |
| 525 | -@ div.mainmenu a, div.mainmenu a:visited{ | |
| 526 | -@ padding: 1px 10px 1px 10px; | |
| 527 | -@ color: #333; | |
| 528 | -@ text-decoration: none; | |
| 529 | -@ } | |
| 530 | -@ div.mainmenu a:hover { | |
| 531 | -@ color: #eee; | |
| 532 | -@ background-color: #333; | |
| 533 | -@ } | |
| 534 | -@ | |
| 535 | -@ /* Container for the sub-menu and content so they don''t spread | |
| 536 | -@ ** out underneath the main menu */ | |
| 537 | -@ #container { | |
| 538 | -@ padding-left: 9em; | |
| 539 | -@ } | |
| 540 | -@ | |
| 541 | -@ /* The submenu bar that *sometimes* appears below the main menu */ | |
| 542 | -@ div.submenu, div.sectionmenu { | |
| 543 | -@ padding: 3px 10px 3px 10px; | |
| 544 | -@ font-size: 0.9em; | |
| 545 | -@ text-align: center; | |
| 546 | -@ border:1px solid #999; | |
| 547 | -@ border-width:1px 0px; | |
| 548 | -@ background-color: #eee; | |
| 549 | -@ color: #333; | |
| 550 | -@ } | |
| 551 | -@ div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, | |
| 552 | -@ div.sectionmenu>a.button:visited { | |
| 553 | -@ padding: 3px 10px 3px 10px; | |
| 554 | -@ color: #333; | |
| 555 | -@ text-decoration: none; | |
| 556 | -@ } | |
| 557 | -@ div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 558 | -@ color: #eee; | |
| 559 | -@ background-color: #333; | |
| 560 | -@ } | |
| 561 | -@ | |
| 562 | -@ /* All page content from the bottom of the menu or submenu down to | |
| 563 | -@ ** the footer */ | |
| 564 | -@ div.content { | |
| 565 | -@ padding: 2ex 1ex 0ex 2ex; | |
| 566 | -@ } | |
| 567 | -@ | |
| 568 | -@ /* Some pages have section dividers */ | |
| 569 | -@ div.section { | |
| 570 | -@ margin-bottom: 0px; | |
| 571 | -@ margin-top: 1em; | |
| 572 | -@ padding: 1px 1px 1px 1px; | |
| 573 | -@ font-size: 1.2em; | |
| 574 | -@ font-weight: bold; | |
| 575 | -@ border-style:solid; | |
| 576 | -@ border-color:#999; | |
| 577 | -@ border-width:1px 0px; | |
| 578 | -@ background-color: #eee; | |
| 579 | -@ color: #333; | |
| 580 | -@ white-space: nowrap; | |
| 581 | -@ } | |
| 582 | -@ | |
| 583 | -@ /* The "Date" that occurs on the left hand side of timelines */ | |
| 584 | -@ div.divider { | |
| 585 | -@ background: #eee; | |
| 586 | -@ border: 2px #999 solid; | |
| 587 | -@ font-size: 1em; font-weight: normal; | |
| 588 | -@ padding: .25em; | |
| 589 | -@ margin: .2em 0 .2em 0; | |
| 590 | -@ float: left; | |
| 591 | -@ clear: left; | |
| 592 | -@ color: #333; | |
| 593 | -@ white-space: nowrap; | |
| 594 | -@ } | |
| 595 | -@ | |
| 596 | -@ /* The footer at the very bottom of the page */ | |
| 597 | -@ div.footer { | |
| 598 | -@ font-size: 0.8em; | |
| 599 | -@ margin-top: 12px; | |
| 600 | -@ padding: 5px 10px 5px 10px; | |
| 601 | -@ text-align: right; | |
| 602 | -@ background-color: #eee; | |
| 603 | -@ color: #555; | |
| 604 | -@ } | |
| 605 | -@ | |
| 606 | -@ /* <verbatim> blocks */ | |
| 607 | -@ pre.verbatim { | |
| 608 | -@ background-color: #f5f5f5; | |
| 609 | -@ padding: 0.5em; | |
| 610 | -@ white-space: pre-wrap; | |
| 611 | -@ } | |
| 612 | -@ | |
| 613 | -@ /* The label/value pairs on (for example) the ci page */ | |
| 614 | -@ table.label-value th { | |
| 615 | -@ vertical-align: top; | |
| 616 | -@ text-align: right; | |
| 617 | -@ padding: 0.2ex 2ex; | |
| 618 | -@ }'); | |
| 619 | -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> | |
| 620 | -@ <head> | |
| 621 | -@ <base href="$baseurl/$current_page" /> | |
| 622 | -@ <title>$<project_name>: $<title></title> | |
| 623 | -@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 624 | -@ href="$home/timeline.rss"> | |
| 625 | -@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" | |
| 626 | -@ media="screen"> | |
| 627 | -@ </head> | |
| 628 | -@ <body> | |
| 629 | -@ <div class="header"> | |
| 630 | -@ <div class="logo"> | |
| 631 | -@ <img src="$logo_image_url" alt="logo"> | |
| 632 | -@ <br />$<project_name> | |
| 633 | -@ </div> | |
| 634 | -@ <div class="title">$<title></div> | |
| 635 | -@ <div class="status"><th1> | |
| 636 | -@ if {[info exists login]} { | |
| 637 | -@ puts "Logged in as $login" | |
| 638 | -@ } else { | |
| 639 | -@ puts "Not logged in" | |
| 640 | -@ } | |
| 641 | -@ </th1></div> | |
| 642 | -@ </div> | |
| 643 | -@ <div class="mainmenu"> | |
| 644 | -@ <th1> | |
| 645 | -@ html "<a href=''$home$index_page''>Home</a>\n" | |
| 646 | -@ if {[anycap jor]} { | |
| 647 | -@ html "<a href=''$home/timeline''>Timeline</a>\n" | |
| 648 | -@ } | |
| 649 | -@ if {[hascap oh]} { | |
| 650 | -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" | |
| 651 | -@ } | |
| 652 | -@ if {[hascap o]} { | |
| 653 | -@ html "<a href=''$home/brlist''>Branches</a>\n" | |
| 654 | -@ html "<a href=''$home/taglist''>Tags</a>\n" | |
| 655 | -@ } | |
| 656 | -@ if {[hascap r]} { | |
| 657 | -@ html "<a href=''$home/reportlist''>Tickets</a>\n" | |
| 658 | -@ } | |
| 659 | -@ if {[hascap j]} { | |
| 660 | -@ html "<a href=''$home/wiki''>Wiki</a>\n" | |
| 661 | -@ } | |
| 662 | -@ if {[hascap s]} { | |
| 663 | -@ html "<a href=''$home/setup''>Admin</a>\n" | |
| 664 | -@ } elseif {[hascap a]} { | |
| 665 | -@ html "<a href=''$home/setup_ulist''>Users</a>\n" | |
| 666 | -@ } | |
| 667 | -@ if {[info exists login]} { | |
| 668 | -@ html "<a href=''$home/login''>Logout</a>\n" | |
| 669 | -@ } else { | |
| 670 | -@ html "<a href=''$home/login''>Login</a>\n" | |
| 671 | -@ } | |
| 672 | -@ </th1></ul></div> | |
| 673 | -@ <div id="container"> | |
| 674 | -@ '); | |
| 675 | -@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div> | |
| 676 | -@ <div class="footer"> | |
| 677 | -@ Fossil version $manifest_version $manifest_date | |
| 678 | -@ </div> | |
| 679 | -@ </body></html> | |
| 680 | -@ '); | |
| 681 | -; | |
| 682 | - | |
| 683 | - | |
| 684 | -/* | |
| 685 | -** Shadow boxes and rounded corners. | |
| 686 | -*/ | |
| 687 | -static const char zBuiltinSkin4[] = | |
| 688 | -@ REPLACE INTO config(name,mtime,value) | |
| 689 | -@ VALUES('css',now(),'/* General settings for the entire page */ | |
| 690 | -@ html { | |
| 691 | -@ min-height: 100%; | |
| 692 | -@ } | |
| 693 | -@ body { | |
| 694 | -@ margin: 0ex 1ex; | |
| 695 | -@ padding: 0px; | |
| 696 | -@ background-color: white; | |
| 697 | -@ color: #333; | |
| 698 | -@ font-family: Verdana, sans-serif; | |
| 699 | -@ font-size: 0.8em; | |
| 700 | -@ } | |
| 701 | -@ | |
| 702 | -@ /* The project logo in the upper left-hand corner of each page */ | |
| 703 | -@ div.logo { | |
| 704 | -@ display: table-cell; | |
| 705 | -@ text-align: right; | |
| 706 | -@ vertical-align: bottom; | |
| 707 | -@ font-weight: normal; | |
| 708 | -@ white-space: nowrap; | |
| 709 | -@ } | |
| 710 | -@ | |
| 711 | -@ /* Widths */ | |
| 712 | -@ div.header, div.mainmenu, div.submenu, div.content, div.footer { | |
| 713 | -@ max-width: 900px; | |
| 714 | -@ margin: auto; | |
| 715 | -@ padding: 3px 20px 3px 20px; | |
| 716 | -@ clear: both; | |
| 717 | -@ } | |
| 718 | -@ | |
| 719 | -@ /* The page title at the top of each page */ | |
| 720 | -@ div.title { | |
| 721 | -@ display: table-cell; | |
| 722 | -@ padding-left: 10px; | |
| 723 | -@ font-size: 2em; | |
| 724 | -@ margin: 10px 0 10px -20px; | |
| 725 | -@ vertical-align: bottom; | |
| 726 | -@ text-align: left; | |
| 727 | -@ width: 80%; | |
| 728 | -@ font-family: Verdana, sans-serif; | |
| 729 | -@ font-weight: bold; | |
| 730 | -@ color: #558195; | |
| 731 | -@ text-shadow: 0px 2px 2px #999999; | |
| 732 | -@ } | |
| 733 | -@ | |
| 734 | -@ /* The login status message in the top right-hand corner */ | |
| 735 | -@ div.status { | |
| 736 | -@ display: table-cell; | |
| 737 | -@ text-align: right; | |
| 738 | -@ vertical-align: bottom; | |
| 739 | -@ color: #333; | |
| 740 | -@ margin-right: -20px; | |
| 741 | -@ white-space: nowrap; | |
| 742 | -@ } | |
| 743 | -@ | |
| 744 | -@ /* The main menu bar that appears at the top of the page beneath | |
| 745 | -@ ** the header */ | |
| 746 | -@ div.mainmenu { | |
| 747 | -@ text-align: center; | |
| 748 | -@ color: white; | |
| 749 | -@ border-top-left-radius: 5px; | |
| 750 | -@ border-top-right-radius: 5px; | |
| 751 | -@ vertical-align: middle; | |
| 752 | -@ padding-top: 8px; | |
| 753 | -@ padding-bottom: 8px; | |
| 754 | -@ background-color: #446979; | |
| 755 | -@ box-shadow: 0px 3px 4px #333333; | |
| 756 | -@ } | |
| 757 | -@ | |
| 758 | -@ /* The submenu bar that *sometimes* appears below the main menu */ | |
| 759 | -@ div.submenu { | |
| 760 | -@ padding-top:10px; | |
| 761 | -@ padding-bottom:0; | |
| 762 | -@ text-align: right; | |
| 763 | -@ color: #000; | |
| 764 | -@ background-color: #fff; | |
| 765 | -@ height: 1.5em; | |
| 766 | -@ vertical-align:middle; | |
| 767 | -@ box-shadow: 0px 3px 4px #999; | |
| 768 | -@ } | |
| 769 | -@ div.mainmenu a, div.mainmenu a:visited { | |
| 770 | -@ padding: 3px 10px 3px 10px; | |
| 771 | -@ color: white; | |
| 772 | -@ text-decoration: none; | |
| 773 | -@ } | |
| 774 | -@ div.submenu a, div.submenu a:visited, a.button, | |
| 775 | -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { | |
| 776 | -@ padding: 2px 8px; | |
| 777 | -@ color: #000; | |
| 778 | -@ font-family: Arial; | |
| 779 | -@ text-decoration: none; | |
| 780 | -@ margin:auto; | |
| 781 | -@ border-radius: 5px; | |
| 782 | -@ background-color: #e0e0e0; | |
| 783 | -@ text-shadow: 0px -1px 0px #eee; | |
| 784 | -@ border: 1px solid #000; | |
| 785 | -@ } | |
| 786 | -@ | |
| 787 | -@ div.mainmenu a:hover { | |
| 788 | -@ color: #000; | |
| 789 | -@ background-color: white; | |
| 790 | -@ } | |
| 791 | -@ | |
| 792 | -@ div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 793 | -@ background-color: #c0c0c0; | |
| 794 | -@ } | |
| 795 | -@ | |
| 796 | -@ /* All page content from the bottom of the menu or submenu down to | |
| 797 | -@ ** the footer */ | |
| 798 | -@ div.content { | |
| 799 | -@ background-color: #fff; | |
| 800 | -@ box-shadow: 0px 3px 4px #999; | |
| 801 | -@ border-bottom-right-radius: 5px; | |
| 802 | -@ border-bottom-left-radius: 5px; | |
| 803 | -@ padding-bottom: 1em; | |
| 804 | -@ min-height:40%; | |
| 805 | -@ } | |
| 806 | -@ | |
| 807 | -@ | |
| 808 | -@ /* Some pages have section dividers */ | |
| 809 | -@ div.section { | |
| 810 | -@ margin-bottom: 0.5em; | |
| 811 | -@ margin-top: 1em; | |
| 812 | -@ margin-right: auto; | |
| 813 | -@ padding: 1px 1px 1px 1px; | |
| 814 | -@ font-size: 1.2em; | |
| 815 | -@ font-weight: bold; | |
| 816 | -@ text-align: center; | |
| 817 | -@ color: white; | |
| 818 | -@ border-radius: 5px; | |
| 819 | -@ background-color: #446979; | |
| 820 | -@ box-shadow: 0px 3px 4px #333333; | |
| 821 | -@ white-space: nowrap; | |
| 822 | -@ } | |
| 823 | -@ | |
| 824 | -@ /* The "Date" that occurs on the left hand side of timelines */ | |
| 825 | -@ div.divider { | |
| 826 | -@ font-size: 1.2em; | |
| 827 | -@ font-family: Georgia, serif; | |
| 828 | -@ font-weight: bold; | |
| 829 | -@ margin-top: 1em; | |
| 830 | -@ white-space: nowrap; | |
| 831 | -@ } | |
| 832 | -@ | |
| 833 | -@ /* The footer at the very bottom of the page */ | |
| 834 | -@ div.footer { | |
| 835 | -@ font-size: 0.9em; | |
| 836 | -@ text-align: right; | |
| 837 | -@ margin-bottom: 1em; | |
| 838 | -@ color: #666; | |
| 839 | -@ } | |
| 840 | -@ | |
| 841 | -@ /* Hyperlink colors in the footer */ | |
| 842 | -@ div.footer a { color: white; } | |
| 843 | -@ div.footer a:link { color: white; } | |
| 844 | -@ div.footer a:visited { color: white; } | |
| 845 | -@ div.footer a:hover { background-color: white; color: #558195; } | |
| 846 | -@ | |
| 847 | -@ /* <verbatim> blocks */ | |
| 848 | -@ pre.verbatim, blockquote pre { | |
| 849 | -@ font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace; | |
| 850 | -@ background-color: #f3f3f3; | |
| 851 | -@ padding: 0.5em; | |
| 852 | -@ white-space: pre-wrap; | |
| 853 | -@ } | |
| 854 | -@ | |
| 855 | -@ blockquote pre { | |
| 856 | -@ border: 1px #000 dashed; | |
| 857 | -@ } | |
| 858 | -@ | |
| 859 | -@ /* The label/value pairs on (for example) the ci page */ | |
| 860 | -@ table.label-value th { | |
| 861 | -@ vertical-align: top; | |
| 862 | -@ text-align: right; | |
| 863 | -@ padding: 0.2ex 2ex; | |
| 864 | -@ } | |
| 865 | -@ | |
| 866 | -@ table.report tr th { | |
| 867 | -@ padding: 3px 5px; | |
| 868 | -@ text-transform: capitalize; | |
| 869 | -@ cursor: pointer; | |
| 870 | -@ } | |
| 871 | -@ | |
| 872 | -@ table.report tr td { | |
| 873 | -@ padding: 3px 5px; | |
| 874 | -@ cursor: pointer; | |
| 875 | -@ } | |
| 876 | -@ | |
| 877 | -@ textarea { | |
| 878 | -@ font-size: 1em; | |
| 879 | -@ } | |
| 880 | -@ | |
| 881 | -@ .fullsize-text { | |
| 882 | -@ font-size: 1.25em; | |
| 883 | -@ }'); | |
| 884 | -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> | |
| 885 | -@ <head> | |
| 886 | -@ <base href="$baseurl/$current_page" /> | |
| 887 | -@ <title>$<project_name>: $<title></title> | |
| 888 | -@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 889 | -@ href="$home/timeline.rss"> | |
| 890 | -@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" | |
| 891 | -@ media="screen"> | |
| 892 | -@ </head> | |
| 893 | -@ <body> | |
| 894 | -@ <div class="header"> | |
| 895 | -@ <div class="logo"> | |
| 896 | -@ <img src="$logo_image_url" alt="logo"> | |
| 897 | -@ <br />$<project_name> | |
| 898 | -@ </div> | |
| 899 | -@ <div class="title">$<title></div> | |
| 900 | -@ <div class="status"><th1> | |
| 901 | -@ if {[info exists login]} { | |
| 902 | -@ puts "Logged in as $login" | |
| 903 | -@ } else { | |
| 904 | -@ puts "Not logged in" | |
| 905 | -@ } | |
| 906 | -@ </th1></div> | |
| 907 | -@ </div> | |
| 908 | -@ <div class="mainmenu"> | |
| 909 | -@ <th1> | |
| 910 | -@ html "<a href=''$home$index_page''>Home</a>\n" | |
| 911 | -@ if {[anycap jor]} { | |
| 912 | -@ html "<a href=''$home/timeline''>Timeline</a>\n" | |
| 913 | -@ } | |
| 914 | -@ if {[hascap oh]} { | |
| 915 | -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" | |
| 916 | -@ } | |
| 917 | -@ if {[hascap o]} { | |
| 918 | -@ html "<a href=''$home/brlist''>Branches</a>\n" | |
| 919 | -@ html "<a href=''$home/taglist''>Tags</a>\n" | |
| 920 | -@ } | |
| 921 | -@ if {[hascap r]} { | |
| 922 | -@ html "<a href=''$home/reportlist''>Tickets</a>\n" | |
| 923 | -@ } | |
| 924 | -@ if {[hascap j]} { | |
| 925 | -@ html "<a href=''$home/wiki''>Wiki</a>\n" | |
| 926 | -@ } | |
| 927 | -@ if {[hascap s]} { | |
| 928 | -@ html "<a href=''$home/setup''>Admin</a>\n" | |
| 929 | -@ } elseif {[hascap a]} { | |
| 930 | -@ html "<a href=''$home/setup_ulist''>Users</a>\n" | |
| 931 | -@ } | |
| 932 | -@ if {[info exists login]} { | |
| 933 | -@ html "<a href=''$home/login''>Logout</a>\n" | |
| 934 | -@ } else { | |
| 935 | -@ html "<a href=''$home/login''>Login</a>\n" | |
| 936 | -@ } | |
| 937 | -@ </th1></div> | |
| 938 | -@ <div id="container"> | |
| 939 | -@ '); | |
| 940 | -@ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div> | |
| 941 | -@ <div class="footer"> | |
| 942 | -@ Fossil version $manifest_version $manifest_date | |
| 943 | -@ </div> | |
| 944 | -@ </body></html> | |
| 945 | -@ '); | |
| 946 | -; | |
| 947 | - | |
| 948 | - | |
| 949 | -/* | |
| 950 | -** This skin is intended to be almost identical to the default one, with the | |
| 951 | -** following changes to the header and footer: | |
| 952 | -** | |
| 953 | -** 1. The logo image in the header has been modified to be a hyperlink to the | |
| 954 | -** root of the web site containing the repository using the same scheme | |
| 955 | -** (i.e. HTTP or HTTPS) as the base URL for the repository. The header | |
| 956 | -** contains a TH1 script block to help accomplish these tasks. | |
| 957 | -** | |
| 958 | -** 2. The Fossil version information in the footer has been augmented with | |
| 959 | -** hyperlinks to the corresponding points on the timeline in the official | |
| 960 | -** Fossil repository. Additionally, if the Tcl integration feature is | |
| 961 | -** enabled, the loaded version of Tcl is included, with a hyperlink to the | |
| 962 | -** official Tcl/Tk web site. The footer also contains a TH1 script block | |
| 963 | -** to help accomplish these tasks. | |
| 964 | -*/ | |
| 965 | -static const char zBuiltinSkin5[] = | |
| 966 | -@ REPLACE INTO config(name,mtime,value) | |
| 967 | -@ VALUES('css',now(),'/* General settings for the entire page */ | |
| 968 | -@ body { | |
| 969 | -@ margin: 0ex 1ex; | |
| 970 | -@ padding: 0px; | |
| 971 | -@ background-color: white; | |
| 972 | -@ font-family: sans-serif; | |
| 973 | -@ } | |
| 974 | -@ | |
| 975 | -@ /* The project logo in the upper left-hand corner of each page */ | |
| 976 | -@ div.logo { | |
| 977 | -@ display: table-cell; | |
| 978 | -@ text-align: center; | |
| 979 | -@ vertical-align: bottom; | |
| 980 | -@ font-weight: bold; | |
| 981 | -@ color: #558195; | |
| 982 | -@ min-width: 200px; | |
| 983 | -@ white-space: nowrap; | |
| 984 | -@ } | |
| 985 | -@ | |
| 986 | -@ /* The page title centered at the top of each page */ | |
| 987 | -@ div.title { | |
| 988 | -@ display: table-cell; | |
| 989 | -@ font-size: 2em; | |
| 990 | -@ font-weight: bold; | |
| 991 | -@ text-align: center; | |
| 992 | -@ padding: 0 0 0 1em; | |
| 993 | -@ color: #558195; | |
| 994 | -@ vertical-align: bottom; | |
| 995 | -@ width: 100%; | |
| 996 | -@ } | |
| 997 | -@ | |
| 998 | -@ /* The login status message in the top right-hand corner */ | |
| 999 | -@ div.status { | |
| 1000 | -@ display: table-cell; | |
| 1001 | -@ text-align: right; | |
| 1002 | -@ vertical-align: bottom; | |
| 1003 | -@ color: #558195; | |
| 1004 | -@ font-size: 0.8em; | |
| 1005 | -@ font-weight: bold; | |
| 1006 | -@ min-width: 200px; | |
| 1007 | -@ white-space: nowrap; | |
| 1008 | -@ } | |
| 1009 | -@ | |
| 1010 | -@ /* The header across the top of the page */ | |
| 1011 | -@ div.header { | |
| 1012 | -@ display: table; | |
| 1013 | -@ width: 100%; | |
| 1014 | -@ } | |
| 1015 | -@ | |
| 1016 | -@ /* The main menu bar that appears at the top of the page beneath | |
| 1017 | -@ ** the header */ | |
| 1018 | -@ div.mainmenu { | |
| 1019 | -@ padding: 5px 10px 5px 10px; | |
| 1020 | -@ font-size: 0.9em; | |
| 1021 | -@ font-weight: bold; | |
| 1022 | -@ text-align: center; | |
| 1023 | -@ letter-spacing: 1px; | |
| 1024 | -@ background-color: #558195; | |
| 1025 | -@ border-top-left-radius: 8px; | |
| 1026 | -@ border-top-right-radius: 8px; | |
| 1027 | -@ color: white; | |
| 1028 | -@ } | |
| 1029 | -@ | |
| 1030 | -@ /* The submenu bar that *sometimes* appears below the main menu */ | |
| 1031 | -@ div.submenu, div.sectionmenu { | |
| 1032 | -@ padding: 3px 10px 3px 0px; | |
| 1033 | -@ font-size: 0.9em; | |
| 1034 | -@ text-align: center; | |
| 1035 | -@ background-color: #456878; | |
| 1036 | -@ color: white; | |
| 1037 | -@ } | |
| 1038 | -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, | |
| 1039 | -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { | |
| 1040 | -@ padding: 3px 10px 3px 10px; | |
| 1041 | -@ color: white; | |
| 1042 | -@ text-decoration: none; | |
| 1043 | -@ } | |
| 1044 | -@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 1045 | -@ color: #558195; | |
| 1046 | -@ background-color: white; | |
| 1047 | -@ } | |
| 1048 | -@ | |
| 1049 | -@ /* All page content from the bottom of the menu or submenu down to | |
| 1050 | -@ ** the footer */ | |
| 1051 | -@ div.content { | |
| 1052 | -@ padding: 0ex 1ex 1ex 1ex; | |
| 1053 | -@ border: solid #aaa; | |
| 1054 | -@ border-width: 1px; | |
| 1055 | -@ } | |
| 1056 | -@ | |
| 1057 | -@ /* Some pages have section dividers */ | |
| 1058 | -@ div.section { | |
| 1059 | -@ margin-bottom: 0px; | |
| 1060 | -@ margin-top: 1em; | |
| 1061 | -@ padding: 1px 1px 1px 1px; | |
| 1062 | -@ font-size: 1.2em; | |
| 1063 | -@ font-weight: bold; | |
| 1064 | -@ background-color: #558195; | |
| 1065 | -@ color: white; | |
| 1066 | -@ white-space: nowrap; | |
| 1067 | -@ } | |
| 1068 | -@ | |
| 1069 | -@ /* The "Date" that occurs on the left hand side of timelines */ | |
| 1070 | -@ div.divider { | |
| 1071 | -@ background: #a1c4d4; | |
| 1072 | -@ border: 2px #558195 solid; | |
| 1073 | -@ font-size: 1em; font-weight: normal; | |
| 1074 | -@ padding: .25em; | |
| 1075 | -@ margin: .2em 0 .2em 0; | |
| 1076 | -@ float: left; | |
| 1077 | -@ clear: left; | |
| 1078 | -@ white-space: nowrap; | |
| 1079 | -@ } | |
| 1080 | -@ | |
| 1081 | -@ /* The footer at the very bottom of the page */ | |
| 1082 | -@ div.footer { | |
| 1083 | -@ clear: both; | |
| 1084 | -@ font-size: 0.8em; | |
| 1085 | -@ padding: 5px 10px 5px 10px; | |
| 1086 | -@ text-align: right; | |
| 1087 | -@ background-color: #558195; | |
| 1088 | -@ border-bottom-left-radius: 8px; | |
| 1089 | -@ border-bottom-right-radius: 8px; | |
| 1090 | -@ color: white; | |
| 1091 | -@ } | |
| 1092 | -@ | |
| 1093 | -@ /* Hyperlink colors in the footer */ | |
| 1094 | -@ div.footer a { color: white; } | |
| 1095 | -@ div.footer a:link { color: white; } | |
| 1096 | -@ div.footer a:visited { color: white; } | |
| 1097 | -@ div.footer a:hover { background-color: white; color: #558195; } | |
| 1098 | -@ | |
| 1099 | -@ /* verbatim blocks */ | |
| 1100 | -@ pre.verbatim { | |
| 1101 | -@ background-color: #f5f5f5; | |
| 1102 | -@ padding: 0.5em; | |
| 1103 | -@ white-space: pre-wrap; | |
| 1104 | -@ } | |
| 1105 | -@ | |
| 1106 | -@ /* The label/value pairs on (for example) the ci page */ | |
| 1107 | -@ table.label-value th { | |
| 1108 | -@ vertical-align: top; | |
| 1109 | -@ text-align: right; | |
| 1110 | -@ padding: 0.2ex 2ex; | |
| 1111 | -@ }'); | |
| 1112 | -@ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> | |
| 1113 | -@ <head> | |
| 1114 | -@ <base href="$baseurl/$current_page" /> | |
| 1115 | -@ <title>$<project_name>: $<title></title> | |
| 1116 | -@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 1117 | -@ href="$home/timeline.rss" /> | |
| 1118 | -@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" | |
| 1119 | -@ media="screen" /> | |
| 1120 | -@ </head> | |
| 1121 | -@ <body> | |
| 1122 | -@ <div class="header"> | |
| 1123 | -@ <div class="logo"> | |
| 1124 | -@ <th1> | |
| 1125 | -@ ## | |
| 1126 | -@ ## NOTE: The purpose of this procedure is to take the base URL of the | |
| 1127 | -@ ## Fossil project and return the root of the entire web site using | |
| 1128 | -@ ## the same URI scheme as the base URL (e.g. http or https). | |
| 1129 | -@ ## | |
| 1130 | -@ proc getLogoUrl { baseurl } { | |
| 1131 | -@ set idx(first) [string first // $baseurl] | |
| 1132 | -@ if {$idx(first) != -1} { | |
| 1133 | -@ ## | |
| 1134 | -@ ## NOTE: Skip second slash. | |
| 1135 | -@ ## | |
| 1136 | -@ set idx(first+1) [expr {$idx(first) + 2}] | |
| 1137 | -@ ## | |
| 1138 | -@ ## NOTE: (part 1) The [string first] command does NOT actually | |
| 1139 | -@ ## the optional startIndex argument as specified in the | |
| 1140 | -@ ## TH1 support manual; therefore, we fake it by using the | |
| 1141 | -@ ## [string range] command and then adding the necessary | |
| 1142 | -@ ## offset to the resulting index manually (below). In Tcl, | |
| 1143 | -@ ## we could use the following instead: | |
| 1144 | -@ ## | |
| 1145 | -@ ## set idx(next) [string first / $baseurl $idx(first+1)] | |
| 1146 | -@ ## | |
| 1147 | -@ set idx(nextRange) [string range $baseurl $idx(first+1) end] | |
| 1148 | -@ set idx(next) [string first / $idx(nextRange)] | |
| 1149 | -@ if {$idx(next) != -1} { | |
| 1150 | -@ ## | |
| 1151 | -@ ## NOTE: (part 2) Add the necessary offset to the result of the | |
| 1152 | -@ ## search for the next slash (i.e. the one after the initial | |
| 1153 | -@ ## search for the two slashes). | |
| 1154 | -@ ## | |
| 1155 | -@ set idx(next) [expr {$idx(next) + $idx(first+1)}] | |
| 1156 | -@ ## | |
| 1157 | -@ ## NOTE: Back up one character from the next slash. | |
| 1158 | -@ ## | |
| 1159 | -@ set idx(next-1) [expr {$idx(next) - 1}] | |
| 1160 | -@ ## | |
| 1161 | -@ ## NOTE: Extract the URI scheme and host from the base URL. | |
| 1162 | -@ ## | |
| 1163 | -@ set scheme [string range $baseurl 0 $idx(first)] | |
| 1164 | -@ set host [string range $baseurl $idx(first+1) $idx(next-1)] | |
| 1165 | -@ ## | |
| 1166 | -@ ## NOTE: Try to stay in SSL mode if we are there now. | |
| 1167 | -@ ## | |
| 1168 | -@ if {[string compare $scheme http:/] == 0} { | |
| 1169 | -@ set scheme http:// | |
| 1170 | -@ } else { | |
| 1171 | -@ set scheme https:// | |
| 1172 | -@ } | |
| 1173 | -@ set logourl $scheme$host/ | |
| 1174 | -@ } else { | |
| 1175 | -@ set logourl $baseurl | |
| 1176 | -@ } | |
| 1177 | -@ } else { | |
| 1178 | -@ set logourl $baseurl | |
| 1179 | -@ } | |
| 1180 | -@ return $logourl | |
| 1181 | -@ } | |
| 1182 | -@ set logourl [getLogoUrl $baseurl] | |
| 1183 | -@ </th1> | |
| 1184 | -@ <a href="$logourl"> | |
| 1185 | -@ <img src="$logo_image_url" border="0" alt="$project_name"> | |
| 1186 | -@ </a> | |
| 1187 | -@ </div> | |
| 1188 | -@ <div class="title"><small>$<project_name></small><br />$<title></div> | |
| 1189 | -@ <div class="status"><th1> | |
| 1190 | -@ if {[info exists login]} { | |
| 1191 | -@ puts "Logged in as $login" | |
| 1192 | -@ } else { | |
| 1193 | -@ puts "Not logged in" | |
| 1194 | -@ } | |
| 1195 | -@ </th1></div> | |
| 1196 | -@ </div> | |
| 1197 | -@ <div class="mainmenu"> | |
| 1198 | -@ <th1> | |
| 1199 | -@ html "<a href=''$home$index_page''>Home</a>\n" | |
| 1200 | -@ if {[anycap jor]} { | |
| 1201 | -@ html "<a href=''$home/timeline''>Timeline</a>\n" | |
| 1202 | -@ } | |
| 1203 | -@ if {[hascap oh]} { | |
| 1204 | -@ html "<a href=''$home/tree?ci=tip''>Files</a>\n" | |
| 1205 | -@ } | |
| 1206 | -@ if {[hascap o]} { | |
| 1207 | -@ html "<a href=''$home/brlist''>Branches</a>\n" | |
| 1208 | -@ html "<a href=''$home/taglist''>Tags</a>\n" | |
| 1209 | -@ } | |
| 1210 | -@ if {[hascap r]} { | |
| 1211 | -@ html "<a href=''$home/reportlist''>Tickets</a>\n" | |
| 1212 | -@ } | |
| 1213 | -@ if {[hascap j]} { | |
| 1214 | -@ html "<a href=''$home/wiki''>Wiki</a>\n" | |
| 1215 | -@ } | |
| 1216 | -@ if {[hascap s]} { | |
| 1217 | -@ html "<a href=''$home/setup''>Admin</a>\n" | |
| 1218 | -@ } elseif {[hascap a]} { | |
| 1219 | -@ html "<a href=''$home/setup_ulist''>Users</a>\n" | |
| 1220 | -@ } | |
| 1221 | -@ if {[info exists login]} { | |
| 1222 | -@ html "<a href=''$home/login''>Logout</a>\n" | |
| 1223 | -@ } else { | |
| 1224 | -@ html "<a href=''$home/login''>Login</a>\n" | |
| 1225 | -@ } | |
| 1226 | -@ </th1></div> | |
| 1227 | -@ '); | |
| 1228 | -@ REPLACE INTO config(name,mtime,value) | |
| 1229 | -@ VALUES('footer',now(),'<div class="footer"> | |
| 1230 | -@ <th1> | |
| 1231 | -@ proc getTclVersion {} { | |
| 1232 | -@ if {[catch {tclEval info patchlevel} tclVersion] == 0} { | |
| 1233 | -@ return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" | |
| 1234 | -@ } | |
| 1235 | -@ return "" | |
| 1236 | -@ } | |
| 1237 | -@ proc getVersion { version } { | |
| 1238 | -@ set length [string length $version] | |
| 1239 | -@ return [string range $version 1 [expr {$length - 2}]] | |
| 1240 | -@ } | |
| 1241 | -@ set version [getVersion $manifest_version] | |
| 1242 | -@ set tclVersion [getTclVersion] | |
| 1243 | -@ set fossilUrl http://www.fossil-scm.org | |
| 1244 | -@ </th1> | |
| 1245 | -@ This page was generated in about | |
| 1246 | -@ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by | |
| 1247 | -@ <a href="$fossilUrl/">Fossil</a> | |
| 1248 | -@ version $release_version $tclVersion | |
| 1249 | -@ <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> | |
| 1250 | -@ <a href="$fossilUrl/index.html/timeline?c=$manifest_date&y=ci">$manifest_date</a> | |
| 1251 | -@ </div> | |
| 1252 | -@ </body></html> | |
| 1253 | -@ '); | |
| 1254 | -; | |
| 1255 | - | |
| 1256 | 24 | /* |
| 1257 | 25 | ** An array of available built-in skins. |
| 26 | +** | |
| 27 | +** To add new built-in skins: | |
| 28 | +** | |
| 29 | +** 1. Pick a name for the new skin. (Here we use "xyzzy"). | |
| 30 | +** | |
| 31 | +** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt, | |
| 32 | +** and skins/xyzzy/footer.txt into the source tree. | |
| 33 | +** | |
| 34 | +** 3. Rerun "tclsh makemake.tcl" in the src/ folder in order to | |
| 35 | +** rebuild the makefiles to reference the new CSS, headers, and footers. | |
| 36 | +** | |
| 37 | +** 4. Make an entry in the following array for the new skin. | |
| 1258 | 38 | */ |
| 1259 | 39 | static struct BuiltinSkin { |
| 1260 | - const char *zName; | |
| 1261 | - const char *zValue; | |
| 40 | + const char *zDesc; /* Description of this skin */ | |
| 41 | + const char *zLabel; /* The directory under skins/ holding this skin */ | |
| 42 | + char *zSQL; /* Filled in at run-time with SQL to insert this skin */ | |
| 1262 | 43 | } aBuiltinSkin[] = { |
| 1263 | - { "Default", 0 /* Filled in at runtime */ }, | |
| 1264 | - { "Plain Gray, No Logo", zBuiltinSkin1 }, | |
| 1265 | - { "Khaki, No Logo", zBuiltinSkin2 }, | |
| 1266 | - { "Black & White, Menu on Left", zBuiltinSkin3 }, | |
| 1267 | - { "Shadow boxes & Rounded Corners", zBuiltinSkin4 }, | |
| 1268 | - { "Enhanced Default", zBuiltinSkin5 }, | |
| 44 | + { "Default", "default", 0 }, | |
| 45 | + { "Plain Gray, No Logo", "plain_gray", 0 }, | |
| 46 | + { "Khaki, No Logo", "khaki", 0 }, | |
| 47 | + { "Black & White, Menu on Left", "black_and_white", 0 }, | |
| 48 | + { "Shadow boxes & Rounded Corners", "rounded1", 0 }, | |
| 49 | + { "Enhanced Default", "enhanced1", 0 }, | |
| 50 | + { "San Francisco Modern", "etienne1", 0 }, | |
| 51 | + { "Eagle", "eagle", 0 }, | |
| 1269 | 52 | }; |
| 1270 | 53 | |
| 1271 | 54 | /* |
| 1272 | 55 | ** For a skin named zSkinName, compute the name of the CONFIG table |
| 1273 | 56 | ** entry where that skin is stored and return it. |
| @@ -1286,57 +69,158 @@ | ||
| 1286 | 69 | } |
| 1287 | 70 | return z; |
| 1288 | 71 | } |
| 1289 | 72 | |
| 1290 | 73 | /* |
| 1291 | -** Construct and return a string that represents the current skin if | |
| 1292 | -** useDefault==0 or a string for the default skin if useDefault==1. | |
| 74 | +** Return true if there exists a skin name "zSkinName". | |
| 75 | +*/ | |
| 76 | +static int skinExists(const char *zSkinName){ | |
| 77 | + int i; | |
| 78 | + if( zSkinName==0 ) return 0; | |
| 79 | + for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ | |
| 80 | + if( fossil_strcmp(zSkinName, aBuiltinSkin[i].zDesc)==0 ) return 1; | |
| 81 | + } | |
| 82 | + return db_exists("SELECT 1 FROM config WHERE name='skin:%q'", zSkinName); | |
| 83 | +} | |
| 84 | + | |
| 85 | +/* | |
| 86 | +** Construct and return an string of SQL statements that represents | |
| 87 | +** a "skin" setting. If zName==0 then return the skin currently | |
| 88 | +** installed. Otherwise, return one of the built-in skins designated | |
| 89 | +** by zName. | |
| 1293 | 90 | ** |
| 1294 | 91 | ** Memory to hold the returned string is obtained from malloc. |
| 1295 | 92 | */ |
| 1296 | -static char *getSkin(int useDefault){ | |
| 93 | +static char *getSkin(const char *zName){ | |
| 94 | + const char *z; | |
| 95 | + char *zLabel; | |
| 96 | + static const char *azType[] = { "css", "header", "footer" }; | |
| 97 | + int i; | |
| 1297 | 98 | Blob val; |
| 1298 | 99 | blob_zero(&val); |
| 1299 | - blob_appendf(&val, | |
| 1300 | - "REPLACE INTO config(name,value,mtime) VALUES('css',%Q,now());\n", | |
| 1301 | - useDefault ? zDefaultCSS : db_get("css", (char*)zDefaultCSS) | |
| 1302 | - ); | |
| 1303 | - blob_appendf(&val, | |
| 1304 | - "REPLACE INTO config(name,value,mtime) VALUES('header',%Q,now());\n", | |
| 1305 | - useDefault ? zDefaultHeader : db_get("header", (char*)zDefaultHeader) | |
| 1306 | - ); | |
| 1307 | - blob_appendf(&val, | |
| 1308 | - "REPLACE INTO config(name,value,mtime) VALUES('footer',%Q,now());\n", | |
| 1309 | - useDefault ? zDefaultFooter : db_get("footer", (char*)zDefaultFooter) | |
| 1310 | - ); | |
| 100 | + for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){ | |
| 101 | + if( zName ){ | |
| 102 | + zLabel = mprintf("skins/%s/%s.txt", zName, azType[i]); | |
| 103 | + z = builtin_text(zLabel); | |
| 104 | + fossil_free(zLabel); | |
| 105 | + }else{ | |
| 106 | + z = db_get(azType[i], 0); | |
| 107 | + if( z==0 ){ | |
| 108 | + zLabel = mprintf("skins/default/%s.txt", azType[i]); | |
| 109 | + z = builtin_text(zLabel); | |
| 110 | + fossil_free(zLabel); | |
| 111 | + } | |
| 112 | + } | |
| 113 | + blob_appendf(&val, | |
| 114 | + "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now());\n", | |
| 115 | + azType[i], z | |
| 116 | + ); | |
| 117 | + } | |
| 1311 | 118 | return blob_str(&val); |
| 1312 | 119 | } |
| 1313 | 120 | |
| 1314 | 121 | /* |
| 1315 | -** Construct the default skin string and fill in the corresponding | |
| 1316 | -** entry in aBuildinSkin[] | |
| 122 | +** Respond to a Rename button press. Return TRUE if a dialog was painted. | |
| 123 | +** Return FALSE to continue with the main Skins page. | |
| 124 | +*/ | |
| 125 | +static int skinRename(void){ | |
| 126 | + const char *zOldName; | |
| 127 | + const char *zNewName; | |
| 128 | + int ex = 0; | |
| 129 | + if( P("rename")==0 ) return 0; | |
| 130 | + zOldName = P("sn"); | |
| 131 | + zNewName = P("newname"); | |
| 132 | + if( zOldName==0 ) return 0; | |
| 133 | + if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){ | |
| 134 | + if( zNewName==0 ) zNewName = zOldName; | |
| 135 | + style_header("Rename A Skin"); | |
| 136 | + if( ex ){ | |
| 137 | + @ <p><span class="generalError">There is already another skin | |
| 138 | + @ named "%h(zNewName)". Choose a different name.</span></p> | |
| 139 | + } | |
| 140 | + @ <form action="%s(g.zTop)/setup_skin" method="post"><div> | |
| 141 | + @ <table border="0"><tr> | |
| 142 | + @ <tr><td align="right">Current name:<td align="left"><b>%h(zOldName)</b> | |
| 143 | + @ <tr><td align="right">New name:<td align="left"> | |
| 144 | + @ <input type="text" size="35" name="newname" value="%h(zNewName)"> | |
| 145 | + @ <tr><td><td> | |
| 146 | + @ <input type="hidden" name="sn" value="%h(zOldName)"> | |
| 147 | + @ <input type="submit" name="rename" value="Rename"> | |
| 148 | + @ <input type="submit" name="canren" value="Cancel"> | |
| 149 | + @ </table> | |
| 150 | + login_insert_csrf_secret(); | |
| 151 | + @ </div></form> | |
| 152 | + style_footer(); | |
| 153 | + return 1; | |
| 154 | + } | |
| 155 | + db_multi_exec( | |
| 156 | + "UPDATE config SET name='skin:%q' WHERE name='skin:%q';", | |
| 157 | + zNewName, zOldName | |
| 158 | + ); | |
| 159 | + return 0; | |
| 160 | +} | |
| 161 | + | |
| 162 | +/* | |
| 163 | +** Respond to a Save button press. Return TRUE if a dialog was painted. | |
| 164 | +** Return FALSE to continue with the main Skins page. | |
| 1317 | 165 | */ |
| 1318 | -static void setDefaultSkin(void){ | |
| 1319 | - aBuiltinSkin[0].zValue = getSkin(1); | |
| 166 | +static int skinSave(const char *zCurrent){ | |
| 167 | + const char *zNewName; | |
| 168 | + int ex = 0; | |
| 169 | + if( P("save")==0 ) return 0; | |
| 170 | + zNewName = P("svname"); | |
| 171 | + if( zNewName && zNewName[0]!=0 ){ | |
| 172 | + } | |
| 173 | + if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){ | |
| 174 | + if( zNewName==0 ) zNewName = ""; | |
| 175 | + style_header("Save Current Skin"); | |
| 176 | + if( ex ){ | |
| 177 | + @ <p><span class="generalError">There is already another skin | |
| 178 | + @ named "%h(zNewName)". Choose a different name.</span></p> | |
| 179 | + } | |
| 180 | + @ <form action="%s(g.zTop)/setup_skin" method="post"><div> | |
| 181 | + @ <table border="0"><tr> | |
| 182 | + @ <tr><td align="right">Name for this skin:<td align="left"> | |
| 183 | + @ <input type="text" size="35" name="svname" value="%h(zNewName)"> | |
| 184 | + @ <tr><td><td> | |
| 185 | + @ <input type="submit" name="save" value="Save"> | |
| 186 | + @ <input type="submit" name="cansave" value="Cancel"> | |
| 187 | + @ </table> | |
| 188 | + login_insert_csrf_secret(); | |
| 189 | + @ </div></form> | |
| 190 | + style_footer(); | |
| 191 | + return 1; | |
| 192 | + } | |
| 193 | + db_multi_exec( | |
| 194 | + "INSERT OR IGNORE INTO config(name, value, mtime)" | |
| 195 | + "VALUES('skin:%q',%Q,now())", | |
| 196 | + zNewName, zCurrent | |
| 197 | + ); | |
| 198 | + return 0; | |
| 1320 | 199 | } |
| 1321 | 200 | |
| 1322 | 201 | /* |
| 1323 | 202 | ** WEBPAGE: setup_skin |
| 1324 | 203 | */ |
| 1325 | 204 | void setup_skin(void){ |
| 1326 | 205 | const char *z; |
| 1327 | 206 | char *zName; |
| 1328 | 207 | char *zErr = 0; |
| 1329 | - const char *zCurrent; /* Current skin */ | |
| 1330 | - int i; /* Loop counter */ | |
| 208 | + const char *zCurrent = 0; /* Current skin */ | |
| 209 | + int i; /* Loop counter */ | |
| 1331 | 210 | Stmt q; |
| 211 | + int seenCurrent = 0; | |
| 1332 | 212 | |
| 1333 | 213 | login_check_credentials(); |
| 1334 | 214 | if( !g.perm.Setup ){ |
| 1335 | 215 | login_needed(); |
| 1336 | 216 | } |
| 1337 | 217 | db_begin_transaction(); |
| 218 | + zCurrent = getSkin(0); | |
| 219 | + for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ | |
| 220 | + aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel); | |
| 221 | + } | |
| 1338 | 222 | |
| 1339 | 223 | /* Process requests to delete a user-defined skin */ |
| 1340 | 224 | if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 1341 | 225 | style_header("Confirm Custom Skin Delete"); |
| 1342 | 226 | @ <form action="%s(g.zTop)/setup_skin" method="post"><div> |
| @@ -1351,51 +235,42 @@ | ||
| 1351 | 235 | return; |
| 1352 | 236 | } |
| 1353 | 237 | if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 1354 | 238 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 1355 | 239 | } |
| 1356 | - | |
| 1357 | - setDefaultSkin(); | |
| 1358 | - zCurrent = getSkin(0); | |
| 1359 | - | |
| 1360 | - if( P("save")!=0 && (zName = skinVarName(P("save"),0))!=0 ){ | |
| 1361 | - if( db_exists("SELECT 1 FROM config WHERE name=%Q", zName) | |
| 1362 | - || fossil_strcmp(zName, "Default")==0 ){ | |
| 1363 | - zErr = mprintf("Skin name \"%h\" already exists. " | |
| 1364 | - "Choose a different name.", P("sn")); | |
| 1365 | - }else{ | |
| 1366 | - db_multi_exec("INSERT INTO config(name,value,mtime) VALUES(%Q,%Q,now())", | |
| 1367 | - zName, zCurrent | |
| 1368 | - ); | |
| 1369 | - } | |
| 1370 | - } | |
| 1371 | - | |
| 1372 | - /* The user pressed the "Use This Skin" button. */ | |
| 240 | + if( skinRename() ) return; | |
| 241 | + if( skinSave(zCurrent) ) return; | |
| 242 | + | |
| 243 | + /* The user pressed one of the "Install" buttons. */ | |
| 1373 | 244 | if( P("load") && (z = P("sn"))!=0 && z[0] ){ |
| 1374 | 245 | int seen = 0; |
| 246 | + | |
| 247 | + /* Check to see if the current skin is already saved. If it is, there | |
| 248 | + ** is no need to create a backup */ | |
| 249 | + zCurrent = getSkin(0); | |
| 1375 | 250 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 1376 | - if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ | |
| 251 | + if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ | |
| 1377 | 252 | seen = 1; |
| 1378 | 253 | break; |
| 1379 | 254 | } |
| 1380 | 255 | } |
| 1381 | 256 | if( !seen ){ |
| 1382 | 257 | seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" |
| 1383 | 258 | " AND value=%Q", zCurrent); |
| 1384 | - } | |
| 1385 | - if( !seen ){ | |
| 1386 | - db_multi_exec( | |
| 1387 | - "INSERT INTO config(name,value,mtime) VALUES(" | |
| 1388 | - " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," | |
| 1389 | - " %Q,now())", zCurrent | |
| 1390 | - ); | |
| 259 | + if( !seen ){ | |
| 260 | + db_multi_exec( | |
| 261 | + "INSERT INTO config(name,value,mtime) VALUES(" | |
| 262 | + " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," | |
| 263 | + " %Q,now())", zCurrent | |
| 264 | + ); | |
| 265 | + } | |
| 1391 | 266 | } |
| 1392 | 267 | seen = 0; |
| 1393 | 268 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 1394 | - if( fossil_strcmp(aBuiltinSkin[i].zName, z)==0 ){ | |
| 269 | + if( fossil_strcmp(aBuiltinSkin[i].zDesc, z)==0 ){ | |
| 1395 | 270 | seen = 1; |
| 1396 | - zCurrent = aBuiltinSkin[i].zValue; | |
| 271 | + zCurrent = aBuiltinSkin[i].zSQL; | |
| 1397 | 272 | db_multi_exec("%s", zCurrent/*safe-for-%s*/); |
| 1398 | 273 | break; |
| 1399 | 274 | } |
| 1400 | 275 | } |
| 1401 | 276 | if( !seen ){ |
| @@ -1409,48 +284,59 @@ | ||
| 1409 | 284 | if( zErr ){ |
| 1410 | 285 | @ <p><font color="red">%h(zErr)</font></p> |
| 1411 | 286 | } |
| 1412 | 287 | @ <p>A "skin" is a combination of |
| 1413 | 288 | @ <a href="setup_editcss">CSS</a>, |
| 1414 | - @ <a href="setup_header">Header</a>, | |
| 1415 | - @ <a href="setup_footer">Footer</a>, and | |
| 1416 | - @ <a href="setup_logo">Logo</a> that determines the look and feel | |
| 289 | + @ <a href="setup_header">Header</a>, and | |
| 290 | + @ <a href="setup_footer">Footer</a> that determines the look and feel | |
| 1417 | 291 | @ of the web interface.</p> |
| 1418 | 292 | @ |
| 1419 | 293 | @ <h2>Available Skins:</h2> |
| 1420 | - @ <ol> | |
| 294 | + @ <table border="0"> | |
| 1421 | 295 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 1422 | - z = aBuiltinSkin[i].zName; | |
| 1423 | - if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ | |
| 1424 | - @ <li><p>%h(z). <b>Currently In Use</b></p> | |
| 296 | + z = aBuiltinSkin[i].zDesc; | |
| 297 | + @ <tr><td>%d(i+1).<td>%h(z)<td> <td> | |
| 298 | + if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ | |
| 299 | + @ (Currently In Use) | |
| 300 | + seenCurrent = 1; | |
| 1425 | 301 | }else{ |
| 1426 | - @ <li><form action="%s(g.zTop)/setup_skin" method="post"><div> | |
| 1427 | - @ %h(z). | |
| 302 | + @ <form action="%s(g.zTop)/setup_skin" method="post"> | |
| 1428 | 303 | @ <input type="hidden" name="sn" value="%h(z)" /> |
| 1429 | - @ <input type="submit" name="load" value="Use This Skin" /> | |
| 1430 | - @ </div></form></li> | |
| 304 | + @ <input type="submit" name="load" value="Install" /> | |
| 305 | + @ </form> | |
| 1431 | 306 | } |
| 307 | + @ </tr> | |
| 1432 | 308 | } |
| 1433 | 309 | db_prepare(&q, |
| 1434 | 310 | "SELECT substr(name, 6), value FROM config" |
| 1435 | 311 | " WHERE name GLOB 'skin:*'" |
| 1436 | 312 | " ORDER BY name" |
| 1437 | 313 | ); |
| 1438 | 314 | while( db_step(&q)==SQLITE_ROW ){ |
| 1439 | 315 | const char *zN = db_column_text(&q, 0); |
| 1440 | 316 | const char *zV = db_column_text(&q, 1); |
| 1441 | - if( fossil_strcmp(zV, zCurrent)==0 ){ | |
| 1442 | - @ <li><p>%h(zN). <b>Currently In Use</b></p> | |
| 1443 | - }else{ | |
| 1444 | - @ <li><form action="%s(g.zTop)/setup_skin" method="post"> | |
| 1445 | - @ %h(zN). | |
| 1446 | - @ <input type="hidden" name="sn" value="%h(zN)"> | |
| 1447 | - @ <input type="submit" name="load" value="Use This Skin"> | |
| 1448 | - @ <input type="submit" name="del1" value="Delete This Skin"> | |
| 1449 | - @ </form></li> | |
| 1450 | - } | |
| 317 | + i++; | |
| 318 | + @ <tr><td>%d(i).<td>%h(zN)<td> <td> | |
| 319 | + @ <form action="%s(g.zTop)/setup_skin" method="post"> | |
| 320 | + if( fossil_strcmp(zV, zCurrent)==0 ){ | |
| 321 | + @ (Currently In Use) | |
| 322 | + seenCurrent = 1; | |
| 323 | + }else{ | |
| 324 | + @ <input type="submit" name="load" value="Install"> | |
| 325 | + @ <input type="submit" name="del1" value="Delete"> | |
| 326 | + } | |
| 327 | + @ <input type="submit" name="rename" value="Rename"> | |
| 328 | + @ <input type="hidden" name="sn" value="%h(zN)"> | |
| 329 | + @ </form></tr> | |
| 1451 | 330 | } |
| 1452 | 331 | db_finalize(&q); |
| 1453 | - @ </ol> | |
| 332 | + if( !seenCurrent ){ | |
| 333 | + i++; | |
| 334 | + @ <tr><td>%d(i).<td><i>Current Configuration</i><td> <td> | |
| 335 | + @ <form action="%s(g.zTop)/setup_skin" method="post"> | |
| 336 | + @ <input type="submit" name="save" value="Save"> | |
| 337 | + @ </form> | |
| 338 | + } | |
| 339 | + @ </table> | |
| 1454 | 340 | style_footer(); |
| 1455 | 341 | db_end_transaction(0); |
| 1456 | 342 | } |
| 1457 | 343 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -19,1255 +19,38 @@ | |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include <assert.h> |
| 22 | #include "skins.h" |
| 23 | |
| 24 | /* @-comment: ## */ |
| 25 | /* |
| 26 | ** A black-and-white theme with the project title in a bar across the top |
| 27 | ** and no logo image. |
| 28 | */ |
| 29 | static const char zBuiltinSkin1[] = |
| 30 | @ REPLACE INTO config(name,mtime,value) |
| 31 | @ VALUES('css',now(),'/* General settings for the entire page */ |
| 32 | @ body { |
| 33 | @ margin: 0ex 1ex; |
| 34 | @ padding: 0px; |
| 35 | @ background-color: white; |
| 36 | @ font-family: sans-serif; |
| 37 | @ } |
| 38 | @ |
| 39 | @ /* The project logo in the upper left-hand corner of each page */ |
| 40 | @ div.logo { |
| 41 | @ display: table-row; |
| 42 | @ text-align: center; |
| 43 | @ /* vertical-align: bottom;*/ |
| 44 | @ font-size: 2em; |
| 45 | @ font-weight: bold; |
| 46 | @ background-color: #707070; |
| 47 | @ color: #ffffff; |
| 48 | @ min-width: 200px; |
| 49 | @ white-space: nowrap; |
| 50 | @ } |
| 51 | @ |
| 52 | @ /* The page title centered at the top of each page */ |
| 53 | @ div.title { |
| 54 | @ display: table-cell; |
| 55 | @ font-size: 1.5em; |
| 56 | @ font-weight: bold; |
| 57 | @ text-align: center; |
| 58 | @ padding: 0 0 0 10px; |
| 59 | @ color: #404040; |
| 60 | @ vertical-align: bottom; |
| 61 | @ width: 100%; |
| 62 | @ } |
| 63 | @ |
| 64 | @ /* The login status message in the top right-hand corner */ |
| 65 | @ div.status { |
| 66 | @ display: table-cell; |
| 67 | @ text-align: right; |
| 68 | @ vertical-align: bottom; |
| 69 | @ color: #404040; |
| 70 | @ font-size: 0.8em; |
| 71 | @ font-weight: bold; |
| 72 | @ min-width: 200px; |
| 73 | @ white-space: nowrap; |
| 74 | @ } |
| 75 | @ |
| 76 | @ /* The header across the top of the page */ |
| 77 | @ div.header { |
| 78 | @ display: table; |
| 79 | @ width: 100%; |
| 80 | @ } |
| 81 | @ |
| 82 | @ /* The main menu bar that appears at the top of the page beneath |
| 83 | @ ** the header */ |
| 84 | @ div.mainmenu { |
| 85 | @ padding: 5px 10px 5px 10px; |
| 86 | @ font-size: 0.9em; |
| 87 | @ font-weight: bold; |
| 88 | @ text-align: center; |
| 89 | @ letter-spacing: 1px; |
| 90 | @ background-color: #404040; |
| 91 | @ color: white; |
| 92 | @ } |
| 93 | @ |
| 94 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 95 | @ div.submenu, div.sectionmenu { |
| 96 | @ padding: 3px 10px 3px 0px; |
| 97 | @ font-size: 0.9em; |
| 98 | @ text-align: center; |
| 99 | @ background-color: #606060; |
| 100 | @ color: white; |
| 101 | @ } |
| 102 | @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, |
| 103 | @ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { |
| 104 | @ padding: 3px 10px 3px 10px; |
| 105 | @ color: white; |
| 106 | @ text-decoration: none; |
| 107 | @ } |
| 108 | @ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { |
| 109 | @ color: #404040; |
| 110 | @ background-color: white; |
| 111 | @ } |
| 112 | @ |
| 113 | @ /* All page content from the bottom of the menu or submenu down to |
| 114 | @ ** the footer */ |
| 115 | @ div.content { |
| 116 | @ padding: 0ex 0ex 0ex 0ex; |
| 117 | @ } |
| 118 | @ /* Hyperlink colors */ |
| 119 | @ div.content a { color: #604000; } |
| 120 | @ div.content a:link { color: #604000;} |
| 121 | @ div.content a:visited { color: #600000; } |
| 122 | @ |
| 123 | @ /* <verbatim> blocks */ |
| 124 | @ pre.verbatim { |
| 125 | @ background-color: #ffffff; |
| 126 | @ padding: 0.5em; |
| 127 | @ white-space: pre-wrap; |
| 128 | @ } |
| 129 | @ |
| 130 | @ /* Some pages have section dividers */ |
| 131 | @ div.section { |
| 132 | @ margin-bottom: 0px; |
| 133 | @ margin-top: 1em; |
| 134 | @ padding: 1px 1px 1px 1px; |
| 135 | @ font-size: 1.2em; |
| 136 | @ font-weight: bold; |
| 137 | @ background-color: #404040; |
| 138 | @ color: white; |
| 139 | @ white-space: nowrap; |
| 140 | @ } |
| 141 | @ |
| 142 | @ /* The "Date" that occurs on the left hand side of timelines */ |
| 143 | @ div.divider { |
| 144 | @ background: #a0a0a0; |
| 145 | @ border: 2px #505050 solid; |
| 146 | @ font-size: 1em; font-weight: normal; |
| 147 | @ padding: .25em; |
| 148 | @ margin: .2em 0 .2em 0; |
| 149 | @ float: left; |
| 150 | @ clear: left; |
| 151 | @ white-space: nowrap; |
| 152 | @ } |
| 153 | @ |
| 154 | @ /* The footer at the very bottom of the page */ |
| 155 | @ div.footer { |
| 156 | @ font-size: 0.8em; |
| 157 | @ margin-top: 12px; |
| 158 | @ padding: 5px 10px 5px 10px; |
| 159 | @ text-align: right; |
| 160 | @ background-color: #404040; |
| 161 | @ color: white; |
| 162 | @ } |
| 163 | @ |
| 164 | @ /* The label/value pairs on (for example) the vinfo page */ |
| 165 | @ table.label-value th { |
| 166 | @ vertical-align: top; |
| 167 | @ text-align: right; |
| 168 | @ padding: 0.2ex 2ex; |
| 169 | @ }'); |
| 170 | @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> |
| 171 | @ <head> |
| 172 | @ <base href="$baseurl/$current_page" /> |
| 173 | @ <title>$<project_name>: $<title></title> |
| 174 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 175 | @ href="$home/timeline.rss"> |
| 176 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" |
| 177 | @ media="screen"> |
| 178 | @ </head> |
| 179 | @ <body> |
| 180 | @ <div class="header"> |
| 181 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 182 | @ <div class="status"><th1> |
| 183 | @ if {[info exists login]} { |
| 184 | @ puts "Logged in as $login" |
| 185 | @ } else { |
| 186 | @ puts "Not logged in" |
| 187 | @ } |
| 188 | @ </th1></div> |
| 189 | @ </div> |
| 190 | @ <div class="mainmenu"> |
| 191 | @ <th1> |
| 192 | @ html "<a href=''$home$index_page''>Home</a>\n" |
| 193 | @ if {[anycap jor]} { |
| 194 | @ html "<a href=''$home/timeline''>Timeline</a>\n" |
| 195 | @ } |
| 196 | @ if {[hascap oh]} { |
| 197 | @ html "<a href=''$home/tree?ci=tip''>Files</a>\n" |
| 198 | @ } |
| 199 | @ if {[hascap o]} { |
| 200 | @ html "<a href=''$home/brlist''>Branches</a>\n" |
| 201 | @ html "<a href=''$home/taglist''>Tags</a>\n" |
| 202 | @ } |
| 203 | @ if {[hascap r]} { |
| 204 | @ html "<a href=''$home/reportlist''>Tickets</a>\n" |
| 205 | @ } |
| 206 | @ if {[hascap j]} { |
| 207 | @ html "<a href=''$home/wiki''>Wiki</a>\n" |
| 208 | @ } |
| 209 | @ if {[hascap s]} { |
| 210 | @ html "<a href=''$home/setup''>Admin</a>\n" |
| 211 | @ } elseif {[hascap a]} { |
| 212 | @ html "<a href=''$home/setup_ulist''>Users</a>\n" |
| 213 | @ } |
| 214 | @ if {[info exists login]} { |
| 215 | @ html "<a href=''$home/login''>Logout</a>\n" |
| 216 | @ } else { |
| 217 | @ html "<a href=''$home/login''>Login</a>\n" |
| 218 | @ } |
| 219 | @ </th1></div> |
| 220 | @ '); |
| 221 | @ REPLACE INTO config(name,mtime,value) |
| 222 | @ VALUES('footer',now(),'<div class="footer"> |
| 223 | @ Fossil version $manifest_version $manifest_date |
| 224 | @ </div> |
| 225 | @ </body></html> |
| 226 | @ '); |
| 227 | ; |
| 228 | |
| 229 | /* |
| 230 | ** A tan theme with the project title above the user identification |
| 231 | ** and no logo image. |
| 232 | */ |
| 233 | static const char zBuiltinSkin2[] = |
| 234 | @ REPLACE INTO config(name,mtime,value) |
| 235 | @ VALUES('css',now(),'/* General settings for the entire page */ |
| 236 | @ body { |
| 237 | @ margin: 0ex 0ex; |
| 238 | @ padding: 0px; |
| 239 | @ background-color: #fef3bc; |
| 240 | @ font-family: sans-serif; |
| 241 | @ } |
| 242 | @ |
| 243 | @ /* The project logo in the upper left-hand corner of each page */ |
| 244 | @ div.logo { |
| 245 | @ display: inline; |
| 246 | @ text-align: center; |
| 247 | @ vertical-align: bottom; |
| 248 | @ font-weight: bold; |
| 249 | @ font-size: 2.5em; |
| 250 | @ color: #a09048; |
| 251 | @ white-space: nowrap; |
| 252 | @ } |
| 253 | @ |
| 254 | @ /* The page title centered at the top of each page */ |
| 255 | @ div.title { |
| 256 | @ display: table-cell; |
| 257 | @ font-size: 2em; |
| 258 | @ font-weight: bold; |
| 259 | @ text-align: left; |
| 260 | @ padding: 0 0 0 5px; |
| 261 | @ color: #a09048; |
| 262 | @ vertical-align: bottom; |
| 263 | @ width: 100%; |
| 264 | @ } |
| 265 | @ |
| 266 | @ /* The login status message in the top right-hand corner */ |
| 267 | @ div.status { |
| 268 | @ display: table-cell; |
| 269 | @ text-align: right; |
| 270 | @ vertical-align: bottom; |
| 271 | @ color: #a09048; |
| 272 | @ padding: 5px 5px 0 0; |
| 273 | @ font-size: 0.8em; |
| 274 | @ font-weight: bold; |
| 275 | @ white-space: nowrap; |
| 276 | @ } |
| 277 | @ |
| 278 | @ /* The header across the top of the page */ |
| 279 | @ div.header { |
| 280 | @ display: table; |
| 281 | @ width: 100%; |
| 282 | @ } |
| 283 | @ |
| 284 | @ /* The main menu bar that appears at the top of the page beneath |
| 285 | @ ** the header */ |
| 286 | @ div.mainmenu { |
| 287 | @ padding: 5px 10px 5px 10px; |
| 288 | @ font-size: 0.9em; |
| 289 | @ font-weight: bold; |
| 290 | @ text-align: center; |
| 291 | @ letter-spacing: 1px; |
| 292 | @ background-color: #a09048; |
| 293 | @ color: black; |
| 294 | @ } |
| 295 | @ |
| 296 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 297 | @ div.submenu, div.sectionmenu { |
| 298 | @ padding: 3px 10px 3px 0px; |
| 299 | @ font-size: 0.9em; |
| 300 | @ text-align: center; |
| 301 | @ background-color: #c0af58; |
| 302 | @ color: white; |
| 303 | @ } |
| 304 | @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, |
| 305 | @ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { |
| 306 | @ padding: 3px 10px 3px 10px; |
| 307 | @ color: white; |
| 308 | @ text-decoration: none; |
| 309 | @ } |
| 310 | @ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { |
| 311 | @ color: #a09048; |
| 312 | @ background-color: white; |
| 313 | @ } |
| 314 | @ |
| 315 | @ /* All page content from the bottom of the menu or submenu down to |
| 316 | @ ** the footer */ |
| 317 | @ div.content { |
| 318 | @ padding: 1ex 5px; |
| 319 | @ } |
| 320 | @ div.content a { color: #706532; } |
| 321 | @ div.content a:link { color: #706532; } |
| 322 | @ div.content a:visited { color: #704032; } |
| 323 | @ div.content a:hover { background-color: white; color: #706532; } |
| 324 | @ |
| 325 | @ /* Some pages have section dividers */ |
| 326 | @ div.section { |
| 327 | @ margin-bottom: 0px; |
| 328 | @ margin-top: 1em; |
| 329 | @ padding: 3px 3px 0 3px; |
| 330 | @ font-size: 1.2em; |
| 331 | @ font-weight: bold; |
| 332 | @ background-color: #a09048; |
| 333 | @ color: white; |
| 334 | @ white-space: nowrap; |
| 335 | @ } |
| 336 | @ |
| 337 | @ /* The "Date" that occurs on the left hand side of timelines */ |
| 338 | @ div.divider { |
| 339 | @ background: #e1d498; |
| 340 | @ border: 2px #a09048 solid; |
| 341 | @ font-size: 1em; font-weight: normal; |
| 342 | @ padding: .25em; |
| 343 | @ margin: .2em 0 .2em 0; |
| 344 | @ float: left; |
| 345 | @ clear: left; |
| 346 | @ white-space: nowrap; |
| 347 | @ } |
| 348 | @ |
| 349 | @ /* The footer at the very bottom of the page */ |
| 350 | @ div.footer { |
| 351 | @ font-size: 0.8em; |
| 352 | @ margin-top: 12px; |
| 353 | @ padding: 5px 10px 5px 10px; |
| 354 | @ text-align: right; |
| 355 | @ background-color: #a09048; |
| 356 | @ color: white; |
| 357 | @ } |
| 358 | @ |
| 359 | @ /* Hyperlink colors */ |
| 360 | @ div.footer a { color: white; } |
| 361 | @ div.footer a:link { color: white; } |
| 362 | @ div.footer a:visited { color: white; } |
| 363 | @ div.footer a:hover { background-color: white; color: #558195; } |
| 364 | @ |
| 365 | @ /* <verbatim> blocks */ |
| 366 | @ pre.verbatim { |
| 367 | @ background-color: #f5f5f5; |
| 368 | @ padding: 0.5em; |
| 369 | @ white-space: pre-wrap; |
| 370 | @ } |
| 371 | @ |
| 372 | @ /* The label/value pairs on (for example) the ci page */ |
| 373 | @ table.label-value th { |
| 374 | @ vertical-align: top; |
| 375 | @ text-align: right; |
| 376 | @ padding: 0.2ex 2ex; |
| 377 | @ }'); |
| 378 | @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> |
| 379 | @ <head> |
| 380 | @ <base href="$baseurl/$current_page" /> |
| 381 | @ <title>$<project_name>: $<title></title> |
| 382 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 383 | @ href="$home/timeline.rss"> |
| 384 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" |
| 385 | @ media="screen"> |
| 386 | @ </head> |
| 387 | @ <body> |
| 388 | @ <div class="header"> |
| 389 | @ <div class="title">$<title></div> |
| 390 | @ <div class="status"> |
| 391 | @ <div class="logo">$<project_name></div><br/> |
| 392 | @ <th1> |
| 393 | @ if {[info exists login]} { |
| 394 | @ puts "Logged in as $login" |
| 395 | @ } else { |
| 396 | @ puts "Not logged in" |
| 397 | @ } |
| 398 | @ </th1></div> |
| 399 | @ </div> |
| 400 | @ <div class="mainmenu"> |
| 401 | @ <th1> |
| 402 | @ html "<a href=''$home$index_page''>Home</a>\n" |
| 403 | @ if {[anycap jor]} { |
| 404 | @ html "<a href=''$home/timeline''>Timeline</a>\n" |
| 405 | @ } |
| 406 | @ if {[hascap oh]} { |
| 407 | @ html "<a href=''$home/tree?ci=tip''>Files</a>\n" |
| 408 | @ } |
| 409 | @ if {[hascap o]} { |
| 410 | @ html "<a href=''$home/brlist''>Branches</a>\n" |
| 411 | @ html "<a href=''$home/taglist''>Tags</a>\n" |
| 412 | @ } |
| 413 | @ if {[hascap r]} { |
| 414 | @ html "<a href=''$home/reportlist''>Tickets</a>\n" |
| 415 | @ } |
| 416 | @ if {[hascap j]} { |
| 417 | @ html "<a href=''$home/wiki''>Wiki</a>\n" |
| 418 | @ } |
| 419 | @ if {[hascap s]} { |
| 420 | @ html "<a href=''$home/setup''>Admin</a>\n" |
| 421 | @ } elseif {[hascap a]} { |
| 422 | @ html "<a href=''$home/setup_ulist''>Users</a>\n" |
| 423 | @ } |
| 424 | @ if {[info exists login]} { |
| 425 | @ html "<a href=''$home/login''>Logout</a>\n" |
| 426 | @ } else { |
| 427 | @ html "<a href=''$home/login''>Login</a>\n" |
| 428 | @ } |
| 429 | @ </th1></div> |
| 430 | @ '); |
| 431 | @ REPLACE INTO config(name,mtime,value) |
| 432 | @ VALUES('footer',now(),'<div class="footer"> |
| 433 | @ Fossil version $manifest_version $manifest_date |
| 434 | @ </div> |
| 435 | @ </body></html> |
| 436 | @ '); |
| 437 | ; |
| 438 | |
| 439 | /* |
| 440 | ** Black letters on a white or cream background with the main menu |
| 441 | ** stuck on the left-hand side. |
| 442 | */ |
| 443 | static const char zBuiltinSkin3[] = |
| 444 | @ REPLACE INTO config(name,mtime,value) |
| 445 | @ VALUES('css',now(),'/* General settings for the entire page */ |
| 446 | @ body { |
| 447 | @ margin:0px 0px 0px 0px; |
| 448 | @ padding:0px; |
| 449 | @ font-family:verdana, arial, helvetica, "sans serif"; |
| 450 | @ color:#333; |
| 451 | @ background-color:white; |
| 452 | @ } |
| 453 | @ |
| 454 | @ /* consistent colours */ |
| 455 | @ h2 { |
| 456 | @ color: #333; |
| 457 | @ } |
| 458 | @ h3 { |
| 459 | @ color: #333; |
| 460 | @ } |
| 461 | @ |
| 462 | @ /* The project logo in the upper left-hand corner of each page */ |
| 463 | @ div.logo { |
| 464 | @ display: table-cell; |
| 465 | @ text-align: left; |
| 466 | @ vertical-align: bottom; |
| 467 | @ font-weight: bold; |
| 468 | @ color: #333; |
| 469 | @ white-space: nowrap; |
| 470 | @ } |
| 471 | @ |
| 472 | @ /* The page title centered at the top of each page */ |
| 473 | @ div.title { |
| 474 | @ display: table-cell; |
| 475 | @ font-size: 2em; |
| 476 | @ font-weight: bold; |
| 477 | @ text-align: center; |
| 478 | @ color: #333; |
| 479 | @ vertical-align: bottom; |
| 480 | @ width: 100%; |
| 481 | @ } |
| 482 | @ |
| 483 | @ /* The login status message in the top right-hand corner */ |
| 484 | @ div.status { |
| 485 | @ display: table-cell; |
| 486 | @ padding-right: 10px; |
| 487 | @ text-align: right; |
| 488 | @ vertical-align: bottom; |
| 489 | @ padding-bottom: 5px; |
| 490 | @ color: #333; |
| 491 | @ font-size: 0.8em; |
| 492 | @ font-weight: bold; |
| 493 | @ white-space: nowrap; |
| 494 | @ } |
| 495 | @ |
| 496 | @ /* The header across the top of the page */ |
| 497 | @ div.header { |
| 498 | @ margin:10px 0px 10px 0px; |
| 499 | @ padding:1px 0px 0px 20px; |
| 500 | @ border-style:solid; |
| 501 | @ border-color:black; |
| 502 | @ border-width:1px 0px; |
| 503 | @ background-color:#eee; |
| 504 | @ } |
| 505 | @ |
| 506 | @ /* The main menu bar that appears at the top left of the page beneath |
| 507 | @ ** the header. Width must be co-ordinated with the container below */ |
| 508 | @ div.mainmenu { |
| 509 | @ float: left; |
| 510 | @ margin-left: 10px; |
| 511 | @ margin-right: 10px; |
| 512 | @ font-size: 0.9em; |
| 513 | @ font-weight: bold; |
| 514 | @ padding:5px; |
| 515 | @ background-color:#eee; |
| 516 | @ border:1px solid #999; |
| 517 | @ width:8em; |
| 518 | @ } |
| 519 | @ |
| 520 | @ /* Main menu is now a list */ |
| 521 | @ div.mainmenu ul { |
| 522 | @ padding: 0; |
| 523 | @ list-style:none; |
| 524 | @ } |
| 525 | @ div.mainmenu a, div.mainmenu a:visited{ |
| 526 | @ padding: 1px 10px 1px 10px; |
| 527 | @ color: #333; |
| 528 | @ text-decoration: none; |
| 529 | @ } |
| 530 | @ div.mainmenu a:hover { |
| 531 | @ color: #eee; |
| 532 | @ background-color: #333; |
| 533 | @ } |
| 534 | @ |
| 535 | @ /* Container for the sub-menu and content so they don''t spread |
| 536 | @ ** out underneath the main menu */ |
| 537 | @ #container { |
| 538 | @ padding-left: 9em; |
| 539 | @ } |
| 540 | @ |
| 541 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 542 | @ div.submenu, div.sectionmenu { |
| 543 | @ padding: 3px 10px 3px 10px; |
| 544 | @ font-size: 0.9em; |
| 545 | @ text-align: center; |
| 546 | @ border:1px solid #999; |
| 547 | @ border-width:1px 0px; |
| 548 | @ background-color: #eee; |
| 549 | @ color: #333; |
| 550 | @ } |
| 551 | @ div.submenu a, div.submenu a:visited, div.sectionmenu>a.button:link, |
| 552 | @ div.sectionmenu>a.button:visited { |
| 553 | @ padding: 3px 10px 3px 10px; |
| 554 | @ color: #333; |
| 555 | @ text-decoration: none; |
| 556 | @ } |
| 557 | @ div.submenu a:hover, div.sectionmenu>a.button:hover { |
| 558 | @ color: #eee; |
| 559 | @ background-color: #333; |
| 560 | @ } |
| 561 | @ |
| 562 | @ /* All page content from the bottom of the menu or submenu down to |
| 563 | @ ** the footer */ |
| 564 | @ div.content { |
| 565 | @ padding: 2ex 1ex 0ex 2ex; |
| 566 | @ } |
| 567 | @ |
| 568 | @ /* Some pages have section dividers */ |
| 569 | @ div.section { |
| 570 | @ margin-bottom: 0px; |
| 571 | @ margin-top: 1em; |
| 572 | @ padding: 1px 1px 1px 1px; |
| 573 | @ font-size: 1.2em; |
| 574 | @ font-weight: bold; |
| 575 | @ border-style:solid; |
| 576 | @ border-color:#999; |
| 577 | @ border-width:1px 0px; |
| 578 | @ background-color: #eee; |
| 579 | @ color: #333; |
| 580 | @ white-space: nowrap; |
| 581 | @ } |
| 582 | @ |
| 583 | @ /* The "Date" that occurs on the left hand side of timelines */ |
| 584 | @ div.divider { |
| 585 | @ background: #eee; |
| 586 | @ border: 2px #999 solid; |
| 587 | @ font-size: 1em; font-weight: normal; |
| 588 | @ padding: .25em; |
| 589 | @ margin: .2em 0 .2em 0; |
| 590 | @ float: left; |
| 591 | @ clear: left; |
| 592 | @ color: #333; |
| 593 | @ white-space: nowrap; |
| 594 | @ } |
| 595 | @ |
| 596 | @ /* The footer at the very bottom of the page */ |
| 597 | @ div.footer { |
| 598 | @ font-size: 0.8em; |
| 599 | @ margin-top: 12px; |
| 600 | @ padding: 5px 10px 5px 10px; |
| 601 | @ text-align: right; |
| 602 | @ background-color: #eee; |
| 603 | @ color: #555; |
| 604 | @ } |
| 605 | @ |
| 606 | @ /* <verbatim> blocks */ |
| 607 | @ pre.verbatim { |
| 608 | @ background-color: #f5f5f5; |
| 609 | @ padding: 0.5em; |
| 610 | @ white-space: pre-wrap; |
| 611 | @ } |
| 612 | @ |
| 613 | @ /* The label/value pairs on (for example) the ci page */ |
| 614 | @ table.label-value th { |
| 615 | @ vertical-align: top; |
| 616 | @ text-align: right; |
| 617 | @ padding: 0.2ex 2ex; |
| 618 | @ }'); |
| 619 | @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> |
| 620 | @ <head> |
| 621 | @ <base href="$baseurl/$current_page" /> |
| 622 | @ <title>$<project_name>: $<title></title> |
| 623 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 624 | @ href="$home/timeline.rss"> |
| 625 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" |
| 626 | @ media="screen"> |
| 627 | @ </head> |
| 628 | @ <body> |
| 629 | @ <div class="header"> |
| 630 | @ <div class="logo"> |
| 631 | @ <img src="$logo_image_url" alt="logo"> |
| 632 | @ <br />$<project_name> |
| 633 | @ </div> |
| 634 | @ <div class="title">$<title></div> |
| 635 | @ <div class="status"><th1> |
| 636 | @ if {[info exists login]} { |
| 637 | @ puts "Logged in as $login" |
| 638 | @ } else { |
| 639 | @ puts "Not logged in" |
| 640 | @ } |
| 641 | @ </th1></div> |
| 642 | @ </div> |
| 643 | @ <div class="mainmenu"> |
| 644 | @ <th1> |
| 645 | @ html "<a href=''$home$index_page''>Home</a>\n" |
| 646 | @ if {[anycap jor]} { |
| 647 | @ html "<a href=''$home/timeline''>Timeline</a>\n" |
| 648 | @ } |
| 649 | @ if {[hascap oh]} { |
| 650 | @ html "<a href=''$home/tree?ci=tip''>Files</a>\n" |
| 651 | @ } |
| 652 | @ if {[hascap o]} { |
| 653 | @ html "<a href=''$home/brlist''>Branches</a>\n" |
| 654 | @ html "<a href=''$home/taglist''>Tags</a>\n" |
| 655 | @ } |
| 656 | @ if {[hascap r]} { |
| 657 | @ html "<a href=''$home/reportlist''>Tickets</a>\n" |
| 658 | @ } |
| 659 | @ if {[hascap j]} { |
| 660 | @ html "<a href=''$home/wiki''>Wiki</a>\n" |
| 661 | @ } |
| 662 | @ if {[hascap s]} { |
| 663 | @ html "<a href=''$home/setup''>Admin</a>\n" |
| 664 | @ } elseif {[hascap a]} { |
| 665 | @ html "<a href=''$home/setup_ulist''>Users</a>\n" |
| 666 | @ } |
| 667 | @ if {[info exists login]} { |
| 668 | @ html "<a href=''$home/login''>Logout</a>\n" |
| 669 | @ } else { |
| 670 | @ html "<a href=''$home/login''>Login</a>\n" |
| 671 | @ } |
| 672 | @ </th1></ul></div> |
| 673 | @ <div id="container"> |
| 674 | @ '); |
| 675 | @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div> |
| 676 | @ <div class="footer"> |
| 677 | @ Fossil version $manifest_version $manifest_date |
| 678 | @ </div> |
| 679 | @ </body></html> |
| 680 | @ '); |
| 681 | ; |
| 682 | |
| 683 | |
| 684 | /* |
| 685 | ** Shadow boxes and rounded corners. |
| 686 | */ |
| 687 | static const char zBuiltinSkin4[] = |
| 688 | @ REPLACE INTO config(name,mtime,value) |
| 689 | @ VALUES('css',now(),'/* General settings for the entire page */ |
| 690 | @ html { |
| 691 | @ min-height: 100%; |
| 692 | @ } |
| 693 | @ body { |
| 694 | @ margin: 0ex 1ex; |
| 695 | @ padding: 0px; |
| 696 | @ background-color: white; |
| 697 | @ color: #333; |
| 698 | @ font-family: Verdana, sans-serif; |
| 699 | @ font-size: 0.8em; |
| 700 | @ } |
| 701 | @ |
| 702 | @ /* The project logo in the upper left-hand corner of each page */ |
| 703 | @ div.logo { |
| 704 | @ display: table-cell; |
| 705 | @ text-align: right; |
| 706 | @ vertical-align: bottom; |
| 707 | @ font-weight: normal; |
| 708 | @ white-space: nowrap; |
| 709 | @ } |
| 710 | @ |
| 711 | @ /* Widths */ |
| 712 | @ div.header, div.mainmenu, div.submenu, div.content, div.footer { |
| 713 | @ max-width: 900px; |
| 714 | @ margin: auto; |
| 715 | @ padding: 3px 20px 3px 20px; |
| 716 | @ clear: both; |
| 717 | @ } |
| 718 | @ |
| 719 | @ /* The page title at the top of each page */ |
| 720 | @ div.title { |
| 721 | @ display: table-cell; |
| 722 | @ padding-left: 10px; |
| 723 | @ font-size: 2em; |
| 724 | @ margin: 10px 0 10px -20px; |
| 725 | @ vertical-align: bottom; |
| 726 | @ text-align: left; |
| 727 | @ width: 80%; |
| 728 | @ font-family: Verdana, sans-serif; |
| 729 | @ font-weight: bold; |
| 730 | @ color: #558195; |
| 731 | @ text-shadow: 0px 2px 2px #999999; |
| 732 | @ } |
| 733 | @ |
| 734 | @ /* The login status message in the top right-hand corner */ |
| 735 | @ div.status { |
| 736 | @ display: table-cell; |
| 737 | @ text-align: right; |
| 738 | @ vertical-align: bottom; |
| 739 | @ color: #333; |
| 740 | @ margin-right: -20px; |
| 741 | @ white-space: nowrap; |
| 742 | @ } |
| 743 | @ |
| 744 | @ /* The main menu bar that appears at the top of the page beneath |
| 745 | @ ** the header */ |
| 746 | @ div.mainmenu { |
| 747 | @ text-align: center; |
| 748 | @ color: white; |
| 749 | @ border-top-left-radius: 5px; |
| 750 | @ border-top-right-radius: 5px; |
| 751 | @ vertical-align: middle; |
| 752 | @ padding-top: 8px; |
| 753 | @ padding-bottom: 8px; |
| 754 | @ background-color: #446979; |
| 755 | @ box-shadow: 0px 3px 4px #333333; |
| 756 | @ } |
| 757 | @ |
| 758 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 759 | @ div.submenu { |
| 760 | @ padding-top:10px; |
| 761 | @ padding-bottom:0; |
| 762 | @ text-align: right; |
| 763 | @ color: #000; |
| 764 | @ background-color: #fff; |
| 765 | @ height: 1.5em; |
| 766 | @ vertical-align:middle; |
| 767 | @ box-shadow: 0px 3px 4px #999; |
| 768 | @ } |
| 769 | @ div.mainmenu a, div.mainmenu a:visited { |
| 770 | @ padding: 3px 10px 3px 10px; |
| 771 | @ color: white; |
| 772 | @ text-decoration: none; |
| 773 | @ } |
| 774 | @ div.submenu a, div.submenu a:visited, a.button, |
| 775 | @ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { |
| 776 | @ padding: 2px 8px; |
| 777 | @ color: #000; |
| 778 | @ font-family: Arial; |
| 779 | @ text-decoration: none; |
| 780 | @ margin:auto; |
| 781 | @ border-radius: 5px; |
| 782 | @ background-color: #e0e0e0; |
| 783 | @ text-shadow: 0px -1px 0px #eee; |
| 784 | @ border: 1px solid #000; |
| 785 | @ } |
| 786 | @ |
| 787 | @ div.mainmenu a:hover { |
| 788 | @ color: #000; |
| 789 | @ background-color: white; |
| 790 | @ } |
| 791 | @ |
| 792 | @ div.submenu a:hover, div.sectionmenu>a.button:hover { |
| 793 | @ background-color: #c0c0c0; |
| 794 | @ } |
| 795 | @ |
| 796 | @ /* All page content from the bottom of the menu or submenu down to |
| 797 | @ ** the footer */ |
| 798 | @ div.content { |
| 799 | @ background-color: #fff; |
| 800 | @ box-shadow: 0px 3px 4px #999; |
| 801 | @ border-bottom-right-radius: 5px; |
| 802 | @ border-bottom-left-radius: 5px; |
| 803 | @ padding-bottom: 1em; |
| 804 | @ min-height:40%; |
| 805 | @ } |
| 806 | @ |
| 807 | @ |
| 808 | @ /* Some pages have section dividers */ |
| 809 | @ div.section { |
| 810 | @ margin-bottom: 0.5em; |
| 811 | @ margin-top: 1em; |
| 812 | @ margin-right: auto; |
| 813 | @ padding: 1px 1px 1px 1px; |
| 814 | @ font-size: 1.2em; |
| 815 | @ font-weight: bold; |
| 816 | @ text-align: center; |
| 817 | @ color: white; |
| 818 | @ border-radius: 5px; |
| 819 | @ background-color: #446979; |
| 820 | @ box-shadow: 0px 3px 4px #333333; |
| 821 | @ white-space: nowrap; |
| 822 | @ } |
| 823 | @ |
| 824 | @ /* The "Date" that occurs on the left hand side of timelines */ |
| 825 | @ div.divider { |
| 826 | @ font-size: 1.2em; |
| 827 | @ font-family: Georgia, serif; |
| 828 | @ font-weight: bold; |
| 829 | @ margin-top: 1em; |
| 830 | @ white-space: nowrap; |
| 831 | @ } |
| 832 | @ |
| 833 | @ /* The footer at the very bottom of the page */ |
| 834 | @ div.footer { |
| 835 | @ font-size: 0.9em; |
| 836 | @ text-align: right; |
| 837 | @ margin-bottom: 1em; |
| 838 | @ color: #666; |
| 839 | @ } |
| 840 | @ |
| 841 | @ /* Hyperlink colors in the footer */ |
| 842 | @ div.footer a { color: white; } |
| 843 | @ div.footer a:link { color: white; } |
| 844 | @ div.footer a:visited { color: white; } |
| 845 | @ div.footer a:hover { background-color: white; color: #558195; } |
| 846 | @ |
| 847 | @ /* <verbatim> blocks */ |
| 848 | @ pre.verbatim, blockquote pre { |
| 849 | @ font-family: Dejavu Sans Mono, Monaco, Lucida Console, monospace; |
| 850 | @ background-color: #f3f3f3; |
| 851 | @ padding: 0.5em; |
| 852 | @ white-space: pre-wrap; |
| 853 | @ } |
| 854 | @ |
| 855 | @ blockquote pre { |
| 856 | @ border: 1px #000 dashed; |
| 857 | @ } |
| 858 | @ |
| 859 | @ /* The label/value pairs on (for example) the ci page */ |
| 860 | @ table.label-value th { |
| 861 | @ vertical-align: top; |
| 862 | @ text-align: right; |
| 863 | @ padding: 0.2ex 2ex; |
| 864 | @ } |
| 865 | @ |
| 866 | @ table.report tr th { |
| 867 | @ padding: 3px 5px; |
| 868 | @ text-transform: capitalize; |
| 869 | @ cursor: pointer; |
| 870 | @ } |
| 871 | @ |
| 872 | @ table.report tr td { |
| 873 | @ padding: 3px 5px; |
| 874 | @ cursor: pointer; |
| 875 | @ } |
| 876 | @ |
| 877 | @ textarea { |
| 878 | @ font-size: 1em; |
| 879 | @ } |
| 880 | @ |
| 881 | @ .fullsize-text { |
| 882 | @ font-size: 1.25em; |
| 883 | @ }'); |
| 884 | @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> |
| 885 | @ <head> |
| 886 | @ <base href="$baseurl/$current_page" /> |
| 887 | @ <title>$<project_name>: $<title></title> |
| 888 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 889 | @ href="$home/timeline.rss"> |
| 890 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" |
| 891 | @ media="screen"> |
| 892 | @ </head> |
| 893 | @ <body> |
| 894 | @ <div class="header"> |
| 895 | @ <div class="logo"> |
| 896 | @ <img src="$logo_image_url" alt="logo"> |
| 897 | @ <br />$<project_name> |
| 898 | @ </div> |
| 899 | @ <div class="title">$<title></div> |
| 900 | @ <div class="status"><th1> |
| 901 | @ if {[info exists login]} { |
| 902 | @ puts "Logged in as $login" |
| 903 | @ } else { |
| 904 | @ puts "Not logged in" |
| 905 | @ } |
| 906 | @ </th1></div> |
| 907 | @ </div> |
| 908 | @ <div class="mainmenu"> |
| 909 | @ <th1> |
| 910 | @ html "<a href=''$home$index_page''>Home</a>\n" |
| 911 | @ if {[anycap jor]} { |
| 912 | @ html "<a href=''$home/timeline''>Timeline</a>\n" |
| 913 | @ } |
| 914 | @ if {[hascap oh]} { |
| 915 | @ html "<a href=''$home/tree?ci=tip''>Files</a>\n" |
| 916 | @ } |
| 917 | @ if {[hascap o]} { |
| 918 | @ html "<a href=''$home/brlist''>Branches</a>\n" |
| 919 | @ html "<a href=''$home/taglist''>Tags</a>\n" |
| 920 | @ } |
| 921 | @ if {[hascap r]} { |
| 922 | @ html "<a href=''$home/reportlist''>Tickets</a>\n" |
| 923 | @ } |
| 924 | @ if {[hascap j]} { |
| 925 | @ html "<a href=''$home/wiki''>Wiki</a>\n" |
| 926 | @ } |
| 927 | @ if {[hascap s]} { |
| 928 | @ html "<a href=''$home/setup''>Admin</a>\n" |
| 929 | @ } elseif {[hascap a]} { |
| 930 | @ html "<a href=''$home/setup_ulist''>Users</a>\n" |
| 931 | @ } |
| 932 | @ if {[info exists login]} { |
| 933 | @ html "<a href=''$home/login''>Logout</a>\n" |
| 934 | @ } else { |
| 935 | @ html "<a href=''$home/login''>Login</a>\n" |
| 936 | @ } |
| 937 | @ </th1></div> |
| 938 | @ <div id="container"> |
| 939 | @ '); |
| 940 | @ REPLACE INTO config(name,mtime,value) VALUES('footer',now(),'</div> |
| 941 | @ <div class="footer"> |
| 942 | @ Fossil version $manifest_version $manifest_date |
| 943 | @ </div> |
| 944 | @ </body></html> |
| 945 | @ '); |
| 946 | ; |
| 947 | |
| 948 | |
| 949 | /* |
| 950 | ** This skin is intended to be almost identical to the default one, with the |
| 951 | ** following changes to the header and footer: |
| 952 | ** |
| 953 | ** 1. The logo image in the header has been modified to be a hyperlink to the |
| 954 | ** root of the web site containing the repository using the same scheme |
| 955 | ** (i.e. HTTP or HTTPS) as the base URL for the repository. The header |
| 956 | ** contains a TH1 script block to help accomplish these tasks. |
| 957 | ** |
| 958 | ** 2. The Fossil version information in the footer has been augmented with |
| 959 | ** hyperlinks to the corresponding points on the timeline in the official |
| 960 | ** Fossil repository. Additionally, if the Tcl integration feature is |
| 961 | ** enabled, the loaded version of Tcl is included, with a hyperlink to the |
| 962 | ** official Tcl/Tk web site. The footer also contains a TH1 script block |
| 963 | ** to help accomplish these tasks. |
| 964 | */ |
| 965 | static const char zBuiltinSkin5[] = |
| 966 | @ REPLACE INTO config(name,mtime,value) |
| 967 | @ VALUES('css',now(),'/* General settings for the entire page */ |
| 968 | @ body { |
| 969 | @ margin: 0ex 1ex; |
| 970 | @ padding: 0px; |
| 971 | @ background-color: white; |
| 972 | @ font-family: sans-serif; |
| 973 | @ } |
| 974 | @ |
| 975 | @ /* The project logo in the upper left-hand corner of each page */ |
| 976 | @ div.logo { |
| 977 | @ display: table-cell; |
| 978 | @ text-align: center; |
| 979 | @ vertical-align: bottom; |
| 980 | @ font-weight: bold; |
| 981 | @ color: #558195; |
| 982 | @ min-width: 200px; |
| 983 | @ white-space: nowrap; |
| 984 | @ } |
| 985 | @ |
| 986 | @ /* The page title centered at the top of each page */ |
| 987 | @ div.title { |
| 988 | @ display: table-cell; |
| 989 | @ font-size: 2em; |
| 990 | @ font-weight: bold; |
| 991 | @ text-align: center; |
| 992 | @ padding: 0 0 0 1em; |
| 993 | @ color: #558195; |
| 994 | @ vertical-align: bottom; |
| 995 | @ width: 100%; |
| 996 | @ } |
| 997 | @ |
| 998 | @ /* The login status message in the top right-hand corner */ |
| 999 | @ div.status { |
| 1000 | @ display: table-cell; |
| 1001 | @ text-align: right; |
| 1002 | @ vertical-align: bottom; |
| 1003 | @ color: #558195; |
| 1004 | @ font-size: 0.8em; |
| 1005 | @ font-weight: bold; |
| 1006 | @ min-width: 200px; |
| 1007 | @ white-space: nowrap; |
| 1008 | @ } |
| 1009 | @ |
| 1010 | @ /* The header across the top of the page */ |
| 1011 | @ div.header { |
| 1012 | @ display: table; |
| 1013 | @ width: 100%; |
| 1014 | @ } |
| 1015 | @ |
| 1016 | @ /* The main menu bar that appears at the top of the page beneath |
| 1017 | @ ** the header */ |
| 1018 | @ div.mainmenu { |
| 1019 | @ padding: 5px 10px 5px 10px; |
| 1020 | @ font-size: 0.9em; |
| 1021 | @ font-weight: bold; |
| 1022 | @ text-align: center; |
| 1023 | @ letter-spacing: 1px; |
| 1024 | @ background-color: #558195; |
| 1025 | @ border-top-left-radius: 8px; |
| 1026 | @ border-top-right-radius: 8px; |
| 1027 | @ color: white; |
| 1028 | @ } |
| 1029 | @ |
| 1030 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 1031 | @ div.submenu, div.sectionmenu { |
| 1032 | @ padding: 3px 10px 3px 0px; |
| 1033 | @ font-size: 0.9em; |
| 1034 | @ text-align: center; |
| 1035 | @ background-color: #456878; |
| 1036 | @ color: white; |
| 1037 | @ } |
| 1038 | @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, |
| 1039 | @ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { |
| 1040 | @ padding: 3px 10px 3px 10px; |
| 1041 | @ color: white; |
| 1042 | @ text-decoration: none; |
| 1043 | @ } |
| 1044 | @ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { |
| 1045 | @ color: #558195; |
| 1046 | @ background-color: white; |
| 1047 | @ } |
| 1048 | @ |
| 1049 | @ /* All page content from the bottom of the menu or submenu down to |
| 1050 | @ ** the footer */ |
| 1051 | @ div.content { |
| 1052 | @ padding: 0ex 1ex 1ex 1ex; |
| 1053 | @ border: solid #aaa; |
| 1054 | @ border-width: 1px; |
| 1055 | @ } |
| 1056 | @ |
| 1057 | @ /* Some pages have section dividers */ |
| 1058 | @ div.section { |
| 1059 | @ margin-bottom: 0px; |
| 1060 | @ margin-top: 1em; |
| 1061 | @ padding: 1px 1px 1px 1px; |
| 1062 | @ font-size: 1.2em; |
| 1063 | @ font-weight: bold; |
| 1064 | @ background-color: #558195; |
| 1065 | @ color: white; |
| 1066 | @ white-space: nowrap; |
| 1067 | @ } |
| 1068 | @ |
| 1069 | @ /* The "Date" that occurs on the left hand side of timelines */ |
| 1070 | @ div.divider { |
| 1071 | @ background: #a1c4d4; |
| 1072 | @ border: 2px #558195 solid; |
| 1073 | @ font-size: 1em; font-weight: normal; |
| 1074 | @ padding: .25em; |
| 1075 | @ margin: .2em 0 .2em 0; |
| 1076 | @ float: left; |
| 1077 | @ clear: left; |
| 1078 | @ white-space: nowrap; |
| 1079 | @ } |
| 1080 | @ |
| 1081 | @ /* The footer at the very bottom of the page */ |
| 1082 | @ div.footer { |
| 1083 | @ clear: both; |
| 1084 | @ font-size: 0.8em; |
| 1085 | @ padding: 5px 10px 5px 10px; |
| 1086 | @ text-align: right; |
| 1087 | @ background-color: #558195; |
| 1088 | @ border-bottom-left-radius: 8px; |
| 1089 | @ border-bottom-right-radius: 8px; |
| 1090 | @ color: white; |
| 1091 | @ } |
| 1092 | @ |
| 1093 | @ /* Hyperlink colors in the footer */ |
| 1094 | @ div.footer a { color: white; } |
| 1095 | @ div.footer a:link { color: white; } |
| 1096 | @ div.footer a:visited { color: white; } |
| 1097 | @ div.footer a:hover { background-color: white; color: #558195; } |
| 1098 | @ |
| 1099 | @ /* verbatim blocks */ |
| 1100 | @ pre.verbatim { |
| 1101 | @ background-color: #f5f5f5; |
| 1102 | @ padding: 0.5em; |
| 1103 | @ white-space: pre-wrap; |
| 1104 | @ } |
| 1105 | @ |
| 1106 | @ /* The label/value pairs on (for example) the ci page */ |
| 1107 | @ table.label-value th { |
| 1108 | @ vertical-align: top; |
| 1109 | @ text-align: right; |
| 1110 | @ padding: 0.2ex 2ex; |
| 1111 | @ }'); |
| 1112 | @ REPLACE INTO config(name,mtime,value) VALUES('header',now(),'<html> |
| 1113 | @ <head> |
| 1114 | @ <base href="$baseurl/$current_page" /> |
| 1115 | @ <title>$<project_name>: $<title></title> |
| 1116 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 1117 | @ href="$home/timeline.rss" /> |
| 1118 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" |
| 1119 | @ media="screen" /> |
| 1120 | @ </head> |
| 1121 | @ <body> |
| 1122 | @ <div class="header"> |
| 1123 | @ <div class="logo"> |
| 1124 | @ <th1> |
| 1125 | @ ## |
| 1126 | @ ## NOTE: The purpose of this procedure is to take the base URL of the |
| 1127 | @ ## Fossil project and return the root of the entire web site using |
| 1128 | @ ## the same URI scheme as the base URL (e.g. http or https). |
| 1129 | @ ## |
| 1130 | @ proc getLogoUrl { baseurl } { |
| 1131 | @ set idx(first) [string first // $baseurl] |
| 1132 | @ if {$idx(first) != -1} { |
| 1133 | @ ## |
| 1134 | @ ## NOTE: Skip second slash. |
| 1135 | @ ## |
| 1136 | @ set idx(first+1) [expr {$idx(first) + 2}] |
| 1137 | @ ## |
| 1138 | @ ## NOTE: (part 1) The [string first] command does NOT actually |
| 1139 | @ ## the optional startIndex argument as specified in the |
| 1140 | @ ## TH1 support manual; therefore, we fake it by using the |
| 1141 | @ ## [string range] command and then adding the necessary |
| 1142 | @ ## offset to the resulting index manually (below). In Tcl, |
| 1143 | @ ## we could use the following instead: |
| 1144 | @ ## |
| 1145 | @ ## set idx(next) [string first / $baseurl $idx(first+1)] |
| 1146 | @ ## |
| 1147 | @ set idx(nextRange) [string range $baseurl $idx(first+1) end] |
| 1148 | @ set idx(next) [string first / $idx(nextRange)] |
| 1149 | @ if {$idx(next) != -1} { |
| 1150 | @ ## |
| 1151 | @ ## NOTE: (part 2) Add the necessary offset to the result of the |
| 1152 | @ ## search for the next slash (i.e. the one after the initial |
| 1153 | @ ## search for the two slashes). |
| 1154 | @ ## |
| 1155 | @ set idx(next) [expr {$idx(next) + $idx(first+1)}] |
| 1156 | @ ## |
| 1157 | @ ## NOTE: Back up one character from the next slash. |
| 1158 | @ ## |
| 1159 | @ set idx(next-1) [expr {$idx(next) - 1}] |
| 1160 | @ ## |
| 1161 | @ ## NOTE: Extract the URI scheme and host from the base URL. |
| 1162 | @ ## |
| 1163 | @ set scheme [string range $baseurl 0 $idx(first)] |
| 1164 | @ set host [string range $baseurl $idx(first+1) $idx(next-1)] |
| 1165 | @ ## |
| 1166 | @ ## NOTE: Try to stay in SSL mode if we are there now. |
| 1167 | @ ## |
| 1168 | @ if {[string compare $scheme http:/] == 0} { |
| 1169 | @ set scheme http:// |
| 1170 | @ } else { |
| 1171 | @ set scheme https:// |
| 1172 | @ } |
| 1173 | @ set logourl $scheme$host/ |
| 1174 | @ } else { |
| 1175 | @ set logourl $baseurl |
| 1176 | @ } |
| 1177 | @ } else { |
| 1178 | @ set logourl $baseurl |
| 1179 | @ } |
| 1180 | @ return $logourl |
| 1181 | @ } |
| 1182 | @ set logourl [getLogoUrl $baseurl] |
| 1183 | @ </th1> |
| 1184 | @ <a href="$logourl"> |
| 1185 | @ <img src="$logo_image_url" border="0" alt="$project_name"> |
| 1186 | @ </a> |
| 1187 | @ </div> |
| 1188 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 1189 | @ <div class="status"><th1> |
| 1190 | @ if {[info exists login]} { |
| 1191 | @ puts "Logged in as $login" |
| 1192 | @ } else { |
| 1193 | @ puts "Not logged in" |
| 1194 | @ } |
| 1195 | @ </th1></div> |
| 1196 | @ </div> |
| 1197 | @ <div class="mainmenu"> |
| 1198 | @ <th1> |
| 1199 | @ html "<a href=''$home$index_page''>Home</a>\n" |
| 1200 | @ if {[anycap jor]} { |
| 1201 | @ html "<a href=''$home/timeline''>Timeline</a>\n" |
| 1202 | @ } |
| 1203 | @ if {[hascap oh]} { |
| 1204 | @ html "<a href=''$home/tree?ci=tip''>Files</a>\n" |
| 1205 | @ } |
| 1206 | @ if {[hascap o]} { |
| 1207 | @ html "<a href=''$home/brlist''>Branches</a>\n" |
| 1208 | @ html "<a href=''$home/taglist''>Tags</a>\n" |
| 1209 | @ } |
| 1210 | @ if {[hascap r]} { |
| 1211 | @ html "<a href=''$home/reportlist''>Tickets</a>\n" |
| 1212 | @ } |
| 1213 | @ if {[hascap j]} { |
| 1214 | @ html "<a href=''$home/wiki''>Wiki</a>\n" |
| 1215 | @ } |
| 1216 | @ if {[hascap s]} { |
| 1217 | @ html "<a href=''$home/setup''>Admin</a>\n" |
| 1218 | @ } elseif {[hascap a]} { |
| 1219 | @ html "<a href=''$home/setup_ulist''>Users</a>\n" |
| 1220 | @ } |
| 1221 | @ if {[info exists login]} { |
| 1222 | @ html "<a href=''$home/login''>Logout</a>\n" |
| 1223 | @ } else { |
| 1224 | @ html "<a href=''$home/login''>Login</a>\n" |
| 1225 | @ } |
| 1226 | @ </th1></div> |
| 1227 | @ '); |
| 1228 | @ REPLACE INTO config(name,mtime,value) |
| 1229 | @ VALUES('footer',now(),'<div class="footer"> |
| 1230 | @ <th1> |
| 1231 | @ proc getTclVersion {} { |
| 1232 | @ if {[catch {tclEval info patchlevel} tclVersion] == 0} { |
| 1233 | @ return "<a href=\"http://www.tcl.tk/\">Tcl</a> version $tclVersion" |
| 1234 | @ } |
| 1235 | @ return "" |
| 1236 | @ } |
| 1237 | @ proc getVersion { version } { |
| 1238 | @ set length [string length $version] |
| 1239 | @ return [string range $version 1 [expr {$length - 2}]] |
| 1240 | @ } |
| 1241 | @ set version [getVersion $manifest_version] |
| 1242 | @ set tclVersion [getTclVersion] |
| 1243 | @ set fossilUrl http://www.fossil-scm.org |
| 1244 | @ </th1> |
| 1245 | @ This page was generated in about |
| 1246 | @ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by |
| 1247 | @ <a href="$fossilUrl/">Fossil</a> |
| 1248 | @ version $release_version $tclVersion |
| 1249 | @ <a href="$fossilUrl/index.html/info/$version">$manifest_version</a> |
| 1250 | @ <a href="$fossilUrl/index.html/timeline?c=$manifest_date&y=ci">$manifest_date</a> |
| 1251 | @ </div> |
| 1252 | @ </body></html> |
| 1253 | @ '); |
| 1254 | ; |
| 1255 | |
| 1256 | /* |
| 1257 | ** An array of available built-in skins. |
| 1258 | */ |
| 1259 | static struct BuiltinSkin { |
| 1260 | const char *zName; |
| 1261 | const char *zValue; |
| 1262 | } aBuiltinSkin[] = { |
| 1263 | { "Default", 0 /* Filled in at runtime */ }, |
| 1264 | { "Plain Gray, No Logo", zBuiltinSkin1 }, |
| 1265 | { "Khaki, No Logo", zBuiltinSkin2 }, |
| 1266 | { "Black & White, Menu on Left", zBuiltinSkin3 }, |
| 1267 | { "Shadow boxes & Rounded Corners", zBuiltinSkin4 }, |
| 1268 | { "Enhanced Default", zBuiltinSkin5 }, |
| 1269 | }; |
| 1270 | |
| 1271 | /* |
| 1272 | ** For a skin named zSkinName, compute the name of the CONFIG table |
| 1273 | ** entry where that skin is stored and return it. |
| @@ -1286,57 +69,158 @@ | |
| 1286 | } |
| 1287 | return z; |
| 1288 | } |
| 1289 | |
| 1290 | /* |
| 1291 | ** Construct and return a string that represents the current skin if |
| 1292 | ** useDefault==0 or a string for the default skin if useDefault==1. |
| 1293 | ** |
| 1294 | ** Memory to hold the returned string is obtained from malloc. |
| 1295 | */ |
| 1296 | static char *getSkin(int useDefault){ |
| 1297 | Blob val; |
| 1298 | blob_zero(&val); |
| 1299 | blob_appendf(&val, |
| 1300 | "REPLACE INTO config(name,value,mtime) VALUES('css',%Q,now());\n", |
| 1301 | useDefault ? zDefaultCSS : db_get("css", (char*)zDefaultCSS) |
| 1302 | ); |
| 1303 | blob_appendf(&val, |
| 1304 | "REPLACE INTO config(name,value,mtime) VALUES('header',%Q,now());\n", |
| 1305 | useDefault ? zDefaultHeader : db_get("header", (char*)zDefaultHeader) |
| 1306 | ); |
| 1307 | blob_appendf(&val, |
| 1308 | "REPLACE INTO config(name,value,mtime) VALUES('footer',%Q,now());\n", |
| 1309 | useDefault ? zDefaultFooter : db_get("footer", (char*)zDefaultFooter) |
| 1310 | ); |
| 1311 | return blob_str(&val); |
| 1312 | } |
| 1313 | |
| 1314 | /* |
| 1315 | ** Construct the default skin string and fill in the corresponding |
| 1316 | ** entry in aBuildinSkin[] |
| 1317 | */ |
| 1318 | static void setDefaultSkin(void){ |
| 1319 | aBuiltinSkin[0].zValue = getSkin(1); |
| 1320 | } |
| 1321 | |
| 1322 | /* |
| 1323 | ** WEBPAGE: setup_skin |
| 1324 | */ |
| 1325 | void setup_skin(void){ |
| 1326 | const char *z; |
| 1327 | char *zName; |
| 1328 | char *zErr = 0; |
| 1329 | const char *zCurrent; /* Current skin */ |
| 1330 | int i; /* Loop counter */ |
| 1331 | Stmt q; |
| 1332 | |
| 1333 | login_check_credentials(); |
| 1334 | if( !g.perm.Setup ){ |
| 1335 | login_needed(); |
| 1336 | } |
| 1337 | db_begin_transaction(); |
| 1338 | |
| 1339 | /* Process requests to delete a user-defined skin */ |
| 1340 | if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 1341 | style_header("Confirm Custom Skin Delete"); |
| 1342 | @ <form action="%s(g.zTop)/setup_skin" method="post"><div> |
| @@ -1351,51 +235,42 @@ | |
| 1351 | return; |
| 1352 | } |
| 1353 | if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 1354 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 1355 | } |
| 1356 | |
| 1357 | setDefaultSkin(); |
| 1358 | zCurrent = getSkin(0); |
| 1359 | |
| 1360 | if( P("save")!=0 && (zName = skinVarName(P("save"),0))!=0 ){ |
| 1361 | if( db_exists("SELECT 1 FROM config WHERE name=%Q", zName) |
| 1362 | || fossil_strcmp(zName, "Default")==0 ){ |
| 1363 | zErr = mprintf("Skin name \"%h\" already exists. " |
| 1364 | "Choose a different name.", P("sn")); |
| 1365 | }else{ |
| 1366 | db_multi_exec("INSERT INTO config(name,value,mtime) VALUES(%Q,%Q,now())", |
| 1367 | zName, zCurrent |
| 1368 | ); |
| 1369 | } |
| 1370 | } |
| 1371 | |
| 1372 | /* The user pressed the "Use This Skin" button. */ |
| 1373 | if( P("load") && (z = P("sn"))!=0 && z[0] ){ |
| 1374 | int seen = 0; |
| 1375 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 1376 | if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ |
| 1377 | seen = 1; |
| 1378 | break; |
| 1379 | } |
| 1380 | } |
| 1381 | if( !seen ){ |
| 1382 | seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" |
| 1383 | " AND value=%Q", zCurrent); |
| 1384 | } |
| 1385 | if( !seen ){ |
| 1386 | db_multi_exec( |
| 1387 | "INSERT INTO config(name,value,mtime) VALUES(" |
| 1388 | " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," |
| 1389 | " %Q,now())", zCurrent |
| 1390 | ); |
| 1391 | } |
| 1392 | seen = 0; |
| 1393 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 1394 | if( fossil_strcmp(aBuiltinSkin[i].zName, z)==0 ){ |
| 1395 | seen = 1; |
| 1396 | zCurrent = aBuiltinSkin[i].zValue; |
| 1397 | db_multi_exec("%s", zCurrent/*safe-for-%s*/); |
| 1398 | break; |
| 1399 | } |
| 1400 | } |
| 1401 | if( !seen ){ |
| @@ -1409,48 +284,59 @@ | |
| 1409 | if( zErr ){ |
| 1410 | @ <p><font color="red">%h(zErr)</font></p> |
| 1411 | } |
| 1412 | @ <p>A "skin" is a combination of |
| 1413 | @ <a href="setup_editcss">CSS</a>, |
| 1414 | @ <a href="setup_header">Header</a>, |
| 1415 | @ <a href="setup_footer">Footer</a>, and |
| 1416 | @ <a href="setup_logo">Logo</a> that determines the look and feel |
| 1417 | @ of the web interface.</p> |
| 1418 | @ |
| 1419 | @ <h2>Available Skins:</h2> |
| 1420 | @ <ol> |
| 1421 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 1422 | z = aBuiltinSkin[i].zName; |
| 1423 | if( fossil_strcmp(aBuiltinSkin[i].zValue, zCurrent)==0 ){ |
| 1424 | @ <li><p>%h(z). <b>Currently In Use</b></p> |
| 1425 | }else{ |
| 1426 | @ <li><form action="%s(g.zTop)/setup_skin" method="post"><div> |
| 1427 | @ %h(z). |
| 1428 | @ <input type="hidden" name="sn" value="%h(z)" /> |
| 1429 | @ <input type="submit" name="load" value="Use This Skin" /> |
| 1430 | @ </div></form></li> |
| 1431 | } |
| 1432 | } |
| 1433 | db_prepare(&q, |
| 1434 | "SELECT substr(name, 6), value FROM config" |
| 1435 | " WHERE name GLOB 'skin:*'" |
| 1436 | " ORDER BY name" |
| 1437 | ); |
| 1438 | while( db_step(&q)==SQLITE_ROW ){ |
| 1439 | const char *zN = db_column_text(&q, 0); |
| 1440 | const char *zV = db_column_text(&q, 1); |
| 1441 | if( fossil_strcmp(zV, zCurrent)==0 ){ |
| 1442 | @ <li><p>%h(zN). <b>Currently In Use</b></p> |
| 1443 | }else{ |
| 1444 | @ <li><form action="%s(g.zTop)/setup_skin" method="post"> |
| 1445 | @ %h(zN). |
| 1446 | @ <input type="hidden" name="sn" value="%h(zN)"> |
| 1447 | @ <input type="submit" name="load" value="Use This Skin"> |
| 1448 | @ <input type="submit" name="del1" value="Delete This Skin"> |
| 1449 | @ </form></li> |
| 1450 | } |
| 1451 | } |
| 1452 | db_finalize(&q); |
| 1453 | @ </ol> |
| 1454 | style_footer(); |
| 1455 | db_end_transaction(0); |
| 1456 | } |
| 1457 |
| --- src/skins.c | |
| +++ src/skins.c | |
| @@ -19,1255 +19,38 @@ | |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include <assert.h> |
| 22 | #include "skins.h" |
| 23 | |
| 24 | /* |
| 25 | ** An array of available built-in skins. |
| 26 | ** |
| 27 | ** To add new built-in skins: |
| 28 | ** |
| 29 | ** 1. Pick a name for the new skin. (Here we use "xyzzy"). |
| 30 | ** |
| 31 | ** 2. Install files skins/xyzzy/css.txt, skins/xyzzy/header.txt, |
| 32 | ** and skins/xyzzy/footer.txt into the source tree. |
| 33 | ** |
| 34 | ** 3. Rerun "tclsh makemake.tcl" in the src/ folder in order to |
| 35 | ** rebuild the makefiles to reference the new CSS, headers, and footers. |
| 36 | ** |
| 37 | ** 4. Make an entry in the following array for the new skin. |
| 38 | */ |
| 39 | static struct BuiltinSkin { |
| 40 | const char *zDesc; /* Description of this skin */ |
| 41 | const char *zLabel; /* The directory under skins/ holding this skin */ |
| 42 | char *zSQL; /* Filled in at run-time with SQL to insert this skin */ |
| 43 | } aBuiltinSkin[] = { |
| 44 | { "Default", "default", 0 }, |
| 45 | { "Plain Gray, No Logo", "plain_gray", 0 }, |
| 46 | { "Khaki, No Logo", "khaki", 0 }, |
| 47 | { "Black & White, Menu on Left", "black_and_white", 0 }, |
| 48 | { "Shadow boxes & Rounded Corners", "rounded1", 0 }, |
| 49 | { "Enhanced Default", "enhanced1", 0 }, |
| 50 | { "San Francisco Modern", "etienne1", 0 }, |
| 51 | { "Eagle", "eagle", 0 }, |
| 52 | }; |
| 53 | |
| 54 | /* |
| 55 | ** For a skin named zSkinName, compute the name of the CONFIG table |
| 56 | ** entry where that skin is stored and return it. |
| @@ -1286,57 +69,158 @@ | |
| 69 | } |
| 70 | return z; |
| 71 | } |
| 72 | |
| 73 | /* |
| 74 | ** Return true if there exists a skin name "zSkinName". |
| 75 | */ |
| 76 | static int skinExists(const char *zSkinName){ |
| 77 | int i; |
| 78 | if( zSkinName==0 ) return 0; |
| 79 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 80 | if( fossil_strcmp(zSkinName, aBuiltinSkin[i].zDesc)==0 ) return 1; |
| 81 | } |
| 82 | return db_exists("SELECT 1 FROM config WHERE name='skin:%q'", zSkinName); |
| 83 | } |
| 84 | |
| 85 | /* |
| 86 | ** Construct and return an string of SQL statements that represents |
| 87 | ** a "skin" setting. If zName==0 then return the skin currently |
| 88 | ** installed. Otherwise, return one of the built-in skins designated |
| 89 | ** by zName. |
| 90 | ** |
| 91 | ** Memory to hold the returned string is obtained from malloc. |
| 92 | */ |
| 93 | static char *getSkin(const char *zName){ |
| 94 | const char *z; |
| 95 | char *zLabel; |
| 96 | static const char *azType[] = { "css", "header", "footer" }; |
| 97 | int i; |
| 98 | Blob val; |
| 99 | blob_zero(&val); |
| 100 | for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){ |
| 101 | if( zName ){ |
| 102 | zLabel = mprintf("skins/%s/%s.txt", zName, azType[i]); |
| 103 | z = builtin_text(zLabel); |
| 104 | fossil_free(zLabel); |
| 105 | }else{ |
| 106 | z = db_get(azType[i], 0); |
| 107 | if( z==0 ){ |
| 108 | zLabel = mprintf("skins/default/%s.txt", azType[i]); |
| 109 | z = builtin_text(zLabel); |
| 110 | fossil_free(zLabel); |
| 111 | } |
| 112 | } |
| 113 | blob_appendf(&val, |
| 114 | "REPLACE INTO config(name,value,mtime) VALUES(%Q,%Q,now());\n", |
| 115 | azType[i], z |
| 116 | ); |
| 117 | } |
| 118 | return blob_str(&val); |
| 119 | } |
| 120 | |
| 121 | /* |
| 122 | ** Respond to a Rename button press. Return TRUE if a dialog was painted. |
| 123 | ** Return FALSE to continue with the main Skins page. |
| 124 | */ |
| 125 | static int skinRename(void){ |
| 126 | const char *zOldName; |
| 127 | const char *zNewName; |
| 128 | int ex = 0; |
| 129 | if( P("rename")==0 ) return 0; |
| 130 | zOldName = P("sn"); |
| 131 | zNewName = P("newname"); |
| 132 | if( zOldName==0 ) return 0; |
| 133 | if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){ |
| 134 | if( zNewName==0 ) zNewName = zOldName; |
| 135 | style_header("Rename A Skin"); |
| 136 | if( ex ){ |
| 137 | @ <p><span class="generalError">There is already another skin |
| 138 | @ named "%h(zNewName)". Choose a different name.</span></p> |
| 139 | } |
| 140 | @ <form action="%s(g.zTop)/setup_skin" method="post"><div> |
| 141 | @ <table border="0"><tr> |
| 142 | @ <tr><td align="right">Current name:<td align="left"><b>%h(zOldName)</b> |
| 143 | @ <tr><td align="right">New name:<td align="left"> |
| 144 | @ <input type="text" size="35" name="newname" value="%h(zNewName)"> |
| 145 | @ <tr><td><td> |
| 146 | @ <input type="hidden" name="sn" value="%h(zOldName)"> |
| 147 | @ <input type="submit" name="rename" value="Rename"> |
| 148 | @ <input type="submit" name="canren" value="Cancel"> |
| 149 | @ </table> |
| 150 | login_insert_csrf_secret(); |
| 151 | @ </div></form> |
| 152 | style_footer(); |
| 153 | return 1; |
| 154 | } |
| 155 | db_multi_exec( |
| 156 | "UPDATE config SET name='skin:%q' WHERE name='skin:%q';", |
| 157 | zNewName, zOldName |
| 158 | ); |
| 159 | return 0; |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | ** Respond to a Save button press. Return TRUE if a dialog was painted. |
| 164 | ** Return FALSE to continue with the main Skins page. |
| 165 | */ |
| 166 | static int skinSave(const char *zCurrent){ |
| 167 | const char *zNewName; |
| 168 | int ex = 0; |
| 169 | if( P("save")==0 ) return 0; |
| 170 | zNewName = P("svname"); |
| 171 | if( zNewName && zNewName[0]!=0 ){ |
| 172 | } |
| 173 | if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){ |
| 174 | if( zNewName==0 ) zNewName = ""; |
| 175 | style_header("Save Current Skin"); |
| 176 | if( ex ){ |
| 177 | @ <p><span class="generalError">There is already another skin |
| 178 | @ named "%h(zNewName)". Choose a different name.</span></p> |
| 179 | } |
| 180 | @ <form action="%s(g.zTop)/setup_skin" method="post"><div> |
| 181 | @ <table border="0"><tr> |
| 182 | @ <tr><td align="right">Name for this skin:<td align="left"> |
| 183 | @ <input type="text" size="35" name="svname" value="%h(zNewName)"> |
| 184 | @ <tr><td><td> |
| 185 | @ <input type="submit" name="save" value="Save"> |
| 186 | @ <input type="submit" name="cansave" value="Cancel"> |
| 187 | @ </table> |
| 188 | login_insert_csrf_secret(); |
| 189 | @ </div></form> |
| 190 | style_footer(); |
| 191 | return 1; |
| 192 | } |
| 193 | db_multi_exec( |
| 194 | "INSERT OR IGNORE INTO config(name, value, mtime)" |
| 195 | "VALUES('skin:%q',%Q,now())", |
| 196 | zNewName, zCurrent |
| 197 | ); |
| 198 | return 0; |
| 199 | } |
| 200 | |
| 201 | /* |
| 202 | ** WEBPAGE: setup_skin |
| 203 | */ |
| 204 | void setup_skin(void){ |
| 205 | const char *z; |
| 206 | char *zName; |
| 207 | char *zErr = 0; |
| 208 | const char *zCurrent = 0; /* Current skin */ |
| 209 | int i; /* Loop counter */ |
| 210 | Stmt q; |
| 211 | int seenCurrent = 0; |
| 212 | |
| 213 | login_check_credentials(); |
| 214 | if( !g.perm.Setup ){ |
| 215 | login_needed(); |
| 216 | } |
| 217 | db_begin_transaction(); |
| 218 | zCurrent = getSkin(0); |
| 219 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 220 | aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel); |
| 221 | } |
| 222 | |
| 223 | /* Process requests to delete a user-defined skin */ |
| 224 | if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 225 | style_header("Confirm Custom Skin Delete"); |
| 226 | @ <form action="%s(g.zTop)/setup_skin" method="post"><div> |
| @@ -1351,51 +235,42 @@ | |
| 235 | return; |
| 236 | } |
| 237 | if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){ |
| 238 | db_multi_exec("DELETE FROM config WHERE name=%Q", zName); |
| 239 | } |
| 240 | if( skinRename() ) return; |
| 241 | if( skinSave(zCurrent) ) return; |
| 242 | |
| 243 | /* The user pressed one of the "Install" buttons. */ |
| 244 | if( P("load") && (z = P("sn"))!=0 && z[0] ){ |
| 245 | int seen = 0; |
| 246 | |
| 247 | /* Check to see if the current skin is already saved. If it is, there |
| 248 | ** is no need to create a backup */ |
| 249 | zCurrent = getSkin(0); |
| 250 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 251 | if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ |
| 252 | seen = 1; |
| 253 | break; |
| 254 | } |
| 255 | } |
| 256 | if( !seen ){ |
| 257 | seen = db_exists("SELECT 1 FROM config WHERE name GLOB 'skin:*'" |
| 258 | " AND value=%Q", zCurrent); |
| 259 | if( !seen ){ |
| 260 | db_multi_exec( |
| 261 | "INSERT INTO config(name,value,mtime) VALUES(" |
| 262 | " strftime('skin:Backup On %%Y-%%m-%%d %%H:%%M:%%S')," |
| 263 | " %Q,now())", zCurrent |
| 264 | ); |
| 265 | } |
| 266 | } |
| 267 | seen = 0; |
| 268 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 269 | if( fossil_strcmp(aBuiltinSkin[i].zDesc, z)==0 ){ |
| 270 | seen = 1; |
| 271 | zCurrent = aBuiltinSkin[i].zSQL; |
| 272 | db_multi_exec("%s", zCurrent/*safe-for-%s*/); |
| 273 | break; |
| 274 | } |
| 275 | } |
| 276 | if( !seen ){ |
| @@ -1409,48 +284,59 @@ | |
| 284 | if( zErr ){ |
| 285 | @ <p><font color="red">%h(zErr)</font></p> |
| 286 | } |
| 287 | @ <p>A "skin" is a combination of |
| 288 | @ <a href="setup_editcss">CSS</a>, |
| 289 | @ <a href="setup_header">Header</a>, and |
| 290 | @ <a href="setup_footer">Footer</a> that determines the look and feel |
| 291 | @ of the web interface.</p> |
| 292 | @ |
| 293 | @ <h2>Available Skins:</h2> |
| 294 | @ <table border="0"> |
| 295 | for(i=0; i<sizeof(aBuiltinSkin)/sizeof(aBuiltinSkin[0]); i++){ |
| 296 | z = aBuiltinSkin[i].zDesc; |
| 297 | @ <tr><td>%d(i+1).<td>%h(z)<td> <td> |
| 298 | if( fossil_strcmp(aBuiltinSkin[i].zSQL, zCurrent)==0 ){ |
| 299 | @ (Currently In Use) |
| 300 | seenCurrent = 1; |
| 301 | }else{ |
| 302 | @ <form action="%s(g.zTop)/setup_skin" method="post"> |
| 303 | @ <input type="hidden" name="sn" value="%h(z)" /> |
| 304 | @ <input type="submit" name="load" value="Install" /> |
| 305 | @ </form> |
| 306 | } |
| 307 | @ </tr> |
| 308 | } |
| 309 | db_prepare(&q, |
| 310 | "SELECT substr(name, 6), value FROM config" |
| 311 | " WHERE name GLOB 'skin:*'" |
| 312 | " ORDER BY name" |
| 313 | ); |
| 314 | while( db_step(&q)==SQLITE_ROW ){ |
| 315 | const char *zN = db_column_text(&q, 0); |
| 316 | const char *zV = db_column_text(&q, 1); |
| 317 | i++; |
| 318 | @ <tr><td>%d(i).<td>%h(zN)<td> <td> |
| 319 | @ <form action="%s(g.zTop)/setup_skin" method="post"> |
| 320 | if( fossil_strcmp(zV, zCurrent)==0 ){ |
| 321 | @ (Currently In Use) |
| 322 | seenCurrent = 1; |
| 323 | }else{ |
| 324 | @ <input type="submit" name="load" value="Install"> |
| 325 | @ <input type="submit" name="del1" value="Delete"> |
| 326 | } |
| 327 | @ <input type="submit" name="rename" value="Rename"> |
| 328 | @ <input type="hidden" name="sn" value="%h(zN)"> |
| 329 | @ </form></tr> |
| 330 | } |
| 331 | db_finalize(&q); |
| 332 | if( !seenCurrent ){ |
| 333 | i++; |
| 334 | @ <tr><td>%d(i).<td><i>Current Configuration</i><td> <td> |
| 335 | @ <form action="%s(g.zTop)/setup_skin" method="post"> |
| 336 | @ <input type="submit" name="save" value="Save"> |
| 337 | @ </form> |
| 338 | } |
| 339 | @ </table> |
| 340 | style_footer(); |
| 341 | db_end_transaction(0); |
| 342 | } |
| 343 |
+34
-2
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -108,11 +108,11 @@ | ||
| 108 | 108 | sqlite3_result_error(context, "input is not zlib compressed", -1); |
| 109 | 109 | } |
| 110 | 110 | } |
| 111 | 111 | |
| 112 | 112 | /* |
| 113 | -** Add the content(), compress(), and decompress() SQL functions to | |
| 113 | +** Add the content(), compress(), and decompress() SQL functions to | |
| 114 | 114 | ** database connection db. |
| 115 | 115 | */ |
| 116 | 116 | int add_content_sql_commands(sqlite3 *db){ |
| 117 | 117 | sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, |
| 118 | 118 | sqlcmd_content, 0, 0); |
| @@ -132,11 +132,13 @@ | ||
| 132 | 132 | sqlite3 *db, |
| 133 | 133 | const char **pzErrMsg, |
| 134 | 134 | const void *notUsed |
| 135 | 135 | ){ |
| 136 | 136 | add_content_sql_commands(db); |
| 137 | + db_add_aux_functions(db); | |
| 137 | 138 | re_add_sql_func(db); |
| 139 | + search_sql_setup(db); | |
| 138 | 140 | g.zMainDbType = "repository"; |
| 139 | 141 | foci_register(db); |
| 140 | 142 | g.repositoryOpen = 1; |
| 141 | 143 | g.db = db; |
| 142 | 144 | return SQLITE_OK; |
| @@ -147,15 +149,45 @@ | ||
| 147 | 149 | ** |
| 148 | 150 | ** Usage: %fossil sqlite3 ?DATABASE? ?OPTIONS? |
| 149 | 151 | ** |
| 150 | 152 | ** Run the standalone sqlite3 command-line shell on DATABASE with OPTIONS. |
| 151 | 153 | ** If DATABASE is omitted, then the repository that serves the working |
| 152 | -** directory is opened. | |
| 154 | +** directory is opened. See https://www.sqlite.org/cli.html for additional | |
| 155 | +** information. | |
| 153 | 156 | ** |
| 154 | 157 | ** WARNING: Careless use of this command can corrupt a Fossil repository |
| 155 | 158 | ** in ways that are unrecoverable. Be sure you know what you are doing before |
| 156 | 159 | ** running any SQL commands that modifies the repository database. |
| 160 | +** | |
| 161 | +** The following extensions to the usual SQLite commands are provided: | |
| 162 | +** | |
| 163 | +** content(X) Return the contenxt of artifact X. X can be a | |
| 164 | +** SHA1 hash or prefix or a tag. | |
| 165 | +** | |
| 166 | +** compress(X) Compress text X. | |
| 167 | +** | |
| 168 | +** decompress(X) Decompress text X. Undoes the work of | |
| 169 | +** compress(X). | |
| 170 | +** | |
| 171 | +** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) | |
| 172 | +** found in check-in X (another BLOB.RID value). | |
| 173 | +** | |
| 174 | +** symbolic_name_to_rid(X) Return a the BLOB.RID corresponding to symbolic | |
| 175 | +** name X. | |
| 176 | +** | |
| 177 | +** now() Return the number of seconds since 1970. | |
| 178 | +** | |
| 179 | +** REGEXP The REGEXP operator works, unlike in | |
| 180 | +** standard SQLite. | |
| 181 | +** | |
| 182 | +** files_of_checkin The "files_of_check" virtual table is | |
| 183 | +** available for decoding manifests. | |
| 184 | +** | |
| 185 | +** Usage example for files_of_checkin: | |
| 186 | +** | |
| 187 | +** CREATE VIRTUAL TABLE temp.foci USING files_of_checkin; | |
| 188 | +** SELECT * FROM foci WHERE checkinID=symbolic_name_to_rid('trunk'); | |
| 157 | 189 | */ |
| 158 | 190 | void cmd_sqlite3(void){ |
| 159 | 191 | extern int sqlite3_shell(int, char**); |
| 160 | 192 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 161 | 193 | db_close(1); |
| 162 | 194 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -108,11 +108,11 @@ | |
| 108 | sqlite3_result_error(context, "input is not zlib compressed", -1); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | /* |
| 113 | ** Add the content(), compress(), and decompress() SQL functions to |
| 114 | ** database connection db. |
| 115 | */ |
| 116 | int add_content_sql_commands(sqlite3 *db){ |
| 117 | sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, |
| 118 | sqlcmd_content, 0, 0); |
| @@ -132,11 +132,13 @@ | |
| 132 | sqlite3 *db, |
| 133 | const char **pzErrMsg, |
| 134 | const void *notUsed |
| 135 | ){ |
| 136 | add_content_sql_commands(db); |
| 137 | re_add_sql_func(db); |
| 138 | g.zMainDbType = "repository"; |
| 139 | foci_register(db); |
| 140 | g.repositoryOpen = 1; |
| 141 | g.db = db; |
| 142 | return SQLITE_OK; |
| @@ -147,15 +149,45 @@ | |
| 147 | ** |
| 148 | ** Usage: %fossil sqlite3 ?DATABASE? ?OPTIONS? |
| 149 | ** |
| 150 | ** Run the standalone sqlite3 command-line shell on DATABASE with OPTIONS. |
| 151 | ** If DATABASE is omitted, then the repository that serves the working |
| 152 | ** directory is opened. |
| 153 | ** |
| 154 | ** WARNING: Careless use of this command can corrupt a Fossil repository |
| 155 | ** in ways that are unrecoverable. Be sure you know what you are doing before |
| 156 | ** running any SQL commands that modifies the repository database. |
| 157 | */ |
| 158 | void cmd_sqlite3(void){ |
| 159 | extern int sqlite3_shell(int, char**); |
| 160 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 161 | db_close(1); |
| 162 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -108,11 +108,11 @@ | |
| 108 | sqlite3_result_error(context, "input is not zlib compressed", -1); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | /* |
| 113 | ** Add the content(), compress(), and decompress() SQL functions to |
| 114 | ** database connection db. |
| 115 | */ |
| 116 | int add_content_sql_commands(sqlite3 *db){ |
| 117 | sqlite3_create_function(db, "content", 1, SQLITE_UTF8, 0, |
| 118 | sqlcmd_content, 0, 0); |
| @@ -132,11 +132,13 @@ | |
| 132 | sqlite3 *db, |
| 133 | const char **pzErrMsg, |
| 134 | const void *notUsed |
| 135 | ){ |
| 136 | add_content_sql_commands(db); |
| 137 | db_add_aux_functions(db); |
| 138 | re_add_sql_func(db); |
| 139 | search_sql_setup(db); |
| 140 | g.zMainDbType = "repository"; |
| 141 | foci_register(db); |
| 142 | g.repositoryOpen = 1; |
| 143 | g.db = db; |
| 144 | return SQLITE_OK; |
| @@ -147,15 +149,45 @@ | |
| 149 | ** |
| 150 | ** Usage: %fossil sqlite3 ?DATABASE? ?OPTIONS? |
| 151 | ** |
| 152 | ** Run the standalone sqlite3 command-line shell on DATABASE with OPTIONS. |
| 153 | ** If DATABASE is omitted, then the repository that serves the working |
| 154 | ** directory is opened. See https://www.sqlite.org/cli.html for additional |
| 155 | ** information. |
| 156 | ** |
| 157 | ** WARNING: Careless use of this command can corrupt a Fossil repository |
| 158 | ** in ways that are unrecoverable. Be sure you know what you are doing before |
| 159 | ** running any SQL commands that modifies the repository database. |
| 160 | ** |
| 161 | ** The following extensions to the usual SQLite commands are provided: |
| 162 | ** |
| 163 | ** content(X) Return the contenxt of artifact X. X can be a |
| 164 | ** SHA1 hash or prefix or a tag. |
| 165 | ** |
| 166 | ** compress(X) Compress text X. |
| 167 | ** |
| 168 | ** decompress(X) Decompress text X. Undoes the work of |
| 169 | ** compress(X). |
| 170 | ** |
| 171 | ** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID) |
| 172 | ** found in check-in X (another BLOB.RID value). |
| 173 | ** |
| 174 | ** symbolic_name_to_rid(X) Return a the BLOB.RID corresponding to symbolic |
| 175 | ** name X. |
| 176 | ** |
| 177 | ** now() Return the number of seconds since 1970. |
| 178 | ** |
| 179 | ** REGEXP The REGEXP operator works, unlike in |
| 180 | ** standard SQLite. |
| 181 | ** |
| 182 | ** files_of_checkin The "files_of_check" virtual table is |
| 183 | ** available for decoding manifests. |
| 184 | ** |
| 185 | ** Usage example for files_of_checkin: |
| 186 | ** |
| 187 | ** CREATE VIRTUAL TABLE temp.foci USING files_of_checkin; |
| 188 | ** SELECT * FROM foci WHERE checkinID=symbolic_name_to_rid('trunk'); |
| 189 | */ |
| 190 | void cmd_sqlite3(void){ |
| 191 | extern int sqlite3_shell(int, char**); |
| 192 | db_find_and_open_repository(OPEN_ANY_SCHEMA, 0); |
| 193 | db_close(1); |
| 194 |
+759
-522
| --- src/sqlite3.c | ||
| +++ src/sqlite3.c | ||
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | /****************************************************************************** |
| 2 | 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | -** version 3.8.8. By combining all the individual C code files into this | |
| 3 | +** version 3.8.8.2. By combining all the individual C code files into this | |
| 4 | 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | 8 | ** translation unit. |
| @@ -41,10 +41,57 @@ | ||
| 41 | 41 | ** |
| 42 | 42 | */ |
| 43 | 43 | #ifndef _SQLITEINT_H_ |
| 44 | 44 | #define _SQLITEINT_H_ |
| 45 | 45 | |
| 46 | +/* | |
| 47 | +** Include the header file used to customize the compiler options for MSVC. | |
| 48 | +** This should be done first so that it can successfully prevent spurious | |
| 49 | +** compiler warnings due to subsequent content in this file and other files | |
| 50 | +** that are included by this file. | |
| 51 | +*/ | |
| 52 | +/************** Include msvc.h in the middle of sqliteInt.h ******************/ | |
| 53 | +/************** Begin file msvc.h ********************************************/ | |
| 54 | +/* | |
| 55 | +** 2015 January 12 | |
| 56 | +** | |
| 57 | +** The author disclaims copyright to this source code. In place of | |
| 58 | +** a legal notice, here is a blessing: | |
| 59 | +** | |
| 60 | +** May you do good and not evil. | |
| 61 | +** May you find forgiveness for yourself and forgive others. | |
| 62 | +** May you share freely, never taking more than you give. | |
| 63 | +** | |
| 64 | +****************************************************************************** | |
| 65 | +** | |
| 66 | +** This file contains code that is specific to MSVC. | |
| 67 | +*/ | |
| 68 | +#ifndef _MSVC_H_ | |
| 69 | +#define _MSVC_H_ | |
| 70 | + | |
| 71 | +#if defined(_MSC_VER) | |
| 72 | +#pragma warning(disable : 4054) | |
| 73 | +#pragma warning(disable : 4055) | |
| 74 | +#pragma warning(disable : 4100) | |
| 75 | +#pragma warning(disable : 4127) | |
| 76 | +#pragma warning(disable : 4152) | |
| 77 | +#pragma warning(disable : 4189) | |
| 78 | +#pragma warning(disable : 4206) | |
| 79 | +#pragma warning(disable : 4210) | |
| 80 | +#pragma warning(disable : 4232) | |
| 81 | +#pragma warning(disable : 4244) | |
| 82 | +#pragma warning(disable : 4305) | |
| 83 | +#pragma warning(disable : 4306) | |
| 84 | +#pragma warning(disable : 4702) | |
| 85 | +#pragma warning(disable : 4706) | |
| 86 | +#endif /* defined(_MSC_VER) */ | |
| 87 | + | |
| 88 | +#endif /* _MSVC_H_ */ | |
| 89 | + | |
| 90 | +/************** End of msvc.h ************************************************/ | |
| 91 | +/************** Continuing where we left off in sqliteInt.h ******************/ | |
| 92 | + | |
| 46 | 93 | /* |
| 47 | 94 | ** These #defines should enable >2GB file support on POSIX if the |
| 48 | 95 | ** underlying operating system supports it. If the OS lacks |
| 49 | 96 | ** large file support, or if the OS is windows, these should be no-ops. |
| 50 | 97 | ** |
| @@ -229,13 +276,13 @@ | ||
| 229 | 276 | ** |
| 230 | 277 | ** See also: [sqlite3_libversion()], |
| 231 | 278 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 232 | 279 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 233 | 280 | */ |
| 234 | -#define SQLITE_VERSION "3.8.8" | |
| 281 | +#define SQLITE_VERSION "3.8.8.2" | |
| 235 | 282 | #define SQLITE_VERSION_NUMBER 3008008 |
| 236 | -#define SQLITE_SOURCE_ID "2014-12-10 04:58:43 3528f8dd39acace8eeb7337994c8617313f4b04b" | |
| 283 | +#define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098" | |
| 237 | 284 | |
| 238 | 285 | /* |
| 239 | 286 | ** CAPI3REF: Run-Time Library Version Numbers |
| 240 | 287 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 241 | 288 | ** |
| @@ -323,11 +370,11 @@ | ||
| 323 | 370 | ** This interface only reports on the compile-time mutex setting |
| 324 | 371 | ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with |
| 325 | 372 | ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but |
| 326 | 373 | ** can be fully or partially disabled using a call to [sqlite3_config()] |
| 327 | 374 | ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], |
| 328 | -** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the | |
| 375 | +** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the | |
| 329 | 376 | ** sqlite3_threadsafe() function shows only the compile-time setting of |
| 330 | 377 | ** thread safety, not any run-time changes to that setting made by |
| 331 | 378 | ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() |
| 332 | 379 | ** is unchanged by calls to sqlite3_config().)^ |
| 333 | 380 | ** |
| @@ -1692,11 +1739,11 @@ | ||
| 1692 | 1739 | ** configuration option. |
| 1693 | 1740 | ** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to |
| 1694 | 1741 | ** 8-byte aligned |
| 1695 | 1742 | ** memory, the size of each page buffer (sz), and the number of pages (N). |
| 1696 | 1743 | ** The sz argument should be the size of the largest database page |
| 1697 | -** (a power of two between 512 and 32768) plus some extra bytes for each | |
| 1744 | +** (a power of two between 512 and 65536) plus some extra bytes for each | |
| 1698 | 1745 | ** page header. ^The number of extra bytes needed by the page header |
| 1699 | 1746 | ** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option |
| 1700 | 1747 | ** to [sqlite3_config()]. |
| 1701 | 1748 | ** ^It is harmless, apart from the wasted memory, |
| 1702 | 1749 | ** for the sz parameter to be larger than necessary. The first |
| @@ -1872,10 +1919,21 @@ | ||
| 1872 | 1919 | ** <dd>^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which |
| 1873 | 1920 | ** is a pointer to an integer and writes into that integer the number of extra |
| 1874 | 1921 | ** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE]. |
| 1875 | 1922 | ** The amount of extra space required can change depending on the compiler, |
| 1876 | 1923 | ** target platform, and SQLite version. |
| 1924 | +** | |
| 1925 | +** [[SQLITE_CONFIG_PMASZ]] | |
| 1926 | +** <dt>SQLITE_CONFIG_PMASZ | |
| 1927 | +** <dd>^The SQLITE_CONFIG_PMASZ option takes a single parameter which | |
| 1928 | +** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded | |
| 1929 | +** sorter to that integer. The default minimum PMA Size is set by the | |
| 1930 | +** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched | |
| 1931 | +** to help with sort operations when multithreaded sorting | |
| 1932 | +** is enabled (using the [PRAGMA threads] command) and the amount of content | |
| 1933 | +** to be sorted exceeds the page size times the minimum of the | |
| 1934 | +** [PRAGMA cache_size] setting and this value. | |
| 1877 | 1935 | ** </dl> |
| 1878 | 1936 | */ |
| 1879 | 1937 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 1880 | 1938 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 1881 | 1939 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| @@ -1898,10 +1956,11 @@ | ||
| 1898 | 1956 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 1899 | 1957 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 1900 | 1958 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 1901 | 1959 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 1902 | 1960 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 1961 | +#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ | |
| 1903 | 1962 | |
| 1904 | 1963 | /* |
| 1905 | 1964 | ** CAPI3REF: Database Connection Configuration Options |
| 1906 | 1965 | ** |
| 1907 | 1966 | ** These constants are the available integer configuration options that |
| @@ -7307,16 +7366,14 @@ | ||
| 7307 | 7366 | |
| 7308 | 7367 | /* |
| 7309 | 7368 | ** CAPI3REF: Write-Ahead Log Commit Hook |
| 7310 | 7369 | ** |
| 7311 | 7370 | ** ^The [sqlite3_wal_hook()] function is used to register a callback that |
| 7312 | -** will be invoked each time a database connection commits data to a | |
| 7313 | -** [write-ahead log] (i.e. whenever a transaction is committed in | |
| 7314 | -** [journal_mode | journal_mode=WAL mode]). | |
| 7371 | +** is invoked each time data is committed to a database in wal mode. | |
| 7315 | 7372 | ** |
| 7316 | -** ^The callback is invoked by SQLite after the commit has taken place and | |
| 7317 | -** the associated write-lock on the database released, so the implementation | |
| 7373 | +** ^(The callback is invoked by SQLite after the commit has taken place and | |
| 7374 | +** the associated write-lock on the database released)^, so the implementation | |
| 7318 | 7375 | ** may read, write or [checkpoint] the database as required. |
| 7319 | 7376 | ** |
| 7320 | 7377 | ** ^The first parameter passed to the callback function when it is invoked |
| 7321 | 7378 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when |
| 7322 | 7379 | ** registering the callback. ^The second is a copy of the database handle. |
| @@ -7603,10 +7660,14 @@ | ||
| 7603 | 7660 | ** |
| 7604 | 7661 | ** The following constants can be used for the T parameter to the |
| 7605 | 7662 | ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a |
| 7606 | 7663 | ** different metric for sqlite3_stmt_scanstatus() to return. |
| 7607 | 7664 | ** |
| 7665 | +** When the value returned to V is a string, space to hold that string is | |
| 7666 | +** managed by the prepared statement S and will be automatically freed when | |
| 7667 | +** S is finalized. | |
| 7668 | +** | |
| 7608 | 7669 | ** <dl> |
| 7609 | 7670 | ** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt> |
| 7610 | 7671 | ** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be |
| 7611 | 7672 | ** set to the total number of times that the X-th loop has run.</dd> |
| 7612 | 7673 | ** |
| @@ -7648,11 +7709,18 @@ | ||
| 7648 | 7709 | #define SQLITE_SCANSTAT_SELECTID 5 |
| 7649 | 7710 | |
| 7650 | 7711 | /* |
| 7651 | 7712 | ** CAPI3REF: Prepared Statement Scan Status |
| 7652 | 7713 | ** |
| 7653 | -** Return status data for a single loop within query pStmt. | |
| 7714 | +** This interface returns information about the predicted and measured | |
| 7715 | +** performance for pStmt. Advanced applications can use this | |
| 7716 | +** interface to compare the predicted and the measured performance and | |
| 7717 | +** issue warnings and/or rerun [ANALYZE] if discrepancies are found. | |
| 7718 | +** | |
| 7719 | +** Since this interface is expected to be rarely used, it is only | |
| 7720 | +** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS] | |
| 7721 | +** compile-time option. | |
| 7654 | 7722 | ** |
| 7655 | 7723 | ** The "iScanStatusOp" parameter determines which status information to return. |
| 7656 | 7724 | ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior |
| 7657 | 7725 | ** of this interface is undefined. |
| 7658 | 7726 | ** ^The requested measurement is written into a variable pointed to by |
| @@ -7666,13 +7734,10 @@ | ||
| 7666 | 7734 | ** ^Statistics might not be available for all loops in all statements. ^In cases |
| 7667 | 7735 | ** where there exist loops with no available statistics, this function behaves |
| 7668 | 7736 | ** as if the loop did not exist - it returns non-zero and leave the variable |
| 7669 | 7737 | ** that pOut points to unchanged. |
| 7670 | 7738 | ** |
| 7671 | -** This API is only available if the library is built with pre-processor | |
| 7672 | -** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. | |
| 7673 | -** | |
| 7674 | 7739 | ** See also: [sqlite3_stmt_scanstatus_reset()] |
| 7675 | 7740 | */ |
| 7676 | 7741 | SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( |
| 7677 | 7742 | sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ |
| 7678 | 7743 | int idx, /* Index of loop to report on */ |
| @@ -9100,11 +9165,11 @@ | ||
| 9100 | 9165 | #define _BTREE_H_ |
| 9101 | 9166 | |
| 9102 | 9167 | /* TODO: This definition is just included so other modules compile. It |
| 9103 | 9168 | ** needs to be revisited. |
| 9104 | 9169 | */ |
| 9105 | -#define SQLITE_N_BTREE_META 10 | |
| 9170 | +#define SQLITE_N_BTREE_META 16 | |
| 9106 | 9171 | |
| 9107 | 9172 | /* |
| 9108 | 9173 | ** If defined as non-zero, auto-vacuum is enabled by default. Otherwise |
| 9109 | 9174 | ** it must be turned on for each database using "PRAGMA auto_vacuum = 1". |
| 9110 | 9175 | */ |
| @@ -9215,10 +9280,15 @@ | ||
| 9215 | 9280 | ** offset = 36 + (idx * 4) |
| 9216 | 9281 | ** |
| 9217 | 9282 | ** For example, the free-page-count field is located at byte offset 36 of |
| 9218 | 9283 | ** the database file header. The incr-vacuum-flag field is located at |
| 9219 | 9284 | ** byte offset 64 (== 36+4*7). |
| 9285 | +** | |
| 9286 | +** The BTREE_DATA_VERSION value is not really a value stored in the header. | |
| 9287 | +** It is a read-only number computed by the pager. But we merge it with | |
| 9288 | +** the header value access routines since its access pattern is the same. | |
| 9289 | +** Call it a "virtual meta value". | |
| 9220 | 9290 | */ |
| 9221 | 9291 | #define BTREE_FREE_PAGE_COUNT 0 |
| 9222 | 9292 | #define BTREE_SCHEMA_VERSION 1 |
| 9223 | 9293 | #define BTREE_FILE_FORMAT 2 |
| 9224 | 9294 | #define BTREE_DEFAULT_CACHE_SIZE 3 |
| @@ -9225,10 +9295,11 @@ | ||
| 9225 | 9295 | #define BTREE_LARGEST_ROOT_PAGE 4 |
| 9226 | 9296 | #define BTREE_TEXT_ENCODING 5 |
| 9227 | 9297 | #define BTREE_USER_VERSION 6 |
| 9228 | 9298 | #define BTREE_INCR_VACUUM 7 |
| 9229 | 9299 | #define BTREE_APPLICATION_ID 8 |
| 9300 | +#define BTREE_DATA_VERSION 15 /* A virtual meta-value */ | |
| 9230 | 9301 | |
| 9231 | 9302 | /* |
| 9232 | 9303 | ** Values that may be OR'd together to form the second argument of an |
| 9233 | 9304 | ** sqlite3BtreeCursorHints() call. |
| 9234 | 9305 | */ |
| @@ -10006,10 +10077,11 @@ | ||
| 10006 | 10077 | SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); |
| 10007 | 10078 | #endif |
| 10008 | 10079 | |
| 10009 | 10080 | /* Functions used to query pager state and configuration. */ |
| 10010 | 10081 | SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); |
| 10082 | +SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*); | |
| 10011 | 10083 | SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); |
| 10012 | 10084 | SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); |
| 10013 | 10085 | SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); |
| 10014 | 10086 | SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); |
| 10015 | 10087 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); |
| @@ -10747,10 +10819,11 @@ | ||
| 10747 | 10819 | i64 szMmap; /* Default mmap_size setting */ |
| 10748 | 10820 | unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ |
| 10749 | 10821 | int errCode; /* Most recent error code (SQLITE_*) */ |
| 10750 | 10822 | int errMask; /* & result codes with this before returning */ |
| 10751 | 10823 | u16 dbOptFlags; /* Flags to enable/disable optimizations */ |
| 10824 | + u8 enc; /* Text encoding */ | |
| 10752 | 10825 | u8 autoCommit; /* The auto-commit flag. */ |
| 10753 | 10826 | u8 temp_store; /* 1: file 2: memory 0: default */ |
| 10754 | 10827 | u8 mallocFailed; /* True if we have seen a malloc failure */ |
| 10755 | 10828 | u8 dfltLockMode; /* Default locking-mode for attached dbs */ |
| 10756 | 10829 | signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ |
| @@ -10848,11 +10921,12 @@ | ||
| 10848 | 10921 | }; |
| 10849 | 10922 | |
| 10850 | 10923 | /* |
| 10851 | 10924 | ** A macro to discover the encoding of a database. |
| 10852 | 10925 | */ |
| 10853 | -#define ENC(db) ((db)->aDb[0].pSchema->enc) | |
| 10926 | +#define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc) | |
| 10927 | +#define ENC(db) ((db)->enc) | |
| 10854 | 10928 | |
| 10855 | 10929 | /* |
| 10856 | 10930 | ** Possible values for the sqlite3.flags. |
| 10857 | 10931 | */ |
| 10858 | 10932 | #define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ |
| @@ -11472,11 +11546,10 @@ | ||
| 11472 | 11546 | Index *pNext; /* The next index associated with the same table */ |
| 11473 | 11547 | Schema *pSchema; /* Schema containing this index */ |
| 11474 | 11548 | u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ |
| 11475 | 11549 | char **azColl; /* Array of collation sequence names for index */ |
| 11476 | 11550 | Expr *pPartIdxWhere; /* WHERE clause for partial indices */ |
| 11477 | - KeyInfo *pKeyInfo; /* A KeyInfo object suitable for this index */ | |
| 11478 | 11551 | int tnum; /* DB Page containing root of this index */ |
| 11479 | 11552 | LogEst szIdxRow; /* Estimated average row size in bytes */ |
| 11480 | 11553 | u16 nKeyCol; /* Number of columns forming the key */ |
| 11481 | 11554 | u16 nColumn; /* Number of columns stored in the index */ |
| 11482 | 11555 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| @@ -12036,11 +12109,11 @@ | ||
| 12036 | 12109 | #define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */ |
| 12037 | 12110 | #define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ |
| 12038 | 12111 | #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ |
| 12039 | 12112 | #define SF_Compound 0x0040 /* Part of a compound query */ |
| 12040 | 12113 | #define SF_Values 0x0080 /* Synthesized from VALUES clause */ |
| 12041 | - /* 0x0100 NOT USED */ | |
| 12114 | +#define SF_AllValues 0x0100 /* All terms of compound are VALUES */ | |
| 12042 | 12115 | #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ |
| 12043 | 12116 | #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ |
| 12044 | 12117 | #define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ |
| 12045 | 12118 | #define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */ |
| 12046 | 12119 | |
| @@ -12526,10 +12599,11 @@ | ||
| 12526 | 12599 | void *pPage; /* Page cache memory */ |
| 12527 | 12600 | int szPage; /* Size of each page in pPage[] */ |
| 12528 | 12601 | int nPage; /* Number of pages in pPage[] */ |
| 12529 | 12602 | int mxParserStack; /* maximum depth of the parser stack */ |
| 12530 | 12603 | int sharedCacheEnabled; /* true if shared-cache mode enabled */ |
| 12604 | + u32 szPma; /* Maximum Sorter PMA size */ | |
| 12531 | 12605 | /* The above might be initialized to non-zero. The following need to always |
| 12532 | 12606 | ** initially be zero, however. */ |
| 12533 | 12607 | int isInit; /* True after initialization has finished */ |
| 12534 | 12608 | int inProgress; /* True while initialization in progress */ |
| 12535 | 12609 | int isMutexInit; /* True after mutexes are initialized */ |
| @@ -12663,11 +12737,11 @@ | ||
| 12663 | 12737 | ** FTS4 is really an extension for FTS3. It is enabled using the |
| 12664 | 12738 | ** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also call |
| 12665 | 12739 | ** the SQLITE_ENABLE_FTS4 macro to serve as an alias for SQLITE_ENABLE_FTS3. |
| 12666 | 12740 | */ |
| 12667 | 12741 | #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) |
| 12668 | -# define SQLITE_ENABLE_FTS3 | |
| 12742 | +# define SQLITE_ENABLE_FTS3 1 | |
| 12669 | 12743 | #endif |
| 12670 | 12744 | |
| 12671 | 12745 | /* |
| 12672 | 12746 | ** The ctype.h header is needed for non-ASCII systems. It is also |
| 12673 | 12747 | ** needed by FTS3 when FTS3 is included in the amalgamation. |
| @@ -13448,11 +13522,11 @@ | ||
| 13448 | 13522 | ** print I/O tracing messages. |
| 13449 | 13523 | */ |
| 13450 | 13524 | #ifdef SQLITE_ENABLE_IOTRACE |
| 13451 | 13525 | # define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; } |
| 13452 | 13526 | SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe*); |
| 13453 | -SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*,...); | |
| 13527 | +void (*sqlite3IoTrace)(const char*,...); | |
| 13454 | 13528 | #else |
| 13455 | 13529 | # define IOTRACE(A) |
| 13456 | 13530 | # define sqlite3VdbeIOTraceSql(X) |
| 13457 | 13531 | #endif |
| 13458 | 13532 | |
| @@ -13661,10 +13735,17 @@ | ||
| 13661 | 13735 | */ |
| 13662 | 13736 | #ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN |
| 13663 | 13737 | # define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 |
| 13664 | 13738 | #endif |
| 13665 | 13739 | |
| 13740 | +/* The minimum PMA size is set to this value multiplied by the database | |
| 13741 | +** page size in bytes. | |
| 13742 | +*/ | |
| 13743 | +#ifndef SQLITE_SORTER_PMASZ | |
| 13744 | +# define SQLITE_SORTER_PMASZ 250 | |
| 13745 | +#endif | |
| 13746 | + | |
| 13666 | 13747 | /* |
| 13667 | 13748 | ** The following singleton contains the global configuration for |
| 13668 | 13749 | ** the SQLite library. |
| 13669 | 13750 | */ |
| 13670 | 13751 | SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { |
| @@ -13691,10 +13772,11 @@ | ||
| 13691 | 13772 | (void*)0, /* pPage */ |
| 13692 | 13773 | 0, /* szPage */ |
| 13693 | 13774 | 0, /* nPage */ |
| 13694 | 13775 | 0, /* mxParserStack */ |
| 13695 | 13776 | 0, /* sharedCacheEnabled */ |
| 13777 | + SQLITE_SORTER_PMASZ, /* szPma */ | |
| 13696 | 13778 | /* All the rest should always be initialized to zero */ |
| 13697 | 13779 | 0, /* isInit */ |
| 13698 | 13780 | 0, /* inProgress */ |
| 13699 | 13781 | 0, /* isMutexInit */ |
| 13700 | 13782 | 0, /* isMallocInit */ |
| @@ -13797,355 +13879,355 @@ | ||
| 13797 | 13879 | /* These macros are provided to "stringify" the value of the define |
| 13798 | 13880 | ** for those options in which the value is meaningful. */ |
| 13799 | 13881 | #define CTIMEOPT_VAL_(opt) #opt |
| 13800 | 13882 | #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) |
| 13801 | 13883 | |
| 13802 | -#ifdef SQLITE_32BIT_ROWID | |
| 13884 | +#if SQLITE_32BIT_ROWID | |
| 13803 | 13885 | "32BIT_ROWID", |
| 13804 | 13886 | #endif |
| 13805 | -#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC | |
| 13887 | +#if SQLITE_4_BYTE_ALIGNED_MALLOC | |
| 13806 | 13888 | "4_BYTE_ALIGNED_MALLOC", |
| 13807 | 13889 | #endif |
| 13808 | -#ifdef SQLITE_CASE_SENSITIVE_LIKE | |
| 13890 | +#if SQLITE_CASE_SENSITIVE_LIKE | |
| 13809 | 13891 | "CASE_SENSITIVE_LIKE", |
| 13810 | 13892 | #endif |
| 13811 | -#ifdef SQLITE_CHECK_PAGES | |
| 13893 | +#if SQLITE_CHECK_PAGES | |
| 13812 | 13894 | "CHECK_PAGES", |
| 13813 | 13895 | #endif |
| 13814 | -#ifdef SQLITE_COVERAGE_TEST | |
| 13896 | +#if SQLITE_COVERAGE_TEST | |
| 13815 | 13897 | "COVERAGE_TEST", |
| 13816 | 13898 | #endif |
| 13817 | -#ifdef SQLITE_DEBUG | |
| 13899 | +#if SQLITE_DEBUG | |
| 13818 | 13900 | "DEBUG", |
| 13819 | 13901 | #endif |
| 13820 | -#ifdef SQLITE_DEFAULT_LOCKING_MODE | |
| 13902 | +#if SQLITE_DEFAULT_LOCKING_MODE | |
| 13821 | 13903 | "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), |
| 13822 | 13904 | #endif |
| 13823 | 13905 | #if defined(SQLITE_DEFAULT_MMAP_SIZE) && !defined(SQLITE_DEFAULT_MMAP_SIZE_xc) |
| 13824 | 13906 | "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), |
| 13825 | 13907 | #endif |
| 13826 | -#ifdef SQLITE_DISABLE_DIRSYNC | |
| 13908 | +#if SQLITE_DISABLE_DIRSYNC | |
| 13827 | 13909 | "DISABLE_DIRSYNC", |
| 13828 | 13910 | #endif |
| 13829 | -#ifdef SQLITE_DISABLE_LFS | |
| 13911 | +#if SQLITE_DISABLE_LFS | |
| 13830 | 13912 | "DISABLE_LFS", |
| 13831 | 13913 | #endif |
| 13832 | -#ifdef SQLITE_ENABLE_API_ARMOR | |
| 13914 | +#if SQLITE_ENABLE_API_ARMOR | |
| 13833 | 13915 | "ENABLE_API_ARMOR", |
| 13834 | 13916 | #endif |
| 13835 | -#ifdef SQLITE_ENABLE_ATOMIC_WRITE | |
| 13917 | +#if SQLITE_ENABLE_ATOMIC_WRITE | |
| 13836 | 13918 | "ENABLE_ATOMIC_WRITE", |
| 13837 | 13919 | #endif |
| 13838 | -#ifdef SQLITE_ENABLE_CEROD | |
| 13920 | +#if SQLITE_ENABLE_CEROD | |
| 13839 | 13921 | "ENABLE_CEROD", |
| 13840 | 13922 | #endif |
| 13841 | -#ifdef SQLITE_ENABLE_COLUMN_METADATA | |
| 13923 | +#if SQLITE_ENABLE_COLUMN_METADATA | |
| 13842 | 13924 | "ENABLE_COLUMN_METADATA", |
| 13843 | 13925 | #endif |
| 13844 | -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT | |
| 13926 | +#if SQLITE_ENABLE_EXPENSIVE_ASSERT | |
| 13845 | 13927 | "ENABLE_EXPENSIVE_ASSERT", |
| 13846 | 13928 | #endif |
| 13847 | -#ifdef SQLITE_ENABLE_FTS1 | |
| 13929 | +#if SQLITE_ENABLE_FTS1 | |
| 13848 | 13930 | "ENABLE_FTS1", |
| 13849 | 13931 | #endif |
| 13850 | -#ifdef SQLITE_ENABLE_FTS2 | |
| 13932 | +#if SQLITE_ENABLE_FTS2 | |
| 13851 | 13933 | "ENABLE_FTS2", |
| 13852 | 13934 | #endif |
| 13853 | -#ifdef SQLITE_ENABLE_FTS3 | |
| 13935 | +#if SQLITE_ENABLE_FTS3 | |
| 13854 | 13936 | "ENABLE_FTS3", |
| 13855 | 13937 | #endif |
| 13856 | -#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS | |
| 13938 | +#if SQLITE_ENABLE_FTS3_PARENTHESIS | |
| 13857 | 13939 | "ENABLE_FTS3_PARENTHESIS", |
| 13858 | 13940 | #endif |
| 13859 | -#ifdef SQLITE_ENABLE_FTS4 | |
| 13941 | +#if SQLITE_ENABLE_FTS4 | |
| 13860 | 13942 | "ENABLE_FTS4", |
| 13861 | 13943 | #endif |
| 13862 | -#ifdef SQLITE_ENABLE_ICU | |
| 13944 | +#if SQLITE_ENABLE_ICU | |
| 13863 | 13945 | "ENABLE_ICU", |
| 13864 | 13946 | #endif |
| 13865 | -#ifdef SQLITE_ENABLE_IOTRACE | |
| 13947 | +#if SQLITE_ENABLE_IOTRACE | |
| 13866 | 13948 | "ENABLE_IOTRACE", |
| 13867 | 13949 | #endif |
| 13868 | -#ifdef SQLITE_ENABLE_LOAD_EXTENSION | |
| 13950 | +#if SQLITE_ENABLE_LOAD_EXTENSION | |
| 13869 | 13951 | "ENABLE_LOAD_EXTENSION", |
| 13870 | 13952 | #endif |
| 13871 | -#ifdef SQLITE_ENABLE_LOCKING_STYLE | |
| 13953 | +#if SQLITE_ENABLE_LOCKING_STYLE | |
| 13872 | 13954 | "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), |
| 13873 | 13955 | #endif |
| 13874 | -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT | |
| 13956 | +#if SQLITE_ENABLE_MEMORY_MANAGEMENT | |
| 13875 | 13957 | "ENABLE_MEMORY_MANAGEMENT", |
| 13876 | 13958 | #endif |
| 13877 | -#ifdef SQLITE_ENABLE_MEMSYS3 | |
| 13959 | +#if SQLITE_ENABLE_MEMSYS3 | |
| 13878 | 13960 | "ENABLE_MEMSYS3", |
| 13879 | 13961 | #endif |
| 13880 | -#ifdef SQLITE_ENABLE_MEMSYS5 | |
| 13962 | +#if SQLITE_ENABLE_MEMSYS5 | |
| 13881 | 13963 | "ENABLE_MEMSYS5", |
| 13882 | 13964 | #endif |
| 13883 | -#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK | |
| 13965 | +#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK | |
| 13884 | 13966 | "ENABLE_OVERSIZE_CELL_CHECK", |
| 13885 | 13967 | #endif |
| 13886 | -#ifdef SQLITE_ENABLE_RTREE | |
| 13968 | +#if SQLITE_ENABLE_RTREE | |
| 13887 | 13969 | "ENABLE_RTREE", |
| 13888 | 13970 | #endif |
| 13889 | 13971 | #if defined(SQLITE_ENABLE_STAT4) |
| 13890 | 13972 | "ENABLE_STAT4", |
| 13891 | 13973 | #elif defined(SQLITE_ENABLE_STAT3) |
| 13892 | 13974 | "ENABLE_STAT3", |
| 13893 | 13975 | #endif |
| 13894 | -#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY | |
| 13976 | +#if SQLITE_ENABLE_UNLOCK_NOTIFY | |
| 13895 | 13977 | "ENABLE_UNLOCK_NOTIFY", |
| 13896 | 13978 | #endif |
| 13897 | -#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT | |
| 13979 | +#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT | |
| 13898 | 13980 | "ENABLE_UPDATE_DELETE_LIMIT", |
| 13899 | 13981 | #endif |
| 13900 | -#ifdef SQLITE_HAS_CODEC | |
| 13982 | +#if SQLITE_HAS_CODEC | |
| 13901 | 13983 | "HAS_CODEC", |
| 13902 | 13984 | #endif |
| 13903 | -#ifdef SQLITE_HAVE_ISNAN | |
| 13985 | +#if HAVE_ISNAN || SQLITE_HAVE_ISNAN | |
| 13904 | 13986 | "HAVE_ISNAN", |
| 13905 | 13987 | #endif |
| 13906 | -#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX | |
| 13988 | +#if SQLITE_HOMEGROWN_RECURSIVE_MUTEX | |
| 13907 | 13989 | "HOMEGROWN_RECURSIVE_MUTEX", |
| 13908 | 13990 | #endif |
| 13909 | -#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS | |
| 13991 | +#if SQLITE_IGNORE_AFP_LOCK_ERRORS | |
| 13910 | 13992 | "IGNORE_AFP_LOCK_ERRORS", |
| 13911 | 13993 | #endif |
| 13912 | -#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS | |
| 13994 | +#if SQLITE_IGNORE_FLOCK_LOCK_ERRORS | |
| 13913 | 13995 | "IGNORE_FLOCK_LOCK_ERRORS", |
| 13914 | 13996 | #endif |
| 13915 | 13997 | #ifdef SQLITE_INT64_TYPE |
| 13916 | 13998 | "INT64_TYPE", |
| 13917 | 13999 | #endif |
| 13918 | -#ifdef SQLITE_LOCK_TRACE | |
| 14000 | +#if SQLITE_LOCK_TRACE | |
| 13919 | 14001 | "LOCK_TRACE", |
| 13920 | 14002 | #endif |
| 13921 | 14003 | #if defined(SQLITE_MAX_MMAP_SIZE) && !defined(SQLITE_MAX_MMAP_SIZE_xc) |
| 13922 | 14004 | "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), |
| 13923 | 14005 | #endif |
| 13924 | 14006 | #ifdef SQLITE_MAX_SCHEMA_RETRY |
| 13925 | 14007 | "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), |
| 13926 | 14008 | #endif |
| 13927 | -#ifdef SQLITE_MEMDEBUG | |
| 14009 | +#if SQLITE_MEMDEBUG | |
| 13928 | 14010 | "MEMDEBUG", |
| 13929 | 14011 | #endif |
| 13930 | -#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT | |
| 14012 | +#if SQLITE_MIXED_ENDIAN_64BIT_FLOAT | |
| 13931 | 14013 | "MIXED_ENDIAN_64BIT_FLOAT", |
| 13932 | 14014 | #endif |
| 13933 | -#ifdef SQLITE_NO_SYNC | |
| 14015 | +#if SQLITE_NO_SYNC | |
| 13934 | 14016 | "NO_SYNC", |
| 13935 | 14017 | #endif |
| 13936 | -#ifdef SQLITE_OMIT_ALTERTABLE | |
| 14018 | +#if SQLITE_OMIT_ALTERTABLE | |
| 13937 | 14019 | "OMIT_ALTERTABLE", |
| 13938 | 14020 | #endif |
| 13939 | -#ifdef SQLITE_OMIT_ANALYZE | |
| 14021 | +#if SQLITE_OMIT_ANALYZE | |
| 13940 | 14022 | "OMIT_ANALYZE", |
| 13941 | 14023 | #endif |
| 13942 | -#ifdef SQLITE_OMIT_ATTACH | |
| 14024 | +#if SQLITE_OMIT_ATTACH | |
| 13943 | 14025 | "OMIT_ATTACH", |
| 13944 | 14026 | #endif |
| 13945 | -#ifdef SQLITE_OMIT_AUTHORIZATION | |
| 14027 | +#if SQLITE_OMIT_AUTHORIZATION | |
| 13946 | 14028 | "OMIT_AUTHORIZATION", |
| 13947 | 14029 | #endif |
| 13948 | -#ifdef SQLITE_OMIT_AUTOINCREMENT | |
| 14030 | +#if SQLITE_OMIT_AUTOINCREMENT | |
| 13949 | 14031 | "OMIT_AUTOINCREMENT", |
| 13950 | 14032 | #endif |
| 13951 | -#ifdef SQLITE_OMIT_AUTOINIT | |
| 14033 | +#if SQLITE_OMIT_AUTOINIT | |
| 13952 | 14034 | "OMIT_AUTOINIT", |
| 13953 | 14035 | #endif |
| 13954 | -#ifdef SQLITE_OMIT_AUTOMATIC_INDEX | |
| 14036 | +#if SQLITE_OMIT_AUTOMATIC_INDEX | |
| 13955 | 14037 | "OMIT_AUTOMATIC_INDEX", |
| 13956 | 14038 | #endif |
| 13957 | -#ifdef SQLITE_OMIT_AUTORESET | |
| 14039 | +#if SQLITE_OMIT_AUTORESET | |
| 13958 | 14040 | "OMIT_AUTORESET", |
| 13959 | 14041 | #endif |
| 13960 | -#ifdef SQLITE_OMIT_AUTOVACUUM | |
| 14042 | +#if SQLITE_OMIT_AUTOVACUUM | |
| 13961 | 14043 | "OMIT_AUTOVACUUM", |
| 13962 | 14044 | #endif |
| 13963 | -#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION | |
| 14045 | +#if SQLITE_OMIT_BETWEEN_OPTIMIZATION | |
| 13964 | 14046 | "OMIT_BETWEEN_OPTIMIZATION", |
| 13965 | 14047 | #endif |
| 13966 | -#ifdef SQLITE_OMIT_BLOB_LITERAL | |
| 14048 | +#if SQLITE_OMIT_BLOB_LITERAL | |
| 13967 | 14049 | "OMIT_BLOB_LITERAL", |
| 13968 | 14050 | #endif |
| 13969 | -#ifdef SQLITE_OMIT_BTREECOUNT | |
| 14051 | +#if SQLITE_OMIT_BTREECOUNT | |
| 13970 | 14052 | "OMIT_BTREECOUNT", |
| 13971 | 14053 | #endif |
| 13972 | -#ifdef SQLITE_OMIT_BUILTIN_TEST | |
| 14054 | +#if SQLITE_OMIT_BUILTIN_TEST | |
| 13973 | 14055 | "OMIT_BUILTIN_TEST", |
| 13974 | 14056 | #endif |
| 13975 | -#ifdef SQLITE_OMIT_CAST | |
| 14057 | +#if SQLITE_OMIT_CAST | |
| 13976 | 14058 | "OMIT_CAST", |
| 13977 | 14059 | #endif |
| 13978 | -#ifdef SQLITE_OMIT_CHECK | |
| 14060 | +#if SQLITE_OMIT_CHECK | |
| 13979 | 14061 | "OMIT_CHECK", |
| 13980 | 14062 | #endif |
| 13981 | -#ifdef SQLITE_OMIT_COMPLETE | |
| 14063 | +#if SQLITE_OMIT_COMPLETE | |
| 13982 | 14064 | "OMIT_COMPLETE", |
| 13983 | 14065 | #endif |
| 13984 | -#ifdef SQLITE_OMIT_COMPOUND_SELECT | |
| 14066 | +#if SQLITE_OMIT_COMPOUND_SELECT | |
| 13985 | 14067 | "OMIT_COMPOUND_SELECT", |
| 13986 | 14068 | #endif |
| 13987 | -#ifdef SQLITE_OMIT_CTE | |
| 14069 | +#if SQLITE_OMIT_CTE | |
| 13988 | 14070 | "OMIT_CTE", |
| 13989 | 14071 | #endif |
| 13990 | -#ifdef SQLITE_OMIT_DATETIME_FUNCS | |
| 14072 | +#if SQLITE_OMIT_DATETIME_FUNCS | |
| 13991 | 14073 | "OMIT_DATETIME_FUNCS", |
| 13992 | 14074 | #endif |
| 13993 | -#ifdef SQLITE_OMIT_DECLTYPE | |
| 14075 | +#if SQLITE_OMIT_DECLTYPE | |
| 13994 | 14076 | "OMIT_DECLTYPE", |
| 13995 | 14077 | #endif |
| 13996 | -#ifdef SQLITE_OMIT_DEPRECATED | |
| 14078 | +#if SQLITE_OMIT_DEPRECATED | |
| 13997 | 14079 | "OMIT_DEPRECATED", |
| 13998 | 14080 | #endif |
| 13999 | -#ifdef SQLITE_OMIT_DISKIO | |
| 14081 | +#if SQLITE_OMIT_DISKIO | |
| 14000 | 14082 | "OMIT_DISKIO", |
| 14001 | 14083 | #endif |
| 14002 | -#ifdef SQLITE_OMIT_EXPLAIN | |
| 14084 | +#if SQLITE_OMIT_EXPLAIN | |
| 14003 | 14085 | "OMIT_EXPLAIN", |
| 14004 | 14086 | #endif |
| 14005 | -#ifdef SQLITE_OMIT_FLAG_PRAGMAS | |
| 14087 | +#if SQLITE_OMIT_FLAG_PRAGMAS | |
| 14006 | 14088 | "OMIT_FLAG_PRAGMAS", |
| 14007 | 14089 | #endif |
| 14008 | -#ifdef SQLITE_OMIT_FLOATING_POINT | |
| 14090 | +#if SQLITE_OMIT_FLOATING_POINT | |
| 14009 | 14091 | "OMIT_FLOATING_POINT", |
| 14010 | 14092 | #endif |
| 14011 | -#ifdef SQLITE_OMIT_FOREIGN_KEY | |
| 14093 | +#if SQLITE_OMIT_FOREIGN_KEY | |
| 14012 | 14094 | "OMIT_FOREIGN_KEY", |
| 14013 | 14095 | #endif |
| 14014 | -#ifdef SQLITE_OMIT_GET_TABLE | |
| 14096 | +#if SQLITE_OMIT_GET_TABLE | |
| 14015 | 14097 | "OMIT_GET_TABLE", |
| 14016 | 14098 | #endif |
| 14017 | -#ifdef SQLITE_OMIT_INCRBLOB | |
| 14099 | +#if SQLITE_OMIT_INCRBLOB | |
| 14018 | 14100 | "OMIT_INCRBLOB", |
| 14019 | 14101 | #endif |
| 14020 | -#ifdef SQLITE_OMIT_INTEGRITY_CHECK | |
| 14102 | +#if SQLITE_OMIT_INTEGRITY_CHECK | |
| 14021 | 14103 | "OMIT_INTEGRITY_CHECK", |
| 14022 | 14104 | #endif |
| 14023 | -#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION | |
| 14105 | +#if SQLITE_OMIT_LIKE_OPTIMIZATION | |
| 14024 | 14106 | "OMIT_LIKE_OPTIMIZATION", |
| 14025 | 14107 | #endif |
| 14026 | -#ifdef SQLITE_OMIT_LOAD_EXTENSION | |
| 14108 | +#if SQLITE_OMIT_LOAD_EXTENSION | |
| 14027 | 14109 | "OMIT_LOAD_EXTENSION", |
| 14028 | 14110 | #endif |
| 14029 | -#ifdef SQLITE_OMIT_LOCALTIME | |
| 14111 | +#if SQLITE_OMIT_LOCALTIME | |
| 14030 | 14112 | "OMIT_LOCALTIME", |
| 14031 | 14113 | #endif |
| 14032 | -#ifdef SQLITE_OMIT_LOOKASIDE | |
| 14114 | +#if SQLITE_OMIT_LOOKASIDE | |
| 14033 | 14115 | "OMIT_LOOKASIDE", |
| 14034 | 14116 | #endif |
| 14035 | -#ifdef SQLITE_OMIT_MEMORYDB | |
| 14117 | +#if SQLITE_OMIT_MEMORYDB | |
| 14036 | 14118 | "OMIT_MEMORYDB", |
| 14037 | 14119 | #endif |
| 14038 | -#ifdef SQLITE_OMIT_OR_OPTIMIZATION | |
| 14120 | +#if SQLITE_OMIT_OR_OPTIMIZATION | |
| 14039 | 14121 | "OMIT_OR_OPTIMIZATION", |
| 14040 | 14122 | #endif |
| 14041 | -#ifdef SQLITE_OMIT_PAGER_PRAGMAS | |
| 14123 | +#if SQLITE_OMIT_PAGER_PRAGMAS | |
| 14042 | 14124 | "OMIT_PAGER_PRAGMAS", |
| 14043 | 14125 | #endif |
| 14044 | -#ifdef SQLITE_OMIT_PRAGMA | |
| 14126 | +#if SQLITE_OMIT_PRAGMA | |
| 14045 | 14127 | "OMIT_PRAGMA", |
| 14046 | 14128 | #endif |
| 14047 | -#ifdef SQLITE_OMIT_PROGRESS_CALLBACK | |
| 14129 | +#if SQLITE_OMIT_PROGRESS_CALLBACK | |
| 14048 | 14130 | "OMIT_PROGRESS_CALLBACK", |
| 14049 | 14131 | #endif |
| 14050 | -#ifdef SQLITE_OMIT_QUICKBALANCE | |
| 14132 | +#if SQLITE_OMIT_QUICKBALANCE | |
| 14051 | 14133 | "OMIT_QUICKBALANCE", |
| 14052 | 14134 | #endif |
| 14053 | -#ifdef SQLITE_OMIT_REINDEX | |
| 14135 | +#if SQLITE_OMIT_REINDEX | |
| 14054 | 14136 | "OMIT_REINDEX", |
| 14055 | 14137 | #endif |
| 14056 | -#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS | |
| 14138 | +#if SQLITE_OMIT_SCHEMA_PRAGMAS | |
| 14057 | 14139 | "OMIT_SCHEMA_PRAGMAS", |
| 14058 | 14140 | #endif |
| 14059 | -#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS | |
| 14141 | +#if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS | |
| 14060 | 14142 | "OMIT_SCHEMA_VERSION_PRAGMAS", |
| 14061 | 14143 | #endif |
| 14062 | -#ifdef SQLITE_OMIT_SHARED_CACHE | |
| 14144 | +#if SQLITE_OMIT_SHARED_CACHE | |
| 14063 | 14145 | "OMIT_SHARED_CACHE", |
| 14064 | 14146 | #endif |
| 14065 | -#ifdef SQLITE_OMIT_SUBQUERY | |
| 14147 | +#if SQLITE_OMIT_SUBQUERY | |
| 14066 | 14148 | "OMIT_SUBQUERY", |
| 14067 | 14149 | #endif |
| 14068 | -#ifdef SQLITE_OMIT_TCL_VARIABLE | |
| 14150 | +#if SQLITE_OMIT_TCL_VARIABLE | |
| 14069 | 14151 | "OMIT_TCL_VARIABLE", |
| 14070 | 14152 | #endif |
| 14071 | -#ifdef SQLITE_OMIT_TEMPDB | |
| 14153 | +#if SQLITE_OMIT_TEMPDB | |
| 14072 | 14154 | "OMIT_TEMPDB", |
| 14073 | 14155 | #endif |
| 14074 | -#ifdef SQLITE_OMIT_TRACE | |
| 14156 | +#if SQLITE_OMIT_TRACE | |
| 14075 | 14157 | "OMIT_TRACE", |
| 14076 | 14158 | #endif |
| 14077 | -#ifdef SQLITE_OMIT_TRIGGER | |
| 14159 | +#if SQLITE_OMIT_TRIGGER | |
| 14078 | 14160 | "OMIT_TRIGGER", |
| 14079 | 14161 | #endif |
| 14080 | -#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION | |
| 14162 | +#if SQLITE_OMIT_TRUNCATE_OPTIMIZATION | |
| 14081 | 14163 | "OMIT_TRUNCATE_OPTIMIZATION", |
| 14082 | 14164 | #endif |
| 14083 | -#ifdef SQLITE_OMIT_UTF16 | |
| 14165 | +#if SQLITE_OMIT_UTF16 | |
| 14084 | 14166 | "OMIT_UTF16", |
| 14085 | 14167 | #endif |
| 14086 | -#ifdef SQLITE_OMIT_VACUUM | |
| 14168 | +#if SQLITE_OMIT_VACUUM | |
| 14087 | 14169 | "OMIT_VACUUM", |
| 14088 | 14170 | #endif |
| 14089 | -#ifdef SQLITE_OMIT_VIEW | |
| 14171 | +#if SQLITE_OMIT_VIEW | |
| 14090 | 14172 | "OMIT_VIEW", |
| 14091 | 14173 | #endif |
| 14092 | -#ifdef SQLITE_OMIT_VIRTUALTABLE | |
| 14174 | +#if SQLITE_OMIT_VIRTUALTABLE | |
| 14093 | 14175 | "OMIT_VIRTUALTABLE", |
| 14094 | 14176 | #endif |
| 14095 | -#ifdef SQLITE_OMIT_WAL | |
| 14177 | +#if SQLITE_OMIT_WAL | |
| 14096 | 14178 | "OMIT_WAL", |
| 14097 | 14179 | #endif |
| 14098 | -#ifdef SQLITE_OMIT_WSD | |
| 14180 | +#if SQLITE_OMIT_WSD | |
| 14099 | 14181 | "OMIT_WSD", |
| 14100 | 14182 | #endif |
| 14101 | -#ifdef SQLITE_OMIT_XFER_OPT | |
| 14183 | +#if SQLITE_OMIT_XFER_OPT | |
| 14102 | 14184 | "OMIT_XFER_OPT", |
| 14103 | 14185 | #endif |
| 14104 | -#ifdef SQLITE_PERFORMANCE_TRACE | |
| 14186 | +#if SQLITE_PERFORMANCE_TRACE | |
| 14105 | 14187 | "PERFORMANCE_TRACE", |
| 14106 | 14188 | #endif |
| 14107 | -#ifdef SQLITE_PROXY_DEBUG | |
| 14189 | +#if SQLITE_PROXY_DEBUG | |
| 14108 | 14190 | "PROXY_DEBUG", |
| 14109 | 14191 | #endif |
| 14110 | -#ifdef SQLITE_RTREE_INT_ONLY | |
| 14192 | +#if SQLITE_RTREE_INT_ONLY | |
| 14111 | 14193 | "RTREE_INT_ONLY", |
| 14112 | 14194 | #endif |
| 14113 | -#ifdef SQLITE_SECURE_DELETE | |
| 14195 | +#if SQLITE_SECURE_DELETE | |
| 14114 | 14196 | "SECURE_DELETE", |
| 14115 | 14197 | #endif |
| 14116 | -#ifdef SQLITE_SMALL_STACK | |
| 14198 | +#if SQLITE_SMALL_STACK | |
| 14117 | 14199 | "SMALL_STACK", |
| 14118 | 14200 | #endif |
| 14119 | -#ifdef SQLITE_SOUNDEX | |
| 14201 | +#if SQLITE_SOUNDEX | |
| 14120 | 14202 | "SOUNDEX", |
| 14121 | 14203 | #endif |
| 14122 | -#ifdef SQLITE_SYSTEM_MALLOC | |
| 14204 | +#if SQLITE_SYSTEM_MALLOC | |
| 14123 | 14205 | "SYSTEM_MALLOC", |
| 14124 | 14206 | #endif |
| 14125 | -#ifdef SQLITE_TCL | |
| 14207 | +#if SQLITE_TCL | |
| 14126 | 14208 | "TCL", |
| 14127 | 14209 | #endif |
| 14128 | 14210 | #if defined(SQLITE_TEMP_STORE) && !defined(SQLITE_TEMP_STORE_xc) |
| 14129 | 14211 | "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), |
| 14130 | 14212 | #endif |
| 14131 | -#ifdef SQLITE_TEST | |
| 14213 | +#if SQLITE_TEST | |
| 14132 | 14214 | "TEST", |
| 14133 | 14215 | #endif |
| 14134 | 14216 | #if defined(SQLITE_THREADSAFE) |
| 14135 | 14217 | "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), |
| 14136 | 14218 | #endif |
| 14137 | -#ifdef SQLITE_USE_ALLOCA | |
| 14219 | +#if SQLITE_USE_ALLOCA | |
| 14138 | 14220 | "USE_ALLOCA", |
| 14139 | 14221 | #endif |
| 14140 | -#ifdef SQLITE_USER_AUTHENTICATION | |
| 14222 | +#if SQLITE_USER_AUTHENTICATION | |
| 14141 | 14223 | "USER_AUTHENTICATION", |
| 14142 | 14224 | #endif |
| 14143 | -#ifdef SQLITE_WIN32_MALLOC | |
| 14225 | +#if SQLITE_WIN32_MALLOC | |
| 14144 | 14226 | "WIN32_MALLOC", |
| 14145 | 14227 | #endif |
| 14146 | -#ifdef SQLITE_ZERO_MALLOC | |
| 14228 | +#if SQLITE_ZERO_MALLOC | |
| 14147 | 14229 | "ZERO_MALLOC" |
| 14148 | 14230 | #endif |
| 14149 | 14231 | }; |
| 14150 | 14232 | |
| 14151 | 14233 | /* |
| @@ -14156,11 +14238,11 @@ | ||
| 14156 | 14238 | ** is not required for a match. |
| 14157 | 14239 | */ |
| 14158 | 14240 | SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ |
| 14159 | 14241 | int i, n; |
| 14160 | 14242 | |
| 14161 | -#ifdef SQLITE_ENABLE_API_ARMOR | |
| 14243 | +#if SQLITE_ENABLE_API_ARMOR | |
| 14162 | 14244 | if( zOptName==0 ){ |
| 14163 | 14245 | (void)SQLITE_MISUSE_BKPT; |
| 14164 | 14246 | return 0; |
| 14165 | 14247 | } |
| 14166 | 14248 | #endif |
| @@ -15384,12 +15466,13 @@ | ||
| 15384 | 15466 | ** |
| 15385 | 15467 | ** If the user has not indicated to use localtime_r() or localtime_s() |
| 15386 | 15468 | ** already, check for an MSVC build environment that provides |
| 15387 | 15469 | ** localtime_s(). |
| 15388 | 15470 | */ |
| 15389 | -#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \ | |
| 15390 | - defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) | |
| 15471 | +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S \ | |
| 15472 | + && defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) | |
| 15473 | +#undef HAVE_LOCALTIME_S | |
| 15391 | 15474 | #define HAVE_LOCALTIME_S 1 |
| 15392 | 15475 | #endif |
| 15393 | 15476 | |
| 15394 | 15477 | #ifndef SQLITE_OMIT_LOCALTIME |
| 15395 | 15478 | /* |
| @@ -15405,12 +15488,11 @@ | ||
| 15405 | 15488 | ** library function localtime_r() is used to assist in the calculation of |
| 15406 | 15489 | ** local time. |
| 15407 | 15490 | */ |
| 15408 | 15491 | static int osLocaltime(time_t *t, struct tm *pTm){ |
| 15409 | 15492 | int rc; |
| 15410 | -#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \ | |
| 15411 | - && (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S) | |
| 15493 | +#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S | |
| 15412 | 15494 | struct tm *pX; |
| 15413 | 15495 | #if SQLITE_THREADSAFE>0 |
| 15414 | 15496 | sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); |
| 15415 | 15497 | #endif |
| 15416 | 15498 | sqlite3_mutex_enter(mutex); |
| @@ -15423,11 +15505,11 @@ | ||
| 15423 | 15505 | rc = pX==0; |
| 15424 | 15506 | #else |
| 15425 | 15507 | #ifndef SQLITE_OMIT_BUILTIN_TEST |
| 15426 | 15508 | if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; |
| 15427 | 15509 | #endif |
| 15428 | -#if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R | |
| 15510 | +#if HAVE_LOCALTIME_R | |
| 15429 | 15511 | rc = localtime_r(t, pTm)==0; |
| 15430 | 15512 | #else |
| 15431 | 15513 | rc = localtime_s(pTm, t); |
| 15432 | 15514 | #endif /* HAVE_LOCALTIME_R */ |
| 15433 | 15515 | #endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */ |
| @@ -15867,12 +15949,14 @@ | ||
| 15867 | 15949 | DateTime x; |
| 15868 | 15950 | u64 n; |
| 15869 | 15951 | size_t i,j; |
| 15870 | 15952 | char *z; |
| 15871 | 15953 | sqlite3 *db; |
| 15872 | - const char *zFmt = (const char*)sqlite3_value_text(argv[0]); | |
| 15954 | + const char *zFmt; | |
| 15873 | 15955 | char zBuf[100]; |
| 15956 | + if( argc==0 ) return; | |
| 15957 | + zFmt = (const char*)sqlite3_value_text(argv[0]); | |
| 15874 | 15958 | if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; |
| 15875 | 15959 | db = sqlite3_context_db_handle(context); |
| 15876 | 15960 | for(i=0, n=1; zFmt[i]; i++, n++){ |
| 15877 | 15961 | if( zFmt[i]=='%' ){ |
| 15878 | 15962 | switch( zFmt[i+1] ){ |
| @@ -16062,11 +16146,11 @@ | ||
| 16062 | 16146 | UNUSED_PARAMETER(argv); |
| 16063 | 16147 | |
| 16064 | 16148 | iT = sqlite3StmtCurrentTime(context); |
| 16065 | 16149 | if( iT<=0 ) return; |
| 16066 | 16150 | t = iT/1000 - 10000*(sqlite3_int64)21086676; |
| 16067 | -#ifdef HAVE_GMTIME_R | |
| 16151 | +#if HAVE_GMTIME_R | |
| 16068 | 16152 | pTm = gmtime_r(&t, &sNow); |
| 16069 | 16153 | #else |
| 16070 | 16154 | sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
| 16071 | 16155 | pTm = gmtime(&t); |
| 16072 | 16156 | if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); |
| @@ -16736,13 +16820,13 @@ | ||
| 16736 | 16820 | |
| 16737 | 16821 | /* |
| 16738 | 16822 | ** The malloc.h header file is needed for malloc_usable_size() function |
| 16739 | 16823 | ** on some systems (e.g. Linux). |
| 16740 | 16824 | */ |
| 16741 | -#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE) | |
| 16742 | -# define SQLITE_USE_MALLOC_H | |
| 16743 | -# define SQLITE_USE_MALLOC_USABLE_SIZE | |
| 16825 | +#if HAVE_MALLOC_H && HAVE_MALLOC_USABLE_SIZE | |
| 16826 | +# define SQLITE_USE_MALLOC_H 1 | |
| 16827 | +# define SQLITE_USE_MALLOC_USABLE_SIZE 1 | |
| 16744 | 16828 | /* |
| 16745 | 16829 | ** The MSVCRT has malloc_usable_size(), but it is called _msize(). The |
| 16746 | 16830 | ** use of _msize() is automatic, but can be disabled by compiling with |
| 16747 | 16831 | ** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires |
| 16748 | 16832 | ** the malloc.h header file. |
| @@ -19976,10 +20060,16 @@ | ||
| 19976 | 20060 | #endif |
| 19977 | 20061 | } |
| 19978 | 20062 | break; |
| 19979 | 20063 | } |
| 19980 | 20064 | default: { |
| 20065 | +#ifdef SQLITE_ENABLE_API_ARMOR | |
| 20066 | + if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){ | |
| 20067 | + (void)SQLITE_MISUSE_BKPT; | |
| 20068 | + return 0; | |
| 20069 | + } | |
| 20070 | +#endif | |
| 19981 | 20071 | assert( iType-2 >= 0 ); |
| 19982 | 20072 | assert( iType-2 < ArraySize(winMutex_staticMutexes) ); |
| 19983 | 20073 | assert( winMutex_isInit==1 ); |
| 19984 | 20074 | p = &winMutex_staticMutexes[iType-2]; |
| 19985 | 20075 | #ifdef SQLITE_DEBUG |
| @@ -20971,21 +21061,10 @@ | ||
| 20971 | 21061 | ** This file contains code for a set of "printf"-like routines. These |
| 20972 | 21062 | ** routines format strings much like the printf() from the standard C |
| 20973 | 21063 | ** library, though the implementation here has enhancements to support |
| 20974 | 21064 | ** SQLlite. |
| 20975 | 21065 | */ |
| 20976 | - | |
| 20977 | -/* | |
| 20978 | -** If the strchrnul() library function is available, then set | |
| 20979 | -** HAVE_STRCHRNUL. If that routine is not available, this module | |
| 20980 | -** will supply its own. The built-in version is slower than | |
| 20981 | -** the glibc version so the glibc version is definitely preferred. | |
| 20982 | -*/ | |
| 20983 | -#if !defined(HAVE_STRCHRNUL) | |
| 20984 | -# define HAVE_STRCHRNUL 0 | |
| 20985 | -#endif | |
| 20986 | - | |
| 20987 | 21066 | |
| 20988 | 21067 | /* |
| 20989 | 21068 | ** Conversion types fall into various categories as defined by the |
| 20990 | 21069 | ** following enumeration. |
| 20991 | 21070 | */ |
| @@ -22280,10 +22359,12 @@ | ||
| 22280 | 22359 | ** single threaded systems. Nothing in SQLite requires multiple threads. |
| 22281 | 22360 | ** This interface exists so that applications that want to take advantage |
| 22282 | 22361 | ** of multiple cores can do so, while also allowing applications to stay |
| 22283 | 22362 | ** single-threaded if desired. |
| 22284 | 22363 | */ |
| 22364 | +#if SQLITE_OS_WIN | |
| 22365 | +#endif | |
| 22285 | 22366 | |
| 22286 | 22367 | #if SQLITE_MAX_WORKER_THREADS>0 |
| 22287 | 22368 | |
| 22288 | 22369 | /********************************* Unix Pthreads ****************************/ |
| 22289 | 22370 | #if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 |
| @@ -23066,11 +23147,11 @@ | ||
| 23066 | 23147 | ** This file contains functions for allocating memory, comparing |
| 23067 | 23148 | ** strings, and stuff like that. |
| 23068 | 23149 | ** |
| 23069 | 23150 | */ |
| 23070 | 23151 | /* #include <stdarg.h> */ |
| 23071 | -#ifdef SQLITE_HAVE_ISNAN | |
| 23152 | +#if HAVE_ISNAN || SQLITE_HAVE_ISNAN | |
| 23072 | 23153 | # include <math.h> |
| 23073 | 23154 | #endif |
| 23074 | 23155 | |
| 23075 | 23156 | /* |
| 23076 | 23157 | ** Routine needed to support the testcase() macro. |
| @@ -23107,11 +23188,11 @@ | ||
| 23107 | 23188 | ** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. |
| 23108 | 23189 | ** Otherwise, we have our own implementation that works on most systems. |
| 23109 | 23190 | */ |
| 23110 | 23191 | SQLITE_PRIVATE int sqlite3IsNaN(double x){ |
| 23111 | 23192 | int rc; /* The value return */ |
| 23112 | -#if !defined(SQLITE_HAVE_ISNAN) | |
| 23193 | +#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN | |
| 23113 | 23194 | /* |
| 23114 | 23195 | ** Systems that support the isnan() library function should probably |
| 23115 | 23196 | ** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have |
| 23116 | 23197 | ** found that many systems do not have a working isnan() function so |
| 23117 | 23198 | ** this implementation is provided as an alternative. |
| @@ -23137,13 +23218,13 @@ | ||
| 23137 | 23218 | # error SQLite will not work correctly with the -ffast-math option of GCC. |
| 23138 | 23219 | #endif |
| 23139 | 23220 | volatile double y = x; |
| 23140 | 23221 | volatile double z = y; |
| 23141 | 23222 | rc = (y!=z); |
| 23142 | -#else /* if defined(SQLITE_HAVE_ISNAN) */ | |
| 23223 | +#else /* if HAVE_ISNAN */ | |
| 23143 | 23224 | rc = isnan(x); |
| 23144 | -#endif /* SQLITE_HAVE_ISNAN */ | |
| 23225 | +#endif /* HAVE_ISNAN */ | |
| 23145 | 23226 | testcase( rc ); |
| 23146 | 23227 | return rc; |
| 23147 | 23228 | } |
| 23148 | 23229 | #endif /* SQLITE_OMIT_FLOATING_POINT */ |
| 23149 | 23230 | |
| @@ -28460,13 +28541,13 @@ | ||
| 28460 | 28541 | |
| 28461 | 28542 | /* |
| 28462 | 28543 | ** We do not trust systems to provide a working fdatasync(). Some do. |
| 28463 | 28544 | ** Others do no. To be safe, we will stick with the (slightly slower) |
| 28464 | 28545 | ** fsync(). If you know that your system does support fdatasync() correctly, |
| 28465 | -** then simply compile with -Dfdatasync=fdatasync | |
| 28546 | +** then simply compile with -Dfdatasync=fdatasync or -DHAVE_FDATASYNC | |
| 28466 | 28547 | */ |
| 28467 | -#if !defined(fdatasync) | |
| 28548 | +#if !defined(fdatasync) && !HAVE_FDATASYNC | |
| 28468 | 28549 | # define fdatasync fsync |
| 28469 | 28550 | #endif |
| 28470 | 28551 | |
| 28471 | 28552 | /* |
| 28472 | 28553 | ** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not |
| @@ -28783,28 +28864,32 @@ | ||
| 28783 | 28864 | do{ |
| 28784 | 28865 | err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); |
| 28785 | 28866 | }while( err==EINTR ); |
| 28786 | 28867 | if( err ) return SQLITE_IOERR_WRITE; |
| 28787 | 28868 | #else |
| 28788 | - /* If the OS does not have posix_fallocate(), fake it. First use | |
| 28789 | - ** ftruncate() to set the file size, then write a single byte to | |
| 28790 | - ** the last byte in each block within the extended region. This | |
| 28791 | - ** is the same technique used by glibc to implement posix_fallocate() | |
| 28792 | - ** on systems that do not have a real fallocate() system call. | |
| 28869 | + /* If the OS does not have posix_fallocate(), fake it. Write a | |
| 28870 | + ** single byte to the last byte in each block that falls entirely | |
| 28871 | + ** within the extended region. Then, if required, a single byte | |
| 28872 | + ** at offset (nSize-1), to set the size of the file correctly. | |
| 28873 | + ** This is a similar technique to that used by glibc on systems | |
| 28874 | + ** that do not have a real fallocate() call. | |
| 28793 | 28875 | */ |
| 28794 | 28876 | int nBlk = buf.st_blksize; /* File-system block size */ |
| 28877 | + int nWrite = 0; /* Number of bytes written by seekAndWrite */ | |
| 28795 | 28878 | i64 iWrite; /* Next offset to write to */ |
| 28796 | 28879 | |
| 28797 | - if( robust_ftruncate(pFile->h, nSize) ){ | |
| 28798 | - pFile->lastErrno = errno; | |
| 28799 | - return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); | |
| 28800 | - } | |
| 28801 | 28880 | iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; |
| 28802 | - while( iWrite<nSize ){ | |
| 28803 | - int nWrite = seekAndWrite(pFile, iWrite, "", 1); | |
| 28881 | + assert( iWrite>=buf.st_size ); | |
| 28882 | + assert( (iWrite/nBlk)==((buf.st_size+nBlk-1)/nBlk) ); | |
| 28883 | + assert( ((iWrite+1)%nBlk)==0 ); | |
| 28884 | + for(/*no-op*/; iWrite<nSize; iWrite+=nBlk ){ | |
| 28885 | + nWrite = seekAndWrite(pFile, iWrite, "", 1); | |
| 28804 | 28886 | if( nWrite!=1 ) return SQLITE_IOERR_WRITE; |
| 28805 | - iWrite += nBlk; | |
| 28887 | + } | |
| 28888 | + if( nWrite==0 || (nSize%nBlk) ){ | |
| 28889 | + nWrite = seekAndWrite(pFile, nSize-1, "", 1); | |
| 28890 | + if( nWrite!=1 ) return SQLITE_IOERR_WRITE; | |
| 28806 | 28891 | } |
| 28807 | 28892 | #endif |
| 28808 | 28893 | } |
| 28809 | 28894 | } |
| 28810 | 28895 | |
| @@ -34018,12 +34103,12 @@ | ||
| 34018 | 34103 | */ |
| 34019 | 34104 | SQLITE_API int sqlite3_win32_reset_heap(){ |
| 34020 | 34105 | int rc; |
| 34021 | 34106 | MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ |
| 34022 | 34107 | MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */ |
| 34023 | - MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) | |
| 34024 | - MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); ) | |
| 34108 | + MUTEX_LOGIC( pMaster = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); ) | |
| 34109 | + MUTEX_LOGIC( pMem = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM); ) | |
| 34025 | 34110 | sqlite3_mutex_enter(pMaster); |
| 34026 | 34111 | sqlite3_mutex_enter(pMem); |
| 34027 | 34112 | winMemAssertMagic(); |
| 34028 | 34113 | if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){ |
| 34029 | 34114 | /* |
| @@ -35294,11 +35379,11 @@ | ||
| 35294 | 35379 | sqlite3_file *id, /* File to read from */ |
| 35295 | 35380 | void *pBuf, /* Write content into this buffer */ |
| 35296 | 35381 | int amt, /* Number of bytes to read */ |
| 35297 | 35382 | sqlite3_int64 offset /* Begin reading at this offset */ |
| 35298 | 35383 | ){ |
| 35299 | -#if !SQLITE_OS_WINCE | |
| 35384 | +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) | |
| 35300 | 35385 | OVERLAPPED overlapped; /* The offset for ReadFile. */ |
| 35301 | 35386 | #endif |
| 35302 | 35387 | winFile *pFile = (winFile*)id; /* file handle */ |
| 35303 | 35388 | DWORD nRead; /* Number of bytes actually read from file */ |
| 35304 | 35389 | int nRetry = 0; /* Number of retrys */ |
| @@ -35326,11 +35411,11 @@ | ||
| 35326 | 35411 | offset += nCopy; |
| 35327 | 35412 | } |
| 35328 | 35413 | } |
| 35329 | 35414 | #endif |
| 35330 | 35415 | |
| 35331 | -#if SQLITE_OS_WINCE | |
| 35416 | +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) | |
| 35332 | 35417 | if( winSeekFile(pFile, offset) ){ |
| 35333 | 35418 | OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h)); |
| 35334 | 35419 | return SQLITE_FULL; |
| 35335 | 35420 | } |
| 35336 | 35421 | while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ |
| @@ -35398,32 +35483,32 @@ | ||
| 35398 | 35483 | offset += nCopy; |
| 35399 | 35484 | } |
| 35400 | 35485 | } |
| 35401 | 35486 | #endif |
| 35402 | 35487 | |
| 35403 | -#if SQLITE_OS_WINCE | |
| 35488 | +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) | |
| 35404 | 35489 | rc = winSeekFile(pFile, offset); |
| 35405 | 35490 | if( rc==0 ){ |
| 35406 | 35491 | #else |
| 35407 | 35492 | { |
| 35408 | 35493 | #endif |
| 35409 | -#if !SQLITE_OS_WINCE | |
| 35494 | +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) | |
| 35410 | 35495 | OVERLAPPED overlapped; /* The offset for WriteFile. */ |
| 35411 | 35496 | #endif |
| 35412 | 35497 | u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ |
| 35413 | 35498 | int nRem = amt; /* Number of bytes yet to be written */ |
| 35414 | 35499 | DWORD nWrite; /* Bytes written by each WriteFile() call */ |
| 35415 | 35500 | DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ |
| 35416 | 35501 | |
| 35417 | -#if !SQLITE_OS_WINCE | |
| 35502 | +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) | |
| 35418 | 35503 | memset(&overlapped, 0, sizeof(OVERLAPPED)); |
| 35419 | 35504 | overlapped.Offset = (LONG)(offset & 0xffffffff); |
| 35420 | 35505 | overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); |
| 35421 | 35506 | #endif |
| 35422 | 35507 | |
| 35423 | 35508 | while( nRem>0 ){ |
| 35424 | -#if SQLITE_OS_WINCE | |
| 35509 | +#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) | |
| 35425 | 35510 | if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ |
| 35426 | 35511 | #else |
| 35427 | 35512 | if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ |
| 35428 | 35513 | #endif |
| 35429 | 35514 | if( winRetryIoerr(&nRetry, &lastErrno) ) continue; |
| @@ -35432,11 +35517,11 @@ | ||
| 35432 | 35517 | assert( nWrite==0 || nWrite<=(DWORD)nRem ); |
| 35433 | 35518 | if( nWrite==0 || nWrite>(DWORD)nRem ){ |
| 35434 | 35519 | lastErrno = osGetLastError(); |
| 35435 | 35520 | break; |
| 35436 | 35521 | } |
| 35437 | -#if !SQLITE_OS_WINCE | |
| 35522 | +#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) | |
| 35438 | 35523 | offset += nWrite; |
| 35439 | 35524 | overlapped.Offset = (LONG)(offset & 0xffffffff); |
| 35440 | 35525 | overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); |
| 35441 | 35526 | #endif |
| 35442 | 35527 | aRem += nWrite; |
| @@ -38813,22 +38898,10 @@ | ||
| 38813 | 38898 | void *pStress; /* Argument to xStress */ |
| 38814 | 38899 | sqlite3_pcache *pCache; /* Pluggable cache module */ |
| 38815 | 38900 | PgHdr *pPage1; /* Reference to page 1 */ |
| 38816 | 38901 | }; |
| 38817 | 38902 | |
| 38818 | -/* | |
| 38819 | -** Some of the assert() macros in this code are too expensive to run | |
| 38820 | -** even during normal debugging. Use them only rarely on long-running | |
| 38821 | -** tests. Enable the expensive asserts using the | |
| 38822 | -** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option. | |
| 38823 | -*/ | |
| 38824 | -#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT | |
| 38825 | -# define expensive_assert(X) assert(X) | |
| 38826 | -#else | |
| 38827 | -# define expensive_assert(X) | |
| 38828 | -#endif | |
| 38829 | - | |
| 38830 | 38903 | /********************************** Linked List Management ********************/ |
| 38831 | 38904 | |
| 38832 | 38905 | /* Allowed values for second argument to pcacheManageDirtyList() */ |
| 38833 | 38906 | #define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */ |
| 38834 | 38907 | #define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */ |
| @@ -38978,11 +39051,12 @@ | ||
| 38978 | 39051 | SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ |
| 38979 | 39052 | assert( pCache->nRef==0 && pCache->pDirty==0 ); |
| 38980 | 39053 | if( pCache->szPage ){ |
| 38981 | 39054 | sqlite3_pcache *pNew; |
| 38982 | 39055 | pNew = sqlite3GlobalConfig.pcache2.xCreate( |
| 38983 | - szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable | |
| 39056 | + szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)), | |
| 39057 | + pCache->bPurgeable | |
| 38984 | 39058 | ); |
| 38985 | 39059 | if( pNew==0 ) return SQLITE_NOMEM; |
| 38986 | 39060 | sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache)); |
| 38987 | 39061 | if( pCache->pCache ){ |
| 38988 | 39062 | sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); |
| @@ -39437,11 +39511,11 @@ | ||
| 39437 | 39511 | |
| 39438 | 39512 | /* |
| 39439 | 39513 | ** Return the size of the header added by this middleware layer |
| 39440 | 39514 | ** in the page-cache hierarchy. |
| 39441 | 39515 | */ |
| 39442 | -SQLITE_PRIVATE int sqlite3HeaderSizePcache(void){ return sizeof(PgHdr); } | |
| 39516 | +SQLITE_PRIVATE int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); } | |
| 39443 | 39517 | |
| 39444 | 39518 | |
| 39445 | 39519 | #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) |
| 39446 | 39520 | /* |
| 39447 | 39521 | ** For all dirty pages currently in the cache, invoke the specified |
| @@ -39753,11 +39827,11 @@ | ||
| 39753 | 39827 | pcache1Free(pPg); |
| 39754 | 39828 | sqlite3_free(p); |
| 39755 | 39829 | pPg = 0; |
| 39756 | 39830 | } |
| 39757 | 39831 | #else |
| 39758 | - pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra); | |
| 39832 | + pPg = pcache1Alloc(ROUND8(sizeof(PgHdr1)) + pCache->szPage + pCache->szExtra); | |
| 39759 | 39833 | p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; |
| 39760 | 39834 | #endif |
| 39761 | 39835 | pcache1EnterMutex(pCache->pGroup); |
| 39762 | 39836 | |
| 39763 | 39837 | if( pPg ){ |
| @@ -40441,11 +40515,11 @@ | ||
| 40441 | 40515 | } |
| 40442 | 40516 | |
| 40443 | 40517 | /* |
| 40444 | 40518 | ** Return the size of the header on each page of this PCACHE implementation. |
| 40445 | 40519 | */ |
| 40446 | -SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void){ return sizeof(PgHdr1); } | |
| 40520 | +SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); } | |
| 40447 | 40521 | |
| 40448 | 40522 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 40449 | 40523 | /* |
| 40450 | 40524 | ** This function is called to free superfluous dynamically allocated memory |
| 40451 | 40525 | ** held by the pager system. Memory in use by any SQLite pager allocated |
| @@ -41799,10 +41873,12 @@ | ||
| 41799 | 41873 | u8 eLock; /* Current lock held on database file */ |
| 41800 | 41874 | u8 changeCountDone; /* Set after incrementing the change-counter */ |
| 41801 | 41875 | u8 setMaster; /* True if a m-j name has been written to jrnl */ |
| 41802 | 41876 | u8 doNotSpill; /* Do not spill the cache when non-zero */ |
| 41803 | 41877 | u8 subjInMemory; /* True to use in-memory sub-journals */ |
| 41878 | + u8 bUseFetch; /* True to use xFetch() */ | |
| 41879 | + u8 hasBeenUsed; /* True if any content previously read from this pager*/ | |
| 41804 | 41880 | Pgno dbSize; /* Number of pages in the database */ |
| 41805 | 41881 | Pgno dbOrigSize; /* dbSize before the current transaction */ |
| 41806 | 41882 | Pgno dbFileSize; /* Number of pages in the database file */ |
| 41807 | 41883 | Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ |
| 41808 | 41884 | int errCode; /* One of several kinds of errors */ |
| @@ -41816,13 +41892,13 @@ | ||
| 41816 | 41892 | i64 journalOff; /* Current write offset in the journal file */ |
| 41817 | 41893 | i64 journalHdr; /* Byte offset to previous journal header */ |
| 41818 | 41894 | sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ |
| 41819 | 41895 | PagerSavepoint *aSavepoint; /* Array of active savepoints */ |
| 41820 | 41896 | int nSavepoint; /* Number of elements in aSavepoint[] */ |
| 41897 | + u32 iDataVersion; /* Changes whenever database content changes */ | |
| 41821 | 41898 | char dbFileVers[16]; /* Changes whenever database file changes */ |
| 41822 | 41899 | |
| 41823 | - u8 bUseFetch; /* True to use xFetch() */ | |
| 41824 | 41900 | int nMmapOut; /* Number of mmap pages currently outstanding */ |
| 41825 | 41901 | sqlite3_int64 szMmap; /* Desired maximum mmap size */ |
| 41826 | 41902 | PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */ |
| 41827 | 41903 | /* |
| 41828 | 41904 | ** End of the routinely-changing class members |
| @@ -42834,13 +42910,22 @@ | ||
| 42834 | 42910 | |
| 42835 | 42911 | /* |
| 42836 | 42912 | ** Discard the entire contents of the in-memory page-cache. |
| 42837 | 42913 | */ |
| 42838 | 42914 | static void pager_reset(Pager *pPager){ |
| 42915 | + pPager->iDataVersion++; | |
| 42839 | 42916 | sqlite3BackupRestart(pPager->pBackup); |
| 42840 | 42917 | sqlite3PcacheClear(pPager->pPCache); |
| 42841 | 42918 | } |
| 42919 | + | |
| 42920 | +/* | |
| 42921 | +** Return the pPager->iDataVersion value | |
| 42922 | +*/ | |
| 42923 | +SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager *pPager){ | |
| 42924 | + assert( pPager->eState>PAGER_OPEN ); | |
| 42925 | + return pPager->iDataVersion; | |
| 42926 | +} | |
| 42842 | 42927 | |
| 42843 | 42928 | /* |
| 42844 | 42929 | ** Free all structures in the Pager.aSavepoint[] array and set both |
| 42845 | 42930 | ** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal |
| 42846 | 42931 | ** if it is open and the pager is not in exclusive mode. |
| @@ -45040,11 +45125,11 @@ | ||
| 45040 | 45125 | Pgno pgno, /* Page number */ |
| 45041 | 45126 | void *pData, /* xFetch()'d data for this page */ |
| 45042 | 45127 | PgHdr **ppPage /* OUT: Acquired page object */ |
| 45043 | 45128 | ){ |
| 45044 | 45129 | PgHdr *p; /* Memory mapped page to return */ |
| 45045 | - | |
| 45130 | + | |
| 45046 | 45131 | if( pPager->pMmapFreelist ){ |
| 45047 | 45132 | *ppPage = p = pPager->pMmapFreelist; |
| 45048 | 45133 | pPager->pMmapFreelist = p->pDirty; |
| 45049 | 45134 | p->pDirty = 0; |
| 45050 | 45135 | memset(p->pExtra, 0, pPager->nExtra); |
| @@ -46271,20 +46356,16 @@ | ||
| 46271 | 46356 | assert( (pPager->eLock==SHARED_LOCK) |
| 46272 | 46357 | || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK) |
| 46273 | 46358 | ); |
| 46274 | 46359 | } |
| 46275 | 46360 | |
| 46276 | - if( !pPager->tempFile && ( | |
| 46277 | - pPager->pBackup | |
| 46278 | - || sqlite3PcachePagecount(pPager->pPCache)>0 | |
| 46279 | - || USEFETCH(pPager) | |
| 46280 | - )){ | |
| 46281 | - /* The shared-lock has just been acquired on the database file | |
| 46282 | - ** and there are already pages in the cache (from a previous | |
| 46283 | - ** read or write transaction). Check to see if the database | |
| 46284 | - ** has been modified. If the database has changed, flush the | |
| 46285 | - ** cache. | |
| 46361 | + if( !pPager->tempFile && pPager->hasBeenUsed ){ | |
| 46362 | + /* The shared-lock has just been acquired then check to | |
| 46363 | + ** see if the database has been modified. If the database has changed, | |
| 46364 | + ** flush the cache. The pPager->hasBeenUsed flag prevents this from | |
| 46365 | + ** occurring on the very first access to a file, in order to save a | |
| 46366 | + ** single unnecessary sqlite3OsRead() call at the start-up. | |
| 46286 | 46367 | ** |
| 46287 | 46368 | ** Database changes is detected by looking at 15 bytes beginning |
| 46288 | 46369 | ** at offset 24 into the file. The first 4 of these 16 bytes are |
| 46289 | 46370 | ** a 32-bit counter that is incremented with each change. The |
| 46290 | 46371 | ** other bytes change randomly with each file change when |
| @@ -46445,10 +46526,11 @@ | ||
| 46445 | 46526 | assert( noContent==0 || bMmapOk==0 ); |
| 46446 | 46527 | |
| 46447 | 46528 | if( pgno==0 ){ |
| 46448 | 46529 | return SQLITE_CORRUPT_BKPT; |
| 46449 | 46530 | } |
| 46531 | + pPager->hasBeenUsed = 1; | |
| 46450 | 46532 | |
| 46451 | 46533 | /* If the pager is in the error state, return an error immediately. |
| 46452 | 46534 | ** Otherwise, request the page from the PCache layer. */ |
| 46453 | 46535 | if( pPager->errCode!=SQLITE_OK ){ |
| 46454 | 46536 | rc = pPager->errCode; |
| @@ -46594,10 +46676,11 @@ | ||
| 46594 | 46676 | sqlite3_pcache_page *pPage; |
| 46595 | 46677 | assert( pPager!=0 ); |
| 46596 | 46678 | assert( pgno!=0 ); |
| 46597 | 46679 | assert( pPager->pPCache!=0 ); |
| 46598 | 46680 | pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0); |
| 46681 | + assert( pPage==0 || pPager->hasBeenUsed ); | |
| 46599 | 46682 | return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); |
| 46600 | 46683 | } |
| 46601 | 46684 | |
| 46602 | 46685 | /* |
| 46603 | 46686 | ** Release a page reference. |
| @@ -47460,10 +47543,11 @@ | ||
| 47460 | 47543 | pPager->eState = PAGER_READER; |
| 47461 | 47544 | return SQLITE_OK; |
| 47462 | 47545 | } |
| 47463 | 47546 | |
| 47464 | 47547 | PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); |
| 47548 | + pPager->iDataVersion++; | |
| 47465 | 47549 | rc = pager_end_transaction(pPager, pPager->setMaster, 1); |
| 47466 | 47550 | return pager_error(pPager, rc); |
| 47467 | 47551 | } |
| 47468 | 47552 | |
| 47469 | 47553 | /* |
| @@ -50111,11 +50195,11 @@ | ||
| 50111 | 50195 | int (*xBusy)(void*), /* Function to call when busy */ |
| 50112 | 50196 | void *pBusyArg, /* Context argument for xBusyHandler */ |
| 50113 | 50197 | int sync_flags, /* Flags for OsSync() (or 0) */ |
| 50114 | 50198 | u8 *zBuf /* Temporary buffer to use */ |
| 50115 | 50199 | ){ |
| 50116 | - int rc; /* Return code */ | |
| 50200 | + int rc = SQLITE_OK; /* Return code */ | |
| 50117 | 50201 | int szPage; /* Database page-size */ |
| 50118 | 50202 | WalIterator *pIter = 0; /* Wal iterator context */ |
| 50119 | 50203 | u32 iDbpage = 0; /* Next database page to write */ |
| 50120 | 50204 | u32 iFrame = 0; /* Wal frame containing data for iDbpage */ |
| 50121 | 50205 | u32 mxSafeFrame; /* Max frame that can be backfilled */ |
| @@ -50125,108 +50209,111 @@ | ||
| 50125 | 50209 | |
| 50126 | 50210 | szPage = walPagesize(pWal); |
| 50127 | 50211 | testcase( szPage<=32768 ); |
| 50128 | 50212 | testcase( szPage>=65536 ); |
| 50129 | 50213 | pInfo = walCkptInfo(pWal); |
| 50130 | - if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK; | |
| 50131 | - | |
| 50132 | - /* Allocate the iterator */ | |
| 50133 | - rc = walIteratorInit(pWal, &pIter); | |
| 50134 | - if( rc!=SQLITE_OK ){ | |
| 50135 | - return rc; | |
| 50136 | - } | |
| 50137 | - assert( pIter ); | |
| 50138 | - | |
| 50139 | - /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked | |
| 50140 | - ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ | |
| 50141 | - assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); | |
| 50142 | - | |
| 50143 | - /* Compute in mxSafeFrame the index of the last frame of the WAL that is | |
| 50144 | - ** safe to write into the database. Frames beyond mxSafeFrame might | |
| 50145 | - ** overwrite database pages that are in use by active readers and thus | |
| 50146 | - ** cannot be backfilled from the WAL. | |
| 50147 | - */ | |
| 50148 | - mxSafeFrame = pWal->hdr.mxFrame; | |
| 50149 | - mxPage = pWal->hdr.nPage; | |
| 50150 | - for(i=1; i<WAL_NREADER; i++){ | |
| 50151 | - u32 y = pInfo->aReadMark[i]; | |
| 50152 | - if( mxSafeFrame>y ){ | |
| 50153 | - assert( y<=pWal->hdr.mxFrame ); | |
| 50154 | - rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); | |
| 50155 | - if( rc==SQLITE_OK ){ | |
| 50156 | - pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); | |
| 50157 | - walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); | |
| 50158 | - }else if( rc==SQLITE_BUSY ){ | |
| 50159 | - mxSafeFrame = y; | |
| 50160 | - xBusy = 0; | |
| 50161 | - }else{ | |
| 50162 | - goto walcheckpoint_out; | |
| 50163 | - } | |
| 50164 | - } | |
| 50165 | - } | |
| 50166 | - | |
| 50167 | - if( pInfo->nBackfill<mxSafeFrame | |
| 50168 | - && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK | |
| 50169 | - ){ | |
| 50170 | - i64 nSize; /* Current size of database file */ | |
| 50171 | - u32 nBackfill = pInfo->nBackfill; | |
| 50172 | - | |
| 50173 | - /* Sync the WAL to disk */ | |
| 50174 | - if( sync_flags ){ | |
| 50175 | - rc = sqlite3OsSync(pWal->pWalFd, sync_flags); | |
| 50176 | - } | |
| 50177 | - | |
| 50178 | - /* If the database may grow as a result of this checkpoint, hint | |
| 50179 | - ** about the eventual size of the db file to the VFS layer. | |
| 50180 | - */ | |
| 50181 | - if( rc==SQLITE_OK ){ | |
| 50182 | - i64 nReq = ((i64)mxPage * szPage); | |
| 50183 | - rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); | |
| 50184 | - if( rc==SQLITE_OK && nSize<nReq ){ | |
| 50185 | - sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); | |
| 50186 | - } | |
| 50187 | - } | |
| 50188 | - | |
| 50189 | - | |
| 50190 | - /* Iterate through the contents of the WAL, copying data to the db file. */ | |
| 50191 | - while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ | |
| 50192 | - i64 iOffset; | |
| 50193 | - assert( walFramePgno(pWal, iFrame)==iDbpage ); | |
| 50194 | - if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue; | |
| 50195 | - iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; | |
| 50196 | - /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ | |
| 50197 | - rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); | |
| 50198 | - if( rc!=SQLITE_OK ) break; | |
| 50199 | - iOffset = (iDbpage-1)*(i64)szPage; | |
| 50200 | - testcase( IS_BIG_INT(iOffset) ); | |
| 50201 | - rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); | |
| 50202 | - if( rc!=SQLITE_OK ) break; | |
| 50203 | - } | |
| 50204 | - | |
| 50205 | - /* If work was actually accomplished... */ | |
| 50206 | - if( rc==SQLITE_OK ){ | |
| 50207 | - if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ | |
| 50208 | - i64 szDb = pWal->hdr.nPage*(i64)szPage; | |
| 50209 | - testcase( IS_BIG_INT(szDb) ); | |
| 50210 | - rc = sqlite3OsTruncate(pWal->pDbFd, szDb); | |
| 50211 | - if( rc==SQLITE_OK && sync_flags ){ | |
| 50212 | - rc = sqlite3OsSync(pWal->pDbFd, sync_flags); | |
| 50213 | - } | |
| 50214 | - } | |
| 50215 | - if( rc==SQLITE_OK ){ | |
| 50216 | - pInfo->nBackfill = mxSafeFrame; | |
| 50217 | - } | |
| 50218 | - } | |
| 50219 | - | |
| 50220 | - /* Release the reader lock held while backfilling */ | |
| 50221 | - walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); | |
| 50222 | - } | |
| 50223 | - | |
| 50224 | - if( rc==SQLITE_BUSY ){ | |
| 50225 | - /* Reset the return code so as not to report a checkpoint failure | |
| 50226 | - ** just because there are active readers. */ | |
| 50227 | - rc = SQLITE_OK; | |
| 50214 | + if( pInfo->nBackfill<pWal->hdr.mxFrame ){ | |
| 50215 | + | |
| 50216 | + /* Allocate the iterator */ | |
| 50217 | + rc = walIteratorInit(pWal, &pIter); | |
| 50218 | + if( rc!=SQLITE_OK ){ | |
| 50219 | + return rc; | |
| 50220 | + } | |
| 50221 | + assert( pIter ); | |
| 50222 | + | |
| 50223 | + /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked | |
| 50224 | + ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ | |
| 50225 | + assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); | |
| 50226 | + | |
| 50227 | + /* Compute in mxSafeFrame the index of the last frame of the WAL that is | |
| 50228 | + ** safe to write into the database. Frames beyond mxSafeFrame might | |
| 50229 | + ** overwrite database pages that are in use by active readers and thus | |
| 50230 | + ** cannot be backfilled from the WAL. | |
| 50231 | + */ | |
| 50232 | + mxSafeFrame = pWal->hdr.mxFrame; | |
| 50233 | + mxPage = pWal->hdr.nPage; | |
| 50234 | + for(i=1; i<WAL_NREADER; i++){ | |
| 50235 | + u32 y = pInfo->aReadMark[i]; | |
| 50236 | + if( mxSafeFrame>y ){ | |
| 50237 | + assert( y<=pWal->hdr.mxFrame ); | |
| 50238 | + rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); | |
| 50239 | + if( rc==SQLITE_OK ){ | |
| 50240 | + pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); | |
| 50241 | + walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); | |
| 50242 | + }else if( rc==SQLITE_BUSY ){ | |
| 50243 | + mxSafeFrame = y; | |
| 50244 | + xBusy = 0; | |
| 50245 | + }else{ | |
| 50246 | + goto walcheckpoint_out; | |
| 50247 | + } | |
| 50248 | + } | |
| 50249 | + } | |
| 50250 | + | |
| 50251 | + if( pInfo->nBackfill<mxSafeFrame | |
| 50252 | + && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK | |
| 50253 | + ){ | |
| 50254 | + i64 nSize; /* Current size of database file */ | |
| 50255 | + u32 nBackfill = pInfo->nBackfill; | |
| 50256 | + | |
| 50257 | + /* Sync the WAL to disk */ | |
| 50258 | + if( sync_flags ){ | |
| 50259 | + rc = sqlite3OsSync(pWal->pWalFd, sync_flags); | |
| 50260 | + } | |
| 50261 | + | |
| 50262 | + /* If the database may grow as a result of this checkpoint, hint | |
| 50263 | + ** about the eventual size of the db file to the VFS layer. | |
| 50264 | + */ | |
| 50265 | + if( rc==SQLITE_OK ){ | |
| 50266 | + i64 nReq = ((i64)mxPage * szPage); | |
| 50267 | + rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); | |
| 50268 | + if( rc==SQLITE_OK && nSize<nReq ){ | |
| 50269 | + sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); | |
| 50270 | + } | |
| 50271 | + } | |
| 50272 | + | |
| 50273 | + | |
| 50274 | + /* Iterate through the contents of the WAL, copying data to the db file */ | |
| 50275 | + while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ | |
| 50276 | + i64 iOffset; | |
| 50277 | + assert( walFramePgno(pWal, iFrame)==iDbpage ); | |
| 50278 | + if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){ | |
| 50279 | + continue; | |
| 50280 | + } | |
| 50281 | + iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; | |
| 50282 | + /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ | |
| 50283 | + rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); | |
| 50284 | + if( rc!=SQLITE_OK ) break; | |
| 50285 | + iOffset = (iDbpage-1)*(i64)szPage; | |
| 50286 | + testcase( IS_BIG_INT(iOffset) ); | |
| 50287 | + rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); | |
| 50288 | + if( rc!=SQLITE_OK ) break; | |
| 50289 | + } | |
| 50290 | + | |
| 50291 | + /* If work was actually accomplished... */ | |
| 50292 | + if( rc==SQLITE_OK ){ | |
| 50293 | + if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ | |
| 50294 | + i64 szDb = pWal->hdr.nPage*(i64)szPage; | |
| 50295 | + testcase( IS_BIG_INT(szDb) ); | |
| 50296 | + rc = sqlite3OsTruncate(pWal->pDbFd, szDb); | |
| 50297 | + if( rc==SQLITE_OK && sync_flags ){ | |
| 50298 | + rc = sqlite3OsSync(pWal->pDbFd, sync_flags); | |
| 50299 | + } | |
| 50300 | + } | |
| 50301 | + if( rc==SQLITE_OK ){ | |
| 50302 | + pInfo->nBackfill = mxSafeFrame; | |
| 50303 | + } | |
| 50304 | + } | |
| 50305 | + | |
| 50306 | + /* Release the reader lock held while backfilling */ | |
| 50307 | + walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); | |
| 50308 | + } | |
| 50309 | + | |
| 50310 | + if( rc==SQLITE_BUSY ){ | |
| 50311 | + /* Reset the return code so as not to report a checkpoint failure | |
| 50312 | + ** just because there are active readers. */ | |
| 50313 | + rc = SQLITE_OK; | |
| 50314 | + } | |
| 50228 | 50315 | } |
| 50229 | 50316 | |
| 50230 | 50317 | /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the |
| 50231 | 50318 | ** entire wal file has been copied into the database file, then block |
| 50232 | 50319 | ** until all readers have finished using the wal file. This ensures that |
| @@ -50237,11 +50324,11 @@ | ||
| 50237 | 50324 | if( pInfo->nBackfill<pWal->hdr.mxFrame ){ |
| 50238 | 50325 | rc = SQLITE_BUSY; |
| 50239 | 50326 | }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ |
| 50240 | 50327 | u32 salt1; |
| 50241 | 50328 | sqlite3_randomness(4, &salt1); |
| 50242 | - assert( mxSafeFrame==pWal->hdr.mxFrame ); | |
| 50329 | + assert( pInfo->nBackfill==pWal->hdr.mxFrame ); | |
| 50243 | 50330 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); |
| 50244 | 50331 | if( rc==SQLITE_OK ){ |
| 50245 | 50332 | if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ |
| 50246 | 50333 | /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as |
| 50247 | 50334 | ** SQLITE_CHECKPOINT_RESTART with the addition that it also |
| @@ -50829,11 +50916,11 @@ | ||
| 50829 | 50916 | } |
| 50830 | 50917 | nCollide = HASHTABLE_NSLOT; |
| 50831 | 50918 | for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){ |
| 50832 | 50919 | u32 iFrame = aHash[iKey] + iZero; |
| 50833 | 50920 | if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){ |
| 50834 | - /* assert( iFrame>iRead ); -- not true if there is corruption */ | |
| 50921 | + assert( iFrame>iRead || CORRUPT_DB ); | |
| 50835 | 50922 | iRead = iFrame; |
| 50836 | 50923 | } |
| 50837 | 50924 | if( (nCollide--)==0 ){ |
| 50838 | 50925 | return SQLITE_CORRUPT_BKPT; |
| 50839 | 50926 | } |
| @@ -51935,10 +52022,11 @@ | ||
| 51935 | 52022 | u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ |
| 51936 | 52023 | u8 sharable; /* True if we can share pBt with another db */ |
| 51937 | 52024 | u8 locked; /* True if db currently has pBt locked */ |
| 51938 | 52025 | int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ |
| 51939 | 52026 | int nBackup; /* Number of backup operations reading this btree */ |
| 52027 | + u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */ | |
| 51940 | 52028 | Btree *pNext; /* List of other sharable Btrees from the same db */ |
| 51941 | 52029 | Btree *pPrev; /* Back pointer of the same list */ |
| 51942 | 52030 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 51943 | 52031 | BtLock lock; /* Object used to lock page 1 */ |
| 51944 | 52032 | #endif |
| @@ -56098,10 +56186,11 @@ | ||
| 56098 | 56186 | rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); |
| 56099 | 56187 | if( rc!=SQLITE_OK && bCleanup==0 ){ |
| 56100 | 56188 | sqlite3BtreeLeave(p); |
| 56101 | 56189 | return rc; |
| 56102 | 56190 | } |
| 56191 | + p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */ | |
| 56103 | 56192 | pBt->inTransaction = TRANS_READ; |
| 56104 | 56193 | btreeClearHasContent(pBt); |
| 56105 | 56194 | } |
| 56106 | 56195 | |
| 56107 | 56196 | btreeEndTransaction(p); |
| @@ -56461,11 +56550,11 @@ | ||
| 56461 | 56550 | } |
| 56462 | 56551 | for(i=0; i<=pCur->iPage; i++){ |
| 56463 | 56552 | releasePage(pCur->apPage[i]); |
| 56464 | 56553 | } |
| 56465 | 56554 | unlockBtreeIfUnused(pBt); |
| 56466 | - sqlite3DbFree(pBtree->db, pCur->aOverflow); | |
| 56555 | + sqlite3_free(pCur->aOverflow); | |
| 56467 | 56556 | /* sqlite3_free(pCur); */ |
| 56468 | 56557 | sqlite3BtreeLeave(pBtree); |
| 56469 | 56558 | } |
| 56470 | 56559 | return SQLITE_OK; |
| 56471 | 56560 | } |
| @@ -56755,10 +56844,11 @@ | ||
| 56755 | 56844 | pBuf += a; |
| 56756 | 56845 | amt -= a; |
| 56757 | 56846 | }else{ |
| 56758 | 56847 | offset -= pCur->info.nLocal; |
| 56759 | 56848 | } |
| 56849 | + | |
| 56760 | 56850 | |
| 56761 | 56851 | if( rc==SQLITE_OK && amt>0 ){ |
| 56762 | 56852 | const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */ |
| 56763 | 56853 | Pgno nextPage; |
| 56764 | 56854 | |
| @@ -56773,12 +56863,12 @@ | ||
| 56773 | 56863 | ** means "not yet known" (the cache is lazily populated). |
| 56774 | 56864 | */ |
| 56775 | 56865 | if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){ |
| 56776 | 56866 | int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; |
| 56777 | 56867 | if( nOvfl>pCur->nOvflAlloc ){ |
| 56778 | - Pgno *aNew = (Pgno*)sqlite3DbRealloc( | |
| 56779 | - pCur->pBtree->db, pCur->aOverflow, nOvfl*2*sizeof(Pgno) | |
| 56868 | + Pgno *aNew = (Pgno*)sqlite3Realloc( | |
| 56869 | + pCur->aOverflow, nOvfl*2*sizeof(Pgno) | |
| 56780 | 56870 | ); |
| 56781 | 56871 | if( aNew==0 ){ |
| 56782 | 56872 | rc = SQLITE_NOMEM; |
| 56783 | 56873 | }else{ |
| 56784 | 56874 | pCur->nOvflAlloc = nOvfl*2; |
| @@ -56821,10 +56911,11 @@ | ||
| 56821 | 56911 | ** Note that the aOverflow[] array must be allocated because eOp!=2 |
| 56822 | 56912 | ** here. If eOp==2, then offset==0 and this branch is never taken. |
| 56823 | 56913 | */ |
| 56824 | 56914 | assert( eOp!=2 ); |
| 56825 | 56915 | assert( pCur->curFlags & BTCF_ValidOvfl ); |
| 56916 | + assert( pCur->pBtree->db==pBt->db ); | |
| 56826 | 56917 | if( pCur->aOverflow[iIdx+1] ){ |
| 56827 | 56918 | nextPage = pCur->aOverflow[iIdx+1]; |
| 56828 | 56919 | }else{ |
| 56829 | 56920 | rc = getOverflowPage(pBt, nextPage, 0, &nextPage); |
| 56830 | 56921 | } |
| @@ -59410,12 +59501,12 @@ | ||
| 59410 | 59501 | assert( leafCorrection==4 ); |
| 59411 | 59502 | if( szCell[nCell]<4 ){ |
| 59412 | 59503 | /* Do not allow any cells smaller than 4 bytes. If a smaller cell |
| 59413 | 59504 | ** does exist, pad it with 0x00 bytes. */ |
| 59414 | 59505 | assert( szCell[nCell]==3 ); |
| 59415 | - assert( apCell[nCell]==&pTemp[iSpace1-3] ); | |
| 59416 | - pTemp[iSpace1++] = 0x00; | |
| 59506 | + assert( apCell[nCell]==&aSpace1[iSpace1-3] ); | |
| 59507 | + aSpace1[iSpace1++] = 0x00; | |
| 59417 | 59508 | szCell[nCell] = 4; |
| 59418 | 59509 | } |
| 59419 | 59510 | } |
| 59420 | 59511 | nCell++; |
| 59421 | 59512 | } |
| @@ -60723,10 +60814,17 @@ | ||
| 60723 | 60814 | ** is read-only, the others are read/write. |
| 60724 | 60815 | ** |
| 60725 | 60816 | ** The schema layer numbers meta values differently. At the schema |
| 60726 | 60817 | ** layer (and the SetCookie and ReadCookie opcodes) the number of |
| 60727 | 60818 | ** free pages is not visible. So Cookie[0] is the same as Meta[1]. |
| 60819 | +** | |
| 60820 | +** This routine treats Meta[BTREE_DATA_VERSION] as a special case. Instead | |
| 60821 | +** of reading the value out of the header, it instead loads the "DataVersion" | |
| 60822 | +** from the pager. The BTREE_DATA_VERSION value is not actually stored in the | |
| 60823 | +** database file. It is a number computed by the pager. But its access | |
| 60824 | +** pattern is the same as header meta values, and so it is convenient to | |
| 60825 | +** read it from this routine. | |
| 60728 | 60826 | */ |
| 60729 | 60827 | SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ |
| 60730 | 60828 | BtShared *pBt = p->pBt; |
| 60731 | 60829 | |
| 60732 | 60830 | sqlite3BtreeEnter(p); |
| @@ -60733,11 +60831,15 @@ | ||
| 60733 | 60831 | assert( p->inTrans>TRANS_NONE ); |
| 60734 | 60832 | assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) ); |
| 60735 | 60833 | assert( pBt->pPage1 ); |
| 60736 | 60834 | assert( idx>=0 && idx<=15 ); |
| 60737 | 60835 | |
| 60738 | - *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); | |
| 60836 | + if( idx==BTREE_DATA_VERSION ){ | |
| 60837 | + *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion; | |
| 60838 | + }else{ | |
| 60839 | + *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); | |
| 60840 | + } | |
| 60739 | 60841 | |
| 60740 | 60842 | /* If auto-vacuum is disabled in this build and this is an auto-vacuum |
| 60741 | 60843 | ** database, mark the database as read-only. */ |
| 60742 | 60844 | #ifdef SQLITE_OMIT_AUTOVACUUM |
| 60743 | 60845 | if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){ |
| @@ -60824,11 +60926,11 @@ | ||
| 60824 | 60926 | if( pPage->leaf ){ |
| 60825 | 60927 | do { |
| 60826 | 60928 | if( pCur->iPage==0 ){ |
| 60827 | 60929 | /* All pages of the b-tree have been visited. Return successfully. */ |
| 60828 | 60930 | *pnEntry = nEntry; |
| 60829 | - return SQLITE_OK; | |
| 60931 | + return moveToRoot(pCur); | |
| 60830 | 60932 | } |
| 60831 | 60933 | moveToParent(pCur); |
| 60832 | 60934 | }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); |
| 60833 | 60935 | |
| 60834 | 60936 | pCur->aiIdx[pCur->iPage]++; |
| @@ -61680,11 +61782,11 @@ | ||
| 61680 | 61782 | } |
| 61681 | 61783 | |
| 61682 | 61784 | /* |
| 61683 | 61785 | ** Return the size of the header added to each page by this module. |
| 61684 | 61786 | */ |
| 61685 | -SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return sizeof(MemPage); } | |
| 61787 | +SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } | |
| 61686 | 61788 | |
| 61687 | 61789 | /************** End of btree.c ***********************************************/ |
| 61688 | 61790 | /************** Begin file backup.c ******************************************/ |
| 61689 | 61791 | /* |
| 61690 | 61792 | ** 2009 January 28 |
| @@ -64444,36 +64546,39 @@ | ||
| 64444 | 64546 | ** |
| 64445 | 64547 | ** assert( sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) ); |
| 64446 | 64548 | */ |
| 64447 | 64549 | SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ |
| 64448 | 64550 | int hasAbort = 0; |
| 64551 | + int hasFkCounter = 0; | |
| 64449 | 64552 | Op *pOp; |
| 64450 | 64553 | VdbeOpIter sIter; |
| 64451 | 64554 | memset(&sIter, 0, sizeof(sIter)); |
| 64452 | 64555 | sIter.v = v; |
| 64453 | 64556 | |
| 64454 | 64557 | while( (pOp = opIterNext(&sIter))!=0 ){ |
| 64455 | 64558 | int opcode = pOp->opcode; |
| 64456 | 64559 | if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename |
| 64457 | -#ifndef SQLITE_OMIT_FOREIGN_KEY | |
| 64458 | - || (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1) | |
| 64459 | -#endif | |
| 64460 | 64560 | || ((opcode==OP_Halt || opcode==OP_HaltIfNull) |
| 64461 | 64561 | && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) |
| 64462 | 64562 | ){ |
| 64463 | 64563 | hasAbort = 1; |
| 64464 | 64564 | break; |
| 64465 | 64565 | } |
| 64566 | +#ifndef SQLITE_OMIT_FOREIGN_KEY | |
| 64567 | + if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){ | |
| 64568 | + hasFkCounter = 1; | |
| 64569 | + } | |
| 64570 | +#endif | |
| 64466 | 64571 | } |
| 64467 | 64572 | sqlite3DbFree(v->db, sIter.apSub); |
| 64468 | 64573 | |
| 64469 | 64574 | /* Return true if hasAbort==mayAbort. Or if a malloc failure occurred. |
| 64470 | 64575 | ** If malloc failed, then the while() loop above may not have iterated |
| 64471 | 64576 | ** through all opcodes and hasAbort may be set incorrectly. Return |
| 64472 | 64577 | ** true for this case to prevent the assert() in the callers frame |
| 64473 | 64578 | ** from failing. */ |
| 64474 | - return ( v->db->mallocFailed || hasAbort==mayAbort ); | |
| 64579 | + return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter ); | |
| 64475 | 64580 | } |
| 64476 | 64581 | #endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */ |
| 64477 | 64582 | |
| 64478 | 64583 | /* |
| 64479 | 64584 | ** Loop through the program looking for P2 values that are negative |
| @@ -67394,10 +67499,45 @@ | ||
| 67394 | 67499 | if( pKeyInfo->db->mallocFailed ) return 1; |
| 67395 | 67500 | return 0; |
| 67396 | 67501 | } |
| 67397 | 67502 | #endif |
| 67398 | 67503 | |
| 67504 | +#if SQLITE_DEBUG | |
| 67505 | +/* | |
| 67506 | +** Count the number of fields (a.k.a. columns) in the record given by | |
| 67507 | +** pKey,nKey. The verify that this count is less than or equal to the | |
| 67508 | +** limit given by pKeyInfo->nField + pKeyInfo->nXField. | |
| 67509 | +** | |
| 67510 | +** If this constraint is not satisfied, it means that the high-speed | |
| 67511 | +** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will | |
| 67512 | +** not work correctly. If this assert() ever fires, it probably means | |
| 67513 | +** that the KeyInfo.nField or KeyInfo.nXField values were computed | |
| 67514 | +** incorrectly. | |
| 67515 | +*/ | |
| 67516 | +static void vdbeAssertFieldCountWithinLimits( | |
| 67517 | + int nKey, const void *pKey, /* The record to verify */ | |
| 67518 | + const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */ | |
| 67519 | +){ | |
| 67520 | + int nField = 0; | |
| 67521 | + u32 szHdr; | |
| 67522 | + u32 idx; | |
| 67523 | + u32 notUsed; | |
| 67524 | + const unsigned char *aKey = (const unsigned char*)pKey; | |
| 67525 | + | |
| 67526 | + if( CORRUPT_DB ) return; | |
| 67527 | + idx = getVarint32(aKey, szHdr); | |
| 67528 | + assert( szHdr<=nKey ); | |
| 67529 | + while( idx<szHdr ){ | |
| 67530 | + idx += getVarint32(aKey+idx, notUsed); | |
| 67531 | + nField++; | |
| 67532 | + } | |
| 67533 | + assert( nField <= pKeyInfo->nField+pKeyInfo->nXField ); | |
| 67534 | +} | |
| 67535 | +#else | |
| 67536 | +# define vdbeAssertFieldCountWithinLimits(A,B,C) | |
| 67537 | +#endif | |
| 67538 | + | |
| 67399 | 67539 | /* |
| 67400 | 67540 | ** Both *pMem1 and *pMem2 contain string values. Compare the two values |
| 67401 | 67541 | ** using the collation sequence pColl. As usual, return a negative , zero |
| 67402 | 67542 | ** or positive value if *pMem1 is less than, equal to or greater than |
| 67403 | 67543 | ** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);". |
| @@ -67805,10 +67945,11 @@ | ||
| 67805 | 67945 | u32 y; |
| 67806 | 67946 | u64 x; |
| 67807 | 67947 | i64 v = pPKey2->aMem[0].u.i; |
| 67808 | 67948 | i64 lhs; |
| 67809 | 67949 | |
| 67950 | + vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); | |
| 67810 | 67951 | assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB ); |
| 67811 | 67952 | switch( serial_type ){ |
| 67812 | 67953 | case 1: { /* 1-byte signed integer */ |
| 67813 | 67954 | lhs = ONE_BYTE_INT(aKey); |
| 67814 | 67955 | testcase( lhs<0 ); |
| @@ -67892,10 +68033,11 @@ | ||
| 67892 | 68033 | ){ |
| 67893 | 68034 | const u8 *aKey1 = (const u8*)pKey1; |
| 67894 | 68035 | int serial_type; |
| 67895 | 68036 | int res; |
| 67896 | 68037 | |
| 68038 | + vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); | |
| 67897 | 68039 | getVarint32(&aKey1[1], serial_type); |
| 67898 | 68040 | if( serial_type<12 ){ |
| 67899 | 68041 | res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ |
| 67900 | 68042 | }else if( !(serial_type & 0x01) ){ |
| 67901 | 68043 | res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ |
| @@ -68593,11 +68735,14 @@ | ||
| 68593 | 68735 | #ifndef SQLITE_OMIT_WAL |
| 68594 | 68736 | int i; |
| 68595 | 68737 | for(i=0; i<db->nDb; i++){ |
| 68596 | 68738 | Btree *pBt = db->aDb[i].pBt; |
| 68597 | 68739 | if( pBt ){ |
| 68598 | - int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); | |
| 68740 | + int nEntry; | |
| 68741 | + sqlite3BtreeEnter(pBt); | |
| 68742 | + nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); | |
| 68743 | + sqlite3BtreeLeave(pBt); | |
| 68599 | 68744 | if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){ |
| 68600 | 68745 | rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry); |
| 68601 | 68746 | } |
| 68602 | 68747 | } |
| 68603 | 68748 | } |
| @@ -68773,11 +68918,10 @@ | ||
| 68773 | 68918 | ** program counter to 0 to ensure that when the statement is |
| 68774 | 68919 | ** finalized or reset the parser error message is available via |
| 68775 | 68920 | ** sqlite3_errmsg() and sqlite3_errcode(). |
| 68776 | 68921 | */ |
| 68777 | 68922 | const char *zErr = (const char *)sqlite3_value_text(db->pErr); |
| 68778 | - assert( zErr!=0 || db->mallocFailed ); | |
| 68779 | 68923 | sqlite3DbFree(db, v->zErrMsg); |
| 68780 | 68924 | if( !db->mallocFailed ){ |
| 68781 | 68925 | v->zErrMsg = sqlite3DbStrDup(db, zErr); |
| 68782 | 68926 | v->rc = rc2; |
| 68783 | 68927 | } else { |
| @@ -73838,12 +73982,12 @@ | ||
| 73838 | 73982 | pIdxKey->default_rc = 0; |
| 73839 | 73983 | if( pOp->opcode==OP_NoConflict ){ |
| 73840 | 73984 | /* For the OP_NoConflict opcode, take the jump if any of the |
| 73841 | 73985 | ** input fields are NULL, since any key with a NULL will not |
| 73842 | 73986 | ** conflict */ |
| 73843 | - for(ii=0; ii<r.nField; ii++){ | |
| 73844 | - if( r.aMem[ii].flags & MEM_Null ){ | |
| 73987 | + for(ii=0; ii<pIdxKey->nField; ii++){ | |
| 73988 | + if( pIdxKey->aMem[ii].flags & MEM_Null ){ | |
| 73845 | 73989 | pc = pOp->p2 - 1; VdbeBranchTaken(1,2); |
| 73846 | 73990 | break; |
| 73847 | 73991 | } |
| 73848 | 73992 | } |
| 73849 | 73993 | } |
| @@ -77135,11 +77279,11 @@ | ||
| 77135 | 77279 | /* |
| 77136 | 77280 | ** Hard-coded maximum amount of data to accumulate in memory before flushing |
| 77137 | 77281 | ** to a level 0 PMA. The purpose of this limit is to prevent various integer |
| 77138 | 77282 | ** overflows. 512MiB. |
| 77139 | 77283 | */ |
| 77140 | -#define SQLITE_MAX_MXPMASIZE (1<<29) | |
| 77284 | +#define SQLITE_MAX_PMASZ (1<<29) | |
| 77141 | 77285 | |
| 77142 | 77286 | /* |
| 77143 | 77287 | ** Private objects used by the sorter |
| 77144 | 77288 | */ |
| 77145 | 77289 | typedef struct MergeEngine MergeEngine; /* Merge PMAs together */ |
| @@ -77431,15 +77575,10 @@ | ||
| 77431 | 77575 | ** |
| 77432 | 77576 | ** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; } |
| 77433 | 77577 | */ |
| 77434 | 77578 | #define SRVAL(p) ((void*)((SorterRecord*)(p) + 1)) |
| 77435 | 77579 | |
| 77436 | -/* The minimum PMA size is set to this value multiplied by the database | |
| 77437 | -** page size in bytes. */ | |
| 77438 | -#ifndef SQLITE_SORTER_PMASZ | |
| 77439 | -# define SQLITE_SORTER_PMASZ 10 | |
| 77440 | -#endif | |
| 77441 | 77580 | |
| 77442 | 77581 | /* Maximum number of PMAs that a single MergeEngine can merge */ |
| 77443 | 77582 | #define SORTER_MAX_MERGE_COUNT 16 |
| 77444 | 77583 | |
| 77445 | 77584 | static int vdbeIncrSwap(IncrMerger*); |
| @@ -77834,14 +77973,15 @@ | ||
| 77834 | 77973 | SortSubtask *pTask = &pSorter->aTask[i]; |
| 77835 | 77974 | pTask->pSorter = pSorter; |
| 77836 | 77975 | } |
| 77837 | 77976 | |
| 77838 | 77977 | if( !sqlite3TempInMemory(db) ){ |
| 77839 | - pSorter->mnPmaSize = SQLITE_SORTER_PMASZ * pgsz; | |
| 77978 | + u32 szPma = sqlite3GlobalConfig.szPma; | |
| 77979 | + pSorter->mnPmaSize = szPma * pgsz; | |
| 77840 | 77980 | mxCache = db->aDb[0].pSchema->cache_size; |
| 77841 | - if( mxCache<SQLITE_SORTER_PMASZ ) mxCache = SQLITE_SORTER_PMASZ; | |
| 77842 | - pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_MXPMASIZE); | |
| 77981 | + if( mxCache<(int)szPma ) mxCache = (int)szPma; | |
| 77982 | + pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_PMASZ); | |
| 77843 | 77983 | |
| 77844 | 77984 | /* EVIDENCE-OF: R-26747-61719 When the application provides any amount of |
| 77845 | 77985 | ** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary |
| 77846 | 77986 | ** large heap allocations. |
| 77847 | 77987 | */ |
| @@ -78115,16 +78255,16 @@ | ||
| 78115 | 78255 | ** Whether or not the file does end up memory mapped of course depends on |
| 78116 | 78256 | ** the specific VFS implementation. |
| 78117 | 78257 | */ |
| 78118 | 78258 | static void vdbeSorterExtendFile(sqlite3 *db, sqlite3_file *pFd, i64 nByte){ |
| 78119 | 78259 | if( nByte<=(i64)(db->nMaxSorterMmap) && pFd->pMethods->iVersion>=3 ){ |
| 78120 | - int rc = sqlite3OsTruncate(pFd, nByte); | |
| 78121 | - if( rc==SQLITE_OK ){ | |
| 78122 | - void *p = 0; | |
| 78123 | - sqlite3OsFetch(pFd, 0, (int)nByte, &p); | |
| 78124 | - sqlite3OsUnfetch(pFd, 0, p); | |
| 78125 | - } | |
| 78260 | + void *p = 0; | |
| 78261 | + int chunksize = 4*1024; | |
| 78262 | + sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize); | |
| 78263 | + sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte); | |
| 78264 | + sqlite3OsFetch(pFd, 0, (int)nByte, &p); | |
| 78265 | + sqlite3OsUnfetch(pFd, 0, p); | |
| 78126 | 78266 | } |
| 78127 | 78267 | } |
| 78128 | 78268 | #else |
| 78129 | 78269 | # define vdbeSorterExtendFile(x,y,z) |
| 78130 | 78270 | #endif |
| @@ -79401,10 +79541,11 @@ | ||
| 79401 | 79541 | rc = vdbePmaReaderNext(pSorter->pReader); |
| 79402 | 79542 | *pbEof = (pSorter->pReader->pFd==0); |
| 79403 | 79543 | }else |
| 79404 | 79544 | #endif |
| 79405 | 79545 | /*if( !pSorter->bUseThreads )*/ { |
| 79546 | + assert( pSorter->pMerger!=0 ); | |
| 79406 | 79547 | assert( pSorter->pMerger->pTask==(&pSorter->aTask[0]) ); |
| 79407 | 79548 | rc = vdbeMergeEngineStep(pSorter->pMerger, pbEof); |
| 79408 | 79549 | } |
| 79409 | 79550 | }else{ |
| 79410 | 79551 | SorterRecord *pFree = pSorter->list.pList; |
| @@ -82167,11 +82308,11 @@ | ||
| 82167 | 82308 | Expr *pLeft, /* Left operand */ |
| 82168 | 82309 | Expr *pRight, /* Right operand */ |
| 82169 | 82310 | const Token *pToken /* Argument token */ |
| 82170 | 82311 | ){ |
| 82171 | 82312 | Expr *p; |
| 82172 | - if( op==TK_AND && pLeft && pRight ){ | |
| 82313 | + if( op==TK_AND && pLeft && pRight && pParse->nErr==0 ){ | |
| 82173 | 82314 | /* Take advantage of short-circuit false optimization for AND */ |
| 82174 | 82315 | p = sqlite3ExprAnd(pParse->db, pLeft, pRight); |
| 82175 | 82316 | }else{ |
| 82176 | 82317 | p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); |
| 82177 | 82318 | sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); |
| @@ -85721,14 +85862,15 @@ | ||
| 85721 | 85862 | ** NEVER() will need to be removed. */ |
| 85722 | 85863 | if( pExpr->op==TK_COLUMN || NEVER(pExpr->op==TK_AGG_COLUMN) ){ |
| 85723 | 85864 | int i; |
| 85724 | 85865 | struct SrcCount *p = pWalker->u.pSrcCount; |
| 85725 | 85866 | SrcList *pSrc = p->pSrc; |
| 85726 | - for(i=0; i<pSrc->nSrc; i++){ | |
| 85867 | + int nSrc = pSrc ? pSrc->nSrc : 0; | |
| 85868 | + for(i=0; i<nSrc; i++){ | |
| 85727 | 85869 | if( pExpr->iTable==pSrc->a[i].iCursor ) break; |
| 85728 | 85870 | } |
| 85729 | - if( i<pSrc->nSrc ){ | |
| 85871 | + if( i<nSrc ){ | |
| 85730 | 85872 | p->nThis++; |
| 85731 | 85873 | }else{ |
| 85732 | 85874 | p->nOther++; |
| 85733 | 85875 | } |
| 85734 | 85876 | } |
| @@ -87302,11 +87444,11 @@ | ||
| 87302 | 87444 | |
| 87303 | 87445 | p->iGet = -1; |
| 87304 | 87446 | p->mxSample = mxSample; |
| 87305 | 87447 | p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1); |
| 87306 | 87448 | p->current.anLt = &p->current.anEq[nColUp]; |
| 87307 | - p->iPrn = nCol*0x689e962d ^ sqlite3_value_int(argv[2])*0xd0944565; | |
| 87449 | + p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]); | |
| 87308 | 87450 | |
| 87309 | 87451 | /* Set up the Stat4Accum.a[] and aBest[] arrays */ |
| 87310 | 87452 | p->a = (struct Stat4Sample*)&p->current.anLt[nColUp]; |
| 87311 | 87453 | p->aBest = &p->a[mxSample]; |
| 87312 | 87454 | pSpace = (u8*)(&p->a[mxSample+nCol]); |
| @@ -88895,17 +89037,19 @@ | ||
| 88895 | 89037 | }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ |
| 88896 | 89038 | zErrDyn = sqlite3MPrintf(db, |
| 88897 | 89039 | "attached databases must use the same text encoding as main database"); |
| 88898 | 89040 | rc = SQLITE_ERROR; |
| 88899 | 89041 | } |
| 89042 | + sqlite3BtreeEnter(aNew->pBt); | |
| 88900 | 89043 | pPager = sqlite3BtreePager(aNew->pBt); |
| 88901 | 89044 | sqlite3PagerLockingMode(pPager, db->dfltLockMode); |
| 88902 | 89045 | sqlite3BtreeSecureDelete(aNew->pBt, |
| 88903 | 89046 | sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) ); |
| 88904 | 89047 | #ifndef SQLITE_OMIT_PAGER_PRAGMAS |
| 88905 | 89048 | sqlite3BtreeSetPagerFlags(aNew->pBt, 3 | (db->flags & PAGER_FLAGS_MASK)); |
| 88906 | 89049 | #endif |
| 89050 | + sqlite3BtreeLeave(aNew->pBt); | |
| 88907 | 89051 | } |
| 88908 | 89052 | aNew->safety_level = 3; |
| 88909 | 89053 | aNew->zName = sqlite3DbStrDup(db, zName); |
| 88910 | 89054 | if( rc==SQLITE_OK && aNew->zName==0 ){ |
| 88911 | 89055 | rc = SQLITE_NOMEM; |
| @@ -90027,11 +90171,10 @@ | ||
| 90027 | 90171 | */ |
| 90028 | 90172 | static void freeIndex(sqlite3 *db, Index *p){ |
| 90029 | 90173 | #ifndef SQLITE_OMIT_ANALYZE |
| 90030 | 90174 | sqlite3DeleteIndexSamples(db, p); |
| 90031 | 90175 | #endif |
| 90032 | - if( db==0 || db->pnBytesFreed==0 ) sqlite3KeyInfoUnref(p->pKeyInfo); | |
| 90033 | 90176 | sqlite3ExprDelete(db, p->pPartIdxWhere); |
| 90034 | 90177 | sqlite3DbFree(db, p->zColAff); |
| 90035 | 90178 | if( p->isResized ) sqlite3DbFree(db, p->azColl); |
| 90036 | 90179 | #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 |
| 90037 | 90180 | sqlite3_free(p->aiRowEst); |
| @@ -91306,10 +91449,23 @@ | ||
| 91306 | 91449 | if( pPk==0 ) return; |
| 91307 | 91450 | pPk->idxType = SQLITE_IDXTYPE_PRIMARYKEY; |
| 91308 | 91451 | pTab->iPKey = -1; |
| 91309 | 91452 | }else{ |
| 91310 | 91453 | pPk = sqlite3PrimaryKeyIndex(pTab); |
| 91454 | + /* | |
| 91455 | + ** Remove all redundant columns from the PRIMARY KEY. For example, change | |
| 91456 | + ** "PRIMARY KEY(a,b,a,b,c,b,c,d)" into just "PRIMARY KEY(a,b,c,d)". Later | |
| 91457 | + ** code assumes the PRIMARY KEY contains no repeated columns. | |
| 91458 | + */ | |
| 91459 | + for(i=j=1; i<pPk->nKeyCol; i++){ | |
| 91460 | + if( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) ){ | |
| 91461 | + pPk->nColumn--; | |
| 91462 | + }else{ | |
| 91463 | + pPk->aiColumn[j++] = pPk->aiColumn[i]; | |
| 91464 | + } | |
| 91465 | + } | |
| 91466 | + pPk->nKeyCol = j; | |
| 91311 | 91467 | } |
| 91312 | 91468 | pPk->isCovering = 1; |
| 91313 | 91469 | assert( pPk!=0 ); |
| 91314 | 91470 | nPk = pPk->nKeyCol; |
| 91315 | 91471 | |
| @@ -93782,44 +93938,35 @@ | ||
| 93782 | 93938 | ** |
| 93783 | 93939 | ** The caller should invoke sqlite3KeyInfoUnref() on the returned object |
| 93784 | 93940 | ** when it has finished using it. |
| 93785 | 93941 | */ |
| 93786 | 93942 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ |
| 93943 | + int i; | |
| 93944 | + int nCol = pIdx->nColumn; | |
| 93945 | + int nKey = pIdx->nKeyCol; | |
| 93946 | + KeyInfo *pKey; | |
| 93787 | 93947 | if( pParse->nErr ) return 0; |
| 93788 | -#ifndef SQLITE_OMIT_SHARED_CACHE | |
| 93789 | - if( pIdx->pKeyInfo && pIdx->pKeyInfo->db!=pParse->db ){ | |
| 93790 | - sqlite3KeyInfoUnref(pIdx->pKeyInfo); | |
| 93791 | - pIdx->pKeyInfo = 0; | |
| 93792 | - } | |
| 93793 | -#endif | |
| 93794 | - if( pIdx->pKeyInfo==0 ){ | |
| 93795 | - int i; | |
| 93796 | - int nCol = pIdx->nColumn; | |
| 93797 | - int nKey = pIdx->nKeyCol; | |
| 93798 | - KeyInfo *pKey; | |
| 93799 | - if( pIdx->uniqNotNull ){ | |
| 93800 | - pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey); | |
| 93801 | - }else{ | |
| 93802 | - pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); | |
| 93803 | - } | |
| 93804 | - if( pKey ){ | |
| 93805 | - assert( sqlite3KeyInfoIsWriteable(pKey) ); | |
| 93806 | - for(i=0; i<nCol; i++){ | |
| 93807 | - char *zColl = pIdx->azColl[i]; | |
| 93808 | - assert( zColl!=0 ); | |
| 93809 | - pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 : | |
| 93810 | - sqlite3LocateCollSeq(pParse, zColl); | |
| 93811 | - pKey->aSortOrder[i] = pIdx->aSortOrder[i]; | |
| 93812 | - } | |
| 93813 | - if( pParse->nErr ){ | |
| 93814 | - sqlite3KeyInfoUnref(pKey); | |
| 93815 | - }else{ | |
| 93816 | - pIdx->pKeyInfo = pKey; | |
| 93817 | - } | |
| 93818 | - } | |
| 93819 | - } | |
| 93820 | - return sqlite3KeyInfoRef(pIdx->pKeyInfo); | |
| 93948 | + if( pIdx->uniqNotNull ){ | |
| 93949 | + pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey); | |
| 93950 | + }else{ | |
| 93951 | + pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); | |
| 93952 | + } | |
| 93953 | + if( pKey ){ | |
| 93954 | + assert( sqlite3KeyInfoIsWriteable(pKey) ); | |
| 93955 | + for(i=0; i<nCol; i++){ | |
| 93956 | + char *zColl = pIdx->azColl[i]; | |
| 93957 | + assert( zColl!=0 ); | |
| 93958 | + pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 : | |
| 93959 | + sqlite3LocateCollSeq(pParse, zColl); | |
| 93960 | + pKey->aSortOrder[i] = pIdx->aSortOrder[i]; | |
| 93961 | + } | |
| 93962 | + if( pParse->nErr ){ | |
| 93963 | + sqlite3KeyInfoUnref(pKey); | |
| 93964 | + pKey = 0; | |
| 93965 | + } | |
| 93966 | + } | |
| 93967 | + return pKey; | |
| 93821 | 93968 | } |
| 93822 | 93969 | |
| 93823 | 93970 | #ifndef SQLITE_OMIT_CTE |
| 93824 | 93971 | /* |
| 93825 | 93972 | ** This routine is invoked once per CTE by the parser while parsing a |
| @@ -94596,12 +94743,12 @@ | ||
| 94596 | 94743 | const char *zDb; /* Name of database holding pTab */ |
| 94597 | 94744 | int i; /* Loop counter */ |
| 94598 | 94745 | WhereInfo *pWInfo; /* Information about the WHERE clause */ |
| 94599 | 94746 | Index *pIdx; /* For looping over indices of the table */ |
| 94600 | 94747 | int iTabCur; /* Cursor number for the table */ |
| 94601 | - int iDataCur; /* VDBE cursor for the canonical data source */ | |
| 94602 | - int iIdxCur; /* Cursor number of the first index */ | |
| 94748 | + int iDataCur = 0; /* VDBE cursor for the canonical data source */ | |
| 94749 | + int iIdxCur = 0; /* Cursor number of the first index */ | |
| 94603 | 94750 | int nIdx; /* Number of indices */ |
| 94604 | 94751 | sqlite3 *db; /* Main database structure */ |
| 94605 | 94752 | AuthContext sContext; /* Authorization context */ |
| 94606 | 94753 | NameContext sNC; /* Name context to resolve expressions in */ |
| 94607 | 94754 | int iDb; /* Database number */ |
| @@ -97436,11 +97583,11 @@ | ||
| 97436 | 97583 | assert( nIncr==1 ); |
| 97437 | 97584 | sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, |
| 97438 | 97585 | OE_Abort, 0, P4_STATIC, P5_ConstraintFK); |
| 97439 | 97586 | }else{ |
| 97440 | 97587 | if( nIncr>0 && pFKey->isDeferred==0 ){ |
| 97441 | - sqlite3ParseToplevel(pParse)->mayAbort = 1; | |
| 97588 | + sqlite3MayAbort(pParse); | |
| 97442 | 97589 | } |
| 97443 | 97590 | sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); |
| 97444 | 97591 | } |
| 97445 | 97592 | |
| 97446 | 97593 | sqlite3VdbeResolveLabel(v, iOk); |
| @@ -97507,10 +97654,14 @@ | ||
| 97507 | 97654 | ** This function is called to generate code executed when a row is deleted |
| 97508 | 97655 | ** from the parent table of foreign key constraint pFKey and, if pFKey is |
| 97509 | 97656 | ** deferred, when a row is inserted into the same table. When generating |
| 97510 | 97657 | ** code for an SQL UPDATE operation, this function may be called twice - |
| 97511 | 97658 | ** once to "delete" the old row and once to "insert" the new row. |
| 97659 | +** | |
| 97660 | +** Parameter nIncr is passed -1 when inserting a row (as this may decrease | |
| 97661 | +** the number of FK violations in the db) or +1 when deleting one (as this | |
| 97662 | +** may increase the number of FK constraint problems). | |
| 97512 | 97663 | ** |
| 97513 | 97664 | ** The code generated by this function scans through the rows in the child |
| 97514 | 97665 | ** table that correspond to the parent table row being deleted or inserted. |
| 97515 | 97666 | ** For each child row found, one of the following actions is taken: |
| 97516 | 97667 | ** |
| @@ -97624,17 +97775,13 @@ | ||
| 97624 | 97775 | sNameContext.pSrcList = pSrc; |
| 97625 | 97776 | sNameContext.pParse = pParse; |
| 97626 | 97777 | sqlite3ResolveExprNames(&sNameContext, pWhere); |
| 97627 | 97778 | |
| 97628 | 97779 | /* Create VDBE to loop through the entries in pSrc that match the WHERE |
| 97629 | - ** clause. If the constraint is not deferred, throw an exception for | |
| 97630 | - ** each row found. Otherwise, for deferred constraints, increment the | |
| 97631 | - ** deferred constraint counter by nIncr for each row selected. */ | |
| 97780 | + ** clause. For each row found, increment either the deferred or immediate | |
| 97781 | + ** foreign key constraint counter. */ | |
| 97632 | 97782 | pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); |
| 97633 | - if( nIncr>0 && pFKey->isDeferred==0 ){ | |
| 97634 | - sqlite3ParseToplevel(pParse)->mayAbort = 1; | |
| 97635 | - } | |
| 97636 | 97783 | sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); |
| 97637 | 97784 | if( pWInfo ){ |
| 97638 | 97785 | sqlite3WhereEnd(pWInfo); |
| 97639 | 97786 | } |
| 97640 | 97787 | |
| @@ -97808,10 +97955,28 @@ | ||
| 97808 | 97955 | } |
| 97809 | 97956 | } |
| 97810 | 97957 | } |
| 97811 | 97958 | return 0; |
| 97812 | 97959 | } |
| 97960 | + | |
| 97961 | +/* | |
| 97962 | +** Return true if the parser passed as the first argument is being | |
| 97963 | +** used to code a trigger that is really a "SET NULL" action belonging | |
| 97964 | +** to trigger pFKey. | |
| 97965 | +*/ | |
| 97966 | +static int isSetNullAction(Parse *pParse, FKey *pFKey){ | |
| 97967 | + Parse *pTop = sqlite3ParseToplevel(pParse); | |
| 97968 | + if( pTop->pTriggerPrg ){ | |
| 97969 | + Trigger *p = pTop->pTriggerPrg->pTrigger; | |
| 97970 | + if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull) | |
| 97971 | + || (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull) | |
| 97972 | + ){ | |
| 97973 | + return 1; | |
| 97974 | + } | |
| 97975 | + } | |
| 97976 | + return 0; | |
| 97977 | +} | |
| 97813 | 97978 | |
| 97814 | 97979 | /* |
| 97815 | 97980 | ** This function is called when inserting, deleting or updating a row of |
| 97816 | 97981 | ** table pTab to generate VDBE code to perform foreign key constraint |
| 97817 | 97982 | ** processing for the operation. |
| @@ -97861,11 +98026,11 @@ | ||
| 97861 | 98026 | Index *pIdx = 0; /* Index on key columns in pTo */ |
| 97862 | 98027 | int *aiFree = 0; |
| 97863 | 98028 | int *aiCol; |
| 97864 | 98029 | int iCol; |
| 97865 | 98030 | int i; |
| 97866 | - int isIgnore = 0; | |
| 98031 | + int bIgnore = 0; | |
| 97867 | 98032 | |
| 97868 | 98033 | if( aChange |
| 97869 | 98034 | && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0 |
| 97870 | 98035 | && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0 |
| 97871 | 98036 | ){ |
| @@ -97920,11 +98085,11 @@ | ||
| 97920 | 98085 | ** values read from the parent table are NULL. */ |
| 97921 | 98086 | if( db->xAuth ){ |
| 97922 | 98087 | int rcauth; |
| 97923 | 98088 | char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; |
| 97924 | 98089 | rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); |
| 97925 | - isIgnore = (rcauth==SQLITE_IGNORE); | |
| 98090 | + bIgnore = (rcauth==SQLITE_IGNORE); | |
| 97926 | 98091 | } |
| 97927 | 98092 | #endif |
| 97928 | 98093 | } |
| 97929 | 98094 | |
| 97930 | 98095 | /* Take a shared-cache advisory read-lock on the parent table. Allocate |
| @@ -97935,16 +98100,22 @@ | ||
| 97935 | 98100 | |
| 97936 | 98101 | if( regOld!=0 ){ |
| 97937 | 98102 | /* A row is being removed from the child table. Search for the parent. |
| 97938 | 98103 | ** If the parent does not exist, removing the child row resolves an |
| 97939 | 98104 | ** outstanding foreign key constraint violation. */ |
| 97940 | - fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore); | |
| 98105 | + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1, bIgnore); | |
| 97941 | 98106 | } |
| 97942 | - if( regNew!=0 ){ | |
| 98107 | + if( regNew!=0 && !isSetNullAction(pParse, pFKey) ){ | |
| 97943 | 98108 | /* A row is being added to the child table. If a parent row cannot |
| 97944 | - ** be found, adding the child row has violated the FK constraint. */ | |
| 97945 | - fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore); | |
| 98109 | + ** be found, adding the child row has violated the FK constraint. | |
| 98110 | + ** | |
| 98111 | + ** If this operation is being performed as part of a trigger program | |
| 98112 | + ** that is actually a "SET NULL" action belonging to this very | |
| 98113 | + ** foreign key, then omit this scan altogether. As all child key | |
| 98114 | + ** values are guaranteed to be NULL, it is not possible for adding | |
| 98115 | + ** this row to cause an FK violation. */ | |
| 98116 | + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1, bIgnore); | |
| 97946 | 98117 | } |
| 97947 | 98118 | |
| 97948 | 98119 | sqlite3DbFree(db, aiFree); |
| 97949 | 98120 | } |
| 97950 | 98121 | |
| @@ -97961,12 +98132,12 @@ | ||
| 97961 | 98132 | |
| 97962 | 98133 | if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs) |
| 97963 | 98134 | && !pParse->pToplevel && !pParse->isMultiWrite |
| 97964 | 98135 | ){ |
| 97965 | 98136 | assert( regOld==0 && regNew!=0 ); |
| 97966 | - /* Inserting a single row into a parent table cannot cause an immediate | |
| 97967 | - ** foreign key violation. So do nothing in this case. */ | |
| 98137 | + /* Inserting a single row into a parent table cannot cause (or fix) | |
| 98138 | + ** an immediate foreign key violation. So do nothing in this case. */ | |
| 97968 | 98139 | continue; |
| 97969 | 98140 | } |
| 97970 | 98141 | |
| 97971 | 98142 | if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ |
| 97972 | 98143 | if( !isIgnoreErrors || db->mallocFailed ) return; |
| @@ -97986,17 +98157,32 @@ | ||
| 97986 | 98157 | |
| 97987 | 98158 | if( regNew!=0 ){ |
| 97988 | 98159 | fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); |
| 97989 | 98160 | } |
| 97990 | 98161 | if( regOld!=0 ){ |
| 97991 | - /* If there is a RESTRICT action configured for the current operation | |
| 97992 | - ** on the parent table of this FK, then throw an exception | |
| 97993 | - ** immediately if the FK constraint is violated, even if this is a | |
| 97994 | - ** deferred trigger. That's what RESTRICT means. To defer checking | |
| 97995 | - ** the constraint, the FK should specify NO ACTION (represented | |
| 97996 | - ** using OE_None). NO ACTION is the default. */ | |
| 98162 | + int eAction = pFKey->aAction[aChange!=0]; | |
| 97997 | 98163 | fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); |
| 98164 | + /* If this is a deferred FK constraint, or a CASCADE or SET NULL | |
| 98165 | + ** action applies, then any foreign key violations caused by | |
| 98166 | + ** removing the parent key will be rectified by the action trigger. | |
| 98167 | + ** So do not set the "may-abort" flag in this case. | |
| 98168 | + ** | |
| 98169 | + ** Note 1: If the FK is declared "ON UPDATE CASCADE", then the | |
| 98170 | + ** may-abort flag will eventually be set on this statement anyway | |
| 98171 | + ** (when this function is called as part of processing the UPDATE | |
| 98172 | + ** within the action trigger). | |
| 98173 | + ** | |
| 98174 | + ** Note 2: At first glance it may seem like SQLite could simply omit | |
| 98175 | + ** all OP_FkCounter related scans when either CASCADE or SET NULL | |
| 98176 | + ** applies. The trouble starts if the CASCADE or SET NULL action | |
| 98177 | + ** trigger causes other triggers or action rules attached to the | |
| 98178 | + ** child table to fire. In these cases the fk constraint counters | |
| 98179 | + ** might be set incorrectly if any OP_FkCounter related scans are | |
| 98180 | + ** omitted. */ | |
| 98181 | + if( !pFKey->isDeferred && eAction!=OE_Cascade && eAction!=OE_SetNull ){ | |
| 98182 | + sqlite3MayAbort(pParse); | |
| 98183 | + } | |
| 97998 | 98184 | } |
| 97999 | 98185 | pItem->zName = 0; |
| 98000 | 98186 | sqlite3SrcListDelete(db, pSrc); |
| 98001 | 98187 | } |
| 98002 | 98188 | sqlite3DbFree(db, aiCol); |
| @@ -101895,10 +102081,11 @@ | ||
| 101895 | 102081 | #define PragTyp_KEY 38 |
| 101896 | 102082 | #define PragTyp_REKEY 39 |
| 101897 | 102083 | #define PragTyp_LOCK_STATUS 40 |
| 101898 | 102084 | #define PragTyp_PARSER_TRACE 41 |
| 101899 | 102085 | #define PragFlag_NeedSchema 0x01 |
| 102086 | +#define PragFlag_ReadOnly 0x02 | |
| 101900 | 102087 | static const struct sPragmaNames { |
| 101901 | 102088 | const char *const zName; /* Name of pragma */ |
| 101902 | 102089 | u8 ePragTyp; /* PragTyp_XXX value */ |
| 101903 | 102090 | u8 mPragFlag; /* Zero or more PragFlag_XXX values */ |
| 101904 | 102091 | u32 iArg; /* Extra argument */ |
| @@ -101911,11 +102098,11 @@ | ||
| 101911 | 102098 | #endif |
| 101912 | 102099 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 101913 | 102100 | { /* zName: */ "application_id", |
| 101914 | 102101 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 101915 | 102102 | /* ePragFlag: */ 0, |
| 101916 | - /* iArg: */ 0 }, | |
| 102103 | + /* iArg: */ BTREE_APPLICATION_ID }, | |
| 101917 | 102104 | #endif |
| 101918 | 102105 | #if !defined(SQLITE_OMIT_AUTOVACUUM) |
| 101919 | 102106 | { /* zName: */ "auto_vacuum", |
| 101920 | 102107 | /* ePragTyp: */ PragTyp_AUTO_VACUUM, |
| 101921 | 102108 | /* ePragFlag: */ PragFlag_NeedSchema, |
| @@ -101977,10 +102164,16 @@ | ||
| 101977 | 102164 | { /* zName: */ "data_store_directory", |
| 101978 | 102165 | /* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY, |
| 101979 | 102166 | /* ePragFlag: */ 0, |
| 101980 | 102167 | /* iArg: */ 0 }, |
| 101981 | 102168 | #endif |
| 102169 | +#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) | |
| 102170 | + { /* zName: */ "data_version", | |
| 102171 | + /* ePragTyp: */ PragTyp_HEADER_VALUE, | |
| 102172 | + /* ePragFlag: */ PragFlag_ReadOnly, | |
| 102173 | + /* iArg: */ BTREE_DATA_VERSION }, | |
| 102174 | +#endif | |
| 101982 | 102175 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 101983 | 102176 | { /* zName: */ "database_list", |
| 101984 | 102177 | /* ePragTyp: */ PragTyp_DATABASE_LIST, |
| 101985 | 102178 | /* ePragFlag: */ PragFlag_NeedSchema, |
| 101986 | 102179 | /* iArg: */ 0 }, |
| @@ -102032,12 +102225,12 @@ | ||
| 102032 | 102225 | #endif |
| 102033 | 102226 | #endif |
| 102034 | 102227 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102035 | 102228 | { /* zName: */ "freelist_count", |
| 102036 | 102229 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102037 | - /* ePragFlag: */ 0, | |
| 102038 | - /* iArg: */ 0 }, | |
| 102230 | + /* ePragFlag: */ PragFlag_ReadOnly, | |
| 102231 | + /* iArg: */ BTREE_FREE_PAGE_COUNT }, | |
| 102039 | 102232 | #endif |
| 102040 | 102233 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 102041 | 102234 | { /* zName: */ "full_column_names", |
| 102042 | 102235 | /* ePragTyp: */ PragTyp_FLAG, |
| 102043 | 102236 | /* ePragFlag: */ 0, |
| @@ -102185,11 +102378,11 @@ | ||
| 102185 | 102378 | #endif |
| 102186 | 102379 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102187 | 102380 | { /* zName: */ "schema_version", |
| 102188 | 102381 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102189 | 102382 | /* ePragFlag: */ 0, |
| 102190 | - /* iArg: */ 0 }, | |
| 102383 | + /* iArg: */ BTREE_SCHEMA_VERSION }, | |
| 102191 | 102384 | #endif |
| 102192 | 102385 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) |
| 102193 | 102386 | { /* zName: */ "secure_delete", |
| 102194 | 102387 | /* ePragTyp: */ PragTyp_SECURE_DELETE, |
| 102195 | 102388 | /* ePragFlag: */ 0, |
| @@ -102251,11 +102444,11 @@ | ||
| 102251 | 102444 | /* iArg: */ 0 }, |
| 102252 | 102445 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102253 | 102446 | { /* zName: */ "user_version", |
| 102254 | 102447 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102255 | 102448 | /* ePragFlag: */ 0, |
| 102256 | - /* iArg: */ 0 }, | |
| 102449 | + /* iArg: */ BTREE_USER_VERSION }, | |
| 102257 | 102450 | #endif |
| 102258 | 102451 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 102259 | 102452 | #if defined(SQLITE_DEBUG) |
| 102260 | 102453 | { /* zName: */ "vdbe_addoptrace", |
| 102261 | 102454 | /* ePragTyp: */ PragTyp_FLAG, |
| @@ -102294,11 +102487,11 @@ | ||
| 102294 | 102487 | /* ePragTyp: */ PragTyp_FLAG, |
| 102295 | 102488 | /* ePragFlag: */ 0, |
| 102296 | 102489 | /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode }, |
| 102297 | 102490 | #endif |
| 102298 | 102491 | }; |
| 102299 | -/* Number of pragmas: 57 on by default, 70 total. */ | |
| 102492 | +/* Number of pragmas: 58 on by default, 71 total. */ | |
| 102300 | 102493 | /* End of the automatically generated pragma table. |
| 102301 | 102494 | ***************************************************************************/ |
| 102302 | 102495 | |
| 102303 | 102496 | /* |
| 102304 | 102497 | ** Interpret the given string as a safety level. Return 0 for OFF, |
| @@ -102544,11 +102737,11 @@ | ||
| 102544 | 102737 | char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */ |
| 102545 | 102738 | const char *zDb = 0; /* The database name */ |
| 102546 | 102739 | Token *pId; /* Pointer to <id> token */ |
| 102547 | 102740 | char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */ |
| 102548 | 102741 | int iDb; /* Database index for <database> */ |
| 102549 | - int lwr, upr, mid; /* Binary search bounds */ | |
| 102742 | + int lwr, upr, mid = 0; /* Binary search bounds */ | |
| 102550 | 102743 | int rc; /* return value form SQLITE_FCNTL_PRAGMA */ |
| 102551 | 102744 | sqlite3 *db = pParse->db; /* The database connection */ |
| 102552 | 102745 | Db *pDb; /* The specific database being pragmaed */ |
| 102553 | 102746 | Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */ |
| 102554 | 102747 | |
| @@ -103904,11 +104097,12 @@ | ||
| 103904 | 104097 | !(DbHasProperty(db, 0, DB_SchemaLoaded)) || |
| 103905 | 104098 | DbHasProperty(db, 0, DB_Empty) |
| 103906 | 104099 | ){ |
| 103907 | 104100 | for(pEnc=&encnames[0]; pEnc->zName; pEnc++){ |
| 103908 | 104101 | if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){ |
| 103909 | - ENC(pParse->db) = pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE; | |
| 104102 | + SCHEMA_ENC(db) = ENC(db) = | |
| 104103 | + pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE; | |
| 103910 | 104104 | break; |
| 103911 | 104105 | } |
| 103912 | 104106 | } |
| 103913 | 104107 | if( !pEnc->zName ){ |
| 103914 | 104108 | sqlite3ErrorMsg(pParse, "unsupported encoding: %s", zRight); |
| @@ -103949,28 +104143,13 @@ | ||
| 103949 | 104143 | ** |
| 103950 | 104144 | ** The user-version is not used internally by SQLite. It may be used by |
| 103951 | 104145 | ** applications for any purpose. |
| 103952 | 104146 | */ |
| 103953 | 104147 | case PragTyp_HEADER_VALUE: { |
| 103954 | - int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */ | |
| 104148 | + int iCookie = aPragmaNames[mid].iArg; /* Which cookie to read or write */ | |
| 103955 | 104149 | sqlite3VdbeUsesBtree(v, iDb); |
| 103956 | - switch( zLeft[0] ){ | |
| 103957 | - case 'a': case 'A': | |
| 103958 | - iCookie = BTREE_APPLICATION_ID; | |
| 103959 | - break; | |
| 103960 | - case 'f': case 'F': | |
| 103961 | - iCookie = BTREE_FREE_PAGE_COUNT; | |
| 103962 | - break; | |
| 103963 | - case 's': case 'S': | |
| 103964 | - iCookie = BTREE_SCHEMA_VERSION; | |
| 103965 | - break; | |
| 103966 | - default: | |
| 103967 | - iCookie = BTREE_USER_VERSION; | |
| 103968 | - break; | |
| 103969 | - } | |
| 103970 | - | |
| 103971 | - if( zRight && iCookie!=BTREE_FREE_PAGE_COUNT ){ | |
| 104150 | + if( zRight && (aPragmaNames[mid].mPragFlag & PragFlag_ReadOnly)==0 ){ | |
| 103972 | 104151 | /* Write the specified cookie value */ |
| 103973 | 104152 | static const VdbeOpList setCookie[] = { |
| 103974 | 104153 | { OP_Transaction, 0, 1, 0}, /* 0 */ |
| 103975 | 104154 | { OP_Integer, 0, 1, 0}, /* 1 */ |
| 103976 | 104155 | { OP_SetCookie, 0, 0, 1}, /* 2 */ |
| @@ -104612,13 +104791,15 @@ | ||
| 104612 | 104791 | SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){ |
| 104613 | 104792 | int i, rc; |
| 104614 | 104793 | int commit_internal = !(db->flags&SQLITE_InternChanges); |
| 104615 | 104794 | |
| 104616 | 104795 | assert( sqlite3_mutex_held(db->mutex) ); |
| 104796 | + assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) ); | |
| 104617 | 104797 | assert( db->init.busy==0 ); |
| 104618 | 104798 | rc = SQLITE_OK; |
| 104619 | 104799 | db->init.busy = 1; |
| 104800 | + ENC(db) = SCHEMA_ENC(db); | |
| 104620 | 104801 | for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ |
| 104621 | 104802 | if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue; |
| 104622 | 104803 | rc = sqlite3InitOne(db, i, pzErrMsg); |
| 104623 | 104804 | if( rc ){ |
| 104624 | 104805 | sqlite3ResetOneSchema(db, i); |
| @@ -105169,24 +105350,29 @@ | ||
| 105169 | 105350 | u8 sortFlags; /* Zero or more SORTFLAG_* bits */ |
| 105170 | 105351 | }; |
| 105171 | 105352 | #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ |
| 105172 | 105353 | |
| 105173 | 105354 | /* |
| 105174 | -** Delete all the content of a Select structure but do not deallocate | |
| 105175 | -** the select structure itself. | |
| 105355 | +** Delete all the content of a Select structure. Deallocate the structure | |
| 105356 | +** itself only if bFree is true. | |
| 105176 | 105357 | */ |
| 105177 | -static void clearSelect(sqlite3 *db, Select *p){ | |
| 105178 | - sqlite3ExprListDelete(db, p->pEList); | |
| 105179 | - sqlite3SrcListDelete(db, p->pSrc); | |
| 105180 | - sqlite3ExprDelete(db, p->pWhere); | |
| 105181 | - sqlite3ExprListDelete(db, p->pGroupBy); | |
| 105182 | - sqlite3ExprDelete(db, p->pHaving); | |
| 105183 | - sqlite3ExprListDelete(db, p->pOrderBy); | |
| 105184 | - sqlite3SelectDelete(db, p->pPrior); | |
| 105185 | - sqlite3ExprDelete(db, p->pLimit); | |
| 105186 | - sqlite3ExprDelete(db, p->pOffset); | |
| 105187 | - sqlite3WithDelete(db, p->pWith); | |
| 105358 | +static void clearSelect(sqlite3 *db, Select *p, int bFree){ | |
| 105359 | + while( p ){ | |
| 105360 | + Select *pPrior = p->pPrior; | |
| 105361 | + sqlite3ExprListDelete(db, p->pEList); | |
| 105362 | + sqlite3SrcListDelete(db, p->pSrc); | |
| 105363 | + sqlite3ExprDelete(db, p->pWhere); | |
| 105364 | + sqlite3ExprListDelete(db, p->pGroupBy); | |
| 105365 | + sqlite3ExprDelete(db, p->pHaving); | |
| 105366 | + sqlite3ExprListDelete(db, p->pOrderBy); | |
| 105367 | + sqlite3ExprDelete(db, p->pLimit); | |
| 105368 | + sqlite3ExprDelete(db, p->pOffset); | |
| 105369 | + sqlite3WithDelete(db, p->pWith); | |
| 105370 | + if( bFree ) sqlite3DbFree(db, p); | |
| 105371 | + p = pPrior; | |
| 105372 | + bFree = 1; | |
| 105373 | + } | |
| 105188 | 105374 | } |
| 105189 | 105375 | |
| 105190 | 105376 | /* |
| 105191 | 105377 | ** Initialize a SelectDest structure. |
| 105192 | 105378 | */ |
| @@ -105241,12 +105427,11 @@ | ||
| 105241 | 105427 | pNew->pOffset = pOffset; |
| 105242 | 105428 | assert( pOffset==0 || pLimit!=0 ); |
| 105243 | 105429 | pNew->addrOpenEphm[0] = -1; |
| 105244 | 105430 | pNew->addrOpenEphm[1] = -1; |
| 105245 | 105431 | if( db->mallocFailed ) { |
| 105246 | - clearSelect(db, pNew); | |
| 105247 | - if( pNew!=&standin ) sqlite3DbFree(db, pNew); | |
| 105432 | + clearSelect(db, pNew, pNew!=&standin); | |
| 105248 | 105433 | pNew = 0; |
| 105249 | 105434 | }else{ |
| 105250 | 105435 | assert( pNew->pSrc!=0 || pParse->nErr>0 ); |
| 105251 | 105436 | } |
| 105252 | 105437 | assert( pNew!=&standin ); |
| @@ -105267,14 +105452,11 @@ | ||
| 105267 | 105452 | |
| 105268 | 105453 | /* |
| 105269 | 105454 | ** Delete the given Select structure and all of its substructures. |
| 105270 | 105455 | */ |
| 105271 | 105456 | SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){ |
| 105272 | - if( p ){ | |
| 105273 | - clearSelect(db, p); | |
| 105274 | - sqlite3DbFree(db, p); | |
| 105275 | - } | |
| 105457 | + clearSelect(db, p, 1); | |
| 105276 | 105458 | } |
| 105277 | 105459 | |
| 105278 | 105460 | /* |
| 105279 | 105461 | ** Return a pointer to the right-most SELECT statement in a compound. |
| 105280 | 105462 | */ |
| @@ -105653,11 +105835,13 @@ | ||
| 105653 | 105835 | if( pParse->db->mallocFailed ) return; |
| 105654 | 105836 | pOp->p2 = nKey + nData; |
| 105655 | 105837 | pKI = pOp->p4.pKeyInfo; |
| 105656 | 105838 | memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */ |
| 105657 | 105839 | sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO); |
| 105658 | - pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, 1); | |
| 105840 | + testcase( pKI->nXField>2 ); | |
| 105841 | + pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, | |
| 105842 | + pKI->nXField-1); | |
| 105659 | 105843 | addrJmp = sqlite3VdbeCurrentAddr(v); |
| 105660 | 105844 | sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); |
| 105661 | 105845 | pSort->labelBkOut = sqlite3VdbeMakeLabel(v); |
| 105662 | 105846 | pSort->regReturn = ++pParse->nMem; |
| 105663 | 105847 | sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
| @@ -106164,11 +106348,11 @@ | ||
| 106164 | 106348 | struct ExprList_item *pItem; |
| 106165 | 106349 | sqlite3 *db = pParse->db; |
| 106166 | 106350 | int i; |
| 106167 | 106351 | |
| 106168 | 106352 | nExpr = pList->nExpr; |
| 106169 | - pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra-iStart, 1); | |
| 106353 | + pInfo = sqlite3KeyInfoAlloc(db, nExpr-iStart, nExtra+1); | |
| 106170 | 106354 | if( pInfo ){ |
| 106171 | 106355 | assert( sqlite3KeyInfoIsWriteable(pInfo) ); |
| 106172 | 106356 | for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){ |
| 106173 | 106357 | CollSeq *pColl; |
| 106174 | 106358 | pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr); |
| @@ -107186,10 +107370,70 @@ | ||
| 107186 | 107370 | Parse *pParse, /* Parsing context */ |
| 107187 | 107371 | Select *p, /* The right-most of SELECTs to be coded */ |
| 107188 | 107372 | SelectDest *pDest /* What to do with query results */ |
| 107189 | 107373 | ); |
| 107190 | 107374 | |
| 107375 | +/* | |
| 107376 | +** Error message for when two or more terms of a compound select have different | |
| 107377 | +** size result sets. | |
| 107378 | +*/ | |
| 107379 | +static void selectWrongNumTermsError(Parse *pParse, Select *p){ | |
| 107380 | + if( p->selFlags & SF_Values ){ | |
| 107381 | + sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); | |
| 107382 | + }else{ | |
| 107383 | + sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" | |
| 107384 | + " do not have the same number of result columns", selectOpName(p->op)); | |
| 107385 | + } | |
| 107386 | +} | |
| 107387 | + | |
| 107388 | +/* | |
| 107389 | +** Handle the special case of a compound-select that originates from a | |
| 107390 | +** VALUES clause. By handling this as a special case, we avoid deep | |
| 107391 | +** recursion, and thus do not need to enforce the SQLITE_LIMIT_COMPOUND_SELECT | |
| 107392 | +** on a VALUES clause. | |
| 107393 | +** | |
| 107394 | +** Because the Select object originates from a VALUES clause: | |
| 107395 | +** (1) It has no LIMIT or OFFSET | |
| 107396 | +** (2) All terms are UNION ALL | |
| 107397 | +** (3) There is no ORDER BY clause | |
| 107398 | +*/ | |
| 107399 | +static int multiSelectValues( | |
| 107400 | + Parse *pParse, /* Parsing context */ | |
| 107401 | + Select *p, /* The right-most of SELECTs to be coded */ | |
| 107402 | + SelectDest *pDest /* What to do with query results */ | |
| 107403 | +){ | |
| 107404 | + Select *pPrior; | |
| 107405 | + int nExpr = p->pEList->nExpr; | |
| 107406 | + int nRow = 1; | |
| 107407 | + int rc = 0; | |
| 107408 | + assert( p->pNext==0 ); | |
| 107409 | + assert( p->selFlags & SF_AllValues ); | |
| 107410 | + do{ | |
| 107411 | + assert( p->selFlags & SF_Values ); | |
| 107412 | + assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) ); | |
| 107413 | + assert( p->pLimit==0 ); | |
| 107414 | + assert( p->pOffset==0 ); | |
| 107415 | + if( p->pEList->nExpr!=nExpr ){ | |
| 107416 | + selectWrongNumTermsError(pParse, p); | |
| 107417 | + return 1; | |
| 107418 | + } | |
| 107419 | + if( p->pPrior==0 ) break; | |
| 107420 | + assert( p->pPrior->pNext==p ); | |
| 107421 | + p = p->pPrior; | |
| 107422 | + nRow++; | |
| 107423 | + }while(1); | |
| 107424 | + while( p ){ | |
| 107425 | + pPrior = p->pPrior; | |
| 107426 | + p->pPrior = 0; | |
| 107427 | + rc = sqlite3Select(pParse, p, pDest); | |
| 107428 | + p->pPrior = pPrior; | |
| 107429 | + if( rc ) break; | |
| 107430 | + p->nSelectRow = nRow; | |
| 107431 | + p = p->pNext; | |
| 107432 | + } | |
| 107433 | + return rc; | |
| 107434 | +} | |
| 107191 | 107435 | |
| 107192 | 107436 | /* |
| 107193 | 107437 | ** This routine is called to process a compound query form from |
| 107194 | 107438 | ** two or more separate queries using UNION, UNION ALL, EXCEPT, or |
| 107195 | 107439 | ** INTERSECT |
| @@ -107266,22 +107510,24 @@ | ||
| 107266 | 107510 | assert( p->pEList ); |
| 107267 | 107511 | sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr); |
| 107268 | 107512 | sqlite3VdbeChangeP5(v, BTREE_UNORDERED); |
| 107269 | 107513 | dest.eDest = SRT_Table; |
| 107270 | 107514 | } |
| 107515 | + | |
| 107516 | + /* Special handling for a compound-select that originates as a VALUES clause. | |
| 107517 | + */ | |
| 107518 | + if( p->selFlags & SF_AllValues ){ | |
| 107519 | + rc = multiSelectValues(pParse, p, &dest); | |
| 107520 | + goto multi_select_end; | |
| 107521 | + } | |
| 107271 | 107522 | |
| 107272 | 107523 | /* Make sure all SELECTs in the statement have the same number of elements |
| 107273 | 107524 | ** in their result sets. |
| 107274 | 107525 | */ |
| 107275 | 107526 | assert( p->pEList && pPrior->pEList ); |
| 107276 | 107527 | if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ |
| 107277 | - if( p->selFlags & SF_Values ){ | |
| 107278 | - sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); | |
| 107279 | - }else{ | |
| 107280 | - sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" | |
| 107281 | - " do not have the same number of result columns", selectOpName(p->op)); | |
| 107282 | - } | |
| 107528 | + selectWrongNumTermsError(pParse, p); | |
| 107283 | 107529 | rc = 1; |
| 107284 | 107530 | goto multi_select_end; |
| 107285 | 107531 | } |
| 107286 | 107532 | |
| 107287 | 107533 | #ifndef SQLITE_OMIT_CTE |
| @@ -109163,11 +109409,13 @@ | ||
| 109163 | 109409 | if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){ |
| 109164 | 109410 | return WRC_Prune; |
| 109165 | 109411 | } |
| 109166 | 109412 | pTabList = p->pSrc; |
| 109167 | 109413 | pEList = p->pEList; |
| 109168 | - sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); | |
| 109414 | + if( pWalker->xSelectCallback2==selectPopWith ){ | |
| 109415 | + sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); | |
| 109416 | + } | |
| 109169 | 109417 | |
| 109170 | 109418 | /* Make sure cursor numbers have been assigned to all entries in |
| 109171 | 109419 | ** the FROM clause of the SELECT statement. |
| 109172 | 109420 | */ |
| 109173 | 109421 | sqlite3SrcListAssignCursors(pParse, pTabList); |
| @@ -109454,11 +109702,13 @@ | ||
| 109454 | 109702 | if( pParse->hasCompound ){ |
| 109455 | 109703 | w.xSelectCallback = convertCompoundSelectToSubquery; |
| 109456 | 109704 | sqlite3WalkSelect(&w, pSelect); |
| 109457 | 109705 | } |
| 109458 | 109706 | w.xSelectCallback = selectExpander; |
| 109459 | - w.xSelectCallback2 = selectPopWith; | |
| 109707 | + if( (pSelect->selFlags & SF_AllValues)==0 ){ | |
| 109708 | + w.xSelectCallback2 = selectPopWith; | |
| 109709 | + } | |
| 109460 | 109710 | sqlite3WalkSelect(&w, pSelect); |
| 109461 | 109711 | } |
| 109462 | 109712 | |
| 109463 | 109713 | |
| 109464 | 109714 | #ifndef SQLITE_OMIT_SUBQUERY |
| @@ -109968,11 +110218,11 @@ | ||
| 109968 | 110218 | ** we figure out that the sorting index is not needed. The addrSortIndex |
| 109969 | 110219 | ** variable is used to facilitate that change. |
| 109970 | 110220 | */ |
| 109971 | 110221 | if( sSort.pOrderBy ){ |
| 109972 | 110222 | KeyInfo *pKeyInfo; |
| 109973 | - pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, 0); | |
| 110223 | + pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr); | |
| 109974 | 110224 | sSort.iECursor = pParse->nTab++; |
| 109975 | 110225 | sSort.addrSortIndex = |
| 109976 | 110226 | sqlite3VdbeAddOp4(v, OP_OpenEphemeral, |
| 109977 | 110227 | sSort.iECursor, sSort.pOrderBy->nExpr+1+pEList->nExpr, 0, |
| 109978 | 110228 | (char*)pKeyInfo, P4_KEYINFO |
| @@ -110142,11 +110392,11 @@ | ||
| 110142 | 110392 | ** implement it. Allocate that sorting index now. If it turns out |
| 110143 | 110393 | ** that we do not need it after all, the OP_SorterOpen instruction |
| 110144 | 110394 | ** will be converted into a Noop. |
| 110145 | 110395 | */ |
| 110146 | 110396 | sAggInfo.sortingIdx = pParse->nTab++; |
| 110147 | - pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, 0); | |
| 110397 | + pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn); | |
| 110148 | 110398 | addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, |
| 110149 | 110399 | sAggInfo.sortingIdx, sAggInfo.nSortingColumn, |
| 110150 | 110400 | 0, (char*)pKeyInfo, P4_KEYINFO); |
| 110151 | 110401 | |
| 110152 | 110402 | /* Initialize memory locations used by GROUP BY aggregate processing |
| @@ -110756,11 +111006,11 @@ | ||
| 110756 | 111006 | ){ |
| 110757 | 111007 | int rc; |
| 110758 | 111008 | TabResult res; |
| 110759 | 111009 | |
| 110760 | 111010 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 110761 | - if( pazResult==0 ) return SQLITE_MISUSE_BKPT; | |
| 111011 | + if( !sqlite3SafetyCheckOk(db) || pazResult==0 ) return SQLITE_MISUSE_BKPT; | |
| 110762 | 111012 | #endif |
| 110763 | 111013 | *pazResult = 0; |
| 110764 | 111014 | if( pnColumn ) *pnColumn = 0; |
| 110765 | 111015 | if( pnRow ) *pnRow = 0; |
| 110766 | 111016 | if( pzErrMsg ) *pzErrMsg = 0; |
| @@ -118626,11 +118876,10 @@ | ||
| 118626 | 118876 | sqlite3_free(p->u.vtab.idxStr); |
| 118627 | 118877 | p->u.vtab.needFree = 0; |
| 118628 | 118878 | p->u.vtab.idxStr = 0; |
| 118629 | 118879 | }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){ |
| 118630 | 118880 | sqlite3DbFree(db, p->u.btree.pIndex->zColAff); |
| 118631 | - sqlite3KeyInfoUnref(p->u.btree.pIndex->pKeyInfo); | |
| 118632 | 118881 | sqlite3DbFree(db, p->u.btree.pIndex); |
| 118633 | 118882 | p->u.btree.pIndex = 0; |
| 118634 | 118883 | } |
| 118635 | 118884 | } |
| 118636 | 118885 | } |
| @@ -123783,17 +124032,23 @@ | ||
| 123783 | 124032 | Select *p = yymsp[0].minor.yy3, *pNext, *pLoop; |
| 123784 | 124033 | if( p ){ |
| 123785 | 124034 | int cnt = 0, mxSelect; |
| 123786 | 124035 | p->pWith = yymsp[-1].minor.yy59; |
| 123787 | 124036 | if( p->pPrior ){ |
| 124037 | + u16 allValues = SF_Values; | |
| 123788 | 124038 | pNext = 0; |
| 123789 | 124039 | for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ |
| 123790 | 124040 | pLoop->pNext = pNext; |
| 123791 | 124041 | pLoop->selFlags |= SF_Compound; |
| 124042 | + allValues &= pLoop->selFlags; | |
| 123792 | 124043 | } |
| 123793 | - mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; | |
| 123794 | - if( mxSelect && cnt>mxSelect ){ | |
| 124044 | + if( allValues ){ | |
| 124045 | + p->selFlags |= SF_AllValues; | |
| 124046 | + }else if( | |
| 124047 | + (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 | |
| 124048 | + && cnt>mxSelect | |
| 124049 | + ){ | |
| 123795 | 124050 | sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); |
| 123796 | 124051 | } |
| 123797 | 124052 | } |
| 123798 | 124053 | }else{ |
| 123799 | 124054 | sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); |
| @@ -125633,10 +125888,13 @@ | ||
| 125633 | 125888 | u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */ |
| 125634 | 125889 | sqlite3 *db = pParse->db; /* The database connection */ |
| 125635 | 125890 | int mxSqlLen; /* Max length of an SQL string */ |
| 125636 | 125891 | |
| 125637 | 125892 | |
| 125893 | +#ifdef SQLITE_ENABLE_API_ARMOR | |
| 125894 | + if( zSql==0 || pzErrMsg==0 ) return SQLITE_MISUSE_BKPT; | |
| 125895 | +#endif | |
| 125638 | 125896 | mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; |
| 125639 | 125897 | if( db->nVdbeActive==0 ){ |
| 125640 | 125898 | db->u1.isInterrupted = 0; |
| 125641 | 125899 | } |
| 125642 | 125900 | pParse->rc = SQLITE_OK; |
| @@ -125871,17 +126129,10 @@ | ||
| 125871 | 126129 | */ |
| 125872 | 126130 | SQLITE_API int sqlite3_complete(const char *zSql){ |
| 125873 | 126131 | u8 state = 0; /* Current state, using numbers defined in header comment */ |
| 125874 | 126132 | u8 token; /* Value of the next token */ |
| 125875 | 126133 | |
| 125876 | -#ifdef SQLITE_ENABLE_API_ARMOR | |
| 125877 | - if( zSql==0 ){ | |
| 125878 | - (void)SQLITE_MISUSE_BKPT; | |
| 125879 | - return 0; | |
| 125880 | - } | |
| 125881 | -#endif | |
| 125882 | - | |
| 125883 | 126134 | #ifndef SQLITE_OMIT_TRIGGER |
| 125884 | 126135 | /* A complex statement machine used to detect the end of a CREATE TRIGGER |
| 125885 | 126136 | ** statement. This is the normal case. |
| 125886 | 126137 | */ |
| 125887 | 126138 | static const u8 trans[8][8] = { |
| @@ -125906,10 +126157,17 @@ | ||
| 125906 | 126157 | /* 0 INVALID: */ { 1, 0, 2, }, |
| 125907 | 126158 | /* 1 START: */ { 1, 1, 2, }, |
| 125908 | 126159 | /* 2 NORMAL: */ { 1, 2, 2, }, |
| 125909 | 126160 | }; |
| 125910 | 126161 | #endif /* SQLITE_OMIT_TRIGGER */ |
| 126162 | + | |
| 126163 | +#ifdef SQLITE_ENABLE_API_ARMOR | |
| 126164 | + if( zSql==0 ){ | |
| 126165 | + (void)SQLITE_MISUSE_BKPT; | |
| 126166 | + return 0; | |
| 126167 | + } | |
| 126168 | +#endif | |
| 125911 | 126169 | |
| 125912 | 126170 | while( *zSql ){ |
| 125913 | 126171 | switch( *zSql ){ |
| 125914 | 126172 | case ';': { /* A semicolon */ |
| 125915 | 126173 | token = tkSEMI; |
| @@ -126208,11 +126466,11 @@ | ||
| 126208 | 126466 | ** If the following function pointer is not NULL and if |
| 126209 | 126467 | ** SQLITE_ENABLE_IOTRACE is enabled, then messages describing |
| 126210 | 126468 | ** I/O active are written using this function. These messages |
| 126211 | 126469 | ** are intended for debugging activity only. |
| 126212 | 126470 | */ |
| 126213 | -SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*, ...) = 0; | |
| 126471 | +/* not-private */ void (*sqlite3IoTrace)(const char*, ...) = 0; | |
| 126214 | 126472 | #endif |
| 126215 | 126473 | |
| 126216 | 126474 | /* |
| 126217 | 126475 | ** If the following global variable points to a string which is the |
| 126218 | 126476 | ** name of a directory, then that directory will be used to store |
| @@ -126417,10 +126675,17 @@ | ||
| 126417 | 126675 | ** routine is not threadsafe. But it is safe to invoke this routine |
| 126418 | 126676 | ** on when SQLite is already shut down. If SQLite is already shut down |
| 126419 | 126677 | ** when this routine is invoked, then this routine is a harmless no-op. |
| 126420 | 126678 | */ |
| 126421 | 126679 | SQLITE_API int sqlite3_shutdown(void){ |
| 126680 | +#ifdef SQLITE_OMIT_WSD | |
| 126681 | + int rc = sqlite3_wsd_init(4096, 24); | |
| 126682 | + if( rc!=SQLITE_OK ){ | |
| 126683 | + return rc; | |
| 126684 | + } | |
| 126685 | +#endif | |
| 126686 | + | |
| 126422 | 126687 | if( sqlite3GlobalConfig.isInit ){ |
| 126423 | 126688 | #ifdef SQLITE_EXTRA_SHUTDOWN |
| 126424 | 126689 | void SQLITE_EXTRA_SHUTDOWN(void); |
| 126425 | 126690 | SQLITE_EXTRA_SHUTDOWN(); |
| 126426 | 126691 | #endif |
| @@ -126732,10 +126997,15 @@ | ||
| 126732 | 126997 | ** heap. */ |
| 126733 | 126998 | sqlite3GlobalConfig.nHeap = va_arg(ap, int); |
| 126734 | 126999 | break; |
| 126735 | 127000 | } |
| 126736 | 127001 | #endif |
| 127002 | + | |
| 127003 | + case SQLITE_CONFIG_PMASZ: { | |
| 127004 | + sqlite3GlobalConfig.szPma = va_arg(ap, unsigned int); | |
| 127005 | + break; | |
| 127006 | + } | |
| 126737 | 127007 | |
| 126738 | 127008 | default: { |
| 126739 | 127009 | rc = SQLITE_ERROR; |
| 126740 | 127010 | break; |
| 126741 | 127011 | } |
| @@ -127178,20 +127448,10 @@ | ||
| 127178 | 127448 | |
| 127179 | 127449 | /* Close all database connections */ |
| 127180 | 127450 | for(j=0; j<db->nDb; j++){ |
| 127181 | 127451 | struct Db *pDb = &db->aDb[j]; |
| 127182 | 127452 | if( pDb->pBt ){ |
| 127183 | - if( pDb->pSchema ){ | |
| 127184 | - /* Must clear the KeyInfo cache. See ticket [e4a18565a36884b00edf] */ | |
| 127185 | - sqlite3BtreeEnter(pDb->pBt); | |
| 127186 | - for(i=sqliteHashFirst(&pDb->pSchema->idxHash); i; i=sqliteHashNext(i)){ | |
| 127187 | - Index *pIdx = sqliteHashData(i); | |
| 127188 | - sqlite3KeyInfoUnref(pIdx->pKeyInfo); | |
| 127189 | - pIdx->pKeyInfo = 0; | |
| 127190 | - } | |
| 127191 | - sqlite3BtreeLeave(pDb->pBt); | |
| 127192 | - } | |
| 127193 | 127453 | sqlite3BtreeClose(pDb->pBt); |
| 127194 | 127454 | pDb->pBt = 0; |
| 127195 | 127455 | if( j!=1 ){ |
| 127196 | 127456 | pDb->pSchema = 0; |
| 127197 | 127457 | } |
| @@ -127494,11 +127754,11 @@ | ||
| 127494 | 127754 | */ |
| 127495 | 127755 | static int sqliteDefaultBusyCallback( |
| 127496 | 127756 | void *ptr, /* Database connection */ |
| 127497 | 127757 | int count /* Number of times table has been busy */ |
| 127498 | 127758 | ){ |
| 127499 | -#if SQLITE_OS_WIN || (defined(HAVE_USLEEP) && HAVE_USLEEP) | |
| 127759 | +#if SQLITE_OS_WIN || HAVE_USLEEP | |
| 127500 | 127760 | static const u8 delays[] = |
| 127501 | 127761 | { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; |
| 127502 | 127762 | static const u8 totals[] = |
| 127503 | 127763 | { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 }; |
| 127504 | 127764 | # define NDELAY ArraySize(delays) |
| @@ -128110,10 +128370,11 @@ | ||
| 128110 | 128370 | } |
| 128111 | 128371 | if( iDb<0 ){ |
| 128112 | 128372 | rc = SQLITE_ERROR; |
| 128113 | 128373 | sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb); |
| 128114 | 128374 | }else{ |
| 128375 | + db->busyHandler.nBusy = 0; | |
| 128115 | 128376 | rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); |
| 128116 | 128377 | sqlite3Error(db, rc); |
| 128117 | 128378 | } |
| 128118 | 128379 | rc = sqlite3ApiExit(db, rc); |
| 128119 | 128380 | sqlite3_mutex_leave(db->mutex); |
| @@ -128315,36 +128576,10 @@ | ||
| 128315 | 128576 | */ |
| 128316 | 128577 | SQLITE_API const char *sqlite3_errstr(int rc){ |
| 128317 | 128578 | return sqlite3ErrStr(rc); |
| 128318 | 128579 | } |
| 128319 | 128580 | |
| 128320 | -/* | |
| 128321 | -** Invalidate all cached KeyInfo objects for database connection "db" | |
| 128322 | -*/ | |
| 128323 | -static void invalidateCachedKeyInfo(sqlite3 *db){ | |
| 128324 | - Db *pDb; /* A single database */ | |
| 128325 | - int iDb; /* The database index number */ | |
| 128326 | - HashElem *k; /* For looping over tables in pDb */ | |
| 128327 | - Table *pTab; /* A table in the database */ | |
| 128328 | - Index *pIdx; /* Each index */ | |
| 128329 | - | |
| 128330 | - for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){ | |
| 128331 | - if( pDb->pBt==0 ) continue; | |
| 128332 | - sqlite3BtreeEnter(pDb->pBt); | |
| 128333 | - for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ | |
| 128334 | - pTab = (Table*)sqliteHashData(k); | |
| 128335 | - for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ | |
| 128336 | - if( pIdx->pKeyInfo && pIdx->pKeyInfo->db==db ){ | |
| 128337 | - sqlite3KeyInfoUnref(pIdx->pKeyInfo); | |
| 128338 | - pIdx->pKeyInfo = 0; | |
| 128339 | - } | |
| 128340 | - } | |
| 128341 | - } | |
| 128342 | - sqlite3BtreeLeave(pDb->pBt); | |
| 128343 | - } | |
| 128344 | -} | |
| 128345 | - | |
| 128346 | 128581 | /* |
| 128347 | 128582 | ** Create a new collating function for database "db". The name is zName |
| 128348 | 128583 | ** and the encoding is enc. |
| 128349 | 128584 | */ |
| 128350 | 128585 | static int createCollation( |
| @@ -128384,11 +128619,10 @@ | ||
| 128384 | 128619 | sqlite3ErrorWithMsg(db, SQLITE_BUSY, |
| 128385 | 128620 | "unable to delete/modify collation sequence due to active statements"); |
| 128386 | 128621 | return SQLITE_BUSY; |
| 128387 | 128622 | } |
| 128388 | 128623 | sqlite3ExpirePreparedStatements(db); |
| 128389 | - invalidateCachedKeyInfo(db); | |
| 128390 | 128624 | |
| 128391 | 128625 | /* If collation sequence pColl was created directly by a call to |
| 128392 | 128626 | ** sqlite3_create_collation, and not generated by synthCollSeq(), |
| 128393 | 128627 | ** then any copies made by synthCollSeq() need to be invalidated. |
| 128394 | 128628 | ** Also, collation destructor - CollSeq.xDel() - function may need |
| @@ -128941,10 +129175,11 @@ | ||
| 128941 | 129175 | sqlite3Error(db, rc); |
| 128942 | 129176 | goto opendb_out; |
| 128943 | 129177 | } |
| 128944 | 129178 | sqlite3BtreeEnter(db->aDb[0].pBt); |
| 128945 | 129179 | db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); |
| 129180 | + if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db); | |
| 128946 | 129181 | sqlite3BtreeLeave(db->aDb[0].pBt); |
| 128947 | 129182 | db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); |
| 128948 | 129183 | |
| 128949 | 129184 | /* The default safety_level for the main database is 'full'; for the temp |
| 128950 | 129185 | ** database it is 'NONE'. This matches the pager layer defaults. |
| @@ -129099,11 +129334,11 @@ | ||
| 129099 | 129334 | if( zFilename8 ){ |
| 129100 | 129335 | rc = openDatabase(zFilename8, ppDb, |
| 129101 | 129336 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); |
| 129102 | 129337 | assert( *ppDb || rc==SQLITE_NOMEM ); |
| 129103 | 129338 | if( rc==SQLITE_OK && !DbHasProperty(*ppDb, 0, DB_SchemaLoaded) ){ |
| 129104 | - ENC(*ppDb) = SQLITE_UTF16NATIVE; | |
| 129339 | + SCHEMA_ENC(*ppDb) = ENC(*ppDb) = SQLITE_UTF16NATIVE; | |
| 129105 | 129340 | } |
| 129106 | 129341 | }else{ |
| 129107 | 129342 | rc = SQLITE_NOMEM; |
| 129108 | 129343 | } |
| 129109 | 129344 | sqlite3ValueFree(pVal); |
| @@ -129310,11 +129545,11 @@ | ||
| 129310 | 129545 | ){ |
| 129311 | 129546 | int rc; |
| 129312 | 129547 | char *zErrMsg = 0; |
| 129313 | 129548 | Table *pTab = 0; |
| 129314 | 129549 | Column *pCol = 0; |
| 129315 | - int iCol; | |
| 129550 | + int iCol = 0; | |
| 129316 | 129551 | |
| 129317 | 129552 | char const *zDataType = 0; |
| 129318 | 129553 | char const *zCollSeq = 0; |
| 129319 | 129554 | int notnull = 0; |
| 129320 | 129555 | int primarykey = 0; |
| @@ -129841,32 +130076,34 @@ | ||
| 129841 | 130076 | /* |
| 129842 | 130077 | ** Return the filename of the database associated with a database |
| 129843 | 130078 | ** connection. |
| 129844 | 130079 | */ |
| 129845 | 130080 | SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ |
| 130081 | + Btree *pBt; | |
| 129846 | 130082 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 129847 | 130083 | if( !sqlite3SafetyCheckOk(db) ){ |
| 129848 | 130084 | (void)SQLITE_MISUSE_BKPT; |
| 129849 | 130085 | return 0; |
| 129850 | 130086 | } |
| 129851 | 130087 | #endif |
| 129852 | - Btree *pBt = sqlite3DbNameToBtree(db, zDbName); | |
| 130088 | + pBt = sqlite3DbNameToBtree(db, zDbName); | |
| 129853 | 130089 | return pBt ? sqlite3BtreeGetFilename(pBt) : 0; |
| 129854 | 130090 | } |
| 129855 | 130091 | |
| 129856 | 130092 | /* |
| 129857 | 130093 | ** Return 1 if database is read-only or 0 if read/write. Return -1 if |
| 129858 | 130094 | ** no such database exists. |
| 129859 | 130095 | */ |
| 129860 | 130096 | SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ |
| 130097 | + Btree *pBt; | |
| 129861 | 130098 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 129862 | 130099 | if( !sqlite3SafetyCheckOk(db) ){ |
| 129863 | 130100 | (void)SQLITE_MISUSE_BKPT; |
| 129864 | 130101 | return -1; |
| 129865 | 130102 | } |
| 129866 | 130103 | #endif |
| 129867 | - Btree *pBt = sqlite3DbNameToBtree(db, zDbName); | |
| 130104 | + pBt = sqlite3DbNameToBtree(db, zDbName); | |
| 129868 | 130105 | return pBt ? sqlite3BtreeIsReadonly(pBt) : -1; |
| 129869 | 130106 | } |
| 129870 | 130107 | |
| 129871 | 130108 | /************** End of main.c ************************************************/ |
| 129872 | 130109 | /************** Begin file notify.c ******************************************/ |
| @@ -132931,11 +133168,11 @@ | ||
| 132931 | 133168 | const char *zNode, /* Buffer containing segment interior node */ |
| 132932 | 133169 | int nNode, /* Size of buffer at zNode */ |
| 132933 | 133170 | sqlite3_int64 *piLeaf, /* Selected leaf node */ |
| 132934 | 133171 | sqlite3_int64 *piLeaf2 /* Selected leaf node */ |
| 132935 | 133172 | ){ |
| 132936 | - int rc; /* Return code */ | |
| 133173 | + int rc = SQLITE_OK; /* Return code */ | |
| 132937 | 133174 | int iHeight; /* Height of this node in tree */ |
| 132938 | 133175 | |
| 132939 | 133176 | assert( piLeaf || piLeaf2 ); |
| 132940 | 133177 | |
| 132941 | 133178 | fts3GetVarint32(zNode, &iHeight); |
| @@ -132942,11 +133179,11 @@ | ||
| 132942 | 133179 | rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); |
| 132943 | 133180 | assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) ); |
| 132944 | 133181 | |
| 132945 | 133182 | if( rc==SQLITE_OK && iHeight>1 ){ |
| 132946 | 133183 | char *zBlob = 0; /* Blob read from %_segments table */ |
| 132947 | - int nBlob; /* Size of zBlob in bytes */ | |
| 133184 | + int nBlob = 0; /* Size of zBlob in bytes */ | |
| 132948 | 133185 | |
| 132949 | 133186 | if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ |
| 132950 | 133187 | rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0); |
| 132951 | 133188 | if( rc==SQLITE_OK ){ |
| 132952 | 133189 | rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0); |
| @@ -134164,11 +134401,11 @@ | ||
| 134164 | 134401 | int idxNum, /* Strategy index */ |
| 134165 | 134402 | const char *idxStr, /* Unused */ |
| 134166 | 134403 | int nVal, /* Number of elements in apVal */ |
| 134167 | 134404 | sqlite3_value **apVal /* Arguments for the indexing scheme */ |
| 134168 | 134405 | ){ |
| 134169 | - int rc; | |
| 134406 | + int rc = SQLITE_OK; | |
| 134170 | 134407 | char *zSql; /* SQL statement used to access %_content */ |
| 134171 | 134408 | int eSearch; |
| 134172 | 134409 | Fts3Table *p = (Fts3Table *)pCursor->pVtab; |
| 134173 | 134410 | Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; |
| 134174 | 134411 | |
| @@ -140652,11 +140889,11 @@ | ||
| 140652 | 140889 | int argc, /* Number of elements in argv array */ |
| 140653 | 140890 | const char * const *argv, /* xCreate/xConnect argument array */ |
| 140654 | 140891 | sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ |
| 140655 | 140892 | char **pzErr /* OUT: sqlite3_malloc'd error message */ |
| 140656 | 140893 | ){ |
| 140657 | - Fts3tokTable *pTab; | |
| 140894 | + Fts3tokTable *pTab = 0; | |
| 140658 | 140895 | const sqlite3_tokenizer_module *pMod = 0; |
| 140659 | 140896 | sqlite3_tokenizer *pTok = 0; |
| 140660 | 140897 | int rc; |
| 140661 | 140898 | char **azDequote = 0; |
| 140662 | 140899 | int nDequote; |
| @@ -144027,12 +144264,12 @@ | ||
| 144027 | 144264 | } |
| 144028 | 144265 | rc = sqlite3_reset(pRange); |
| 144029 | 144266 | |
| 144030 | 144267 | if( bOk ){ |
| 144031 | 144268 | int iIdx = 0; |
| 144032 | - sqlite3_stmt *pUpdate1; | |
| 144033 | - sqlite3_stmt *pUpdate2; | |
| 144269 | + sqlite3_stmt *pUpdate1 = 0; | |
| 144270 | + sqlite3_stmt *pUpdate2 = 0; | |
| 144034 | 144271 | |
| 144035 | 144272 | if( rc==SQLITE_OK ){ |
| 144036 | 144273 | rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL_IDX, &pUpdate1, 0); |
| 144037 | 144274 | } |
| 144038 | 144275 | if( rc==SQLITE_OK ){ |
| @@ -149260,17 +149497,16 @@ | ||
| 149260 | 149497 | */ |
| 149261 | 149498 | static int readInt16(u8 *p){ |
| 149262 | 149499 | return (p[0]<<8) + p[1]; |
| 149263 | 149500 | } |
| 149264 | 149501 | static void readCoord(u8 *p, RtreeCoord *pCoord){ |
| 149265 | - u32 i = ( | |
| 149502 | + pCoord->u = ( | |
| 149266 | 149503 | (((u32)p[0]) << 24) + |
| 149267 | 149504 | (((u32)p[1]) << 16) + |
| 149268 | 149505 | (((u32)p[2]) << 8) + |
| 149269 | 149506 | (((u32)p[3]) << 0) |
| 149270 | 149507 | ); |
| 149271 | - *(u32 *)pCoord = i; | |
| 149272 | 149508 | } |
| 149273 | 149509 | static i64 readInt64(u8 *p){ |
| 149274 | 149510 | return ( |
| 149275 | 149511 | (((i64)p[0]) << 56) + |
| 149276 | 149512 | (((i64)p[1]) << 48) + |
| @@ -149295,11 +149531,11 @@ | ||
| 149295 | 149531 | } |
| 149296 | 149532 | static int writeCoord(u8 *p, RtreeCoord *pCoord){ |
| 149297 | 149533 | u32 i; |
| 149298 | 149534 | assert( sizeof(RtreeCoord)==4 ); |
| 149299 | 149535 | assert( sizeof(u32)==4 ); |
| 149300 | - i = *(u32 *)pCoord; | |
| 149536 | + i = pCoord->u; | |
| 149301 | 149537 | p[0] = (i>>24)&0xFF; |
| 149302 | 149538 | p[1] = (i>>16)&0xFF; |
| 149303 | 149539 | p[2] = (i>> 8)&0xFF; |
| 149304 | 149540 | p[3] = (i>> 0)&0xFF; |
| 149305 | 149541 | return 4; |
| @@ -149626,18 +149862,17 @@ | ||
| 149626 | 149862 | RtreeNode *pNode, /* The node containing the cell to be read */ |
| 149627 | 149863 | int iCell, /* Index of the cell within the node */ |
| 149628 | 149864 | RtreeCell *pCell /* OUT: Write the cell contents here */ |
| 149629 | 149865 | ){ |
| 149630 | 149866 | u8 *pData; |
| 149631 | - u8 *pEnd; | |
| 149632 | 149867 | RtreeCoord *pCoord; |
| 149868 | + int ii; | |
| 149633 | 149869 | pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); |
| 149634 | 149870 | pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell); |
| 149635 | - pEnd = pData + pRtree->nDim*8; | |
| 149636 | 149871 | pCoord = pCell->aCoord; |
| 149637 | - for(; pData<pEnd; pData+=4, pCoord++){ | |
| 149638 | - readCoord(pData, pCoord); | |
| 149872 | + for(ii=0; ii<pRtree->nDim*2; ii++){ | |
| 149873 | + readCoord(&pData[ii*4], &pCoord[ii]); | |
| 149639 | 149874 | } |
| 149640 | 149875 | } |
| 149641 | 149876 | |
| 149642 | 149877 | |
| 149643 | 149878 | /* Forward declaration for the function that does the work of |
| @@ -150073,11 +150308,11 @@ | ||
| 150073 | 150308 | } |
| 150074 | 150309 | i = pCur->nPoint++; |
| 150075 | 150310 | pNew = pCur->aPoint + i; |
| 150076 | 150311 | pNew->rScore = rScore; |
| 150077 | 150312 | pNew->iLevel = iLevel; |
| 150078 | - assert( iLevel>=0 && iLevel<=RTREE_MAX_DEPTH ); | |
| 150313 | + assert( iLevel<=RTREE_MAX_DEPTH ); | |
| 150079 | 150314 | while( i>0 ){ |
| 150080 | 150315 | RtreeSearchPoint *pParent; |
| 150081 | 150316 | j = (i-1)/2; |
| 150082 | 150317 | pParent = pCur->aPoint + j; |
| 150083 | 150318 | if( rtreeSearchPointCompare(pNew, pParent)>=0 ) break; |
| @@ -151696,10 +151931,12 @@ | ||
| 151696 | 151931 | RtreeCell cell; /* New cell to insert if nData>1 */ |
| 151697 | 151932 | int bHaveRowid = 0; /* Set to 1 after new rowid is determined */ |
| 151698 | 151933 | |
| 151699 | 151934 | rtreeReference(pRtree); |
| 151700 | 151935 | assert(nData>=1); |
| 151936 | + | |
| 151937 | + cell.iRowid = 0; /* Used only to suppress a compiler warning */ | |
| 151701 | 151938 | |
| 151702 | 151939 | /* Constraint handling. A write operation on an r-tree table may return |
| 151703 | 151940 | ** SQLITE_CONSTRAINT for two reasons: |
| 151704 | 151941 | ** |
| 151705 | 151942 | ** 1. A duplicate rowid value, or |
| 151706 | 151943 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1,8 +1,8 @@ | |
| 1 | /****************************************************************************** |
| 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | ** version 3.8.8. By combining all the individual C code files into this |
| 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | ** translation unit. |
| @@ -41,10 +41,57 @@ | |
| 41 | ** |
| 42 | */ |
| 43 | #ifndef _SQLITEINT_H_ |
| 44 | #define _SQLITEINT_H_ |
| 45 | |
| 46 | /* |
| 47 | ** These #defines should enable >2GB file support on POSIX if the |
| 48 | ** underlying operating system supports it. If the OS lacks |
| 49 | ** large file support, or if the OS is windows, these should be no-ops. |
| 50 | ** |
| @@ -229,13 +276,13 @@ | |
| 229 | ** |
| 230 | ** See also: [sqlite3_libversion()], |
| 231 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 232 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 233 | */ |
| 234 | #define SQLITE_VERSION "3.8.8" |
| 235 | #define SQLITE_VERSION_NUMBER 3008008 |
| 236 | #define SQLITE_SOURCE_ID "2014-12-10 04:58:43 3528f8dd39acace8eeb7337994c8617313f4b04b" |
| 237 | |
| 238 | /* |
| 239 | ** CAPI3REF: Run-Time Library Version Numbers |
| 240 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 241 | ** |
| @@ -323,11 +370,11 @@ | |
| 323 | ** This interface only reports on the compile-time mutex setting |
| 324 | ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with |
| 325 | ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but |
| 326 | ** can be fully or partially disabled using a call to [sqlite3_config()] |
| 327 | ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], |
| 328 | ** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the |
| 329 | ** sqlite3_threadsafe() function shows only the compile-time setting of |
| 330 | ** thread safety, not any run-time changes to that setting made by |
| 331 | ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() |
| 332 | ** is unchanged by calls to sqlite3_config().)^ |
| 333 | ** |
| @@ -1692,11 +1739,11 @@ | |
| 1692 | ** configuration option. |
| 1693 | ** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to |
| 1694 | ** 8-byte aligned |
| 1695 | ** memory, the size of each page buffer (sz), and the number of pages (N). |
| 1696 | ** The sz argument should be the size of the largest database page |
| 1697 | ** (a power of two between 512 and 32768) plus some extra bytes for each |
| 1698 | ** page header. ^The number of extra bytes needed by the page header |
| 1699 | ** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option |
| 1700 | ** to [sqlite3_config()]. |
| 1701 | ** ^It is harmless, apart from the wasted memory, |
| 1702 | ** for the sz parameter to be larger than necessary. The first |
| @@ -1872,10 +1919,21 @@ | |
| 1872 | ** <dd>^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which |
| 1873 | ** is a pointer to an integer and writes into that integer the number of extra |
| 1874 | ** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE]. |
| 1875 | ** The amount of extra space required can change depending on the compiler, |
| 1876 | ** target platform, and SQLite version. |
| 1877 | ** </dl> |
| 1878 | */ |
| 1879 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 1880 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 1881 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| @@ -1898,10 +1956,11 @@ | |
| 1898 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 1899 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 1900 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 1901 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 1902 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 1903 | |
| 1904 | /* |
| 1905 | ** CAPI3REF: Database Connection Configuration Options |
| 1906 | ** |
| 1907 | ** These constants are the available integer configuration options that |
| @@ -7307,16 +7366,14 @@ | |
| 7307 | |
| 7308 | /* |
| 7309 | ** CAPI3REF: Write-Ahead Log Commit Hook |
| 7310 | ** |
| 7311 | ** ^The [sqlite3_wal_hook()] function is used to register a callback that |
| 7312 | ** will be invoked each time a database connection commits data to a |
| 7313 | ** [write-ahead log] (i.e. whenever a transaction is committed in |
| 7314 | ** [journal_mode | journal_mode=WAL mode]). |
| 7315 | ** |
| 7316 | ** ^The callback is invoked by SQLite after the commit has taken place and |
| 7317 | ** the associated write-lock on the database released, so the implementation |
| 7318 | ** may read, write or [checkpoint] the database as required. |
| 7319 | ** |
| 7320 | ** ^The first parameter passed to the callback function when it is invoked |
| 7321 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when |
| 7322 | ** registering the callback. ^The second is a copy of the database handle. |
| @@ -7603,10 +7660,14 @@ | |
| 7603 | ** |
| 7604 | ** The following constants can be used for the T parameter to the |
| 7605 | ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a |
| 7606 | ** different metric for sqlite3_stmt_scanstatus() to return. |
| 7607 | ** |
| 7608 | ** <dl> |
| 7609 | ** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt> |
| 7610 | ** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be |
| 7611 | ** set to the total number of times that the X-th loop has run.</dd> |
| 7612 | ** |
| @@ -7648,11 +7709,18 @@ | |
| 7648 | #define SQLITE_SCANSTAT_SELECTID 5 |
| 7649 | |
| 7650 | /* |
| 7651 | ** CAPI3REF: Prepared Statement Scan Status |
| 7652 | ** |
| 7653 | ** Return status data for a single loop within query pStmt. |
| 7654 | ** |
| 7655 | ** The "iScanStatusOp" parameter determines which status information to return. |
| 7656 | ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior |
| 7657 | ** of this interface is undefined. |
| 7658 | ** ^The requested measurement is written into a variable pointed to by |
| @@ -7666,13 +7734,10 @@ | |
| 7666 | ** ^Statistics might not be available for all loops in all statements. ^In cases |
| 7667 | ** where there exist loops with no available statistics, this function behaves |
| 7668 | ** as if the loop did not exist - it returns non-zero and leave the variable |
| 7669 | ** that pOut points to unchanged. |
| 7670 | ** |
| 7671 | ** This API is only available if the library is built with pre-processor |
| 7672 | ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. |
| 7673 | ** |
| 7674 | ** See also: [sqlite3_stmt_scanstatus_reset()] |
| 7675 | */ |
| 7676 | SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( |
| 7677 | sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ |
| 7678 | int idx, /* Index of loop to report on */ |
| @@ -9100,11 +9165,11 @@ | |
| 9100 | #define _BTREE_H_ |
| 9101 | |
| 9102 | /* TODO: This definition is just included so other modules compile. It |
| 9103 | ** needs to be revisited. |
| 9104 | */ |
| 9105 | #define SQLITE_N_BTREE_META 10 |
| 9106 | |
| 9107 | /* |
| 9108 | ** If defined as non-zero, auto-vacuum is enabled by default. Otherwise |
| 9109 | ** it must be turned on for each database using "PRAGMA auto_vacuum = 1". |
| 9110 | */ |
| @@ -9215,10 +9280,15 @@ | |
| 9215 | ** offset = 36 + (idx * 4) |
| 9216 | ** |
| 9217 | ** For example, the free-page-count field is located at byte offset 36 of |
| 9218 | ** the database file header. The incr-vacuum-flag field is located at |
| 9219 | ** byte offset 64 (== 36+4*7). |
| 9220 | */ |
| 9221 | #define BTREE_FREE_PAGE_COUNT 0 |
| 9222 | #define BTREE_SCHEMA_VERSION 1 |
| 9223 | #define BTREE_FILE_FORMAT 2 |
| 9224 | #define BTREE_DEFAULT_CACHE_SIZE 3 |
| @@ -9225,10 +9295,11 @@ | |
| 9225 | #define BTREE_LARGEST_ROOT_PAGE 4 |
| 9226 | #define BTREE_TEXT_ENCODING 5 |
| 9227 | #define BTREE_USER_VERSION 6 |
| 9228 | #define BTREE_INCR_VACUUM 7 |
| 9229 | #define BTREE_APPLICATION_ID 8 |
| 9230 | |
| 9231 | /* |
| 9232 | ** Values that may be OR'd together to form the second argument of an |
| 9233 | ** sqlite3BtreeCursorHints() call. |
| 9234 | */ |
| @@ -10006,10 +10077,11 @@ | |
| 10006 | SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); |
| 10007 | #endif |
| 10008 | |
| 10009 | /* Functions used to query pager state and configuration. */ |
| 10010 | SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); |
| 10011 | SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); |
| 10012 | SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); |
| 10013 | SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); |
| 10014 | SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); |
| 10015 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); |
| @@ -10747,10 +10819,11 @@ | |
| 10747 | i64 szMmap; /* Default mmap_size setting */ |
| 10748 | unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ |
| 10749 | int errCode; /* Most recent error code (SQLITE_*) */ |
| 10750 | int errMask; /* & result codes with this before returning */ |
| 10751 | u16 dbOptFlags; /* Flags to enable/disable optimizations */ |
| 10752 | u8 autoCommit; /* The auto-commit flag. */ |
| 10753 | u8 temp_store; /* 1: file 2: memory 0: default */ |
| 10754 | u8 mallocFailed; /* True if we have seen a malloc failure */ |
| 10755 | u8 dfltLockMode; /* Default locking-mode for attached dbs */ |
| 10756 | signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ |
| @@ -10848,11 +10921,12 @@ | |
| 10848 | }; |
| 10849 | |
| 10850 | /* |
| 10851 | ** A macro to discover the encoding of a database. |
| 10852 | */ |
| 10853 | #define ENC(db) ((db)->aDb[0].pSchema->enc) |
| 10854 | |
| 10855 | /* |
| 10856 | ** Possible values for the sqlite3.flags. |
| 10857 | */ |
| 10858 | #define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ |
| @@ -11472,11 +11546,10 @@ | |
| 11472 | Index *pNext; /* The next index associated with the same table */ |
| 11473 | Schema *pSchema; /* Schema containing this index */ |
| 11474 | u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ |
| 11475 | char **azColl; /* Array of collation sequence names for index */ |
| 11476 | Expr *pPartIdxWhere; /* WHERE clause for partial indices */ |
| 11477 | KeyInfo *pKeyInfo; /* A KeyInfo object suitable for this index */ |
| 11478 | int tnum; /* DB Page containing root of this index */ |
| 11479 | LogEst szIdxRow; /* Estimated average row size in bytes */ |
| 11480 | u16 nKeyCol; /* Number of columns forming the key */ |
| 11481 | u16 nColumn; /* Number of columns stored in the index */ |
| 11482 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| @@ -12036,11 +12109,11 @@ | |
| 12036 | #define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */ |
| 12037 | #define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ |
| 12038 | #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ |
| 12039 | #define SF_Compound 0x0040 /* Part of a compound query */ |
| 12040 | #define SF_Values 0x0080 /* Synthesized from VALUES clause */ |
| 12041 | /* 0x0100 NOT USED */ |
| 12042 | #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ |
| 12043 | #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ |
| 12044 | #define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ |
| 12045 | #define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */ |
| 12046 | |
| @@ -12526,10 +12599,11 @@ | |
| 12526 | void *pPage; /* Page cache memory */ |
| 12527 | int szPage; /* Size of each page in pPage[] */ |
| 12528 | int nPage; /* Number of pages in pPage[] */ |
| 12529 | int mxParserStack; /* maximum depth of the parser stack */ |
| 12530 | int sharedCacheEnabled; /* true if shared-cache mode enabled */ |
| 12531 | /* The above might be initialized to non-zero. The following need to always |
| 12532 | ** initially be zero, however. */ |
| 12533 | int isInit; /* True after initialization has finished */ |
| 12534 | int inProgress; /* True while initialization in progress */ |
| 12535 | int isMutexInit; /* True after mutexes are initialized */ |
| @@ -12663,11 +12737,11 @@ | |
| 12663 | ** FTS4 is really an extension for FTS3. It is enabled using the |
| 12664 | ** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also call |
| 12665 | ** the SQLITE_ENABLE_FTS4 macro to serve as an alias for SQLITE_ENABLE_FTS3. |
| 12666 | */ |
| 12667 | #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) |
| 12668 | # define SQLITE_ENABLE_FTS3 |
| 12669 | #endif |
| 12670 | |
| 12671 | /* |
| 12672 | ** The ctype.h header is needed for non-ASCII systems. It is also |
| 12673 | ** needed by FTS3 when FTS3 is included in the amalgamation. |
| @@ -13448,11 +13522,11 @@ | |
| 13448 | ** print I/O tracing messages. |
| 13449 | */ |
| 13450 | #ifdef SQLITE_ENABLE_IOTRACE |
| 13451 | # define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; } |
| 13452 | SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe*); |
| 13453 | SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*,...); |
| 13454 | #else |
| 13455 | # define IOTRACE(A) |
| 13456 | # define sqlite3VdbeIOTraceSql(X) |
| 13457 | #endif |
| 13458 | |
| @@ -13661,10 +13735,17 @@ | |
| 13661 | */ |
| 13662 | #ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN |
| 13663 | # define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 |
| 13664 | #endif |
| 13665 | |
| 13666 | /* |
| 13667 | ** The following singleton contains the global configuration for |
| 13668 | ** the SQLite library. |
| 13669 | */ |
| 13670 | SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { |
| @@ -13691,10 +13772,11 @@ | |
| 13691 | (void*)0, /* pPage */ |
| 13692 | 0, /* szPage */ |
| 13693 | 0, /* nPage */ |
| 13694 | 0, /* mxParserStack */ |
| 13695 | 0, /* sharedCacheEnabled */ |
| 13696 | /* All the rest should always be initialized to zero */ |
| 13697 | 0, /* isInit */ |
| 13698 | 0, /* inProgress */ |
| 13699 | 0, /* isMutexInit */ |
| 13700 | 0, /* isMallocInit */ |
| @@ -13797,355 +13879,355 @@ | |
| 13797 | /* These macros are provided to "stringify" the value of the define |
| 13798 | ** for those options in which the value is meaningful. */ |
| 13799 | #define CTIMEOPT_VAL_(opt) #opt |
| 13800 | #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) |
| 13801 | |
| 13802 | #ifdef SQLITE_32BIT_ROWID |
| 13803 | "32BIT_ROWID", |
| 13804 | #endif |
| 13805 | #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC |
| 13806 | "4_BYTE_ALIGNED_MALLOC", |
| 13807 | #endif |
| 13808 | #ifdef SQLITE_CASE_SENSITIVE_LIKE |
| 13809 | "CASE_SENSITIVE_LIKE", |
| 13810 | #endif |
| 13811 | #ifdef SQLITE_CHECK_PAGES |
| 13812 | "CHECK_PAGES", |
| 13813 | #endif |
| 13814 | #ifdef SQLITE_COVERAGE_TEST |
| 13815 | "COVERAGE_TEST", |
| 13816 | #endif |
| 13817 | #ifdef SQLITE_DEBUG |
| 13818 | "DEBUG", |
| 13819 | #endif |
| 13820 | #ifdef SQLITE_DEFAULT_LOCKING_MODE |
| 13821 | "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), |
| 13822 | #endif |
| 13823 | #if defined(SQLITE_DEFAULT_MMAP_SIZE) && !defined(SQLITE_DEFAULT_MMAP_SIZE_xc) |
| 13824 | "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), |
| 13825 | #endif |
| 13826 | #ifdef SQLITE_DISABLE_DIRSYNC |
| 13827 | "DISABLE_DIRSYNC", |
| 13828 | #endif |
| 13829 | #ifdef SQLITE_DISABLE_LFS |
| 13830 | "DISABLE_LFS", |
| 13831 | #endif |
| 13832 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 13833 | "ENABLE_API_ARMOR", |
| 13834 | #endif |
| 13835 | #ifdef SQLITE_ENABLE_ATOMIC_WRITE |
| 13836 | "ENABLE_ATOMIC_WRITE", |
| 13837 | #endif |
| 13838 | #ifdef SQLITE_ENABLE_CEROD |
| 13839 | "ENABLE_CEROD", |
| 13840 | #endif |
| 13841 | #ifdef SQLITE_ENABLE_COLUMN_METADATA |
| 13842 | "ENABLE_COLUMN_METADATA", |
| 13843 | #endif |
| 13844 | #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT |
| 13845 | "ENABLE_EXPENSIVE_ASSERT", |
| 13846 | #endif |
| 13847 | #ifdef SQLITE_ENABLE_FTS1 |
| 13848 | "ENABLE_FTS1", |
| 13849 | #endif |
| 13850 | #ifdef SQLITE_ENABLE_FTS2 |
| 13851 | "ENABLE_FTS2", |
| 13852 | #endif |
| 13853 | #ifdef SQLITE_ENABLE_FTS3 |
| 13854 | "ENABLE_FTS3", |
| 13855 | #endif |
| 13856 | #ifdef SQLITE_ENABLE_FTS3_PARENTHESIS |
| 13857 | "ENABLE_FTS3_PARENTHESIS", |
| 13858 | #endif |
| 13859 | #ifdef SQLITE_ENABLE_FTS4 |
| 13860 | "ENABLE_FTS4", |
| 13861 | #endif |
| 13862 | #ifdef SQLITE_ENABLE_ICU |
| 13863 | "ENABLE_ICU", |
| 13864 | #endif |
| 13865 | #ifdef SQLITE_ENABLE_IOTRACE |
| 13866 | "ENABLE_IOTRACE", |
| 13867 | #endif |
| 13868 | #ifdef SQLITE_ENABLE_LOAD_EXTENSION |
| 13869 | "ENABLE_LOAD_EXTENSION", |
| 13870 | #endif |
| 13871 | #ifdef SQLITE_ENABLE_LOCKING_STYLE |
| 13872 | "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), |
| 13873 | #endif |
| 13874 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 13875 | "ENABLE_MEMORY_MANAGEMENT", |
| 13876 | #endif |
| 13877 | #ifdef SQLITE_ENABLE_MEMSYS3 |
| 13878 | "ENABLE_MEMSYS3", |
| 13879 | #endif |
| 13880 | #ifdef SQLITE_ENABLE_MEMSYS5 |
| 13881 | "ENABLE_MEMSYS5", |
| 13882 | #endif |
| 13883 | #ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK |
| 13884 | "ENABLE_OVERSIZE_CELL_CHECK", |
| 13885 | #endif |
| 13886 | #ifdef SQLITE_ENABLE_RTREE |
| 13887 | "ENABLE_RTREE", |
| 13888 | #endif |
| 13889 | #if defined(SQLITE_ENABLE_STAT4) |
| 13890 | "ENABLE_STAT4", |
| 13891 | #elif defined(SQLITE_ENABLE_STAT3) |
| 13892 | "ENABLE_STAT3", |
| 13893 | #endif |
| 13894 | #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY |
| 13895 | "ENABLE_UNLOCK_NOTIFY", |
| 13896 | #endif |
| 13897 | #ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT |
| 13898 | "ENABLE_UPDATE_DELETE_LIMIT", |
| 13899 | #endif |
| 13900 | #ifdef SQLITE_HAS_CODEC |
| 13901 | "HAS_CODEC", |
| 13902 | #endif |
| 13903 | #ifdef SQLITE_HAVE_ISNAN |
| 13904 | "HAVE_ISNAN", |
| 13905 | #endif |
| 13906 | #ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX |
| 13907 | "HOMEGROWN_RECURSIVE_MUTEX", |
| 13908 | #endif |
| 13909 | #ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS |
| 13910 | "IGNORE_AFP_LOCK_ERRORS", |
| 13911 | #endif |
| 13912 | #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS |
| 13913 | "IGNORE_FLOCK_LOCK_ERRORS", |
| 13914 | #endif |
| 13915 | #ifdef SQLITE_INT64_TYPE |
| 13916 | "INT64_TYPE", |
| 13917 | #endif |
| 13918 | #ifdef SQLITE_LOCK_TRACE |
| 13919 | "LOCK_TRACE", |
| 13920 | #endif |
| 13921 | #if defined(SQLITE_MAX_MMAP_SIZE) && !defined(SQLITE_MAX_MMAP_SIZE_xc) |
| 13922 | "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), |
| 13923 | #endif |
| 13924 | #ifdef SQLITE_MAX_SCHEMA_RETRY |
| 13925 | "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), |
| 13926 | #endif |
| 13927 | #ifdef SQLITE_MEMDEBUG |
| 13928 | "MEMDEBUG", |
| 13929 | #endif |
| 13930 | #ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT |
| 13931 | "MIXED_ENDIAN_64BIT_FLOAT", |
| 13932 | #endif |
| 13933 | #ifdef SQLITE_NO_SYNC |
| 13934 | "NO_SYNC", |
| 13935 | #endif |
| 13936 | #ifdef SQLITE_OMIT_ALTERTABLE |
| 13937 | "OMIT_ALTERTABLE", |
| 13938 | #endif |
| 13939 | #ifdef SQLITE_OMIT_ANALYZE |
| 13940 | "OMIT_ANALYZE", |
| 13941 | #endif |
| 13942 | #ifdef SQLITE_OMIT_ATTACH |
| 13943 | "OMIT_ATTACH", |
| 13944 | #endif |
| 13945 | #ifdef SQLITE_OMIT_AUTHORIZATION |
| 13946 | "OMIT_AUTHORIZATION", |
| 13947 | #endif |
| 13948 | #ifdef SQLITE_OMIT_AUTOINCREMENT |
| 13949 | "OMIT_AUTOINCREMENT", |
| 13950 | #endif |
| 13951 | #ifdef SQLITE_OMIT_AUTOINIT |
| 13952 | "OMIT_AUTOINIT", |
| 13953 | #endif |
| 13954 | #ifdef SQLITE_OMIT_AUTOMATIC_INDEX |
| 13955 | "OMIT_AUTOMATIC_INDEX", |
| 13956 | #endif |
| 13957 | #ifdef SQLITE_OMIT_AUTORESET |
| 13958 | "OMIT_AUTORESET", |
| 13959 | #endif |
| 13960 | #ifdef SQLITE_OMIT_AUTOVACUUM |
| 13961 | "OMIT_AUTOVACUUM", |
| 13962 | #endif |
| 13963 | #ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 13964 | "OMIT_BETWEEN_OPTIMIZATION", |
| 13965 | #endif |
| 13966 | #ifdef SQLITE_OMIT_BLOB_LITERAL |
| 13967 | "OMIT_BLOB_LITERAL", |
| 13968 | #endif |
| 13969 | #ifdef SQLITE_OMIT_BTREECOUNT |
| 13970 | "OMIT_BTREECOUNT", |
| 13971 | #endif |
| 13972 | #ifdef SQLITE_OMIT_BUILTIN_TEST |
| 13973 | "OMIT_BUILTIN_TEST", |
| 13974 | #endif |
| 13975 | #ifdef SQLITE_OMIT_CAST |
| 13976 | "OMIT_CAST", |
| 13977 | #endif |
| 13978 | #ifdef SQLITE_OMIT_CHECK |
| 13979 | "OMIT_CHECK", |
| 13980 | #endif |
| 13981 | #ifdef SQLITE_OMIT_COMPLETE |
| 13982 | "OMIT_COMPLETE", |
| 13983 | #endif |
| 13984 | #ifdef SQLITE_OMIT_COMPOUND_SELECT |
| 13985 | "OMIT_COMPOUND_SELECT", |
| 13986 | #endif |
| 13987 | #ifdef SQLITE_OMIT_CTE |
| 13988 | "OMIT_CTE", |
| 13989 | #endif |
| 13990 | #ifdef SQLITE_OMIT_DATETIME_FUNCS |
| 13991 | "OMIT_DATETIME_FUNCS", |
| 13992 | #endif |
| 13993 | #ifdef SQLITE_OMIT_DECLTYPE |
| 13994 | "OMIT_DECLTYPE", |
| 13995 | #endif |
| 13996 | #ifdef SQLITE_OMIT_DEPRECATED |
| 13997 | "OMIT_DEPRECATED", |
| 13998 | #endif |
| 13999 | #ifdef SQLITE_OMIT_DISKIO |
| 14000 | "OMIT_DISKIO", |
| 14001 | #endif |
| 14002 | #ifdef SQLITE_OMIT_EXPLAIN |
| 14003 | "OMIT_EXPLAIN", |
| 14004 | #endif |
| 14005 | #ifdef SQLITE_OMIT_FLAG_PRAGMAS |
| 14006 | "OMIT_FLAG_PRAGMAS", |
| 14007 | #endif |
| 14008 | #ifdef SQLITE_OMIT_FLOATING_POINT |
| 14009 | "OMIT_FLOATING_POINT", |
| 14010 | #endif |
| 14011 | #ifdef SQLITE_OMIT_FOREIGN_KEY |
| 14012 | "OMIT_FOREIGN_KEY", |
| 14013 | #endif |
| 14014 | #ifdef SQLITE_OMIT_GET_TABLE |
| 14015 | "OMIT_GET_TABLE", |
| 14016 | #endif |
| 14017 | #ifdef SQLITE_OMIT_INCRBLOB |
| 14018 | "OMIT_INCRBLOB", |
| 14019 | #endif |
| 14020 | #ifdef SQLITE_OMIT_INTEGRITY_CHECK |
| 14021 | "OMIT_INTEGRITY_CHECK", |
| 14022 | #endif |
| 14023 | #ifdef SQLITE_OMIT_LIKE_OPTIMIZATION |
| 14024 | "OMIT_LIKE_OPTIMIZATION", |
| 14025 | #endif |
| 14026 | #ifdef SQLITE_OMIT_LOAD_EXTENSION |
| 14027 | "OMIT_LOAD_EXTENSION", |
| 14028 | #endif |
| 14029 | #ifdef SQLITE_OMIT_LOCALTIME |
| 14030 | "OMIT_LOCALTIME", |
| 14031 | #endif |
| 14032 | #ifdef SQLITE_OMIT_LOOKASIDE |
| 14033 | "OMIT_LOOKASIDE", |
| 14034 | #endif |
| 14035 | #ifdef SQLITE_OMIT_MEMORYDB |
| 14036 | "OMIT_MEMORYDB", |
| 14037 | #endif |
| 14038 | #ifdef SQLITE_OMIT_OR_OPTIMIZATION |
| 14039 | "OMIT_OR_OPTIMIZATION", |
| 14040 | #endif |
| 14041 | #ifdef SQLITE_OMIT_PAGER_PRAGMAS |
| 14042 | "OMIT_PAGER_PRAGMAS", |
| 14043 | #endif |
| 14044 | #ifdef SQLITE_OMIT_PRAGMA |
| 14045 | "OMIT_PRAGMA", |
| 14046 | #endif |
| 14047 | #ifdef SQLITE_OMIT_PROGRESS_CALLBACK |
| 14048 | "OMIT_PROGRESS_CALLBACK", |
| 14049 | #endif |
| 14050 | #ifdef SQLITE_OMIT_QUICKBALANCE |
| 14051 | "OMIT_QUICKBALANCE", |
| 14052 | #endif |
| 14053 | #ifdef SQLITE_OMIT_REINDEX |
| 14054 | "OMIT_REINDEX", |
| 14055 | #endif |
| 14056 | #ifdef SQLITE_OMIT_SCHEMA_PRAGMAS |
| 14057 | "OMIT_SCHEMA_PRAGMAS", |
| 14058 | #endif |
| 14059 | #ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS |
| 14060 | "OMIT_SCHEMA_VERSION_PRAGMAS", |
| 14061 | #endif |
| 14062 | #ifdef SQLITE_OMIT_SHARED_CACHE |
| 14063 | "OMIT_SHARED_CACHE", |
| 14064 | #endif |
| 14065 | #ifdef SQLITE_OMIT_SUBQUERY |
| 14066 | "OMIT_SUBQUERY", |
| 14067 | #endif |
| 14068 | #ifdef SQLITE_OMIT_TCL_VARIABLE |
| 14069 | "OMIT_TCL_VARIABLE", |
| 14070 | #endif |
| 14071 | #ifdef SQLITE_OMIT_TEMPDB |
| 14072 | "OMIT_TEMPDB", |
| 14073 | #endif |
| 14074 | #ifdef SQLITE_OMIT_TRACE |
| 14075 | "OMIT_TRACE", |
| 14076 | #endif |
| 14077 | #ifdef SQLITE_OMIT_TRIGGER |
| 14078 | "OMIT_TRIGGER", |
| 14079 | #endif |
| 14080 | #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION |
| 14081 | "OMIT_TRUNCATE_OPTIMIZATION", |
| 14082 | #endif |
| 14083 | #ifdef SQLITE_OMIT_UTF16 |
| 14084 | "OMIT_UTF16", |
| 14085 | #endif |
| 14086 | #ifdef SQLITE_OMIT_VACUUM |
| 14087 | "OMIT_VACUUM", |
| 14088 | #endif |
| 14089 | #ifdef SQLITE_OMIT_VIEW |
| 14090 | "OMIT_VIEW", |
| 14091 | #endif |
| 14092 | #ifdef SQLITE_OMIT_VIRTUALTABLE |
| 14093 | "OMIT_VIRTUALTABLE", |
| 14094 | #endif |
| 14095 | #ifdef SQLITE_OMIT_WAL |
| 14096 | "OMIT_WAL", |
| 14097 | #endif |
| 14098 | #ifdef SQLITE_OMIT_WSD |
| 14099 | "OMIT_WSD", |
| 14100 | #endif |
| 14101 | #ifdef SQLITE_OMIT_XFER_OPT |
| 14102 | "OMIT_XFER_OPT", |
| 14103 | #endif |
| 14104 | #ifdef SQLITE_PERFORMANCE_TRACE |
| 14105 | "PERFORMANCE_TRACE", |
| 14106 | #endif |
| 14107 | #ifdef SQLITE_PROXY_DEBUG |
| 14108 | "PROXY_DEBUG", |
| 14109 | #endif |
| 14110 | #ifdef SQLITE_RTREE_INT_ONLY |
| 14111 | "RTREE_INT_ONLY", |
| 14112 | #endif |
| 14113 | #ifdef SQLITE_SECURE_DELETE |
| 14114 | "SECURE_DELETE", |
| 14115 | #endif |
| 14116 | #ifdef SQLITE_SMALL_STACK |
| 14117 | "SMALL_STACK", |
| 14118 | #endif |
| 14119 | #ifdef SQLITE_SOUNDEX |
| 14120 | "SOUNDEX", |
| 14121 | #endif |
| 14122 | #ifdef SQLITE_SYSTEM_MALLOC |
| 14123 | "SYSTEM_MALLOC", |
| 14124 | #endif |
| 14125 | #ifdef SQLITE_TCL |
| 14126 | "TCL", |
| 14127 | #endif |
| 14128 | #if defined(SQLITE_TEMP_STORE) && !defined(SQLITE_TEMP_STORE_xc) |
| 14129 | "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), |
| 14130 | #endif |
| 14131 | #ifdef SQLITE_TEST |
| 14132 | "TEST", |
| 14133 | #endif |
| 14134 | #if defined(SQLITE_THREADSAFE) |
| 14135 | "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), |
| 14136 | #endif |
| 14137 | #ifdef SQLITE_USE_ALLOCA |
| 14138 | "USE_ALLOCA", |
| 14139 | #endif |
| 14140 | #ifdef SQLITE_USER_AUTHENTICATION |
| 14141 | "USER_AUTHENTICATION", |
| 14142 | #endif |
| 14143 | #ifdef SQLITE_WIN32_MALLOC |
| 14144 | "WIN32_MALLOC", |
| 14145 | #endif |
| 14146 | #ifdef SQLITE_ZERO_MALLOC |
| 14147 | "ZERO_MALLOC" |
| 14148 | #endif |
| 14149 | }; |
| 14150 | |
| 14151 | /* |
| @@ -14156,11 +14238,11 @@ | |
| 14156 | ** is not required for a match. |
| 14157 | */ |
| 14158 | SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ |
| 14159 | int i, n; |
| 14160 | |
| 14161 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 14162 | if( zOptName==0 ){ |
| 14163 | (void)SQLITE_MISUSE_BKPT; |
| 14164 | return 0; |
| 14165 | } |
| 14166 | #endif |
| @@ -15384,12 +15466,13 @@ | |
| 15384 | ** |
| 15385 | ** If the user has not indicated to use localtime_r() or localtime_s() |
| 15386 | ** already, check for an MSVC build environment that provides |
| 15387 | ** localtime_s(). |
| 15388 | */ |
| 15389 | #if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \ |
| 15390 | defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) |
| 15391 | #define HAVE_LOCALTIME_S 1 |
| 15392 | #endif |
| 15393 | |
| 15394 | #ifndef SQLITE_OMIT_LOCALTIME |
| 15395 | /* |
| @@ -15405,12 +15488,11 @@ | |
| 15405 | ** library function localtime_r() is used to assist in the calculation of |
| 15406 | ** local time. |
| 15407 | */ |
| 15408 | static int osLocaltime(time_t *t, struct tm *pTm){ |
| 15409 | int rc; |
| 15410 | #if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \ |
| 15411 | && (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S) |
| 15412 | struct tm *pX; |
| 15413 | #if SQLITE_THREADSAFE>0 |
| 15414 | sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); |
| 15415 | #endif |
| 15416 | sqlite3_mutex_enter(mutex); |
| @@ -15423,11 +15505,11 @@ | |
| 15423 | rc = pX==0; |
| 15424 | #else |
| 15425 | #ifndef SQLITE_OMIT_BUILTIN_TEST |
| 15426 | if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; |
| 15427 | #endif |
| 15428 | #if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R |
| 15429 | rc = localtime_r(t, pTm)==0; |
| 15430 | #else |
| 15431 | rc = localtime_s(pTm, t); |
| 15432 | #endif /* HAVE_LOCALTIME_R */ |
| 15433 | #endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */ |
| @@ -15867,12 +15949,14 @@ | |
| 15867 | DateTime x; |
| 15868 | u64 n; |
| 15869 | size_t i,j; |
| 15870 | char *z; |
| 15871 | sqlite3 *db; |
| 15872 | const char *zFmt = (const char*)sqlite3_value_text(argv[0]); |
| 15873 | char zBuf[100]; |
| 15874 | if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; |
| 15875 | db = sqlite3_context_db_handle(context); |
| 15876 | for(i=0, n=1; zFmt[i]; i++, n++){ |
| 15877 | if( zFmt[i]=='%' ){ |
| 15878 | switch( zFmt[i+1] ){ |
| @@ -16062,11 +16146,11 @@ | |
| 16062 | UNUSED_PARAMETER(argv); |
| 16063 | |
| 16064 | iT = sqlite3StmtCurrentTime(context); |
| 16065 | if( iT<=0 ) return; |
| 16066 | t = iT/1000 - 10000*(sqlite3_int64)21086676; |
| 16067 | #ifdef HAVE_GMTIME_R |
| 16068 | pTm = gmtime_r(&t, &sNow); |
| 16069 | #else |
| 16070 | sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
| 16071 | pTm = gmtime(&t); |
| 16072 | if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); |
| @@ -16736,13 +16820,13 @@ | |
| 16736 | |
| 16737 | /* |
| 16738 | ** The malloc.h header file is needed for malloc_usable_size() function |
| 16739 | ** on some systems (e.g. Linux). |
| 16740 | */ |
| 16741 | #if defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE) |
| 16742 | # define SQLITE_USE_MALLOC_H |
| 16743 | # define SQLITE_USE_MALLOC_USABLE_SIZE |
| 16744 | /* |
| 16745 | ** The MSVCRT has malloc_usable_size(), but it is called _msize(). The |
| 16746 | ** use of _msize() is automatic, but can be disabled by compiling with |
| 16747 | ** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires |
| 16748 | ** the malloc.h header file. |
| @@ -19976,10 +20060,16 @@ | |
| 19976 | #endif |
| 19977 | } |
| 19978 | break; |
| 19979 | } |
| 19980 | default: { |
| 19981 | assert( iType-2 >= 0 ); |
| 19982 | assert( iType-2 < ArraySize(winMutex_staticMutexes) ); |
| 19983 | assert( winMutex_isInit==1 ); |
| 19984 | p = &winMutex_staticMutexes[iType-2]; |
| 19985 | #ifdef SQLITE_DEBUG |
| @@ -20971,21 +21061,10 @@ | |
| 20971 | ** This file contains code for a set of "printf"-like routines. These |
| 20972 | ** routines format strings much like the printf() from the standard C |
| 20973 | ** library, though the implementation here has enhancements to support |
| 20974 | ** SQLlite. |
| 20975 | */ |
| 20976 | |
| 20977 | /* |
| 20978 | ** If the strchrnul() library function is available, then set |
| 20979 | ** HAVE_STRCHRNUL. If that routine is not available, this module |
| 20980 | ** will supply its own. The built-in version is slower than |
| 20981 | ** the glibc version so the glibc version is definitely preferred. |
| 20982 | */ |
| 20983 | #if !defined(HAVE_STRCHRNUL) |
| 20984 | # define HAVE_STRCHRNUL 0 |
| 20985 | #endif |
| 20986 | |
| 20987 | |
| 20988 | /* |
| 20989 | ** Conversion types fall into various categories as defined by the |
| 20990 | ** following enumeration. |
| 20991 | */ |
| @@ -22280,10 +22359,12 @@ | |
| 22280 | ** single threaded systems. Nothing in SQLite requires multiple threads. |
| 22281 | ** This interface exists so that applications that want to take advantage |
| 22282 | ** of multiple cores can do so, while also allowing applications to stay |
| 22283 | ** single-threaded if desired. |
| 22284 | */ |
| 22285 | |
| 22286 | #if SQLITE_MAX_WORKER_THREADS>0 |
| 22287 | |
| 22288 | /********************************* Unix Pthreads ****************************/ |
| 22289 | #if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 |
| @@ -23066,11 +23147,11 @@ | |
| 23066 | ** This file contains functions for allocating memory, comparing |
| 23067 | ** strings, and stuff like that. |
| 23068 | ** |
| 23069 | */ |
| 23070 | /* #include <stdarg.h> */ |
| 23071 | #ifdef SQLITE_HAVE_ISNAN |
| 23072 | # include <math.h> |
| 23073 | #endif |
| 23074 | |
| 23075 | /* |
| 23076 | ** Routine needed to support the testcase() macro. |
| @@ -23107,11 +23188,11 @@ | |
| 23107 | ** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. |
| 23108 | ** Otherwise, we have our own implementation that works on most systems. |
| 23109 | */ |
| 23110 | SQLITE_PRIVATE int sqlite3IsNaN(double x){ |
| 23111 | int rc; /* The value return */ |
| 23112 | #if !defined(SQLITE_HAVE_ISNAN) |
| 23113 | /* |
| 23114 | ** Systems that support the isnan() library function should probably |
| 23115 | ** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have |
| 23116 | ** found that many systems do not have a working isnan() function so |
| 23117 | ** this implementation is provided as an alternative. |
| @@ -23137,13 +23218,13 @@ | |
| 23137 | # error SQLite will not work correctly with the -ffast-math option of GCC. |
| 23138 | #endif |
| 23139 | volatile double y = x; |
| 23140 | volatile double z = y; |
| 23141 | rc = (y!=z); |
| 23142 | #else /* if defined(SQLITE_HAVE_ISNAN) */ |
| 23143 | rc = isnan(x); |
| 23144 | #endif /* SQLITE_HAVE_ISNAN */ |
| 23145 | testcase( rc ); |
| 23146 | return rc; |
| 23147 | } |
| 23148 | #endif /* SQLITE_OMIT_FLOATING_POINT */ |
| 23149 | |
| @@ -28460,13 +28541,13 @@ | |
| 28460 | |
| 28461 | /* |
| 28462 | ** We do not trust systems to provide a working fdatasync(). Some do. |
| 28463 | ** Others do no. To be safe, we will stick with the (slightly slower) |
| 28464 | ** fsync(). If you know that your system does support fdatasync() correctly, |
| 28465 | ** then simply compile with -Dfdatasync=fdatasync |
| 28466 | */ |
| 28467 | #if !defined(fdatasync) |
| 28468 | # define fdatasync fsync |
| 28469 | #endif |
| 28470 | |
| 28471 | /* |
| 28472 | ** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not |
| @@ -28783,28 +28864,32 @@ | |
| 28783 | do{ |
| 28784 | err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); |
| 28785 | }while( err==EINTR ); |
| 28786 | if( err ) return SQLITE_IOERR_WRITE; |
| 28787 | #else |
| 28788 | /* If the OS does not have posix_fallocate(), fake it. First use |
| 28789 | ** ftruncate() to set the file size, then write a single byte to |
| 28790 | ** the last byte in each block within the extended region. This |
| 28791 | ** is the same technique used by glibc to implement posix_fallocate() |
| 28792 | ** on systems that do not have a real fallocate() system call. |
| 28793 | */ |
| 28794 | int nBlk = buf.st_blksize; /* File-system block size */ |
| 28795 | i64 iWrite; /* Next offset to write to */ |
| 28796 | |
| 28797 | if( robust_ftruncate(pFile->h, nSize) ){ |
| 28798 | pFile->lastErrno = errno; |
| 28799 | return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); |
| 28800 | } |
| 28801 | iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; |
| 28802 | while( iWrite<nSize ){ |
| 28803 | int nWrite = seekAndWrite(pFile, iWrite, "", 1); |
| 28804 | if( nWrite!=1 ) return SQLITE_IOERR_WRITE; |
| 28805 | iWrite += nBlk; |
| 28806 | } |
| 28807 | #endif |
| 28808 | } |
| 28809 | } |
| 28810 | |
| @@ -34018,12 +34103,12 @@ | |
| 34018 | */ |
| 34019 | SQLITE_API int sqlite3_win32_reset_heap(){ |
| 34020 | int rc; |
| 34021 | MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ |
| 34022 | MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */ |
| 34023 | MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); ) |
| 34024 | MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); ) |
| 34025 | sqlite3_mutex_enter(pMaster); |
| 34026 | sqlite3_mutex_enter(pMem); |
| 34027 | winMemAssertMagic(); |
| 34028 | if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){ |
| 34029 | /* |
| @@ -35294,11 +35379,11 @@ | |
| 35294 | sqlite3_file *id, /* File to read from */ |
| 35295 | void *pBuf, /* Write content into this buffer */ |
| 35296 | int amt, /* Number of bytes to read */ |
| 35297 | sqlite3_int64 offset /* Begin reading at this offset */ |
| 35298 | ){ |
| 35299 | #if !SQLITE_OS_WINCE |
| 35300 | OVERLAPPED overlapped; /* The offset for ReadFile. */ |
| 35301 | #endif |
| 35302 | winFile *pFile = (winFile*)id; /* file handle */ |
| 35303 | DWORD nRead; /* Number of bytes actually read from file */ |
| 35304 | int nRetry = 0; /* Number of retrys */ |
| @@ -35326,11 +35411,11 @@ | |
| 35326 | offset += nCopy; |
| 35327 | } |
| 35328 | } |
| 35329 | #endif |
| 35330 | |
| 35331 | #if SQLITE_OS_WINCE |
| 35332 | if( winSeekFile(pFile, offset) ){ |
| 35333 | OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h)); |
| 35334 | return SQLITE_FULL; |
| 35335 | } |
| 35336 | while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ |
| @@ -35398,32 +35483,32 @@ | |
| 35398 | offset += nCopy; |
| 35399 | } |
| 35400 | } |
| 35401 | #endif |
| 35402 | |
| 35403 | #if SQLITE_OS_WINCE |
| 35404 | rc = winSeekFile(pFile, offset); |
| 35405 | if( rc==0 ){ |
| 35406 | #else |
| 35407 | { |
| 35408 | #endif |
| 35409 | #if !SQLITE_OS_WINCE |
| 35410 | OVERLAPPED overlapped; /* The offset for WriteFile. */ |
| 35411 | #endif |
| 35412 | u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ |
| 35413 | int nRem = amt; /* Number of bytes yet to be written */ |
| 35414 | DWORD nWrite; /* Bytes written by each WriteFile() call */ |
| 35415 | DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ |
| 35416 | |
| 35417 | #if !SQLITE_OS_WINCE |
| 35418 | memset(&overlapped, 0, sizeof(OVERLAPPED)); |
| 35419 | overlapped.Offset = (LONG)(offset & 0xffffffff); |
| 35420 | overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); |
| 35421 | #endif |
| 35422 | |
| 35423 | while( nRem>0 ){ |
| 35424 | #if SQLITE_OS_WINCE |
| 35425 | if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ |
| 35426 | #else |
| 35427 | if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ |
| 35428 | #endif |
| 35429 | if( winRetryIoerr(&nRetry, &lastErrno) ) continue; |
| @@ -35432,11 +35517,11 @@ | |
| 35432 | assert( nWrite==0 || nWrite<=(DWORD)nRem ); |
| 35433 | if( nWrite==0 || nWrite>(DWORD)nRem ){ |
| 35434 | lastErrno = osGetLastError(); |
| 35435 | break; |
| 35436 | } |
| 35437 | #if !SQLITE_OS_WINCE |
| 35438 | offset += nWrite; |
| 35439 | overlapped.Offset = (LONG)(offset & 0xffffffff); |
| 35440 | overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); |
| 35441 | #endif |
| 35442 | aRem += nWrite; |
| @@ -38813,22 +38898,10 @@ | |
| 38813 | void *pStress; /* Argument to xStress */ |
| 38814 | sqlite3_pcache *pCache; /* Pluggable cache module */ |
| 38815 | PgHdr *pPage1; /* Reference to page 1 */ |
| 38816 | }; |
| 38817 | |
| 38818 | /* |
| 38819 | ** Some of the assert() macros in this code are too expensive to run |
| 38820 | ** even during normal debugging. Use them only rarely on long-running |
| 38821 | ** tests. Enable the expensive asserts using the |
| 38822 | ** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option. |
| 38823 | */ |
| 38824 | #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT |
| 38825 | # define expensive_assert(X) assert(X) |
| 38826 | #else |
| 38827 | # define expensive_assert(X) |
| 38828 | #endif |
| 38829 | |
| 38830 | /********************************** Linked List Management ********************/ |
| 38831 | |
| 38832 | /* Allowed values for second argument to pcacheManageDirtyList() */ |
| 38833 | #define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */ |
| 38834 | #define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */ |
| @@ -38978,11 +39051,12 @@ | |
| 38978 | SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ |
| 38979 | assert( pCache->nRef==0 && pCache->pDirty==0 ); |
| 38980 | if( pCache->szPage ){ |
| 38981 | sqlite3_pcache *pNew; |
| 38982 | pNew = sqlite3GlobalConfig.pcache2.xCreate( |
| 38983 | szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable |
| 38984 | ); |
| 38985 | if( pNew==0 ) return SQLITE_NOMEM; |
| 38986 | sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache)); |
| 38987 | if( pCache->pCache ){ |
| 38988 | sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); |
| @@ -39437,11 +39511,11 @@ | |
| 39437 | |
| 39438 | /* |
| 39439 | ** Return the size of the header added by this middleware layer |
| 39440 | ** in the page-cache hierarchy. |
| 39441 | */ |
| 39442 | SQLITE_PRIVATE int sqlite3HeaderSizePcache(void){ return sizeof(PgHdr); } |
| 39443 | |
| 39444 | |
| 39445 | #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) |
| 39446 | /* |
| 39447 | ** For all dirty pages currently in the cache, invoke the specified |
| @@ -39753,11 +39827,11 @@ | |
| 39753 | pcache1Free(pPg); |
| 39754 | sqlite3_free(p); |
| 39755 | pPg = 0; |
| 39756 | } |
| 39757 | #else |
| 39758 | pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra); |
| 39759 | p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; |
| 39760 | #endif |
| 39761 | pcache1EnterMutex(pCache->pGroup); |
| 39762 | |
| 39763 | if( pPg ){ |
| @@ -40441,11 +40515,11 @@ | |
| 40441 | } |
| 40442 | |
| 40443 | /* |
| 40444 | ** Return the size of the header on each page of this PCACHE implementation. |
| 40445 | */ |
| 40446 | SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void){ return sizeof(PgHdr1); } |
| 40447 | |
| 40448 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 40449 | /* |
| 40450 | ** This function is called to free superfluous dynamically allocated memory |
| 40451 | ** held by the pager system. Memory in use by any SQLite pager allocated |
| @@ -41799,10 +41873,12 @@ | |
| 41799 | u8 eLock; /* Current lock held on database file */ |
| 41800 | u8 changeCountDone; /* Set after incrementing the change-counter */ |
| 41801 | u8 setMaster; /* True if a m-j name has been written to jrnl */ |
| 41802 | u8 doNotSpill; /* Do not spill the cache when non-zero */ |
| 41803 | u8 subjInMemory; /* True to use in-memory sub-journals */ |
| 41804 | Pgno dbSize; /* Number of pages in the database */ |
| 41805 | Pgno dbOrigSize; /* dbSize before the current transaction */ |
| 41806 | Pgno dbFileSize; /* Number of pages in the database file */ |
| 41807 | Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ |
| 41808 | int errCode; /* One of several kinds of errors */ |
| @@ -41816,13 +41892,13 @@ | |
| 41816 | i64 journalOff; /* Current write offset in the journal file */ |
| 41817 | i64 journalHdr; /* Byte offset to previous journal header */ |
| 41818 | sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ |
| 41819 | PagerSavepoint *aSavepoint; /* Array of active savepoints */ |
| 41820 | int nSavepoint; /* Number of elements in aSavepoint[] */ |
| 41821 | char dbFileVers[16]; /* Changes whenever database file changes */ |
| 41822 | |
| 41823 | u8 bUseFetch; /* True to use xFetch() */ |
| 41824 | int nMmapOut; /* Number of mmap pages currently outstanding */ |
| 41825 | sqlite3_int64 szMmap; /* Desired maximum mmap size */ |
| 41826 | PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */ |
| 41827 | /* |
| 41828 | ** End of the routinely-changing class members |
| @@ -42834,13 +42910,22 @@ | |
| 42834 | |
| 42835 | /* |
| 42836 | ** Discard the entire contents of the in-memory page-cache. |
| 42837 | */ |
| 42838 | static void pager_reset(Pager *pPager){ |
| 42839 | sqlite3BackupRestart(pPager->pBackup); |
| 42840 | sqlite3PcacheClear(pPager->pPCache); |
| 42841 | } |
| 42842 | |
| 42843 | /* |
| 42844 | ** Free all structures in the Pager.aSavepoint[] array and set both |
| 42845 | ** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal |
| 42846 | ** if it is open and the pager is not in exclusive mode. |
| @@ -45040,11 +45125,11 @@ | |
| 45040 | Pgno pgno, /* Page number */ |
| 45041 | void *pData, /* xFetch()'d data for this page */ |
| 45042 | PgHdr **ppPage /* OUT: Acquired page object */ |
| 45043 | ){ |
| 45044 | PgHdr *p; /* Memory mapped page to return */ |
| 45045 | |
| 45046 | if( pPager->pMmapFreelist ){ |
| 45047 | *ppPage = p = pPager->pMmapFreelist; |
| 45048 | pPager->pMmapFreelist = p->pDirty; |
| 45049 | p->pDirty = 0; |
| 45050 | memset(p->pExtra, 0, pPager->nExtra); |
| @@ -46271,20 +46356,16 @@ | |
| 46271 | assert( (pPager->eLock==SHARED_LOCK) |
| 46272 | || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK) |
| 46273 | ); |
| 46274 | } |
| 46275 | |
| 46276 | if( !pPager->tempFile && ( |
| 46277 | pPager->pBackup |
| 46278 | || sqlite3PcachePagecount(pPager->pPCache)>0 |
| 46279 | || USEFETCH(pPager) |
| 46280 | )){ |
| 46281 | /* The shared-lock has just been acquired on the database file |
| 46282 | ** and there are already pages in the cache (from a previous |
| 46283 | ** read or write transaction). Check to see if the database |
| 46284 | ** has been modified. If the database has changed, flush the |
| 46285 | ** cache. |
| 46286 | ** |
| 46287 | ** Database changes is detected by looking at 15 bytes beginning |
| 46288 | ** at offset 24 into the file. The first 4 of these 16 bytes are |
| 46289 | ** a 32-bit counter that is incremented with each change. The |
| 46290 | ** other bytes change randomly with each file change when |
| @@ -46445,10 +46526,11 @@ | |
| 46445 | assert( noContent==0 || bMmapOk==0 ); |
| 46446 | |
| 46447 | if( pgno==0 ){ |
| 46448 | return SQLITE_CORRUPT_BKPT; |
| 46449 | } |
| 46450 | |
| 46451 | /* If the pager is in the error state, return an error immediately. |
| 46452 | ** Otherwise, request the page from the PCache layer. */ |
| 46453 | if( pPager->errCode!=SQLITE_OK ){ |
| 46454 | rc = pPager->errCode; |
| @@ -46594,10 +46676,11 @@ | |
| 46594 | sqlite3_pcache_page *pPage; |
| 46595 | assert( pPager!=0 ); |
| 46596 | assert( pgno!=0 ); |
| 46597 | assert( pPager->pPCache!=0 ); |
| 46598 | pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0); |
| 46599 | return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); |
| 46600 | } |
| 46601 | |
| 46602 | /* |
| 46603 | ** Release a page reference. |
| @@ -47460,10 +47543,11 @@ | |
| 47460 | pPager->eState = PAGER_READER; |
| 47461 | return SQLITE_OK; |
| 47462 | } |
| 47463 | |
| 47464 | PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); |
| 47465 | rc = pager_end_transaction(pPager, pPager->setMaster, 1); |
| 47466 | return pager_error(pPager, rc); |
| 47467 | } |
| 47468 | |
| 47469 | /* |
| @@ -50111,11 +50195,11 @@ | |
| 50111 | int (*xBusy)(void*), /* Function to call when busy */ |
| 50112 | void *pBusyArg, /* Context argument for xBusyHandler */ |
| 50113 | int sync_flags, /* Flags for OsSync() (or 0) */ |
| 50114 | u8 *zBuf /* Temporary buffer to use */ |
| 50115 | ){ |
| 50116 | int rc; /* Return code */ |
| 50117 | int szPage; /* Database page-size */ |
| 50118 | WalIterator *pIter = 0; /* Wal iterator context */ |
| 50119 | u32 iDbpage = 0; /* Next database page to write */ |
| 50120 | u32 iFrame = 0; /* Wal frame containing data for iDbpage */ |
| 50121 | u32 mxSafeFrame; /* Max frame that can be backfilled */ |
| @@ -50125,108 +50209,111 @@ | |
| 50125 | |
| 50126 | szPage = walPagesize(pWal); |
| 50127 | testcase( szPage<=32768 ); |
| 50128 | testcase( szPage>=65536 ); |
| 50129 | pInfo = walCkptInfo(pWal); |
| 50130 | if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK; |
| 50131 | |
| 50132 | /* Allocate the iterator */ |
| 50133 | rc = walIteratorInit(pWal, &pIter); |
| 50134 | if( rc!=SQLITE_OK ){ |
| 50135 | return rc; |
| 50136 | } |
| 50137 | assert( pIter ); |
| 50138 | |
| 50139 | /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked |
| 50140 | ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ |
| 50141 | assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); |
| 50142 | |
| 50143 | /* Compute in mxSafeFrame the index of the last frame of the WAL that is |
| 50144 | ** safe to write into the database. Frames beyond mxSafeFrame might |
| 50145 | ** overwrite database pages that are in use by active readers and thus |
| 50146 | ** cannot be backfilled from the WAL. |
| 50147 | */ |
| 50148 | mxSafeFrame = pWal->hdr.mxFrame; |
| 50149 | mxPage = pWal->hdr.nPage; |
| 50150 | for(i=1; i<WAL_NREADER; i++){ |
| 50151 | u32 y = pInfo->aReadMark[i]; |
| 50152 | if( mxSafeFrame>y ){ |
| 50153 | assert( y<=pWal->hdr.mxFrame ); |
| 50154 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); |
| 50155 | if( rc==SQLITE_OK ){ |
| 50156 | pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); |
| 50157 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 50158 | }else if( rc==SQLITE_BUSY ){ |
| 50159 | mxSafeFrame = y; |
| 50160 | xBusy = 0; |
| 50161 | }else{ |
| 50162 | goto walcheckpoint_out; |
| 50163 | } |
| 50164 | } |
| 50165 | } |
| 50166 | |
| 50167 | if( pInfo->nBackfill<mxSafeFrame |
| 50168 | && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK |
| 50169 | ){ |
| 50170 | i64 nSize; /* Current size of database file */ |
| 50171 | u32 nBackfill = pInfo->nBackfill; |
| 50172 | |
| 50173 | /* Sync the WAL to disk */ |
| 50174 | if( sync_flags ){ |
| 50175 | rc = sqlite3OsSync(pWal->pWalFd, sync_flags); |
| 50176 | } |
| 50177 | |
| 50178 | /* If the database may grow as a result of this checkpoint, hint |
| 50179 | ** about the eventual size of the db file to the VFS layer. |
| 50180 | */ |
| 50181 | if( rc==SQLITE_OK ){ |
| 50182 | i64 nReq = ((i64)mxPage * szPage); |
| 50183 | rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); |
| 50184 | if( rc==SQLITE_OK && nSize<nReq ){ |
| 50185 | sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); |
| 50186 | } |
| 50187 | } |
| 50188 | |
| 50189 | |
| 50190 | /* Iterate through the contents of the WAL, copying data to the db file. */ |
| 50191 | while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ |
| 50192 | i64 iOffset; |
| 50193 | assert( walFramePgno(pWal, iFrame)==iDbpage ); |
| 50194 | if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue; |
| 50195 | iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; |
| 50196 | /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ |
| 50197 | rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); |
| 50198 | if( rc!=SQLITE_OK ) break; |
| 50199 | iOffset = (iDbpage-1)*(i64)szPage; |
| 50200 | testcase( IS_BIG_INT(iOffset) ); |
| 50201 | rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); |
| 50202 | if( rc!=SQLITE_OK ) break; |
| 50203 | } |
| 50204 | |
| 50205 | /* If work was actually accomplished... */ |
| 50206 | if( rc==SQLITE_OK ){ |
| 50207 | if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ |
| 50208 | i64 szDb = pWal->hdr.nPage*(i64)szPage; |
| 50209 | testcase( IS_BIG_INT(szDb) ); |
| 50210 | rc = sqlite3OsTruncate(pWal->pDbFd, szDb); |
| 50211 | if( rc==SQLITE_OK && sync_flags ){ |
| 50212 | rc = sqlite3OsSync(pWal->pDbFd, sync_flags); |
| 50213 | } |
| 50214 | } |
| 50215 | if( rc==SQLITE_OK ){ |
| 50216 | pInfo->nBackfill = mxSafeFrame; |
| 50217 | } |
| 50218 | } |
| 50219 | |
| 50220 | /* Release the reader lock held while backfilling */ |
| 50221 | walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); |
| 50222 | } |
| 50223 | |
| 50224 | if( rc==SQLITE_BUSY ){ |
| 50225 | /* Reset the return code so as not to report a checkpoint failure |
| 50226 | ** just because there are active readers. */ |
| 50227 | rc = SQLITE_OK; |
| 50228 | } |
| 50229 | |
| 50230 | /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the |
| 50231 | ** entire wal file has been copied into the database file, then block |
| 50232 | ** until all readers have finished using the wal file. This ensures that |
| @@ -50237,11 +50324,11 @@ | |
| 50237 | if( pInfo->nBackfill<pWal->hdr.mxFrame ){ |
| 50238 | rc = SQLITE_BUSY; |
| 50239 | }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ |
| 50240 | u32 salt1; |
| 50241 | sqlite3_randomness(4, &salt1); |
| 50242 | assert( mxSafeFrame==pWal->hdr.mxFrame ); |
| 50243 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); |
| 50244 | if( rc==SQLITE_OK ){ |
| 50245 | if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ |
| 50246 | /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as |
| 50247 | ** SQLITE_CHECKPOINT_RESTART with the addition that it also |
| @@ -50829,11 +50916,11 @@ | |
| 50829 | } |
| 50830 | nCollide = HASHTABLE_NSLOT; |
| 50831 | for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){ |
| 50832 | u32 iFrame = aHash[iKey] + iZero; |
| 50833 | if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){ |
| 50834 | /* assert( iFrame>iRead ); -- not true if there is corruption */ |
| 50835 | iRead = iFrame; |
| 50836 | } |
| 50837 | if( (nCollide--)==0 ){ |
| 50838 | return SQLITE_CORRUPT_BKPT; |
| 50839 | } |
| @@ -51935,10 +52022,11 @@ | |
| 51935 | u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ |
| 51936 | u8 sharable; /* True if we can share pBt with another db */ |
| 51937 | u8 locked; /* True if db currently has pBt locked */ |
| 51938 | int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ |
| 51939 | int nBackup; /* Number of backup operations reading this btree */ |
| 51940 | Btree *pNext; /* List of other sharable Btrees from the same db */ |
| 51941 | Btree *pPrev; /* Back pointer of the same list */ |
| 51942 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 51943 | BtLock lock; /* Object used to lock page 1 */ |
| 51944 | #endif |
| @@ -56098,10 +56186,11 @@ | |
| 56098 | rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); |
| 56099 | if( rc!=SQLITE_OK && bCleanup==0 ){ |
| 56100 | sqlite3BtreeLeave(p); |
| 56101 | return rc; |
| 56102 | } |
| 56103 | pBt->inTransaction = TRANS_READ; |
| 56104 | btreeClearHasContent(pBt); |
| 56105 | } |
| 56106 | |
| 56107 | btreeEndTransaction(p); |
| @@ -56461,11 +56550,11 @@ | |
| 56461 | } |
| 56462 | for(i=0; i<=pCur->iPage; i++){ |
| 56463 | releasePage(pCur->apPage[i]); |
| 56464 | } |
| 56465 | unlockBtreeIfUnused(pBt); |
| 56466 | sqlite3DbFree(pBtree->db, pCur->aOverflow); |
| 56467 | /* sqlite3_free(pCur); */ |
| 56468 | sqlite3BtreeLeave(pBtree); |
| 56469 | } |
| 56470 | return SQLITE_OK; |
| 56471 | } |
| @@ -56755,10 +56844,11 @@ | |
| 56755 | pBuf += a; |
| 56756 | amt -= a; |
| 56757 | }else{ |
| 56758 | offset -= pCur->info.nLocal; |
| 56759 | } |
| 56760 | |
| 56761 | if( rc==SQLITE_OK && amt>0 ){ |
| 56762 | const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */ |
| 56763 | Pgno nextPage; |
| 56764 | |
| @@ -56773,12 +56863,12 @@ | |
| 56773 | ** means "not yet known" (the cache is lazily populated). |
| 56774 | */ |
| 56775 | if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){ |
| 56776 | int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; |
| 56777 | if( nOvfl>pCur->nOvflAlloc ){ |
| 56778 | Pgno *aNew = (Pgno*)sqlite3DbRealloc( |
| 56779 | pCur->pBtree->db, pCur->aOverflow, nOvfl*2*sizeof(Pgno) |
| 56780 | ); |
| 56781 | if( aNew==0 ){ |
| 56782 | rc = SQLITE_NOMEM; |
| 56783 | }else{ |
| 56784 | pCur->nOvflAlloc = nOvfl*2; |
| @@ -56821,10 +56911,11 @@ | |
| 56821 | ** Note that the aOverflow[] array must be allocated because eOp!=2 |
| 56822 | ** here. If eOp==2, then offset==0 and this branch is never taken. |
| 56823 | */ |
| 56824 | assert( eOp!=2 ); |
| 56825 | assert( pCur->curFlags & BTCF_ValidOvfl ); |
| 56826 | if( pCur->aOverflow[iIdx+1] ){ |
| 56827 | nextPage = pCur->aOverflow[iIdx+1]; |
| 56828 | }else{ |
| 56829 | rc = getOverflowPage(pBt, nextPage, 0, &nextPage); |
| 56830 | } |
| @@ -59410,12 +59501,12 @@ | |
| 59410 | assert( leafCorrection==4 ); |
| 59411 | if( szCell[nCell]<4 ){ |
| 59412 | /* Do not allow any cells smaller than 4 bytes. If a smaller cell |
| 59413 | ** does exist, pad it with 0x00 bytes. */ |
| 59414 | assert( szCell[nCell]==3 ); |
| 59415 | assert( apCell[nCell]==&pTemp[iSpace1-3] ); |
| 59416 | pTemp[iSpace1++] = 0x00; |
| 59417 | szCell[nCell] = 4; |
| 59418 | } |
| 59419 | } |
| 59420 | nCell++; |
| 59421 | } |
| @@ -60723,10 +60814,17 @@ | |
| 60723 | ** is read-only, the others are read/write. |
| 60724 | ** |
| 60725 | ** The schema layer numbers meta values differently. At the schema |
| 60726 | ** layer (and the SetCookie and ReadCookie opcodes) the number of |
| 60727 | ** free pages is not visible. So Cookie[0] is the same as Meta[1]. |
| 60728 | */ |
| 60729 | SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ |
| 60730 | BtShared *pBt = p->pBt; |
| 60731 | |
| 60732 | sqlite3BtreeEnter(p); |
| @@ -60733,11 +60831,15 @@ | |
| 60733 | assert( p->inTrans>TRANS_NONE ); |
| 60734 | assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) ); |
| 60735 | assert( pBt->pPage1 ); |
| 60736 | assert( idx>=0 && idx<=15 ); |
| 60737 | |
| 60738 | *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); |
| 60739 | |
| 60740 | /* If auto-vacuum is disabled in this build and this is an auto-vacuum |
| 60741 | ** database, mark the database as read-only. */ |
| 60742 | #ifdef SQLITE_OMIT_AUTOVACUUM |
| 60743 | if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){ |
| @@ -60824,11 +60926,11 @@ | |
| 60824 | if( pPage->leaf ){ |
| 60825 | do { |
| 60826 | if( pCur->iPage==0 ){ |
| 60827 | /* All pages of the b-tree have been visited. Return successfully. */ |
| 60828 | *pnEntry = nEntry; |
| 60829 | return SQLITE_OK; |
| 60830 | } |
| 60831 | moveToParent(pCur); |
| 60832 | }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); |
| 60833 | |
| 60834 | pCur->aiIdx[pCur->iPage]++; |
| @@ -61680,11 +61782,11 @@ | |
| 61680 | } |
| 61681 | |
| 61682 | /* |
| 61683 | ** Return the size of the header added to each page by this module. |
| 61684 | */ |
| 61685 | SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return sizeof(MemPage); } |
| 61686 | |
| 61687 | /************** End of btree.c ***********************************************/ |
| 61688 | /************** Begin file backup.c ******************************************/ |
| 61689 | /* |
| 61690 | ** 2009 January 28 |
| @@ -64444,36 +64546,39 @@ | |
| 64444 | ** |
| 64445 | ** assert( sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) ); |
| 64446 | */ |
| 64447 | SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ |
| 64448 | int hasAbort = 0; |
| 64449 | Op *pOp; |
| 64450 | VdbeOpIter sIter; |
| 64451 | memset(&sIter, 0, sizeof(sIter)); |
| 64452 | sIter.v = v; |
| 64453 | |
| 64454 | while( (pOp = opIterNext(&sIter))!=0 ){ |
| 64455 | int opcode = pOp->opcode; |
| 64456 | if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename |
| 64457 | #ifndef SQLITE_OMIT_FOREIGN_KEY |
| 64458 | || (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1) |
| 64459 | #endif |
| 64460 | || ((opcode==OP_Halt || opcode==OP_HaltIfNull) |
| 64461 | && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) |
| 64462 | ){ |
| 64463 | hasAbort = 1; |
| 64464 | break; |
| 64465 | } |
| 64466 | } |
| 64467 | sqlite3DbFree(v->db, sIter.apSub); |
| 64468 | |
| 64469 | /* Return true if hasAbort==mayAbort. Or if a malloc failure occurred. |
| 64470 | ** If malloc failed, then the while() loop above may not have iterated |
| 64471 | ** through all opcodes and hasAbort may be set incorrectly. Return |
| 64472 | ** true for this case to prevent the assert() in the callers frame |
| 64473 | ** from failing. */ |
| 64474 | return ( v->db->mallocFailed || hasAbort==mayAbort ); |
| 64475 | } |
| 64476 | #endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */ |
| 64477 | |
| 64478 | /* |
| 64479 | ** Loop through the program looking for P2 values that are negative |
| @@ -67394,10 +67499,45 @@ | |
| 67394 | if( pKeyInfo->db->mallocFailed ) return 1; |
| 67395 | return 0; |
| 67396 | } |
| 67397 | #endif |
| 67398 | |
| 67399 | /* |
| 67400 | ** Both *pMem1 and *pMem2 contain string values. Compare the two values |
| 67401 | ** using the collation sequence pColl. As usual, return a negative , zero |
| 67402 | ** or positive value if *pMem1 is less than, equal to or greater than |
| 67403 | ** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);". |
| @@ -67805,10 +67945,11 @@ | |
| 67805 | u32 y; |
| 67806 | u64 x; |
| 67807 | i64 v = pPKey2->aMem[0].u.i; |
| 67808 | i64 lhs; |
| 67809 | |
| 67810 | assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB ); |
| 67811 | switch( serial_type ){ |
| 67812 | case 1: { /* 1-byte signed integer */ |
| 67813 | lhs = ONE_BYTE_INT(aKey); |
| 67814 | testcase( lhs<0 ); |
| @@ -67892,10 +68033,11 @@ | |
| 67892 | ){ |
| 67893 | const u8 *aKey1 = (const u8*)pKey1; |
| 67894 | int serial_type; |
| 67895 | int res; |
| 67896 | |
| 67897 | getVarint32(&aKey1[1], serial_type); |
| 67898 | if( serial_type<12 ){ |
| 67899 | res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ |
| 67900 | }else if( !(serial_type & 0x01) ){ |
| 67901 | res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ |
| @@ -68593,11 +68735,14 @@ | |
| 68593 | #ifndef SQLITE_OMIT_WAL |
| 68594 | int i; |
| 68595 | for(i=0; i<db->nDb; i++){ |
| 68596 | Btree *pBt = db->aDb[i].pBt; |
| 68597 | if( pBt ){ |
| 68598 | int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); |
| 68599 | if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){ |
| 68600 | rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry); |
| 68601 | } |
| 68602 | } |
| 68603 | } |
| @@ -68773,11 +68918,10 @@ | |
| 68773 | ** program counter to 0 to ensure that when the statement is |
| 68774 | ** finalized or reset the parser error message is available via |
| 68775 | ** sqlite3_errmsg() and sqlite3_errcode(). |
| 68776 | */ |
| 68777 | const char *zErr = (const char *)sqlite3_value_text(db->pErr); |
| 68778 | assert( zErr!=0 || db->mallocFailed ); |
| 68779 | sqlite3DbFree(db, v->zErrMsg); |
| 68780 | if( !db->mallocFailed ){ |
| 68781 | v->zErrMsg = sqlite3DbStrDup(db, zErr); |
| 68782 | v->rc = rc2; |
| 68783 | } else { |
| @@ -73838,12 +73982,12 @@ | |
| 73838 | pIdxKey->default_rc = 0; |
| 73839 | if( pOp->opcode==OP_NoConflict ){ |
| 73840 | /* For the OP_NoConflict opcode, take the jump if any of the |
| 73841 | ** input fields are NULL, since any key with a NULL will not |
| 73842 | ** conflict */ |
| 73843 | for(ii=0; ii<r.nField; ii++){ |
| 73844 | if( r.aMem[ii].flags & MEM_Null ){ |
| 73845 | pc = pOp->p2 - 1; VdbeBranchTaken(1,2); |
| 73846 | break; |
| 73847 | } |
| 73848 | } |
| 73849 | } |
| @@ -77135,11 +77279,11 @@ | |
| 77135 | /* |
| 77136 | ** Hard-coded maximum amount of data to accumulate in memory before flushing |
| 77137 | ** to a level 0 PMA. The purpose of this limit is to prevent various integer |
| 77138 | ** overflows. 512MiB. |
| 77139 | */ |
| 77140 | #define SQLITE_MAX_MXPMASIZE (1<<29) |
| 77141 | |
| 77142 | /* |
| 77143 | ** Private objects used by the sorter |
| 77144 | */ |
| 77145 | typedef struct MergeEngine MergeEngine; /* Merge PMAs together */ |
| @@ -77431,15 +77575,10 @@ | |
| 77431 | ** |
| 77432 | ** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; } |
| 77433 | */ |
| 77434 | #define SRVAL(p) ((void*)((SorterRecord*)(p) + 1)) |
| 77435 | |
| 77436 | /* The minimum PMA size is set to this value multiplied by the database |
| 77437 | ** page size in bytes. */ |
| 77438 | #ifndef SQLITE_SORTER_PMASZ |
| 77439 | # define SQLITE_SORTER_PMASZ 10 |
| 77440 | #endif |
| 77441 | |
| 77442 | /* Maximum number of PMAs that a single MergeEngine can merge */ |
| 77443 | #define SORTER_MAX_MERGE_COUNT 16 |
| 77444 | |
| 77445 | static int vdbeIncrSwap(IncrMerger*); |
| @@ -77834,14 +77973,15 @@ | |
| 77834 | SortSubtask *pTask = &pSorter->aTask[i]; |
| 77835 | pTask->pSorter = pSorter; |
| 77836 | } |
| 77837 | |
| 77838 | if( !sqlite3TempInMemory(db) ){ |
| 77839 | pSorter->mnPmaSize = SQLITE_SORTER_PMASZ * pgsz; |
| 77840 | mxCache = db->aDb[0].pSchema->cache_size; |
| 77841 | if( mxCache<SQLITE_SORTER_PMASZ ) mxCache = SQLITE_SORTER_PMASZ; |
| 77842 | pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_MXPMASIZE); |
| 77843 | |
| 77844 | /* EVIDENCE-OF: R-26747-61719 When the application provides any amount of |
| 77845 | ** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary |
| 77846 | ** large heap allocations. |
| 77847 | */ |
| @@ -78115,16 +78255,16 @@ | |
| 78115 | ** Whether or not the file does end up memory mapped of course depends on |
| 78116 | ** the specific VFS implementation. |
| 78117 | */ |
| 78118 | static void vdbeSorterExtendFile(sqlite3 *db, sqlite3_file *pFd, i64 nByte){ |
| 78119 | if( nByte<=(i64)(db->nMaxSorterMmap) && pFd->pMethods->iVersion>=3 ){ |
| 78120 | int rc = sqlite3OsTruncate(pFd, nByte); |
| 78121 | if( rc==SQLITE_OK ){ |
| 78122 | void *p = 0; |
| 78123 | sqlite3OsFetch(pFd, 0, (int)nByte, &p); |
| 78124 | sqlite3OsUnfetch(pFd, 0, p); |
| 78125 | } |
| 78126 | } |
| 78127 | } |
| 78128 | #else |
| 78129 | # define vdbeSorterExtendFile(x,y,z) |
| 78130 | #endif |
| @@ -79401,10 +79541,11 @@ | |
| 79401 | rc = vdbePmaReaderNext(pSorter->pReader); |
| 79402 | *pbEof = (pSorter->pReader->pFd==0); |
| 79403 | }else |
| 79404 | #endif |
| 79405 | /*if( !pSorter->bUseThreads )*/ { |
| 79406 | assert( pSorter->pMerger->pTask==(&pSorter->aTask[0]) ); |
| 79407 | rc = vdbeMergeEngineStep(pSorter->pMerger, pbEof); |
| 79408 | } |
| 79409 | }else{ |
| 79410 | SorterRecord *pFree = pSorter->list.pList; |
| @@ -82167,11 +82308,11 @@ | |
| 82167 | Expr *pLeft, /* Left operand */ |
| 82168 | Expr *pRight, /* Right operand */ |
| 82169 | const Token *pToken /* Argument token */ |
| 82170 | ){ |
| 82171 | Expr *p; |
| 82172 | if( op==TK_AND && pLeft && pRight ){ |
| 82173 | /* Take advantage of short-circuit false optimization for AND */ |
| 82174 | p = sqlite3ExprAnd(pParse->db, pLeft, pRight); |
| 82175 | }else{ |
| 82176 | p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); |
| 82177 | sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); |
| @@ -85721,14 +85862,15 @@ | |
| 85721 | ** NEVER() will need to be removed. */ |
| 85722 | if( pExpr->op==TK_COLUMN || NEVER(pExpr->op==TK_AGG_COLUMN) ){ |
| 85723 | int i; |
| 85724 | struct SrcCount *p = pWalker->u.pSrcCount; |
| 85725 | SrcList *pSrc = p->pSrc; |
| 85726 | for(i=0; i<pSrc->nSrc; i++){ |
| 85727 | if( pExpr->iTable==pSrc->a[i].iCursor ) break; |
| 85728 | } |
| 85729 | if( i<pSrc->nSrc ){ |
| 85730 | p->nThis++; |
| 85731 | }else{ |
| 85732 | p->nOther++; |
| 85733 | } |
| 85734 | } |
| @@ -87302,11 +87444,11 @@ | |
| 87302 | |
| 87303 | p->iGet = -1; |
| 87304 | p->mxSample = mxSample; |
| 87305 | p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1); |
| 87306 | p->current.anLt = &p->current.anEq[nColUp]; |
| 87307 | p->iPrn = nCol*0x689e962d ^ sqlite3_value_int(argv[2])*0xd0944565; |
| 87308 | |
| 87309 | /* Set up the Stat4Accum.a[] and aBest[] arrays */ |
| 87310 | p->a = (struct Stat4Sample*)&p->current.anLt[nColUp]; |
| 87311 | p->aBest = &p->a[mxSample]; |
| 87312 | pSpace = (u8*)(&p->a[mxSample+nCol]); |
| @@ -88895,17 +89037,19 @@ | |
| 88895 | }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ |
| 88896 | zErrDyn = sqlite3MPrintf(db, |
| 88897 | "attached databases must use the same text encoding as main database"); |
| 88898 | rc = SQLITE_ERROR; |
| 88899 | } |
| 88900 | pPager = sqlite3BtreePager(aNew->pBt); |
| 88901 | sqlite3PagerLockingMode(pPager, db->dfltLockMode); |
| 88902 | sqlite3BtreeSecureDelete(aNew->pBt, |
| 88903 | sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) ); |
| 88904 | #ifndef SQLITE_OMIT_PAGER_PRAGMAS |
| 88905 | sqlite3BtreeSetPagerFlags(aNew->pBt, 3 | (db->flags & PAGER_FLAGS_MASK)); |
| 88906 | #endif |
| 88907 | } |
| 88908 | aNew->safety_level = 3; |
| 88909 | aNew->zName = sqlite3DbStrDup(db, zName); |
| 88910 | if( rc==SQLITE_OK && aNew->zName==0 ){ |
| 88911 | rc = SQLITE_NOMEM; |
| @@ -90027,11 +90171,10 @@ | |
| 90027 | */ |
| 90028 | static void freeIndex(sqlite3 *db, Index *p){ |
| 90029 | #ifndef SQLITE_OMIT_ANALYZE |
| 90030 | sqlite3DeleteIndexSamples(db, p); |
| 90031 | #endif |
| 90032 | if( db==0 || db->pnBytesFreed==0 ) sqlite3KeyInfoUnref(p->pKeyInfo); |
| 90033 | sqlite3ExprDelete(db, p->pPartIdxWhere); |
| 90034 | sqlite3DbFree(db, p->zColAff); |
| 90035 | if( p->isResized ) sqlite3DbFree(db, p->azColl); |
| 90036 | #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 |
| 90037 | sqlite3_free(p->aiRowEst); |
| @@ -91306,10 +91449,23 @@ | |
| 91306 | if( pPk==0 ) return; |
| 91307 | pPk->idxType = SQLITE_IDXTYPE_PRIMARYKEY; |
| 91308 | pTab->iPKey = -1; |
| 91309 | }else{ |
| 91310 | pPk = sqlite3PrimaryKeyIndex(pTab); |
| 91311 | } |
| 91312 | pPk->isCovering = 1; |
| 91313 | assert( pPk!=0 ); |
| 91314 | nPk = pPk->nKeyCol; |
| 91315 | |
| @@ -93782,44 +93938,35 @@ | |
| 93782 | ** |
| 93783 | ** The caller should invoke sqlite3KeyInfoUnref() on the returned object |
| 93784 | ** when it has finished using it. |
| 93785 | */ |
| 93786 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ |
| 93787 | if( pParse->nErr ) return 0; |
| 93788 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 93789 | if( pIdx->pKeyInfo && pIdx->pKeyInfo->db!=pParse->db ){ |
| 93790 | sqlite3KeyInfoUnref(pIdx->pKeyInfo); |
| 93791 | pIdx->pKeyInfo = 0; |
| 93792 | } |
| 93793 | #endif |
| 93794 | if( pIdx->pKeyInfo==0 ){ |
| 93795 | int i; |
| 93796 | int nCol = pIdx->nColumn; |
| 93797 | int nKey = pIdx->nKeyCol; |
| 93798 | KeyInfo *pKey; |
| 93799 | if( pIdx->uniqNotNull ){ |
| 93800 | pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey); |
| 93801 | }else{ |
| 93802 | pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); |
| 93803 | } |
| 93804 | if( pKey ){ |
| 93805 | assert( sqlite3KeyInfoIsWriteable(pKey) ); |
| 93806 | for(i=0; i<nCol; i++){ |
| 93807 | char *zColl = pIdx->azColl[i]; |
| 93808 | assert( zColl!=0 ); |
| 93809 | pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 : |
| 93810 | sqlite3LocateCollSeq(pParse, zColl); |
| 93811 | pKey->aSortOrder[i] = pIdx->aSortOrder[i]; |
| 93812 | } |
| 93813 | if( pParse->nErr ){ |
| 93814 | sqlite3KeyInfoUnref(pKey); |
| 93815 | }else{ |
| 93816 | pIdx->pKeyInfo = pKey; |
| 93817 | } |
| 93818 | } |
| 93819 | } |
| 93820 | return sqlite3KeyInfoRef(pIdx->pKeyInfo); |
| 93821 | } |
| 93822 | |
| 93823 | #ifndef SQLITE_OMIT_CTE |
| 93824 | /* |
| 93825 | ** This routine is invoked once per CTE by the parser while parsing a |
| @@ -94596,12 +94743,12 @@ | |
| 94596 | const char *zDb; /* Name of database holding pTab */ |
| 94597 | int i; /* Loop counter */ |
| 94598 | WhereInfo *pWInfo; /* Information about the WHERE clause */ |
| 94599 | Index *pIdx; /* For looping over indices of the table */ |
| 94600 | int iTabCur; /* Cursor number for the table */ |
| 94601 | int iDataCur; /* VDBE cursor for the canonical data source */ |
| 94602 | int iIdxCur; /* Cursor number of the first index */ |
| 94603 | int nIdx; /* Number of indices */ |
| 94604 | sqlite3 *db; /* Main database structure */ |
| 94605 | AuthContext sContext; /* Authorization context */ |
| 94606 | NameContext sNC; /* Name context to resolve expressions in */ |
| 94607 | int iDb; /* Database number */ |
| @@ -97436,11 +97583,11 @@ | |
| 97436 | assert( nIncr==1 ); |
| 97437 | sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, |
| 97438 | OE_Abort, 0, P4_STATIC, P5_ConstraintFK); |
| 97439 | }else{ |
| 97440 | if( nIncr>0 && pFKey->isDeferred==0 ){ |
| 97441 | sqlite3ParseToplevel(pParse)->mayAbort = 1; |
| 97442 | } |
| 97443 | sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); |
| 97444 | } |
| 97445 | |
| 97446 | sqlite3VdbeResolveLabel(v, iOk); |
| @@ -97507,10 +97654,14 @@ | |
| 97507 | ** This function is called to generate code executed when a row is deleted |
| 97508 | ** from the parent table of foreign key constraint pFKey and, if pFKey is |
| 97509 | ** deferred, when a row is inserted into the same table. When generating |
| 97510 | ** code for an SQL UPDATE operation, this function may be called twice - |
| 97511 | ** once to "delete" the old row and once to "insert" the new row. |
| 97512 | ** |
| 97513 | ** The code generated by this function scans through the rows in the child |
| 97514 | ** table that correspond to the parent table row being deleted or inserted. |
| 97515 | ** For each child row found, one of the following actions is taken: |
| 97516 | ** |
| @@ -97624,17 +97775,13 @@ | |
| 97624 | sNameContext.pSrcList = pSrc; |
| 97625 | sNameContext.pParse = pParse; |
| 97626 | sqlite3ResolveExprNames(&sNameContext, pWhere); |
| 97627 | |
| 97628 | /* Create VDBE to loop through the entries in pSrc that match the WHERE |
| 97629 | ** clause. If the constraint is not deferred, throw an exception for |
| 97630 | ** each row found. Otherwise, for deferred constraints, increment the |
| 97631 | ** deferred constraint counter by nIncr for each row selected. */ |
| 97632 | pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); |
| 97633 | if( nIncr>0 && pFKey->isDeferred==0 ){ |
| 97634 | sqlite3ParseToplevel(pParse)->mayAbort = 1; |
| 97635 | } |
| 97636 | sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); |
| 97637 | if( pWInfo ){ |
| 97638 | sqlite3WhereEnd(pWInfo); |
| 97639 | } |
| 97640 | |
| @@ -97808,10 +97955,28 @@ | |
| 97808 | } |
| 97809 | } |
| 97810 | } |
| 97811 | return 0; |
| 97812 | } |
| 97813 | |
| 97814 | /* |
| 97815 | ** This function is called when inserting, deleting or updating a row of |
| 97816 | ** table pTab to generate VDBE code to perform foreign key constraint |
| 97817 | ** processing for the operation. |
| @@ -97861,11 +98026,11 @@ | |
| 97861 | Index *pIdx = 0; /* Index on key columns in pTo */ |
| 97862 | int *aiFree = 0; |
| 97863 | int *aiCol; |
| 97864 | int iCol; |
| 97865 | int i; |
| 97866 | int isIgnore = 0; |
| 97867 | |
| 97868 | if( aChange |
| 97869 | && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0 |
| 97870 | && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0 |
| 97871 | ){ |
| @@ -97920,11 +98085,11 @@ | |
| 97920 | ** values read from the parent table are NULL. */ |
| 97921 | if( db->xAuth ){ |
| 97922 | int rcauth; |
| 97923 | char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; |
| 97924 | rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); |
| 97925 | isIgnore = (rcauth==SQLITE_IGNORE); |
| 97926 | } |
| 97927 | #endif |
| 97928 | } |
| 97929 | |
| 97930 | /* Take a shared-cache advisory read-lock on the parent table. Allocate |
| @@ -97935,16 +98100,22 @@ | |
| 97935 | |
| 97936 | if( regOld!=0 ){ |
| 97937 | /* A row is being removed from the child table. Search for the parent. |
| 97938 | ** If the parent does not exist, removing the child row resolves an |
| 97939 | ** outstanding foreign key constraint violation. */ |
| 97940 | fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore); |
| 97941 | } |
| 97942 | if( regNew!=0 ){ |
| 97943 | /* A row is being added to the child table. If a parent row cannot |
| 97944 | ** be found, adding the child row has violated the FK constraint. */ |
| 97945 | fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore); |
| 97946 | } |
| 97947 | |
| 97948 | sqlite3DbFree(db, aiFree); |
| 97949 | } |
| 97950 | |
| @@ -97961,12 +98132,12 @@ | |
| 97961 | |
| 97962 | if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs) |
| 97963 | && !pParse->pToplevel && !pParse->isMultiWrite |
| 97964 | ){ |
| 97965 | assert( regOld==0 && regNew!=0 ); |
| 97966 | /* Inserting a single row into a parent table cannot cause an immediate |
| 97967 | ** foreign key violation. So do nothing in this case. */ |
| 97968 | continue; |
| 97969 | } |
| 97970 | |
| 97971 | if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ |
| 97972 | if( !isIgnoreErrors || db->mallocFailed ) return; |
| @@ -97986,17 +98157,32 @@ | |
| 97986 | |
| 97987 | if( regNew!=0 ){ |
| 97988 | fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); |
| 97989 | } |
| 97990 | if( regOld!=0 ){ |
| 97991 | /* If there is a RESTRICT action configured for the current operation |
| 97992 | ** on the parent table of this FK, then throw an exception |
| 97993 | ** immediately if the FK constraint is violated, even if this is a |
| 97994 | ** deferred trigger. That's what RESTRICT means. To defer checking |
| 97995 | ** the constraint, the FK should specify NO ACTION (represented |
| 97996 | ** using OE_None). NO ACTION is the default. */ |
| 97997 | fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); |
| 97998 | } |
| 97999 | pItem->zName = 0; |
| 98000 | sqlite3SrcListDelete(db, pSrc); |
| 98001 | } |
| 98002 | sqlite3DbFree(db, aiCol); |
| @@ -101895,10 +102081,11 @@ | |
| 101895 | #define PragTyp_KEY 38 |
| 101896 | #define PragTyp_REKEY 39 |
| 101897 | #define PragTyp_LOCK_STATUS 40 |
| 101898 | #define PragTyp_PARSER_TRACE 41 |
| 101899 | #define PragFlag_NeedSchema 0x01 |
| 101900 | static const struct sPragmaNames { |
| 101901 | const char *const zName; /* Name of pragma */ |
| 101902 | u8 ePragTyp; /* PragTyp_XXX value */ |
| 101903 | u8 mPragFlag; /* Zero or more PragFlag_XXX values */ |
| 101904 | u32 iArg; /* Extra argument */ |
| @@ -101911,11 +102098,11 @@ | |
| 101911 | #endif |
| 101912 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 101913 | { /* zName: */ "application_id", |
| 101914 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 101915 | /* ePragFlag: */ 0, |
| 101916 | /* iArg: */ 0 }, |
| 101917 | #endif |
| 101918 | #if !defined(SQLITE_OMIT_AUTOVACUUM) |
| 101919 | { /* zName: */ "auto_vacuum", |
| 101920 | /* ePragTyp: */ PragTyp_AUTO_VACUUM, |
| 101921 | /* ePragFlag: */ PragFlag_NeedSchema, |
| @@ -101977,10 +102164,16 @@ | |
| 101977 | { /* zName: */ "data_store_directory", |
| 101978 | /* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY, |
| 101979 | /* ePragFlag: */ 0, |
| 101980 | /* iArg: */ 0 }, |
| 101981 | #endif |
| 101982 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 101983 | { /* zName: */ "database_list", |
| 101984 | /* ePragTyp: */ PragTyp_DATABASE_LIST, |
| 101985 | /* ePragFlag: */ PragFlag_NeedSchema, |
| 101986 | /* iArg: */ 0 }, |
| @@ -102032,12 +102225,12 @@ | |
| 102032 | #endif |
| 102033 | #endif |
| 102034 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102035 | { /* zName: */ "freelist_count", |
| 102036 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102037 | /* ePragFlag: */ 0, |
| 102038 | /* iArg: */ 0 }, |
| 102039 | #endif |
| 102040 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 102041 | { /* zName: */ "full_column_names", |
| 102042 | /* ePragTyp: */ PragTyp_FLAG, |
| 102043 | /* ePragFlag: */ 0, |
| @@ -102185,11 +102378,11 @@ | |
| 102185 | #endif |
| 102186 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102187 | { /* zName: */ "schema_version", |
| 102188 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102189 | /* ePragFlag: */ 0, |
| 102190 | /* iArg: */ 0 }, |
| 102191 | #endif |
| 102192 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) |
| 102193 | { /* zName: */ "secure_delete", |
| 102194 | /* ePragTyp: */ PragTyp_SECURE_DELETE, |
| 102195 | /* ePragFlag: */ 0, |
| @@ -102251,11 +102444,11 @@ | |
| 102251 | /* iArg: */ 0 }, |
| 102252 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102253 | { /* zName: */ "user_version", |
| 102254 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102255 | /* ePragFlag: */ 0, |
| 102256 | /* iArg: */ 0 }, |
| 102257 | #endif |
| 102258 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 102259 | #if defined(SQLITE_DEBUG) |
| 102260 | { /* zName: */ "vdbe_addoptrace", |
| 102261 | /* ePragTyp: */ PragTyp_FLAG, |
| @@ -102294,11 +102487,11 @@ | |
| 102294 | /* ePragTyp: */ PragTyp_FLAG, |
| 102295 | /* ePragFlag: */ 0, |
| 102296 | /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode }, |
| 102297 | #endif |
| 102298 | }; |
| 102299 | /* Number of pragmas: 57 on by default, 70 total. */ |
| 102300 | /* End of the automatically generated pragma table. |
| 102301 | ***************************************************************************/ |
| 102302 | |
| 102303 | /* |
| 102304 | ** Interpret the given string as a safety level. Return 0 for OFF, |
| @@ -102544,11 +102737,11 @@ | |
| 102544 | char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */ |
| 102545 | const char *zDb = 0; /* The database name */ |
| 102546 | Token *pId; /* Pointer to <id> token */ |
| 102547 | char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */ |
| 102548 | int iDb; /* Database index for <database> */ |
| 102549 | int lwr, upr, mid; /* Binary search bounds */ |
| 102550 | int rc; /* return value form SQLITE_FCNTL_PRAGMA */ |
| 102551 | sqlite3 *db = pParse->db; /* The database connection */ |
| 102552 | Db *pDb; /* The specific database being pragmaed */ |
| 102553 | Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */ |
| 102554 | |
| @@ -103904,11 +104097,12 @@ | |
| 103904 | !(DbHasProperty(db, 0, DB_SchemaLoaded)) || |
| 103905 | DbHasProperty(db, 0, DB_Empty) |
| 103906 | ){ |
| 103907 | for(pEnc=&encnames[0]; pEnc->zName; pEnc++){ |
| 103908 | if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){ |
| 103909 | ENC(pParse->db) = pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE; |
| 103910 | break; |
| 103911 | } |
| 103912 | } |
| 103913 | if( !pEnc->zName ){ |
| 103914 | sqlite3ErrorMsg(pParse, "unsupported encoding: %s", zRight); |
| @@ -103949,28 +104143,13 @@ | |
| 103949 | ** |
| 103950 | ** The user-version is not used internally by SQLite. It may be used by |
| 103951 | ** applications for any purpose. |
| 103952 | */ |
| 103953 | case PragTyp_HEADER_VALUE: { |
| 103954 | int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */ |
| 103955 | sqlite3VdbeUsesBtree(v, iDb); |
| 103956 | switch( zLeft[0] ){ |
| 103957 | case 'a': case 'A': |
| 103958 | iCookie = BTREE_APPLICATION_ID; |
| 103959 | break; |
| 103960 | case 'f': case 'F': |
| 103961 | iCookie = BTREE_FREE_PAGE_COUNT; |
| 103962 | break; |
| 103963 | case 's': case 'S': |
| 103964 | iCookie = BTREE_SCHEMA_VERSION; |
| 103965 | break; |
| 103966 | default: |
| 103967 | iCookie = BTREE_USER_VERSION; |
| 103968 | break; |
| 103969 | } |
| 103970 | |
| 103971 | if( zRight && iCookie!=BTREE_FREE_PAGE_COUNT ){ |
| 103972 | /* Write the specified cookie value */ |
| 103973 | static const VdbeOpList setCookie[] = { |
| 103974 | { OP_Transaction, 0, 1, 0}, /* 0 */ |
| 103975 | { OP_Integer, 0, 1, 0}, /* 1 */ |
| 103976 | { OP_SetCookie, 0, 0, 1}, /* 2 */ |
| @@ -104612,13 +104791,15 @@ | |
| 104612 | SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){ |
| 104613 | int i, rc; |
| 104614 | int commit_internal = !(db->flags&SQLITE_InternChanges); |
| 104615 | |
| 104616 | assert( sqlite3_mutex_held(db->mutex) ); |
| 104617 | assert( db->init.busy==0 ); |
| 104618 | rc = SQLITE_OK; |
| 104619 | db->init.busy = 1; |
| 104620 | for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ |
| 104621 | if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue; |
| 104622 | rc = sqlite3InitOne(db, i, pzErrMsg); |
| 104623 | if( rc ){ |
| 104624 | sqlite3ResetOneSchema(db, i); |
| @@ -105169,24 +105350,29 @@ | |
| 105169 | u8 sortFlags; /* Zero or more SORTFLAG_* bits */ |
| 105170 | }; |
| 105171 | #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ |
| 105172 | |
| 105173 | /* |
| 105174 | ** Delete all the content of a Select structure but do not deallocate |
| 105175 | ** the select structure itself. |
| 105176 | */ |
| 105177 | static void clearSelect(sqlite3 *db, Select *p){ |
| 105178 | sqlite3ExprListDelete(db, p->pEList); |
| 105179 | sqlite3SrcListDelete(db, p->pSrc); |
| 105180 | sqlite3ExprDelete(db, p->pWhere); |
| 105181 | sqlite3ExprListDelete(db, p->pGroupBy); |
| 105182 | sqlite3ExprDelete(db, p->pHaving); |
| 105183 | sqlite3ExprListDelete(db, p->pOrderBy); |
| 105184 | sqlite3SelectDelete(db, p->pPrior); |
| 105185 | sqlite3ExprDelete(db, p->pLimit); |
| 105186 | sqlite3ExprDelete(db, p->pOffset); |
| 105187 | sqlite3WithDelete(db, p->pWith); |
| 105188 | } |
| 105189 | |
| 105190 | /* |
| 105191 | ** Initialize a SelectDest structure. |
| 105192 | */ |
| @@ -105241,12 +105427,11 @@ | |
| 105241 | pNew->pOffset = pOffset; |
| 105242 | assert( pOffset==0 || pLimit!=0 ); |
| 105243 | pNew->addrOpenEphm[0] = -1; |
| 105244 | pNew->addrOpenEphm[1] = -1; |
| 105245 | if( db->mallocFailed ) { |
| 105246 | clearSelect(db, pNew); |
| 105247 | if( pNew!=&standin ) sqlite3DbFree(db, pNew); |
| 105248 | pNew = 0; |
| 105249 | }else{ |
| 105250 | assert( pNew->pSrc!=0 || pParse->nErr>0 ); |
| 105251 | } |
| 105252 | assert( pNew!=&standin ); |
| @@ -105267,14 +105452,11 @@ | |
| 105267 | |
| 105268 | /* |
| 105269 | ** Delete the given Select structure and all of its substructures. |
| 105270 | */ |
| 105271 | SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){ |
| 105272 | if( p ){ |
| 105273 | clearSelect(db, p); |
| 105274 | sqlite3DbFree(db, p); |
| 105275 | } |
| 105276 | } |
| 105277 | |
| 105278 | /* |
| 105279 | ** Return a pointer to the right-most SELECT statement in a compound. |
| 105280 | */ |
| @@ -105653,11 +105835,13 @@ | |
| 105653 | if( pParse->db->mallocFailed ) return; |
| 105654 | pOp->p2 = nKey + nData; |
| 105655 | pKI = pOp->p4.pKeyInfo; |
| 105656 | memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */ |
| 105657 | sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO); |
| 105658 | pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, 1); |
| 105659 | addrJmp = sqlite3VdbeCurrentAddr(v); |
| 105660 | sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); |
| 105661 | pSort->labelBkOut = sqlite3VdbeMakeLabel(v); |
| 105662 | pSort->regReturn = ++pParse->nMem; |
| 105663 | sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
| @@ -106164,11 +106348,11 @@ | |
| 106164 | struct ExprList_item *pItem; |
| 106165 | sqlite3 *db = pParse->db; |
| 106166 | int i; |
| 106167 | |
| 106168 | nExpr = pList->nExpr; |
| 106169 | pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra-iStart, 1); |
| 106170 | if( pInfo ){ |
| 106171 | assert( sqlite3KeyInfoIsWriteable(pInfo) ); |
| 106172 | for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){ |
| 106173 | CollSeq *pColl; |
| 106174 | pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr); |
| @@ -107186,10 +107370,70 @@ | |
| 107186 | Parse *pParse, /* Parsing context */ |
| 107187 | Select *p, /* The right-most of SELECTs to be coded */ |
| 107188 | SelectDest *pDest /* What to do with query results */ |
| 107189 | ); |
| 107190 | |
| 107191 | |
| 107192 | /* |
| 107193 | ** This routine is called to process a compound query form from |
| 107194 | ** two or more separate queries using UNION, UNION ALL, EXCEPT, or |
| 107195 | ** INTERSECT |
| @@ -107266,22 +107510,24 @@ | |
| 107266 | assert( p->pEList ); |
| 107267 | sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr); |
| 107268 | sqlite3VdbeChangeP5(v, BTREE_UNORDERED); |
| 107269 | dest.eDest = SRT_Table; |
| 107270 | } |
| 107271 | |
| 107272 | /* Make sure all SELECTs in the statement have the same number of elements |
| 107273 | ** in their result sets. |
| 107274 | */ |
| 107275 | assert( p->pEList && pPrior->pEList ); |
| 107276 | if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ |
| 107277 | if( p->selFlags & SF_Values ){ |
| 107278 | sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); |
| 107279 | }else{ |
| 107280 | sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" |
| 107281 | " do not have the same number of result columns", selectOpName(p->op)); |
| 107282 | } |
| 107283 | rc = 1; |
| 107284 | goto multi_select_end; |
| 107285 | } |
| 107286 | |
| 107287 | #ifndef SQLITE_OMIT_CTE |
| @@ -109163,11 +109409,13 @@ | |
| 109163 | if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){ |
| 109164 | return WRC_Prune; |
| 109165 | } |
| 109166 | pTabList = p->pSrc; |
| 109167 | pEList = p->pEList; |
| 109168 | sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); |
| 109169 | |
| 109170 | /* Make sure cursor numbers have been assigned to all entries in |
| 109171 | ** the FROM clause of the SELECT statement. |
| 109172 | */ |
| 109173 | sqlite3SrcListAssignCursors(pParse, pTabList); |
| @@ -109454,11 +109702,13 @@ | |
| 109454 | if( pParse->hasCompound ){ |
| 109455 | w.xSelectCallback = convertCompoundSelectToSubquery; |
| 109456 | sqlite3WalkSelect(&w, pSelect); |
| 109457 | } |
| 109458 | w.xSelectCallback = selectExpander; |
| 109459 | w.xSelectCallback2 = selectPopWith; |
| 109460 | sqlite3WalkSelect(&w, pSelect); |
| 109461 | } |
| 109462 | |
| 109463 | |
| 109464 | #ifndef SQLITE_OMIT_SUBQUERY |
| @@ -109968,11 +110218,11 @@ | |
| 109968 | ** we figure out that the sorting index is not needed. The addrSortIndex |
| 109969 | ** variable is used to facilitate that change. |
| 109970 | */ |
| 109971 | if( sSort.pOrderBy ){ |
| 109972 | KeyInfo *pKeyInfo; |
| 109973 | pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, 0); |
| 109974 | sSort.iECursor = pParse->nTab++; |
| 109975 | sSort.addrSortIndex = |
| 109976 | sqlite3VdbeAddOp4(v, OP_OpenEphemeral, |
| 109977 | sSort.iECursor, sSort.pOrderBy->nExpr+1+pEList->nExpr, 0, |
| 109978 | (char*)pKeyInfo, P4_KEYINFO |
| @@ -110142,11 +110392,11 @@ | |
| 110142 | ** implement it. Allocate that sorting index now. If it turns out |
| 110143 | ** that we do not need it after all, the OP_SorterOpen instruction |
| 110144 | ** will be converted into a Noop. |
| 110145 | */ |
| 110146 | sAggInfo.sortingIdx = pParse->nTab++; |
| 110147 | pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, 0); |
| 110148 | addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, |
| 110149 | sAggInfo.sortingIdx, sAggInfo.nSortingColumn, |
| 110150 | 0, (char*)pKeyInfo, P4_KEYINFO); |
| 110151 | |
| 110152 | /* Initialize memory locations used by GROUP BY aggregate processing |
| @@ -110756,11 +111006,11 @@ | |
| 110756 | ){ |
| 110757 | int rc; |
| 110758 | TabResult res; |
| 110759 | |
| 110760 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 110761 | if( pazResult==0 ) return SQLITE_MISUSE_BKPT; |
| 110762 | #endif |
| 110763 | *pazResult = 0; |
| 110764 | if( pnColumn ) *pnColumn = 0; |
| 110765 | if( pnRow ) *pnRow = 0; |
| 110766 | if( pzErrMsg ) *pzErrMsg = 0; |
| @@ -118626,11 +118876,10 @@ | |
| 118626 | sqlite3_free(p->u.vtab.idxStr); |
| 118627 | p->u.vtab.needFree = 0; |
| 118628 | p->u.vtab.idxStr = 0; |
| 118629 | }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){ |
| 118630 | sqlite3DbFree(db, p->u.btree.pIndex->zColAff); |
| 118631 | sqlite3KeyInfoUnref(p->u.btree.pIndex->pKeyInfo); |
| 118632 | sqlite3DbFree(db, p->u.btree.pIndex); |
| 118633 | p->u.btree.pIndex = 0; |
| 118634 | } |
| 118635 | } |
| 118636 | } |
| @@ -123783,17 +124032,23 @@ | |
| 123783 | Select *p = yymsp[0].minor.yy3, *pNext, *pLoop; |
| 123784 | if( p ){ |
| 123785 | int cnt = 0, mxSelect; |
| 123786 | p->pWith = yymsp[-1].minor.yy59; |
| 123787 | if( p->pPrior ){ |
| 123788 | pNext = 0; |
| 123789 | for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ |
| 123790 | pLoop->pNext = pNext; |
| 123791 | pLoop->selFlags |= SF_Compound; |
| 123792 | } |
| 123793 | mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; |
| 123794 | if( mxSelect && cnt>mxSelect ){ |
| 123795 | sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); |
| 123796 | } |
| 123797 | } |
| 123798 | }else{ |
| 123799 | sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); |
| @@ -125633,10 +125888,13 @@ | |
| 125633 | u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */ |
| 125634 | sqlite3 *db = pParse->db; /* The database connection */ |
| 125635 | int mxSqlLen; /* Max length of an SQL string */ |
| 125636 | |
| 125637 | |
| 125638 | mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; |
| 125639 | if( db->nVdbeActive==0 ){ |
| 125640 | db->u1.isInterrupted = 0; |
| 125641 | } |
| 125642 | pParse->rc = SQLITE_OK; |
| @@ -125871,17 +126129,10 @@ | |
| 125871 | */ |
| 125872 | SQLITE_API int sqlite3_complete(const char *zSql){ |
| 125873 | u8 state = 0; /* Current state, using numbers defined in header comment */ |
| 125874 | u8 token; /* Value of the next token */ |
| 125875 | |
| 125876 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 125877 | if( zSql==0 ){ |
| 125878 | (void)SQLITE_MISUSE_BKPT; |
| 125879 | return 0; |
| 125880 | } |
| 125881 | #endif |
| 125882 | |
| 125883 | #ifndef SQLITE_OMIT_TRIGGER |
| 125884 | /* A complex statement machine used to detect the end of a CREATE TRIGGER |
| 125885 | ** statement. This is the normal case. |
| 125886 | */ |
| 125887 | static const u8 trans[8][8] = { |
| @@ -125906,10 +126157,17 @@ | |
| 125906 | /* 0 INVALID: */ { 1, 0, 2, }, |
| 125907 | /* 1 START: */ { 1, 1, 2, }, |
| 125908 | /* 2 NORMAL: */ { 1, 2, 2, }, |
| 125909 | }; |
| 125910 | #endif /* SQLITE_OMIT_TRIGGER */ |
| 125911 | |
| 125912 | while( *zSql ){ |
| 125913 | switch( *zSql ){ |
| 125914 | case ';': { /* A semicolon */ |
| 125915 | token = tkSEMI; |
| @@ -126208,11 +126466,11 @@ | |
| 126208 | ** If the following function pointer is not NULL and if |
| 126209 | ** SQLITE_ENABLE_IOTRACE is enabled, then messages describing |
| 126210 | ** I/O active are written using this function. These messages |
| 126211 | ** are intended for debugging activity only. |
| 126212 | */ |
| 126213 | SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*, ...) = 0; |
| 126214 | #endif |
| 126215 | |
| 126216 | /* |
| 126217 | ** If the following global variable points to a string which is the |
| 126218 | ** name of a directory, then that directory will be used to store |
| @@ -126417,10 +126675,17 @@ | |
| 126417 | ** routine is not threadsafe. But it is safe to invoke this routine |
| 126418 | ** on when SQLite is already shut down. If SQLite is already shut down |
| 126419 | ** when this routine is invoked, then this routine is a harmless no-op. |
| 126420 | */ |
| 126421 | SQLITE_API int sqlite3_shutdown(void){ |
| 126422 | if( sqlite3GlobalConfig.isInit ){ |
| 126423 | #ifdef SQLITE_EXTRA_SHUTDOWN |
| 126424 | void SQLITE_EXTRA_SHUTDOWN(void); |
| 126425 | SQLITE_EXTRA_SHUTDOWN(); |
| 126426 | #endif |
| @@ -126732,10 +126997,15 @@ | |
| 126732 | ** heap. */ |
| 126733 | sqlite3GlobalConfig.nHeap = va_arg(ap, int); |
| 126734 | break; |
| 126735 | } |
| 126736 | #endif |
| 126737 | |
| 126738 | default: { |
| 126739 | rc = SQLITE_ERROR; |
| 126740 | break; |
| 126741 | } |
| @@ -127178,20 +127448,10 @@ | |
| 127178 | |
| 127179 | /* Close all database connections */ |
| 127180 | for(j=0; j<db->nDb; j++){ |
| 127181 | struct Db *pDb = &db->aDb[j]; |
| 127182 | if( pDb->pBt ){ |
| 127183 | if( pDb->pSchema ){ |
| 127184 | /* Must clear the KeyInfo cache. See ticket [e4a18565a36884b00edf] */ |
| 127185 | sqlite3BtreeEnter(pDb->pBt); |
| 127186 | for(i=sqliteHashFirst(&pDb->pSchema->idxHash); i; i=sqliteHashNext(i)){ |
| 127187 | Index *pIdx = sqliteHashData(i); |
| 127188 | sqlite3KeyInfoUnref(pIdx->pKeyInfo); |
| 127189 | pIdx->pKeyInfo = 0; |
| 127190 | } |
| 127191 | sqlite3BtreeLeave(pDb->pBt); |
| 127192 | } |
| 127193 | sqlite3BtreeClose(pDb->pBt); |
| 127194 | pDb->pBt = 0; |
| 127195 | if( j!=1 ){ |
| 127196 | pDb->pSchema = 0; |
| 127197 | } |
| @@ -127494,11 +127754,11 @@ | |
| 127494 | */ |
| 127495 | static int sqliteDefaultBusyCallback( |
| 127496 | void *ptr, /* Database connection */ |
| 127497 | int count /* Number of times table has been busy */ |
| 127498 | ){ |
| 127499 | #if SQLITE_OS_WIN || (defined(HAVE_USLEEP) && HAVE_USLEEP) |
| 127500 | static const u8 delays[] = |
| 127501 | { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; |
| 127502 | static const u8 totals[] = |
| 127503 | { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 }; |
| 127504 | # define NDELAY ArraySize(delays) |
| @@ -128110,10 +128370,11 @@ | |
| 128110 | } |
| 128111 | if( iDb<0 ){ |
| 128112 | rc = SQLITE_ERROR; |
| 128113 | sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb); |
| 128114 | }else{ |
| 128115 | rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); |
| 128116 | sqlite3Error(db, rc); |
| 128117 | } |
| 128118 | rc = sqlite3ApiExit(db, rc); |
| 128119 | sqlite3_mutex_leave(db->mutex); |
| @@ -128315,36 +128576,10 @@ | |
| 128315 | */ |
| 128316 | SQLITE_API const char *sqlite3_errstr(int rc){ |
| 128317 | return sqlite3ErrStr(rc); |
| 128318 | } |
| 128319 | |
| 128320 | /* |
| 128321 | ** Invalidate all cached KeyInfo objects for database connection "db" |
| 128322 | */ |
| 128323 | static void invalidateCachedKeyInfo(sqlite3 *db){ |
| 128324 | Db *pDb; /* A single database */ |
| 128325 | int iDb; /* The database index number */ |
| 128326 | HashElem *k; /* For looping over tables in pDb */ |
| 128327 | Table *pTab; /* A table in the database */ |
| 128328 | Index *pIdx; /* Each index */ |
| 128329 | |
| 128330 | for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){ |
| 128331 | if( pDb->pBt==0 ) continue; |
| 128332 | sqlite3BtreeEnter(pDb->pBt); |
| 128333 | for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ |
| 128334 | pTab = (Table*)sqliteHashData(k); |
| 128335 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ |
| 128336 | if( pIdx->pKeyInfo && pIdx->pKeyInfo->db==db ){ |
| 128337 | sqlite3KeyInfoUnref(pIdx->pKeyInfo); |
| 128338 | pIdx->pKeyInfo = 0; |
| 128339 | } |
| 128340 | } |
| 128341 | } |
| 128342 | sqlite3BtreeLeave(pDb->pBt); |
| 128343 | } |
| 128344 | } |
| 128345 | |
| 128346 | /* |
| 128347 | ** Create a new collating function for database "db". The name is zName |
| 128348 | ** and the encoding is enc. |
| 128349 | */ |
| 128350 | static int createCollation( |
| @@ -128384,11 +128619,10 @@ | |
| 128384 | sqlite3ErrorWithMsg(db, SQLITE_BUSY, |
| 128385 | "unable to delete/modify collation sequence due to active statements"); |
| 128386 | return SQLITE_BUSY; |
| 128387 | } |
| 128388 | sqlite3ExpirePreparedStatements(db); |
| 128389 | invalidateCachedKeyInfo(db); |
| 128390 | |
| 128391 | /* If collation sequence pColl was created directly by a call to |
| 128392 | ** sqlite3_create_collation, and not generated by synthCollSeq(), |
| 128393 | ** then any copies made by synthCollSeq() need to be invalidated. |
| 128394 | ** Also, collation destructor - CollSeq.xDel() - function may need |
| @@ -128941,10 +129175,11 @@ | |
| 128941 | sqlite3Error(db, rc); |
| 128942 | goto opendb_out; |
| 128943 | } |
| 128944 | sqlite3BtreeEnter(db->aDb[0].pBt); |
| 128945 | db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); |
| 128946 | sqlite3BtreeLeave(db->aDb[0].pBt); |
| 128947 | db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); |
| 128948 | |
| 128949 | /* The default safety_level for the main database is 'full'; for the temp |
| 128950 | ** database it is 'NONE'. This matches the pager layer defaults. |
| @@ -129099,11 +129334,11 @@ | |
| 129099 | if( zFilename8 ){ |
| 129100 | rc = openDatabase(zFilename8, ppDb, |
| 129101 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); |
| 129102 | assert( *ppDb || rc==SQLITE_NOMEM ); |
| 129103 | if( rc==SQLITE_OK && !DbHasProperty(*ppDb, 0, DB_SchemaLoaded) ){ |
| 129104 | ENC(*ppDb) = SQLITE_UTF16NATIVE; |
| 129105 | } |
| 129106 | }else{ |
| 129107 | rc = SQLITE_NOMEM; |
| 129108 | } |
| 129109 | sqlite3ValueFree(pVal); |
| @@ -129310,11 +129545,11 @@ | |
| 129310 | ){ |
| 129311 | int rc; |
| 129312 | char *zErrMsg = 0; |
| 129313 | Table *pTab = 0; |
| 129314 | Column *pCol = 0; |
| 129315 | int iCol; |
| 129316 | |
| 129317 | char const *zDataType = 0; |
| 129318 | char const *zCollSeq = 0; |
| 129319 | int notnull = 0; |
| 129320 | int primarykey = 0; |
| @@ -129841,32 +130076,34 @@ | |
| 129841 | /* |
| 129842 | ** Return the filename of the database associated with a database |
| 129843 | ** connection. |
| 129844 | */ |
| 129845 | SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ |
| 129846 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 129847 | if( !sqlite3SafetyCheckOk(db) ){ |
| 129848 | (void)SQLITE_MISUSE_BKPT; |
| 129849 | return 0; |
| 129850 | } |
| 129851 | #endif |
| 129852 | Btree *pBt = sqlite3DbNameToBtree(db, zDbName); |
| 129853 | return pBt ? sqlite3BtreeGetFilename(pBt) : 0; |
| 129854 | } |
| 129855 | |
| 129856 | /* |
| 129857 | ** Return 1 if database is read-only or 0 if read/write. Return -1 if |
| 129858 | ** no such database exists. |
| 129859 | */ |
| 129860 | SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ |
| 129861 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 129862 | if( !sqlite3SafetyCheckOk(db) ){ |
| 129863 | (void)SQLITE_MISUSE_BKPT; |
| 129864 | return -1; |
| 129865 | } |
| 129866 | #endif |
| 129867 | Btree *pBt = sqlite3DbNameToBtree(db, zDbName); |
| 129868 | return pBt ? sqlite3BtreeIsReadonly(pBt) : -1; |
| 129869 | } |
| 129870 | |
| 129871 | /************** End of main.c ************************************************/ |
| 129872 | /************** Begin file notify.c ******************************************/ |
| @@ -132931,11 +133168,11 @@ | |
| 132931 | const char *zNode, /* Buffer containing segment interior node */ |
| 132932 | int nNode, /* Size of buffer at zNode */ |
| 132933 | sqlite3_int64 *piLeaf, /* Selected leaf node */ |
| 132934 | sqlite3_int64 *piLeaf2 /* Selected leaf node */ |
| 132935 | ){ |
| 132936 | int rc; /* Return code */ |
| 132937 | int iHeight; /* Height of this node in tree */ |
| 132938 | |
| 132939 | assert( piLeaf || piLeaf2 ); |
| 132940 | |
| 132941 | fts3GetVarint32(zNode, &iHeight); |
| @@ -132942,11 +133179,11 @@ | |
| 132942 | rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); |
| 132943 | assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) ); |
| 132944 | |
| 132945 | if( rc==SQLITE_OK && iHeight>1 ){ |
| 132946 | char *zBlob = 0; /* Blob read from %_segments table */ |
| 132947 | int nBlob; /* Size of zBlob in bytes */ |
| 132948 | |
| 132949 | if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ |
| 132950 | rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0); |
| 132951 | if( rc==SQLITE_OK ){ |
| 132952 | rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0); |
| @@ -134164,11 +134401,11 @@ | |
| 134164 | int idxNum, /* Strategy index */ |
| 134165 | const char *idxStr, /* Unused */ |
| 134166 | int nVal, /* Number of elements in apVal */ |
| 134167 | sqlite3_value **apVal /* Arguments for the indexing scheme */ |
| 134168 | ){ |
| 134169 | int rc; |
| 134170 | char *zSql; /* SQL statement used to access %_content */ |
| 134171 | int eSearch; |
| 134172 | Fts3Table *p = (Fts3Table *)pCursor->pVtab; |
| 134173 | Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; |
| 134174 | |
| @@ -140652,11 +140889,11 @@ | |
| 140652 | int argc, /* Number of elements in argv array */ |
| 140653 | const char * const *argv, /* xCreate/xConnect argument array */ |
| 140654 | sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ |
| 140655 | char **pzErr /* OUT: sqlite3_malloc'd error message */ |
| 140656 | ){ |
| 140657 | Fts3tokTable *pTab; |
| 140658 | const sqlite3_tokenizer_module *pMod = 0; |
| 140659 | sqlite3_tokenizer *pTok = 0; |
| 140660 | int rc; |
| 140661 | char **azDequote = 0; |
| 140662 | int nDequote; |
| @@ -144027,12 +144264,12 @@ | |
| 144027 | } |
| 144028 | rc = sqlite3_reset(pRange); |
| 144029 | |
| 144030 | if( bOk ){ |
| 144031 | int iIdx = 0; |
| 144032 | sqlite3_stmt *pUpdate1; |
| 144033 | sqlite3_stmt *pUpdate2; |
| 144034 | |
| 144035 | if( rc==SQLITE_OK ){ |
| 144036 | rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL_IDX, &pUpdate1, 0); |
| 144037 | } |
| 144038 | if( rc==SQLITE_OK ){ |
| @@ -149260,17 +149497,16 @@ | |
| 149260 | */ |
| 149261 | static int readInt16(u8 *p){ |
| 149262 | return (p[0]<<8) + p[1]; |
| 149263 | } |
| 149264 | static void readCoord(u8 *p, RtreeCoord *pCoord){ |
| 149265 | u32 i = ( |
| 149266 | (((u32)p[0]) << 24) + |
| 149267 | (((u32)p[1]) << 16) + |
| 149268 | (((u32)p[2]) << 8) + |
| 149269 | (((u32)p[3]) << 0) |
| 149270 | ); |
| 149271 | *(u32 *)pCoord = i; |
| 149272 | } |
| 149273 | static i64 readInt64(u8 *p){ |
| 149274 | return ( |
| 149275 | (((i64)p[0]) << 56) + |
| 149276 | (((i64)p[1]) << 48) + |
| @@ -149295,11 +149531,11 @@ | |
| 149295 | } |
| 149296 | static int writeCoord(u8 *p, RtreeCoord *pCoord){ |
| 149297 | u32 i; |
| 149298 | assert( sizeof(RtreeCoord)==4 ); |
| 149299 | assert( sizeof(u32)==4 ); |
| 149300 | i = *(u32 *)pCoord; |
| 149301 | p[0] = (i>>24)&0xFF; |
| 149302 | p[1] = (i>>16)&0xFF; |
| 149303 | p[2] = (i>> 8)&0xFF; |
| 149304 | p[3] = (i>> 0)&0xFF; |
| 149305 | return 4; |
| @@ -149626,18 +149862,17 @@ | |
| 149626 | RtreeNode *pNode, /* The node containing the cell to be read */ |
| 149627 | int iCell, /* Index of the cell within the node */ |
| 149628 | RtreeCell *pCell /* OUT: Write the cell contents here */ |
| 149629 | ){ |
| 149630 | u8 *pData; |
| 149631 | u8 *pEnd; |
| 149632 | RtreeCoord *pCoord; |
| 149633 | pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); |
| 149634 | pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell); |
| 149635 | pEnd = pData + pRtree->nDim*8; |
| 149636 | pCoord = pCell->aCoord; |
| 149637 | for(; pData<pEnd; pData+=4, pCoord++){ |
| 149638 | readCoord(pData, pCoord); |
| 149639 | } |
| 149640 | } |
| 149641 | |
| 149642 | |
| 149643 | /* Forward declaration for the function that does the work of |
| @@ -150073,11 +150308,11 @@ | |
| 150073 | } |
| 150074 | i = pCur->nPoint++; |
| 150075 | pNew = pCur->aPoint + i; |
| 150076 | pNew->rScore = rScore; |
| 150077 | pNew->iLevel = iLevel; |
| 150078 | assert( iLevel>=0 && iLevel<=RTREE_MAX_DEPTH ); |
| 150079 | while( i>0 ){ |
| 150080 | RtreeSearchPoint *pParent; |
| 150081 | j = (i-1)/2; |
| 150082 | pParent = pCur->aPoint + j; |
| 150083 | if( rtreeSearchPointCompare(pNew, pParent)>=0 ) break; |
| @@ -151696,10 +151931,12 @@ | |
| 151696 | RtreeCell cell; /* New cell to insert if nData>1 */ |
| 151697 | int bHaveRowid = 0; /* Set to 1 after new rowid is determined */ |
| 151698 | |
| 151699 | rtreeReference(pRtree); |
| 151700 | assert(nData>=1); |
| 151701 | |
| 151702 | /* Constraint handling. A write operation on an r-tree table may return |
| 151703 | ** SQLITE_CONSTRAINT for two reasons: |
| 151704 | ** |
| 151705 | ** 1. A duplicate rowid value, or |
| 151706 |
| --- src/sqlite3.c | |
| +++ src/sqlite3.c | |
| @@ -1,8 +1,8 @@ | |
| 1 | /****************************************************************************** |
| 2 | ** This file is an amalgamation of many separate C source files from SQLite |
| 3 | ** version 3.8.8.2. By combining all the individual C code files into this |
| 4 | ** single large file, the entire code can be compiled as a single translation |
| 5 | ** unit. This allows many compilers to do optimizations that would not be |
| 6 | ** possible if the files were compiled separately. Performance improvements |
| 7 | ** of 5% or more are commonly seen when SQLite is compiled as a single |
| 8 | ** translation unit. |
| @@ -41,10 +41,57 @@ | |
| 41 | ** |
| 42 | */ |
| 43 | #ifndef _SQLITEINT_H_ |
| 44 | #define _SQLITEINT_H_ |
| 45 | |
| 46 | /* |
| 47 | ** Include the header file used to customize the compiler options for MSVC. |
| 48 | ** This should be done first so that it can successfully prevent spurious |
| 49 | ** compiler warnings due to subsequent content in this file and other files |
| 50 | ** that are included by this file. |
| 51 | */ |
| 52 | /************** Include msvc.h in the middle of sqliteInt.h ******************/ |
| 53 | /************** Begin file msvc.h ********************************************/ |
| 54 | /* |
| 55 | ** 2015 January 12 |
| 56 | ** |
| 57 | ** The author disclaims copyright to this source code. In place of |
| 58 | ** a legal notice, here is a blessing: |
| 59 | ** |
| 60 | ** May you do good and not evil. |
| 61 | ** May you find forgiveness for yourself and forgive others. |
| 62 | ** May you share freely, never taking more than you give. |
| 63 | ** |
| 64 | ****************************************************************************** |
| 65 | ** |
| 66 | ** This file contains code that is specific to MSVC. |
| 67 | */ |
| 68 | #ifndef _MSVC_H_ |
| 69 | #define _MSVC_H_ |
| 70 | |
| 71 | #if defined(_MSC_VER) |
| 72 | #pragma warning(disable : 4054) |
| 73 | #pragma warning(disable : 4055) |
| 74 | #pragma warning(disable : 4100) |
| 75 | #pragma warning(disable : 4127) |
| 76 | #pragma warning(disable : 4152) |
| 77 | #pragma warning(disable : 4189) |
| 78 | #pragma warning(disable : 4206) |
| 79 | #pragma warning(disable : 4210) |
| 80 | #pragma warning(disable : 4232) |
| 81 | #pragma warning(disable : 4244) |
| 82 | #pragma warning(disable : 4305) |
| 83 | #pragma warning(disable : 4306) |
| 84 | #pragma warning(disable : 4702) |
| 85 | #pragma warning(disable : 4706) |
| 86 | #endif /* defined(_MSC_VER) */ |
| 87 | |
| 88 | #endif /* _MSVC_H_ */ |
| 89 | |
| 90 | /************** End of msvc.h ************************************************/ |
| 91 | /************** Continuing where we left off in sqliteInt.h ******************/ |
| 92 | |
| 93 | /* |
| 94 | ** These #defines should enable >2GB file support on POSIX if the |
| 95 | ** underlying operating system supports it. If the OS lacks |
| 96 | ** large file support, or if the OS is windows, these should be no-ops. |
| 97 | ** |
| @@ -229,13 +276,13 @@ | |
| 276 | ** |
| 277 | ** See also: [sqlite3_libversion()], |
| 278 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 279 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 280 | */ |
| 281 | #define SQLITE_VERSION "3.8.8.2" |
| 282 | #define SQLITE_VERSION_NUMBER 3008008 |
| 283 | #define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098" |
| 284 | |
| 285 | /* |
| 286 | ** CAPI3REF: Run-Time Library Version Numbers |
| 287 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 288 | ** |
| @@ -323,11 +370,11 @@ | |
| 370 | ** This interface only reports on the compile-time mutex setting |
| 371 | ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with |
| 372 | ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but |
| 373 | ** can be fully or partially disabled using a call to [sqlite3_config()] |
| 374 | ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], |
| 375 | ** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the |
| 376 | ** sqlite3_threadsafe() function shows only the compile-time setting of |
| 377 | ** thread safety, not any run-time changes to that setting made by |
| 378 | ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() |
| 379 | ** is unchanged by calls to sqlite3_config().)^ |
| 380 | ** |
| @@ -1692,11 +1739,11 @@ | |
| 1739 | ** configuration option. |
| 1740 | ** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to |
| 1741 | ** 8-byte aligned |
| 1742 | ** memory, the size of each page buffer (sz), and the number of pages (N). |
| 1743 | ** The sz argument should be the size of the largest database page |
| 1744 | ** (a power of two between 512 and 65536) plus some extra bytes for each |
| 1745 | ** page header. ^The number of extra bytes needed by the page header |
| 1746 | ** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option |
| 1747 | ** to [sqlite3_config()]. |
| 1748 | ** ^It is harmless, apart from the wasted memory, |
| 1749 | ** for the sz parameter to be larger than necessary. The first |
| @@ -1872,10 +1919,21 @@ | |
| 1919 | ** <dd>^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which |
| 1920 | ** is a pointer to an integer and writes into that integer the number of extra |
| 1921 | ** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE]. |
| 1922 | ** The amount of extra space required can change depending on the compiler, |
| 1923 | ** target platform, and SQLite version. |
| 1924 | ** |
| 1925 | ** [[SQLITE_CONFIG_PMASZ]] |
| 1926 | ** <dt>SQLITE_CONFIG_PMASZ |
| 1927 | ** <dd>^The SQLITE_CONFIG_PMASZ option takes a single parameter which |
| 1928 | ** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded |
| 1929 | ** sorter to that integer. The default minimum PMA Size is set by the |
| 1930 | ** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched |
| 1931 | ** to help with sort operations when multithreaded sorting |
| 1932 | ** is enabled (using the [PRAGMA threads] command) and the amount of content |
| 1933 | ** to be sorted exceeds the page size times the minimum of the |
| 1934 | ** [PRAGMA cache_size] setting and this value. |
| 1935 | ** </dl> |
| 1936 | */ |
| 1937 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 1938 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 1939 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| @@ -1898,10 +1956,11 @@ | |
| 1956 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 1957 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 1958 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 1959 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 1960 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 1961 | #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ |
| 1962 | |
| 1963 | /* |
| 1964 | ** CAPI3REF: Database Connection Configuration Options |
| 1965 | ** |
| 1966 | ** These constants are the available integer configuration options that |
| @@ -7307,16 +7366,14 @@ | |
| 7366 | |
| 7367 | /* |
| 7368 | ** CAPI3REF: Write-Ahead Log Commit Hook |
| 7369 | ** |
| 7370 | ** ^The [sqlite3_wal_hook()] function is used to register a callback that |
| 7371 | ** is invoked each time data is committed to a database in wal mode. |
| 7372 | ** |
| 7373 | ** ^(The callback is invoked by SQLite after the commit has taken place and |
| 7374 | ** the associated write-lock on the database released)^, so the implementation |
| 7375 | ** may read, write or [checkpoint] the database as required. |
| 7376 | ** |
| 7377 | ** ^The first parameter passed to the callback function when it is invoked |
| 7378 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when |
| 7379 | ** registering the callback. ^The second is a copy of the database handle. |
| @@ -7603,10 +7660,14 @@ | |
| 7660 | ** |
| 7661 | ** The following constants can be used for the T parameter to the |
| 7662 | ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a |
| 7663 | ** different metric for sqlite3_stmt_scanstatus() to return. |
| 7664 | ** |
| 7665 | ** When the value returned to V is a string, space to hold that string is |
| 7666 | ** managed by the prepared statement S and will be automatically freed when |
| 7667 | ** S is finalized. |
| 7668 | ** |
| 7669 | ** <dl> |
| 7670 | ** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt> |
| 7671 | ** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be |
| 7672 | ** set to the total number of times that the X-th loop has run.</dd> |
| 7673 | ** |
| @@ -7648,11 +7709,18 @@ | |
| 7709 | #define SQLITE_SCANSTAT_SELECTID 5 |
| 7710 | |
| 7711 | /* |
| 7712 | ** CAPI3REF: Prepared Statement Scan Status |
| 7713 | ** |
| 7714 | ** This interface returns information about the predicted and measured |
| 7715 | ** performance for pStmt. Advanced applications can use this |
| 7716 | ** interface to compare the predicted and the measured performance and |
| 7717 | ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. |
| 7718 | ** |
| 7719 | ** Since this interface is expected to be rarely used, it is only |
| 7720 | ** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS] |
| 7721 | ** compile-time option. |
| 7722 | ** |
| 7723 | ** The "iScanStatusOp" parameter determines which status information to return. |
| 7724 | ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior |
| 7725 | ** of this interface is undefined. |
| 7726 | ** ^The requested measurement is written into a variable pointed to by |
| @@ -7666,13 +7734,10 @@ | |
| 7734 | ** ^Statistics might not be available for all loops in all statements. ^In cases |
| 7735 | ** where there exist loops with no available statistics, this function behaves |
| 7736 | ** as if the loop did not exist - it returns non-zero and leave the variable |
| 7737 | ** that pOut points to unchanged. |
| 7738 | ** |
| 7739 | ** See also: [sqlite3_stmt_scanstatus_reset()] |
| 7740 | */ |
| 7741 | SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( |
| 7742 | sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ |
| 7743 | int idx, /* Index of loop to report on */ |
| @@ -9100,11 +9165,11 @@ | |
| 9165 | #define _BTREE_H_ |
| 9166 | |
| 9167 | /* TODO: This definition is just included so other modules compile. It |
| 9168 | ** needs to be revisited. |
| 9169 | */ |
| 9170 | #define SQLITE_N_BTREE_META 16 |
| 9171 | |
| 9172 | /* |
| 9173 | ** If defined as non-zero, auto-vacuum is enabled by default. Otherwise |
| 9174 | ** it must be turned on for each database using "PRAGMA auto_vacuum = 1". |
| 9175 | */ |
| @@ -9215,10 +9280,15 @@ | |
| 9280 | ** offset = 36 + (idx * 4) |
| 9281 | ** |
| 9282 | ** For example, the free-page-count field is located at byte offset 36 of |
| 9283 | ** the database file header. The incr-vacuum-flag field is located at |
| 9284 | ** byte offset 64 (== 36+4*7). |
| 9285 | ** |
| 9286 | ** The BTREE_DATA_VERSION value is not really a value stored in the header. |
| 9287 | ** It is a read-only number computed by the pager. But we merge it with |
| 9288 | ** the header value access routines since its access pattern is the same. |
| 9289 | ** Call it a "virtual meta value". |
| 9290 | */ |
| 9291 | #define BTREE_FREE_PAGE_COUNT 0 |
| 9292 | #define BTREE_SCHEMA_VERSION 1 |
| 9293 | #define BTREE_FILE_FORMAT 2 |
| 9294 | #define BTREE_DEFAULT_CACHE_SIZE 3 |
| @@ -9225,10 +9295,11 @@ | |
| 9295 | #define BTREE_LARGEST_ROOT_PAGE 4 |
| 9296 | #define BTREE_TEXT_ENCODING 5 |
| 9297 | #define BTREE_USER_VERSION 6 |
| 9298 | #define BTREE_INCR_VACUUM 7 |
| 9299 | #define BTREE_APPLICATION_ID 8 |
| 9300 | #define BTREE_DATA_VERSION 15 /* A virtual meta-value */ |
| 9301 | |
| 9302 | /* |
| 9303 | ** Values that may be OR'd together to form the second argument of an |
| 9304 | ** sqlite3BtreeCursorHints() call. |
| 9305 | */ |
| @@ -10006,10 +10077,11 @@ | |
| 10077 | SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); |
| 10078 | #endif |
| 10079 | |
| 10080 | /* Functions used to query pager state and configuration. */ |
| 10081 | SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); |
| 10082 | SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*); |
| 10083 | SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); |
| 10084 | SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); |
| 10085 | SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); |
| 10086 | SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); |
| 10087 | SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); |
| @@ -10747,10 +10819,11 @@ | |
| 10819 | i64 szMmap; /* Default mmap_size setting */ |
| 10820 | unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ |
| 10821 | int errCode; /* Most recent error code (SQLITE_*) */ |
| 10822 | int errMask; /* & result codes with this before returning */ |
| 10823 | u16 dbOptFlags; /* Flags to enable/disable optimizations */ |
| 10824 | u8 enc; /* Text encoding */ |
| 10825 | u8 autoCommit; /* The auto-commit flag. */ |
| 10826 | u8 temp_store; /* 1: file 2: memory 0: default */ |
| 10827 | u8 mallocFailed; /* True if we have seen a malloc failure */ |
| 10828 | u8 dfltLockMode; /* Default locking-mode for attached dbs */ |
| 10829 | signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ |
| @@ -10848,11 +10921,12 @@ | |
| 10921 | }; |
| 10922 | |
| 10923 | /* |
| 10924 | ** A macro to discover the encoding of a database. |
| 10925 | */ |
| 10926 | #define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc) |
| 10927 | #define ENC(db) ((db)->enc) |
| 10928 | |
| 10929 | /* |
| 10930 | ** Possible values for the sqlite3.flags. |
| 10931 | */ |
| 10932 | #define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ |
| @@ -11472,11 +11546,10 @@ | |
| 11546 | Index *pNext; /* The next index associated with the same table */ |
| 11547 | Schema *pSchema; /* Schema containing this index */ |
| 11548 | u8 *aSortOrder; /* for each column: True==DESC, False==ASC */ |
| 11549 | char **azColl; /* Array of collation sequence names for index */ |
| 11550 | Expr *pPartIdxWhere; /* WHERE clause for partial indices */ |
| 11551 | int tnum; /* DB Page containing root of this index */ |
| 11552 | LogEst szIdxRow; /* Estimated average row size in bytes */ |
| 11553 | u16 nKeyCol; /* Number of columns forming the key */ |
| 11554 | u16 nColumn; /* Number of columns stored in the index */ |
| 11555 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| @@ -12036,11 +12109,11 @@ | |
| 12109 | #define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */ |
| 12110 | #define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */ |
| 12111 | #define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */ |
| 12112 | #define SF_Compound 0x0040 /* Part of a compound query */ |
| 12113 | #define SF_Values 0x0080 /* Synthesized from VALUES clause */ |
| 12114 | #define SF_AllValues 0x0100 /* All terms of compound are VALUES */ |
| 12115 | #define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */ |
| 12116 | #define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */ |
| 12117 | #define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */ |
| 12118 | #define SF_MinMaxAgg 0x1000 /* Aggregate containing min() or max() */ |
| 12119 | |
| @@ -12526,10 +12599,11 @@ | |
| 12599 | void *pPage; /* Page cache memory */ |
| 12600 | int szPage; /* Size of each page in pPage[] */ |
| 12601 | int nPage; /* Number of pages in pPage[] */ |
| 12602 | int mxParserStack; /* maximum depth of the parser stack */ |
| 12603 | int sharedCacheEnabled; /* true if shared-cache mode enabled */ |
| 12604 | u32 szPma; /* Maximum Sorter PMA size */ |
| 12605 | /* The above might be initialized to non-zero. The following need to always |
| 12606 | ** initially be zero, however. */ |
| 12607 | int isInit; /* True after initialization has finished */ |
| 12608 | int inProgress; /* True while initialization in progress */ |
| 12609 | int isMutexInit; /* True after mutexes are initialized */ |
| @@ -12663,11 +12737,11 @@ | |
| 12737 | ** FTS4 is really an extension for FTS3. It is enabled using the |
| 12738 | ** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also call |
| 12739 | ** the SQLITE_ENABLE_FTS4 macro to serve as an alias for SQLITE_ENABLE_FTS3. |
| 12740 | */ |
| 12741 | #if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3) |
| 12742 | # define SQLITE_ENABLE_FTS3 1 |
| 12743 | #endif |
| 12744 | |
| 12745 | /* |
| 12746 | ** The ctype.h header is needed for non-ASCII systems. It is also |
| 12747 | ** needed by FTS3 when FTS3 is included in the amalgamation. |
| @@ -13448,11 +13522,11 @@ | |
| 13522 | ** print I/O tracing messages. |
| 13523 | */ |
| 13524 | #ifdef SQLITE_ENABLE_IOTRACE |
| 13525 | # define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; } |
| 13526 | SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe*); |
| 13527 | void (*sqlite3IoTrace)(const char*,...); |
| 13528 | #else |
| 13529 | # define IOTRACE(A) |
| 13530 | # define sqlite3VdbeIOTraceSql(X) |
| 13531 | #endif |
| 13532 | |
| @@ -13661,10 +13735,17 @@ | |
| 13735 | */ |
| 13736 | #ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN |
| 13737 | # define SQLITE_ALLOW_COVERING_INDEX_SCAN 1 |
| 13738 | #endif |
| 13739 | |
| 13740 | /* The minimum PMA size is set to this value multiplied by the database |
| 13741 | ** page size in bytes. |
| 13742 | */ |
| 13743 | #ifndef SQLITE_SORTER_PMASZ |
| 13744 | # define SQLITE_SORTER_PMASZ 250 |
| 13745 | #endif |
| 13746 | |
| 13747 | /* |
| 13748 | ** The following singleton contains the global configuration for |
| 13749 | ** the SQLite library. |
| 13750 | */ |
| 13751 | SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { |
| @@ -13691,10 +13772,11 @@ | |
| 13772 | (void*)0, /* pPage */ |
| 13773 | 0, /* szPage */ |
| 13774 | 0, /* nPage */ |
| 13775 | 0, /* mxParserStack */ |
| 13776 | 0, /* sharedCacheEnabled */ |
| 13777 | SQLITE_SORTER_PMASZ, /* szPma */ |
| 13778 | /* All the rest should always be initialized to zero */ |
| 13779 | 0, /* isInit */ |
| 13780 | 0, /* inProgress */ |
| 13781 | 0, /* isMutexInit */ |
| 13782 | 0, /* isMallocInit */ |
| @@ -13797,355 +13879,355 @@ | |
| 13879 | /* These macros are provided to "stringify" the value of the define |
| 13880 | ** for those options in which the value is meaningful. */ |
| 13881 | #define CTIMEOPT_VAL_(opt) #opt |
| 13882 | #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) |
| 13883 | |
| 13884 | #if SQLITE_32BIT_ROWID |
| 13885 | "32BIT_ROWID", |
| 13886 | #endif |
| 13887 | #if SQLITE_4_BYTE_ALIGNED_MALLOC |
| 13888 | "4_BYTE_ALIGNED_MALLOC", |
| 13889 | #endif |
| 13890 | #if SQLITE_CASE_SENSITIVE_LIKE |
| 13891 | "CASE_SENSITIVE_LIKE", |
| 13892 | #endif |
| 13893 | #if SQLITE_CHECK_PAGES |
| 13894 | "CHECK_PAGES", |
| 13895 | #endif |
| 13896 | #if SQLITE_COVERAGE_TEST |
| 13897 | "COVERAGE_TEST", |
| 13898 | #endif |
| 13899 | #if SQLITE_DEBUG |
| 13900 | "DEBUG", |
| 13901 | #endif |
| 13902 | #if SQLITE_DEFAULT_LOCKING_MODE |
| 13903 | "DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE), |
| 13904 | #endif |
| 13905 | #if defined(SQLITE_DEFAULT_MMAP_SIZE) && !defined(SQLITE_DEFAULT_MMAP_SIZE_xc) |
| 13906 | "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE), |
| 13907 | #endif |
| 13908 | #if SQLITE_DISABLE_DIRSYNC |
| 13909 | "DISABLE_DIRSYNC", |
| 13910 | #endif |
| 13911 | #if SQLITE_DISABLE_LFS |
| 13912 | "DISABLE_LFS", |
| 13913 | #endif |
| 13914 | #if SQLITE_ENABLE_API_ARMOR |
| 13915 | "ENABLE_API_ARMOR", |
| 13916 | #endif |
| 13917 | #if SQLITE_ENABLE_ATOMIC_WRITE |
| 13918 | "ENABLE_ATOMIC_WRITE", |
| 13919 | #endif |
| 13920 | #if SQLITE_ENABLE_CEROD |
| 13921 | "ENABLE_CEROD", |
| 13922 | #endif |
| 13923 | #if SQLITE_ENABLE_COLUMN_METADATA |
| 13924 | "ENABLE_COLUMN_METADATA", |
| 13925 | #endif |
| 13926 | #if SQLITE_ENABLE_EXPENSIVE_ASSERT |
| 13927 | "ENABLE_EXPENSIVE_ASSERT", |
| 13928 | #endif |
| 13929 | #if SQLITE_ENABLE_FTS1 |
| 13930 | "ENABLE_FTS1", |
| 13931 | #endif |
| 13932 | #if SQLITE_ENABLE_FTS2 |
| 13933 | "ENABLE_FTS2", |
| 13934 | #endif |
| 13935 | #if SQLITE_ENABLE_FTS3 |
| 13936 | "ENABLE_FTS3", |
| 13937 | #endif |
| 13938 | #if SQLITE_ENABLE_FTS3_PARENTHESIS |
| 13939 | "ENABLE_FTS3_PARENTHESIS", |
| 13940 | #endif |
| 13941 | #if SQLITE_ENABLE_FTS4 |
| 13942 | "ENABLE_FTS4", |
| 13943 | #endif |
| 13944 | #if SQLITE_ENABLE_ICU |
| 13945 | "ENABLE_ICU", |
| 13946 | #endif |
| 13947 | #if SQLITE_ENABLE_IOTRACE |
| 13948 | "ENABLE_IOTRACE", |
| 13949 | #endif |
| 13950 | #if SQLITE_ENABLE_LOAD_EXTENSION |
| 13951 | "ENABLE_LOAD_EXTENSION", |
| 13952 | #endif |
| 13953 | #if SQLITE_ENABLE_LOCKING_STYLE |
| 13954 | "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE), |
| 13955 | #endif |
| 13956 | #if SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 13957 | "ENABLE_MEMORY_MANAGEMENT", |
| 13958 | #endif |
| 13959 | #if SQLITE_ENABLE_MEMSYS3 |
| 13960 | "ENABLE_MEMSYS3", |
| 13961 | #endif |
| 13962 | #if SQLITE_ENABLE_MEMSYS5 |
| 13963 | "ENABLE_MEMSYS5", |
| 13964 | #endif |
| 13965 | #if SQLITE_ENABLE_OVERSIZE_CELL_CHECK |
| 13966 | "ENABLE_OVERSIZE_CELL_CHECK", |
| 13967 | #endif |
| 13968 | #if SQLITE_ENABLE_RTREE |
| 13969 | "ENABLE_RTREE", |
| 13970 | #endif |
| 13971 | #if defined(SQLITE_ENABLE_STAT4) |
| 13972 | "ENABLE_STAT4", |
| 13973 | #elif defined(SQLITE_ENABLE_STAT3) |
| 13974 | "ENABLE_STAT3", |
| 13975 | #endif |
| 13976 | #if SQLITE_ENABLE_UNLOCK_NOTIFY |
| 13977 | "ENABLE_UNLOCK_NOTIFY", |
| 13978 | #endif |
| 13979 | #if SQLITE_ENABLE_UPDATE_DELETE_LIMIT |
| 13980 | "ENABLE_UPDATE_DELETE_LIMIT", |
| 13981 | #endif |
| 13982 | #if SQLITE_HAS_CODEC |
| 13983 | "HAS_CODEC", |
| 13984 | #endif |
| 13985 | #if HAVE_ISNAN || SQLITE_HAVE_ISNAN |
| 13986 | "HAVE_ISNAN", |
| 13987 | #endif |
| 13988 | #if SQLITE_HOMEGROWN_RECURSIVE_MUTEX |
| 13989 | "HOMEGROWN_RECURSIVE_MUTEX", |
| 13990 | #endif |
| 13991 | #if SQLITE_IGNORE_AFP_LOCK_ERRORS |
| 13992 | "IGNORE_AFP_LOCK_ERRORS", |
| 13993 | #endif |
| 13994 | #if SQLITE_IGNORE_FLOCK_LOCK_ERRORS |
| 13995 | "IGNORE_FLOCK_LOCK_ERRORS", |
| 13996 | #endif |
| 13997 | #ifdef SQLITE_INT64_TYPE |
| 13998 | "INT64_TYPE", |
| 13999 | #endif |
| 14000 | #if SQLITE_LOCK_TRACE |
| 14001 | "LOCK_TRACE", |
| 14002 | #endif |
| 14003 | #if defined(SQLITE_MAX_MMAP_SIZE) && !defined(SQLITE_MAX_MMAP_SIZE_xc) |
| 14004 | "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE), |
| 14005 | #endif |
| 14006 | #ifdef SQLITE_MAX_SCHEMA_RETRY |
| 14007 | "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY), |
| 14008 | #endif |
| 14009 | #if SQLITE_MEMDEBUG |
| 14010 | "MEMDEBUG", |
| 14011 | #endif |
| 14012 | #if SQLITE_MIXED_ENDIAN_64BIT_FLOAT |
| 14013 | "MIXED_ENDIAN_64BIT_FLOAT", |
| 14014 | #endif |
| 14015 | #if SQLITE_NO_SYNC |
| 14016 | "NO_SYNC", |
| 14017 | #endif |
| 14018 | #if SQLITE_OMIT_ALTERTABLE |
| 14019 | "OMIT_ALTERTABLE", |
| 14020 | #endif |
| 14021 | #if SQLITE_OMIT_ANALYZE |
| 14022 | "OMIT_ANALYZE", |
| 14023 | #endif |
| 14024 | #if SQLITE_OMIT_ATTACH |
| 14025 | "OMIT_ATTACH", |
| 14026 | #endif |
| 14027 | #if SQLITE_OMIT_AUTHORIZATION |
| 14028 | "OMIT_AUTHORIZATION", |
| 14029 | #endif |
| 14030 | #if SQLITE_OMIT_AUTOINCREMENT |
| 14031 | "OMIT_AUTOINCREMENT", |
| 14032 | #endif |
| 14033 | #if SQLITE_OMIT_AUTOINIT |
| 14034 | "OMIT_AUTOINIT", |
| 14035 | #endif |
| 14036 | #if SQLITE_OMIT_AUTOMATIC_INDEX |
| 14037 | "OMIT_AUTOMATIC_INDEX", |
| 14038 | #endif |
| 14039 | #if SQLITE_OMIT_AUTORESET |
| 14040 | "OMIT_AUTORESET", |
| 14041 | #endif |
| 14042 | #if SQLITE_OMIT_AUTOVACUUM |
| 14043 | "OMIT_AUTOVACUUM", |
| 14044 | #endif |
| 14045 | #if SQLITE_OMIT_BETWEEN_OPTIMIZATION |
| 14046 | "OMIT_BETWEEN_OPTIMIZATION", |
| 14047 | #endif |
| 14048 | #if SQLITE_OMIT_BLOB_LITERAL |
| 14049 | "OMIT_BLOB_LITERAL", |
| 14050 | #endif |
| 14051 | #if SQLITE_OMIT_BTREECOUNT |
| 14052 | "OMIT_BTREECOUNT", |
| 14053 | #endif |
| 14054 | #if SQLITE_OMIT_BUILTIN_TEST |
| 14055 | "OMIT_BUILTIN_TEST", |
| 14056 | #endif |
| 14057 | #if SQLITE_OMIT_CAST |
| 14058 | "OMIT_CAST", |
| 14059 | #endif |
| 14060 | #if SQLITE_OMIT_CHECK |
| 14061 | "OMIT_CHECK", |
| 14062 | #endif |
| 14063 | #if SQLITE_OMIT_COMPLETE |
| 14064 | "OMIT_COMPLETE", |
| 14065 | #endif |
| 14066 | #if SQLITE_OMIT_COMPOUND_SELECT |
| 14067 | "OMIT_COMPOUND_SELECT", |
| 14068 | #endif |
| 14069 | #if SQLITE_OMIT_CTE |
| 14070 | "OMIT_CTE", |
| 14071 | #endif |
| 14072 | #if SQLITE_OMIT_DATETIME_FUNCS |
| 14073 | "OMIT_DATETIME_FUNCS", |
| 14074 | #endif |
| 14075 | #if SQLITE_OMIT_DECLTYPE |
| 14076 | "OMIT_DECLTYPE", |
| 14077 | #endif |
| 14078 | #if SQLITE_OMIT_DEPRECATED |
| 14079 | "OMIT_DEPRECATED", |
| 14080 | #endif |
| 14081 | #if SQLITE_OMIT_DISKIO |
| 14082 | "OMIT_DISKIO", |
| 14083 | #endif |
| 14084 | #if SQLITE_OMIT_EXPLAIN |
| 14085 | "OMIT_EXPLAIN", |
| 14086 | #endif |
| 14087 | #if SQLITE_OMIT_FLAG_PRAGMAS |
| 14088 | "OMIT_FLAG_PRAGMAS", |
| 14089 | #endif |
| 14090 | #if SQLITE_OMIT_FLOATING_POINT |
| 14091 | "OMIT_FLOATING_POINT", |
| 14092 | #endif |
| 14093 | #if SQLITE_OMIT_FOREIGN_KEY |
| 14094 | "OMIT_FOREIGN_KEY", |
| 14095 | #endif |
| 14096 | #if SQLITE_OMIT_GET_TABLE |
| 14097 | "OMIT_GET_TABLE", |
| 14098 | #endif |
| 14099 | #if SQLITE_OMIT_INCRBLOB |
| 14100 | "OMIT_INCRBLOB", |
| 14101 | #endif |
| 14102 | #if SQLITE_OMIT_INTEGRITY_CHECK |
| 14103 | "OMIT_INTEGRITY_CHECK", |
| 14104 | #endif |
| 14105 | #if SQLITE_OMIT_LIKE_OPTIMIZATION |
| 14106 | "OMIT_LIKE_OPTIMIZATION", |
| 14107 | #endif |
| 14108 | #if SQLITE_OMIT_LOAD_EXTENSION |
| 14109 | "OMIT_LOAD_EXTENSION", |
| 14110 | #endif |
| 14111 | #if SQLITE_OMIT_LOCALTIME |
| 14112 | "OMIT_LOCALTIME", |
| 14113 | #endif |
| 14114 | #if SQLITE_OMIT_LOOKASIDE |
| 14115 | "OMIT_LOOKASIDE", |
| 14116 | #endif |
| 14117 | #if SQLITE_OMIT_MEMORYDB |
| 14118 | "OMIT_MEMORYDB", |
| 14119 | #endif |
| 14120 | #if SQLITE_OMIT_OR_OPTIMIZATION |
| 14121 | "OMIT_OR_OPTIMIZATION", |
| 14122 | #endif |
| 14123 | #if SQLITE_OMIT_PAGER_PRAGMAS |
| 14124 | "OMIT_PAGER_PRAGMAS", |
| 14125 | #endif |
| 14126 | #if SQLITE_OMIT_PRAGMA |
| 14127 | "OMIT_PRAGMA", |
| 14128 | #endif |
| 14129 | #if SQLITE_OMIT_PROGRESS_CALLBACK |
| 14130 | "OMIT_PROGRESS_CALLBACK", |
| 14131 | #endif |
| 14132 | #if SQLITE_OMIT_QUICKBALANCE |
| 14133 | "OMIT_QUICKBALANCE", |
| 14134 | #endif |
| 14135 | #if SQLITE_OMIT_REINDEX |
| 14136 | "OMIT_REINDEX", |
| 14137 | #endif |
| 14138 | #if SQLITE_OMIT_SCHEMA_PRAGMAS |
| 14139 | "OMIT_SCHEMA_PRAGMAS", |
| 14140 | #endif |
| 14141 | #if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS |
| 14142 | "OMIT_SCHEMA_VERSION_PRAGMAS", |
| 14143 | #endif |
| 14144 | #if SQLITE_OMIT_SHARED_CACHE |
| 14145 | "OMIT_SHARED_CACHE", |
| 14146 | #endif |
| 14147 | #if SQLITE_OMIT_SUBQUERY |
| 14148 | "OMIT_SUBQUERY", |
| 14149 | #endif |
| 14150 | #if SQLITE_OMIT_TCL_VARIABLE |
| 14151 | "OMIT_TCL_VARIABLE", |
| 14152 | #endif |
| 14153 | #if SQLITE_OMIT_TEMPDB |
| 14154 | "OMIT_TEMPDB", |
| 14155 | #endif |
| 14156 | #if SQLITE_OMIT_TRACE |
| 14157 | "OMIT_TRACE", |
| 14158 | #endif |
| 14159 | #if SQLITE_OMIT_TRIGGER |
| 14160 | "OMIT_TRIGGER", |
| 14161 | #endif |
| 14162 | #if SQLITE_OMIT_TRUNCATE_OPTIMIZATION |
| 14163 | "OMIT_TRUNCATE_OPTIMIZATION", |
| 14164 | #endif |
| 14165 | #if SQLITE_OMIT_UTF16 |
| 14166 | "OMIT_UTF16", |
| 14167 | #endif |
| 14168 | #if SQLITE_OMIT_VACUUM |
| 14169 | "OMIT_VACUUM", |
| 14170 | #endif |
| 14171 | #if SQLITE_OMIT_VIEW |
| 14172 | "OMIT_VIEW", |
| 14173 | #endif |
| 14174 | #if SQLITE_OMIT_VIRTUALTABLE |
| 14175 | "OMIT_VIRTUALTABLE", |
| 14176 | #endif |
| 14177 | #if SQLITE_OMIT_WAL |
| 14178 | "OMIT_WAL", |
| 14179 | #endif |
| 14180 | #if SQLITE_OMIT_WSD |
| 14181 | "OMIT_WSD", |
| 14182 | #endif |
| 14183 | #if SQLITE_OMIT_XFER_OPT |
| 14184 | "OMIT_XFER_OPT", |
| 14185 | #endif |
| 14186 | #if SQLITE_PERFORMANCE_TRACE |
| 14187 | "PERFORMANCE_TRACE", |
| 14188 | #endif |
| 14189 | #if SQLITE_PROXY_DEBUG |
| 14190 | "PROXY_DEBUG", |
| 14191 | #endif |
| 14192 | #if SQLITE_RTREE_INT_ONLY |
| 14193 | "RTREE_INT_ONLY", |
| 14194 | #endif |
| 14195 | #if SQLITE_SECURE_DELETE |
| 14196 | "SECURE_DELETE", |
| 14197 | #endif |
| 14198 | #if SQLITE_SMALL_STACK |
| 14199 | "SMALL_STACK", |
| 14200 | #endif |
| 14201 | #if SQLITE_SOUNDEX |
| 14202 | "SOUNDEX", |
| 14203 | #endif |
| 14204 | #if SQLITE_SYSTEM_MALLOC |
| 14205 | "SYSTEM_MALLOC", |
| 14206 | #endif |
| 14207 | #if SQLITE_TCL |
| 14208 | "TCL", |
| 14209 | #endif |
| 14210 | #if defined(SQLITE_TEMP_STORE) && !defined(SQLITE_TEMP_STORE_xc) |
| 14211 | "TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE), |
| 14212 | #endif |
| 14213 | #if SQLITE_TEST |
| 14214 | "TEST", |
| 14215 | #endif |
| 14216 | #if defined(SQLITE_THREADSAFE) |
| 14217 | "THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE), |
| 14218 | #endif |
| 14219 | #if SQLITE_USE_ALLOCA |
| 14220 | "USE_ALLOCA", |
| 14221 | #endif |
| 14222 | #if SQLITE_USER_AUTHENTICATION |
| 14223 | "USER_AUTHENTICATION", |
| 14224 | #endif |
| 14225 | #if SQLITE_WIN32_MALLOC |
| 14226 | "WIN32_MALLOC", |
| 14227 | #endif |
| 14228 | #if SQLITE_ZERO_MALLOC |
| 14229 | "ZERO_MALLOC" |
| 14230 | #endif |
| 14231 | }; |
| 14232 | |
| 14233 | /* |
| @@ -14156,11 +14238,11 @@ | |
| 14238 | ** is not required for a match. |
| 14239 | */ |
| 14240 | SQLITE_API int sqlite3_compileoption_used(const char *zOptName){ |
| 14241 | int i, n; |
| 14242 | |
| 14243 | #if SQLITE_ENABLE_API_ARMOR |
| 14244 | if( zOptName==0 ){ |
| 14245 | (void)SQLITE_MISUSE_BKPT; |
| 14246 | return 0; |
| 14247 | } |
| 14248 | #endif |
| @@ -15384,12 +15466,13 @@ | |
| 15466 | ** |
| 15467 | ** If the user has not indicated to use localtime_r() or localtime_s() |
| 15468 | ** already, check for an MSVC build environment that provides |
| 15469 | ** localtime_s(). |
| 15470 | */ |
| 15471 | #if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S \ |
| 15472 | && defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) |
| 15473 | #undef HAVE_LOCALTIME_S |
| 15474 | #define HAVE_LOCALTIME_S 1 |
| 15475 | #endif |
| 15476 | |
| 15477 | #ifndef SQLITE_OMIT_LOCALTIME |
| 15478 | /* |
| @@ -15405,12 +15488,11 @@ | |
| 15488 | ** library function localtime_r() is used to assist in the calculation of |
| 15489 | ** local time. |
| 15490 | */ |
| 15491 | static int osLocaltime(time_t *t, struct tm *pTm){ |
| 15492 | int rc; |
| 15493 | #if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S |
| 15494 | struct tm *pX; |
| 15495 | #if SQLITE_THREADSAFE>0 |
| 15496 | sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); |
| 15497 | #endif |
| 15498 | sqlite3_mutex_enter(mutex); |
| @@ -15423,11 +15505,11 @@ | |
| 15505 | rc = pX==0; |
| 15506 | #else |
| 15507 | #ifndef SQLITE_OMIT_BUILTIN_TEST |
| 15508 | if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; |
| 15509 | #endif |
| 15510 | #if HAVE_LOCALTIME_R |
| 15511 | rc = localtime_r(t, pTm)==0; |
| 15512 | #else |
| 15513 | rc = localtime_s(pTm, t); |
| 15514 | #endif /* HAVE_LOCALTIME_R */ |
| 15515 | #endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */ |
| @@ -15867,12 +15949,14 @@ | |
| 15949 | DateTime x; |
| 15950 | u64 n; |
| 15951 | size_t i,j; |
| 15952 | char *z; |
| 15953 | sqlite3 *db; |
| 15954 | const char *zFmt; |
| 15955 | char zBuf[100]; |
| 15956 | if( argc==0 ) return; |
| 15957 | zFmt = (const char*)sqlite3_value_text(argv[0]); |
| 15958 | if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return; |
| 15959 | db = sqlite3_context_db_handle(context); |
| 15960 | for(i=0, n=1; zFmt[i]; i++, n++){ |
| 15961 | if( zFmt[i]=='%' ){ |
| 15962 | switch( zFmt[i+1] ){ |
| @@ -16062,11 +16146,11 @@ | |
| 16146 | UNUSED_PARAMETER(argv); |
| 16147 | |
| 16148 | iT = sqlite3StmtCurrentTime(context); |
| 16149 | if( iT<=0 ) return; |
| 16150 | t = iT/1000 - 10000*(sqlite3_int64)21086676; |
| 16151 | #if HAVE_GMTIME_R |
| 16152 | pTm = gmtime_r(&t, &sNow); |
| 16153 | #else |
| 16154 | sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); |
| 16155 | pTm = gmtime(&t); |
| 16156 | if( pTm ) memcpy(&sNow, pTm, sizeof(sNow)); |
| @@ -16736,13 +16820,13 @@ | |
| 16820 | |
| 16821 | /* |
| 16822 | ** The malloc.h header file is needed for malloc_usable_size() function |
| 16823 | ** on some systems (e.g. Linux). |
| 16824 | */ |
| 16825 | #if HAVE_MALLOC_H && HAVE_MALLOC_USABLE_SIZE |
| 16826 | # define SQLITE_USE_MALLOC_H 1 |
| 16827 | # define SQLITE_USE_MALLOC_USABLE_SIZE 1 |
| 16828 | /* |
| 16829 | ** The MSVCRT has malloc_usable_size(), but it is called _msize(). The |
| 16830 | ** use of _msize() is automatic, but can be disabled by compiling with |
| 16831 | ** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires |
| 16832 | ** the malloc.h header file. |
| @@ -19976,10 +20060,16 @@ | |
| 20060 | #endif |
| 20061 | } |
| 20062 | break; |
| 20063 | } |
| 20064 | default: { |
| 20065 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 20066 | if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){ |
| 20067 | (void)SQLITE_MISUSE_BKPT; |
| 20068 | return 0; |
| 20069 | } |
| 20070 | #endif |
| 20071 | assert( iType-2 >= 0 ); |
| 20072 | assert( iType-2 < ArraySize(winMutex_staticMutexes) ); |
| 20073 | assert( winMutex_isInit==1 ); |
| 20074 | p = &winMutex_staticMutexes[iType-2]; |
| 20075 | #ifdef SQLITE_DEBUG |
| @@ -20971,21 +21061,10 @@ | |
| 21061 | ** This file contains code for a set of "printf"-like routines. These |
| 21062 | ** routines format strings much like the printf() from the standard C |
| 21063 | ** library, though the implementation here has enhancements to support |
| 21064 | ** SQLlite. |
| 21065 | */ |
| 21066 | |
| 21067 | /* |
| 21068 | ** Conversion types fall into various categories as defined by the |
| 21069 | ** following enumeration. |
| 21070 | */ |
| @@ -22280,10 +22359,12 @@ | |
| 22359 | ** single threaded systems. Nothing in SQLite requires multiple threads. |
| 22360 | ** This interface exists so that applications that want to take advantage |
| 22361 | ** of multiple cores can do so, while also allowing applications to stay |
| 22362 | ** single-threaded if desired. |
| 22363 | */ |
| 22364 | #if SQLITE_OS_WIN |
| 22365 | #endif |
| 22366 | |
| 22367 | #if SQLITE_MAX_WORKER_THREADS>0 |
| 22368 | |
| 22369 | /********************************* Unix Pthreads ****************************/ |
| 22370 | #if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 |
| @@ -23066,11 +23147,11 @@ | |
| 23147 | ** This file contains functions for allocating memory, comparing |
| 23148 | ** strings, and stuff like that. |
| 23149 | ** |
| 23150 | */ |
| 23151 | /* #include <stdarg.h> */ |
| 23152 | #if HAVE_ISNAN || SQLITE_HAVE_ISNAN |
| 23153 | # include <math.h> |
| 23154 | #endif |
| 23155 | |
| 23156 | /* |
| 23157 | ** Routine needed to support the testcase() macro. |
| @@ -23107,11 +23188,11 @@ | |
| 23188 | ** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. |
| 23189 | ** Otherwise, we have our own implementation that works on most systems. |
| 23190 | */ |
| 23191 | SQLITE_PRIVATE int sqlite3IsNaN(double x){ |
| 23192 | int rc; /* The value return */ |
| 23193 | #if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN |
| 23194 | /* |
| 23195 | ** Systems that support the isnan() library function should probably |
| 23196 | ** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have |
| 23197 | ** found that many systems do not have a working isnan() function so |
| 23198 | ** this implementation is provided as an alternative. |
| @@ -23137,13 +23218,13 @@ | |
| 23218 | # error SQLite will not work correctly with the -ffast-math option of GCC. |
| 23219 | #endif |
| 23220 | volatile double y = x; |
| 23221 | volatile double z = y; |
| 23222 | rc = (y!=z); |
| 23223 | #else /* if HAVE_ISNAN */ |
| 23224 | rc = isnan(x); |
| 23225 | #endif /* HAVE_ISNAN */ |
| 23226 | testcase( rc ); |
| 23227 | return rc; |
| 23228 | } |
| 23229 | #endif /* SQLITE_OMIT_FLOATING_POINT */ |
| 23230 | |
| @@ -28460,13 +28541,13 @@ | |
| 28541 | |
| 28542 | /* |
| 28543 | ** We do not trust systems to provide a working fdatasync(). Some do. |
| 28544 | ** Others do no. To be safe, we will stick with the (slightly slower) |
| 28545 | ** fsync(). If you know that your system does support fdatasync() correctly, |
| 28546 | ** then simply compile with -Dfdatasync=fdatasync or -DHAVE_FDATASYNC |
| 28547 | */ |
| 28548 | #if !defined(fdatasync) && !HAVE_FDATASYNC |
| 28549 | # define fdatasync fsync |
| 28550 | #endif |
| 28551 | |
| 28552 | /* |
| 28553 | ** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not |
| @@ -28783,28 +28864,32 @@ | |
| 28864 | do{ |
| 28865 | err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); |
| 28866 | }while( err==EINTR ); |
| 28867 | if( err ) return SQLITE_IOERR_WRITE; |
| 28868 | #else |
| 28869 | /* If the OS does not have posix_fallocate(), fake it. Write a |
| 28870 | ** single byte to the last byte in each block that falls entirely |
| 28871 | ** within the extended region. Then, if required, a single byte |
| 28872 | ** at offset (nSize-1), to set the size of the file correctly. |
| 28873 | ** This is a similar technique to that used by glibc on systems |
| 28874 | ** that do not have a real fallocate() call. |
| 28875 | */ |
| 28876 | int nBlk = buf.st_blksize; /* File-system block size */ |
| 28877 | int nWrite = 0; /* Number of bytes written by seekAndWrite */ |
| 28878 | i64 iWrite; /* Next offset to write to */ |
| 28879 | |
| 28880 | iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; |
| 28881 | assert( iWrite>=buf.st_size ); |
| 28882 | assert( (iWrite/nBlk)==((buf.st_size+nBlk-1)/nBlk) ); |
| 28883 | assert( ((iWrite+1)%nBlk)==0 ); |
| 28884 | for(/*no-op*/; iWrite<nSize; iWrite+=nBlk ){ |
| 28885 | nWrite = seekAndWrite(pFile, iWrite, "", 1); |
| 28886 | if( nWrite!=1 ) return SQLITE_IOERR_WRITE; |
| 28887 | } |
| 28888 | if( nWrite==0 || (nSize%nBlk) ){ |
| 28889 | nWrite = seekAndWrite(pFile, nSize-1, "", 1); |
| 28890 | if( nWrite!=1 ) return SQLITE_IOERR_WRITE; |
| 28891 | } |
| 28892 | #endif |
| 28893 | } |
| 28894 | } |
| 28895 | |
| @@ -34018,12 +34103,12 @@ | |
| 34103 | */ |
| 34104 | SQLITE_API int sqlite3_win32_reset_heap(){ |
| 34105 | int rc; |
| 34106 | MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ |
| 34107 | MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */ |
| 34108 | MUTEX_LOGIC( pMaster = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); ) |
| 34109 | MUTEX_LOGIC( pMem = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM); ) |
| 34110 | sqlite3_mutex_enter(pMaster); |
| 34111 | sqlite3_mutex_enter(pMem); |
| 34112 | winMemAssertMagic(); |
| 34113 | if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){ |
| 34114 | /* |
| @@ -35294,11 +35379,11 @@ | |
| 35379 | sqlite3_file *id, /* File to read from */ |
| 35380 | void *pBuf, /* Write content into this buffer */ |
| 35381 | int amt, /* Number of bytes to read */ |
| 35382 | sqlite3_int64 offset /* Begin reading at this offset */ |
| 35383 | ){ |
| 35384 | #if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) |
| 35385 | OVERLAPPED overlapped; /* The offset for ReadFile. */ |
| 35386 | #endif |
| 35387 | winFile *pFile = (winFile*)id; /* file handle */ |
| 35388 | DWORD nRead; /* Number of bytes actually read from file */ |
| 35389 | int nRetry = 0; /* Number of retrys */ |
| @@ -35326,11 +35411,11 @@ | |
| 35411 | offset += nCopy; |
| 35412 | } |
| 35413 | } |
| 35414 | #endif |
| 35415 | |
| 35416 | #if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) |
| 35417 | if( winSeekFile(pFile, offset) ){ |
| 35418 | OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h)); |
| 35419 | return SQLITE_FULL; |
| 35420 | } |
| 35421 | while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ |
| @@ -35398,32 +35483,32 @@ | |
| 35483 | offset += nCopy; |
| 35484 | } |
| 35485 | } |
| 35486 | #endif |
| 35487 | |
| 35488 | #if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) |
| 35489 | rc = winSeekFile(pFile, offset); |
| 35490 | if( rc==0 ){ |
| 35491 | #else |
| 35492 | { |
| 35493 | #endif |
| 35494 | #if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) |
| 35495 | OVERLAPPED overlapped; /* The offset for WriteFile. */ |
| 35496 | #endif |
| 35497 | u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ |
| 35498 | int nRem = amt; /* Number of bytes yet to be written */ |
| 35499 | DWORD nWrite; /* Bytes written by each WriteFile() call */ |
| 35500 | DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ |
| 35501 | |
| 35502 | #if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) |
| 35503 | memset(&overlapped, 0, sizeof(OVERLAPPED)); |
| 35504 | overlapped.Offset = (LONG)(offset & 0xffffffff); |
| 35505 | overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); |
| 35506 | #endif |
| 35507 | |
| 35508 | while( nRem>0 ){ |
| 35509 | #if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) |
| 35510 | if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ |
| 35511 | #else |
| 35512 | if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ |
| 35513 | #endif |
| 35514 | if( winRetryIoerr(&nRetry, &lastErrno) ) continue; |
| @@ -35432,11 +35517,11 @@ | |
| 35517 | assert( nWrite==0 || nWrite<=(DWORD)nRem ); |
| 35518 | if( nWrite==0 || nWrite>(DWORD)nRem ){ |
| 35519 | lastErrno = osGetLastError(); |
| 35520 | break; |
| 35521 | } |
| 35522 | #if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) |
| 35523 | offset += nWrite; |
| 35524 | overlapped.Offset = (LONG)(offset & 0xffffffff); |
| 35525 | overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); |
| 35526 | #endif |
| 35527 | aRem += nWrite; |
| @@ -38813,22 +38898,10 @@ | |
| 38898 | void *pStress; /* Argument to xStress */ |
| 38899 | sqlite3_pcache *pCache; /* Pluggable cache module */ |
| 38900 | PgHdr *pPage1; /* Reference to page 1 */ |
| 38901 | }; |
| 38902 | |
| 38903 | /********************************** Linked List Management ********************/ |
| 38904 | |
| 38905 | /* Allowed values for second argument to pcacheManageDirtyList() */ |
| 38906 | #define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */ |
| 38907 | #define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */ |
| @@ -38978,11 +39051,12 @@ | |
| 39051 | SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ |
| 39052 | assert( pCache->nRef==0 && pCache->pDirty==0 ); |
| 39053 | if( pCache->szPage ){ |
| 39054 | sqlite3_pcache *pNew; |
| 39055 | pNew = sqlite3GlobalConfig.pcache2.xCreate( |
| 39056 | szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)), |
| 39057 | pCache->bPurgeable |
| 39058 | ); |
| 39059 | if( pNew==0 ) return SQLITE_NOMEM; |
| 39060 | sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache)); |
| 39061 | if( pCache->pCache ){ |
| 39062 | sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); |
| @@ -39437,11 +39511,11 @@ | |
| 39511 | |
| 39512 | /* |
| 39513 | ** Return the size of the header added by this middleware layer |
| 39514 | ** in the page-cache hierarchy. |
| 39515 | */ |
| 39516 | SQLITE_PRIVATE int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); } |
| 39517 | |
| 39518 | |
| 39519 | #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) |
| 39520 | /* |
| 39521 | ** For all dirty pages currently in the cache, invoke the specified |
| @@ -39753,11 +39827,11 @@ | |
| 39827 | pcache1Free(pPg); |
| 39828 | sqlite3_free(p); |
| 39829 | pPg = 0; |
| 39830 | } |
| 39831 | #else |
| 39832 | pPg = pcache1Alloc(ROUND8(sizeof(PgHdr1)) + pCache->szPage + pCache->szExtra); |
| 39833 | p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; |
| 39834 | #endif |
| 39835 | pcache1EnterMutex(pCache->pGroup); |
| 39836 | |
| 39837 | if( pPg ){ |
| @@ -40441,11 +40515,11 @@ | |
| 40515 | } |
| 40516 | |
| 40517 | /* |
| 40518 | ** Return the size of the header on each page of this PCACHE implementation. |
| 40519 | */ |
| 40520 | SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); } |
| 40521 | |
| 40522 | #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
| 40523 | /* |
| 40524 | ** This function is called to free superfluous dynamically allocated memory |
| 40525 | ** held by the pager system. Memory in use by any SQLite pager allocated |
| @@ -41799,10 +41873,12 @@ | |
| 41873 | u8 eLock; /* Current lock held on database file */ |
| 41874 | u8 changeCountDone; /* Set after incrementing the change-counter */ |
| 41875 | u8 setMaster; /* True if a m-j name has been written to jrnl */ |
| 41876 | u8 doNotSpill; /* Do not spill the cache when non-zero */ |
| 41877 | u8 subjInMemory; /* True to use in-memory sub-journals */ |
| 41878 | u8 bUseFetch; /* True to use xFetch() */ |
| 41879 | u8 hasBeenUsed; /* True if any content previously read from this pager*/ |
| 41880 | Pgno dbSize; /* Number of pages in the database */ |
| 41881 | Pgno dbOrigSize; /* dbSize before the current transaction */ |
| 41882 | Pgno dbFileSize; /* Number of pages in the database file */ |
| 41883 | Pgno dbHintSize; /* Value passed to FCNTL_SIZE_HINT call */ |
| 41884 | int errCode; /* One of several kinds of errors */ |
| @@ -41816,13 +41892,13 @@ | |
| 41892 | i64 journalOff; /* Current write offset in the journal file */ |
| 41893 | i64 journalHdr; /* Byte offset to previous journal header */ |
| 41894 | sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */ |
| 41895 | PagerSavepoint *aSavepoint; /* Array of active savepoints */ |
| 41896 | int nSavepoint; /* Number of elements in aSavepoint[] */ |
| 41897 | u32 iDataVersion; /* Changes whenever database content changes */ |
| 41898 | char dbFileVers[16]; /* Changes whenever database file changes */ |
| 41899 | |
| 41900 | int nMmapOut; /* Number of mmap pages currently outstanding */ |
| 41901 | sqlite3_int64 szMmap; /* Desired maximum mmap size */ |
| 41902 | PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */ |
| 41903 | /* |
| 41904 | ** End of the routinely-changing class members |
| @@ -42834,13 +42910,22 @@ | |
| 42910 | |
| 42911 | /* |
| 42912 | ** Discard the entire contents of the in-memory page-cache. |
| 42913 | */ |
| 42914 | static void pager_reset(Pager *pPager){ |
| 42915 | pPager->iDataVersion++; |
| 42916 | sqlite3BackupRestart(pPager->pBackup); |
| 42917 | sqlite3PcacheClear(pPager->pPCache); |
| 42918 | } |
| 42919 | |
| 42920 | /* |
| 42921 | ** Return the pPager->iDataVersion value |
| 42922 | */ |
| 42923 | SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager *pPager){ |
| 42924 | assert( pPager->eState>PAGER_OPEN ); |
| 42925 | return pPager->iDataVersion; |
| 42926 | } |
| 42927 | |
| 42928 | /* |
| 42929 | ** Free all structures in the Pager.aSavepoint[] array and set both |
| 42930 | ** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal |
| 42931 | ** if it is open and the pager is not in exclusive mode. |
| @@ -45040,11 +45125,11 @@ | |
| 45125 | Pgno pgno, /* Page number */ |
| 45126 | void *pData, /* xFetch()'d data for this page */ |
| 45127 | PgHdr **ppPage /* OUT: Acquired page object */ |
| 45128 | ){ |
| 45129 | PgHdr *p; /* Memory mapped page to return */ |
| 45130 | |
| 45131 | if( pPager->pMmapFreelist ){ |
| 45132 | *ppPage = p = pPager->pMmapFreelist; |
| 45133 | pPager->pMmapFreelist = p->pDirty; |
| 45134 | p->pDirty = 0; |
| 45135 | memset(p->pExtra, 0, pPager->nExtra); |
| @@ -46271,20 +46356,16 @@ | |
| 46356 | assert( (pPager->eLock==SHARED_LOCK) |
| 46357 | || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK) |
| 46358 | ); |
| 46359 | } |
| 46360 | |
| 46361 | if( !pPager->tempFile && pPager->hasBeenUsed ){ |
| 46362 | /* The shared-lock has just been acquired then check to |
| 46363 | ** see if the database has been modified. If the database has changed, |
| 46364 | ** flush the cache. The pPager->hasBeenUsed flag prevents this from |
| 46365 | ** occurring on the very first access to a file, in order to save a |
| 46366 | ** single unnecessary sqlite3OsRead() call at the start-up. |
| 46367 | ** |
| 46368 | ** Database changes is detected by looking at 15 bytes beginning |
| 46369 | ** at offset 24 into the file. The first 4 of these 16 bytes are |
| 46370 | ** a 32-bit counter that is incremented with each change. The |
| 46371 | ** other bytes change randomly with each file change when |
| @@ -46445,10 +46526,11 @@ | |
| 46526 | assert( noContent==0 || bMmapOk==0 ); |
| 46527 | |
| 46528 | if( pgno==0 ){ |
| 46529 | return SQLITE_CORRUPT_BKPT; |
| 46530 | } |
| 46531 | pPager->hasBeenUsed = 1; |
| 46532 | |
| 46533 | /* If the pager is in the error state, return an error immediately. |
| 46534 | ** Otherwise, request the page from the PCache layer. */ |
| 46535 | if( pPager->errCode!=SQLITE_OK ){ |
| 46536 | rc = pPager->errCode; |
| @@ -46594,10 +46676,11 @@ | |
| 46676 | sqlite3_pcache_page *pPage; |
| 46677 | assert( pPager!=0 ); |
| 46678 | assert( pgno!=0 ); |
| 46679 | assert( pPager->pPCache!=0 ); |
| 46680 | pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0); |
| 46681 | assert( pPage==0 || pPager->hasBeenUsed ); |
| 46682 | return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage); |
| 46683 | } |
| 46684 | |
| 46685 | /* |
| 46686 | ** Release a page reference. |
| @@ -47460,10 +47543,11 @@ | |
| 47543 | pPager->eState = PAGER_READER; |
| 47544 | return SQLITE_OK; |
| 47545 | } |
| 47546 | |
| 47547 | PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); |
| 47548 | pPager->iDataVersion++; |
| 47549 | rc = pager_end_transaction(pPager, pPager->setMaster, 1); |
| 47550 | return pager_error(pPager, rc); |
| 47551 | } |
| 47552 | |
| 47553 | /* |
| @@ -50111,11 +50195,11 @@ | |
| 50195 | int (*xBusy)(void*), /* Function to call when busy */ |
| 50196 | void *pBusyArg, /* Context argument for xBusyHandler */ |
| 50197 | int sync_flags, /* Flags for OsSync() (or 0) */ |
| 50198 | u8 *zBuf /* Temporary buffer to use */ |
| 50199 | ){ |
| 50200 | int rc = SQLITE_OK; /* Return code */ |
| 50201 | int szPage; /* Database page-size */ |
| 50202 | WalIterator *pIter = 0; /* Wal iterator context */ |
| 50203 | u32 iDbpage = 0; /* Next database page to write */ |
| 50204 | u32 iFrame = 0; /* Wal frame containing data for iDbpage */ |
| 50205 | u32 mxSafeFrame; /* Max frame that can be backfilled */ |
| @@ -50125,108 +50209,111 @@ | |
| 50209 | |
| 50210 | szPage = walPagesize(pWal); |
| 50211 | testcase( szPage<=32768 ); |
| 50212 | testcase( szPage>=65536 ); |
| 50213 | pInfo = walCkptInfo(pWal); |
| 50214 | if( pInfo->nBackfill<pWal->hdr.mxFrame ){ |
| 50215 | |
| 50216 | /* Allocate the iterator */ |
| 50217 | rc = walIteratorInit(pWal, &pIter); |
| 50218 | if( rc!=SQLITE_OK ){ |
| 50219 | return rc; |
| 50220 | } |
| 50221 | assert( pIter ); |
| 50222 | |
| 50223 | /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked |
| 50224 | ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ |
| 50225 | assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 ); |
| 50226 | |
| 50227 | /* Compute in mxSafeFrame the index of the last frame of the WAL that is |
| 50228 | ** safe to write into the database. Frames beyond mxSafeFrame might |
| 50229 | ** overwrite database pages that are in use by active readers and thus |
| 50230 | ** cannot be backfilled from the WAL. |
| 50231 | */ |
| 50232 | mxSafeFrame = pWal->hdr.mxFrame; |
| 50233 | mxPage = pWal->hdr.nPage; |
| 50234 | for(i=1; i<WAL_NREADER; i++){ |
| 50235 | u32 y = pInfo->aReadMark[i]; |
| 50236 | if( mxSafeFrame>y ){ |
| 50237 | assert( y<=pWal->hdr.mxFrame ); |
| 50238 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); |
| 50239 | if( rc==SQLITE_OK ){ |
| 50240 | pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED); |
| 50241 | walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); |
| 50242 | }else if( rc==SQLITE_BUSY ){ |
| 50243 | mxSafeFrame = y; |
| 50244 | xBusy = 0; |
| 50245 | }else{ |
| 50246 | goto walcheckpoint_out; |
| 50247 | } |
| 50248 | } |
| 50249 | } |
| 50250 | |
| 50251 | if( pInfo->nBackfill<mxSafeFrame |
| 50252 | && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK |
| 50253 | ){ |
| 50254 | i64 nSize; /* Current size of database file */ |
| 50255 | u32 nBackfill = pInfo->nBackfill; |
| 50256 | |
| 50257 | /* Sync the WAL to disk */ |
| 50258 | if( sync_flags ){ |
| 50259 | rc = sqlite3OsSync(pWal->pWalFd, sync_flags); |
| 50260 | } |
| 50261 | |
| 50262 | /* If the database may grow as a result of this checkpoint, hint |
| 50263 | ** about the eventual size of the db file to the VFS layer. |
| 50264 | */ |
| 50265 | if( rc==SQLITE_OK ){ |
| 50266 | i64 nReq = ((i64)mxPage * szPage); |
| 50267 | rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); |
| 50268 | if( rc==SQLITE_OK && nSize<nReq ){ |
| 50269 | sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); |
| 50270 | } |
| 50271 | } |
| 50272 | |
| 50273 | |
| 50274 | /* Iterate through the contents of the WAL, copying data to the db file */ |
| 50275 | while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ |
| 50276 | i64 iOffset; |
| 50277 | assert( walFramePgno(pWal, iFrame)==iDbpage ); |
| 50278 | if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){ |
| 50279 | continue; |
| 50280 | } |
| 50281 | iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; |
| 50282 | /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ |
| 50283 | rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); |
| 50284 | if( rc!=SQLITE_OK ) break; |
| 50285 | iOffset = (iDbpage-1)*(i64)szPage; |
| 50286 | testcase( IS_BIG_INT(iOffset) ); |
| 50287 | rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); |
| 50288 | if( rc!=SQLITE_OK ) break; |
| 50289 | } |
| 50290 | |
| 50291 | /* If work was actually accomplished... */ |
| 50292 | if( rc==SQLITE_OK ){ |
| 50293 | if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){ |
| 50294 | i64 szDb = pWal->hdr.nPage*(i64)szPage; |
| 50295 | testcase( IS_BIG_INT(szDb) ); |
| 50296 | rc = sqlite3OsTruncate(pWal->pDbFd, szDb); |
| 50297 | if( rc==SQLITE_OK && sync_flags ){ |
| 50298 | rc = sqlite3OsSync(pWal->pDbFd, sync_flags); |
| 50299 | } |
| 50300 | } |
| 50301 | if( rc==SQLITE_OK ){ |
| 50302 | pInfo->nBackfill = mxSafeFrame; |
| 50303 | } |
| 50304 | } |
| 50305 | |
| 50306 | /* Release the reader lock held while backfilling */ |
| 50307 | walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); |
| 50308 | } |
| 50309 | |
| 50310 | if( rc==SQLITE_BUSY ){ |
| 50311 | /* Reset the return code so as not to report a checkpoint failure |
| 50312 | ** just because there are active readers. */ |
| 50313 | rc = SQLITE_OK; |
| 50314 | } |
| 50315 | } |
| 50316 | |
| 50317 | /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the |
| 50318 | ** entire wal file has been copied into the database file, then block |
| 50319 | ** until all readers have finished using the wal file. This ensures that |
| @@ -50237,11 +50324,11 @@ | |
| 50324 | if( pInfo->nBackfill<pWal->hdr.mxFrame ){ |
| 50325 | rc = SQLITE_BUSY; |
| 50326 | }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){ |
| 50327 | u32 salt1; |
| 50328 | sqlite3_randomness(4, &salt1); |
| 50329 | assert( pInfo->nBackfill==pWal->hdr.mxFrame ); |
| 50330 | rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); |
| 50331 | if( rc==SQLITE_OK ){ |
| 50332 | if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ |
| 50333 | /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as |
| 50334 | ** SQLITE_CHECKPOINT_RESTART with the addition that it also |
| @@ -50829,11 +50916,11 @@ | |
| 50916 | } |
| 50917 | nCollide = HASHTABLE_NSLOT; |
| 50918 | for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){ |
| 50919 | u32 iFrame = aHash[iKey] + iZero; |
| 50920 | if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){ |
| 50921 | assert( iFrame>iRead || CORRUPT_DB ); |
| 50922 | iRead = iFrame; |
| 50923 | } |
| 50924 | if( (nCollide--)==0 ){ |
| 50925 | return SQLITE_CORRUPT_BKPT; |
| 50926 | } |
| @@ -51935,10 +52022,11 @@ | |
| 52022 | u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ |
| 52023 | u8 sharable; /* True if we can share pBt with another db */ |
| 52024 | u8 locked; /* True if db currently has pBt locked */ |
| 52025 | int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ |
| 52026 | int nBackup; /* Number of backup operations reading this btree */ |
| 52027 | u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */ |
| 52028 | Btree *pNext; /* List of other sharable Btrees from the same db */ |
| 52029 | Btree *pPrev; /* Back pointer of the same list */ |
| 52030 | #ifndef SQLITE_OMIT_SHARED_CACHE |
| 52031 | BtLock lock; /* Object used to lock page 1 */ |
| 52032 | #endif |
| @@ -56098,10 +56186,11 @@ | |
| 56186 | rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); |
| 56187 | if( rc!=SQLITE_OK && bCleanup==0 ){ |
| 56188 | sqlite3BtreeLeave(p); |
| 56189 | return rc; |
| 56190 | } |
| 56191 | p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */ |
| 56192 | pBt->inTransaction = TRANS_READ; |
| 56193 | btreeClearHasContent(pBt); |
| 56194 | } |
| 56195 | |
| 56196 | btreeEndTransaction(p); |
| @@ -56461,11 +56550,11 @@ | |
| 56550 | } |
| 56551 | for(i=0; i<=pCur->iPage; i++){ |
| 56552 | releasePage(pCur->apPage[i]); |
| 56553 | } |
| 56554 | unlockBtreeIfUnused(pBt); |
| 56555 | sqlite3_free(pCur->aOverflow); |
| 56556 | /* sqlite3_free(pCur); */ |
| 56557 | sqlite3BtreeLeave(pBtree); |
| 56558 | } |
| 56559 | return SQLITE_OK; |
| 56560 | } |
| @@ -56755,10 +56844,11 @@ | |
| 56844 | pBuf += a; |
| 56845 | amt -= a; |
| 56846 | }else{ |
| 56847 | offset -= pCur->info.nLocal; |
| 56848 | } |
| 56849 | |
| 56850 | |
| 56851 | if( rc==SQLITE_OK && amt>0 ){ |
| 56852 | const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */ |
| 56853 | Pgno nextPage; |
| 56854 | |
| @@ -56773,12 +56863,12 @@ | |
| 56863 | ** means "not yet known" (the cache is lazily populated). |
| 56864 | */ |
| 56865 | if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){ |
| 56866 | int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; |
| 56867 | if( nOvfl>pCur->nOvflAlloc ){ |
| 56868 | Pgno *aNew = (Pgno*)sqlite3Realloc( |
| 56869 | pCur->aOverflow, nOvfl*2*sizeof(Pgno) |
| 56870 | ); |
| 56871 | if( aNew==0 ){ |
| 56872 | rc = SQLITE_NOMEM; |
| 56873 | }else{ |
| 56874 | pCur->nOvflAlloc = nOvfl*2; |
| @@ -56821,10 +56911,11 @@ | |
| 56911 | ** Note that the aOverflow[] array must be allocated because eOp!=2 |
| 56912 | ** here. If eOp==2, then offset==0 and this branch is never taken. |
| 56913 | */ |
| 56914 | assert( eOp!=2 ); |
| 56915 | assert( pCur->curFlags & BTCF_ValidOvfl ); |
| 56916 | assert( pCur->pBtree->db==pBt->db ); |
| 56917 | if( pCur->aOverflow[iIdx+1] ){ |
| 56918 | nextPage = pCur->aOverflow[iIdx+1]; |
| 56919 | }else{ |
| 56920 | rc = getOverflowPage(pBt, nextPage, 0, &nextPage); |
| 56921 | } |
| @@ -59410,12 +59501,12 @@ | |
| 59501 | assert( leafCorrection==4 ); |
| 59502 | if( szCell[nCell]<4 ){ |
| 59503 | /* Do not allow any cells smaller than 4 bytes. If a smaller cell |
| 59504 | ** does exist, pad it with 0x00 bytes. */ |
| 59505 | assert( szCell[nCell]==3 ); |
| 59506 | assert( apCell[nCell]==&aSpace1[iSpace1-3] ); |
| 59507 | aSpace1[iSpace1++] = 0x00; |
| 59508 | szCell[nCell] = 4; |
| 59509 | } |
| 59510 | } |
| 59511 | nCell++; |
| 59512 | } |
| @@ -60723,10 +60814,17 @@ | |
| 60814 | ** is read-only, the others are read/write. |
| 60815 | ** |
| 60816 | ** The schema layer numbers meta values differently. At the schema |
| 60817 | ** layer (and the SetCookie and ReadCookie opcodes) the number of |
| 60818 | ** free pages is not visible. So Cookie[0] is the same as Meta[1]. |
| 60819 | ** |
| 60820 | ** This routine treats Meta[BTREE_DATA_VERSION] as a special case. Instead |
| 60821 | ** of reading the value out of the header, it instead loads the "DataVersion" |
| 60822 | ** from the pager. The BTREE_DATA_VERSION value is not actually stored in the |
| 60823 | ** database file. It is a number computed by the pager. But its access |
| 60824 | ** pattern is the same as header meta values, and so it is convenient to |
| 60825 | ** read it from this routine. |
| 60826 | */ |
| 60827 | SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ |
| 60828 | BtShared *pBt = p->pBt; |
| 60829 | |
| 60830 | sqlite3BtreeEnter(p); |
| @@ -60733,11 +60831,15 @@ | |
| 60831 | assert( p->inTrans>TRANS_NONE ); |
| 60832 | assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) ); |
| 60833 | assert( pBt->pPage1 ); |
| 60834 | assert( idx>=0 && idx<=15 ); |
| 60835 | |
| 60836 | if( idx==BTREE_DATA_VERSION ){ |
| 60837 | *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion; |
| 60838 | }else{ |
| 60839 | *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); |
| 60840 | } |
| 60841 | |
| 60842 | /* If auto-vacuum is disabled in this build and this is an auto-vacuum |
| 60843 | ** database, mark the database as read-only. */ |
| 60844 | #ifdef SQLITE_OMIT_AUTOVACUUM |
| 60845 | if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){ |
| @@ -60824,11 +60926,11 @@ | |
| 60926 | if( pPage->leaf ){ |
| 60927 | do { |
| 60928 | if( pCur->iPage==0 ){ |
| 60929 | /* All pages of the b-tree have been visited. Return successfully. */ |
| 60930 | *pnEntry = nEntry; |
| 60931 | return moveToRoot(pCur); |
| 60932 | } |
| 60933 | moveToParent(pCur); |
| 60934 | }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); |
| 60935 | |
| 60936 | pCur->aiIdx[pCur->iPage]++; |
| @@ -61680,11 +61782,11 @@ | |
| 61782 | } |
| 61783 | |
| 61784 | /* |
| 61785 | ** Return the size of the header added to each page by this module. |
| 61786 | */ |
| 61787 | SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } |
| 61788 | |
| 61789 | /************** End of btree.c ***********************************************/ |
| 61790 | /************** Begin file backup.c ******************************************/ |
| 61791 | /* |
| 61792 | ** 2009 January 28 |
| @@ -64444,36 +64546,39 @@ | |
| 64546 | ** |
| 64547 | ** assert( sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) ); |
| 64548 | */ |
| 64549 | SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ |
| 64550 | int hasAbort = 0; |
| 64551 | int hasFkCounter = 0; |
| 64552 | Op *pOp; |
| 64553 | VdbeOpIter sIter; |
| 64554 | memset(&sIter, 0, sizeof(sIter)); |
| 64555 | sIter.v = v; |
| 64556 | |
| 64557 | while( (pOp = opIterNext(&sIter))!=0 ){ |
| 64558 | int opcode = pOp->opcode; |
| 64559 | if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename |
| 64560 | || ((opcode==OP_Halt || opcode==OP_HaltIfNull) |
| 64561 | && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) |
| 64562 | ){ |
| 64563 | hasAbort = 1; |
| 64564 | break; |
| 64565 | } |
| 64566 | #ifndef SQLITE_OMIT_FOREIGN_KEY |
| 64567 | if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){ |
| 64568 | hasFkCounter = 1; |
| 64569 | } |
| 64570 | #endif |
| 64571 | } |
| 64572 | sqlite3DbFree(v->db, sIter.apSub); |
| 64573 | |
| 64574 | /* Return true if hasAbort==mayAbort. Or if a malloc failure occurred. |
| 64575 | ** If malloc failed, then the while() loop above may not have iterated |
| 64576 | ** through all opcodes and hasAbort may be set incorrectly. Return |
| 64577 | ** true for this case to prevent the assert() in the callers frame |
| 64578 | ** from failing. */ |
| 64579 | return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter ); |
| 64580 | } |
| 64581 | #endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */ |
| 64582 | |
| 64583 | /* |
| 64584 | ** Loop through the program looking for P2 values that are negative |
| @@ -67394,10 +67499,45 @@ | |
| 67499 | if( pKeyInfo->db->mallocFailed ) return 1; |
| 67500 | return 0; |
| 67501 | } |
| 67502 | #endif |
| 67503 | |
| 67504 | #if SQLITE_DEBUG |
| 67505 | /* |
| 67506 | ** Count the number of fields (a.k.a. columns) in the record given by |
| 67507 | ** pKey,nKey. The verify that this count is less than or equal to the |
| 67508 | ** limit given by pKeyInfo->nField + pKeyInfo->nXField. |
| 67509 | ** |
| 67510 | ** If this constraint is not satisfied, it means that the high-speed |
| 67511 | ** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will |
| 67512 | ** not work correctly. If this assert() ever fires, it probably means |
| 67513 | ** that the KeyInfo.nField or KeyInfo.nXField values were computed |
| 67514 | ** incorrectly. |
| 67515 | */ |
| 67516 | static void vdbeAssertFieldCountWithinLimits( |
| 67517 | int nKey, const void *pKey, /* The record to verify */ |
| 67518 | const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */ |
| 67519 | ){ |
| 67520 | int nField = 0; |
| 67521 | u32 szHdr; |
| 67522 | u32 idx; |
| 67523 | u32 notUsed; |
| 67524 | const unsigned char *aKey = (const unsigned char*)pKey; |
| 67525 | |
| 67526 | if( CORRUPT_DB ) return; |
| 67527 | idx = getVarint32(aKey, szHdr); |
| 67528 | assert( szHdr<=nKey ); |
| 67529 | while( idx<szHdr ){ |
| 67530 | idx += getVarint32(aKey+idx, notUsed); |
| 67531 | nField++; |
| 67532 | } |
| 67533 | assert( nField <= pKeyInfo->nField+pKeyInfo->nXField ); |
| 67534 | } |
| 67535 | #else |
| 67536 | # define vdbeAssertFieldCountWithinLimits(A,B,C) |
| 67537 | #endif |
| 67538 | |
| 67539 | /* |
| 67540 | ** Both *pMem1 and *pMem2 contain string values. Compare the two values |
| 67541 | ** using the collation sequence pColl. As usual, return a negative , zero |
| 67542 | ** or positive value if *pMem1 is less than, equal to or greater than |
| 67543 | ** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);". |
| @@ -67805,10 +67945,11 @@ | |
| 67945 | u32 y; |
| 67946 | u64 x; |
| 67947 | i64 v = pPKey2->aMem[0].u.i; |
| 67948 | i64 lhs; |
| 67949 | |
| 67950 | vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); |
| 67951 | assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB ); |
| 67952 | switch( serial_type ){ |
| 67953 | case 1: { /* 1-byte signed integer */ |
| 67954 | lhs = ONE_BYTE_INT(aKey); |
| 67955 | testcase( lhs<0 ); |
| @@ -67892,10 +68033,11 @@ | |
| 68033 | ){ |
| 68034 | const u8 *aKey1 = (const u8*)pKey1; |
| 68035 | int serial_type; |
| 68036 | int res; |
| 68037 | |
| 68038 | vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo); |
| 68039 | getVarint32(&aKey1[1], serial_type); |
| 68040 | if( serial_type<12 ){ |
| 68041 | res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */ |
| 68042 | }else if( !(serial_type & 0x01) ){ |
| 68043 | res = pPKey2->r2; /* (pKey1/nKey1) is a blob */ |
| @@ -68593,11 +68735,14 @@ | |
| 68735 | #ifndef SQLITE_OMIT_WAL |
| 68736 | int i; |
| 68737 | for(i=0; i<db->nDb; i++){ |
| 68738 | Btree *pBt = db->aDb[i].pBt; |
| 68739 | if( pBt ){ |
| 68740 | int nEntry; |
| 68741 | sqlite3BtreeEnter(pBt); |
| 68742 | nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt)); |
| 68743 | sqlite3BtreeLeave(pBt); |
| 68744 | if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){ |
| 68745 | rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry); |
| 68746 | } |
| 68747 | } |
| 68748 | } |
| @@ -68773,11 +68918,10 @@ | |
| 68918 | ** program counter to 0 to ensure that when the statement is |
| 68919 | ** finalized or reset the parser error message is available via |
| 68920 | ** sqlite3_errmsg() and sqlite3_errcode(). |
| 68921 | */ |
| 68922 | const char *zErr = (const char *)sqlite3_value_text(db->pErr); |
| 68923 | sqlite3DbFree(db, v->zErrMsg); |
| 68924 | if( !db->mallocFailed ){ |
| 68925 | v->zErrMsg = sqlite3DbStrDup(db, zErr); |
| 68926 | v->rc = rc2; |
| 68927 | } else { |
| @@ -73838,12 +73982,12 @@ | |
| 73982 | pIdxKey->default_rc = 0; |
| 73983 | if( pOp->opcode==OP_NoConflict ){ |
| 73984 | /* For the OP_NoConflict opcode, take the jump if any of the |
| 73985 | ** input fields are NULL, since any key with a NULL will not |
| 73986 | ** conflict */ |
| 73987 | for(ii=0; ii<pIdxKey->nField; ii++){ |
| 73988 | if( pIdxKey->aMem[ii].flags & MEM_Null ){ |
| 73989 | pc = pOp->p2 - 1; VdbeBranchTaken(1,2); |
| 73990 | break; |
| 73991 | } |
| 73992 | } |
| 73993 | } |
| @@ -77135,11 +77279,11 @@ | |
| 77279 | /* |
| 77280 | ** Hard-coded maximum amount of data to accumulate in memory before flushing |
| 77281 | ** to a level 0 PMA. The purpose of this limit is to prevent various integer |
| 77282 | ** overflows. 512MiB. |
| 77283 | */ |
| 77284 | #define SQLITE_MAX_PMASZ (1<<29) |
| 77285 | |
| 77286 | /* |
| 77287 | ** Private objects used by the sorter |
| 77288 | */ |
| 77289 | typedef struct MergeEngine MergeEngine; /* Merge PMAs together */ |
| @@ -77431,15 +77575,10 @@ | |
| 77575 | ** |
| 77576 | ** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; } |
| 77577 | */ |
| 77578 | #define SRVAL(p) ((void*)((SorterRecord*)(p) + 1)) |
| 77579 | |
| 77580 | |
| 77581 | /* Maximum number of PMAs that a single MergeEngine can merge */ |
| 77582 | #define SORTER_MAX_MERGE_COUNT 16 |
| 77583 | |
| 77584 | static int vdbeIncrSwap(IncrMerger*); |
| @@ -77834,14 +77973,15 @@ | |
| 77973 | SortSubtask *pTask = &pSorter->aTask[i]; |
| 77974 | pTask->pSorter = pSorter; |
| 77975 | } |
| 77976 | |
| 77977 | if( !sqlite3TempInMemory(db) ){ |
| 77978 | u32 szPma = sqlite3GlobalConfig.szPma; |
| 77979 | pSorter->mnPmaSize = szPma * pgsz; |
| 77980 | mxCache = db->aDb[0].pSchema->cache_size; |
| 77981 | if( mxCache<(int)szPma ) mxCache = (int)szPma; |
| 77982 | pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_PMASZ); |
| 77983 | |
| 77984 | /* EVIDENCE-OF: R-26747-61719 When the application provides any amount of |
| 77985 | ** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary |
| 77986 | ** large heap allocations. |
| 77987 | */ |
| @@ -78115,16 +78255,16 @@ | |
| 78255 | ** Whether or not the file does end up memory mapped of course depends on |
| 78256 | ** the specific VFS implementation. |
| 78257 | */ |
| 78258 | static void vdbeSorterExtendFile(sqlite3 *db, sqlite3_file *pFd, i64 nByte){ |
| 78259 | if( nByte<=(i64)(db->nMaxSorterMmap) && pFd->pMethods->iVersion>=3 ){ |
| 78260 | void *p = 0; |
| 78261 | int chunksize = 4*1024; |
| 78262 | sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize); |
| 78263 | sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte); |
| 78264 | sqlite3OsFetch(pFd, 0, (int)nByte, &p); |
| 78265 | sqlite3OsUnfetch(pFd, 0, p); |
| 78266 | } |
| 78267 | } |
| 78268 | #else |
| 78269 | # define vdbeSorterExtendFile(x,y,z) |
| 78270 | #endif |
| @@ -79401,10 +79541,11 @@ | |
| 79541 | rc = vdbePmaReaderNext(pSorter->pReader); |
| 79542 | *pbEof = (pSorter->pReader->pFd==0); |
| 79543 | }else |
| 79544 | #endif |
| 79545 | /*if( !pSorter->bUseThreads )*/ { |
| 79546 | assert( pSorter->pMerger!=0 ); |
| 79547 | assert( pSorter->pMerger->pTask==(&pSorter->aTask[0]) ); |
| 79548 | rc = vdbeMergeEngineStep(pSorter->pMerger, pbEof); |
| 79549 | } |
| 79550 | }else{ |
| 79551 | SorterRecord *pFree = pSorter->list.pList; |
| @@ -82167,11 +82308,11 @@ | |
| 82308 | Expr *pLeft, /* Left operand */ |
| 82309 | Expr *pRight, /* Right operand */ |
| 82310 | const Token *pToken /* Argument token */ |
| 82311 | ){ |
| 82312 | Expr *p; |
| 82313 | if( op==TK_AND && pLeft && pRight && pParse->nErr==0 ){ |
| 82314 | /* Take advantage of short-circuit false optimization for AND */ |
| 82315 | p = sqlite3ExprAnd(pParse->db, pLeft, pRight); |
| 82316 | }else{ |
| 82317 | p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); |
| 82318 | sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); |
| @@ -85721,14 +85862,15 @@ | |
| 85862 | ** NEVER() will need to be removed. */ |
| 85863 | if( pExpr->op==TK_COLUMN || NEVER(pExpr->op==TK_AGG_COLUMN) ){ |
| 85864 | int i; |
| 85865 | struct SrcCount *p = pWalker->u.pSrcCount; |
| 85866 | SrcList *pSrc = p->pSrc; |
| 85867 | int nSrc = pSrc ? pSrc->nSrc : 0; |
| 85868 | for(i=0; i<nSrc; i++){ |
| 85869 | if( pExpr->iTable==pSrc->a[i].iCursor ) break; |
| 85870 | } |
| 85871 | if( i<nSrc ){ |
| 85872 | p->nThis++; |
| 85873 | }else{ |
| 85874 | p->nOther++; |
| 85875 | } |
| 85876 | } |
| @@ -87302,11 +87444,11 @@ | |
| 87444 | |
| 87445 | p->iGet = -1; |
| 87446 | p->mxSample = mxSample; |
| 87447 | p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1); |
| 87448 | p->current.anLt = &p->current.anEq[nColUp]; |
| 87449 | p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]); |
| 87450 | |
| 87451 | /* Set up the Stat4Accum.a[] and aBest[] arrays */ |
| 87452 | p->a = (struct Stat4Sample*)&p->current.anLt[nColUp]; |
| 87453 | p->aBest = &p->a[mxSample]; |
| 87454 | pSpace = (u8*)(&p->a[mxSample+nCol]); |
| @@ -88895,17 +89037,19 @@ | |
| 89037 | }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){ |
| 89038 | zErrDyn = sqlite3MPrintf(db, |
| 89039 | "attached databases must use the same text encoding as main database"); |
| 89040 | rc = SQLITE_ERROR; |
| 89041 | } |
| 89042 | sqlite3BtreeEnter(aNew->pBt); |
| 89043 | pPager = sqlite3BtreePager(aNew->pBt); |
| 89044 | sqlite3PagerLockingMode(pPager, db->dfltLockMode); |
| 89045 | sqlite3BtreeSecureDelete(aNew->pBt, |
| 89046 | sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) ); |
| 89047 | #ifndef SQLITE_OMIT_PAGER_PRAGMAS |
| 89048 | sqlite3BtreeSetPagerFlags(aNew->pBt, 3 | (db->flags & PAGER_FLAGS_MASK)); |
| 89049 | #endif |
| 89050 | sqlite3BtreeLeave(aNew->pBt); |
| 89051 | } |
| 89052 | aNew->safety_level = 3; |
| 89053 | aNew->zName = sqlite3DbStrDup(db, zName); |
| 89054 | if( rc==SQLITE_OK && aNew->zName==0 ){ |
| 89055 | rc = SQLITE_NOMEM; |
| @@ -90027,11 +90171,10 @@ | |
| 90171 | */ |
| 90172 | static void freeIndex(sqlite3 *db, Index *p){ |
| 90173 | #ifndef SQLITE_OMIT_ANALYZE |
| 90174 | sqlite3DeleteIndexSamples(db, p); |
| 90175 | #endif |
| 90176 | sqlite3ExprDelete(db, p->pPartIdxWhere); |
| 90177 | sqlite3DbFree(db, p->zColAff); |
| 90178 | if( p->isResized ) sqlite3DbFree(db, p->azColl); |
| 90179 | #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 |
| 90180 | sqlite3_free(p->aiRowEst); |
| @@ -91306,10 +91449,23 @@ | |
| 91449 | if( pPk==0 ) return; |
| 91450 | pPk->idxType = SQLITE_IDXTYPE_PRIMARYKEY; |
| 91451 | pTab->iPKey = -1; |
| 91452 | }else{ |
| 91453 | pPk = sqlite3PrimaryKeyIndex(pTab); |
| 91454 | /* |
| 91455 | ** Remove all redundant columns from the PRIMARY KEY. For example, change |
| 91456 | ** "PRIMARY KEY(a,b,a,b,c,b,c,d)" into just "PRIMARY KEY(a,b,c,d)". Later |
| 91457 | ** code assumes the PRIMARY KEY contains no repeated columns. |
| 91458 | */ |
| 91459 | for(i=j=1; i<pPk->nKeyCol; i++){ |
| 91460 | if( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) ){ |
| 91461 | pPk->nColumn--; |
| 91462 | }else{ |
| 91463 | pPk->aiColumn[j++] = pPk->aiColumn[i]; |
| 91464 | } |
| 91465 | } |
| 91466 | pPk->nKeyCol = j; |
| 91467 | } |
| 91468 | pPk->isCovering = 1; |
| 91469 | assert( pPk!=0 ); |
| 91470 | nPk = pPk->nKeyCol; |
| 91471 | |
| @@ -93782,44 +93938,35 @@ | |
| 93938 | ** |
| 93939 | ** The caller should invoke sqlite3KeyInfoUnref() on the returned object |
| 93940 | ** when it has finished using it. |
| 93941 | */ |
| 93942 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){ |
| 93943 | int i; |
| 93944 | int nCol = pIdx->nColumn; |
| 93945 | int nKey = pIdx->nKeyCol; |
| 93946 | KeyInfo *pKey; |
| 93947 | if( pParse->nErr ) return 0; |
| 93948 | if( pIdx->uniqNotNull ){ |
| 93949 | pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey); |
| 93950 | }else{ |
| 93951 | pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0); |
| 93952 | } |
| 93953 | if( pKey ){ |
| 93954 | assert( sqlite3KeyInfoIsWriteable(pKey) ); |
| 93955 | for(i=0; i<nCol; i++){ |
| 93956 | char *zColl = pIdx->azColl[i]; |
| 93957 | assert( zColl!=0 ); |
| 93958 | pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 : |
| 93959 | sqlite3LocateCollSeq(pParse, zColl); |
| 93960 | pKey->aSortOrder[i] = pIdx->aSortOrder[i]; |
| 93961 | } |
| 93962 | if( pParse->nErr ){ |
| 93963 | sqlite3KeyInfoUnref(pKey); |
| 93964 | pKey = 0; |
| 93965 | } |
| 93966 | } |
| 93967 | return pKey; |
| 93968 | } |
| 93969 | |
| 93970 | #ifndef SQLITE_OMIT_CTE |
| 93971 | /* |
| 93972 | ** This routine is invoked once per CTE by the parser while parsing a |
| @@ -94596,12 +94743,12 @@ | |
| 94743 | const char *zDb; /* Name of database holding pTab */ |
| 94744 | int i; /* Loop counter */ |
| 94745 | WhereInfo *pWInfo; /* Information about the WHERE clause */ |
| 94746 | Index *pIdx; /* For looping over indices of the table */ |
| 94747 | int iTabCur; /* Cursor number for the table */ |
| 94748 | int iDataCur = 0; /* VDBE cursor for the canonical data source */ |
| 94749 | int iIdxCur = 0; /* Cursor number of the first index */ |
| 94750 | int nIdx; /* Number of indices */ |
| 94751 | sqlite3 *db; /* Main database structure */ |
| 94752 | AuthContext sContext; /* Authorization context */ |
| 94753 | NameContext sNC; /* Name context to resolve expressions in */ |
| 94754 | int iDb; /* Database number */ |
| @@ -97436,11 +97583,11 @@ | |
| 97583 | assert( nIncr==1 ); |
| 97584 | sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY, |
| 97585 | OE_Abort, 0, P4_STATIC, P5_ConstraintFK); |
| 97586 | }else{ |
| 97587 | if( nIncr>0 && pFKey->isDeferred==0 ){ |
| 97588 | sqlite3MayAbort(pParse); |
| 97589 | } |
| 97590 | sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); |
| 97591 | } |
| 97592 | |
| 97593 | sqlite3VdbeResolveLabel(v, iOk); |
| @@ -97507,10 +97654,14 @@ | |
| 97654 | ** This function is called to generate code executed when a row is deleted |
| 97655 | ** from the parent table of foreign key constraint pFKey and, if pFKey is |
| 97656 | ** deferred, when a row is inserted into the same table. When generating |
| 97657 | ** code for an SQL UPDATE operation, this function may be called twice - |
| 97658 | ** once to "delete" the old row and once to "insert" the new row. |
| 97659 | ** |
| 97660 | ** Parameter nIncr is passed -1 when inserting a row (as this may decrease |
| 97661 | ** the number of FK violations in the db) or +1 when deleting one (as this |
| 97662 | ** may increase the number of FK constraint problems). |
| 97663 | ** |
| 97664 | ** The code generated by this function scans through the rows in the child |
| 97665 | ** table that correspond to the parent table row being deleted or inserted. |
| 97666 | ** For each child row found, one of the following actions is taken: |
| 97667 | ** |
| @@ -97624,17 +97775,13 @@ | |
| 97775 | sNameContext.pSrcList = pSrc; |
| 97776 | sNameContext.pParse = pParse; |
| 97777 | sqlite3ResolveExprNames(&sNameContext, pWhere); |
| 97778 | |
| 97779 | /* Create VDBE to loop through the entries in pSrc that match the WHERE |
| 97780 | ** clause. For each row found, increment either the deferred or immediate |
| 97781 | ** foreign key constraint counter. */ |
| 97782 | pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0); |
| 97783 | sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); |
| 97784 | if( pWInfo ){ |
| 97785 | sqlite3WhereEnd(pWInfo); |
| 97786 | } |
| 97787 | |
| @@ -97808,10 +97955,28 @@ | |
| 97955 | } |
| 97956 | } |
| 97957 | } |
| 97958 | return 0; |
| 97959 | } |
| 97960 | |
| 97961 | /* |
| 97962 | ** Return true if the parser passed as the first argument is being |
| 97963 | ** used to code a trigger that is really a "SET NULL" action belonging |
| 97964 | ** to trigger pFKey. |
| 97965 | */ |
| 97966 | static int isSetNullAction(Parse *pParse, FKey *pFKey){ |
| 97967 | Parse *pTop = sqlite3ParseToplevel(pParse); |
| 97968 | if( pTop->pTriggerPrg ){ |
| 97969 | Trigger *p = pTop->pTriggerPrg->pTrigger; |
| 97970 | if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull) |
| 97971 | || (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull) |
| 97972 | ){ |
| 97973 | return 1; |
| 97974 | } |
| 97975 | } |
| 97976 | return 0; |
| 97977 | } |
| 97978 | |
| 97979 | /* |
| 97980 | ** This function is called when inserting, deleting or updating a row of |
| 97981 | ** table pTab to generate VDBE code to perform foreign key constraint |
| 97982 | ** processing for the operation. |
| @@ -97861,11 +98026,11 @@ | |
| 98026 | Index *pIdx = 0; /* Index on key columns in pTo */ |
| 98027 | int *aiFree = 0; |
| 98028 | int *aiCol; |
| 98029 | int iCol; |
| 98030 | int i; |
| 98031 | int bIgnore = 0; |
| 98032 | |
| 98033 | if( aChange |
| 98034 | && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0 |
| 98035 | && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0 |
| 98036 | ){ |
| @@ -97920,11 +98085,11 @@ | |
| 98085 | ** values read from the parent table are NULL. */ |
| 98086 | if( db->xAuth ){ |
| 98087 | int rcauth; |
| 98088 | char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; |
| 98089 | rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); |
| 98090 | bIgnore = (rcauth==SQLITE_IGNORE); |
| 98091 | } |
| 98092 | #endif |
| 98093 | } |
| 98094 | |
| 98095 | /* Take a shared-cache advisory read-lock on the parent table. Allocate |
| @@ -97935,16 +98100,22 @@ | |
| 98100 | |
| 98101 | if( regOld!=0 ){ |
| 98102 | /* A row is being removed from the child table. Search for the parent. |
| 98103 | ** If the parent does not exist, removing the child row resolves an |
| 98104 | ** outstanding foreign key constraint violation. */ |
| 98105 | fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1, bIgnore); |
| 98106 | } |
| 98107 | if( regNew!=0 && !isSetNullAction(pParse, pFKey) ){ |
| 98108 | /* A row is being added to the child table. If a parent row cannot |
| 98109 | ** be found, adding the child row has violated the FK constraint. |
| 98110 | ** |
| 98111 | ** If this operation is being performed as part of a trigger program |
| 98112 | ** that is actually a "SET NULL" action belonging to this very |
| 98113 | ** foreign key, then omit this scan altogether. As all child key |
| 98114 | ** values are guaranteed to be NULL, it is not possible for adding |
| 98115 | ** this row to cause an FK violation. */ |
| 98116 | fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1, bIgnore); |
| 98117 | } |
| 98118 | |
| 98119 | sqlite3DbFree(db, aiFree); |
| 98120 | } |
| 98121 | |
| @@ -97961,12 +98132,12 @@ | |
| 98132 | |
| 98133 | if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs) |
| 98134 | && !pParse->pToplevel && !pParse->isMultiWrite |
| 98135 | ){ |
| 98136 | assert( regOld==0 && regNew!=0 ); |
| 98137 | /* Inserting a single row into a parent table cannot cause (or fix) |
| 98138 | ** an immediate foreign key violation. So do nothing in this case. */ |
| 98139 | continue; |
| 98140 | } |
| 98141 | |
| 98142 | if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ |
| 98143 | if( !isIgnoreErrors || db->mallocFailed ) return; |
| @@ -97986,17 +98157,32 @@ | |
| 98157 | |
| 98158 | if( regNew!=0 ){ |
| 98159 | fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); |
| 98160 | } |
| 98161 | if( regOld!=0 ){ |
| 98162 | int eAction = pFKey->aAction[aChange!=0]; |
| 98163 | fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); |
| 98164 | /* If this is a deferred FK constraint, or a CASCADE or SET NULL |
| 98165 | ** action applies, then any foreign key violations caused by |
| 98166 | ** removing the parent key will be rectified by the action trigger. |
| 98167 | ** So do not set the "may-abort" flag in this case. |
| 98168 | ** |
| 98169 | ** Note 1: If the FK is declared "ON UPDATE CASCADE", then the |
| 98170 | ** may-abort flag will eventually be set on this statement anyway |
| 98171 | ** (when this function is called as part of processing the UPDATE |
| 98172 | ** within the action trigger). |
| 98173 | ** |
| 98174 | ** Note 2: At first glance it may seem like SQLite could simply omit |
| 98175 | ** all OP_FkCounter related scans when either CASCADE or SET NULL |
| 98176 | ** applies. The trouble starts if the CASCADE or SET NULL action |
| 98177 | ** trigger causes other triggers or action rules attached to the |
| 98178 | ** child table to fire. In these cases the fk constraint counters |
| 98179 | ** might be set incorrectly if any OP_FkCounter related scans are |
| 98180 | ** omitted. */ |
| 98181 | if( !pFKey->isDeferred && eAction!=OE_Cascade && eAction!=OE_SetNull ){ |
| 98182 | sqlite3MayAbort(pParse); |
| 98183 | } |
| 98184 | } |
| 98185 | pItem->zName = 0; |
| 98186 | sqlite3SrcListDelete(db, pSrc); |
| 98187 | } |
| 98188 | sqlite3DbFree(db, aiCol); |
| @@ -101895,10 +102081,11 @@ | |
| 102081 | #define PragTyp_KEY 38 |
| 102082 | #define PragTyp_REKEY 39 |
| 102083 | #define PragTyp_LOCK_STATUS 40 |
| 102084 | #define PragTyp_PARSER_TRACE 41 |
| 102085 | #define PragFlag_NeedSchema 0x01 |
| 102086 | #define PragFlag_ReadOnly 0x02 |
| 102087 | static const struct sPragmaNames { |
| 102088 | const char *const zName; /* Name of pragma */ |
| 102089 | u8 ePragTyp; /* PragTyp_XXX value */ |
| 102090 | u8 mPragFlag; /* Zero or more PragFlag_XXX values */ |
| 102091 | u32 iArg; /* Extra argument */ |
| @@ -101911,11 +102098,11 @@ | |
| 102098 | #endif |
| 102099 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102100 | { /* zName: */ "application_id", |
| 102101 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102102 | /* ePragFlag: */ 0, |
| 102103 | /* iArg: */ BTREE_APPLICATION_ID }, |
| 102104 | #endif |
| 102105 | #if !defined(SQLITE_OMIT_AUTOVACUUM) |
| 102106 | { /* zName: */ "auto_vacuum", |
| 102107 | /* ePragTyp: */ PragTyp_AUTO_VACUUM, |
| 102108 | /* ePragFlag: */ PragFlag_NeedSchema, |
| @@ -101977,10 +102164,16 @@ | |
| 102164 | { /* zName: */ "data_store_directory", |
| 102165 | /* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY, |
| 102166 | /* ePragFlag: */ 0, |
| 102167 | /* iArg: */ 0 }, |
| 102168 | #endif |
| 102169 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102170 | { /* zName: */ "data_version", |
| 102171 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102172 | /* ePragFlag: */ PragFlag_ReadOnly, |
| 102173 | /* iArg: */ BTREE_DATA_VERSION }, |
| 102174 | #endif |
| 102175 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 102176 | { /* zName: */ "database_list", |
| 102177 | /* ePragTyp: */ PragTyp_DATABASE_LIST, |
| 102178 | /* ePragFlag: */ PragFlag_NeedSchema, |
| 102179 | /* iArg: */ 0 }, |
| @@ -102032,12 +102225,12 @@ | |
| 102225 | #endif |
| 102226 | #endif |
| 102227 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102228 | { /* zName: */ "freelist_count", |
| 102229 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102230 | /* ePragFlag: */ PragFlag_ReadOnly, |
| 102231 | /* iArg: */ BTREE_FREE_PAGE_COUNT }, |
| 102232 | #endif |
| 102233 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 102234 | { /* zName: */ "full_column_names", |
| 102235 | /* ePragTyp: */ PragTyp_FLAG, |
| 102236 | /* ePragFlag: */ 0, |
| @@ -102185,11 +102378,11 @@ | |
| 102378 | #endif |
| 102379 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102380 | { /* zName: */ "schema_version", |
| 102381 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102382 | /* ePragFlag: */ 0, |
| 102383 | /* iArg: */ BTREE_SCHEMA_VERSION }, |
| 102384 | #endif |
| 102385 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) |
| 102386 | { /* zName: */ "secure_delete", |
| 102387 | /* ePragTyp: */ PragTyp_SECURE_DELETE, |
| 102388 | /* ePragFlag: */ 0, |
| @@ -102251,11 +102444,11 @@ | |
| 102444 | /* iArg: */ 0 }, |
| 102445 | #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS) |
| 102446 | { /* zName: */ "user_version", |
| 102447 | /* ePragTyp: */ PragTyp_HEADER_VALUE, |
| 102448 | /* ePragFlag: */ 0, |
| 102449 | /* iArg: */ BTREE_USER_VERSION }, |
| 102450 | #endif |
| 102451 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 102452 | #if defined(SQLITE_DEBUG) |
| 102453 | { /* zName: */ "vdbe_addoptrace", |
| 102454 | /* ePragTyp: */ PragTyp_FLAG, |
| @@ -102294,11 +102487,11 @@ | |
| 102487 | /* ePragTyp: */ PragTyp_FLAG, |
| 102488 | /* ePragFlag: */ 0, |
| 102489 | /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode }, |
| 102490 | #endif |
| 102491 | }; |
| 102492 | /* Number of pragmas: 58 on by default, 71 total. */ |
| 102493 | /* End of the automatically generated pragma table. |
| 102494 | ***************************************************************************/ |
| 102495 | |
| 102496 | /* |
| 102497 | ** Interpret the given string as a safety level. Return 0 for OFF, |
| @@ -102544,11 +102737,11 @@ | |
| 102737 | char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */ |
| 102738 | const char *zDb = 0; /* The database name */ |
| 102739 | Token *pId; /* Pointer to <id> token */ |
| 102740 | char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */ |
| 102741 | int iDb; /* Database index for <database> */ |
| 102742 | int lwr, upr, mid = 0; /* Binary search bounds */ |
| 102743 | int rc; /* return value form SQLITE_FCNTL_PRAGMA */ |
| 102744 | sqlite3 *db = pParse->db; /* The database connection */ |
| 102745 | Db *pDb; /* The specific database being pragmaed */ |
| 102746 | Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */ |
| 102747 | |
| @@ -103904,11 +104097,12 @@ | |
| 104097 | !(DbHasProperty(db, 0, DB_SchemaLoaded)) || |
| 104098 | DbHasProperty(db, 0, DB_Empty) |
| 104099 | ){ |
| 104100 | for(pEnc=&encnames[0]; pEnc->zName; pEnc++){ |
| 104101 | if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){ |
| 104102 | SCHEMA_ENC(db) = ENC(db) = |
| 104103 | pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE; |
| 104104 | break; |
| 104105 | } |
| 104106 | } |
| 104107 | if( !pEnc->zName ){ |
| 104108 | sqlite3ErrorMsg(pParse, "unsupported encoding: %s", zRight); |
| @@ -103949,28 +104143,13 @@ | |
| 104143 | ** |
| 104144 | ** The user-version is not used internally by SQLite. It may be used by |
| 104145 | ** applications for any purpose. |
| 104146 | */ |
| 104147 | case PragTyp_HEADER_VALUE: { |
| 104148 | int iCookie = aPragmaNames[mid].iArg; /* Which cookie to read or write */ |
| 104149 | sqlite3VdbeUsesBtree(v, iDb); |
| 104150 | if( zRight && (aPragmaNames[mid].mPragFlag & PragFlag_ReadOnly)==0 ){ |
| 104151 | /* Write the specified cookie value */ |
| 104152 | static const VdbeOpList setCookie[] = { |
| 104153 | { OP_Transaction, 0, 1, 0}, /* 0 */ |
| 104154 | { OP_Integer, 0, 1, 0}, /* 1 */ |
| 104155 | { OP_SetCookie, 0, 0, 1}, /* 2 */ |
| @@ -104612,13 +104791,15 @@ | |
| 104791 | SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){ |
| 104792 | int i, rc; |
| 104793 | int commit_internal = !(db->flags&SQLITE_InternChanges); |
| 104794 | |
| 104795 | assert( sqlite3_mutex_held(db->mutex) ); |
| 104796 | assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) ); |
| 104797 | assert( db->init.busy==0 ); |
| 104798 | rc = SQLITE_OK; |
| 104799 | db->init.busy = 1; |
| 104800 | ENC(db) = SCHEMA_ENC(db); |
| 104801 | for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ |
| 104802 | if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue; |
| 104803 | rc = sqlite3InitOne(db, i, pzErrMsg); |
| 104804 | if( rc ){ |
| 104805 | sqlite3ResetOneSchema(db, i); |
| @@ -105169,24 +105350,29 @@ | |
| 105350 | u8 sortFlags; /* Zero or more SORTFLAG_* bits */ |
| 105351 | }; |
| 105352 | #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */ |
| 105353 | |
| 105354 | /* |
| 105355 | ** Delete all the content of a Select structure. Deallocate the structure |
| 105356 | ** itself only if bFree is true. |
| 105357 | */ |
| 105358 | static void clearSelect(sqlite3 *db, Select *p, int bFree){ |
| 105359 | while( p ){ |
| 105360 | Select *pPrior = p->pPrior; |
| 105361 | sqlite3ExprListDelete(db, p->pEList); |
| 105362 | sqlite3SrcListDelete(db, p->pSrc); |
| 105363 | sqlite3ExprDelete(db, p->pWhere); |
| 105364 | sqlite3ExprListDelete(db, p->pGroupBy); |
| 105365 | sqlite3ExprDelete(db, p->pHaving); |
| 105366 | sqlite3ExprListDelete(db, p->pOrderBy); |
| 105367 | sqlite3ExprDelete(db, p->pLimit); |
| 105368 | sqlite3ExprDelete(db, p->pOffset); |
| 105369 | sqlite3WithDelete(db, p->pWith); |
| 105370 | if( bFree ) sqlite3DbFree(db, p); |
| 105371 | p = pPrior; |
| 105372 | bFree = 1; |
| 105373 | } |
| 105374 | } |
| 105375 | |
| 105376 | /* |
| 105377 | ** Initialize a SelectDest structure. |
| 105378 | */ |
| @@ -105241,12 +105427,11 @@ | |
| 105427 | pNew->pOffset = pOffset; |
| 105428 | assert( pOffset==0 || pLimit!=0 ); |
| 105429 | pNew->addrOpenEphm[0] = -1; |
| 105430 | pNew->addrOpenEphm[1] = -1; |
| 105431 | if( db->mallocFailed ) { |
| 105432 | clearSelect(db, pNew, pNew!=&standin); |
| 105433 | pNew = 0; |
| 105434 | }else{ |
| 105435 | assert( pNew->pSrc!=0 || pParse->nErr>0 ); |
| 105436 | } |
| 105437 | assert( pNew!=&standin ); |
| @@ -105267,14 +105452,11 @@ | |
| 105452 | |
| 105453 | /* |
| 105454 | ** Delete the given Select structure and all of its substructures. |
| 105455 | */ |
| 105456 | SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){ |
| 105457 | clearSelect(db, p, 1); |
| 105458 | } |
| 105459 | |
| 105460 | /* |
| 105461 | ** Return a pointer to the right-most SELECT statement in a compound. |
| 105462 | */ |
| @@ -105653,11 +105835,13 @@ | |
| 105835 | if( pParse->db->mallocFailed ) return; |
| 105836 | pOp->p2 = nKey + nData; |
| 105837 | pKI = pOp->p4.pKeyInfo; |
| 105838 | memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */ |
| 105839 | sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO); |
| 105840 | testcase( pKI->nXField>2 ); |
| 105841 | pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, |
| 105842 | pKI->nXField-1); |
| 105843 | addrJmp = sqlite3VdbeCurrentAddr(v); |
| 105844 | sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); |
| 105845 | pSort->labelBkOut = sqlite3VdbeMakeLabel(v); |
| 105846 | pSort->regReturn = ++pParse->nMem; |
| 105847 | sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); |
| @@ -106164,11 +106348,11 @@ | |
| 106348 | struct ExprList_item *pItem; |
| 106349 | sqlite3 *db = pParse->db; |
| 106350 | int i; |
| 106351 | |
| 106352 | nExpr = pList->nExpr; |
| 106353 | pInfo = sqlite3KeyInfoAlloc(db, nExpr-iStart, nExtra+1); |
| 106354 | if( pInfo ){ |
| 106355 | assert( sqlite3KeyInfoIsWriteable(pInfo) ); |
| 106356 | for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){ |
| 106357 | CollSeq *pColl; |
| 106358 | pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr); |
| @@ -107186,10 +107370,70 @@ | |
| 107370 | Parse *pParse, /* Parsing context */ |
| 107371 | Select *p, /* The right-most of SELECTs to be coded */ |
| 107372 | SelectDest *pDest /* What to do with query results */ |
| 107373 | ); |
| 107374 | |
| 107375 | /* |
| 107376 | ** Error message for when two or more terms of a compound select have different |
| 107377 | ** size result sets. |
| 107378 | */ |
| 107379 | static void selectWrongNumTermsError(Parse *pParse, Select *p){ |
| 107380 | if( p->selFlags & SF_Values ){ |
| 107381 | sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms"); |
| 107382 | }else{ |
| 107383 | sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s" |
| 107384 | " do not have the same number of result columns", selectOpName(p->op)); |
| 107385 | } |
| 107386 | } |
| 107387 | |
| 107388 | /* |
| 107389 | ** Handle the special case of a compound-select that originates from a |
| 107390 | ** VALUES clause. By handling this as a special case, we avoid deep |
| 107391 | ** recursion, and thus do not need to enforce the SQLITE_LIMIT_COMPOUND_SELECT |
| 107392 | ** on a VALUES clause. |
| 107393 | ** |
| 107394 | ** Because the Select object originates from a VALUES clause: |
| 107395 | ** (1) It has no LIMIT or OFFSET |
| 107396 | ** (2) All terms are UNION ALL |
| 107397 | ** (3) There is no ORDER BY clause |
| 107398 | */ |
| 107399 | static int multiSelectValues( |
| 107400 | Parse *pParse, /* Parsing context */ |
| 107401 | Select *p, /* The right-most of SELECTs to be coded */ |
| 107402 | SelectDest *pDest /* What to do with query results */ |
| 107403 | ){ |
| 107404 | Select *pPrior; |
| 107405 | int nExpr = p->pEList->nExpr; |
| 107406 | int nRow = 1; |
| 107407 | int rc = 0; |
| 107408 | assert( p->pNext==0 ); |
| 107409 | assert( p->selFlags & SF_AllValues ); |
| 107410 | do{ |
| 107411 | assert( p->selFlags & SF_Values ); |
| 107412 | assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) ); |
| 107413 | assert( p->pLimit==0 ); |
| 107414 | assert( p->pOffset==0 ); |
| 107415 | if( p->pEList->nExpr!=nExpr ){ |
| 107416 | selectWrongNumTermsError(pParse, p); |
| 107417 | return 1; |
| 107418 | } |
| 107419 | if( p->pPrior==0 ) break; |
| 107420 | assert( p->pPrior->pNext==p ); |
| 107421 | p = p->pPrior; |
| 107422 | nRow++; |
| 107423 | }while(1); |
| 107424 | while( p ){ |
| 107425 | pPrior = p->pPrior; |
| 107426 | p->pPrior = 0; |
| 107427 | rc = sqlite3Select(pParse, p, pDest); |
| 107428 | p->pPrior = pPrior; |
| 107429 | if( rc ) break; |
| 107430 | p->nSelectRow = nRow; |
| 107431 | p = p->pNext; |
| 107432 | } |
| 107433 | return rc; |
| 107434 | } |
| 107435 | |
| 107436 | /* |
| 107437 | ** This routine is called to process a compound query form from |
| 107438 | ** two or more separate queries using UNION, UNION ALL, EXCEPT, or |
| 107439 | ** INTERSECT |
| @@ -107266,22 +107510,24 @@ | |
| 107510 | assert( p->pEList ); |
| 107511 | sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr); |
| 107512 | sqlite3VdbeChangeP5(v, BTREE_UNORDERED); |
| 107513 | dest.eDest = SRT_Table; |
| 107514 | } |
| 107515 | |
| 107516 | /* Special handling for a compound-select that originates as a VALUES clause. |
| 107517 | */ |
| 107518 | if( p->selFlags & SF_AllValues ){ |
| 107519 | rc = multiSelectValues(pParse, p, &dest); |
| 107520 | goto multi_select_end; |
| 107521 | } |
| 107522 | |
| 107523 | /* Make sure all SELECTs in the statement have the same number of elements |
| 107524 | ** in their result sets. |
| 107525 | */ |
| 107526 | assert( p->pEList && pPrior->pEList ); |
| 107527 | if( p->pEList->nExpr!=pPrior->pEList->nExpr ){ |
| 107528 | selectWrongNumTermsError(pParse, p); |
| 107529 | rc = 1; |
| 107530 | goto multi_select_end; |
| 107531 | } |
| 107532 | |
| 107533 | #ifndef SQLITE_OMIT_CTE |
| @@ -109163,11 +109409,13 @@ | |
| 109409 | if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){ |
| 109410 | return WRC_Prune; |
| 109411 | } |
| 109412 | pTabList = p->pSrc; |
| 109413 | pEList = p->pEList; |
| 109414 | if( pWalker->xSelectCallback2==selectPopWith ){ |
| 109415 | sqlite3WithPush(pParse, findRightmost(p)->pWith, 0); |
| 109416 | } |
| 109417 | |
| 109418 | /* Make sure cursor numbers have been assigned to all entries in |
| 109419 | ** the FROM clause of the SELECT statement. |
| 109420 | */ |
| 109421 | sqlite3SrcListAssignCursors(pParse, pTabList); |
| @@ -109454,11 +109702,13 @@ | |
| 109702 | if( pParse->hasCompound ){ |
| 109703 | w.xSelectCallback = convertCompoundSelectToSubquery; |
| 109704 | sqlite3WalkSelect(&w, pSelect); |
| 109705 | } |
| 109706 | w.xSelectCallback = selectExpander; |
| 109707 | if( (pSelect->selFlags & SF_AllValues)==0 ){ |
| 109708 | w.xSelectCallback2 = selectPopWith; |
| 109709 | } |
| 109710 | sqlite3WalkSelect(&w, pSelect); |
| 109711 | } |
| 109712 | |
| 109713 | |
| 109714 | #ifndef SQLITE_OMIT_SUBQUERY |
| @@ -109968,11 +110218,11 @@ | |
| 110218 | ** we figure out that the sorting index is not needed. The addrSortIndex |
| 110219 | ** variable is used to facilitate that change. |
| 110220 | */ |
| 110221 | if( sSort.pOrderBy ){ |
| 110222 | KeyInfo *pKeyInfo; |
| 110223 | pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr); |
| 110224 | sSort.iECursor = pParse->nTab++; |
| 110225 | sSort.addrSortIndex = |
| 110226 | sqlite3VdbeAddOp4(v, OP_OpenEphemeral, |
| 110227 | sSort.iECursor, sSort.pOrderBy->nExpr+1+pEList->nExpr, 0, |
| 110228 | (char*)pKeyInfo, P4_KEYINFO |
| @@ -110142,11 +110392,11 @@ | |
| 110392 | ** implement it. Allocate that sorting index now. If it turns out |
| 110393 | ** that we do not need it after all, the OP_SorterOpen instruction |
| 110394 | ** will be converted into a Noop. |
| 110395 | */ |
| 110396 | sAggInfo.sortingIdx = pParse->nTab++; |
| 110397 | pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn); |
| 110398 | addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, |
| 110399 | sAggInfo.sortingIdx, sAggInfo.nSortingColumn, |
| 110400 | 0, (char*)pKeyInfo, P4_KEYINFO); |
| 110401 | |
| 110402 | /* Initialize memory locations used by GROUP BY aggregate processing |
| @@ -110756,11 +111006,11 @@ | |
| 111006 | ){ |
| 111007 | int rc; |
| 111008 | TabResult res; |
| 111009 | |
| 111010 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 111011 | if( !sqlite3SafetyCheckOk(db) || pazResult==0 ) return SQLITE_MISUSE_BKPT; |
| 111012 | #endif |
| 111013 | *pazResult = 0; |
| 111014 | if( pnColumn ) *pnColumn = 0; |
| 111015 | if( pnRow ) *pnRow = 0; |
| 111016 | if( pzErrMsg ) *pzErrMsg = 0; |
| @@ -118626,11 +118876,10 @@ | |
| 118876 | sqlite3_free(p->u.vtab.idxStr); |
| 118877 | p->u.vtab.needFree = 0; |
| 118878 | p->u.vtab.idxStr = 0; |
| 118879 | }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){ |
| 118880 | sqlite3DbFree(db, p->u.btree.pIndex->zColAff); |
| 118881 | sqlite3DbFree(db, p->u.btree.pIndex); |
| 118882 | p->u.btree.pIndex = 0; |
| 118883 | } |
| 118884 | } |
| 118885 | } |
| @@ -123783,17 +124032,23 @@ | |
| 124032 | Select *p = yymsp[0].minor.yy3, *pNext, *pLoop; |
| 124033 | if( p ){ |
| 124034 | int cnt = 0, mxSelect; |
| 124035 | p->pWith = yymsp[-1].minor.yy59; |
| 124036 | if( p->pPrior ){ |
| 124037 | u16 allValues = SF_Values; |
| 124038 | pNext = 0; |
| 124039 | for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){ |
| 124040 | pLoop->pNext = pNext; |
| 124041 | pLoop->selFlags |= SF_Compound; |
| 124042 | allValues &= pLoop->selFlags; |
| 124043 | } |
| 124044 | if( allValues ){ |
| 124045 | p->selFlags |= SF_AllValues; |
| 124046 | }else if( |
| 124047 | (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 |
| 124048 | && cnt>mxSelect |
| 124049 | ){ |
| 124050 | sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); |
| 124051 | } |
| 124052 | } |
| 124053 | }else{ |
| 124054 | sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy59); |
| @@ -125633,10 +125888,13 @@ | |
| 125888 | u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */ |
| 125889 | sqlite3 *db = pParse->db; /* The database connection */ |
| 125890 | int mxSqlLen; /* Max length of an SQL string */ |
| 125891 | |
| 125892 | |
| 125893 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 125894 | if( zSql==0 || pzErrMsg==0 ) return SQLITE_MISUSE_BKPT; |
| 125895 | #endif |
| 125896 | mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; |
| 125897 | if( db->nVdbeActive==0 ){ |
| 125898 | db->u1.isInterrupted = 0; |
| 125899 | } |
| 125900 | pParse->rc = SQLITE_OK; |
| @@ -125871,17 +126129,10 @@ | |
| 126129 | */ |
| 126130 | SQLITE_API int sqlite3_complete(const char *zSql){ |
| 126131 | u8 state = 0; /* Current state, using numbers defined in header comment */ |
| 126132 | u8 token; /* Value of the next token */ |
| 126133 | |
| 126134 | #ifndef SQLITE_OMIT_TRIGGER |
| 126135 | /* A complex statement machine used to detect the end of a CREATE TRIGGER |
| 126136 | ** statement. This is the normal case. |
| 126137 | */ |
| 126138 | static const u8 trans[8][8] = { |
| @@ -125906,10 +126157,17 @@ | |
| 126157 | /* 0 INVALID: */ { 1, 0, 2, }, |
| 126158 | /* 1 START: */ { 1, 1, 2, }, |
| 126159 | /* 2 NORMAL: */ { 1, 2, 2, }, |
| 126160 | }; |
| 126161 | #endif /* SQLITE_OMIT_TRIGGER */ |
| 126162 | |
| 126163 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 126164 | if( zSql==0 ){ |
| 126165 | (void)SQLITE_MISUSE_BKPT; |
| 126166 | return 0; |
| 126167 | } |
| 126168 | #endif |
| 126169 | |
| 126170 | while( *zSql ){ |
| 126171 | switch( *zSql ){ |
| 126172 | case ';': { /* A semicolon */ |
| 126173 | token = tkSEMI; |
| @@ -126208,11 +126466,11 @@ | |
| 126466 | ** If the following function pointer is not NULL and if |
| 126467 | ** SQLITE_ENABLE_IOTRACE is enabled, then messages describing |
| 126468 | ** I/O active are written using this function. These messages |
| 126469 | ** are intended for debugging activity only. |
| 126470 | */ |
| 126471 | /* not-private */ void (*sqlite3IoTrace)(const char*, ...) = 0; |
| 126472 | #endif |
| 126473 | |
| 126474 | /* |
| 126475 | ** If the following global variable points to a string which is the |
| 126476 | ** name of a directory, then that directory will be used to store |
| @@ -126417,10 +126675,17 @@ | |
| 126675 | ** routine is not threadsafe. But it is safe to invoke this routine |
| 126676 | ** on when SQLite is already shut down. If SQLite is already shut down |
| 126677 | ** when this routine is invoked, then this routine is a harmless no-op. |
| 126678 | */ |
| 126679 | SQLITE_API int sqlite3_shutdown(void){ |
| 126680 | #ifdef SQLITE_OMIT_WSD |
| 126681 | int rc = sqlite3_wsd_init(4096, 24); |
| 126682 | if( rc!=SQLITE_OK ){ |
| 126683 | return rc; |
| 126684 | } |
| 126685 | #endif |
| 126686 | |
| 126687 | if( sqlite3GlobalConfig.isInit ){ |
| 126688 | #ifdef SQLITE_EXTRA_SHUTDOWN |
| 126689 | void SQLITE_EXTRA_SHUTDOWN(void); |
| 126690 | SQLITE_EXTRA_SHUTDOWN(); |
| 126691 | #endif |
| @@ -126732,10 +126997,15 @@ | |
| 126997 | ** heap. */ |
| 126998 | sqlite3GlobalConfig.nHeap = va_arg(ap, int); |
| 126999 | break; |
| 127000 | } |
| 127001 | #endif |
| 127002 | |
| 127003 | case SQLITE_CONFIG_PMASZ: { |
| 127004 | sqlite3GlobalConfig.szPma = va_arg(ap, unsigned int); |
| 127005 | break; |
| 127006 | } |
| 127007 | |
| 127008 | default: { |
| 127009 | rc = SQLITE_ERROR; |
| 127010 | break; |
| 127011 | } |
| @@ -127178,20 +127448,10 @@ | |
| 127448 | |
| 127449 | /* Close all database connections */ |
| 127450 | for(j=0; j<db->nDb; j++){ |
| 127451 | struct Db *pDb = &db->aDb[j]; |
| 127452 | if( pDb->pBt ){ |
| 127453 | sqlite3BtreeClose(pDb->pBt); |
| 127454 | pDb->pBt = 0; |
| 127455 | if( j!=1 ){ |
| 127456 | pDb->pSchema = 0; |
| 127457 | } |
| @@ -127494,11 +127754,11 @@ | |
| 127754 | */ |
| 127755 | static int sqliteDefaultBusyCallback( |
| 127756 | void *ptr, /* Database connection */ |
| 127757 | int count /* Number of times table has been busy */ |
| 127758 | ){ |
| 127759 | #if SQLITE_OS_WIN || HAVE_USLEEP |
| 127760 | static const u8 delays[] = |
| 127761 | { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 }; |
| 127762 | static const u8 totals[] = |
| 127763 | { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228 }; |
| 127764 | # define NDELAY ArraySize(delays) |
| @@ -128110,10 +128370,11 @@ | |
| 128370 | } |
| 128371 | if( iDb<0 ){ |
| 128372 | rc = SQLITE_ERROR; |
| 128373 | sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb); |
| 128374 | }else{ |
| 128375 | db->busyHandler.nBusy = 0; |
| 128376 | rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); |
| 128377 | sqlite3Error(db, rc); |
| 128378 | } |
| 128379 | rc = sqlite3ApiExit(db, rc); |
| 128380 | sqlite3_mutex_leave(db->mutex); |
| @@ -128315,36 +128576,10 @@ | |
| 128576 | */ |
| 128577 | SQLITE_API const char *sqlite3_errstr(int rc){ |
| 128578 | return sqlite3ErrStr(rc); |
| 128579 | } |
| 128580 | |
| 128581 | /* |
| 128582 | ** Create a new collating function for database "db". The name is zName |
| 128583 | ** and the encoding is enc. |
| 128584 | */ |
| 128585 | static int createCollation( |
| @@ -128384,11 +128619,10 @@ | |
| 128619 | sqlite3ErrorWithMsg(db, SQLITE_BUSY, |
| 128620 | "unable to delete/modify collation sequence due to active statements"); |
| 128621 | return SQLITE_BUSY; |
| 128622 | } |
| 128623 | sqlite3ExpirePreparedStatements(db); |
| 128624 | |
| 128625 | /* If collation sequence pColl was created directly by a call to |
| 128626 | ** sqlite3_create_collation, and not generated by synthCollSeq(), |
| 128627 | ** then any copies made by synthCollSeq() need to be invalidated. |
| 128628 | ** Also, collation destructor - CollSeq.xDel() - function may need |
| @@ -128941,10 +129175,11 @@ | |
| 129175 | sqlite3Error(db, rc); |
| 129176 | goto opendb_out; |
| 129177 | } |
| 129178 | sqlite3BtreeEnter(db->aDb[0].pBt); |
| 129179 | db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); |
| 129180 | if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db); |
| 129181 | sqlite3BtreeLeave(db->aDb[0].pBt); |
| 129182 | db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); |
| 129183 | |
| 129184 | /* The default safety_level for the main database is 'full'; for the temp |
| 129185 | ** database it is 'NONE'. This matches the pager layer defaults. |
| @@ -129099,11 +129334,11 @@ | |
| 129334 | if( zFilename8 ){ |
| 129335 | rc = openDatabase(zFilename8, ppDb, |
| 129336 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0); |
| 129337 | assert( *ppDb || rc==SQLITE_NOMEM ); |
| 129338 | if( rc==SQLITE_OK && !DbHasProperty(*ppDb, 0, DB_SchemaLoaded) ){ |
| 129339 | SCHEMA_ENC(*ppDb) = ENC(*ppDb) = SQLITE_UTF16NATIVE; |
| 129340 | } |
| 129341 | }else{ |
| 129342 | rc = SQLITE_NOMEM; |
| 129343 | } |
| 129344 | sqlite3ValueFree(pVal); |
| @@ -129310,11 +129545,11 @@ | |
| 129545 | ){ |
| 129546 | int rc; |
| 129547 | char *zErrMsg = 0; |
| 129548 | Table *pTab = 0; |
| 129549 | Column *pCol = 0; |
| 129550 | int iCol = 0; |
| 129551 | |
| 129552 | char const *zDataType = 0; |
| 129553 | char const *zCollSeq = 0; |
| 129554 | int notnull = 0; |
| 129555 | int primarykey = 0; |
| @@ -129841,32 +130076,34 @@ | |
| 130076 | /* |
| 130077 | ** Return the filename of the database associated with a database |
| 130078 | ** connection. |
| 130079 | */ |
| 130080 | SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){ |
| 130081 | Btree *pBt; |
| 130082 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 130083 | if( !sqlite3SafetyCheckOk(db) ){ |
| 130084 | (void)SQLITE_MISUSE_BKPT; |
| 130085 | return 0; |
| 130086 | } |
| 130087 | #endif |
| 130088 | pBt = sqlite3DbNameToBtree(db, zDbName); |
| 130089 | return pBt ? sqlite3BtreeGetFilename(pBt) : 0; |
| 130090 | } |
| 130091 | |
| 130092 | /* |
| 130093 | ** Return 1 if database is read-only or 0 if read/write. Return -1 if |
| 130094 | ** no such database exists. |
| 130095 | */ |
| 130096 | SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){ |
| 130097 | Btree *pBt; |
| 130098 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 130099 | if( !sqlite3SafetyCheckOk(db) ){ |
| 130100 | (void)SQLITE_MISUSE_BKPT; |
| 130101 | return -1; |
| 130102 | } |
| 130103 | #endif |
| 130104 | pBt = sqlite3DbNameToBtree(db, zDbName); |
| 130105 | return pBt ? sqlite3BtreeIsReadonly(pBt) : -1; |
| 130106 | } |
| 130107 | |
| 130108 | /************** End of main.c ************************************************/ |
| 130109 | /************** Begin file notify.c ******************************************/ |
| @@ -132931,11 +133168,11 @@ | |
| 133168 | const char *zNode, /* Buffer containing segment interior node */ |
| 133169 | int nNode, /* Size of buffer at zNode */ |
| 133170 | sqlite3_int64 *piLeaf, /* Selected leaf node */ |
| 133171 | sqlite3_int64 *piLeaf2 /* Selected leaf node */ |
| 133172 | ){ |
| 133173 | int rc = SQLITE_OK; /* Return code */ |
| 133174 | int iHeight; /* Height of this node in tree */ |
| 133175 | |
| 133176 | assert( piLeaf || piLeaf2 ); |
| 133177 | |
| 133178 | fts3GetVarint32(zNode, &iHeight); |
| @@ -132942,11 +133179,11 @@ | |
| 133179 | rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); |
| 133180 | assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) ); |
| 133181 | |
| 133182 | if( rc==SQLITE_OK && iHeight>1 ){ |
| 133183 | char *zBlob = 0; /* Blob read from %_segments table */ |
| 133184 | int nBlob = 0; /* Size of zBlob in bytes */ |
| 133185 | |
| 133186 | if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ |
| 133187 | rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0); |
| 133188 | if( rc==SQLITE_OK ){ |
| 133189 | rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0); |
| @@ -134164,11 +134401,11 @@ | |
| 134401 | int idxNum, /* Strategy index */ |
| 134402 | const char *idxStr, /* Unused */ |
| 134403 | int nVal, /* Number of elements in apVal */ |
| 134404 | sqlite3_value **apVal /* Arguments for the indexing scheme */ |
| 134405 | ){ |
| 134406 | int rc = SQLITE_OK; |
| 134407 | char *zSql; /* SQL statement used to access %_content */ |
| 134408 | int eSearch; |
| 134409 | Fts3Table *p = (Fts3Table *)pCursor->pVtab; |
| 134410 | Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; |
| 134411 | |
| @@ -140652,11 +140889,11 @@ | |
| 140889 | int argc, /* Number of elements in argv array */ |
| 140890 | const char * const *argv, /* xCreate/xConnect argument array */ |
| 140891 | sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ |
| 140892 | char **pzErr /* OUT: sqlite3_malloc'd error message */ |
| 140893 | ){ |
| 140894 | Fts3tokTable *pTab = 0; |
| 140895 | const sqlite3_tokenizer_module *pMod = 0; |
| 140896 | sqlite3_tokenizer *pTok = 0; |
| 140897 | int rc; |
| 140898 | char **azDequote = 0; |
| 140899 | int nDequote; |
| @@ -144027,12 +144264,12 @@ | |
| 144264 | } |
| 144265 | rc = sqlite3_reset(pRange); |
| 144266 | |
| 144267 | if( bOk ){ |
| 144268 | int iIdx = 0; |
| 144269 | sqlite3_stmt *pUpdate1 = 0; |
| 144270 | sqlite3_stmt *pUpdate2 = 0; |
| 144271 | |
| 144272 | if( rc==SQLITE_OK ){ |
| 144273 | rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL_IDX, &pUpdate1, 0); |
| 144274 | } |
| 144275 | if( rc==SQLITE_OK ){ |
| @@ -149260,17 +149497,16 @@ | |
| 149497 | */ |
| 149498 | static int readInt16(u8 *p){ |
| 149499 | return (p[0]<<8) + p[1]; |
| 149500 | } |
| 149501 | static void readCoord(u8 *p, RtreeCoord *pCoord){ |
| 149502 | pCoord->u = ( |
| 149503 | (((u32)p[0]) << 24) + |
| 149504 | (((u32)p[1]) << 16) + |
| 149505 | (((u32)p[2]) << 8) + |
| 149506 | (((u32)p[3]) << 0) |
| 149507 | ); |
| 149508 | } |
| 149509 | static i64 readInt64(u8 *p){ |
| 149510 | return ( |
| 149511 | (((i64)p[0]) << 56) + |
| 149512 | (((i64)p[1]) << 48) + |
| @@ -149295,11 +149531,11 @@ | |
| 149531 | } |
| 149532 | static int writeCoord(u8 *p, RtreeCoord *pCoord){ |
| 149533 | u32 i; |
| 149534 | assert( sizeof(RtreeCoord)==4 ); |
| 149535 | assert( sizeof(u32)==4 ); |
| 149536 | i = pCoord->u; |
| 149537 | p[0] = (i>>24)&0xFF; |
| 149538 | p[1] = (i>>16)&0xFF; |
| 149539 | p[2] = (i>> 8)&0xFF; |
| 149540 | p[3] = (i>> 0)&0xFF; |
| 149541 | return 4; |
| @@ -149626,18 +149862,17 @@ | |
| 149862 | RtreeNode *pNode, /* The node containing the cell to be read */ |
| 149863 | int iCell, /* Index of the cell within the node */ |
| 149864 | RtreeCell *pCell /* OUT: Write the cell contents here */ |
| 149865 | ){ |
| 149866 | u8 *pData; |
| 149867 | RtreeCoord *pCoord; |
| 149868 | int ii; |
| 149869 | pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell); |
| 149870 | pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell); |
| 149871 | pCoord = pCell->aCoord; |
| 149872 | for(ii=0; ii<pRtree->nDim*2; ii++){ |
| 149873 | readCoord(&pData[ii*4], &pCoord[ii]); |
| 149874 | } |
| 149875 | } |
| 149876 | |
| 149877 | |
| 149878 | /* Forward declaration for the function that does the work of |
| @@ -150073,11 +150308,11 @@ | |
| 150308 | } |
| 150309 | i = pCur->nPoint++; |
| 150310 | pNew = pCur->aPoint + i; |
| 150311 | pNew->rScore = rScore; |
| 150312 | pNew->iLevel = iLevel; |
| 150313 | assert( iLevel<=RTREE_MAX_DEPTH ); |
| 150314 | while( i>0 ){ |
| 150315 | RtreeSearchPoint *pParent; |
| 150316 | j = (i-1)/2; |
| 150317 | pParent = pCur->aPoint + j; |
| 150318 | if( rtreeSearchPointCompare(pNew, pParent)>=0 ) break; |
| @@ -151696,10 +151931,12 @@ | |
| 151931 | RtreeCell cell; /* New cell to insert if nData>1 */ |
| 151932 | int bHaveRowid = 0; /* Set to 1 after new rowid is determined */ |
| 151933 | |
| 151934 | rtreeReference(pRtree); |
| 151935 | assert(nData>=1); |
| 151936 | |
| 151937 | cell.iRowid = 0; /* Used only to suppress a compiler warning */ |
| 151938 | |
| 151939 | /* Constraint handling. A write operation on an r-tree table may return |
| 151940 | ** SQLITE_CONSTRAINT for two reasons: |
| 151941 | ** |
| 151942 | ** 1. A duplicate rowid value, or |
| 151943 |
+31
-13
| --- src/sqlite3.h | ||
| +++ src/sqlite3.h | ||
| @@ -105,13 +105,13 @@ | ||
| 105 | 105 | ** |
| 106 | 106 | ** See also: [sqlite3_libversion()], |
| 107 | 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | 109 | */ |
| 110 | -#define SQLITE_VERSION "3.8.8" | |
| 110 | +#define SQLITE_VERSION "3.8.8.2" | |
| 111 | 111 | #define SQLITE_VERSION_NUMBER 3008008 |
| 112 | -#define SQLITE_SOURCE_ID "2014-12-10 04:58:43 3528f8dd39acace8eeb7337994c8617313f4b04b" | |
| 112 | +#define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098" | |
| 113 | 113 | |
| 114 | 114 | /* |
| 115 | 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | 117 | ** |
| @@ -199,11 +199,11 @@ | ||
| 199 | 199 | ** This interface only reports on the compile-time mutex setting |
| 200 | 200 | ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with |
| 201 | 201 | ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but |
| 202 | 202 | ** can be fully or partially disabled using a call to [sqlite3_config()] |
| 203 | 203 | ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], |
| 204 | -** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the | |
| 204 | +** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the | |
| 205 | 205 | ** sqlite3_threadsafe() function shows only the compile-time setting of |
| 206 | 206 | ** thread safety, not any run-time changes to that setting made by |
| 207 | 207 | ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() |
| 208 | 208 | ** is unchanged by calls to sqlite3_config().)^ |
| 209 | 209 | ** |
| @@ -1568,11 +1568,11 @@ | ||
| 1568 | 1568 | ** configuration option. |
| 1569 | 1569 | ** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to |
| 1570 | 1570 | ** 8-byte aligned |
| 1571 | 1571 | ** memory, the size of each page buffer (sz), and the number of pages (N). |
| 1572 | 1572 | ** The sz argument should be the size of the largest database page |
| 1573 | -** (a power of two between 512 and 32768) plus some extra bytes for each | |
| 1573 | +** (a power of two between 512 and 65536) plus some extra bytes for each | |
| 1574 | 1574 | ** page header. ^The number of extra bytes needed by the page header |
| 1575 | 1575 | ** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option |
| 1576 | 1576 | ** to [sqlite3_config()]. |
| 1577 | 1577 | ** ^It is harmless, apart from the wasted memory, |
| 1578 | 1578 | ** for the sz parameter to be larger than necessary. The first |
| @@ -1748,10 +1748,21 @@ | ||
| 1748 | 1748 | ** <dd>^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which |
| 1749 | 1749 | ** is a pointer to an integer and writes into that integer the number of extra |
| 1750 | 1750 | ** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE]. |
| 1751 | 1751 | ** The amount of extra space required can change depending on the compiler, |
| 1752 | 1752 | ** target platform, and SQLite version. |
| 1753 | +** | |
| 1754 | +** [[SQLITE_CONFIG_PMASZ]] | |
| 1755 | +** <dt>SQLITE_CONFIG_PMASZ | |
| 1756 | +** <dd>^The SQLITE_CONFIG_PMASZ option takes a single parameter which | |
| 1757 | +** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded | |
| 1758 | +** sorter to that integer. The default minimum PMA Size is set by the | |
| 1759 | +** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched | |
| 1760 | +** to help with sort operations when multithreaded sorting | |
| 1761 | +** is enabled (using the [PRAGMA threads] command) and the amount of content | |
| 1762 | +** to be sorted exceeds the page size times the minimum of the | |
| 1763 | +** [PRAGMA cache_size] setting and this value. | |
| 1753 | 1764 | ** </dl> |
| 1754 | 1765 | */ |
| 1755 | 1766 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 1756 | 1767 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 1757 | 1768 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| @@ -1774,10 +1785,11 @@ | ||
| 1774 | 1785 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 1775 | 1786 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 1776 | 1787 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 1777 | 1788 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 1778 | 1789 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 1790 | +#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ | |
| 1779 | 1791 | |
| 1780 | 1792 | /* |
| 1781 | 1793 | ** CAPI3REF: Database Connection Configuration Options |
| 1782 | 1794 | ** |
| 1783 | 1795 | ** These constants are the available integer configuration options that |
| @@ -7183,16 +7195,14 @@ | ||
| 7183 | 7195 | |
| 7184 | 7196 | /* |
| 7185 | 7197 | ** CAPI3REF: Write-Ahead Log Commit Hook |
| 7186 | 7198 | ** |
| 7187 | 7199 | ** ^The [sqlite3_wal_hook()] function is used to register a callback that |
| 7188 | -** will be invoked each time a database connection commits data to a | |
| 7189 | -** [write-ahead log] (i.e. whenever a transaction is committed in | |
| 7190 | -** [journal_mode | journal_mode=WAL mode]). | |
| 7200 | +** is invoked each time data is committed to a database in wal mode. | |
| 7191 | 7201 | ** |
| 7192 | -** ^The callback is invoked by SQLite after the commit has taken place and | |
| 7193 | -** the associated write-lock on the database released, so the implementation | |
| 7202 | +** ^(The callback is invoked by SQLite after the commit has taken place and | |
| 7203 | +** the associated write-lock on the database released)^, so the implementation | |
| 7194 | 7204 | ** may read, write or [checkpoint] the database as required. |
| 7195 | 7205 | ** |
| 7196 | 7206 | ** ^The first parameter passed to the callback function when it is invoked |
| 7197 | 7207 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when |
| 7198 | 7208 | ** registering the callback. ^The second is a copy of the database handle. |
| @@ -7479,10 +7489,14 @@ | ||
| 7479 | 7489 | ** |
| 7480 | 7490 | ** The following constants can be used for the T parameter to the |
| 7481 | 7491 | ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a |
| 7482 | 7492 | ** different metric for sqlite3_stmt_scanstatus() to return. |
| 7483 | 7493 | ** |
| 7494 | +** When the value returned to V is a string, space to hold that string is | |
| 7495 | +** managed by the prepared statement S and will be automatically freed when | |
| 7496 | +** S is finalized. | |
| 7497 | +** | |
| 7484 | 7498 | ** <dl> |
| 7485 | 7499 | ** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt> |
| 7486 | 7500 | ** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be |
| 7487 | 7501 | ** set to the total number of times that the X-th loop has run.</dd> |
| 7488 | 7502 | ** |
| @@ -7524,11 +7538,18 @@ | ||
| 7524 | 7538 | #define SQLITE_SCANSTAT_SELECTID 5 |
| 7525 | 7539 | |
| 7526 | 7540 | /* |
| 7527 | 7541 | ** CAPI3REF: Prepared Statement Scan Status |
| 7528 | 7542 | ** |
| 7529 | -** Return status data for a single loop within query pStmt. | |
| 7543 | +** This interface returns information about the predicted and measured | |
| 7544 | +** performance for pStmt. Advanced applications can use this | |
| 7545 | +** interface to compare the predicted and the measured performance and | |
| 7546 | +** issue warnings and/or rerun [ANALYZE] if discrepancies are found. | |
| 7547 | +** | |
| 7548 | +** Since this interface is expected to be rarely used, it is only | |
| 7549 | +** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS] | |
| 7550 | +** compile-time option. | |
| 7530 | 7551 | ** |
| 7531 | 7552 | ** The "iScanStatusOp" parameter determines which status information to return. |
| 7532 | 7553 | ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior |
| 7533 | 7554 | ** of this interface is undefined. |
| 7534 | 7555 | ** ^The requested measurement is written into a variable pointed to by |
| @@ -7542,13 +7563,10 @@ | ||
| 7542 | 7563 | ** ^Statistics might not be available for all loops in all statements. ^In cases |
| 7543 | 7564 | ** where there exist loops with no available statistics, this function behaves |
| 7544 | 7565 | ** as if the loop did not exist - it returns non-zero and leave the variable |
| 7545 | 7566 | ** that pOut points to unchanged. |
| 7546 | 7567 | ** |
| 7547 | -** This API is only available if the library is built with pre-processor | |
| 7548 | -** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. | |
| 7549 | -** | |
| 7550 | 7568 | ** See also: [sqlite3_stmt_scanstatus_reset()] |
| 7551 | 7569 | */ |
| 7552 | 7570 | SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( |
| 7553 | 7571 | sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ |
| 7554 | 7572 | int idx, /* Index of loop to report on */ |
| 7555 | 7573 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -105,13 +105,13 @@ | |
| 105 | ** |
| 106 | ** See also: [sqlite3_libversion()], |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.8.8" |
| 111 | #define SQLITE_VERSION_NUMBER 3008008 |
| 112 | #define SQLITE_SOURCE_ID "2014-12-10 04:58:43 3528f8dd39acace8eeb7337994c8617313f4b04b" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| @@ -199,11 +199,11 @@ | |
| 199 | ** This interface only reports on the compile-time mutex setting |
| 200 | ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with |
| 201 | ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but |
| 202 | ** can be fully or partially disabled using a call to [sqlite3_config()] |
| 203 | ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], |
| 204 | ** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the |
| 205 | ** sqlite3_threadsafe() function shows only the compile-time setting of |
| 206 | ** thread safety, not any run-time changes to that setting made by |
| 207 | ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() |
| 208 | ** is unchanged by calls to sqlite3_config().)^ |
| 209 | ** |
| @@ -1568,11 +1568,11 @@ | |
| 1568 | ** configuration option. |
| 1569 | ** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to |
| 1570 | ** 8-byte aligned |
| 1571 | ** memory, the size of each page buffer (sz), and the number of pages (N). |
| 1572 | ** The sz argument should be the size of the largest database page |
| 1573 | ** (a power of two between 512 and 32768) plus some extra bytes for each |
| 1574 | ** page header. ^The number of extra bytes needed by the page header |
| 1575 | ** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option |
| 1576 | ** to [sqlite3_config()]. |
| 1577 | ** ^It is harmless, apart from the wasted memory, |
| 1578 | ** for the sz parameter to be larger than necessary. The first |
| @@ -1748,10 +1748,21 @@ | |
| 1748 | ** <dd>^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which |
| 1749 | ** is a pointer to an integer and writes into that integer the number of extra |
| 1750 | ** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE]. |
| 1751 | ** The amount of extra space required can change depending on the compiler, |
| 1752 | ** target platform, and SQLite version. |
| 1753 | ** </dl> |
| 1754 | */ |
| 1755 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 1756 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 1757 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| @@ -1774,10 +1785,11 @@ | |
| 1774 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 1775 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 1776 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 1777 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 1778 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 1779 | |
| 1780 | /* |
| 1781 | ** CAPI3REF: Database Connection Configuration Options |
| 1782 | ** |
| 1783 | ** These constants are the available integer configuration options that |
| @@ -7183,16 +7195,14 @@ | |
| 7183 | |
| 7184 | /* |
| 7185 | ** CAPI3REF: Write-Ahead Log Commit Hook |
| 7186 | ** |
| 7187 | ** ^The [sqlite3_wal_hook()] function is used to register a callback that |
| 7188 | ** will be invoked each time a database connection commits data to a |
| 7189 | ** [write-ahead log] (i.e. whenever a transaction is committed in |
| 7190 | ** [journal_mode | journal_mode=WAL mode]). |
| 7191 | ** |
| 7192 | ** ^The callback is invoked by SQLite after the commit has taken place and |
| 7193 | ** the associated write-lock on the database released, so the implementation |
| 7194 | ** may read, write or [checkpoint] the database as required. |
| 7195 | ** |
| 7196 | ** ^The first parameter passed to the callback function when it is invoked |
| 7197 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when |
| 7198 | ** registering the callback. ^The second is a copy of the database handle. |
| @@ -7479,10 +7489,14 @@ | |
| 7479 | ** |
| 7480 | ** The following constants can be used for the T parameter to the |
| 7481 | ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a |
| 7482 | ** different metric for sqlite3_stmt_scanstatus() to return. |
| 7483 | ** |
| 7484 | ** <dl> |
| 7485 | ** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt> |
| 7486 | ** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be |
| 7487 | ** set to the total number of times that the X-th loop has run.</dd> |
| 7488 | ** |
| @@ -7524,11 +7538,18 @@ | |
| 7524 | #define SQLITE_SCANSTAT_SELECTID 5 |
| 7525 | |
| 7526 | /* |
| 7527 | ** CAPI3REF: Prepared Statement Scan Status |
| 7528 | ** |
| 7529 | ** Return status data for a single loop within query pStmt. |
| 7530 | ** |
| 7531 | ** The "iScanStatusOp" parameter determines which status information to return. |
| 7532 | ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior |
| 7533 | ** of this interface is undefined. |
| 7534 | ** ^The requested measurement is written into a variable pointed to by |
| @@ -7542,13 +7563,10 @@ | |
| 7542 | ** ^Statistics might not be available for all loops in all statements. ^In cases |
| 7543 | ** where there exist loops with no available statistics, this function behaves |
| 7544 | ** as if the loop did not exist - it returns non-zero and leave the variable |
| 7545 | ** that pOut points to unchanged. |
| 7546 | ** |
| 7547 | ** This API is only available if the library is built with pre-processor |
| 7548 | ** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined. |
| 7549 | ** |
| 7550 | ** See also: [sqlite3_stmt_scanstatus_reset()] |
| 7551 | */ |
| 7552 | SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( |
| 7553 | sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ |
| 7554 | int idx, /* Index of loop to report on */ |
| 7555 |
| --- src/sqlite3.h | |
| +++ src/sqlite3.h | |
| @@ -105,13 +105,13 @@ | |
| 105 | ** |
| 106 | ** See also: [sqlite3_libversion()], |
| 107 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 108 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 109 | */ |
| 110 | #define SQLITE_VERSION "3.8.8.2" |
| 111 | #define SQLITE_VERSION_NUMBER 3008008 |
| 112 | #define SQLITE_SOURCE_ID "2015-01-30 14:30:45 7757fc721220e136620a89c9d28247f28bbbc098" |
| 113 | |
| 114 | /* |
| 115 | ** CAPI3REF: Run-Time Library Version Numbers |
| 116 | ** KEYWORDS: sqlite3_version, sqlite3_sourceid |
| 117 | ** |
| @@ -199,11 +199,11 @@ | |
| 199 | ** This interface only reports on the compile-time mutex setting |
| 200 | ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with |
| 201 | ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but |
| 202 | ** can be fully or partially disabled using a call to [sqlite3_config()] |
| 203 | ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], |
| 204 | ** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the |
| 205 | ** sqlite3_threadsafe() function shows only the compile-time setting of |
| 206 | ** thread safety, not any run-time changes to that setting made by |
| 207 | ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() |
| 208 | ** is unchanged by calls to sqlite3_config().)^ |
| 209 | ** |
| @@ -1568,11 +1568,11 @@ | |
| 1568 | ** configuration option. |
| 1569 | ** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to |
| 1570 | ** 8-byte aligned |
| 1571 | ** memory, the size of each page buffer (sz), and the number of pages (N). |
| 1572 | ** The sz argument should be the size of the largest database page |
| 1573 | ** (a power of two between 512 and 65536) plus some extra bytes for each |
| 1574 | ** page header. ^The number of extra bytes needed by the page header |
| 1575 | ** can be determined using the [SQLITE_CONFIG_PCACHE_HDRSZ] option |
| 1576 | ** to [sqlite3_config()]. |
| 1577 | ** ^It is harmless, apart from the wasted memory, |
| 1578 | ** for the sz parameter to be larger than necessary. The first |
| @@ -1748,10 +1748,21 @@ | |
| 1748 | ** <dd>^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which |
| 1749 | ** is a pointer to an integer and writes into that integer the number of extra |
| 1750 | ** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE]. |
| 1751 | ** The amount of extra space required can change depending on the compiler, |
| 1752 | ** target platform, and SQLite version. |
| 1753 | ** |
| 1754 | ** [[SQLITE_CONFIG_PMASZ]] |
| 1755 | ** <dt>SQLITE_CONFIG_PMASZ |
| 1756 | ** <dd>^The SQLITE_CONFIG_PMASZ option takes a single parameter which |
| 1757 | ** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded |
| 1758 | ** sorter to that integer. The default minimum PMA Size is set by the |
| 1759 | ** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched |
| 1760 | ** to help with sort operations when multithreaded sorting |
| 1761 | ** is enabled (using the [PRAGMA threads] command) and the amount of content |
| 1762 | ** to be sorted exceeds the page size times the minimum of the |
| 1763 | ** [PRAGMA cache_size] setting and this value. |
| 1764 | ** </dl> |
| 1765 | */ |
| 1766 | #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ |
| 1767 | #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ |
| 1768 | #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ |
| @@ -1774,10 +1785,11 @@ | |
| 1785 | #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ |
| 1786 | #define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ |
| 1787 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ |
| 1788 | #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ |
| 1789 | #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ |
| 1790 | #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ |
| 1791 | |
| 1792 | /* |
| 1793 | ** CAPI3REF: Database Connection Configuration Options |
| 1794 | ** |
| 1795 | ** These constants are the available integer configuration options that |
| @@ -7183,16 +7195,14 @@ | |
| 7195 | |
| 7196 | /* |
| 7197 | ** CAPI3REF: Write-Ahead Log Commit Hook |
| 7198 | ** |
| 7199 | ** ^The [sqlite3_wal_hook()] function is used to register a callback that |
| 7200 | ** is invoked each time data is committed to a database in wal mode. |
| 7201 | ** |
| 7202 | ** ^(The callback is invoked by SQLite after the commit has taken place and |
| 7203 | ** the associated write-lock on the database released)^, so the implementation |
| 7204 | ** may read, write or [checkpoint] the database as required. |
| 7205 | ** |
| 7206 | ** ^The first parameter passed to the callback function when it is invoked |
| 7207 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when |
| 7208 | ** registering the callback. ^The second is a copy of the database handle. |
| @@ -7479,10 +7489,14 @@ | |
| 7489 | ** |
| 7490 | ** The following constants can be used for the T parameter to the |
| 7491 | ** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a |
| 7492 | ** different metric for sqlite3_stmt_scanstatus() to return. |
| 7493 | ** |
| 7494 | ** When the value returned to V is a string, space to hold that string is |
| 7495 | ** managed by the prepared statement S and will be automatically freed when |
| 7496 | ** S is finalized. |
| 7497 | ** |
| 7498 | ** <dl> |
| 7499 | ** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt> |
| 7500 | ** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be |
| 7501 | ** set to the total number of times that the X-th loop has run.</dd> |
| 7502 | ** |
| @@ -7524,11 +7538,18 @@ | |
| 7538 | #define SQLITE_SCANSTAT_SELECTID 5 |
| 7539 | |
| 7540 | /* |
| 7541 | ** CAPI3REF: Prepared Statement Scan Status |
| 7542 | ** |
| 7543 | ** This interface returns information about the predicted and measured |
| 7544 | ** performance for pStmt. Advanced applications can use this |
| 7545 | ** interface to compare the predicted and the measured performance and |
| 7546 | ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. |
| 7547 | ** |
| 7548 | ** Since this interface is expected to be rarely used, it is only |
| 7549 | ** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS] |
| 7550 | ** compile-time option. |
| 7551 | ** |
| 7552 | ** The "iScanStatusOp" parameter determines which status information to return. |
| 7553 | ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior |
| 7554 | ** of this interface is undefined. |
| 7555 | ** ^The requested measurement is written into a variable pointed to by |
| @@ -7542,13 +7563,10 @@ | |
| 7563 | ** ^Statistics might not be available for all loops in all statements. ^In cases |
| 7564 | ** where there exist loops with no available statistics, this function behaves |
| 7565 | ** as if the loop did not exist - it returns non-zero and leave the variable |
| 7566 | ** that pOut points to unchanged. |
| 7567 | ** |
| 7568 | ** See also: [sqlite3_stmt_scanstatus_reset()] |
| 7569 | */ |
| 7570 | SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_scanstatus( |
| 7571 | sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ |
| 7572 | int idx, /* Index of loop to report on */ |
| 7573 |
+6
| --- src/stat.c | ||
| +++ src/stat.c | ||
| @@ -55,15 +55,17 @@ | ||
| 55 | 55 | |
| 56 | 56 | login_check_credentials(); |
| 57 | 57 | if( !g.perm.Read ){ login_needed(); return; } |
| 58 | 58 | brief = P("brief")!=0; |
| 59 | 59 | style_header("Repository Statistics"); |
| 60 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 60 | 61 | if( g.perm.Admin ){ |
| 61 | 62 | style_submenu_element("URLs", "URLs and Checkouts", "urllist"); |
| 62 | 63 | style_submenu_element("Schema", "Repository Schema", "repo_schema"); |
| 63 | 64 | style_submenu_element("Web-Cache", "Web-Cache Stats", "cachestat"); |
| 64 | 65 | } |
| 66 | + style_submenu_element("Activity", "Activity Reports", "reports"); | |
| 65 | 67 | @ <table class="label-value"> |
| 66 | 68 | @ <tr><th>Repository Size:</th><td> |
| 67 | 69 | fsize = file_size(g.zRepositoryName); |
| 68 | 70 | bigSizeName(sizeof(zBuf), zBuf, fsize); |
| 69 | 71 | @ %s(zBuf) |
| @@ -132,10 +134,11 @@ | ||
| 132 | 134 | @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 133 | 135 | @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] |
| 134 | 136 | @ </td></tr> |
| 135 | 137 | @ <tr><th>SQLite Version:</th><td>%.19s(sqlite3_sourceid()) |
| 136 | 138 | @ [%.10s(&sqlite3_sourceid()[20])] (%s(sqlite3_libversion()))</td></tr> |
| 139 | + @ <tr><th>Schema Version:</th><td>%h(g.zAuxSchema)</td></tr> | |
| 137 | 140 | @ <tr><th>Repository Rebuilt:</th><td> |
| 138 | 141 | @ %h(db_get_mtime("rebuilt","%Y-%m-%d %H:%M:%S","Never")) |
| 139 | 142 | @ By Fossil %h(db_get("rebuilt","Unknown"))</td></tr> |
| 140 | 143 | @ <tr><th>Database Stats:</th><td> |
| 141 | 144 | zDb = db_name("repository"); |
| @@ -253,10 +256,11 @@ | ||
| 253 | 256 | } |
| 254 | 257 | #if 0 |
| 255 | 258 | /* Server-id is not useful information any more */ |
| 256 | 259 | fossil_print("%*s%s\n", colWidth, "server-id:", db_get("server-code", 0)); |
| 257 | 260 | #endif |
| 261 | + fossil_print("%*s%s\n", colWidth, "schema-version:", g.zAuxSchema); | |
| 258 | 262 | if( !omitVers ){ |
| 259 | 263 | fossil_print("%*s%s %s [%s] (%s)\n", |
| 260 | 264 | colWidth, "fossil-version:", |
| 261 | 265 | MANIFEST_DATE, MANIFEST_VERSION, RELEASE_VERSION, |
| 262 | 266 | COMPILER_NAME); |
| @@ -290,10 +294,11 @@ | ||
| 290 | 294 | int cnt; |
| 291 | 295 | login_check_credentials(); |
| 292 | 296 | if( !g.perm.Admin ){ login_needed(); return; } |
| 293 | 297 | |
| 294 | 298 | style_header("URLs and Checkouts"); |
| 299 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 295 | 300 | style_submenu_element("Stat", "Repository Stats", "stat"); |
| 296 | 301 | style_submenu_element("Schema", "Repository Schema", "repo_schema"); |
| 297 | 302 | @ <div class="section">URLs</div> |
| 298 | 303 | @ <table border="0" width='100%%'> |
| 299 | 304 | db_prepare(&q, "SELECT substr(name,9), datetime(mtime,'unixepoch')" |
| @@ -336,10 +341,11 @@ | ||
| 336 | 341 | Stmt q; |
| 337 | 342 | login_check_credentials(); |
| 338 | 343 | if( !g.perm.Admin ){ login_needed(); return; } |
| 339 | 344 | |
| 340 | 345 | style_header("Repository Schema"); |
| 346 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 341 | 347 | style_submenu_element("Stat", "Repository Stats", "stat"); |
| 342 | 348 | style_submenu_element("URLs", "URLs and Checkouts", "urllist"); |
| 343 | 349 | db_prepare(&q, "SELECT sql FROM %s.sqlite_master WHERE sql IS NOT NULL", |
| 344 | 350 | db_name("repository")); |
| 345 | 351 | @ <pre> |
| 346 | 352 | |
| 347 | 353 | ADDED src/statrep.c |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -55,15 +55,17 @@ | |
| 55 | |
| 56 | login_check_credentials(); |
| 57 | if( !g.perm.Read ){ login_needed(); return; } |
| 58 | brief = P("brief")!=0; |
| 59 | style_header("Repository Statistics"); |
| 60 | if( g.perm.Admin ){ |
| 61 | style_submenu_element("URLs", "URLs and Checkouts", "urllist"); |
| 62 | style_submenu_element("Schema", "Repository Schema", "repo_schema"); |
| 63 | style_submenu_element("Web-Cache", "Web-Cache Stats", "cachestat"); |
| 64 | } |
| 65 | @ <table class="label-value"> |
| 66 | @ <tr><th>Repository Size:</th><td> |
| 67 | fsize = file_size(g.zRepositoryName); |
| 68 | bigSizeName(sizeof(zBuf), zBuf, fsize); |
| 69 | @ %s(zBuf) |
| @@ -132,10 +134,11 @@ | |
| 132 | @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 133 | @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] |
| 134 | @ </td></tr> |
| 135 | @ <tr><th>SQLite Version:</th><td>%.19s(sqlite3_sourceid()) |
| 136 | @ [%.10s(&sqlite3_sourceid()[20])] (%s(sqlite3_libversion()))</td></tr> |
| 137 | @ <tr><th>Repository Rebuilt:</th><td> |
| 138 | @ %h(db_get_mtime("rebuilt","%Y-%m-%d %H:%M:%S","Never")) |
| 139 | @ By Fossil %h(db_get("rebuilt","Unknown"))</td></tr> |
| 140 | @ <tr><th>Database Stats:</th><td> |
| 141 | zDb = db_name("repository"); |
| @@ -253,10 +256,11 @@ | |
| 253 | } |
| 254 | #if 0 |
| 255 | /* Server-id is not useful information any more */ |
| 256 | fossil_print("%*s%s\n", colWidth, "server-id:", db_get("server-code", 0)); |
| 257 | #endif |
| 258 | if( !omitVers ){ |
| 259 | fossil_print("%*s%s %s [%s] (%s)\n", |
| 260 | colWidth, "fossil-version:", |
| 261 | MANIFEST_DATE, MANIFEST_VERSION, RELEASE_VERSION, |
| 262 | COMPILER_NAME); |
| @@ -290,10 +294,11 @@ | |
| 290 | int cnt; |
| 291 | login_check_credentials(); |
| 292 | if( !g.perm.Admin ){ login_needed(); return; } |
| 293 | |
| 294 | style_header("URLs and Checkouts"); |
| 295 | style_submenu_element("Stat", "Repository Stats", "stat"); |
| 296 | style_submenu_element("Schema", "Repository Schema", "repo_schema"); |
| 297 | @ <div class="section">URLs</div> |
| 298 | @ <table border="0" width='100%%'> |
| 299 | db_prepare(&q, "SELECT substr(name,9), datetime(mtime,'unixepoch')" |
| @@ -336,10 +341,11 @@ | |
| 336 | Stmt q; |
| 337 | login_check_credentials(); |
| 338 | if( !g.perm.Admin ){ login_needed(); return; } |
| 339 | |
| 340 | style_header("Repository Schema"); |
| 341 | style_submenu_element("Stat", "Repository Stats", "stat"); |
| 342 | style_submenu_element("URLs", "URLs and Checkouts", "urllist"); |
| 343 | db_prepare(&q, "SELECT sql FROM %s.sqlite_master WHERE sql IS NOT NULL", |
| 344 | db_name("repository")); |
| 345 | @ <pre> |
| 346 | |
| 347 | DDED src/statrep.c |
| --- src/stat.c | |
| +++ src/stat.c | |
| @@ -55,15 +55,17 @@ | |
| 55 | |
| 56 | login_check_credentials(); |
| 57 | if( !g.perm.Read ){ login_needed(); return; } |
| 58 | brief = P("brief")!=0; |
| 59 | style_header("Repository Statistics"); |
| 60 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 61 | if( g.perm.Admin ){ |
| 62 | style_submenu_element("URLs", "URLs and Checkouts", "urllist"); |
| 63 | style_submenu_element("Schema", "Repository Schema", "repo_schema"); |
| 64 | style_submenu_element("Web-Cache", "Web-Cache Stats", "cachestat"); |
| 65 | } |
| 66 | style_submenu_element("Activity", "Activity Reports", "reports"); |
| 67 | @ <table class="label-value"> |
| 68 | @ <tr><th>Repository Size:</th><td> |
| 69 | fsize = file_size(g.zRepositoryName); |
| 70 | bigSizeName(sizeof(zBuf), zBuf, fsize); |
| 71 | @ %s(zBuf) |
| @@ -132,10 +134,11 @@ | |
| 134 | @ %h(MANIFEST_DATE) %h(MANIFEST_VERSION) |
| 135 | @ (%h(RELEASE_VERSION)) [compiled using %h(COMPILER_NAME)] |
| 136 | @ </td></tr> |
| 137 | @ <tr><th>SQLite Version:</th><td>%.19s(sqlite3_sourceid()) |
| 138 | @ [%.10s(&sqlite3_sourceid()[20])] (%s(sqlite3_libversion()))</td></tr> |
| 139 | @ <tr><th>Schema Version:</th><td>%h(g.zAuxSchema)</td></tr> |
| 140 | @ <tr><th>Repository Rebuilt:</th><td> |
| 141 | @ %h(db_get_mtime("rebuilt","%Y-%m-%d %H:%M:%S","Never")) |
| 142 | @ By Fossil %h(db_get("rebuilt","Unknown"))</td></tr> |
| 143 | @ <tr><th>Database Stats:</th><td> |
| 144 | zDb = db_name("repository"); |
| @@ -253,10 +256,11 @@ | |
| 256 | } |
| 257 | #if 0 |
| 258 | /* Server-id is not useful information any more */ |
| 259 | fossil_print("%*s%s\n", colWidth, "server-id:", db_get("server-code", 0)); |
| 260 | #endif |
| 261 | fossil_print("%*s%s\n", colWidth, "schema-version:", g.zAuxSchema); |
| 262 | if( !omitVers ){ |
| 263 | fossil_print("%*s%s %s [%s] (%s)\n", |
| 264 | colWidth, "fossil-version:", |
| 265 | MANIFEST_DATE, MANIFEST_VERSION, RELEASE_VERSION, |
| 266 | COMPILER_NAME); |
| @@ -290,10 +294,11 @@ | |
| 294 | int cnt; |
| 295 | login_check_credentials(); |
| 296 | if( !g.perm.Admin ){ login_needed(); return; } |
| 297 | |
| 298 | style_header("URLs and Checkouts"); |
| 299 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 300 | style_submenu_element("Stat", "Repository Stats", "stat"); |
| 301 | style_submenu_element("Schema", "Repository Schema", "repo_schema"); |
| 302 | @ <div class="section">URLs</div> |
| 303 | @ <table border="0" width='100%%'> |
| 304 | db_prepare(&q, "SELECT substr(name,9), datetime(mtime,'unixepoch')" |
| @@ -336,10 +341,11 @@ | |
| 341 | Stmt q; |
| 342 | login_check_credentials(); |
| 343 | if( !g.perm.Admin ){ login_needed(); return; } |
| 344 | |
| 345 | style_header("Repository Schema"); |
| 346 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 347 | style_submenu_element("Stat", "Repository Stats", "stat"); |
| 348 | style_submenu_element("URLs", "URLs and Checkouts", "urllist"); |
| 349 | db_prepare(&q, "SELECT sql FROM %s.sqlite_master WHERE sql IS NOT NULL", |
| 350 | db_name("repository")); |
| 351 | @ <pre> |
| 352 | |
| 353 | DDED src/statrep.c |
+535
| --- a/src/statrep.c | ||
| +++ b/src/statrep.c | ||
| @@ -0,0 +1,535 @@ | ||
| 1 | +eea"zT | |
| 2 | + ; | |
| 3 | + @ claT | |
| 4 | + ; | |
| 5 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 6 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 7 | + style_footer(); | |
| 8 | +} | |
| 9 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 10 | + ; | |
| 11 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 12 | + style_footer(); | |
| 13 | +} | |
| 14 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 15 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 16 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 17 | + @ svg class="pie-chart" | |
| 18 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 19 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 20 | + , "Sunday" | |
| 21 | + }; | |
| 22 | + | |
| 23 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( | |
| 24 | + HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT | |
| 25 | + ; | |
| 26 | + @ claT | |
| 27 | + ; | |
| 28 | + @ ce><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 29 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 30 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 31 | + @ svg class="pie-chart" | |
| 32 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 33 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 34 | + , "Sunday" | |
| 35 | + }; | |
| 36 | + | |
| 37 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 38 | + ; | |
| 39 | + @ claT | |
| 40 | + e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 41 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 42 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 43 | + @ svg class="pie-chart" | |
| 44 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 45 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 46 | + , "Sunday" | |
| 47 | + }; | |
| 48 | + | |
| 49 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 50 | + ; | |
| 51 | + @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 52 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 53 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 54 | + @ svg class="pie-chart" | |
| 55 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 56 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 57 | + , "Sunday" | |
| 58 | + }; | |
| 59 | + | |
| 60 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 61 | + ; | |
| 62 | + element(zMenuName, zMenuName, "%s url_render(pUrl, zParBy File", ny TypeelpiechartTABLE peea"zT | |
| 63 | + ; | |
| 64 | + @ claT | |
| 65 | + ; | |
| 66 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 67 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 68 | + style_footer(); | |
| 69 | +} | |
| 70 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 71 | + ; | |
| 72 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 73 | + style_footer(); | |
| 74 | +} | |
| 75 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 76 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 77 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 78 | + @ svg class="pie-chart" | |
| 79 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 80 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 81 | + , "Sunday" | |
| 82 | + }; | |
| 83 | + | |
| 84 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( | |
| 85 | + HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT | |
| 86 | + ; | |
| 87 | + @ claT | |
| 88 | + ; | |
| 89 | + @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ | |
| 90 | + azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); | |
| 91 | + azYear[n] = fossil_strdup(,0)); | |
| 92 | + azYear[n+1] = azYear[n]; | |
| 93 | + if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) zYear = azYear[0];zT | |
| 94 | + ; | |
| 95 | + @ claT | |
| 96 | + ; | |
| 97 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 98 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 99 | + style_footer(); | |
| 100 | +} | |
| 101 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 102 | + ; | |
| 103 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 104 | + style_footer(); | |
| 105 | +} | |
| 106 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 107 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 108 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 109 | + @ svg class="pie-chart" | |
| 110 | + @ HQuery url;/* RL for eventbranch linksurl_initialize(&url, "reports"); | |
| 111 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 112 | + , "Sunday" | |
| 113 | + }; | |
| 114 | + | |
| 115 | + ;mtime %% 7mt/* | |
| 116 | +** A helper for the /reports family of pages which prints out a menu | |
| 117 | +** of links for the various type=XXX flags. zCurrentViewName must be | |
| 118 | +** the name/value of the 'view' parameter which is in effect at the | |
| 119 | +** time this is called. e.g. if called from the 'byuser' view then | |
| 120 | +** zCurrentViewName must be "byuser". Any URL parameters which need to | |
| 121 | +** be added to the generated URLs should be passed in zParam. The | |
| 122 | +** caller is expected to have already encoded any zParam in event_types_menu(c><svg</centre>center><svg</centre> "Thursday", | |
| 123 | + , "Sunday" | |
| 124 | + }; | |
| 125 | + | |
| 126 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 127 | + ; | |
| 128 | + @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 129 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 130 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 131 | + @ svg class="pie-chart"uery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 132 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>csnter><svg</centre> "Thursday", | |
| 133 | + , "Sunday" | |
| 134 | + }; | |
| 135 | + | |
| 136 | + ;mtime %% 7mtime %% 7Mg.zTop,MoTuesWednThur5Sunday eea"zT | |
| 137 | + ; | |
| 138 | + element(zMenuName, zMenuNameinsass="pie-chart" | |
| 139 | + @ HQuery url;/*ci'>checkin"2;" | |
| 140 | + "UPDATE piechareea"zT | |
| 141 | + ; | |
| 142 | + @ claT | |
| 143 | + ; | |
| 144 | + even class='lastchngTable'nameeea"zTuserlist","tT} | |
| 145 | + style_footer();e'>event; @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 146 | + style_footer(); | |
| 147 | +} | |
| 148 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 149 | + ; | |
| 150 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 151 | + style_footer(); | |
| 152 | +} | |
| 153 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 154 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 155 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 156 | + @ svg class="pie-chart" | |
| 157 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 158 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 159 | + , "Sunday" | |
| 160 | + }; | |
| 161 | + | |
| 162 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( | |
| 163 | + HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT | |
| 164 | + ; | |
| 165 | + @ claT | |
| 166 | + ; | |
| 167 | + @ ce><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 168 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 169 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 170 | + @ svg class="pie-chart" | |
| 171 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 172 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 173 | + , "Sunday" | |
| 174 | + }; | |
| 175 | + | |
| 176 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 177 | + ; | |
| 178 | + @ claT | |
| 179 | + e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 180 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 181 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 182 | + @ svg class="pie-chart" | |
| 183 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 184 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 185 | + , "Sunday" | |
| 186 | + }; | |
| 187 | + | |
| 188 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 189 | + ; | |
| 190 | + @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 191 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 192 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 193 | + @ svg class="pie-chart" | |
| 194 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 195 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 196 | + , "Sunday" | |
| 197 | + }; | |
| 198 | + | |
| 199 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 200 | + ; | |
| 201 | + element(zMenuName, zMenuName, "%s url_render(pUrl, zParBy File", ny TypeelpiechartTABLE peea"zT | |
| 202 | + ; | |
| 203 | + @ claT | |
| 204 | + ; | |
| 205 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 206 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 207 | + style_footer(); | |
| 208 | +} | |
| 209 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 210 | + ; | |
| 211 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 212 | + style_footer(); | |
| 213 | +} | |
| 214 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 215 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 216 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 217 | + @ svg class="pie-chart" | |
| 218 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 219 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 220 | + , "Sunday" | |
| 221 | + }; | |
| 222 | + | |
| 223 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( | |
| 224 | + HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT | |
| 225 | + ; | |
| 226 | + @ claT | |
| 227 | + ; | |
| 228 | + @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ | |
| 229 | + azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); | |
| 230 | + azYear[n] = fossil_strdup(,0)); | |
| 231 | + azYear[n+1] = azYear[n]; | |
| 232 | + if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) zYear = azYear[0];zT | |
| 233 | + ; | |
| 234 | + @ claT | |
| 235 | + ; | |
| 236 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 237 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 238 | + style_footer(); | |
| 239 | +} | |
| 240 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 241 | + ; | |
| 242 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 243 | + style_footer(); | |
| 244 | +} | |
| 245 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 246 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 247 | + "ELSE 'ERRsR' END;"svg class="pie-chart" | |
| 248 | + @ svg class="pie-chart" | |
| 249 | + @ Hg.zTop,HQuery url;/* RL for various branch linksurl_initialize(&url, "reports"); | |
| 250 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 251 | + , "Sunday" | |
| 252 | + }; | |
| 253 | + | |
| 254 | + ;mtime %% 7mt/* | |
| 255 | +** A helper for the /reports family of pages which prints out a menu | |
| 256 | +** of links for the various type=XXX flags. zCurrentViewName must be | |
| 257 | +** the name/value of the 'view' parameter which is in effect at the | |
| 258 | +** time this is called. e.g. if called from the 'byuser' view then | |
| 259 | +** zCurrentViewName must be "byuser". Any URL parameters which need to | |
| 260 | +** be added to the generated URLs should be passed in zParam. The | |
| 261 | +** caller is expected to have already encoded any zParam in event_types_menu(c><svg</centre>center><svg</centre> "Thursday", | |
| 262 | + , "Sunday" | |
| 263 | + }; | |
| 264 | + | |
| 265 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 266 | + ; | |
| 267 | + @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 268 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 269 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 270 | + @ svg class="pie-chart"uery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 271 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 272 | + , "Sunday" | |
| 273 | + }; | |
| 274 | + | |
| 275 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 276 | + ; | |
| 277 | + element(zMenuName, zMenuNameinsass="pie-chart" | |
| 278 | + @ HQuery url;/*ci'>checkin"2;" | |
| 279 | + "UPDATE piechareea"zT | |
| 280 | + ; | |
| 281 | + @ claT | |
| 282 | + ; | |
| 283 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 284 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 285 | + style_footer(); | |
| 286 | +} | |
| 287 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 288 | + ; | |
| 289 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 290 | + style_footer(); | |
| 291 | +} | |
| 292 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 293 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 294 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 295 | + @ svg class="pie-chart" | |
| 296 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 297 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 298 | + , "Sunday" | |
| 299 | + }; | |
| 300 | + | |
| 301 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( | |
| 302 | + HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT | |
| 303 | + ; | |
| 304 | + @ claT | |
| 305 | + ; | |
| 306 | + @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ | |
| 307 | + azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); | |
| 308 | + azYear[n] = fossil_strdup(,0)); | |
| 309 | + azYear[n+1] = azYear[n]; | |
| 310 | + if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) ='lastchngTeble'nameeea"zTuserlist","tT} | |
| 311 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 312 | + style_footer(); | |
| 313 | +} | |
| 314 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 315 | + ; | |
| 316 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 317 | + style_footer(); | |
| 318 | +} | |
| 319 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 320 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 321 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 322 | + @ svg class="pie-chart" | |
| 323 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 324 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 325 | + , "Sunday" | |
| 326 | + }; | |
| 327 | + | |
| 328 | + ;mtime %% 7mtimand not empty | |
| 329 | +** class="pie-chart"slist","tT} | |
| 330 | + style_feeaclass='lastchngTable'nameeea"zTuserlist","tT} | |
| 331 | + style_footer(); | |
| 332 | +} | |
| 333 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 334 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 335 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 336 | + @ svg class="pie-chart" | |
| 337 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports");Blob sql-chart" | |
| 338 | + @ HQuery /* SQo_url(&url);url_rese_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 339 | + , "Sunday" | |
| 340 | + }; | |
| 341 | + | |
| 342 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( | |
| 343 | + HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT | |
| 344 | + ; | |
| 345 | + @ claT | |
| 346 | + ; | |
| 347 | + @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ | |
| 348 | + azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); | |
| 349 | + azYear[n] = fossil_strdup(,0)); | |
| 350 | + azYear[n+1] = azYear[n]; | |
| 351 | + if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) ='lastchngTeble'nameeea"zTuserlist","tT} | |
| 352 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 353 | + style_footer(); | |
| 354 | +} | |
| 355 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 356 | + ; | |
| 357 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 358 | + unday" | |
| 359 | + } NULL ); | |
| 360 | +header, "s) by yearL for various branchmtime %% 7mtime %% 7MoTuesWedme=TABLE piechart(amt,labelpie); | |
| 361 | + blob_append_sql(&sqlr5Sunday eea"zT | |
| 362 | + ; | |
| 363 | + @e><htimeframe,a"/center><svg/centeea"zincludeMonth if( PB("pie") ){ cgi_query_paramete | |
| 364 | + " ySizeArraySizeArraySizeeea"zT | |
| 365 | + ; | |
| 366 | + @ claT | |
| 367 | + ; | |
| 368 | + @"zT | |
| 369 | + ; | |
| 370 | + @ claT | |
| 371 | + ; | |
| 372 | + @ 'lastchngTeble'name claT | |
| 373 | + ; | |
| 374 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 375 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 376 | + style_footer();Usereea"zT | |
| 377 | + ; | |
| 378 | + @ claT | |
| 379 | + ; | |
| 380 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 381 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 382 | + style_footer(); | |
| 383 | +} | |
| 384 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 385 | + ; | |
| 386 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 387 | + style_footer(); | |
| 388 | +} | |
| 389 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 390 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 391 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 392 | + @ svg class="pie-chart" | |
| 393 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 394 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 395 | + , "Sunday" | |
| 396 | + }; | |
| 397 | + | |
| 398 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( | |
| 399 | + HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT | |
| 400 | + ; | |
| 401 | + @ claT | |
| 402 | + ; | |
| 403 | + @ ce><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 404 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 405 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 406 | + @ svg class="pie-chart" | |
| 407 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 408 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 409 | + , "Sunday" | |
| 410 | + }; | |
| 411 | + | |
| 412 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 413 | + ; | |
| 414 | + @ claT | |
| 415 | + e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 416 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 417 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 418 | + @ svg class="pie-chart" | |
| 419 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 420 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 421 | + , "Sunday" | |
| 422 | + }; | |
| 423 | + | |
| 424 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 425 | + ; | |
| 426 | + @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 427 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 428 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 429 | + @ svg class="pie-chart" | |
| 430 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 431 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 432 | + , "Sunday" | |
| 433 | + }; | |
| 434 | + | |
| 435 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT | |
| 436 | + ; | |
| 437 | + element(zMenuName, zMenuName, "%s url_render(pUrl, zParBy File", ny TypeelpiechartTABLE peea"zT | |
| 438 | + ; | |
| 439 | + @ claT | |
| 440 | + ; | |
| 441 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 442 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 443 | + style_footer(); | |
| 444 | +} | |
| 445 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 446 | + ; | |
| 447 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 448 | + style_footer(); | |
| 449 | +} | |
| 450 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 451 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 452 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 453 | + @ svg class="pie-chart" | |
| 454 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 455 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 456 | + , "Sunday" | |
| 457 | + }; | |
| 458 | + | |
| 459 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( | |
| 460 | + HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT | |
| 461 | + ; | |
| 462 | + @ claT | |
| 463 | + ; | |
| 464 | + @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ | |
| 465 | + azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); | |
| 466 | + azYear[n] = fossil_strdup(,0)); | |
| 467 | + azYear[n+1] = azYear[n]; | |
| 468 | + if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) zYear = azYear[0];zT | |
| 469 | + ; | |
| 470 | + @ claT | |
| 471 | + ; | |
| 472 | + @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 473 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 474 | + style_footer(); | |
| 475 | +} | |
| 476 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 477 | + ; | |
| 478 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 479 | + style_footer(); | |
| 480 | +} | |
| 481 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 482 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 483 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 484 | + @ svg class="pie-chart" | |
| 485 | + @ HQuery url;/* RL for various branch linksurl_initialize(&url, "reports"); | |
| 486 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 487 | + , "Sunday" | |
| 488 | + }; | |
| 489 | + | |
| 490 | + ;mtime %% 7mt/* | |
| 491 | +** A helper for the /reports family of pages which prints out a menu | |
| 492 | +** of links for the various type=XXX flags. zCurrentViewName must be | |
| 493 | +** the name/value of the 'view' parameter which is in effect at the | |
| 494 | +** time this is called. e.g. if called from the 'byuser' view then | |
| 495 | +** zCurrentViewName musa<br>?name=Teea"zT | |
| 496 | + ;g.zTop,; | |
| 497 | + @ claT | |
| 498 | + ; | |
| 499 | + @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ | |
| 500 | + azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); | |
| 501 | + azYear[n] = fossil_strdup(,0)); | |
| 502 | + azYear[n+1] = azYear[n]; | |
| 503 | + if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) ='lastchngTeble'nameeea"zTuserlist","tT} | |
| 504 | + style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} | |
| 505 | + style_footer(); | |
| 506 | +} | |
| 507 | +svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT | |
| 508 | + ; | |
| 509 | + @ class='lastchngTable'nameeea"zTuserlist","tT} | |
| 510 | + style_footer(); | |
| 511 | +} | |
| 512 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 513 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 514 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 515 | + @ svg class="pie-chart" | |
| 516 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); | |
| 517 | + cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 518 | + , "Sunday" | |
| 519 | + }; | |
| 520 | + | |
| 521 | + ;mtime %% 7mtimand not empty | |
| 522 | +** class="pie-chart"slist","tT} | |
| 523 | + style_feeaclass='lastchngTable'nameeea"zTuserlist","tT} | |
| 524 | + style_footer(); | |
| 525 | +} | |
| 526 | +center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" | |
| 527 | + "UPDATE piechart SEWHEN 5 THEN 'Friday'" | |
| 528 | + "ELSE 'ERROR' END;"svg class="pie-chart" | |
| 529 | + @ svg class="pie-chart" | |
| 530 | + @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports");Blob sql-chart" | |
| 531 | + @ HQuery /* SQo_url(&url);url_rese_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", | |
| 532 | + , "Sunday" | |
| 533 | + }; | |
| 534 | + | |
| 535 | + ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Ge |
| --- a/src/statrep.c | |
| +++ b/src/statrep.c | |
| @@ -0,0 +1,535 @@ | |
| --- a/src/statrep.c | |
| +++ b/src/statrep.c | |
| @@ -0,0 +1,535 @@ | |
| 1 | eea"zT |
| 2 | ; |
| 3 | @ claT |
| 4 | ; |
| 5 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 6 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 7 | style_footer(); |
| 8 | } |
| 9 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 10 | ; |
| 11 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 12 | style_footer(); |
| 13 | } |
| 14 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 15 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 16 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 17 | @ svg class="pie-chart" |
| 18 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 19 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 20 | , "Sunday" |
| 21 | }; |
| 22 | |
| 23 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( |
| 24 | HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT |
| 25 | ; |
| 26 | @ claT |
| 27 | ; |
| 28 | @ ce><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 29 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 30 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 31 | @ svg class="pie-chart" |
| 32 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 33 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 34 | , "Sunday" |
| 35 | }; |
| 36 | |
| 37 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 38 | ; |
| 39 | @ claT |
| 40 | e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 41 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 42 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 43 | @ svg class="pie-chart" |
| 44 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 45 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 46 | , "Sunday" |
| 47 | }; |
| 48 | |
| 49 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 50 | ; |
| 51 | @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 52 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 53 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 54 | @ svg class="pie-chart" |
| 55 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 56 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 57 | , "Sunday" |
| 58 | }; |
| 59 | |
| 60 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 61 | ; |
| 62 | element(zMenuName, zMenuName, "%s url_render(pUrl, zParBy File", ny TypeelpiechartTABLE peea"zT |
| 63 | ; |
| 64 | @ claT |
| 65 | ; |
| 66 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 67 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 68 | style_footer(); |
| 69 | } |
| 70 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 71 | ; |
| 72 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 73 | style_footer(); |
| 74 | } |
| 75 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 76 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 77 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 78 | @ svg class="pie-chart" |
| 79 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 80 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 81 | , "Sunday" |
| 82 | }; |
| 83 | |
| 84 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( |
| 85 | HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT |
| 86 | ; |
| 87 | @ claT |
| 88 | ; |
| 89 | @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ |
| 90 | azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); |
| 91 | azYear[n] = fossil_strdup(,0)); |
| 92 | azYear[n+1] = azYear[n]; |
| 93 | if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) zYear = azYear[0];zT |
| 94 | ; |
| 95 | @ claT |
| 96 | ; |
| 97 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 98 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 99 | style_footer(); |
| 100 | } |
| 101 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 102 | ; |
| 103 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 104 | style_footer(); |
| 105 | } |
| 106 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 107 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 108 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 109 | @ svg class="pie-chart" |
| 110 | @ HQuery url;/* RL for eventbranch linksurl_initialize(&url, "reports"); |
| 111 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 112 | , "Sunday" |
| 113 | }; |
| 114 | |
| 115 | ;mtime %% 7mt/* |
| 116 | ** A helper for the /reports family of pages which prints out a menu |
| 117 | ** of links for the various type=XXX flags. zCurrentViewName must be |
| 118 | ** the name/value of the 'view' parameter which is in effect at the |
| 119 | ** time this is called. e.g. if called from the 'byuser' view then |
| 120 | ** zCurrentViewName must be "byuser". Any URL parameters which need to |
| 121 | ** be added to the generated URLs should be passed in zParam. The |
| 122 | ** caller is expected to have already encoded any zParam in event_types_menu(c><svg</centre>center><svg</centre> "Thursday", |
| 123 | , "Sunday" |
| 124 | }; |
| 125 | |
| 126 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 127 | ; |
| 128 | @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 129 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 130 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 131 | @ svg class="pie-chart"uery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 132 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>csnter><svg</centre> "Thursday", |
| 133 | , "Sunday" |
| 134 | }; |
| 135 | |
| 136 | ;mtime %% 7mtime %% 7Mg.zTop,MoTuesWednThur5Sunday eea"zT |
| 137 | ; |
| 138 | element(zMenuName, zMenuNameinsass="pie-chart" |
| 139 | @ HQuery url;/*ci'>checkin"2;" |
| 140 | "UPDATE piechareea"zT |
| 141 | ; |
| 142 | @ claT |
| 143 | ; |
| 144 | even class='lastchngTable'nameeea"zTuserlist","tT} |
| 145 | style_footer();e'>event; @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 146 | style_footer(); |
| 147 | } |
| 148 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 149 | ; |
| 150 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 151 | style_footer(); |
| 152 | } |
| 153 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 154 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 155 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 156 | @ svg class="pie-chart" |
| 157 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 158 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 159 | , "Sunday" |
| 160 | }; |
| 161 | |
| 162 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( |
| 163 | HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT |
| 164 | ; |
| 165 | @ claT |
| 166 | ; |
| 167 | @ ce><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 168 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 169 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 170 | @ svg class="pie-chart" |
| 171 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 172 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 173 | , "Sunday" |
| 174 | }; |
| 175 | |
| 176 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 177 | ; |
| 178 | @ claT |
| 179 | e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 180 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 181 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 182 | @ svg class="pie-chart" |
| 183 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 184 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 185 | , "Sunday" |
| 186 | }; |
| 187 | |
| 188 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 189 | ; |
| 190 | @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 191 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 192 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 193 | @ svg class="pie-chart" |
| 194 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 195 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 196 | , "Sunday" |
| 197 | }; |
| 198 | |
| 199 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 200 | ; |
| 201 | element(zMenuName, zMenuName, "%s url_render(pUrl, zParBy File", ny TypeelpiechartTABLE peea"zT |
| 202 | ; |
| 203 | @ claT |
| 204 | ; |
| 205 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 206 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 207 | style_footer(); |
| 208 | } |
| 209 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 210 | ; |
| 211 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 212 | style_footer(); |
| 213 | } |
| 214 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 215 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 216 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 217 | @ svg class="pie-chart" |
| 218 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 219 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 220 | , "Sunday" |
| 221 | }; |
| 222 | |
| 223 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( |
| 224 | HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT |
| 225 | ; |
| 226 | @ claT |
| 227 | ; |
| 228 | @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ |
| 229 | azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); |
| 230 | azYear[n] = fossil_strdup(,0)); |
| 231 | azYear[n+1] = azYear[n]; |
| 232 | if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) zYear = azYear[0];zT |
| 233 | ; |
| 234 | @ claT |
| 235 | ; |
| 236 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 237 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 238 | style_footer(); |
| 239 | } |
| 240 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 241 | ; |
| 242 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 243 | style_footer(); |
| 244 | } |
| 245 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 246 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 247 | "ELSE 'ERRsR' END;"svg class="pie-chart" |
| 248 | @ svg class="pie-chart" |
| 249 | @ Hg.zTop,HQuery url;/* RL for various branch linksurl_initialize(&url, "reports"); |
| 250 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 251 | , "Sunday" |
| 252 | }; |
| 253 | |
| 254 | ;mtime %% 7mt/* |
| 255 | ** A helper for the /reports family of pages which prints out a menu |
| 256 | ** of links for the various type=XXX flags. zCurrentViewName must be |
| 257 | ** the name/value of the 'view' parameter which is in effect at the |
| 258 | ** time this is called. e.g. if called from the 'byuser' view then |
| 259 | ** zCurrentViewName must be "byuser". Any URL parameters which need to |
| 260 | ** be added to the generated URLs should be passed in zParam. The |
| 261 | ** caller is expected to have already encoded any zParam in event_types_menu(c><svg</centre>center><svg</centre> "Thursday", |
| 262 | , "Sunday" |
| 263 | }; |
| 264 | |
| 265 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 266 | ; |
| 267 | @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 268 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 269 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 270 | @ svg class="pie-chart"uery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 271 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 272 | , "Sunday" |
| 273 | }; |
| 274 | |
| 275 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 276 | ; |
| 277 | element(zMenuName, zMenuNameinsass="pie-chart" |
| 278 | @ HQuery url;/*ci'>checkin"2;" |
| 279 | "UPDATE piechareea"zT |
| 280 | ; |
| 281 | @ claT |
| 282 | ; |
| 283 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 284 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 285 | style_footer(); |
| 286 | } |
| 287 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 288 | ; |
| 289 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 290 | style_footer(); |
| 291 | } |
| 292 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 293 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 294 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 295 | @ svg class="pie-chart" |
| 296 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 297 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 298 | , "Sunday" |
| 299 | }; |
| 300 | |
| 301 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( |
| 302 | HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT |
| 303 | ; |
| 304 | @ claT |
| 305 | ; |
| 306 | @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ |
| 307 | azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); |
| 308 | azYear[n] = fossil_strdup(,0)); |
| 309 | azYear[n+1] = azYear[n]; |
| 310 | if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) ='lastchngTeble'nameeea"zTuserlist","tT} |
| 311 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 312 | style_footer(); |
| 313 | } |
| 314 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 315 | ; |
| 316 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 317 | style_footer(); |
| 318 | } |
| 319 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 320 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 321 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 322 | @ svg class="pie-chart" |
| 323 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 324 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 325 | , "Sunday" |
| 326 | }; |
| 327 | |
| 328 | ;mtime %% 7mtimand not empty |
| 329 | ** class="pie-chart"slist","tT} |
| 330 | style_feeaclass='lastchngTable'nameeea"zTuserlist","tT} |
| 331 | style_footer(); |
| 332 | } |
| 333 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 334 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 335 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 336 | @ svg class="pie-chart" |
| 337 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports");Blob sql-chart" |
| 338 | @ HQuery /* SQo_url(&url);url_rese_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 339 | , "Sunday" |
| 340 | }; |
| 341 | |
| 342 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( |
| 343 | HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT |
| 344 | ; |
| 345 | @ claT |
| 346 | ; |
| 347 | @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ |
| 348 | azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); |
| 349 | azYear[n] = fossil_strdup(,0)); |
| 350 | azYear[n+1] = azYear[n]; |
| 351 | if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) ='lastchngTeble'nameeea"zTuserlist","tT} |
| 352 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 353 | style_footer(); |
| 354 | } |
| 355 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 356 | ; |
| 357 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 358 | unday" |
| 359 | } NULL ); |
| 360 | header, "s) by yearL for various branchmtime %% 7mtime %% 7MoTuesWedme=TABLE piechart(amt,labelpie); |
| 361 | blob_append_sql(&sqlr5Sunday eea"zT |
| 362 | ; |
| 363 | @e><htimeframe,a"/center><svg/centeea"zincludeMonth if( PB("pie") ){ cgi_query_paramete |
| 364 | " ySizeArraySizeArraySizeeea"zT |
| 365 | ; |
| 366 | @ claT |
| 367 | ; |
| 368 | @"zT |
| 369 | ; |
| 370 | @ claT |
| 371 | ; |
| 372 | @ 'lastchngTeble'name claT |
| 373 | ; |
| 374 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 375 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 376 | style_footer();Usereea"zT |
| 377 | ; |
| 378 | @ claT |
| 379 | ; |
| 380 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 381 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 382 | style_footer(); |
| 383 | } |
| 384 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 385 | ; |
| 386 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 387 | style_footer(); |
| 388 | } |
| 389 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 390 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 391 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 392 | @ svg class="pie-chart" |
| 393 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 394 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 395 | , "Sunday" |
| 396 | }; |
| 397 | |
| 398 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( |
| 399 | HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT |
| 400 | ; |
| 401 | @ claT |
| 402 | ; |
| 403 | @ ce><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 404 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 405 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 406 | @ svg class="pie-chart" |
| 407 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 408 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 409 | , "Sunday" |
| 410 | }; |
| 411 | |
| 412 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 413 | ; |
| 414 | @ claT |
| 415 | e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 416 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 417 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 418 | @ svg class="pie-chart" |
| 419 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 420 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 421 | , "Sunday" |
| 422 | }; |
| 423 | |
| 424 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 425 | ; |
| 426 | @e><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 427 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 428 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 429 | @ svg class="pie-chart" |
| 430 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 431 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 432 | , "Sunday" |
| 433 | }; |
| 434 | |
| 435 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday eea"zT |
| 436 | ; |
| 437 | element(zMenuName, zMenuName, "%s url_render(pUrl, zParBy File", ny TypeelpiechartTABLE peea"zT |
| 438 | ; |
| 439 | @ claT |
| 440 | ; |
| 441 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 442 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 443 | style_footer(); |
| 444 | } |
| 445 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 446 | ; |
| 447 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 448 | style_footer(); |
| 449 | } |
| 450 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 451 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 452 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 453 | @ svg class="pie-chart" |
| 454 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 455 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 456 | , "Sunday" |
| 457 | }; |
| 458 | |
| 459 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Generate a submenu element witrep_submenu( |
| 460 | HQuery *pUrl,/* Base URLe><hreea<br>?name=Teea"zT |
| 461 | ; |
| 462 | @ claT |
| 463 | ; |
| 464 | @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ |
| 465 | azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); |
| 466 | azYear[n] = fossil_strdup(,0)); |
| 467 | azYear[n+1] = azYear[n]; |
| 468 | if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) zYear = azYear[0];zT |
| 469 | ; |
| 470 | @ claT |
| 471 | ; |
| 472 | @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 473 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 474 | style_footer(); |
| 475 | } |
| 476 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 477 | ; |
| 478 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 479 | style_footer(); |
| 480 | } |
| 481 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 482 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 483 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 484 | @ svg class="pie-chart" |
| 485 | @ HQuery url;/* RL for various branch linksurl_initialize(&url, "reports"); |
| 486 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 487 | , "Sunday" |
| 488 | }; |
| 489 | |
| 490 | ;mtime %% 7mt/* |
| 491 | ** A helper for the /reports family of pages which prints out a menu |
| 492 | ** of links for the various type=XXX flags. zCurrentViewName must be |
| 493 | ** the name/value of the 'view' parameter which is in effect at the |
| 494 | ** time this is called. e.g. if called from the 'byuser' view then |
| 495 | ** zCurrentViewName musa<br>?name=Teea"zT |
| 496 | ;g.zTop,; |
| 497 | @ claT |
| 498 | ; |
| 499 | @ ce>4) AS yy ORDER BY y DESC", zUserName) ){ |
| 500 | azYear = fossil_realloc(azYear, sizeof(char*)*(n+2)); |
| 501 | azYear[n] = fossil_strdup(,0)); |
| 502 | azYear[n+1] = azYear[n]; |
| 503 | if( !isValidYear && fossil_strcmp(zYear,azYear[n])==0 ) ='lastchngTeble'nameeea"zTuserlist","tT} |
| 504 | style_footer(); @ class='lastchngTeble'nameeea"zTuserlist","tT} |
| 505 | style_footer(); |
| 506 | } |
| 507 | svg class="pie-chart"svg class="pie-chart"ArraySizeArraySizeArraySizeeea"zT |
| 508 | ; |
| 509 | @ class='lastchngTable'nameeea"zTuserlist","tT} |
| 510 | style_footer(); |
| 511 | } |
| 512 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 513 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 514 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 515 | @ svg class="pie-chart" |
| 516 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports"); |
| 517 | cgi_query_parameters_to_url(&url);url_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 518 | , "Sunday" |
| 519 | }; |
| 520 | |
| 521 | ;mtime %% 7mtimand not empty |
| 522 | ** class="pie-chart"slist","tT} |
| 523 | style_feeaclass='lastchngTable'nameeea"zTuserlist","tT} |
| 524 | style_footer(); |
| 525 | } |
| 526 | center><svg/centre><hreea"/center><svg/centre><hreea<br>?name=TABLE piechart(amt,labelpiechartTABLE piechart(amt,labelpiechart)"2;" |
| 527 | "UPDATE piechart SEWHEN 5 THEN 'Friday'" |
| 528 | "ELSE 'ERROR' END;"svg class="pie-chart" |
| 529 | @ svg class="pie-chart" |
| 530 | @ HQuery url;/* URL for various branch linksurl_initialize(&url, "reports");Blob sql-chart" |
| 531 | @ HQuery /* SQo_url(&url);url_rese_reset(&urlcenter><svg</centre>center><svg</centre> "Thursday", |
| 532 | , "Sunday" |
| 533 | }; |
| 534 | |
| 535 | ;mtime %% 7mtime %% 7MoTuesWednThur5Sunday Ge |
+92
-240
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -46,10 +46,15 @@ | ||
| 46 | 46 | /* |
| 47 | 47 | ** remember, if a sidebox was used |
| 48 | 48 | */ |
| 49 | 49 | static int sideboxUsed = 0; |
| 50 | 50 | |
| 51 | +/* | |
| 52 | +** Ad-unit styles. | |
| 53 | +*/ | |
| 54 | +static unsigned adUnitFlags = 0; | |
| 55 | + | |
| 51 | 56 | |
| 52 | 57 | /* |
| 53 | 58 | ** List of hyperlinks and forms that need to be resolved by javascript in |
| 54 | 59 | ** the footer. |
| 55 | 60 | */ |
| @@ -284,11 +289,12 @@ | ||
| 284 | 289 | ** Draw the header. |
| 285 | 290 | */ |
| 286 | 291 | void style_header(const char *zTitleFormat, ...){ |
| 287 | 292 | va_list ap; |
| 288 | 293 | char *zTitle; |
| 289 | - const char *zHeader = db_get("header", (char*)zDefaultHeader); | |
| 294 | + const char *zHeader = db_get("header", 0); | |
| 295 | + if( zHeader==0 ) zHeader = builtin_text("skins/default/header.txt"); | |
| 290 | 296 | login_check_credentials(); |
| 291 | 297 | |
| 292 | 298 | va_start(ap, zTitleFormat); |
| 293 | 299 | zTitle = vmprintf(zTitleFormat, ap); |
| 294 | 300 | va_end(ap); |
| @@ -343,34 +349,64 @@ | ||
| 343 | 349 | @ var e = document.getElementById(x); |
| 344 | 350 | @ if(!e) throw new Error("Expecting element with ID "+x); |
| 345 | 351 | @ else return e;} |
| 346 | 352 | @ </script> |
| 347 | 353 | } |
| 354 | + | |
| 355 | +#if INTERFACE | |
| 356 | +/* Allowed parameters for style_adunit() */ | |
| 357 | +#define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ | |
| 358 | +#define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */ | |
| 359 | +#endif | |
| 360 | + | |
| 361 | +/* | |
| 362 | +** Various page implementations can invoke this interface to let the | |
| 363 | +** style manager know what kinds of ads are appropriate for this page. | |
| 364 | +*/ | |
| 365 | +void style_adunit_config(unsigned int mFlags){ | |
| 366 | + adUnitFlags = mFlags; | |
| 367 | +} | |
| 348 | 368 | |
| 349 | 369 | /* |
| 350 | -** Append ad unit text if appropriate. | |
| 370 | +** Return the text of an ad-unit, if one should be rendered. Return | |
| 371 | +** NULL if no ad-unit is desired. | |
| 372 | +** | |
| 373 | +** The *pAdFlag value might be set to ADUNIT_RIGHT_OK if this is | |
| 374 | +** a right-hand vertical ad. | |
| 351 | 375 | */ |
| 352 | -static void style_ad_unit(void){ | |
| 353 | - const char *zAd; | |
| 376 | +static const char *style_adunit_text(unsigned int *pAdFlag){ | |
| 377 | + const char *zAd = 0; | |
| 378 | + *pAdFlag = 0; | |
| 379 | + if( adUnitFlags & ADUNIT_OFF ) return 0; /* Disallow ads on this page */ | |
| 354 | 380 | if( g.perm.Admin && db_get_boolean("adunit-omit-if-admin",0) ){ |
| 355 | - return; | |
| 381 | + return 0; | |
| 356 | 382 | } |
| 357 | 383 | if( !login_is_nobody() |
| 358 | 384 | && fossil_strcmp(g.zLogin,"anonymous")!=0 |
| 359 | 385 | && db_get_boolean("adunit-omit-if-user",0) |
| 360 | 386 | ){ |
| 361 | - return; | |
| 387 | + return 0; | |
| 388 | + } | |
| 389 | + if( (adUnitFlags & ADUNIT_RIGHT_OK)!=0 | |
| 390 | + && !fossil_all_whitespace(zAd = db_get("adunit-right", 0)) | |
| 391 | + && !cgi_body_contains("<table") | |
| 392 | + ){ | |
| 393 | + *pAdFlag = ADUNIT_RIGHT_OK; | |
| 394 | + return zAd; | |
| 395 | + }else if( !fossil_all_whitespace(zAd = db_get("adunit",0)) ){ | |
| 396 | + return zAd; | |
| 362 | 397 | } |
| 363 | - zAd = db_get("adunit", 0); | |
| 364 | - if( zAd ) cgi_append_content(zAd, -1); | |
| 398 | + return 0; | |
| 365 | 399 | } |
| 366 | 400 | |
| 367 | 401 | /* |
| 368 | 402 | ** Draw the footer at the bottom of the page. |
| 369 | 403 | */ |
| 370 | 404 | void style_footer(void){ |
| 371 | 405 | const char *zFooter; |
| 406 | + const char *zAd = 0; | |
| 407 | + unsigned int mAdFlags = 0; | |
| 372 | 408 | |
| 373 | 409 | if( !headerHasBeenGenerated ) return; |
| 374 | 410 | |
| 375 | 411 | /* Go back and put the submenu at the top of the page. We delay the |
| 376 | 412 | ** creation of the submenu until the end so that we can add elements |
| @@ -389,12 +425,25 @@ | ||
| 389 | 425 | @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a> |
| 390 | 426 | } |
| 391 | 427 | } |
| 392 | 428 | @ </div> |
| 393 | 429 | } |
| 394 | - style_ad_unit(); | |
| 395 | - @ <div class="content"> | |
| 430 | + | |
| 431 | + zAd = style_adunit_text(&mAdFlags); | |
| 432 | + if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){ | |
| 433 | + @ <div class="content adunit_right_container"> | |
| 434 | + @ <div class="adunit_right"> | |
| 435 | + cgi_append_content(zAd, -1); | |
| 436 | + @ </div> | |
| 437 | + }else{ | |
| 438 | + if( zAd ){ | |
| 439 | + @ <div class="adunit_banner"> | |
| 440 | + cgi_append_content(zAd, -1); | |
| 441 | + @ </div> | |
| 442 | + } | |
| 443 | + @ <div class="content"> | |
| 444 | + } | |
| 396 | 445 | cgi_destination(CGI_BODY); |
| 397 | 446 | |
| 398 | 447 | if( sideboxUsed ){ |
| 399 | 448 | /* Put the footer at the bottom of the page. |
| 400 | 449 | ** the additional clear/both is needed to extend the content |
| @@ -406,11 +455,12 @@ | ||
| 406 | 455 | |
| 407 | 456 | /* Set the href= field on hyperlinks. Do this before the footer since |
| 408 | 457 | ** the footer will be generating </html> */ |
| 409 | 458 | style_resolve_href(); |
| 410 | 459 | |
| 411 | - zFooter = db_get("footer", (char*)zDefaultFooter); | |
| 460 | + zFooter = db_get("footer", 0); | |
| 461 | + if( zFooter==0 ) zFooter = builtin_text("skins/default/footer.txt"); | |
| 412 | 462 | if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1); |
| 413 | 463 | Th_Render(zFooter); |
| 414 | 464 | if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1); |
| 415 | 465 | |
| 416 | 466 | /* Render trace log if TH1 tracing is enabled. */ |
| @@ -436,230 +486,10 @@ | ||
| 436 | 486 | */ |
| 437 | 487 | void style_sidebox_end(void){ |
| 438 | 488 | @ </div> |
| 439 | 489 | } |
| 440 | 490 | |
| 441 | -/* @-comment: // */ | |
| 442 | -/* | |
| 443 | -** The default page header. | |
| 444 | -*/ | |
| 445 | -const char zDefaultHeader[] = | |
| 446 | -@ <html> | |
| 447 | -@ <head> | |
| 448 | -@ <base href="$baseurl/$current_page" /> | |
| 449 | -@ <title>$<project_name>: $<title></title> | |
| 450 | -@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" | |
| 451 | -@ href="$home/timeline.rss" /> | |
| 452 | -@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" | |
| 453 | -@ media="screen" /> | |
| 454 | -@ </head> | |
| 455 | -@ <body> | |
| 456 | -@ <div class="header"> | |
| 457 | -@ <div class="logo"> | |
| 458 | -@ <img src="$logo_image_url" alt="logo" /> | |
| 459 | -@ </div> | |
| 460 | -@ <div class="title"><small>$<project_name></small><br />$<title></div> | |
| 461 | -@ <div class="status"><th1> | |
| 462 | -@ if {[info exists login]} { | |
| 463 | -@ puts "Logged in as $login" | |
| 464 | -@ } else { | |
| 465 | -@ puts "Not logged in" | |
| 466 | -@ } | |
| 467 | -@ </th1></div> | |
| 468 | -@ </div> | |
| 469 | -@ <div class="mainmenu"> | |
| 470 | -@ <th1> | |
| 471 | -@ html "<a href='$home$index_page'>Home</a>\n" | |
| 472 | -@ if {[anycap jor]} { | |
| 473 | -@ html "<a href='$home/timeline'>Timeline</a>\n" | |
| 474 | -@ } | |
| 475 | -@ if {[hascap oh]} { | |
| 476 | -@ html "<a href='$home/tree?ci=tip'>Files</a>\n" | |
| 477 | -@ } | |
| 478 | -@ if {[hascap o]} { | |
| 479 | -@ html "<a href='$home/brlist'>Branches</a>\n" | |
| 480 | -@ html "<a href='$home/taglist'>Tags</a>\n" | |
| 481 | -@ } | |
| 482 | -@ if {[hascap r]} { | |
| 483 | -@ html "<a href='$home/reportlist'>Tickets</a>\n" | |
| 484 | -@ } | |
| 485 | -@ if {[hascap j]} { | |
| 486 | -@ html "<a href='$home/wiki'>Wiki</a>\n" | |
| 487 | -@ } | |
| 488 | -@ if {[hascap s]} { | |
| 489 | -@ html "<a href='$home/setup'>Admin</a>\n" | |
| 490 | -@ } elseif {[hascap a]} { | |
| 491 | -@ html "<a href='$home/setup_ulist'>Users</a>\n" | |
| 492 | -@ } | |
| 493 | -@ if {[info exists login]} { | |
| 494 | -@ html "<a href='$home/login'>Logout</a>\n" | |
| 495 | -@ } else { | |
| 496 | -@ html "<a href='$home/login'>Login</a>\n" | |
| 497 | -@ } | |
| 498 | -@ </th1></div> | |
| 499 | -; | |
| 500 | - | |
| 501 | -/* | |
| 502 | -** The default page footer | |
| 503 | -*/ | |
| 504 | -const char zDefaultFooter[] = | |
| 505 | -@ <div class="footer"> | |
| 506 | -@ This page was generated in about | |
| 507 | -@ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by | |
| 508 | -@ Fossil version $manifest_version $manifest_date | |
| 509 | -@ </div> | |
| 510 | -@ </body></html> | |
| 511 | -; | |
| 512 | - | |
| 513 | -/* | |
| 514 | -** The default Cascading Style Sheet. | |
| 515 | -** It's assembled by different strings for each class. | |
| 516 | -** The default css contains all definitions. | |
| 517 | -** The style sheet, send to the client only contains the ones, | |
| 518 | -** not defined in the user defined css. | |
| 519 | -*/ | |
| 520 | -const char zDefaultCSS[] = | |
| 521 | -@ /* General settings for the entire page */ | |
| 522 | -@ body { | |
| 523 | -@ margin: 0ex 1ex; | |
| 524 | -@ padding: 0px; | |
| 525 | -@ background-color: white; | |
| 526 | -@ font-family: sans-serif; | |
| 527 | -@ } | |
| 528 | -@ | |
| 529 | -@ /* The project logo in the upper left-hand corner of each page */ | |
| 530 | -@ div.logo { | |
| 531 | -@ display: table-cell; | |
| 532 | -@ text-align: center; | |
| 533 | -@ vertical-align: bottom; | |
| 534 | -@ font-weight: bold; | |
| 535 | -@ color: #558195; | |
| 536 | -@ min-width: 200px; | |
| 537 | -@ white-space: nowrap; | |
| 538 | -@ } | |
| 539 | -@ | |
| 540 | -@ /* The page title centered at the top of each page */ | |
| 541 | -@ div.title { | |
| 542 | -@ display: table-cell; | |
| 543 | -@ font-size: 2em; | |
| 544 | -@ font-weight: bold; | |
| 545 | -@ text-align: center; | |
| 546 | -@ padding: 0 0 0 1em; | |
| 547 | -@ color: #558195; | |
| 548 | -@ vertical-align: bottom; | |
| 549 | -@ width: 100%; | |
| 550 | -@ } | |
| 551 | -@ | |
| 552 | -@ /* The login status message in the top right-hand corner */ | |
| 553 | -@ div.status { | |
| 554 | -@ display: table-cell; | |
| 555 | -@ text-align: right; | |
| 556 | -@ vertical-align: bottom; | |
| 557 | -@ color: #558195; | |
| 558 | -@ font-size: 0.8em; | |
| 559 | -@ font-weight: bold; | |
| 560 | -@ min-width: 200px; | |
| 561 | -@ white-space: nowrap; | |
| 562 | -@ } | |
| 563 | -@ | |
| 564 | -@ /* The header across the top of the page */ | |
| 565 | -@ div.header { | |
| 566 | -@ display: table; | |
| 567 | -@ width: 100%; | |
| 568 | -@ } | |
| 569 | -@ | |
| 570 | -@ /* The main menu bar that appears at the top of the page beneath | |
| 571 | -@ ** the header */ | |
| 572 | -@ div.mainmenu { | |
| 573 | -@ padding: 5px 10px 5px 10px; | |
| 574 | -@ font-size: 0.9em; | |
| 575 | -@ font-weight: bold; | |
| 576 | -@ text-align: center; | |
| 577 | -@ letter-spacing: 1px; | |
| 578 | -@ background-color: #558195; | |
| 579 | -@ border-top-left-radius: 8px; | |
| 580 | -@ border-top-right-radius: 8px; | |
| 581 | -@ color: white; | |
| 582 | -@ } | |
| 583 | -@ | |
| 584 | -@ /* The submenu bar that *sometimes* appears below the main menu */ | |
| 585 | -@ div.submenu, div.sectionmenu { | |
| 586 | -@ padding: 3px 10px 3px 0px; | |
| 587 | -@ font-size: 0.9em; | |
| 588 | -@ text-align: center; | |
| 589 | -@ background-color: #456878; | |
| 590 | -@ color: white; | |
| 591 | -@ } | |
| 592 | -@ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, | |
| 593 | -@ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { | |
| 594 | -@ padding: 3px 10px 3px 10px; | |
| 595 | -@ color: white; | |
| 596 | -@ text-decoration: none; | |
| 597 | -@ } | |
| 598 | -@ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { | |
| 599 | -@ color: #558195; | |
| 600 | -@ background-color: white; | |
| 601 | -@ } | |
| 602 | -@ | |
| 603 | -@ /* All page content from the bottom of the menu or submenu down to | |
| 604 | -@ ** the footer */ | |
| 605 | -@ div.content { | |
| 606 | -@ padding: 0ex 1ex 1ex 1ex; | |
| 607 | -@ border: solid #aaa; | |
| 608 | -@ border-width: 1px; | |
| 609 | -@ } | |
| 610 | -@ | |
| 611 | -@ /* Some pages have section dividers */ | |
| 612 | -@ div.section { | |
| 613 | -@ margin-bottom: 0px; | |
| 614 | -@ margin-top: 1em; | |
| 615 | -@ padding: 1px 1px 1px 1px; | |
| 616 | -@ font-size: 1.2em; | |
| 617 | -@ font-weight: bold; | |
| 618 | -@ background-color: #558195; | |
| 619 | -@ color: white; | |
| 620 | -@ white-space: nowrap; | |
| 621 | -@ } | |
| 622 | -@ | |
| 623 | -@ /* The "Date" that occurs on the left hand side of timelines */ | |
| 624 | -@ div.divider { | |
| 625 | -@ background: #a1c4d4; | |
| 626 | -@ border: 2px #558195 solid; | |
| 627 | -@ font-size: 1em; font-weight: normal; | |
| 628 | -@ padding: .25em; | |
| 629 | -@ margin: .2em 0 .2em 0; | |
| 630 | -@ float: left; | |
| 631 | -@ clear: left; | |
| 632 | -@ white-space: nowrap; | |
| 633 | -@ } | |
| 634 | -@ | |
| 635 | -@ /* The footer at the very bottom of the page */ | |
| 636 | -@ div.footer { | |
| 637 | -@ clear: both; | |
| 638 | -@ font-size: 0.8em; | |
| 639 | -@ padding: 5px 10px 5px 10px; | |
| 640 | -@ text-align: right; | |
| 641 | -@ background-color: #558195; | |
| 642 | -@ border-bottom-left-radius: 8px; | |
| 643 | -@ border-bottom-right-radius: 8px; | |
| 644 | -@ color: white; | |
| 645 | -@ } | |
| 646 | -@ | |
| 647 | -@ /* Hyperlink colors in the footer */ | |
| 648 | -@ div.footer a { color: white; } | |
| 649 | -@ div.footer a:link { color: white; } | |
| 650 | -@ div.footer a:visited { color: white; } | |
| 651 | -@ div.footer a:hover { background-color: white; color: #558195; } | |
| 652 | -@ | |
| 653 | -@ /* verbatim blocks */ | |
| 654 | -@ pre.verbatim { | |
| 655 | -@ background-color: #f5f5f5; | |
| 656 | -@ padding: 0.5em; | |
| 657 | -@ white-space: pre-wrap; | |
| 658 | -@} | |
| 659 | -; | |
| 660 | - | |
| 661 | 491 | |
| 662 | 492 | /* The following table contains bits of default CSS that must |
| 663 | 493 | ** be included if they are not found in the application-defined |
| 664 | 494 | ** CSS. |
| 665 | 495 | */ |
| @@ -666,14 +496,10 @@ | ||
| 666 | 496 | const struct strctCssDefaults { |
| 667 | 497 | const char *elementClass; /* Name of element needed */ |
| 668 | 498 | const char *comment; /* Comment text */ |
| 669 | 499 | const char *value; /* CSS text */ |
| 670 | 500 | } cssDefaultList[] = { |
| 671 | - { "", | |
| 672 | - "", | |
| 673 | - zDefaultCSS | |
| 674 | - }, | |
| 675 | 501 | { "div.sidebox", |
| 676 | 502 | "The nomenclature sidebox for branches,..", |
| 677 | 503 | @ float: right; |
| 678 | 504 | @ background-color: white; |
| 679 | 505 | @ border-width: medium; |
| @@ -1270,10 +1096,39 @@ | ||
| 1270 | 1096 | "fileage third column (the check-in comment)", |
| 1271 | 1097 | @ word-break: break-all; |
| 1272 | 1098 | @ word-wrap: break-word; |
| 1273 | 1099 | @ max-width: 50%; |
| 1274 | 1100 | }, |
| 1101 | + { ".brlist table", "The list of branches", | |
| 1102 | + @ border-spacing: 0; | |
| 1103 | + }, | |
| 1104 | + { ".brlist table th", "Branch list table headers", | |
| 1105 | + @ text-align: left; | |
| 1106 | + @ padding: 0px 1em 0.5ex 0px; | |
| 1107 | + }, | |
| 1108 | + { ".brlist table td", "Branch list table headers", | |
| 1109 | + @ padding: 0px 2em 0px 0px; | |
| 1110 | + @ white-space: nowrap; | |
| 1111 | + }, | |
| 1112 | + { "th.sort:after", | |
| 1113 | + "General styles for sortable column marker", | |
| 1114 | + @ margin-left: .4em; | |
| 1115 | + @ cursor: pointer; | |
| 1116 | + @ text-shadow: 0 0 0 #000; /* Makes arrow darker */ | |
| 1117 | + }, | |
| 1118 | + { "th.sort.none:after", | |
| 1119 | + "None sort column marker", | |
| 1120 | + @ content: '\2666'; | |
| 1121 | + }, | |
| 1122 | + { "th.sort.asc:after", | |
| 1123 | + "Ascending sort column marker", | |
| 1124 | + @ content: '\2193'; | |
| 1125 | + }, | |
| 1126 | + { "th.sort.desc:after", | |
| 1127 | + "Descending sort column marker", | |
| 1128 | + @ content: '\2191'; | |
| 1129 | + }, | |
| 1275 | 1130 | { 0, |
| 1276 | 1131 | 0, |
| 1277 | 1132 | 0 |
| 1278 | 1133 | } |
| 1279 | 1134 | }; |
| @@ -1282,19 +1137,16 @@ | ||
| 1282 | 1137 | ** Append all of the default CSS to the CGI output. |
| 1283 | 1138 | */ |
| 1284 | 1139 | void cgi_append_default_css(void) { |
| 1285 | 1140 | int i; |
| 1286 | 1141 | |
| 1142 | + cgi_printf("%s", builtin_text("skins/default/css.txt")); | |
| 1287 | 1143 | for( i=0; cssDefaultList[i].elementClass; i++ ){ |
| 1288 | 1144 | if( cssDefaultList[i].elementClass[0] ){ |
| 1289 | 1145 | cgi_printf("/* %s */\n%s {\n%s\n}\n\n", |
| 1290 | 1146 | cssDefaultList[i].comment, |
| 1291 | 1147 | cssDefaultList[i].elementClass, |
| 1292 | - cssDefaultList[i].value | |
| 1293 | - ); | |
| 1294 | - }else{ | |
| 1295 | - cgi_printf("%s", | |
| 1296 | 1148 | cssDefaultList[i].value |
| 1297 | 1149 | ); |
| 1298 | 1150 | } |
| 1299 | 1151 | } |
| 1300 | 1152 | } |
| @@ -1305,11 +1157,11 @@ | ||
| 1305 | 1157 | void page_style_css(void){ |
| 1306 | 1158 | Blob css; |
| 1307 | 1159 | int i; |
| 1308 | 1160 | |
| 1309 | 1161 | cgi_set_content_type("text/css"); |
| 1310 | - blob_init(&css, db_get("css",(char*)zDefaultCSS), -1); | |
| 1162 | + blob_init(&css, db_get("css",(char*)builtin_text("skins/default/css.txt")), -1); | |
| 1311 | 1163 | |
| 1312 | 1164 | /* add special missing definitions */ |
| 1313 | 1165 | for(i=1; cssDefaultList[i].elementClass; i++){ |
| 1314 | 1166 | if( strstr(blob_str(&css), cssDefaultList[i].elementClass)==0 ){ |
| 1315 | 1167 | blob_appendf(&css, "/* %s */\n%s {\n%s}\n", |
| 1316 | 1168 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -46,10 +46,15 @@ | |
| 46 | /* |
| 47 | ** remember, if a sidebox was used |
| 48 | */ |
| 49 | static int sideboxUsed = 0; |
| 50 | |
| 51 | |
| 52 | /* |
| 53 | ** List of hyperlinks and forms that need to be resolved by javascript in |
| 54 | ** the footer. |
| 55 | */ |
| @@ -284,11 +289,12 @@ | |
| 284 | ** Draw the header. |
| 285 | */ |
| 286 | void style_header(const char *zTitleFormat, ...){ |
| 287 | va_list ap; |
| 288 | char *zTitle; |
| 289 | const char *zHeader = db_get("header", (char*)zDefaultHeader); |
| 290 | login_check_credentials(); |
| 291 | |
| 292 | va_start(ap, zTitleFormat); |
| 293 | zTitle = vmprintf(zTitleFormat, ap); |
| 294 | va_end(ap); |
| @@ -343,34 +349,64 @@ | |
| 343 | @ var e = document.getElementById(x); |
| 344 | @ if(!e) throw new Error("Expecting element with ID "+x); |
| 345 | @ else return e;} |
| 346 | @ </script> |
| 347 | } |
| 348 | |
| 349 | /* |
| 350 | ** Append ad unit text if appropriate. |
| 351 | */ |
| 352 | static void style_ad_unit(void){ |
| 353 | const char *zAd; |
| 354 | if( g.perm.Admin && db_get_boolean("adunit-omit-if-admin",0) ){ |
| 355 | return; |
| 356 | } |
| 357 | if( !login_is_nobody() |
| 358 | && fossil_strcmp(g.zLogin,"anonymous")!=0 |
| 359 | && db_get_boolean("adunit-omit-if-user",0) |
| 360 | ){ |
| 361 | return; |
| 362 | } |
| 363 | zAd = db_get("adunit", 0); |
| 364 | if( zAd ) cgi_append_content(zAd, -1); |
| 365 | } |
| 366 | |
| 367 | /* |
| 368 | ** Draw the footer at the bottom of the page. |
| 369 | */ |
| 370 | void style_footer(void){ |
| 371 | const char *zFooter; |
| 372 | |
| 373 | if( !headerHasBeenGenerated ) return; |
| 374 | |
| 375 | /* Go back and put the submenu at the top of the page. We delay the |
| 376 | ** creation of the submenu until the end so that we can add elements |
| @@ -389,12 +425,25 @@ | |
| 389 | @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a> |
| 390 | } |
| 391 | } |
| 392 | @ </div> |
| 393 | } |
| 394 | style_ad_unit(); |
| 395 | @ <div class="content"> |
| 396 | cgi_destination(CGI_BODY); |
| 397 | |
| 398 | if( sideboxUsed ){ |
| 399 | /* Put the footer at the bottom of the page. |
| 400 | ** the additional clear/both is needed to extend the content |
| @@ -406,11 +455,12 @@ | |
| 406 | |
| 407 | /* Set the href= field on hyperlinks. Do this before the footer since |
| 408 | ** the footer will be generating </html> */ |
| 409 | style_resolve_href(); |
| 410 | |
| 411 | zFooter = db_get("footer", (char*)zDefaultFooter); |
| 412 | if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1); |
| 413 | Th_Render(zFooter); |
| 414 | if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1); |
| 415 | |
| 416 | /* Render trace log if TH1 tracing is enabled. */ |
| @@ -436,230 +486,10 @@ | |
| 436 | */ |
| 437 | void style_sidebox_end(void){ |
| 438 | @ </div> |
| 439 | } |
| 440 | |
| 441 | /* @-comment: // */ |
| 442 | /* |
| 443 | ** The default page header. |
| 444 | */ |
| 445 | const char zDefaultHeader[] = |
| 446 | @ <html> |
| 447 | @ <head> |
| 448 | @ <base href="$baseurl/$current_page" /> |
| 449 | @ <title>$<project_name>: $<title></title> |
| 450 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" |
| 451 | @ href="$home/timeline.rss" /> |
| 452 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" |
| 453 | @ media="screen" /> |
| 454 | @ </head> |
| 455 | @ <body> |
| 456 | @ <div class="header"> |
| 457 | @ <div class="logo"> |
| 458 | @ <img src="$logo_image_url" alt="logo" /> |
| 459 | @ </div> |
| 460 | @ <div class="title"><small>$<project_name></small><br />$<title></div> |
| 461 | @ <div class="status"><th1> |
| 462 | @ if {[info exists login]} { |
| 463 | @ puts "Logged in as $login" |
| 464 | @ } else { |
| 465 | @ puts "Not logged in" |
| 466 | @ } |
| 467 | @ </th1></div> |
| 468 | @ </div> |
| 469 | @ <div class="mainmenu"> |
| 470 | @ <th1> |
| 471 | @ html "<a href='$home$index_page'>Home</a>\n" |
| 472 | @ if {[anycap jor]} { |
| 473 | @ html "<a href='$home/timeline'>Timeline</a>\n" |
| 474 | @ } |
| 475 | @ if {[hascap oh]} { |
| 476 | @ html "<a href='$home/tree?ci=tip'>Files</a>\n" |
| 477 | @ } |
| 478 | @ if {[hascap o]} { |
| 479 | @ html "<a href='$home/brlist'>Branches</a>\n" |
| 480 | @ html "<a href='$home/taglist'>Tags</a>\n" |
| 481 | @ } |
| 482 | @ if {[hascap r]} { |
| 483 | @ html "<a href='$home/reportlist'>Tickets</a>\n" |
| 484 | @ } |
| 485 | @ if {[hascap j]} { |
| 486 | @ html "<a href='$home/wiki'>Wiki</a>\n" |
| 487 | @ } |
| 488 | @ if {[hascap s]} { |
| 489 | @ html "<a href='$home/setup'>Admin</a>\n" |
| 490 | @ } elseif {[hascap a]} { |
| 491 | @ html "<a href='$home/setup_ulist'>Users</a>\n" |
| 492 | @ } |
| 493 | @ if {[info exists login]} { |
| 494 | @ html "<a href='$home/login'>Logout</a>\n" |
| 495 | @ } else { |
| 496 | @ html "<a href='$home/login'>Login</a>\n" |
| 497 | @ } |
| 498 | @ </th1></div> |
| 499 | ; |
| 500 | |
| 501 | /* |
| 502 | ** The default page footer |
| 503 | */ |
| 504 | const char zDefaultFooter[] = |
| 505 | @ <div class="footer"> |
| 506 | @ This page was generated in about |
| 507 | @ <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]</th1>s by |
| 508 | @ Fossil version $manifest_version $manifest_date |
| 509 | @ </div> |
| 510 | @ </body></html> |
| 511 | ; |
| 512 | |
| 513 | /* |
| 514 | ** The default Cascading Style Sheet. |
| 515 | ** It's assembled by different strings for each class. |
| 516 | ** The default css contains all definitions. |
| 517 | ** The style sheet, send to the client only contains the ones, |
| 518 | ** not defined in the user defined css. |
| 519 | */ |
| 520 | const char zDefaultCSS[] = |
| 521 | @ /* General settings for the entire page */ |
| 522 | @ body { |
| 523 | @ margin: 0ex 1ex; |
| 524 | @ padding: 0px; |
| 525 | @ background-color: white; |
| 526 | @ font-family: sans-serif; |
| 527 | @ } |
| 528 | @ |
| 529 | @ /* The project logo in the upper left-hand corner of each page */ |
| 530 | @ div.logo { |
| 531 | @ display: table-cell; |
| 532 | @ text-align: center; |
| 533 | @ vertical-align: bottom; |
| 534 | @ font-weight: bold; |
| 535 | @ color: #558195; |
| 536 | @ min-width: 200px; |
| 537 | @ white-space: nowrap; |
| 538 | @ } |
| 539 | @ |
| 540 | @ /* The page title centered at the top of each page */ |
| 541 | @ div.title { |
| 542 | @ display: table-cell; |
| 543 | @ font-size: 2em; |
| 544 | @ font-weight: bold; |
| 545 | @ text-align: center; |
| 546 | @ padding: 0 0 0 1em; |
| 547 | @ color: #558195; |
| 548 | @ vertical-align: bottom; |
| 549 | @ width: 100%; |
| 550 | @ } |
| 551 | @ |
| 552 | @ /* The login status message in the top right-hand corner */ |
| 553 | @ div.status { |
| 554 | @ display: table-cell; |
| 555 | @ text-align: right; |
| 556 | @ vertical-align: bottom; |
| 557 | @ color: #558195; |
| 558 | @ font-size: 0.8em; |
| 559 | @ font-weight: bold; |
| 560 | @ min-width: 200px; |
| 561 | @ white-space: nowrap; |
| 562 | @ } |
| 563 | @ |
| 564 | @ /* The header across the top of the page */ |
| 565 | @ div.header { |
| 566 | @ display: table; |
| 567 | @ width: 100%; |
| 568 | @ } |
| 569 | @ |
| 570 | @ /* The main menu bar that appears at the top of the page beneath |
| 571 | @ ** the header */ |
| 572 | @ div.mainmenu { |
| 573 | @ padding: 5px 10px 5px 10px; |
| 574 | @ font-size: 0.9em; |
| 575 | @ font-weight: bold; |
| 576 | @ text-align: center; |
| 577 | @ letter-spacing: 1px; |
| 578 | @ background-color: #558195; |
| 579 | @ border-top-left-radius: 8px; |
| 580 | @ border-top-right-radius: 8px; |
| 581 | @ color: white; |
| 582 | @ } |
| 583 | @ |
| 584 | @ /* The submenu bar that *sometimes* appears below the main menu */ |
| 585 | @ div.submenu, div.sectionmenu { |
| 586 | @ padding: 3px 10px 3px 0px; |
| 587 | @ font-size: 0.9em; |
| 588 | @ text-align: center; |
| 589 | @ background-color: #456878; |
| 590 | @ color: white; |
| 591 | @ } |
| 592 | @ div.mainmenu a, div.mainmenu a:visited, div.submenu a, div.submenu a:visited, |
| 593 | @ div.sectionmenu>a.button:link, div.sectionmenu>a.button:visited { |
| 594 | @ padding: 3px 10px 3px 10px; |
| 595 | @ color: white; |
| 596 | @ text-decoration: none; |
| 597 | @ } |
| 598 | @ div.mainmenu a:hover, div.submenu a:hover, div.sectionmenu>a.button:hover { |
| 599 | @ color: #558195; |
| 600 | @ background-color: white; |
| 601 | @ } |
| 602 | @ |
| 603 | @ /* All page content from the bottom of the menu or submenu down to |
| 604 | @ ** the footer */ |
| 605 | @ div.content { |
| 606 | @ padding: 0ex 1ex 1ex 1ex; |
| 607 | @ border: solid #aaa; |
| 608 | @ border-width: 1px; |
| 609 | @ } |
| 610 | @ |
| 611 | @ /* Some pages have section dividers */ |
| 612 | @ div.section { |
| 613 | @ margin-bottom: 0px; |
| 614 | @ margin-top: 1em; |
| 615 | @ padding: 1px 1px 1px 1px; |
| 616 | @ font-size: 1.2em; |
| 617 | @ font-weight: bold; |
| 618 | @ background-color: #558195; |
| 619 | @ color: white; |
| 620 | @ white-space: nowrap; |
| 621 | @ } |
| 622 | @ |
| 623 | @ /* The "Date" that occurs on the left hand side of timelines */ |
| 624 | @ div.divider { |
| 625 | @ background: #a1c4d4; |
| 626 | @ border: 2px #558195 solid; |
| 627 | @ font-size: 1em; font-weight: normal; |
| 628 | @ padding: .25em; |
| 629 | @ margin: .2em 0 .2em 0; |
| 630 | @ float: left; |
| 631 | @ clear: left; |
| 632 | @ white-space: nowrap; |
| 633 | @ } |
| 634 | @ |
| 635 | @ /* The footer at the very bottom of the page */ |
| 636 | @ div.footer { |
| 637 | @ clear: both; |
| 638 | @ font-size: 0.8em; |
| 639 | @ padding: 5px 10px 5px 10px; |
| 640 | @ text-align: right; |
| 641 | @ background-color: #558195; |
| 642 | @ border-bottom-left-radius: 8px; |
| 643 | @ border-bottom-right-radius: 8px; |
| 644 | @ color: white; |
| 645 | @ } |
| 646 | @ |
| 647 | @ /* Hyperlink colors in the footer */ |
| 648 | @ div.footer a { color: white; } |
| 649 | @ div.footer a:link { color: white; } |
| 650 | @ div.footer a:visited { color: white; } |
| 651 | @ div.footer a:hover { background-color: white; color: #558195; } |
| 652 | @ |
| 653 | @ /* verbatim blocks */ |
| 654 | @ pre.verbatim { |
| 655 | @ background-color: #f5f5f5; |
| 656 | @ padding: 0.5em; |
| 657 | @ white-space: pre-wrap; |
| 658 | @} |
| 659 | ; |
| 660 | |
| 661 | |
| 662 | /* The following table contains bits of default CSS that must |
| 663 | ** be included if they are not found in the application-defined |
| 664 | ** CSS. |
| 665 | */ |
| @@ -666,14 +496,10 @@ | |
| 666 | const struct strctCssDefaults { |
| 667 | const char *elementClass; /* Name of element needed */ |
| 668 | const char *comment; /* Comment text */ |
| 669 | const char *value; /* CSS text */ |
| 670 | } cssDefaultList[] = { |
| 671 | { "", |
| 672 | "", |
| 673 | zDefaultCSS |
| 674 | }, |
| 675 | { "div.sidebox", |
| 676 | "The nomenclature sidebox for branches,..", |
| 677 | @ float: right; |
| 678 | @ background-color: white; |
| 679 | @ border-width: medium; |
| @@ -1270,10 +1096,39 @@ | |
| 1270 | "fileage third column (the check-in comment)", |
| 1271 | @ word-break: break-all; |
| 1272 | @ word-wrap: break-word; |
| 1273 | @ max-width: 50%; |
| 1274 | }, |
| 1275 | { 0, |
| 1276 | 0, |
| 1277 | 0 |
| 1278 | } |
| 1279 | }; |
| @@ -1282,19 +1137,16 @@ | |
| 1282 | ** Append all of the default CSS to the CGI output. |
| 1283 | */ |
| 1284 | void cgi_append_default_css(void) { |
| 1285 | int i; |
| 1286 | |
| 1287 | for( i=0; cssDefaultList[i].elementClass; i++ ){ |
| 1288 | if( cssDefaultList[i].elementClass[0] ){ |
| 1289 | cgi_printf("/* %s */\n%s {\n%s\n}\n\n", |
| 1290 | cssDefaultList[i].comment, |
| 1291 | cssDefaultList[i].elementClass, |
| 1292 | cssDefaultList[i].value |
| 1293 | ); |
| 1294 | }else{ |
| 1295 | cgi_printf("%s", |
| 1296 | cssDefaultList[i].value |
| 1297 | ); |
| 1298 | } |
| 1299 | } |
| 1300 | } |
| @@ -1305,11 +1157,11 @@ | |
| 1305 | void page_style_css(void){ |
| 1306 | Blob css; |
| 1307 | int i; |
| 1308 | |
| 1309 | cgi_set_content_type("text/css"); |
| 1310 | blob_init(&css, db_get("css",(char*)zDefaultCSS), -1); |
| 1311 | |
| 1312 | /* add special missing definitions */ |
| 1313 | for(i=1; cssDefaultList[i].elementClass; i++){ |
| 1314 | if( strstr(blob_str(&css), cssDefaultList[i].elementClass)==0 ){ |
| 1315 | blob_appendf(&css, "/* %s */\n%s {\n%s}\n", |
| 1316 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -46,10 +46,15 @@ | |
| 46 | /* |
| 47 | ** remember, if a sidebox was used |
| 48 | */ |
| 49 | static int sideboxUsed = 0; |
| 50 | |
| 51 | /* |
| 52 | ** Ad-unit styles. |
| 53 | */ |
| 54 | static unsigned adUnitFlags = 0; |
| 55 | |
| 56 | |
| 57 | /* |
| 58 | ** List of hyperlinks and forms that need to be resolved by javascript in |
| 59 | ** the footer. |
| 60 | */ |
| @@ -284,11 +289,12 @@ | |
| 289 | ** Draw the header. |
| 290 | */ |
| 291 | void style_header(const char *zTitleFormat, ...){ |
| 292 | va_list ap; |
| 293 | char *zTitle; |
| 294 | const char *zHeader = db_get("header", 0); |
| 295 | if( zHeader==0 ) zHeader = builtin_text("skins/default/header.txt"); |
| 296 | login_check_credentials(); |
| 297 | |
| 298 | va_start(ap, zTitleFormat); |
| 299 | zTitle = vmprintf(zTitleFormat, ap); |
| 300 | va_end(ap); |
| @@ -343,34 +349,64 @@ | |
| 349 | @ var e = document.getElementById(x); |
| 350 | @ if(!e) throw new Error("Expecting element with ID "+x); |
| 351 | @ else return e;} |
| 352 | @ </script> |
| 353 | } |
| 354 | |
| 355 | #if INTERFACE |
| 356 | /* Allowed parameters for style_adunit() */ |
| 357 | #define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ |
| 358 | #define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */ |
| 359 | #endif |
| 360 | |
| 361 | /* |
| 362 | ** Various page implementations can invoke this interface to let the |
| 363 | ** style manager know what kinds of ads are appropriate for this page. |
| 364 | */ |
| 365 | void style_adunit_config(unsigned int mFlags){ |
| 366 | adUnitFlags = mFlags; |
| 367 | } |
| 368 | |
| 369 | /* |
| 370 | ** Return the text of an ad-unit, if one should be rendered. Return |
| 371 | ** NULL if no ad-unit is desired. |
| 372 | ** |
| 373 | ** The *pAdFlag value might be set to ADUNIT_RIGHT_OK if this is |
| 374 | ** a right-hand vertical ad. |
| 375 | */ |
| 376 | static const char *style_adunit_text(unsigned int *pAdFlag){ |
| 377 | const char *zAd = 0; |
| 378 | *pAdFlag = 0; |
| 379 | if( adUnitFlags & ADUNIT_OFF ) return 0; /* Disallow ads on this page */ |
| 380 | if( g.perm.Admin && db_get_boolean("adunit-omit-if-admin",0) ){ |
| 381 | return 0; |
| 382 | } |
| 383 | if( !login_is_nobody() |
| 384 | && fossil_strcmp(g.zLogin,"anonymous")!=0 |
| 385 | && db_get_boolean("adunit-omit-if-user",0) |
| 386 | ){ |
| 387 | return 0; |
| 388 | } |
| 389 | if( (adUnitFlags & ADUNIT_RIGHT_OK)!=0 |
| 390 | && !fossil_all_whitespace(zAd = db_get("adunit-right", 0)) |
| 391 | && !cgi_body_contains("<table") |
| 392 | ){ |
| 393 | *pAdFlag = ADUNIT_RIGHT_OK; |
| 394 | return zAd; |
| 395 | }else if( !fossil_all_whitespace(zAd = db_get("adunit",0)) ){ |
| 396 | return zAd; |
| 397 | } |
| 398 | return 0; |
| 399 | } |
| 400 | |
| 401 | /* |
| 402 | ** Draw the footer at the bottom of the page. |
| 403 | */ |
| 404 | void style_footer(void){ |
| 405 | const char *zFooter; |
| 406 | const char *zAd = 0; |
| 407 | unsigned int mAdFlags = 0; |
| 408 | |
| 409 | if( !headerHasBeenGenerated ) return; |
| 410 | |
| 411 | /* Go back and put the submenu at the top of the page. We delay the |
| 412 | ** creation of the submenu until the end so that we can add elements |
| @@ -389,12 +425,25 @@ | |
| 425 | @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a> |
| 426 | } |
| 427 | } |
| 428 | @ </div> |
| 429 | } |
| 430 | |
| 431 | zAd = style_adunit_text(&mAdFlags); |
| 432 | if( (mAdFlags & ADUNIT_RIGHT_OK)!=0 ){ |
| 433 | @ <div class="content adunit_right_container"> |
| 434 | @ <div class="adunit_right"> |
| 435 | cgi_append_content(zAd, -1); |
| 436 | @ </div> |
| 437 | }else{ |
| 438 | if( zAd ){ |
| 439 | @ <div class="adunit_banner"> |
| 440 | cgi_append_content(zAd, -1); |
| 441 | @ </div> |
| 442 | } |
| 443 | @ <div class="content"> |
| 444 | } |
| 445 | cgi_destination(CGI_BODY); |
| 446 | |
| 447 | if( sideboxUsed ){ |
| 448 | /* Put the footer at the bottom of the page. |
| 449 | ** the additional clear/both is needed to extend the content |
| @@ -406,11 +455,12 @@ | |
| 455 | |
| 456 | /* Set the href= field on hyperlinks. Do this before the footer since |
| 457 | ** the footer will be generating </html> */ |
| 458 | style_resolve_href(); |
| 459 | |
| 460 | zFooter = db_get("footer", 0); |
| 461 | if( zFooter==0 ) zFooter = builtin_text("skins/default/footer.txt"); |
| 462 | if( g.thTrace ) Th_Trace("BEGIN_FOOTER<br />\n", -1); |
| 463 | Th_Render(zFooter); |
| 464 | if( g.thTrace ) Th_Trace("END_FOOTER<br />\n", -1); |
| 465 | |
| 466 | /* Render trace log if TH1 tracing is enabled. */ |
| @@ -436,230 +486,10 @@ | |
| 486 | */ |
| 487 | void style_sidebox_end(void){ |
| 488 | @ </div> |
| 489 | } |
| 490 | |
| 491 | |
| 492 | /* The following table contains bits of default CSS that must |
| 493 | ** be included if they are not found in the application-defined |
| 494 | ** CSS. |
| 495 | */ |
| @@ -666,14 +496,10 @@ | |
| 496 | const struct strctCssDefaults { |
| 497 | const char *elementClass; /* Name of element needed */ |
| 498 | const char *comment; /* Comment text */ |
| 499 | const char *value; /* CSS text */ |
| 500 | } cssDefaultList[] = { |
| 501 | { "div.sidebox", |
| 502 | "The nomenclature sidebox for branches,..", |
| 503 | @ float: right; |
| 504 | @ background-color: white; |
| 505 | @ border-width: medium; |
| @@ -1270,10 +1096,39 @@ | |
| 1096 | "fileage third column (the check-in comment)", |
| 1097 | @ word-break: break-all; |
| 1098 | @ word-wrap: break-word; |
| 1099 | @ max-width: 50%; |
| 1100 | }, |
| 1101 | { ".brlist table", "The list of branches", |
| 1102 | @ border-spacing: 0; |
| 1103 | }, |
| 1104 | { ".brlist table th", "Branch list table headers", |
| 1105 | @ text-align: left; |
| 1106 | @ padding: 0px 1em 0.5ex 0px; |
| 1107 | }, |
| 1108 | { ".brlist table td", "Branch list table headers", |
| 1109 | @ padding: 0px 2em 0px 0px; |
| 1110 | @ white-space: nowrap; |
| 1111 | }, |
| 1112 | { "th.sort:after", |
| 1113 | "General styles for sortable column marker", |
| 1114 | @ margin-left: .4em; |
| 1115 | @ cursor: pointer; |
| 1116 | @ text-shadow: 0 0 0 #000; /* Makes arrow darker */ |
| 1117 | }, |
| 1118 | { "th.sort.none:after", |
| 1119 | "None sort column marker", |
| 1120 | @ content: '\2666'; |
| 1121 | }, |
| 1122 | { "th.sort.asc:after", |
| 1123 | "Ascending sort column marker", |
| 1124 | @ content: '\2193'; |
| 1125 | }, |
| 1126 | { "th.sort.desc:after", |
| 1127 | "Descending sort column marker", |
| 1128 | @ content: '\2191'; |
| 1129 | }, |
| 1130 | { 0, |
| 1131 | 0, |
| 1132 | 0 |
| 1133 | } |
| 1134 | }; |
| @@ -1282,19 +1137,16 @@ | |
| 1137 | ** Append all of the default CSS to the CGI output. |
| 1138 | */ |
| 1139 | void cgi_append_default_css(void) { |
| 1140 | int i; |
| 1141 | |
| 1142 | cgi_printf("%s", builtin_text("skins/default/css.txt")); |
| 1143 | for( i=0; cssDefaultList[i].elementClass; i++ ){ |
| 1144 | if( cssDefaultList[i].elementClass[0] ){ |
| 1145 | cgi_printf("/* %s */\n%s {\n%s\n}\n\n", |
| 1146 | cssDefaultList[i].comment, |
| 1147 | cssDefaultList[i].elementClass, |
| 1148 | cssDefaultList[i].value |
| 1149 | ); |
| 1150 | } |
| 1151 | } |
| 1152 | } |
| @@ -1305,11 +1157,11 @@ | |
| 1157 | void page_style_css(void){ |
| 1158 | Blob css; |
| 1159 | int i; |
| 1160 | |
| 1161 | cgi_set_content_type("text/css"); |
| 1162 | blob_init(&css, db_get("css",(char*)builtin_text("skins/default/css.txt")), -1); |
| 1163 | |
| 1164 | /* add special missing definitions */ |
| 1165 | for(i=1; cssDefaultList[i].elementClass; i++){ |
| 1166 | if( strstr(blob_str(&css), cssDefaultList[i].elementClass)==0 ){ |
| 1167 | blob_appendf(&css, "/* %s */\n%s {\n%s}\n", |
| 1168 |
+5
-5
| --- src/sync.c | ||
| +++ src/sync.c | ||
| @@ -48,11 +48,11 @@ | ||
| 48 | 48 | } |
| 49 | 49 | }else{ |
| 50 | 50 | /* Autosync defaults on. To make it default off, "return" here. */ |
| 51 | 51 | } |
| 52 | 52 | url_parse(0, URL_REMEMBER); |
| 53 | - if( g.url.protocol==0 ) return 0; | |
| 53 | + if( g.url.protocol==0 ) return 0; | |
| 54 | 54 | if( g.url.user!=0 && g.url.passwd==0 ){ |
| 55 | 55 | g.url.passwd = unobscure(db_get("last-sync-pw", 0)); |
| 56 | 56 | g.url.flags |= URL_PROMPT_PW; |
| 57 | 57 | url_prompt_for_password(); |
| 58 | 58 | } |
| @@ -61,11 +61,11 @@ | ||
| 61 | 61 | #if 0 /* Disabled for now */ |
| 62 | 62 | if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ |
| 63 | 63 | /* When doing an automatic pull, also automatically pull shuns from |
| 64 | 64 | ** the server if pull_shuns is enabled. |
| 65 | 65 | ** |
| 66 | - ** TODO: What happens if the shun list gets really big? | |
| 66 | + ** TODO: What happens if the shun list gets really big? | |
| 67 | 67 | ** Maybe the shunning list should only be pulled on every 10th |
| 68 | 68 | ** autosync, or something? |
| 69 | 69 | */ |
| 70 | 70 | configSync = CONFIGSET_SHUN; |
| 71 | 71 | } |
| @@ -186,11 +186,11 @@ | ||
| 186 | 186 | */ |
| 187 | 187 | void pull_cmd(void){ |
| 188 | 188 | unsigned configFlags = 0; |
| 189 | 189 | unsigned syncFlags = SYNC_PULL; |
| 190 | 190 | process_sync_args(&configFlags, &syncFlags); |
| 191 | - | |
| 191 | + | |
| 192 | 192 | /* We should be done with options.. */ |
| 193 | 193 | verify_all_options(); |
| 194 | 194 | |
| 195 | 195 | client_sync(syncFlags, configFlags, 0); |
| 196 | 196 | } |
| @@ -221,11 +221,11 @@ | ||
| 221 | 221 | */ |
| 222 | 222 | void push_cmd(void){ |
| 223 | 223 | unsigned configFlags = 0; |
| 224 | 224 | unsigned syncFlags = SYNC_PUSH; |
| 225 | 225 | process_sync_args(&configFlags, &syncFlags); |
| 226 | - | |
| 226 | + | |
| 227 | 227 | /* We should be done with options.. */ |
| 228 | 228 | verify_all_options(); |
| 229 | 229 | |
| 230 | 230 | if( db_get_boolean("dont-push",0) ){ |
| 231 | 231 | fossil_fatal("pushing is prohibited: the 'dont-push' option is set"); |
| @@ -261,11 +261,11 @@ | ||
| 261 | 261 | */ |
| 262 | 262 | void sync_cmd(void){ |
| 263 | 263 | unsigned configFlags = 0; |
| 264 | 264 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 265 | 265 | process_sync_args(&configFlags, &syncFlags); |
| 266 | - | |
| 266 | + | |
| 267 | 267 | /* We should be done with options.. */ |
| 268 | 268 | verify_all_options(); |
| 269 | 269 | |
| 270 | 270 | if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; |
| 271 | 271 | client_sync(syncFlags, configFlags, 0); |
| 272 | 272 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -48,11 +48,11 @@ | |
| 48 | } |
| 49 | }else{ |
| 50 | /* Autosync defaults on. To make it default off, "return" here. */ |
| 51 | } |
| 52 | url_parse(0, URL_REMEMBER); |
| 53 | if( g.url.protocol==0 ) return 0; |
| 54 | if( g.url.user!=0 && g.url.passwd==0 ){ |
| 55 | g.url.passwd = unobscure(db_get("last-sync-pw", 0)); |
| 56 | g.url.flags |= URL_PROMPT_PW; |
| 57 | url_prompt_for_password(); |
| 58 | } |
| @@ -61,11 +61,11 @@ | |
| 61 | #if 0 /* Disabled for now */ |
| 62 | if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ |
| 63 | /* When doing an automatic pull, also automatically pull shuns from |
| 64 | ** the server if pull_shuns is enabled. |
| 65 | ** |
| 66 | ** TODO: What happens if the shun list gets really big? |
| 67 | ** Maybe the shunning list should only be pulled on every 10th |
| 68 | ** autosync, or something? |
| 69 | */ |
| 70 | configSync = CONFIGSET_SHUN; |
| 71 | } |
| @@ -186,11 +186,11 @@ | |
| 186 | */ |
| 187 | void pull_cmd(void){ |
| 188 | unsigned configFlags = 0; |
| 189 | unsigned syncFlags = SYNC_PULL; |
| 190 | process_sync_args(&configFlags, &syncFlags); |
| 191 | |
| 192 | /* We should be done with options.. */ |
| 193 | verify_all_options(); |
| 194 | |
| 195 | client_sync(syncFlags, configFlags, 0); |
| 196 | } |
| @@ -221,11 +221,11 @@ | |
| 221 | */ |
| 222 | void push_cmd(void){ |
| 223 | unsigned configFlags = 0; |
| 224 | unsigned syncFlags = SYNC_PUSH; |
| 225 | process_sync_args(&configFlags, &syncFlags); |
| 226 | |
| 227 | /* We should be done with options.. */ |
| 228 | verify_all_options(); |
| 229 | |
| 230 | if( db_get_boolean("dont-push",0) ){ |
| 231 | fossil_fatal("pushing is prohibited: the 'dont-push' option is set"); |
| @@ -261,11 +261,11 @@ | |
| 261 | */ |
| 262 | void sync_cmd(void){ |
| 263 | unsigned configFlags = 0; |
| 264 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 265 | process_sync_args(&configFlags, &syncFlags); |
| 266 | |
| 267 | /* We should be done with options.. */ |
| 268 | verify_all_options(); |
| 269 | |
| 270 | if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; |
| 271 | client_sync(syncFlags, configFlags, 0); |
| 272 |
| --- src/sync.c | |
| +++ src/sync.c | |
| @@ -48,11 +48,11 @@ | |
| 48 | } |
| 49 | }else{ |
| 50 | /* Autosync defaults on. To make it default off, "return" here. */ |
| 51 | } |
| 52 | url_parse(0, URL_REMEMBER); |
| 53 | if( g.url.protocol==0 ) return 0; |
| 54 | if( g.url.user!=0 && g.url.passwd==0 ){ |
| 55 | g.url.passwd = unobscure(db_get("last-sync-pw", 0)); |
| 56 | g.url.flags |= URL_PROMPT_PW; |
| 57 | url_prompt_for_password(); |
| 58 | } |
| @@ -61,11 +61,11 @@ | |
| 61 | #if 0 /* Disabled for now */ |
| 62 | if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ |
| 63 | /* When doing an automatic pull, also automatically pull shuns from |
| 64 | ** the server if pull_shuns is enabled. |
| 65 | ** |
| 66 | ** TODO: What happens if the shun list gets really big? |
| 67 | ** Maybe the shunning list should only be pulled on every 10th |
| 68 | ** autosync, or something? |
| 69 | */ |
| 70 | configSync = CONFIGSET_SHUN; |
| 71 | } |
| @@ -186,11 +186,11 @@ | |
| 186 | */ |
| 187 | void pull_cmd(void){ |
| 188 | unsigned configFlags = 0; |
| 189 | unsigned syncFlags = SYNC_PULL; |
| 190 | process_sync_args(&configFlags, &syncFlags); |
| 191 | |
| 192 | /* We should be done with options.. */ |
| 193 | verify_all_options(); |
| 194 | |
| 195 | client_sync(syncFlags, configFlags, 0); |
| 196 | } |
| @@ -221,11 +221,11 @@ | |
| 221 | */ |
| 222 | void push_cmd(void){ |
| 223 | unsigned configFlags = 0; |
| 224 | unsigned syncFlags = SYNC_PUSH; |
| 225 | process_sync_args(&configFlags, &syncFlags); |
| 226 | |
| 227 | /* We should be done with options.. */ |
| 228 | verify_all_options(); |
| 229 | |
| 230 | if( db_get_boolean("dont-push",0) ){ |
| 231 | fossil_fatal("pushing is prohibited: the 'dont-push' option is set"); |
| @@ -261,11 +261,11 @@ | |
| 261 | */ |
| 262 | void sync_cmd(void){ |
| 263 | unsigned configFlags = 0; |
| 264 | unsigned syncFlags = SYNC_PUSH|SYNC_PULL; |
| 265 | process_sync_args(&configFlags, &syncFlags); |
| 266 | |
| 267 | /* We should be done with options.. */ |
| 268 | verify_all_options(); |
| 269 | |
| 270 | if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH; |
| 271 | client_sync(syncFlags, configFlags, 0); |
| 272 |
+3
-2
| --- src/tag.c | ||
| +++ src/tag.c | ||
| @@ -269,11 +269,11 @@ | ||
| 269 | 269 | } |
| 270 | 270 | g.markPrivate = content_is_private(rid); |
| 271 | 271 | zValue = g.argc==5 ? g.argv[4] : 0; |
| 272 | 272 | db_begin_transaction(); |
| 273 | 273 | tag_insert(zTag, tagtype, zValue, -1, 0.0, rid); |
| 274 | - db_end_transaction(0); | |
| 274 | + db_end_transaction(0); | |
| 275 | 275 | } |
| 276 | 276 | |
| 277 | 277 | /* |
| 278 | 278 | ** Add a control record to the repository that either creates |
| 279 | 279 | ** or cancels a tag. |
| @@ -535,11 +535,11 @@ | ||
| 535 | 535 | tag_cmd_usage: |
| 536 | 536 | usage("add|cancel|find|list ..."); |
| 537 | 537 | } |
| 538 | 538 | |
| 539 | 539 | /* |
| 540 | -** WEBPAGE: /taglist | |
| 540 | +** WEBPAGE: taglist | |
| 541 | 541 | */ |
| 542 | 542 | void taglist_page(void){ |
| 543 | 543 | Stmt q; |
| 544 | 544 | |
| 545 | 545 | login_check_credentials(); |
| @@ -546,10 +546,11 @@ | ||
| 546 | 546 | if( !g.perm.Read ){ |
| 547 | 547 | login_needed(); |
| 548 | 548 | } |
| 549 | 549 | login_anonymous_available(); |
| 550 | 550 | style_header("Tags"); |
| 551 | + style_adunit_config(ADUNIT_RIGHT_OK); | |
| 551 | 552 | style_submenu_element("Timeline", "Timeline", "tagtimeline"); |
| 552 | 553 | @ <h2>Non-propagating tags:</h2> |
| 553 | 554 | db_prepare(&q, |
| 554 | 555 | "SELECT substr(tagname,5)" |
| 555 | 556 | " FROM tag" |
| 556 | 557 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -269,11 +269,11 @@ | |
| 269 | } |
| 270 | g.markPrivate = content_is_private(rid); |
| 271 | zValue = g.argc==5 ? g.argv[4] : 0; |
| 272 | db_begin_transaction(); |
| 273 | tag_insert(zTag, tagtype, zValue, -1, 0.0, rid); |
| 274 | db_end_transaction(0); |
| 275 | } |
| 276 | |
| 277 | /* |
| 278 | ** Add a control record to the repository that either creates |
| 279 | ** or cancels a tag. |
| @@ -535,11 +535,11 @@ | |
| 535 | tag_cmd_usage: |
| 536 | usage("add|cancel|find|list ..."); |
| 537 | } |
| 538 | |
| 539 | /* |
| 540 | ** WEBPAGE: /taglist |
| 541 | */ |
| 542 | void taglist_page(void){ |
| 543 | Stmt q; |
| 544 | |
| 545 | login_check_credentials(); |
| @@ -546,10 +546,11 @@ | |
| 546 | if( !g.perm.Read ){ |
| 547 | login_needed(); |
| 548 | } |
| 549 | login_anonymous_available(); |
| 550 | style_header("Tags"); |
| 551 | style_submenu_element("Timeline", "Timeline", "tagtimeline"); |
| 552 | @ <h2>Non-propagating tags:</h2> |
| 553 | db_prepare(&q, |
| 554 | "SELECT substr(tagname,5)" |
| 555 | " FROM tag" |
| 556 |
| --- src/tag.c | |
| +++ src/tag.c | |
| @@ -269,11 +269,11 @@ | |
| 269 | } |
| 270 | g.markPrivate = content_is_private(rid); |
| 271 | zValue = g.argc==5 ? g.argv[4] : 0; |
| 272 | db_begin_transaction(); |
| 273 | tag_insert(zTag, tagtype, zValue, -1, 0.0, rid); |
| 274 | db_end_transaction(0); |
| 275 | } |
| 276 | |
| 277 | /* |
| 278 | ** Add a control record to the repository that either creates |
| 279 | ** or cancels a tag. |
| @@ -535,11 +535,11 @@ | |
| 535 | tag_cmd_usage: |
| 536 | usage("add|cancel|find|list ..."); |
| 537 | } |
| 538 | |
| 539 | /* |
| 540 | ** WEBPAGE: taglist |
| 541 | */ |
| 542 | void taglist_page(void){ |
| 543 | Stmt q; |
| 544 | |
| 545 | login_check_credentials(); |
| @@ -546,10 +546,11 @@ | |
| 546 | if( !g.perm.Read ){ |
| 547 | login_needed(); |
| 548 | } |
| 549 | login_anonymous_available(); |
| 550 | style_header("Tags"); |
| 551 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 552 | style_submenu_element("Timeline", "Timeline", "tagtimeline"); |
| 553 | @ <h2>Non-propagating tags:</h2> |
| 554 | db_prepare(&q, |
| 555 | "SELECT substr(tagname,5)" |
| 556 | " FROM tag" |
| 557 |
M
src/th.h
+1
-1
| --- src/th.h | ||
| +++ src/th.h | ||
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | |
| 2 | 2 | /* This header file defines the external interface to the custom Scripting |
| 3 | -** Language (TH) interpreter. TH is very similar to TCL but is not an | |
| 3 | +** Language (TH) interpreter. TH is very similar to Tcl but is not an | |
| 4 | 4 | ** exact clone. |
| 5 | 5 | */ |
| 6 | 6 | |
| 7 | 7 | /* |
| 8 | 8 | ** Before creating an interpreter, the application must allocate and |
| 9 | 9 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -1,8 +1,8 @@ | |
| 1 | |
| 2 | /* This header file defines the external interface to the custom Scripting |
| 3 | ** Language (TH) interpreter. TH is very similar to TCL but is not an |
| 4 | ** exact clone. |
| 5 | */ |
| 6 | |
| 7 | /* |
| 8 | ** Before creating an interpreter, the application must allocate and |
| 9 |
| --- src/th.h | |
| +++ src/th.h | |
| @@ -1,8 +1,8 @@ | |
| 1 | |
| 2 | /* This header file defines the external interface to the custom Scripting |
| 3 | ** Language (TH) interpreter. TH is very similar to Tcl but is not an |
| 4 | ** exact clone. |
| 5 | */ |
| 6 | |
| 7 | /* |
| 8 | ** Before creating an interpreter, the application must allocate and |
| 9 |
+2
-2
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -391,15 +391,15 @@ | ||
| 391 | 391 | int argc, |
| 392 | 392 | const char **argv, |
| 393 | 393 | int *argl |
| 394 | 394 | ){ |
| 395 | 395 | int rc = 0; |
| 396 | - char const * zArg; | |
| 396 | + const char *zArg; | |
| 397 | 397 | if( argc!=2 ){ |
| 398 | 398 | return Th_WrongNumArgs(interp, "hasfeature STRING"); |
| 399 | 399 | } |
| 400 | - zArg = (char const*)argv[1]; | |
| 400 | + zArg = (const char *)argv[1]; | |
| 401 | 401 | if(NULL==zArg){ |
| 402 | 402 | /* placeholder for following ifdefs... */ |
| 403 | 403 | } |
| 404 | 404 | #if defined(FOSSIL_ENABLE_SSL) |
| 405 | 405 | else if( 0 == fossil_strnicmp( zArg, "ssl\0", 4 ) ){ |
| 406 | 406 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -391,15 +391,15 @@ | |
| 391 | int argc, |
| 392 | const char **argv, |
| 393 | int *argl |
| 394 | ){ |
| 395 | int rc = 0; |
| 396 | char const * zArg; |
| 397 | if( argc!=2 ){ |
| 398 | return Th_WrongNumArgs(interp, "hasfeature STRING"); |
| 399 | } |
| 400 | zArg = (char const*)argv[1]; |
| 401 | if(NULL==zArg){ |
| 402 | /* placeholder for following ifdefs... */ |
| 403 | } |
| 404 | #if defined(FOSSIL_ENABLE_SSL) |
| 405 | else if( 0 == fossil_strnicmp( zArg, "ssl\0", 4 ) ){ |
| 406 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -391,15 +391,15 @@ | |
| 391 | int argc, |
| 392 | const char **argv, |
| 393 | int *argl |
| 394 | ){ |
| 395 | int rc = 0; |
| 396 | const char *zArg; |
| 397 | if( argc!=2 ){ |
| 398 | return Th_WrongNumArgs(interp, "hasfeature STRING"); |
| 399 | } |
| 400 | zArg = (const char *)argv[1]; |
| 401 | if(NULL==zArg){ |
| 402 | /* placeholder for following ifdefs... */ |
| 403 | } |
| 404 | #if defined(FOSSIL_ENABLE_SSL) |
| 405 | else if( 0 == fossil_strnicmp( zArg, "ssl\0", 4 ) ){ |
| 406 |
+13
-689
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -87,10 +87,11 @@ | ||
| 87 | 87 | #define TIMELINE_FCHANGES 0x0020 /* Detail file changes */ |
| 88 | 88 | #define TIMELINE_BRCOLOR 0x0040 /* Background color by branch name */ |
| 89 | 89 | #define TIMELINE_UCOLOR 0x0080 /* Background color by user */ |
| 90 | 90 | #define TIMELINE_FRENAMES 0x0100 /* Detail only file name changes */ |
| 91 | 91 | #define TIMELINE_UNHIDE 0x0200 /* Unhide check-ins with "hidden" tag */ |
| 92 | +#define TIMELINE_SHOWRID 0x0400 /* Show RID values in addition to UUIDs */ | |
| 92 | 93 | #endif |
| 93 | 94 | |
| 94 | 95 | /* |
| 95 | 96 | ** Hash a string and use the hash to determine a background color. |
| 96 | 97 | */ |
| @@ -359,20 +360,20 @@ | ||
| 359 | 360 | } |
| 360 | 361 | } |
| 361 | 362 | } |
| 362 | 363 | if( zType[0]=='c' && (pGraph || (tmFlags & TIMELINE_BRCOLOR)!=0) ){ |
| 363 | 364 | int nParent = 0; |
| 364 | - int aParent[32]; | |
| 365 | + int aParent[GR_MAX_RAIL]; | |
| 365 | 366 | int gidx; |
| 366 | 367 | static Stmt qparent; |
| 367 | 368 | db_static_prepare(&qparent, |
| 368 | 369 | "SELECT pid FROM plink" |
| 369 | 370 | " WHERE cid=:rid AND pid NOT IN phantom" |
| 370 | 371 | " ORDER BY isprim DESC /*sort*/" |
| 371 | 372 | ); |
| 372 | 373 | db_bind_int(&qparent, ":rid", rid); |
| 373 | - while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){ | |
| 374 | + while( db_step(&qparent)==SQLITE_ROW && nParent<ArraySize(aParent) ){ | |
| 374 | 375 | aParent[nParent++] = db_column_int(&qparent, 0); |
| 375 | 376 | } |
| 376 | 377 | db_reset(&qparent); |
| 377 | 378 | gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr, |
| 378 | 379 | zUuid, isLeaf); |
| @@ -404,10 +405,13 @@ | ||
| 404 | 405 | } |
| 405 | 406 | }else if( zType[0]=='e' && tagid ){ |
| 406 | 407 | hyperlink_to_event_tagid(tagid<0?-tagid:tagid); |
| 407 | 408 | }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ |
| 408 | 409 | hyperlink_to_uuid(zUuid); |
| 410 | + } | |
| 411 | + if( tmFlags & TIMELINE_SHOWRID ){ | |
| 412 | + @ (%d(rid)) | |
| 409 | 413 | } |
| 410 | 414 | db_column_blob(pQuery, commentColumn, &comment); |
| 411 | 415 | if( zType[0]!='c' ){ |
| 412 | 416 | /* Comments for anything other than a check-in are generated by |
| 413 | 417 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| @@ -604,12 +608,12 @@ | ||
| 604 | 608 | ** for the upward portion of a merge arrow. The merge arrow goes up |
| 605 | 609 | ** to the row identified by mu:. If this value is zero then |
| 606 | 610 | ** node has no merge children and no merge-out line is drawn. |
| 607 | 611 | ** mu: The id of the row which is the top of the merge-out arrow. |
| 608 | 612 | ** u: Draw a thick child-line out of the top of this node and up to |
| 609 | - ** the node with an id equal to this value. 0 if there is no | |
| 610 | - ** thick-line riser. | |
| 613 | + ** the node with an id equal to this value. 0 if it is straight to | |
| 614 | + ** the top of the page, -1 if there is no thick-line riser. | |
| 611 | 615 | ** f: 0x01: a leaf node. |
| 612 | 616 | ** au: An array of integers that define thick-line risers for branches. |
| 613 | 617 | ** The integers are in pairs. For each pair, the first integer is |
| 614 | 618 | ** is the rail on which the riser should run and the second integer |
| 615 | 619 | ** is the id of the node upto which the riser should run. |
| @@ -1031,11 +1035,10 @@ | ||
| 1031 | 1035 | ** nd Suppress "divider" lines |
| 1032 | 1036 | ** v Show details of files changed |
| 1033 | 1037 | ** f=UUID Show family (immediate parents and children) of UUID |
| 1034 | 1038 | ** from=UUID Path from... |
| 1035 | 1039 | ** to=UUID ... to this |
| 1036 | -** nomerge ... avoid merge links on the path | |
| 1037 | 1040 | ** shortest ... show only the shortest path |
| 1038 | 1041 | ** uf=FUUID Show only checkins that use given file version |
| 1039 | 1042 | ** brbg Background color from branch name |
| 1040 | 1043 | ** ubg Background color from user |
| 1041 | 1044 | ** namechng Show only checkins that filename changes |
| @@ -1070,11 +1073,11 @@ | ||
| 1070 | 1073 | const char *zYearMonth = P("ym"); /* Show checkins for the given YYYY-MM */ |
| 1071 | 1074 | const char *zYearWeek = P("yw"); /* Show checkins for the given YYYY-WW (week-of-year)*/ |
| 1072 | 1075 | int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ |
| 1073 | 1076 | int renameOnly = P("namechng")!=0; /* Show only checkins that rename files */ |
| 1074 | 1077 | int tagid; /* Tag ID */ |
| 1075 | - int tmFlags; /* Timeline flags */ | |
| 1078 | + int tmFlags = 0; /* Timeline flags */ | |
| 1076 | 1079 | const char *zThisTag = 0; /* Suppress links to this tag */ |
| 1077 | 1080 | const char *zThisUser = 0; /* Suppress links to this user */ |
| 1078 | 1081 | HQuery url; /* URL for various branch links */ |
| 1079 | 1082 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| 1080 | 1083 | int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */ |
| @@ -1110,13 +1113,13 @@ | ||
| 1110 | 1113 | ){ |
| 1111 | 1114 | zCirca = zBefore = zAfter = 0; |
| 1112 | 1115 | nEntry = -1; |
| 1113 | 1116 | } |
| 1114 | 1117 | if( zType[0]=='a' ){ |
| 1115 | - tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH; | |
| 1118 | + tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH; | |
| 1116 | 1119 | }else{ |
| 1117 | - tmFlags = TIMELINE_GRAPH; | |
| 1120 | + tmFlags |= TIMELINE_GRAPH; | |
| 1118 | 1121 | } |
| 1119 | 1122 | if( nEntry>0 ) url_add_parameter(&url, "n", mprintf("%d", nEntry)); |
| 1120 | 1123 | if( P("ng")!=0 || zSearch!=0 ){ |
| 1121 | 1124 | tmFlags &= ~TIMELINE_GRAPH; |
| 1122 | 1125 | url_add_parameter(&url, "ng", 0); |
| @@ -1206,10 +1209,11 @@ | ||
| 1206 | 1209 | }else if( (p_rid || d_rid) && g.perm.Read ){ |
| 1207 | 1210 | /* If p= or d= is present, ignore all other parameters other than n= */ |
| 1208 | 1211 | char *zUuid; |
| 1209 | 1212 | int np, nd; |
| 1210 | 1213 | |
| 1214 | + tmFlags |= TIMELINE_DISJOINT; | |
| 1211 | 1215 | if( p_rid && d_rid ){ |
| 1212 | 1216 | if( p_rid!=d_rid ) p_rid = d_rid; |
| 1213 | 1217 | if( P("n")==0 ) nEntry = 10; |
| 1214 | 1218 | } |
| 1215 | 1219 | db_multi_exec( |
| @@ -1552,10 +1556,11 @@ | ||
| 1552 | 1556 | } |
| 1553 | 1557 | } |
| 1554 | 1558 | if( P("showsql") ){ |
| 1555 | 1559 | @ <blockquote>%h(blob_sql_text(&sql))</blockquote> |
| 1556 | 1560 | } |
| 1561 | + if( P("showid") ) tmFlags |= TIMELINE_SHOWRID; | |
| 1557 | 1562 | blob_zero(&sql); |
| 1558 | 1563 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); |
| 1559 | 1564 | @ <h2>%b(&desc)</h2> |
| 1560 | 1565 | blob_reset(&desc); |
| 1561 | 1566 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, 0); |
| @@ -2039,688 +2044,7 @@ | ||
| 2039 | 2044 | const char *zUuid = db_column_text(&q, 0); |
| 2040 | 2045 | @ <li> |
| 2041 | 2046 | @ <a href="%s(g.zTop)/timeline?p=%s(zUuid)&d=%s(zUuid)&unhide">%S(zUuid)</a> |
| 2042 | 2047 | } |
| 2043 | 2048 | db_finalize(&q); |
| 2044 | - style_footer(); | |
| 2045 | -} | |
| 2046 | - | |
| 2047 | - | |
| 2048 | -/* | |
| 2049 | -** Used by stats_report_xxxxx() to remember which type of events | |
| 2050 | -** to show. Populated by stats_report_init_view() and holds the | |
| 2051 | -** return value of that function. | |
| 2052 | -*/ | |
| 2053 | -static int statsReportType = 0; | |
| 2054 | - | |
| 2055 | -/* | |
| 2056 | -** Set by stats_report_init_view() to one of the y=XXXX values | |
| 2057 | -** accepted by /timeline?y=XXXX. | |
| 2058 | -*/ | |
| 2059 | -static const char *statsReportTimelineYFlag = NULL; | |
| 2060 | - | |
| 2061 | -/* | |
| 2062 | -** Creates a TEMP VIEW named v_reports which is a wrapper around the | |
| 2063 | -** EVENT table filtered on event.type. It looks for the request | |
| 2064 | -** parameter 'type' (reminder: we "should" use 'y' for consistency | |
| 2065 | -** with /timeline, but /reports uses 'y' for the year) and expects it | |
| 2066 | -** to contain one of the conventional values from event.type or the | |
| 2067 | -** value "all", which is treated as equivalent to "*". By default (if | |
| 2068 | -** no 'y' is specified), "*" is assumed (that is also the default for | |
| 2069 | -** invalid/unknown filter values). That 'y' filter is the one used for | |
| 2070 | -** the event list. Note that a filter of "*" or "all" is equivalent to | |
| 2071 | -** querying against the full event table. The view, however, adds an | |
| 2072 | -** abstraction level to simplify the implementation code for the | |
| 2073 | -** various /reports pages. | |
| 2074 | -** | |
| 2075 | -** Returns one of: 'c', 'w', 'g', 't', 'e', representing the type of | |
| 2076 | -** filter it applies, or '*' if no filter is applied (i.e. if "all" is | |
| 2077 | -** used). | |
| 2078 | -*/ | |
| 2079 | -static int stats_report_init_view(){ | |
| 2080 | - const char *zType = PD("type","*"); /* analog to /timeline?y=... */ | |
| 2081 | - const char *zRealType = NULL; /* normalized form of zType */ | |
| 2082 | - int rc = 0; /* result code */ | |
| 2083 | - assert( !statsReportType && "Must not be called more than once." ); | |
| 2084 | - switch( (zType && *zType) ? *zType : 0 ){ | |
| 2085 | - case 'c': | |
| 2086 | - case 'C': | |
| 2087 | - zRealType = "ci"; | |
| 2088 | - rc = *zRealType; | |
| 2089 | - break; | |
| 2090 | - case 'e': | |
| 2091 | - case 'E': | |
| 2092 | - zRealType = "e"; | |
| 2093 | - rc = *zRealType; | |
| 2094 | - break; | |
| 2095 | - case 'g': | |
| 2096 | - case 'G': | |
| 2097 | - zRealType = "g"; | |
| 2098 | - rc = *zRealType; | |
| 2099 | - break; | |
| 2100 | - case 't': | |
| 2101 | - case 'T': | |
| 2102 | - zRealType = "t"; | |
| 2103 | - rc = *zRealType; | |
| 2104 | - break; | |
| 2105 | - case 'w': | |
| 2106 | - case 'W': | |
| 2107 | - zRealType = "w"; | |
| 2108 | - rc = *zRealType; | |
| 2109 | - break; | |
| 2110 | - default: | |
| 2111 | - rc = '*'; | |
| 2112 | - break; | |
| 2113 | - } | |
| 2114 | - assert(0 != rc); | |
| 2115 | - if(zRealType){ | |
| 2116 | - statsReportTimelineYFlag = zRealType; | |
| 2117 | - db_multi_exec("CREATE TEMP VIEW v_reports AS " | |
| 2118 | - "SELECT * FROM event WHERE type GLOB %Q", | |
| 2119 | - zRealType); | |
| 2120 | - }else{ | |
| 2121 | - statsReportTimelineYFlag = "a"; | |
| 2122 | - db_multi_exec("CREATE TEMP VIEW v_reports AS " | |
| 2123 | - "SELECT * FROM event"); | |
| 2124 | - } | |
| 2125 | - return statsReportType = rc; | |
| 2126 | -} | |
| 2127 | - | |
| 2128 | -/* | |
| 2129 | -** Returns a string suitable (for a given value of suitable) for | |
| 2130 | -** use in a label with the header of the /reports pages, dependent | |
| 2131 | -** on the 'type' flag. See stats_report_init_view(). | |
| 2132 | -** The returned bytes are static. | |
| 2133 | -*/ | |
| 2134 | -static const char *stats_report_label_for_type(){ | |
| 2135 | - assert( statsReportType && "Must call stats_report_init_view() first." ); | |
| 2136 | - switch( statsReportType ){ | |
| 2137 | - case 'c': | |
| 2138 | - return "checkins"; | |
| 2139 | - case 'e': | |
| 2140 | - return "events"; | |
| 2141 | - case 'w': | |
| 2142 | - return "wiki changes"; | |
| 2143 | - case 't': | |
| 2144 | - return "ticket changes"; | |
| 2145 | - case 'g': | |
| 2146 | - return "tag changes"; | |
| 2147 | - default: | |
| 2148 | - return "all types"; | |
| 2149 | - } | |
| 2150 | -} | |
| 2151 | - | |
| 2152 | -/* | |
| 2153 | -** A helper for the /reports family of pages which prints out a menu | |
| 2154 | -** of links for the various type=XXX flags. zCurrentViewName must be | |
| 2155 | -** the name/value of the 'view' parameter which is in effect at the | |
| 2156 | -** time this is called. e.g. if called from the 'byuser' view then | |
| 2157 | -** zCurrentViewName must be "byuser". Any URL parameters which need to | |
| 2158 | -** be added to the generated URLs should be passed in zParam. The | |
| 2159 | -** caller is expected to have already encoded any zParam in the %T or | |
| 2160 | -** %t encoding. */ | |
| 2161 | -static void stats_report_event_types_menu(const char *zCurrentViewName, | |
| 2162 | - const char *zParam){ | |
| 2163 | - char *zTop; | |
| 2164 | - if(zParam && !*zParam){ | |
| 2165 | - zParam = NULL; | |
| 2166 | - } | |
| 2167 | - zTop = mprintf("%s/reports?view=%s%s%s", g.zTop, zCurrentViewName, | |
| 2168 | - zParam ? "&" : "", zParam); | |
| 2169 | - cgi_printf("<div>"); | |
| 2170 | - cgi_printf("<span>Types:</span> "); | |
| 2171 | - if('*' == statsReportType){ | |
| 2172 | - cgi_printf(" <strong>all</strong>", zTop); | |
| 2173 | - }else{ | |
| 2174 | - cgi_printf(" <a href='%s'>all</a>", zTop); | |
| 2175 | - } | |
| 2176 | - if('c' == statsReportType){ | |
| 2177 | - cgi_printf(" <strong>checkins</strong>", zTop); | |
| 2178 | - }else{ | |
| 2179 | - cgi_printf(" <a href='%s&type=ci'>checkins</a>", zTop); | |
| 2180 | - } | |
| 2181 | - if('e' == statsReportType){ | |
| 2182 | - cgi_printf(" <strong>events</strong>", zTop); | |
| 2183 | - }else{ | |
| 2184 | - cgi_printf(" <a href='%s&type=e'>events</a>", zTop); | |
| 2185 | - } | |
| 2186 | - if( 't' == statsReportType ){ | |
| 2187 | - cgi_printf(" <strong>tickets</strong>", zTop); | |
| 2188 | - }else{ | |
| 2189 | - cgi_printf(" <a href='%s&type=t'>tickets</a>", zTop); | |
| 2190 | - } | |
| 2191 | - if( 'g' == statsReportType ){ | |
| 2192 | - cgi_printf(" <strong>tags</strong>", zTop); | |
| 2193 | - }else{ | |
| 2194 | - cgi_printf(" <a href='%s&type=g'>tags</a>", zTop); | |
| 2195 | - } | |
| 2196 | - if( 'w' == statsReportType ){ | |
| 2197 | - cgi_printf(" <strong>wiki</strong>", zTop); | |
| 2198 | - }else{ | |
| 2199 | - cgi_printf(" <a href='%s&type=w'>wiki</a>", zTop); | |
| 2200 | - } | |
| 2201 | - fossil_free(zTop); | |
| 2202 | - cgi_printf("</div>"); | |
| 2203 | -} | |
| 2204 | - | |
| 2205 | - | |
| 2206 | -/* | |
| 2207 | -** Helper for stats_report_by_month_year(), which generates a list of | |
| 2208 | -** week numbers. zTimeframe should be either a timeframe in the form YYYY | |
| 2209 | -** or YYYY-MM. | |
| 2210 | -*/ | |
| 2211 | -static void stats_report_output_week_links(const char *zTimeframe){ | |
| 2212 | - Stmt stWeek = empty_Stmt; | |
| 2213 | - char yearPart[5] = {0,0,0,0,0}; | |
| 2214 | - memcpy(yearPart, zTimeframe, 4); | |
| 2215 | - db_prepare(&stWeek, | |
| 2216 | - "SELECT DISTINCT strftime('%%W',mtime) AS wk, " | |
| 2217 | - "count(*) AS n, " | |
| 2218 | - "substr(date(mtime),1,%d) AS ym " | |
| 2219 | - "FROM v_reports " | |
| 2220 | - "WHERE ym=%Q AND mtime < current_timestamp " | |
| 2221 | - "GROUP BY wk ORDER BY wk", | |
| 2222 | - strlen(zTimeframe), | |
| 2223 | - zTimeframe); | |
| 2224 | - while( SQLITE_ROW == db_step(&stWeek) ){ | |
| 2225 | - const char *zWeek = db_column_text(&stWeek,0); | |
| 2226 | - const int nCount = db_column_int(&stWeek,1); | |
| 2227 | - cgi_printf("<a href='%s/timeline?" | |
| 2228 | - "yw=%t-%t&n=%d&y=%s'>%s</a>", | |
| 2229 | - g.zTop, yearPart, zWeek, | |
| 2230 | - nCount, statsReportTimelineYFlag, zWeek); | |
| 2231 | - } | |
| 2232 | - db_finalize(&stWeek); | |
| 2233 | -} | |
| 2234 | - | |
| 2235 | -/* | |
| 2236 | -** Implements the "byyear" and "bymonth" reports for /reports. | |
| 2237 | -** If includeMonth is true then it generates the "bymonth" report, | |
| 2238 | -** else the "byyear" report. If zUserName is not NULL and not empty | |
| 2239 | -** then the report is restricted to events created by the named user | |
| 2240 | -** account. | |
| 2241 | -*/ | |
| 2242 | -static void stats_report_by_month_year(char includeMonth, | |
| 2243 | - char includeWeeks, | |
| 2244 | - const char *zUserName){ | |
| 2245 | - Stmt query = empty_Stmt; | |
| 2246 | - int nRowNumber = 0; /* current TR number */ | |
| 2247 | - int nEventTotal = 0; /* Total event count */ | |
| 2248 | - int rowClass = 0; /* counter for alternating | |
| 2249 | - row colors */ | |
| 2250 | - Blob sql = empty_blob; /* SQL */ | |
| 2251 | - const char *zTimeLabel = includeMonth ? "Year/Month" : "Year"; | |
| 2252 | - char zPrevYear[5] = {0}; /* For keeping track of when | |
| 2253 | - we change years while looping */ | |
| 2254 | - int nEventsPerYear = 0; /* Total event count for the | |
| 2255 | - current year */ | |
| 2256 | - char showYearTotal = 0; /* Flag telling us when to show | |
| 2257 | - the per-year event totals */ | |
| 2258 | - Blob header = empty_blob; /* Page header text */ | |
| 2259 | - int nMaxEvents = 1; /* for calculating length of graph | |
| 2260 | - bars. */ | |
| 2261 | - int iterations = 0; /* number of weeks/months we iterate | |
| 2262 | - over */ | |
| 2263 | - stats_report_init_view(); | |
| 2264 | - stats_report_event_types_menu( includeMonth ? "bymonth" : "byyear", NULL ); | |
| 2265 | - blob_appendf(&header, "Timeline Events (%s) by year%s", | |
| 2266 | - stats_report_label_for_type(), | |
| 2267 | - (includeMonth ? "/month" : "")); | |
| 2268 | - blob_append_sql(&sql, | |
| 2269 | - "SELECT substr(date(mtime),1,%d) AS timeframe, " | |
| 2270 | - "count(*) AS eventCount " | |
| 2271 | - "FROM v_reports ", | |
| 2272 | - includeMonth ? 7 : 4); | |
| 2273 | - if(zUserName&&*zUserName){ | |
| 2274 | - blob_append_sql(&sql, " WHERE user=%Q ", zUserName); | |
| 2275 | - blob_appendf(&header," for user %q", zUserName); | |
| 2276 | - } | |
| 2277 | - blob_append(&sql, | |
| 2278 | - " GROUP BY timeframe" | |
| 2279 | - " ORDER BY timeframe DESC", | |
| 2280 | - -1); | |
| 2281 | - db_prepare(&query, "%s", blob_sql_text(&sql)); | |
| 2282 | - blob_reset(&sql); | |
| 2283 | - @ <h1>%b(&header)</h1> | |
| 2284 | - @ <table class='statistics-report-table-events' border='0' cellpadding='2' | |
| 2285 | - @ cellspacing='0' id='statsTable'> | |
| 2286 | - @ <thead> | |
| 2287 | - @ <th>%s(zTimeLabel)</th> | |
| 2288 | - @ <th>Events</th> | |
| 2289 | - @ <th width='90%%'><!-- relative commits graph --></th> | |
| 2290 | - @ </thead><tbody> | |
| 2291 | - blob_reset(&header); | |
| 2292 | - /* | |
| 2293 | - Run the query twice. The first time we calculate the maximum | |
| 2294 | - number of events for a given row. Maybe someone with better SQL | |
| 2295 | - Fu can re-implement this with a single query. | |
| 2296 | - */ | |
| 2297 | - while( SQLITE_ROW == db_step(&query) ){ | |
| 2298 | - const int nCount = db_column_int(&query, 1); | |
| 2299 | - if(nCount>nMaxEvents){ | |
| 2300 | - nMaxEvents = nCount; | |
| 2301 | - } | |
| 2302 | - ++iterations; | |
| 2303 | - } | |
| 2304 | - db_reset(&query); | |
| 2305 | - while( SQLITE_ROW == db_step(&query) ){ | |
| 2306 | - const char *zTimeframe = db_column_text(&query, 0); | |
| 2307 | - const int nCount = db_column_int(&query, 1); | |
| 2308 | - int nSize = nCount | |
| 2309 | - ? (int)(100 * nCount / nMaxEvents) | |
| 2310 | - : 1; | |
| 2311 | - showYearTotal = 0; | |
| 2312 | - if(!nSize) nSize = 1; | |
| 2313 | - if(includeMonth){ | |
| 2314 | - /* For Month/year view, add a separator for each distinct year. */ | |
| 2315 | - if(!*zPrevYear || | |
| 2316 | - (0!=fossil_strncmp(zPrevYear,zTimeframe,4))){ | |
| 2317 | - showYearTotal = *zPrevYear; | |
| 2318 | - if(showYearTotal){ | |
| 2319 | - rowClass = ++nRowNumber % 2; | |
| 2320 | - @ <tr class='row%d(rowClass)'> | |
| 2321 | - @ <td></td> | |
| 2322 | - @ <td colspan='2'>Yearly total: %d(nEventsPerYear)</td> | |
| 2323 | - @</tr> | |
| 2324 | - } | |
| 2325 | - nEventsPerYear = 0; | |
| 2326 | - memcpy(zPrevYear,zTimeframe,4); | |
| 2327 | - rowClass = ++nRowNumber % 2; | |
| 2328 | - @ <tr class='row%d(rowClass)'> | |
| 2329 | - @ <th colspan='3' class='statistics-report-row-year'>%s(zPrevYear)</th> | |
| 2330 | - @ </tr> | |
| 2331 | - } | |
| 2332 | - } | |
| 2333 | - rowClass = ++nRowNumber % 2; | |
| 2334 | - nEventTotal += nCount; | |
| 2335 | - nEventsPerYear += nCount; | |
| 2336 | - @<tr class='row%d(rowClass)'> | |
| 2337 | - @ <td> | |
| 2338 | - if(includeMonth){ | |
| 2339 | - cgi_printf("<a href='%s/timeline?" | |
| 2340 | - "ym=%t&n=%d&y=%s", | |
| 2341 | - g.zTop, zTimeframe, nCount, | |
| 2342 | - statsReportTimelineYFlag ); | |
| 2343 | - /* Reminder: n=nCount is not actually correct for bymonth unless | |
| 2344 | - that was the only user who caused events. | |
| 2345 | - */ | |
| 2346 | - if( zUserName && *zUserName ){ | |
| 2347 | - cgi_printf("&u=%t", zUserName); | |
| 2348 | - } | |
| 2349 | - cgi_printf("' target='_new'>%s</a>",zTimeframe); | |
| 2350 | - }else { | |
| 2351 | - cgi_printf("<a href='?view=byweek&y=%s&type=%c", | |
| 2352 | - zTimeframe, (char)statsReportType); | |
| 2353 | - if(zUserName && *zUserName){ | |
| 2354 | - cgi_printf("&u=%t", zUserName); | |
| 2355 | - } | |
| 2356 | - cgi_printf("'>%s</a>", zTimeframe); | |
| 2357 | - } | |
| 2358 | - @ </td><td>%d(nCount)</td> | |
| 2359 | - @ <td> | |
| 2360 | - @ <div class='statistics-report-graph-line' | |
| 2361 | - @ style='width:%d(nSize)%%;'> </div> | |
| 2362 | - @ </td> | |
| 2363 | - @</tr> | |
| 2364 | - if(includeWeeks){ | |
| 2365 | - /* This part works fine for months but it terribly slow (4.5s on my PC), | |
| 2366 | - so it's only shown for by-year for now. Suggestions/patches for | |
| 2367 | - a better/faster layout are welcomed. */ | |
| 2368 | - @ <tr class='row%d(rowClass)'> | |
| 2369 | - @ <td colspan='2' class='statistics-report-week-number-label'>Week #:</td> | |
| 2370 | - @ <td class='statistics-report-week-of-year-list'> | |
| 2371 | - stats_report_output_week_links(zTimeframe); | |
| 2372 | - @ </td></tr> | |
| 2373 | - } | |
| 2374 | - | |
| 2375 | - /* | |
| 2376 | - Potential improvement: calculate the min/max event counts and | |
| 2377 | - use percent-based graph bars. | |
| 2378 | - */ | |
| 2379 | - } | |
| 2380 | - db_finalize(&query); | |
| 2381 | - if(includeMonth && !showYearTotal && *zPrevYear){ | |
| 2382 | - /* Add final year total separator. */ | |
| 2383 | - rowClass = ++nRowNumber % 2; | |
| 2384 | - @ <tr class='row%d(rowClass)'> | |
| 2385 | - @ <td></td> | |
| 2386 | - @ <td colspan='2'>Yearly total: %d(nEventsPerYear)</td> | |
| 2387 | - @</tr> | |
| 2388 | - } | |
| 2389 | - @ </tbody></table> | |
| 2390 | - if(nEventTotal){ | |
| 2391 | - const char *zAvgLabel = includeMonth ? "month" : "year"; | |
| 2392 | - int nAvg = iterations ? (nEventTotal/iterations) : 0; | |
| 2393 | - @ <br><div>Total events: %d(nEventTotal) | |
| 2394 | - @ <br>Average per active %s(zAvgLabel): %d(nAvg) | |
| 2395 | - @ </div> | |
| 2396 | - } | |
| 2397 | - if( !includeMonth ){ | |
| 2398 | - output_table_sorting_javascript("statsTable","tnx"); | |
| 2399 | - } | |
| 2400 | -} | |
| 2401 | - | |
| 2402 | -/* | |
| 2403 | -** Implements the "byuser" view for /reports. | |
| 2404 | -*/ | |
| 2405 | -static void stats_report_by_user(){ | |
| 2406 | - Stmt query = empty_Stmt; | |
| 2407 | - int nRowNumber = 0; /* current TR number */ | |
| 2408 | - int nEventTotal = 0; /* Total event count */ | |
| 2409 | - int rowClass = 0; /* counter for alternating | |
| 2410 | - row colors */ | |
| 2411 | - int nMaxEvents = 1; /* max number of events for | |
| 2412 | - all rows. */ | |
| 2413 | - stats_report_init_view(); | |
| 2414 | - stats_report_event_types_menu("byuser", NULL); | |
| 2415 | - db_prepare(&query, | |
| 2416 | - "SELECT user, " | |
| 2417 | - "COUNT(*) AS eventCount " | |
| 2418 | - "FROM v_reports " | |
| 2419 | - "GROUP BY user ORDER BY eventCount DESC"); | |
| 2420 | - @ <h1>Timeline Events | |
| 2421 | - @ (%s(stats_report_label_for_type())) by User</h1> | |
| 2422 | - @ <table class='statistics-report-table-events' border='0' | |
| 2423 | - @ cellpadding='2' cellspacing='0' id='statsTable'> | |
| 2424 | - @ <thead><tr> | |
| 2425 | - @ <th>User</th> | |
| 2426 | - @ <th>Events</th> | |
| 2427 | - @ <th width='90%%'><!-- relative commits graph --></th> | |
| 2428 | - @ </tr></thead><tbody> | |
| 2429 | - while( SQLITE_ROW == db_step(&query) ){ | |
| 2430 | - const int nCount = db_column_int(&query, 1); | |
| 2431 | - if(nCount>nMaxEvents){ | |
| 2432 | - nMaxEvents = nCount; | |
| 2433 | - } | |
| 2434 | - } | |
| 2435 | - db_reset(&query); | |
| 2436 | - while( SQLITE_ROW == db_step(&query) ){ | |
| 2437 | - const char *zUser = db_column_text(&query, 0); | |
| 2438 | - const int nCount = db_column_int(&query, 1); | |
| 2439 | - int nSize = nCount | |
| 2440 | - ? (int)(100 * nCount / nMaxEvents) | |
| 2441 | - : 0; | |
| 2442 | - if(!nCount) continue /* arguable! Possible? */; | |
| 2443 | - else if(!nSize) nSize = 1; | |
| 2444 | - rowClass = ++nRowNumber % 2; | |
| 2445 | - nEventTotal += nCount; | |
| 2446 | - @<tr class='row%d(rowClass)'> | |
| 2447 | - @ <td> | |
| 2448 | - @ <a href="?view=bymonth&user=%h(zUser)&type=%c((char)statsReportType)">%h(zUser)</a> | |
| 2449 | - @ </td><td>%d(nCount)</td> | |
| 2450 | - @ <td> | |
| 2451 | - @ <div class='statistics-report-graph-line' | |
| 2452 | - @ style='width:%d(nSize)%%;'> </div> | |
| 2453 | - @ </td> | |
| 2454 | - @</tr> | |
| 2455 | - /* | |
| 2456 | - Potential improvement: calculate the min/max event counts and | |
| 2457 | - use percent-based graph bars. | |
| 2458 | - */ | |
| 2459 | - } | |
| 2460 | - @ </tbody></table> | |
| 2461 | - db_finalize(&query); | |
| 2462 | - output_table_sorting_javascript("statsTable","tnx"); | |
| 2463 | -} | |
| 2464 | - | |
| 2465 | -/* | |
| 2466 | -** Implements the "byweekday" view for /reports. | |
| 2467 | -*/ | |
| 2468 | -static void stats_report_day_of_week(){ | |
| 2469 | - Stmt query = empty_Stmt; | |
| 2470 | - int nRowNumber = 0; /* current TR number */ | |
| 2471 | - int nEventTotal = 0; /* Total event count */ | |
| 2472 | - int rowClass = 0; /* counter for alternating | |
| 2473 | - row colors */ | |
| 2474 | - int nMaxEvents = 1; /* max number of events for | |
| 2475 | - all rows. */ | |
| 2476 | - static const char *const daysOfWeek[] = { | |
| 2477 | - "Monday", "Tuesday", "Wednesday", "Thursday", | |
| 2478 | - "Friday", "Saturday", "Sunday" | |
| 2479 | - }; | |
| 2480 | - | |
| 2481 | - stats_report_init_view(); | |
| 2482 | - stats_report_event_types_menu("byweekday", NULL); | |
| 2483 | - db_prepare(&query, | |
| 2484 | - "SELECT cast(mtime %% 7 AS INTEGER) dow, " | |
| 2485 | - "COUNT(*) AS eventCount " | |
| 2486 | - "FROM v_reports " | |
| 2487 | - "GROUP BY dow ORDER BY dow"); | |
| 2488 | - @ <h1>Timeline Events | |
| 2489 | - @ (%s(stats_report_label_for_type())) by Day of the Week</h1> | |
| 2490 | - @ <table class='statistics-report-table-events' border='0' | |
| 2491 | - @ cellpadding='2' cellspacing='0' id='statsTable'> | |
| 2492 | - @ <thead><tr> | |
| 2493 | - @ <th>DoW</th> | |
| 2494 | - @ <th>Day</th> | |
| 2495 | - @ <th>Events</th> | |
| 2496 | - @ <th width='90%%'><!-- relative commits graph --></th> | |
| 2497 | - @ </tr></thead><tbody> | |
| 2498 | - while( SQLITE_ROW == db_step(&query) ){ | |
| 2499 | - const int nCount = db_column_int(&query, 1); | |
| 2500 | - if(nCount>nMaxEvents){ | |
| 2501 | - nMaxEvents = nCount; | |
| 2502 | - } | |
| 2503 | - } | |
| 2504 | - db_reset(&query); | |
| 2505 | - while( SQLITE_ROW == db_step(&query) ){ | |
| 2506 | - const int dayNum =db_column_int(&query, 0); | |
| 2507 | - const int nCount = db_column_int(&query, 1); | |
| 2508 | - int nSize = nCount | |
| 2509 | - ? (int)(100 * nCount / nMaxEvents) | |
| 2510 | - : 0; | |
| 2511 | - if(!nCount) continue /* arguable! Possible? */; | |
| 2512 | - else if(!nSize) nSize = 1; | |
| 2513 | - rowClass = ++nRowNumber % 2; | |
| 2514 | - nEventTotal += nCount; | |
| 2515 | - @<tr class='row%d(rowClass)'> | |
| 2516 | - @ <td>%d(dayNum)</td> | |
| 2517 | - @ <td>%s(daysOfWeek[dayNum])</td> | |
| 2518 | - @ <td>%d(nCount)</td> | |
| 2519 | - @ <td> | |
| 2520 | - @ <div class='statistics-report-graph-line' | |
| 2521 | - @ style='width:%d(nSize)%%;'> </div> | |
| 2522 | - @ </td> | |
| 2523 | - @</tr> | |
| 2524 | - } | |
| 2525 | - @ </tbody></table> | |
| 2526 | - db_finalize(&query); | |
| 2527 | - output_table_sorting_javascript("statsTable","ntnx"); | |
| 2528 | -} | |
| 2529 | - | |
| 2530 | - | |
| 2531 | -/* | |
| 2532 | -** Helper for stats_report_by_month_year(), which generates a list of | |
| 2533 | -** week numbers. zTimeframe should be either a timeframe in the form YYYY | |
| 2534 | -** or YYYY-MM. | |
| 2535 | -*/ | |
| 2536 | -static void stats_report_year_weeks(const char *zUserName){ | |
| 2537 | - const char *zYear = P("y"); | |
| 2538 | - int nYear = zYear ? strlen(zYear) : 0; | |
| 2539 | - int i = 0; | |
| 2540 | - Stmt qYears = empty_Stmt; | |
| 2541 | - char *zDefaultYear = NULL; | |
| 2542 | - Blob sql = empty_blob; | |
| 2543 | - int nMaxEvents = 1; /* max number of events for | |
| 2544 | - all rows. */ | |
| 2545 | - int iterations = 0; /* # of active time periods. */ | |
| 2546 | - stats_report_init_view(); | |
| 2547 | - if(4==nYear){ | |
| 2548 | - Blob urlParams = empty_blob; | |
| 2549 | - blob_appendf(&urlParams, "y=%T", zYear); | |
| 2550 | - stats_report_event_types_menu("byweek", blob_str(&urlParams)); | |
| 2551 | - blob_reset(&urlParams); | |
| 2552 | - }else{ | |
| 2553 | - stats_report_event_types_menu("byweek", NULL); | |
| 2554 | - } | |
| 2555 | - blob_append(&sql, | |
| 2556 | - "SELECT DISTINCT substr(date(mtime),1,4) AS y " | |
| 2557 | - "FROM v_reports WHERE 1 ", -1); | |
| 2558 | - if(zUserName&&*zUserName){ | |
| 2559 | - blob_append_sql(&sql,"AND user=%Q ", zUserName); | |
| 2560 | - } | |
| 2561 | - blob_append(&sql,"GROUP BY y ORDER BY y", -1); | |
| 2562 | - db_prepare(&qYears, "%s", blob_sql_text(&sql)); | |
| 2563 | - blob_reset(&sql); | |
| 2564 | - cgi_printf("Select year: "); | |
| 2565 | - while( SQLITE_ROW == db_step(&qYears) ){ | |
| 2566 | - const char *zT = db_column_text(&qYears, 0); | |
| 2567 | - if( i++ ){ | |
| 2568 | - cgi_printf(" "); | |
| 2569 | - } | |
| 2570 | - cgi_printf("<a href='?view=byweek&y=%s&type=%c", zT, | |
| 2571 | - (char)statsReportType); | |
| 2572 | - if(zUserName && *zUserName){ | |
| 2573 | - cgi_printf("&user=%t",zUserName); | |
| 2574 | - } | |
| 2575 | - cgi_printf("'>%s</a>",zT); | |
| 2576 | - } | |
| 2577 | - db_finalize(&qYears); | |
| 2578 | - cgi_printf("<br/>"); | |
| 2579 | - if(!zYear || !*zYear){ | |
| 2580 | - zDefaultYear = db_text("????", "SELECT strftime('%%Y')"); | |
| 2581 | - zYear = zDefaultYear; | |
| 2582 | - nYear = 4; | |
| 2583 | - } | |
| 2584 | - if(4 == nYear){ | |
| 2585 | - Stmt stWeek = empty_Stmt; | |
| 2586 | - int rowCount = 0; | |
| 2587 | - int total = 0; | |
| 2588 | - Blob header = empty_blob; | |
| 2589 | - blob_appendf(&header, "Timeline events (%s) for the calendar weeks " | |
| 2590 | - "of %h", stats_report_label_for_type(), | |
| 2591 | - zYear); | |
| 2592 | - blob_append_sql(&sql, | |
| 2593 | - "SELECT DISTINCT strftime('%%W',mtime) AS wk, " | |
| 2594 | - "count(*) AS n " | |
| 2595 | - "FROM v_reports " | |
| 2596 | - "WHERE %Q=substr(date(mtime),1,4) " | |
| 2597 | - "AND mtime < current_timestamp ", | |
| 2598 | - zYear); | |
| 2599 | - if(zUserName&&*zUserName){ | |
| 2600 | - blob_append_sql(&sql, " AND user=%Q ", zUserName); | |
| 2601 | - blob_appendf(&header," for user %h", zUserName); | |
| 2602 | - } | |
| 2603 | - blob_append_sql(&sql, "GROUP BY wk ORDER BY wk DESC"); | |
| 2604 | - cgi_printf("<h1>%h</h1>", blob_str(&header)); | |
| 2605 | - blob_reset(&header); | |
| 2606 | - cgi_printf("<table class='statistics-report-table-events' " | |
| 2607 | - "border='0' cellpadding='2' width='100%%' " | |
| 2608 | - "cellspacing='0' id='statsTable'>"); | |
| 2609 | - cgi_printf("<thead><tr>" | |
| 2610 | - "<th>Week</th>" | |
| 2611 | - "<th>Events</th>" | |
| 2612 | - "<th width='90%%'><!-- relative commits graph --></th>" | |
| 2613 | - "</tr></thead>" | |
| 2614 | - "<tbody>"); | |
| 2615 | - db_prepare(&stWeek, "%s", blob_sql_text(&sql)); | |
| 2616 | - blob_reset(&sql); | |
| 2617 | - while( SQLITE_ROW == db_step(&stWeek) ){ | |
| 2618 | - const int nCount = db_column_int(&stWeek, 1); | |
| 2619 | - if(nCount>nMaxEvents){ | |
| 2620 | - nMaxEvents = nCount; | |
| 2621 | - } | |
| 2622 | - ++iterations; | |
| 2623 | - } | |
| 2624 | - db_reset(&stWeek); | |
| 2625 | - while( SQLITE_ROW == db_step(&stWeek) ){ | |
| 2626 | - const char *zWeek = db_column_text(&stWeek,0); | |
| 2627 | - const int nCount = db_column_int(&stWeek,1); | |
| 2628 | - int nSize = nCount | |
| 2629 | - ? (int)(100 * nCount / nMaxEvents) | |
| 2630 | - : 0; | |
| 2631 | - if(!nSize) nSize = 1; | |
| 2632 | - total += nCount; | |
| 2633 | - cgi_printf("<tr class='row%d'>", ++rowCount % 2 ); | |
| 2634 | - cgi_printf("<td><a href='%s/timeline?yw=%t-%s&n=%d&y=%s", | |
| 2635 | - g.zTop, zYear, zWeek, nCount, | |
| 2636 | - statsReportTimelineYFlag); | |
| 2637 | - if(zUserName && *zUserName){ | |
| 2638 | - cgi_printf("&u=%t",zUserName); | |
| 2639 | - } | |
| 2640 | - cgi_printf("'>%s</a></td>",zWeek); | |
| 2641 | - | |
| 2642 | - cgi_printf("<td>%d</td>",nCount); | |
| 2643 | - cgi_printf("<td>"); | |
| 2644 | - if(nCount){ | |
| 2645 | - cgi_printf("<div class='statistics-report-graph-line'" | |
| 2646 | - "style='width:%d%%;'> </div>", | |
| 2647 | - nSize); | |
| 2648 | - } | |
| 2649 | - cgi_printf("</td></tr>"); | |
| 2650 | - } | |
| 2651 | - db_finalize(&stWeek); | |
| 2652 | - free(zDefaultYear); | |
| 2653 | - cgi_printf("</tbody></table>"); | |
| 2654 | - if(total){ | |
| 2655 | - int nAvg = iterations ? (total/iterations) : 0; | |
| 2656 | - cgi_printf("<br><div>Total events: %d<br>" | |
| 2657 | - "Average per active week: %d</div>", | |
| 2658 | - total, nAvg); | |
| 2659 | - } | |
| 2660 | - output_table_sorting_javascript("statsTable","tnx"); | |
| 2661 | - } | |
| 2662 | -} | |
| 2663 | - | |
| 2664 | -/* | |
| 2665 | -** WEBPAGE: reports | |
| 2666 | -** | |
| 2667 | -** Shows activity reports for the repository. | |
| 2668 | -** | |
| 2669 | -** Query Parameters: | |
| 2670 | -** | |
| 2671 | -** view=REPORT_NAME Valid values: bymonth, byyear, byuser | |
| 2672 | -** user=NAME Restricts statistics to the given user | |
| 2673 | -** type=TYPE Restricts the report to a specific event type: | |
| 2674 | -** ci (checkin), w (wiki), t (ticket), g (tag) | |
| 2675 | -** Defaulting to all event types. | |
| 2676 | -** | |
| 2677 | -** The view-specific query parameters include: | |
| 2678 | -** | |
| 2679 | -** view=byweek: | |
| 2680 | -** | |
| 2681 | -** y=YYYY The year to report (default is the server's | |
| 2682 | -** current year). | |
| 2683 | -*/ | |
| 2684 | -void stats_report_page(){ | |
| 2685 | - HQuery url; /* URL for various branch links */ | |
| 2686 | - const char *zView = P("view"); /* Which view/report to show. */ | |
| 2687 | - const char *zUserName = P("user"); | |
| 2688 | - | |
| 2689 | - login_check_credentials(); | |
| 2690 | - if( !g.perm.Read ){ login_needed(); return; } | |
| 2691 | - if(!zUserName) zUserName = P("u"); | |
| 2692 | - url_initialize(&url, "reports"); | |
| 2693 | - if(zUserName && *zUserName){ | |
| 2694 | - url_add_parameter(&url,"user", zUserName); | |
| 2695 | - timeline_submenu(&url, "(Remove User Flag)", "view", zView, "user"); | |
| 2696 | - } | |
| 2697 | - timeline_submenu(&url, "By Year", "view", "byyear", 0); | |
| 2698 | - timeline_submenu(&url, "By Month", "view", "bymonth", 0); | |
| 2699 | - timeline_submenu(&url, "By Week", "view", "byweek", 0); | |
| 2700 | - timeline_submenu(&url, "By Weekday", "view", "byweekday", 0); | |
| 2701 | - timeline_submenu(&url, "By User", "view", "byuser", "user"); | |
| 2702 | - url_reset(&url); | |
| 2703 | - style_header("Activity Reports"); | |
| 2704 | - if(0==fossil_strcmp(zView,"byyear")){ | |
| 2705 | - stats_report_by_month_year(0, 0, zUserName); | |
| 2706 | - }else if(0==fossil_strcmp(zView,"bymonth")){ | |
| 2707 | - stats_report_by_month_year(1, 0, zUserName); | |
| 2708 | - }else if(0==fossil_strcmp(zView,"byweek")){ | |
| 2709 | - stats_report_year_weeks(zUserName); | |
| 2710 | - }else if(0==fossil_strcmp(zView,"byuser")){ | |
| 2711 | - stats_report_by_user(); | |
| 2712 | - }else if(0==fossil_strcmp(zView,"byweekday")){ | |
| 2713 | - stats_report_day_of_week(); | |
| 2714 | - }else{ | |
| 2715 | - @ <h1>Select a report to show:</h1> | |
| 2716 | - @ <ul> | |
| 2717 | - @ <li><a href='?view=byyear'>Events by year</a></li> | |
| 2718 | - @ <li><a href='?view=bymonth'>Events by month</a></li> | |
| 2719 | - @ <li><a href='?view=byweek'>Events by calendar week</a></li> | |
| 2720 | - @ <li><a href='?view=byweekday'>Events by day of the week</a></li> | |
| 2721 | - @ <li><a href='?view=byuser'>Events by user</a></li> | |
| 2722 | - @ </ul> | |
| 2723 | - } | |
| 2724 | - | |
| 2725 | 2049 | style_footer(); |
| 2726 | 2050 | } |
| 2727 | 2051 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -87,10 +87,11 @@ | |
| 87 | #define TIMELINE_FCHANGES 0x0020 /* Detail file changes */ |
| 88 | #define TIMELINE_BRCOLOR 0x0040 /* Background color by branch name */ |
| 89 | #define TIMELINE_UCOLOR 0x0080 /* Background color by user */ |
| 90 | #define TIMELINE_FRENAMES 0x0100 /* Detail only file name changes */ |
| 91 | #define TIMELINE_UNHIDE 0x0200 /* Unhide check-ins with "hidden" tag */ |
| 92 | #endif |
| 93 | |
| 94 | /* |
| 95 | ** Hash a string and use the hash to determine a background color. |
| 96 | */ |
| @@ -359,20 +360,20 @@ | |
| 359 | } |
| 360 | } |
| 361 | } |
| 362 | if( zType[0]=='c' && (pGraph || (tmFlags & TIMELINE_BRCOLOR)!=0) ){ |
| 363 | int nParent = 0; |
| 364 | int aParent[32]; |
| 365 | int gidx; |
| 366 | static Stmt qparent; |
| 367 | db_static_prepare(&qparent, |
| 368 | "SELECT pid FROM plink" |
| 369 | " WHERE cid=:rid AND pid NOT IN phantom" |
| 370 | " ORDER BY isprim DESC /*sort*/" |
| 371 | ); |
| 372 | db_bind_int(&qparent, ":rid", rid); |
| 373 | while( db_step(&qparent)==SQLITE_ROW && nParent<32 ){ |
| 374 | aParent[nParent++] = db_column_int(&qparent, 0); |
| 375 | } |
| 376 | db_reset(&qparent); |
| 377 | gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr, |
| 378 | zUuid, isLeaf); |
| @@ -404,10 +405,13 @@ | |
| 404 | } |
| 405 | }else if( zType[0]=='e' && tagid ){ |
| 406 | hyperlink_to_event_tagid(tagid<0?-tagid:tagid); |
| 407 | }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ |
| 408 | hyperlink_to_uuid(zUuid); |
| 409 | } |
| 410 | db_column_blob(pQuery, commentColumn, &comment); |
| 411 | if( zType[0]!='c' ){ |
| 412 | /* Comments for anything other than a check-in are generated by |
| 413 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| @@ -604,12 +608,12 @@ | |
| 604 | ** for the upward portion of a merge arrow. The merge arrow goes up |
| 605 | ** to the row identified by mu:. If this value is zero then |
| 606 | ** node has no merge children and no merge-out line is drawn. |
| 607 | ** mu: The id of the row which is the top of the merge-out arrow. |
| 608 | ** u: Draw a thick child-line out of the top of this node and up to |
| 609 | ** the node with an id equal to this value. 0 if there is no |
| 610 | ** thick-line riser. |
| 611 | ** f: 0x01: a leaf node. |
| 612 | ** au: An array of integers that define thick-line risers for branches. |
| 613 | ** The integers are in pairs. For each pair, the first integer is |
| 614 | ** is the rail on which the riser should run and the second integer |
| 615 | ** is the id of the node upto which the riser should run. |
| @@ -1031,11 +1035,10 @@ | |
| 1031 | ** nd Suppress "divider" lines |
| 1032 | ** v Show details of files changed |
| 1033 | ** f=UUID Show family (immediate parents and children) of UUID |
| 1034 | ** from=UUID Path from... |
| 1035 | ** to=UUID ... to this |
| 1036 | ** nomerge ... avoid merge links on the path |
| 1037 | ** shortest ... show only the shortest path |
| 1038 | ** uf=FUUID Show only checkins that use given file version |
| 1039 | ** brbg Background color from branch name |
| 1040 | ** ubg Background color from user |
| 1041 | ** namechng Show only checkins that filename changes |
| @@ -1070,11 +1073,11 @@ | |
| 1070 | const char *zYearMonth = P("ym"); /* Show checkins for the given YYYY-MM */ |
| 1071 | const char *zYearWeek = P("yw"); /* Show checkins for the given YYYY-WW (week-of-year)*/ |
| 1072 | int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ |
| 1073 | int renameOnly = P("namechng")!=0; /* Show only checkins that rename files */ |
| 1074 | int tagid; /* Tag ID */ |
| 1075 | int tmFlags; /* Timeline flags */ |
| 1076 | const char *zThisTag = 0; /* Suppress links to this tag */ |
| 1077 | const char *zThisUser = 0; /* Suppress links to this user */ |
| 1078 | HQuery url; /* URL for various branch links */ |
| 1079 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| 1080 | int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */ |
| @@ -1110,13 +1113,13 @@ | |
| 1110 | ){ |
| 1111 | zCirca = zBefore = zAfter = 0; |
| 1112 | nEntry = -1; |
| 1113 | } |
| 1114 | if( zType[0]=='a' ){ |
| 1115 | tmFlags = TIMELINE_BRIEF | TIMELINE_GRAPH; |
| 1116 | }else{ |
| 1117 | tmFlags = TIMELINE_GRAPH; |
| 1118 | } |
| 1119 | if( nEntry>0 ) url_add_parameter(&url, "n", mprintf("%d", nEntry)); |
| 1120 | if( P("ng")!=0 || zSearch!=0 ){ |
| 1121 | tmFlags &= ~TIMELINE_GRAPH; |
| 1122 | url_add_parameter(&url, "ng", 0); |
| @@ -1206,10 +1209,11 @@ | |
| 1206 | }else if( (p_rid || d_rid) && g.perm.Read ){ |
| 1207 | /* If p= or d= is present, ignore all other parameters other than n= */ |
| 1208 | char *zUuid; |
| 1209 | int np, nd; |
| 1210 | |
| 1211 | if( p_rid && d_rid ){ |
| 1212 | if( p_rid!=d_rid ) p_rid = d_rid; |
| 1213 | if( P("n")==0 ) nEntry = 10; |
| 1214 | } |
| 1215 | db_multi_exec( |
| @@ -1552,10 +1556,11 @@ | |
| 1552 | } |
| 1553 | } |
| 1554 | if( P("showsql") ){ |
| 1555 | @ <blockquote>%h(blob_sql_text(&sql))</blockquote> |
| 1556 | } |
| 1557 | blob_zero(&sql); |
| 1558 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); |
| 1559 | @ <h2>%b(&desc)</h2> |
| 1560 | blob_reset(&desc); |
| 1561 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, 0); |
| @@ -2039,688 +2044,7 @@ | |
| 2039 | const char *zUuid = db_column_text(&q, 0); |
| 2040 | @ <li> |
| 2041 | @ <a href="%s(g.zTop)/timeline?p=%s(zUuid)&d=%s(zUuid)&unhide">%S(zUuid)</a> |
| 2042 | } |
| 2043 | db_finalize(&q); |
| 2044 | style_footer(); |
| 2045 | } |
| 2046 | |
| 2047 | |
| 2048 | /* |
| 2049 | ** Used by stats_report_xxxxx() to remember which type of events |
| 2050 | ** to show. Populated by stats_report_init_view() and holds the |
| 2051 | ** return value of that function. |
| 2052 | */ |
| 2053 | static int statsReportType = 0; |
| 2054 | |
| 2055 | /* |
| 2056 | ** Set by stats_report_init_view() to one of the y=XXXX values |
| 2057 | ** accepted by /timeline?y=XXXX. |
| 2058 | */ |
| 2059 | static const char *statsReportTimelineYFlag = NULL; |
| 2060 | |
| 2061 | /* |
| 2062 | ** Creates a TEMP VIEW named v_reports which is a wrapper around the |
| 2063 | ** EVENT table filtered on event.type. It looks for the request |
| 2064 | ** parameter 'type' (reminder: we "should" use 'y' for consistency |
| 2065 | ** with /timeline, but /reports uses 'y' for the year) and expects it |
| 2066 | ** to contain one of the conventional values from event.type or the |
| 2067 | ** value "all", which is treated as equivalent to "*". By default (if |
| 2068 | ** no 'y' is specified), "*" is assumed (that is also the default for |
| 2069 | ** invalid/unknown filter values). That 'y' filter is the one used for |
| 2070 | ** the event list. Note that a filter of "*" or "all" is equivalent to |
| 2071 | ** querying against the full event table. The view, however, adds an |
| 2072 | ** abstraction level to simplify the implementation code for the |
| 2073 | ** various /reports pages. |
| 2074 | ** |
| 2075 | ** Returns one of: 'c', 'w', 'g', 't', 'e', representing the type of |
| 2076 | ** filter it applies, or '*' if no filter is applied (i.e. if "all" is |
| 2077 | ** used). |
| 2078 | */ |
| 2079 | static int stats_report_init_view(){ |
| 2080 | const char *zType = PD("type","*"); /* analog to /timeline?y=... */ |
| 2081 | const char *zRealType = NULL; /* normalized form of zType */ |
| 2082 | int rc = 0; /* result code */ |
| 2083 | assert( !statsReportType && "Must not be called more than once." ); |
| 2084 | switch( (zType && *zType) ? *zType : 0 ){ |
| 2085 | case 'c': |
| 2086 | case 'C': |
| 2087 | zRealType = "ci"; |
| 2088 | rc = *zRealType; |
| 2089 | break; |
| 2090 | case 'e': |
| 2091 | case 'E': |
| 2092 | zRealType = "e"; |
| 2093 | rc = *zRealType; |
| 2094 | break; |
| 2095 | case 'g': |
| 2096 | case 'G': |
| 2097 | zRealType = "g"; |
| 2098 | rc = *zRealType; |
| 2099 | break; |
| 2100 | case 't': |
| 2101 | case 'T': |
| 2102 | zRealType = "t"; |
| 2103 | rc = *zRealType; |
| 2104 | break; |
| 2105 | case 'w': |
| 2106 | case 'W': |
| 2107 | zRealType = "w"; |
| 2108 | rc = *zRealType; |
| 2109 | break; |
| 2110 | default: |
| 2111 | rc = '*'; |
| 2112 | break; |
| 2113 | } |
| 2114 | assert(0 != rc); |
| 2115 | if(zRealType){ |
| 2116 | statsReportTimelineYFlag = zRealType; |
| 2117 | db_multi_exec("CREATE TEMP VIEW v_reports AS " |
| 2118 | "SELECT * FROM event WHERE type GLOB %Q", |
| 2119 | zRealType); |
| 2120 | }else{ |
| 2121 | statsReportTimelineYFlag = "a"; |
| 2122 | db_multi_exec("CREATE TEMP VIEW v_reports AS " |
| 2123 | "SELECT * FROM event"); |
| 2124 | } |
| 2125 | return statsReportType = rc; |
| 2126 | } |
| 2127 | |
| 2128 | /* |
| 2129 | ** Returns a string suitable (for a given value of suitable) for |
| 2130 | ** use in a label with the header of the /reports pages, dependent |
| 2131 | ** on the 'type' flag. See stats_report_init_view(). |
| 2132 | ** The returned bytes are static. |
| 2133 | */ |
| 2134 | static const char *stats_report_label_for_type(){ |
| 2135 | assert( statsReportType && "Must call stats_report_init_view() first." ); |
| 2136 | switch( statsReportType ){ |
| 2137 | case 'c': |
| 2138 | return "checkins"; |
| 2139 | case 'e': |
| 2140 | return "events"; |
| 2141 | case 'w': |
| 2142 | return "wiki changes"; |
| 2143 | case 't': |
| 2144 | return "ticket changes"; |
| 2145 | case 'g': |
| 2146 | return "tag changes"; |
| 2147 | default: |
| 2148 | return "all types"; |
| 2149 | } |
| 2150 | } |
| 2151 | |
| 2152 | /* |
| 2153 | ** A helper for the /reports family of pages which prints out a menu |
| 2154 | ** of links for the various type=XXX flags. zCurrentViewName must be |
| 2155 | ** the name/value of the 'view' parameter which is in effect at the |
| 2156 | ** time this is called. e.g. if called from the 'byuser' view then |
| 2157 | ** zCurrentViewName must be "byuser". Any URL parameters which need to |
| 2158 | ** be added to the generated URLs should be passed in zParam. The |
| 2159 | ** caller is expected to have already encoded any zParam in the %T or |
| 2160 | ** %t encoding. */ |
| 2161 | static void stats_report_event_types_menu(const char *zCurrentViewName, |
| 2162 | const char *zParam){ |
| 2163 | char *zTop; |
| 2164 | if(zParam && !*zParam){ |
| 2165 | zParam = NULL; |
| 2166 | } |
| 2167 | zTop = mprintf("%s/reports?view=%s%s%s", g.zTop, zCurrentViewName, |
| 2168 | zParam ? "&" : "", zParam); |
| 2169 | cgi_printf("<div>"); |
| 2170 | cgi_printf("<span>Types:</span> "); |
| 2171 | if('*' == statsReportType){ |
| 2172 | cgi_printf(" <strong>all</strong>", zTop); |
| 2173 | }else{ |
| 2174 | cgi_printf(" <a href='%s'>all</a>", zTop); |
| 2175 | } |
| 2176 | if('c' == statsReportType){ |
| 2177 | cgi_printf(" <strong>checkins</strong>", zTop); |
| 2178 | }else{ |
| 2179 | cgi_printf(" <a href='%s&type=ci'>checkins</a>", zTop); |
| 2180 | } |
| 2181 | if('e' == statsReportType){ |
| 2182 | cgi_printf(" <strong>events</strong>", zTop); |
| 2183 | }else{ |
| 2184 | cgi_printf(" <a href='%s&type=e'>events</a>", zTop); |
| 2185 | } |
| 2186 | if( 't' == statsReportType ){ |
| 2187 | cgi_printf(" <strong>tickets</strong>", zTop); |
| 2188 | }else{ |
| 2189 | cgi_printf(" <a href='%s&type=t'>tickets</a>", zTop); |
| 2190 | } |
| 2191 | if( 'g' == statsReportType ){ |
| 2192 | cgi_printf(" <strong>tags</strong>", zTop); |
| 2193 | }else{ |
| 2194 | cgi_printf(" <a href='%s&type=g'>tags</a>", zTop); |
| 2195 | } |
| 2196 | if( 'w' == statsReportType ){ |
| 2197 | cgi_printf(" <strong>wiki</strong>", zTop); |
| 2198 | }else{ |
| 2199 | cgi_printf(" <a href='%s&type=w'>wiki</a>", zTop); |
| 2200 | } |
| 2201 | fossil_free(zTop); |
| 2202 | cgi_printf("</div>"); |
| 2203 | } |
| 2204 | |
| 2205 | |
| 2206 | /* |
| 2207 | ** Helper for stats_report_by_month_year(), which generates a list of |
| 2208 | ** week numbers. zTimeframe should be either a timeframe in the form YYYY |
| 2209 | ** or YYYY-MM. |
| 2210 | */ |
| 2211 | static void stats_report_output_week_links(const char *zTimeframe){ |
| 2212 | Stmt stWeek = empty_Stmt; |
| 2213 | char yearPart[5] = {0,0,0,0,0}; |
| 2214 | memcpy(yearPart, zTimeframe, 4); |
| 2215 | db_prepare(&stWeek, |
| 2216 | "SELECT DISTINCT strftime('%%W',mtime) AS wk, " |
| 2217 | "count(*) AS n, " |
| 2218 | "substr(date(mtime),1,%d) AS ym " |
| 2219 | "FROM v_reports " |
| 2220 | "WHERE ym=%Q AND mtime < current_timestamp " |
| 2221 | "GROUP BY wk ORDER BY wk", |
| 2222 | strlen(zTimeframe), |
| 2223 | zTimeframe); |
| 2224 | while( SQLITE_ROW == db_step(&stWeek) ){ |
| 2225 | const char *zWeek = db_column_text(&stWeek,0); |
| 2226 | const int nCount = db_column_int(&stWeek,1); |
| 2227 | cgi_printf("<a href='%s/timeline?" |
| 2228 | "yw=%t-%t&n=%d&y=%s'>%s</a>", |
| 2229 | g.zTop, yearPart, zWeek, |
| 2230 | nCount, statsReportTimelineYFlag, zWeek); |
| 2231 | } |
| 2232 | db_finalize(&stWeek); |
| 2233 | } |
| 2234 | |
| 2235 | /* |
| 2236 | ** Implements the "byyear" and "bymonth" reports for /reports. |
| 2237 | ** If includeMonth is true then it generates the "bymonth" report, |
| 2238 | ** else the "byyear" report. If zUserName is not NULL and not empty |
| 2239 | ** then the report is restricted to events created by the named user |
| 2240 | ** account. |
| 2241 | */ |
| 2242 | static void stats_report_by_month_year(char includeMonth, |
| 2243 | char includeWeeks, |
| 2244 | const char *zUserName){ |
| 2245 | Stmt query = empty_Stmt; |
| 2246 | int nRowNumber = 0; /* current TR number */ |
| 2247 | int nEventTotal = 0; /* Total event count */ |
| 2248 | int rowClass = 0; /* counter for alternating |
| 2249 | row colors */ |
| 2250 | Blob sql = empty_blob; /* SQL */ |
| 2251 | const char *zTimeLabel = includeMonth ? "Year/Month" : "Year"; |
| 2252 | char zPrevYear[5] = {0}; /* For keeping track of when |
| 2253 | we change years while looping */ |
| 2254 | int nEventsPerYear = 0; /* Total event count for the |
| 2255 | current year */ |
| 2256 | char showYearTotal = 0; /* Flag telling us when to show |
| 2257 | the per-year event totals */ |
| 2258 | Blob header = empty_blob; /* Page header text */ |
| 2259 | int nMaxEvents = 1; /* for calculating length of graph |
| 2260 | bars. */ |
| 2261 | int iterations = 0; /* number of weeks/months we iterate |
| 2262 | over */ |
| 2263 | stats_report_init_view(); |
| 2264 | stats_report_event_types_menu( includeMonth ? "bymonth" : "byyear", NULL ); |
| 2265 | blob_appendf(&header, "Timeline Events (%s) by year%s", |
| 2266 | stats_report_label_for_type(), |
| 2267 | (includeMonth ? "/month" : "")); |
| 2268 | blob_append_sql(&sql, |
| 2269 | "SELECT substr(date(mtime),1,%d) AS timeframe, " |
| 2270 | "count(*) AS eventCount " |
| 2271 | "FROM v_reports ", |
| 2272 | includeMonth ? 7 : 4); |
| 2273 | if(zUserName&&*zUserName){ |
| 2274 | blob_append_sql(&sql, " WHERE user=%Q ", zUserName); |
| 2275 | blob_appendf(&header," for user %q", zUserName); |
| 2276 | } |
| 2277 | blob_append(&sql, |
| 2278 | " GROUP BY timeframe" |
| 2279 | " ORDER BY timeframe DESC", |
| 2280 | -1); |
| 2281 | db_prepare(&query, "%s", blob_sql_text(&sql)); |
| 2282 | blob_reset(&sql); |
| 2283 | @ <h1>%b(&header)</h1> |
| 2284 | @ <table class='statistics-report-table-events' border='0' cellpadding='2' |
| 2285 | @ cellspacing='0' id='statsTable'> |
| 2286 | @ <thead> |
| 2287 | @ <th>%s(zTimeLabel)</th> |
| 2288 | @ <th>Events</th> |
| 2289 | @ <th width='90%%'><!-- relative commits graph --></th> |
| 2290 | @ </thead><tbody> |
| 2291 | blob_reset(&header); |
| 2292 | /* |
| 2293 | Run the query twice. The first time we calculate the maximum |
| 2294 | number of events for a given row. Maybe someone with better SQL |
| 2295 | Fu can re-implement this with a single query. |
| 2296 | */ |
| 2297 | while( SQLITE_ROW == db_step(&query) ){ |
| 2298 | const int nCount = db_column_int(&query, 1); |
| 2299 | if(nCount>nMaxEvents){ |
| 2300 | nMaxEvents = nCount; |
| 2301 | } |
| 2302 | ++iterations; |
| 2303 | } |
| 2304 | db_reset(&query); |
| 2305 | while( SQLITE_ROW == db_step(&query) ){ |
| 2306 | const char *zTimeframe = db_column_text(&query, 0); |
| 2307 | const int nCount = db_column_int(&query, 1); |
| 2308 | int nSize = nCount |
| 2309 | ? (int)(100 * nCount / nMaxEvents) |
| 2310 | : 1; |
| 2311 | showYearTotal = 0; |
| 2312 | if(!nSize) nSize = 1; |
| 2313 | if(includeMonth){ |
| 2314 | /* For Month/year view, add a separator for each distinct year. */ |
| 2315 | if(!*zPrevYear || |
| 2316 | (0!=fossil_strncmp(zPrevYear,zTimeframe,4))){ |
| 2317 | showYearTotal = *zPrevYear; |
| 2318 | if(showYearTotal){ |
| 2319 | rowClass = ++nRowNumber % 2; |
| 2320 | @ <tr class='row%d(rowClass)'> |
| 2321 | @ <td></td> |
| 2322 | @ <td colspan='2'>Yearly total: %d(nEventsPerYear)</td> |
| 2323 | @</tr> |
| 2324 | } |
| 2325 | nEventsPerYear = 0; |
| 2326 | memcpy(zPrevYear,zTimeframe,4); |
| 2327 | rowClass = ++nRowNumber % 2; |
| 2328 | @ <tr class='row%d(rowClass)'> |
| 2329 | @ <th colspan='3' class='statistics-report-row-year'>%s(zPrevYear)</th> |
| 2330 | @ </tr> |
| 2331 | } |
| 2332 | } |
| 2333 | rowClass = ++nRowNumber % 2; |
| 2334 | nEventTotal += nCount; |
| 2335 | nEventsPerYear += nCount; |
| 2336 | @<tr class='row%d(rowClass)'> |
| 2337 | @ <td> |
| 2338 | if(includeMonth){ |
| 2339 | cgi_printf("<a href='%s/timeline?" |
| 2340 | "ym=%t&n=%d&y=%s", |
| 2341 | g.zTop, zTimeframe, nCount, |
| 2342 | statsReportTimelineYFlag ); |
| 2343 | /* Reminder: n=nCount is not actually correct for bymonth unless |
| 2344 | that was the only user who caused events. |
| 2345 | */ |
| 2346 | if( zUserName && *zUserName ){ |
| 2347 | cgi_printf("&u=%t", zUserName); |
| 2348 | } |
| 2349 | cgi_printf("' target='_new'>%s</a>",zTimeframe); |
| 2350 | }else { |
| 2351 | cgi_printf("<a href='?view=byweek&y=%s&type=%c", |
| 2352 | zTimeframe, (char)statsReportType); |
| 2353 | if(zUserName && *zUserName){ |
| 2354 | cgi_printf("&u=%t", zUserName); |
| 2355 | } |
| 2356 | cgi_printf("'>%s</a>", zTimeframe); |
| 2357 | } |
| 2358 | @ </td><td>%d(nCount)</td> |
| 2359 | @ <td> |
| 2360 | @ <div class='statistics-report-graph-line' |
| 2361 | @ style='width:%d(nSize)%%;'> </div> |
| 2362 | @ </td> |
| 2363 | @</tr> |
| 2364 | if(includeWeeks){ |
| 2365 | /* This part works fine for months but it terribly slow (4.5s on my PC), |
| 2366 | so it's only shown for by-year for now. Suggestions/patches for |
| 2367 | a better/faster layout are welcomed. */ |
| 2368 | @ <tr class='row%d(rowClass)'> |
| 2369 | @ <td colspan='2' class='statistics-report-week-number-label'>Week #:</td> |
| 2370 | @ <td class='statistics-report-week-of-year-list'> |
| 2371 | stats_report_output_week_links(zTimeframe); |
| 2372 | @ </td></tr> |
| 2373 | } |
| 2374 | |
| 2375 | /* |
| 2376 | Potential improvement: calculate the min/max event counts and |
| 2377 | use percent-based graph bars. |
| 2378 | */ |
| 2379 | } |
| 2380 | db_finalize(&query); |
| 2381 | if(includeMonth && !showYearTotal && *zPrevYear){ |
| 2382 | /* Add final year total separator. */ |
| 2383 | rowClass = ++nRowNumber % 2; |
| 2384 | @ <tr class='row%d(rowClass)'> |
| 2385 | @ <td></td> |
| 2386 | @ <td colspan='2'>Yearly total: %d(nEventsPerYear)</td> |
| 2387 | @</tr> |
| 2388 | } |
| 2389 | @ </tbody></table> |
| 2390 | if(nEventTotal){ |
| 2391 | const char *zAvgLabel = includeMonth ? "month" : "year"; |
| 2392 | int nAvg = iterations ? (nEventTotal/iterations) : 0; |
| 2393 | @ <br><div>Total events: %d(nEventTotal) |
| 2394 | @ <br>Average per active %s(zAvgLabel): %d(nAvg) |
| 2395 | @ </div> |
| 2396 | } |
| 2397 | if( !includeMonth ){ |
| 2398 | output_table_sorting_javascript("statsTable","tnx"); |
| 2399 | } |
| 2400 | } |
| 2401 | |
| 2402 | /* |
| 2403 | ** Implements the "byuser" view for /reports. |
| 2404 | */ |
| 2405 | static void stats_report_by_user(){ |
| 2406 | Stmt query = empty_Stmt; |
| 2407 | int nRowNumber = 0; /* current TR number */ |
| 2408 | int nEventTotal = 0; /* Total event count */ |
| 2409 | int rowClass = 0; /* counter for alternating |
| 2410 | row colors */ |
| 2411 | int nMaxEvents = 1; /* max number of events for |
| 2412 | all rows. */ |
| 2413 | stats_report_init_view(); |
| 2414 | stats_report_event_types_menu("byuser", NULL); |
| 2415 | db_prepare(&query, |
| 2416 | "SELECT user, " |
| 2417 | "COUNT(*) AS eventCount " |
| 2418 | "FROM v_reports " |
| 2419 | "GROUP BY user ORDER BY eventCount DESC"); |
| 2420 | @ <h1>Timeline Events |
| 2421 | @ (%s(stats_report_label_for_type())) by User</h1> |
| 2422 | @ <table class='statistics-report-table-events' border='0' |
| 2423 | @ cellpadding='2' cellspacing='0' id='statsTable'> |
| 2424 | @ <thead><tr> |
| 2425 | @ <th>User</th> |
| 2426 | @ <th>Events</th> |
| 2427 | @ <th width='90%%'><!-- relative commits graph --></th> |
| 2428 | @ </tr></thead><tbody> |
| 2429 | while( SQLITE_ROW == db_step(&query) ){ |
| 2430 | const int nCount = db_column_int(&query, 1); |
| 2431 | if(nCount>nMaxEvents){ |
| 2432 | nMaxEvents = nCount; |
| 2433 | } |
| 2434 | } |
| 2435 | db_reset(&query); |
| 2436 | while( SQLITE_ROW == db_step(&query) ){ |
| 2437 | const char *zUser = db_column_text(&query, 0); |
| 2438 | const int nCount = db_column_int(&query, 1); |
| 2439 | int nSize = nCount |
| 2440 | ? (int)(100 * nCount / nMaxEvents) |
| 2441 | : 0; |
| 2442 | if(!nCount) continue /* arguable! Possible? */; |
| 2443 | else if(!nSize) nSize = 1; |
| 2444 | rowClass = ++nRowNumber % 2; |
| 2445 | nEventTotal += nCount; |
| 2446 | @<tr class='row%d(rowClass)'> |
| 2447 | @ <td> |
| 2448 | @ <a href="?view=bymonth&user=%h(zUser)&type=%c((char)statsReportType)">%h(zUser)</a> |
| 2449 | @ </td><td>%d(nCount)</td> |
| 2450 | @ <td> |
| 2451 | @ <div class='statistics-report-graph-line' |
| 2452 | @ style='width:%d(nSize)%%;'> </div> |
| 2453 | @ </td> |
| 2454 | @</tr> |
| 2455 | /* |
| 2456 | Potential improvement: calculate the min/max event counts and |
| 2457 | use percent-based graph bars. |
| 2458 | */ |
| 2459 | } |
| 2460 | @ </tbody></table> |
| 2461 | db_finalize(&query); |
| 2462 | output_table_sorting_javascript("statsTable","tnx"); |
| 2463 | } |
| 2464 | |
| 2465 | /* |
| 2466 | ** Implements the "byweekday" view for /reports. |
| 2467 | */ |
| 2468 | static void stats_report_day_of_week(){ |
| 2469 | Stmt query = empty_Stmt; |
| 2470 | int nRowNumber = 0; /* current TR number */ |
| 2471 | int nEventTotal = 0; /* Total event count */ |
| 2472 | int rowClass = 0; /* counter for alternating |
| 2473 | row colors */ |
| 2474 | int nMaxEvents = 1; /* max number of events for |
| 2475 | all rows. */ |
| 2476 | static const char *const daysOfWeek[] = { |
| 2477 | "Monday", "Tuesday", "Wednesday", "Thursday", |
| 2478 | "Friday", "Saturday", "Sunday" |
| 2479 | }; |
| 2480 | |
| 2481 | stats_report_init_view(); |
| 2482 | stats_report_event_types_menu("byweekday", NULL); |
| 2483 | db_prepare(&query, |
| 2484 | "SELECT cast(mtime %% 7 AS INTEGER) dow, " |
| 2485 | "COUNT(*) AS eventCount " |
| 2486 | "FROM v_reports " |
| 2487 | "GROUP BY dow ORDER BY dow"); |
| 2488 | @ <h1>Timeline Events |
| 2489 | @ (%s(stats_report_label_for_type())) by Day of the Week</h1> |
| 2490 | @ <table class='statistics-report-table-events' border='0' |
| 2491 | @ cellpadding='2' cellspacing='0' id='statsTable'> |
| 2492 | @ <thead><tr> |
| 2493 | @ <th>DoW</th> |
| 2494 | @ <th>Day</th> |
| 2495 | @ <th>Events</th> |
| 2496 | @ <th width='90%%'><!-- relative commits graph --></th> |
| 2497 | @ </tr></thead><tbody> |
| 2498 | while( SQLITE_ROW == db_step(&query) ){ |
| 2499 | const int nCount = db_column_int(&query, 1); |
| 2500 | if(nCount>nMaxEvents){ |
| 2501 | nMaxEvents = nCount; |
| 2502 | } |
| 2503 | } |
| 2504 | db_reset(&query); |
| 2505 | while( SQLITE_ROW == db_step(&query) ){ |
| 2506 | const int dayNum =db_column_int(&query, 0); |
| 2507 | const int nCount = db_column_int(&query, 1); |
| 2508 | int nSize = nCount |
| 2509 | ? (int)(100 * nCount / nMaxEvents) |
| 2510 | : 0; |
| 2511 | if(!nCount) continue /* arguable! Possible? */; |
| 2512 | else if(!nSize) nSize = 1; |
| 2513 | rowClass = ++nRowNumber % 2; |
| 2514 | nEventTotal += nCount; |
| 2515 | @<tr class='row%d(rowClass)'> |
| 2516 | @ <td>%d(dayNum)</td> |
| 2517 | @ <td>%s(daysOfWeek[dayNum])</td> |
| 2518 | @ <td>%d(nCount)</td> |
| 2519 | @ <td> |
| 2520 | @ <div class='statistics-report-graph-line' |
| 2521 | @ style='width:%d(nSize)%%;'> </div> |
| 2522 | @ </td> |
| 2523 | @</tr> |
| 2524 | } |
| 2525 | @ </tbody></table> |
| 2526 | db_finalize(&query); |
| 2527 | output_table_sorting_javascript("statsTable","ntnx"); |
| 2528 | } |
| 2529 | |
| 2530 | |
| 2531 | /* |
| 2532 | ** Helper for stats_report_by_month_year(), which generates a list of |
| 2533 | ** week numbers. zTimeframe should be either a timeframe in the form YYYY |
| 2534 | ** or YYYY-MM. |
| 2535 | */ |
| 2536 | static void stats_report_year_weeks(const char *zUserName){ |
| 2537 | const char *zYear = P("y"); |
| 2538 | int nYear = zYear ? strlen(zYear) : 0; |
| 2539 | int i = 0; |
| 2540 | Stmt qYears = empty_Stmt; |
| 2541 | char *zDefaultYear = NULL; |
| 2542 | Blob sql = empty_blob; |
| 2543 | int nMaxEvents = 1; /* max number of events for |
| 2544 | all rows. */ |
| 2545 | int iterations = 0; /* # of active time periods. */ |
| 2546 | stats_report_init_view(); |
| 2547 | if(4==nYear){ |
| 2548 | Blob urlParams = empty_blob; |
| 2549 | blob_appendf(&urlParams, "y=%T", zYear); |
| 2550 | stats_report_event_types_menu("byweek", blob_str(&urlParams)); |
| 2551 | blob_reset(&urlParams); |
| 2552 | }else{ |
| 2553 | stats_report_event_types_menu("byweek", NULL); |
| 2554 | } |
| 2555 | blob_append(&sql, |
| 2556 | "SELECT DISTINCT substr(date(mtime),1,4) AS y " |
| 2557 | "FROM v_reports WHERE 1 ", -1); |
| 2558 | if(zUserName&&*zUserName){ |
| 2559 | blob_append_sql(&sql,"AND user=%Q ", zUserName); |
| 2560 | } |
| 2561 | blob_append(&sql,"GROUP BY y ORDER BY y", -1); |
| 2562 | db_prepare(&qYears, "%s", blob_sql_text(&sql)); |
| 2563 | blob_reset(&sql); |
| 2564 | cgi_printf("Select year: "); |
| 2565 | while( SQLITE_ROW == db_step(&qYears) ){ |
| 2566 | const char *zT = db_column_text(&qYears, 0); |
| 2567 | if( i++ ){ |
| 2568 | cgi_printf(" "); |
| 2569 | } |
| 2570 | cgi_printf("<a href='?view=byweek&y=%s&type=%c", zT, |
| 2571 | (char)statsReportType); |
| 2572 | if(zUserName && *zUserName){ |
| 2573 | cgi_printf("&user=%t",zUserName); |
| 2574 | } |
| 2575 | cgi_printf("'>%s</a>",zT); |
| 2576 | } |
| 2577 | db_finalize(&qYears); |
| 2578 | cgi_printf("<br/>"); |
| 2579 | if(!zYear || !*zYear){ |
| 2580 | zDefaultYear = db_text("????", "SELECT strftime('%%Y')"); |
| 2581 | zYear = zDefaultYear; |
| 2582 | nYear = 4; |
| 2583 | } |
| 2584 | if(4 == nYear){ |
| 2585 | Stmt stWeek = empty_Stmt; |
| 2586 | int rowCount = 0; |
| 2587 | int total = 0; |
| 2588 | Blob header = empty_blob; |
| 2589 | blob_appendf(&header, "Timeline events (%s) for the calendar weeks " |
| 2590 | "of %h", stats_report_label_for_type(), |
| 2591 | zYear); |
| 2592 | blob_append_sql(&sql, |
| 2593 | "SELECT DISTINCT strftime('%%W',mtime) AS wk, " |
| 2594 | "count(*) AS n " |
| 2595 | "FROM v_reports " |
| 2596 | "WHERE %Q=substr(date(mtime),1,4) " |
| 2597 | "AND mtime < current_timestamp ", |
| 2598 | zYear); |
| 2599 | if(zUserName&&*zUserName){ |
| 2600 | blob_append_sql(&sql, " AND user=%Q ", zUserName); |
| 2601 | blob_appendf(&header," for user %h", zUserName); |
| 2602 | } |
| 2603 | blob_append_sql(&sql, "GROUP BY wk ORDER BY wk DESC"); |
| 2604 | cgi_printf("<h1>%h</h1>", blob_str(&header)); |
| 2605 | blob_reset(&header); |
| 2606 | cgi_printf("<table class='statistics-report-table-events' " |
| 2607 | "border='0' cellpadding='2' width='100%%' " |
| 2608 | "cellspacing='0' id='statsTable'>"); |
| 2609 | cgi_printf("<thead><tr>" |
| 2610 | "<th>Week</th>" |
| 2611 | "<th>Events</th>" |
| 2612 | "<th width='90%%'><!-- relative commits graph --></th>" |
| 2613 | "</tr></thead>" |
| 2614 | "<tbody>"); |
| 2615 | db_prepare(&stWeek, "%s", blob_sql_text(&sql)); |
| 2616 | blob_reset(&sql); |
| 2617 | while( SQLITE_ROW == db_step(&stWeek) ){ |
| 2618 | const int nCount = db_column_int(&stWeek, 1); |
| 2619 | if(nCount>nMaxEvents){ |
| 2620 | nMaxEvents = nCount; |
| 2621 | } |
| 2622 | ++iterations; |
| 2623 | } |
| 2624 | db_reset(&stWeek); |
| 2625 | while( SQLITE_ROW == db_step(&stWeek) ){ |
| 2626 | const char *zWeek = db_column_text(&stWeek,0); |
| 2627 | const int nCount = db_column_int(&stWeek,1); |
| 2628 | int nSize = nCount |
| 2629 | ? (int)(100 * nCount / nMaxEvents) |
| 2630 | : 0; |
| 2631 | if(!nSize) nSize = 1; |
| 2632 | total += nCount; |
| 2633 | cgi_printf("<tr class='row%d'>", ++rowCount % 2 ); |
| 2634 | cgi_printf("<td><a href='%s/timeline?yw=%t-%s&n=%d&y=%s", |
| 2635 | g.zTop, zYear, zWeek, nCount, |
| 2636 | statsReportTimelineYFlag); |
| 2637 | if(zUserName && *zUserName){ |
| 2638 | cgi_printf("&u=%t",zUserName); |
| 2639 | } |
| 2640 | cgi_printf("'>%s</a></td>",zWeek); |
| 2641 | |
| 2642 | cgi_printf("<td>%d</td>",nCount); |
| 2643 | cgi_printf("<td>"); |
| 2644 | if(nCount){ |
| 2645 | cgi_printf("<div class='statistics-report-graph-line'" |
| 2646 | "style='width:%d%%;'> </div>", |
| 2647 | nSize); |
| 2648 | } |
| 2649 | cgi_printf("</td></tr>"); |
| 2650 | } |
| 2651 | db_finalize(&stWeek); |
| 2652 | free(zDefaultYear); |
| 2653 | cgi_printf("</tbody></table>"); |
| 2654 | if(total){ |
| 2655 | int nAvg = iterations ? (total/iterations) : 0; |
| 2656 | cgi_printf("<br><div>Total events: %d<br>" |
| 2657 | "Average per active week: %d</div>", |
| 2658 | total, nAvg); |
| 2659 | } |
| 2660 | output_table_sorting_javascript("statsTable","tnx"); |
| 2661 | } |
| 2662 | } |
| 2663 | |
| 2664 | /* |
| 2665 | ** WEBPAGE: reports |
| 2666 | ** |
| 2667 | ** Shows activity reports for the repository. |
| 2668 | ** |
| 2669 | ** Query Parameters: |
| 2670 | ** |
| 2671 | ** view=REPORT_NAME Valid values: bymonth, byyear, byuser |
| 2672 | ** user=NAME Restricts statistics to the given user |
| 2673 | ** type=TYPE Restricts the report to a specific event type: |
| 2674 | ** ci (checkin), w (wiki), t (ticket), g (tag) |
| 2675 | ** Defaulting to all event types. |
| 2676 | ** |
| 2677 | ** The view-specific query parameters include: |
| 2678 | ** |
| 2679 | ** view=byweek: |
| 2680 | ** |
| 2681 | ** y=YYYY The year to report (default is the server's |
| 2682 | ** current year). |
| 2683 | */ |
| 2684 | void stats_report_page(){ |
| 2685 | HQuery url; /* URL for various branch links */ |
| 2686 | const char *zView = P("view"); /* Which view/report to show. */ |
| 2687 | const char *zUserName = P("user"); |
| 2688 | |
| 2689 | login_check_credentials(); |
| 2690 | if( !g.perm.Read ){ login_needed(); return; } |
| 2691 | if(!zUserName) zUserName = P("u"); |
| 2692 | url_initialize(&url, "reports"); |
| 2693 | if(zUserName && *zUserName){ |
| 2694 | url_add_parameter(&url,"user", zUserName); |
| 2695 | timeline_submenu(&url, "(Remove User Flag)", "view", zView, "user"); |
| 2696 | } |
| 2697 | timeline_submenu(&url, "By Year", "view", "byyear", 0); |
| 2698 | timeline_submenu(&url, "By Month", "view", "bymonth", 0); |
| 2699 | timeline_submenu(&url, "By Week", "view", "byweek", 0); |
| 2700 | timeline_submenu(&url, "By Weekday", "view", "byweekday", 0); |
| 2701 | timeline_submenu(&url, "By User", "view", "byuser", "user"); |
| 2702 | url_reset(&url); |
| 2703 | style_header("Activity Reports"); |
| 2704 | if(0==fossil_strcmp(zView,"byyear")){ |
| 2705 | stats_report_by_month_year(0, 0, zUserName); |
| 2706 | }else if(0==fossil_strcmp(zView,"bymonth")){ |
| 2707 | stats_report_by_month_year(1, 0, zUserName); |
| 2708 | }else if(0==fossil_strcmp(zView,"byweek")){ |
| 2709 | stats_report_year_weeks(zUserName); |
| 2710 | }else if(0==fossil_strcmp(zView,"byuser")){ |
| 2711 | stats_report_by_user(); |
| 2712 | }else if(0==fossil_strcmp(zView,"byweekday")){ |
| 2713 | stats_report_day_of_week(); |
| 2714 | }else{ |
| 2715 | @ <h1>Select a report to show:</h1> |
| 2716 | @ <ul> |
| 2717 | @ <li><a href='?view=byyear'>Events by year</a></li> |
| 2718 | @ <li><a href='?view=bymonth'>Events by month</a></li> |
| 2719 | @ <li><a href='?view=byweek'>Events by calendar week</a></li> |
| 2720 | @ <li><a href='?view=byweekday'>Events by day of the week</a></li> |
| 2721 | @ <li><a href='?view=byuser'>Events by user</a></li> |
| 2722 | @ </ul> |
| 2723 | } |
| 2724 | |
| 2725 | style_footer(); |
| 2726 | } |
| 2727 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -87,10 +87,11 @@ | |
| 87 | #define TIMELINE_FCHANGES 0x0020 /* Detail file changes */ |
| 88 | #define TIMELINE_BRCOLOR 0x0040 /* Background color by branch name */ |
| 89 | #define TIMELINE_UCOLOR 0x0080 /* Background color by user */ |
| 90 | #define TIMELINE_FRENAMES 0x0100 /* Detail only file name changes */ |
| 91 | #define TIMELINE_UNHIDE 0x0200 /* Unhide check-ins with "hidden" tag */ |
| 92 | #define TIMELINE_SHOWRID 0x0400 /* Show RID values in addition to UUIDs */ |
| 93 | #endif |
| 94 | |
| 95 | /* |
| 96 | ** Hash a string and use the hash to determine a background color. |
| 97 | */ |
| @@ -359,20 +360,20 @@ | |
| 360 | } |
| 361 | } |
| 362 | } |
| 363 | if( zType[0]=='c' && (pGraph || (tmFlags & TIMELINE_BRCOLOR)!=0) ){ |
| 364 | int nParent = 0; |
| 365 | int aParent[GR_MAX_RAIL]; |
| 366 | int gidx; |
| 367 | static Stmt qparent; |
| 368 | db_static_prepare(&qparent, |
| 369 | "SELECT pid FROM plink" |
| 370 | " WHERE cid=:rid AND pid NOT IN phantom" |
| 371 | " ORDER BY isprim DESC /*sort*/" |
| 372 | ); |
| 373 | db_bind_int(&qparent, ":rid", rid); |
| 374 | while( db_step(&qparent)==SQLITE_ROW && nParent<ArraySize(aParent) ){ |
| 375 | aParent[nParent++] = db_column_int(&qparent, 0); |
| 376 | } |
| 377 | db_reset(&qparent); |
| 378 | gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr, |
| 379 | zUuid, isLeaf); |
| @@ -404,10 +405,13 @@ | |
| 405 | } |
| 406 | }else if( zType[0]=='e' && tagid ){ |
| 407 | hyperlink_to_event_tagid(tagid<0?-tagid:tagid); |
| 408 | }else if( (tmFlags & TIMELINE_ARTID)!=0 ){ |
| 409 | hyperlink_to_uuid(zUuid); |
| 410 | } |
| 411 | if( tmFlags & TIMELINE_SHOWRID ){ |
| 412 | @ (%d(rid)) |
| 413 | } |
| 414 | db_column_blob(pQuery, commentColumn, &comment); |
| 415 | if( zType[0]!='c' ){ |
| 416 | /* Comments for anything other than a check-in are generated by |
| 417 | ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */ |
| @@ -604,12 +608,12 @@ | |
| 608 | ** for the upward portion of a merge arrow. The merge arrow goes up |
| 609 | ** to the row identified by mu:. If this value is zero then |
| 610 | ** node has no merge children and no merge-out line is drawn. |
| 611 | ** mu: The id of the row which is the top of the merge-out arrow. |
| 612 | ** u: Draw a thick child-line out of the top of this node and up to |
| 613 | ** the node with an id equal to this value. 0 if it is straight to |
| 614 | ** the top of the page, -1 if there is no thick-line riser. |
| 615 | ** f: 0x01: a leaf node. |
| 616 | ** au: An array of integers that define thick-line risers for branches. |
| 617 | ** The integers are in pairs. For each pair, the first integer is |
| 618 | ** is the rail on which the riser should run and the second integer |
| 619 | ** is the id of the node upto which the riser should run. |
| @@ -1031,11 +1035,10 @@ | |
| 1035 | ** nd Suppress "divider" lines |
| 1036 | ** v Show details of files changed |
| 1037 | ** f=UUID Show family (immediate parents and children) of UUID |
| 1038 | ** from=UUID Path from... |
| 1039 | ** to=UUID ... to this |
| 1040 | ** shortest ... show only the shortest path |
| 1041 | ** uf=FUUID Show only checkins that use given file version |
| 1042 | ** brbg Background color from branch name |
| 1043 | ** ubg Background color from user |
| 1044 | ** namechng Show only checkins that filename changes |
| @@ -1070,11 +1073,11 @@ | |
| 1073 | const char *zYearMonth = P("ym"); /* Show checkins for the given YYYY-MM */ |
| 1074 | const char *zYearWeek = P("yw"); /* Show checkins for the given YYYY-WW (week-of-year)*/ |
| 1075 | int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */ |
| 1076 | int renameOnly = P("namechng")!=0; /* Show only checkins that rename files */ |
| 1077 | int tagid; /* Tag ID */ |
| 1078 | int tmFlags = 0; /* Timeline flags */ |
| 1079 | const char *zThisTag = 0; /* Suppress links to this tag */ |
| 1080 | const char *zThisUser = 0; /* Suppress links to this user */ |
| 1081 | HQuery url; /* URL for various branch links */ |
| 1082 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| 1083 | int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */ |
| @@ -1110,13 +1113,13 @@ | |
| 1113 | ){ |
| 1114 | zCirca = zBefore = zAfter = 0; |
| 1115 | nEntry = -1; |
| 1116 | } |
| 1117 | if( zType[0]=='a' ){ |
| 1118 | tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH; |
| 1119 | }else{ |
| 1120 | tmFlags |= TIMELINE_GRAPH; |
| 1121 | } |
| 1122 | if( nEntry>0 ) url_add_parameter(&url, "n", mprintf("%d", nEntry)); |
| 1123 | if( P("ng")!=0 || zSearch!=0 ){ |
| 1124 | tmFlags &= ~TIMELINE_GRAPH; |
| 1125 | url_add_parameter(&url, "ng", 0); |
| @@ -1206,10 +1209,11 @@ | |
| 1209 | }else if( (p_rid || d_rid) && g.perm.Read ){ |
| 1210 | /* If p= or d= is present, ignore all other parameters other than n= */ |
| 1211 | char *zUuid; |
| 1212 | int np, nd; |
| 1213 | |
| 1214 | tmFlags |= TIMELINE_DISJOINT; |
| 1215 | if( p_rid && d_rid ){ |
| 1216 | if( p_rid!=d_rid ) p_rid = d_rid; |
| 1217 | if( P("n")==0 ) nEntry = 10; |
| 1218 | } |
| 1219 | db_multi_exec( |
| @@ -1552,10 +1556,11 @@ | |
| 1556 | } |
| 1557 | } |
| 1558 | if( P("showsql") ){ |
| 1559 | @ <blockquote>%h(blob_sql_text(&sql))</blockquote> |
| 1560 | } |
| 1561 | if( P("showid") ) tmFlags |= TIMELINE_SHOWRID; |
| 1562 | blob_zero(&sql); |
| 1563 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/"); |
| 1564 | @ <h2>%b(&desc)</h2> |
| 1565 | blob_reset(&desc); |
| 1566 | www_print_timeline(&q, tmFlags, zThisUser, zThisTag, 0); |
| @@ -2039,688 +2044,7 @@ | |
| 2044 | const char *zUuid = db_column_text(&q, 0); |
| 2045 | @ <li> |
| 2046 | @ <a href="%s(g.zTop)/timeline?p=%s(zUuid)&d=%s(zUuid)&unhide">%S(zUuid)</a> |
| 2047 | } |
| 2048 | db_finalize(&q); |
| 2049 | style_footer(); |
| 2050 | } |
| 2051 |
+1
-1
| --- src/translate.c | ||
| +++ src/translate.c | ||
| @@ -42,11 +42,11 @@ | ||
| 42 | 42 | ** |
| 43 | 43 | ** Enhancement #2: |
| 44 | 44 | ** |
| 45 | 45 | ** Comments of the form: "/* @-comment: CC" cause CC to become a |
| 46 | 46 | ** comment character for the @-substitution. Typical values for CC are |
| 47 | -** "--" (for SQL text) or "#" (for TCL script) or "//" (for C++ code). | |
| 47 | +** "--" (for SQL text) or "#" (for Tcl script) or "//" (for C++ code). | |
| 48 | 48 | ** Lines of subsequent @-blocks that begin with CC are omitted from the |
| 49 | 49 | ** output. |
| 50 | 50 | ** |
| 51 | 51 | */ |
| 52 | 52 | #include <stdio.h> |
| 53 | 53 |
| --- src/translate.c | |
| +++ src/translate.c | |
| @@ -42,11 +42,11 @@ | |
| 42 | ** |
| 43 | ** Enhancement #2: |
| 44 | ** |
| 45 | ** Comments of the form: "/* @-comment: CC" cause CC to become a |
| 46 | ** comment character for the @-substitution. Typical values for CC are |
| 47 | ** "--" (for SQL text) or "#" (for TCL script) or "//" (for C++ code). |
| 48 | ** Lines of subsequent @-blocks that begin with CC are omitted from the |
| 49 | ** output. |
| 50 | ** |
| 51 | */ |
| 52 | #include <stdio.h> |
| 53 |
| --- src/translate.c | |
| +++ src/translate.c | |
| @@ -42,11 +42,11 @@ | |
| 42 | ** |
| 43 | ** Enhancement #2: |
| 44 | ** |
| 45 | ** Comments of the form: "/* @-comment: CC" cause CC to become a |
| 46 | ** comment character for the @-substitution. Typical values for CC are |
| 47 | ** "--" (for SQL text) or "#" (for Tcl script) or "//" (for C++ code). |
| 48 | ** Lines of subsequent @-blocks that begin with CC are omitted from the |
| 49 | ** output. |
| 50 | ** |
| 51 | */ |
| 52 | #include <stdio.h> |
| 53 |
+17
-17
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -34,11 +34,11 @@ | ||
| 34 | 34 | */ |
| 35 | 35 | static int internalUpdate = 0; |
| 36 | 36 | static int internalConflictCnt = 0; |
| 37 | 37 | |
| 38 | 38 | /* |
| 39 | -** Do an update to version vid. | |
| 39 | +** Do an update to version vid. | |
| 40 | 40 | ** |
| 41 | 41 | ** Start an undo session but do not terminate it. Do not autosync. |
| 42 | 42 | */ |
| 43 | 43 | int update_to(int vid){ |
| 44 | 44 | int savedArgc; |
| @@ -157,11 +157,11 @@ | ||
| 157 | 157 | if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, |
| 158 | 158 | db_get_int("autosync-tries", 1)) ){ |
| 159 | 159 | fossil_fatal("Cannot proceed with update"); |
| 160 | 160 | } |
| 161 | 161 | } |
| 162 | - | |
| 162 | + | |
| 163 | 163 | /* Create any empty directories now, as well as after the update, |
| 164 | 164 | ** so changes in settings are reflected now */ |
| 165 | 165 | if( !dryRunFlag ) ensure_empty_dirs_created(); |
| 166 | 166 | |
| 167 | 167 | if( internalUpdate ){ |
| @@ -182,11 +182,11 @@ | ||
| 182 | 182 | }else if( !is_a_version(tid) ){ |
| 183 | 183 | fossil_fatal("no such version: %s", g.argv[2]); |
| 184 | 184 | } |
| 185 | 185 | } |
| 186 | 186 | } |
| 187 | - | |
| 187 | + | |
| 188 | 188 | /* If no VERSION is specified on the command-line, then look for a |
| 189 | 189 | ** descendent of the current version. If there are multiple descendants, |
| 190 | 190 | ** look for one from the same branch as the current version. If there |
| 191 | 191 | ** are still multiple descendants, show them all and refuse to update |
| 192 | 192 | ** until the user selects one. |
| @@ -207,11 +207,11 @@ | ||
| 207 | 207 | " WHERE tagid=%d AND rid=%d))", |
| 208 | 208 | TAG_BRANCH, TAG_BRANCH, vid |
| 209 | 209 | ); |
| 210 | 210 | if( db_int(0, "SELECT count(*) FROM leaves")>1 ){ |
| 211 | 211 | compute_leaves(vid, closeCode); |
| 212 | - db_prepare(&q, | |
| 212 | + db_prepare(&q, | |
| 213 | 213 | "%s " |
| 214 | 214 | " AND event.objid IN leaves" |
| 215 | 215 | " ORDER BY event.mtime DESC", |
| 216 | 216 | timeline_query_for_tty() |
| 217 | 217 | ); |
| @@ -220,11 +220,11 @@ | ||
| 220 | 220 | fossil_fatal("Multiple descendants"); |
| 221 | 221 | } |
| 222 | 222 | } |
| 223 | 223 | tid = db_int(0, "SELECT rid FROM leaves, event" |
| 224 | 224 | " WHERE event.objid=leaves.rid" |
| 225 | - " ORDER BY event.mtime DESC"); | |
| 225 | + " ORDER BY event.mtime DESC"); | |
| 226 | 226 | if( tid==0 ) tid = vid; |
| 227 | 227 | } |
| 228 | 228 | |
| 229 | 229 | if( tid==0 ){ |
| 230 | 230 | return; |
| @@ -357,11 +357,11 @@ | ||
| 357 | 357 | zSep = ""; |
| 358 | 358 | for(i=3; i<g.argc; i++){ |
| 359 | 359 | file_tree_name(g.argv[i], &treename, 1); |
| 360 | 360 | if( file_wd_isdir(g.argv[i])==1 ){ |
| 361 | 361 | if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ){ |
| 362 | - blob_append_sql(&sql, "%sfn NOT GLOB '%q/*' ", | |
| 362 | + blob_append_sql(&sql, "%sfn NOT GLOB '%q/*' ", | |
| 363 | 363 | zSep /*safe-for-%s*/, blob_str(&treename)); |
| 364 | 364 | }else{ |
| 365 | 365 | blob_reset(&sql); |
| 366 | 366 | break; |
| 367 | 367 | } |
| @@ -378,11 +378,11 @@ | ||
| 378 | 378 | |
| 379 | 379 | /* |
| 380 | 380 | ** Alter the content of the checkout so that it conforms with the |
| 381 | 381 | ** target |
| 382 | 382 | */ |
| 383 | - db_prepare(&q, | |
| 383 | + db_prepare(&q, | |
| 384 | 384 | "SELECT fn, idv, ridv, idt, ridt, chnged, fnt," |
| 385 | 385 | " isexe, islinkv, islinkt, deleted FROM fv ORDER BY 1" |
| 386 | 386 | ); |
| 387 | 387 | db_prepare(&mtimeXfer, |
| 388 | 388 | "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)" |
| @@ -470,11 +470,11 @@ | ||
| 470 | 470 | }else{ |
| 471 | 471 | fossil_print("MERGE %s\n", zName); |
| 472 | 472 | } |
| 473 | 473 | if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){ |
| 474 | 474 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 475 | - nConflict++; | |
| 475 | + nConflict++; | |
| 476 | 476 | }else{ |
| 477 | 477 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 478 | 478 | undo_save(zName); |
| 479 | 479 | content_get(ridt, &t); |
| 480 | 480 | content_get(ridv, &v); |
| @@ -544,11 +544,11 @@ | ||
| 544 | 544 | fossil_warning("uncommitted %s against %S.", |
| 545 | 545 | zLabel, db_column_text(&q, 0)); |
| 546 | 546 | nMerge++; |
| 547 | 547 | } |
| 548 | 548 | db_finalize(&q); |
| 549 | - | |
| 549 | + | |
| 550 | 550 | if( nConflict ){ |
| 551 | 551 | if( internalUpdate ){ |
| 552 | 552 | internalConflictCnt = nConflict; |
| 553 | 553 | nConflict = 0; |
| 554 | 554 | }else{ |
| @@ -561,11 +561,11 @@ | ||
| 561 | 561 | } |
| 562 | 562 | if( nMerge ){ |
| 563 | 563 | fossil_warning("WARNING: %d uncommitted prior merges", nMerge); |
| 564 | 564 | } |
| 565 | 565 | } |
| 566 | - | |
| 566 | + | |
| 567 | 567 | /* |
| 568 | 568 | ** Clean up the mid and pid VFILE entries. Then commit the changes. |
| 569 | 569 | */ |
| 570 | 570 | if( dryRunFlag ){ |
| 571 | 571 | db_end_transaction(1); /* With --dry-run, rollback changes */ |
| @@ -615,27 +615,27 @@ | ||
| 615 | 615 | Blob path; |
| 616 | 616 | const char *zPath; |
| 617 | 617 | |
| 618 | 618 | blob_zero(&path); |
| 619 | 619 | blob_appendf(&path, "%s/%s", g.zLocalRoot, zDir); |
| 620 | - zPath = blob_str(&path); | |
| 620 | + zPath = blob_str(&path); | |
| 621 | 621 | /* Handle various cases of existence of the directory */ |
| 622 | 622 | switch( file_wd_isdir(zPath) ){ |
| 623 | 623 | case 0: { /* doesn't exist */ |
| 624 | 624 | if( file_mkdir(zPath, 0)!=0 ) { |
| 625 | 625 | fossil_warning("couldn't create directory %s as " |
| 626 | 626 | "required by empty-dirs setting", zDir); |
| 627 | - } | |
| 627 | + } | |
| 628 | 628 | break; |
| 629 | 629 | } |
| 630 | 630 | case 1: { /* exists, and is a directory */ |
| 631 | 631 | /* do nothing - required directory exists already */ |
| 632 | 632 | break; |
| 633 | 633 | } |
| 634 | 634 | case 2: { /* exists, but isn't a directory */ |
| 635 | 635 | fossil_warning("file %s found, but a directory is required " |
| 636 | - "by empty-dirs setting", zDir); | |
| 636 | + "by empty-dirs setting", zDir); | |
| 637 | 637 | } |
| 638 | 638 | } |
| 639 | 639 | blob_reset(&path); |
| 640 | 640 | } |
| 641 | 641 | } |
| @@ -656,11 +656,11 @@ | ||
| 656 | 656 | int errCode /* Error code if file not found. Panic if 0. */ |
| 657 | 657 | ){ |
| 658 | 658 | Manifest *pManifest; |
| 659 | 659 | ManifestFile *pFile; |
| 660 | 660 | int rid=0; |
| 661 | - | |
| 661 | + | |
| 662 | 662 | if( revision ){ |
| 663 | 663 | rid = name_to_typed_rid(revision,"ci"); |
| 664 | 664 | }else if( !g.localOpen ){ |
| 665 | 665 | rid = name_to_typed_rid(db_get("main-branch","trunk"),"ci"); |
| 666 | 666 | }else{ |
| @@ -669,11 +669,11 @@ | ||
| 669 | 669 | if( !is_a_version(rid) ){ |
| 670 | 670 | if( errCode>0 ) return errCode; |
| 671 | 671 | fossil_fatal("no such checkin: %s", revision); |
| 672 | 672 | } |
| 673 | 673 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 674 | - | |
| 674 | + | |
| 675 | 675 | if( pManifest ){ |
| 676 | 676 | pFile = manifest_file_find(pManifest, file); |
| 677 | 677 | if( pFile ){ |
| 678 | 678 | int rc; |
| 679 | 679 | rid = uuid_to_rid(pFile->zUuid, 0); |
| @@ -728,14 +728,14 @@ | ||
| 728 | 728 | Blob record; |
| 729 | 729 | int i; |
| 730 | 730 | int errCode; |
| 731 | 731 | Stmt q; |
| 732 | 732 | |
| 733 | - undo_capture_command_line(); | |
| 733 | + undo_capture_command_line(); | |
| 734 | 734 | zRevision = find_option("revision", "r", 1); |
| 735 | 735 | verify_all_options(); |
| 736 | - | |
| 736 | + | |
| 737 | 737 | if( g.argc<2 ){ |
| 738 | 738 | usage("?OPTIONS? [FILE] ..."); |
| 739 | 739 | } |
| 740 | 740 | if( zRevision && g.argc<3 ){ |
| 741 | 741 | fossil_fatal("the --revision option does not work for the entire tree"); |
| 742 | 742 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -34,11 +34,11 @@ | |
| 34 | */ |
| 35 | static int internalUpdate = 0; |
| 36 | static int internalConflictCnt = 0; |
| 37 | |
| 38 | /* |
| 39 | ** Do an update to version vid. |
| 40 | ** |
| 41 | ** Start an undo session but do not terminate it. Do not autosync. |
| 42 | */ |
| 43 | int update_to(int vid){ |
| 44 | int savedArgc; |
| @@ -157,11 +157,11 @@ | |
| 157 | if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, |
| 158 | db_get_int("autosync-tries", 1)) ){ |
| 159 | fossil_fatal("Cannot proceed with update"); |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | /* Create any empty directories now, as well as after the update, |
| 164 | ** so changes in settings are reflected now */ |
| 165 | if( !dryRunFlag ) ensure_empty_dirs_created(); |
| 166 | |
| 167 | if( internalUpdate ){ |
| @@ -182,11 +182,11 @@ | |
| 182 | }else if( !is_a_version(tid) ){ |
| 183 | fossil_fatal("no such version: %s", g.argv[2]); |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /* If no VERSION is specified on the command-line, then look for a |
| 189 | ** descendent of the current version. If there are multiple descendants, |
| 190 | ** look for one from the same branch as the current version. If there |
| 191 | ** are still multiple descendants, show them all and refuse to update |
| 192 | ** until the user selects one. |
| @@ -207,11 +207,11 @@ | |
| 207 | " WHERE tagid=%d AND rid=%d))", |
| 208 | TAG_BRANCH, TAG_BRANCH, vid |
| 209 | ); |
| 210 | if( db_int(0, "SELECT count(*) FROM leaves")>1 ){ |
| 211 | compute_leaves(vid, closeCode); |
| 212 | db_prepare(&q, |
| 213 | "%s " |
| 214 | " AND event.objid IN leaves" |
| 215 | " ORDER BY event.mtime DESC", |
| 216 | timeline_query_for_tty() |
| 217 | ); |
| @@ -220,11 +220,11 @@ | |
| 220 | fossil_fatal("Multiple descendants"); |
| 221 | } |
| 222 | } |
| 223 | tid = db_int(0, "SELECT rid FROM leaves, event" |
| 224 | " WHERE event.objid=leaves.rid" |
| 225 | " ORDER BY event.mtime DESC"); |
| 226 | if( tid==0 ) tid = vid; |
| 227 | } |
| 228 | |
| 229 | if( tid==0 ){ |
| 230 | return; |
| @@ -357,11 +357,11 @@ | |
| 357 | zSep = ""; |
| 358 | for(i=3; i<g.argc; i++){ |
| 359 | file_tree_name(g.argv[i], &treename, 1); |
| 360 | if( file_wd_isdir(g.argv[i])==1 ){ |
| 361 | if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ){ |
| 362 | blob_append_sql(&sql, "%sfn NOT GLOB '%q/*' ", |
| 363 | zSep /*safe-for-%s*/, blob_str(&treename)); |
| 364 | }else{ |
| 365 | blob_reset(&sql); |
| 366 | break; |
| 367 | } |
| @@ -378,11 +378,11 @@ | |
| 378 | |
| 379 | /* |
| 380 | ** Alter the content of the checkout so that it conforms with the |
| 381 | ** target |
| 382 | */ |
| 383 | db_prepare(&q, |
| 384 | "SELECT fn, idv, ridv, idt, ridt, chnged, fnt," |
| 385 | " isexe, islinkv, islinkt, deleted FROM fv ORDER BY 1" |
| 386 | ); |
| 387 | db_prepare(&mtimeXfer, |
| 388 | "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)" |
| @@ -470,11 +470,11 @@ | |
| 470 | }else{ |
| 471 | fossil_print("MERGE %s\n", zName); |
| 472 | } |
| 473 | if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){ |
| 474 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 475 | nConflict++; |
| 476 | }else{ |
| 477 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 478 | undo_save(zName); |
| 479 | content_get(ridt, &t); |
| 480 | content_get(ridv, &v); |
| @@ -544,11 +544,11 @@ | |
| 544 | fossil_warning("uncommitted %s against %S.", |
| 545 | zLabel, db_column_text(&q, 0)); |
| 546 | nMerge++; |
| 547 | } |
| 548 | db_finalize(&q); |
| 549 | |
| 550 | if( nConflict ){ |
| 551 | if( internalUpdate ){ |
| 552 | internalConflictCnt = nConflict; |
| 553 | nConflict = 0; |
| 554 | }else{ |
| @@ -561,11 +561,11 @@ | |
| 561 | } |
| 562 | if( nMerge ){ |
| 563 | fossil_warning("WARNING: %d uncommitted prior merges", nMerge); |
| 564 | } |
| 565 | } |
| 566 | |
| 567 | /* |
| 568 | ** Clean up the mid and pid VFILE entries. Then commit the changes. |
| 569 | */ |
| 570 | if( dryRunFlag ){ |
| 571 | db_end_transaction(1); /* With --dry-run, rollback changes */ |
| @@ -615,27 +615,27 @@ | |
| 615 | Blob path; |
| 616 | const char *zPath; |
| 617 | |
| 618 | blob_zero(&path); |
| 619 | blob_appendf(&path, "%s/%s", g.zLocalRoot, zDir); |
| 620 | zPath = blob_str(&path); |
| 621 | /* Handle various cases of existence of the directory */ |
| 622 | switch( file_wd_isdir(zPath) ){ |
| 623 | case 0: { /* doesn't exist */ |
| 624 | if( file_mkdir(zPath, 0)!=0 ) { |
| 625 | fossil_warning("couldn't create directory %s as " |
| 626 | "required by empty-dirs setting", zDir); |
| 627 | } |
| 628 | break; |
| 629 | } |
| 630 | case 1: { /* exists, and is a directory */ |
| 631 | /* do nothing - required directory exists already */ |
| 632 | break; |
| 633 | } |
| 634 | case 2: { /* exists, but isn't a directory */ |
| 635 | fossil_warning("file %s found, but a directory is required " |
| 636 | "by empty-dirs setting", zDir); |
| 637 | } |
| 638 | } |
| 639 | blob_reset(&path); |
| 640 | } |
| 641 | } |
| @@ -656,11 +656,11 @@ | |
| 656 | int errCode /* Error code if file not found. Panic if 0. */ |
| 657 | ){ |
| 658 | Manifest *pManifest; |
| 659 | ManifestFile *pFile; |
| 660 | int rid=0; |
| 661 | |
| 662 | if( revision ){ |
| 663 | rid = name_to_typed_rid(revision,"ci"); |
| 664 | }else if( !g.localOpen ){ |
| 665 | rid = name_to_typed_rid(db_get("main-branch","trunk"),"ci"); |
| 666 | }else{ |
| @@ -669,11 +669,11 @@ | |
| 669 | if( !is_a_version(rid) ){ |
| 670 | if( errCode>0 ) return errCode; |
| 671 | fossil_fatal("no such checkin: %s", revision); |
| 672 | } |
| 673 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 674 | |
| 675 | if( pManifest ){ |
| 676 | pFile = manifest_file_find(pManifest, file); |
| 677 | if( pFile ){ |
| 678 | int rc; |
| 679 | rid = uuid_to_rid(pFile->zUuid, 0); |
| @@ -728,14 +728,14 @@ | |
| 728 | Blob record; |
| 729 | int i; |
| 730 | int errCode; |
| 731 | Stmt q; |
| 732 | |
| 733 | undo_capture_command_line(); |
| 734 | zRevision = find_option("revision", "r", 1); |
| 735 | verify_all_options(); |
| 736 | |
| 737 | if( g.argc<2 ){ |
| 738 | usage("?OPTIONS? [FILE] ..."); |
| 739 | } |
| 740 | if( zRevision && g.argc<3 ){ |
| 741 | fossil_fatal("the --revision option does not work for the entire tree"); |
| 742 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -34,11 +34,11 @@ | |
| 34 | */ |
| 35 | static int internalUpdate = 0; |
| 36 | static int internalConflictCnt = 0; |
| 37 | |
| 38 | /* |
| 39 | ** Do an update to version vid. |
| 40 | ** |
| 41 | ** Start an undo session but do not terminate it. Do not autosync. |
| 42 | */ |
| 43 | int update_to(int vid){ |
| 44 | int savedArgc; |
| @@ -157,11 +157,11 @@ | |
| 157 | if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, |
| 158 | db_get_int("autosync-tries", 1)) ){ |
| 159 | fossil_fatal("Cannot proceed with update"); |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | /* Create any empty directories now, as well as after the update, |
| 164 | ** so changes in settings are reflected now */ |
| 165 | if( !dryRunFlag ) ensure_empty_dirs_created(); |
| 166 | |
| 167 | if( internalUpdate ){ |
| @@ -182,11 +182,11 @@ | |
| 182 | }else if( !is_a_version(tid) ){ |
| 183 | fossil_fatal("no such version: %s", g.argv[2]); |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /* If no VERSION is specified on the command-line, then look for a |
| 189 | ** descendent of the current version. If there are multiple descendants, |
| 190 | ** look for one from the same branch as the current version. If there |
| 191 | ** are still multiple descendants, show them all and refuse to update |
| 192 | ** until the user selects one. |
| @@ -207,11 +207,11 @@ | |
| 207 | " WHERE tagid=%d AND rid=%d))", |
| 208 | TAG_BRANCH, TAG_BRANCH, vid |
| 209 | ); |
| 210 | if( db_int(0, "SELECT count(*) FROM leaves")>1 ){ |
| 211 | compute_leaves(vid, closeCode); |
| 212 | db_prepare(&q, |
| 213 | "%s " |
| 214 | " AND event.objid IN leaves" |
| 215 | " ORDER BY event.mtime DESC", |
| 216 | timeline_query_for_tty() |
| 217 | ); |
| @@ -220,11 +220,11 @@ | |
| 220 | fossil_fatal("Multiple descendants"); |
| 221 | } |
| 222 | } |
| 223 | tid = db_int(0, "SELECT rid FROM leaves, event" |
| 224 | " WHERE event.objid=leaves.rid" |
| 225 | " ORDER BY event.mtime DESC"); |
| 226 | if( tid==0 ) tid = vid; |
| 227 | } |
| 228 | |
| 229 | if( tid==0 ){ |
| 230 | return; |
| @@ -357,11 +357,11 @@ | |
| 357 | zSep = ""; |
| 358 | for(i=3; i<g.argc; i++){ |
| 359 | file_tree_name(g.argv[i], &treename, 1); |
| 360 | if( file_wd_isdir(g.argv[i])==1 ){ |
| 361 | if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ){ |
| 362 | blob_append_sql(&sql, "%sfn NOT GLOB '%q/*' ", |
| 363 | zSep /*safe-for-%s*/, blob_str(&treename)); |
| 364 | }else{ |
| 365 | blob_reset(&sql); |
| 366 | break; |
| 367 | } |
| @@ -378,11 +378,11 @@ | |
| 378 | |
| 379 | /* |
| 380 | ** Alter the content of the checkout so that it conforms with the |
| 381 | ** target |
| 382 | */ |
| 383 | db_prepare(&q, |
| 384 | "SELECT fn, idv, ridv, idt, ridt, chnged, fnt," |
| 385 | " isexe, islinkv, islinkt, deleted FROM fv ORDER BY 1" |
| 386 | ); |
| 387 | db_prepare(&mtimeXfer, |
| 388 | "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)" |
| @@ -470,11 +470,11 @@ | |
| 470 | }else{ |
| 471 | fossil_print("MERGE %s\n", zName); |
| 472 | } |
| 473 | if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ){ |
| 474 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 475 | nConflict++; |
| 476 | }else{ |
| 477 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 478 | undo_save(zName); |
| 479 | content_get(ridt, &t); |
| 480 | content_get(ridv, &v); |
| @@ -544,11 +544,11 @@ | |
| 544 | fossil_warning("uncommitted %s against %S.", |
| 545 | zLabel, db_column_text(&q, 0)); |
| 546 | nMerge++; |
| 547 | } |
| 548 | db_finalize(&q); |
| 549 | |
| 550 | if( nConflict ){ |
| 551 | if( internalUpdate ){ |
| 552 | internalConflictCnt = nConflict; |
| 553 | nConflict = 0; |
| 554 | }else{ |
| @@ -561,11 +561,11 @@ | |
| 561 | } |
| 562 | if( nMerge ){ |
| 563 | fossil_warning("WARNING: %d uncommitted prior merges", nMerge); |
| 564 | } |
| 565 | } |
| 566 | |
| 567 | /* |
| 568 | ** Clean up the mid and pid VFILE entries. Then commit the changes. |
| 569 | */ |
| 570 | if( dryRunFlag ){ |
| 571 | db_end_transaction(1); /* With --dry-run, rollback changes */ |
| @@ -615,27 +615,27 @@ | |
| 615 | Blob path; |
| 616 | const char *zPath; |
| 617 | |
| 618 | blob_zero(&path); |
| 619 | blob_appendf(&path, "%s/%s", g.zLocalRoot, zDir); |
| 620 | zPath = blob_str(&path); |
| 621 | /* Handle various cases of existence of the directory */ |
| 622 | switch( file_wd_isdir(zPath) ){ |
| 623 | case 0: { /* doesn't exist */ |
| 624 | if( file_mkdir(zPath, 0)!=0 ) { |
| 625 | fossil_warning("couldn't create directory %s as " |
| 626 | "required by empty-dirs setting", zDir); |
| 627 | } |
| 628 | break; |
| 629 | } |
| 630 | case 1: { /* exists, and is a directory */ |
| 631 | /* do nothing - required directory exists already */ |
| 632 | break; |
| 633 | } |
| 634 | case 2: { /* exists, but isn't a directory */ |
| 635 | fossil_warning("file %s found, but a directory is required " |
| 636 | "by empty-dirs setting", zDir); |
| 637 | } |
| 638 | } |
| 639 | blob_reset(&path); |
| 640 | } |
| 641 | } |
| @@ -656,11 +656,11 @@ | |
| 656 | int errCode /* Error code if file not found. Panic if 0. */ |
| 657 | ){ |
| 658 | Manifest *pManifest; |
| 659 | ManifestFile *pFile; |
| 660 | int rid=0; |
| 661 | |
| 662 | if( revision ){ |
| 663 | rid = name_to_typed_rid(revision,"ci"); |
| 664 | }else if( !g.localOpen ){ |
| 665 | rid = name_to_typed_rid(db_get("main-branch","trunk"),"ci"); |
| 666 | }else{ |
| @@ -669,11 +669,11 @@ | |
| 669 | if( !is_a_version(rid) ){ |
| 670 | if( errCode>0 ) return errCode; |
| 671 | fossil_fatal("no such checkin: %s", revision); |
| 672 | } |
| 673 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 674 | |
| 675 | if( pManifest ){ |
| 676 | pFile = manifest_file_find(pManifest, file); |
| 677 | if( pFile ){ |
| 678 | int rc; |
| 679 | rid = uuid_to_rid(pFile->zUuid, 0); |
| @@ -728,14 +728,14 @@ | |
| 728 | Blob record; |
| 729 | int i; |
| 730 | int errCode; |
| 731 | Stmt q; |
| 732 | |
| 733 | undo_capture_command_line(); |
| 734 | zRevision = find_option("revision", "r", 1); |
| 735 | verify_all_options(); |
| 736 | |
| 737 | if( g.argc<2 ){ |
| 738 | usage("?OPTIONS? [FILE] ..."); |
| 739 | } |
| 740 | if( zRevision && g.argc<3 ){ |
| 741 | fossil_fatal("the --revision option does not work for the entire tree"); |
| 742 |
+12
-1
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -158,12 +158,22 @@ | ||
| 158 | 158 | zLogin = mprintf("%t@", pUrlData->user); |
| 159 | 159 | for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} |
| 160 | 160 | pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 161 | 161 | i = j; |
| 162 | 162 | }else{ |
| 163 | - for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} | |
| 163 | + int inSquare = 0; | |
| 164 | + int n; | |
| 165 | + for(i=iStart; (c=zUrl[i])!=0 && c!='/' && (inSquare || c!=':'); i++){ | |
| 166 | + if( c=='[' ) inSquare = 1; | |
| 167 | + if( c==']' ) inSquare = 0; | |
| 168 | + } | |
| 164 | 169 | pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 170 | + n = strlen(pUrlData->name); | |
| 171 | + if( pUrlData->name[0]=='[' && n>2 && pUrlData->name[n-1]==']' ){ | |
| 172 | + pUrlData->name++; | |
| 173 | + pUrlData->name[n-2] = 0; | |
| 174 | + } | |
| 165 | 175 | zLogin = mprintf(""); |
| 166 | 176 | } |
| 167 | 177 | url_tolower(pUrlData->name); |
| 168 | 178 | if( c==':' ){ |
| 169 | 179 | pUrlData->port = 0; |
| @@ -362,10 +372,11 @@ | ||
| 362 | 372 | ** feature. |
| 363 | 373 | */ |
| 364 | 374 | void url_proxy_options(void){ |
| 365 | 375 | zProxyOpt = find_option("proxy", 0, 1); |
| 366 | 376 | if( find_option("nosync",0,0) ) g.fNoSync = 1; |
| 377 | + if( find_option("ipv4",0,0) ) g.fIPv4 = 1; | |
| 367 | 378 | } |
| 368 | 379 | |
| 369 | 380 | /* |
| 370 | 381 | ** If the "proxy" setting is defined, then change the URL settings |
| 371 | 382 | ** (initialized by a prior call to url_parse()) so that the HTTP |
| 372 | 383 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -158,12 +158,22 @@ | |
| 158 | zLogin = mprintf("%t@", pUrlData->user); |
| 159 | for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} |
| 160 | pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 161 | i = j; |
| 162 | }else{ |
| 163 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && c!=':'; i++){} |
| 164 | pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 165 | zLogin = mprintf(""); |
| 166 | } |
| 167 | url_tolower(pUrlData->name); |
| 168 | if( c==':' ){ |
| 169 | pUrlData->port = 0; |
| @@ -362,10 +372,11 @@ | |
| 362 | ** feature. |
| 363 | */ |
| 364 | void url_proxy_options(void){ |
| 365 | zProxyOpt = find_option("proxy", 0, 1); |
| 366 | if( find_option("nosync",0,0) ) g.fNoSync = 1; |
| 367 | } |
| 368 | |
| 369 | /* |
| 370 | ** If the "proxy" setting is defined, then change the URL settings |
| 371 | ** (initialized by a prior call to url_parse()) so that the HTTP |
| 372 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -158,12 +158,22 @@ | |
| 158 | zLogin = mprintf("%t@", pUrlData->user); |
| 159 | for(j=i+1; (c=zUrl[j])!=0 && c!='/' && c!=':'; j++){} |
| 160 | pUrlData->name = mprintf("%.*s", j-i-1, &zUrl[i+1]); |
| 161 | i = j; |
| 162 | }else{ |
| 163 | int inSquare = 0; |
| 164 | int n; |
| 165 | for(i=iStart; (c=zUrl[i])!=0 && c!='/' && (inSquare || c!=':'); i++){ |
| 166 | if( c=='[' ) inSquare = 1; |
| 167 | if( c==']' ) inSquare = 0; |
| 168 | } |
| 169 | pUrlData->name = mprintf("%.*s", i-iStart, &zUrl[iStart]); |
| 170 | n = strlen(pUrlData->name); |
| 171 | if( pUrlData->name[0]=='[' && n>2 && pUrlData->name[n-1]==']' ){ |
| 172 | pUrlData->name++; |
| 173 | pUrlData->name[n-2] = 0; |
| 174 | } |
| 175 | zLogin = mprintf(""); |
| 176 | } |
| 177 | url_tolower(pUrlData->name); |
| 178 | if( c==':' ){ |
| 179 | pUrlData->port = 0; |
| @@ -362,10 +372,11 @@ | |
| 372 | ** feature. |
| 373 | */ |
| 374 | void url_proxy_options(void){ |
| 375 | zProxyOpt = find_option("proxy", 0, 1); |
| 376 | if( find_option("nosync",0,0) ) g.fNoSync = 1; |
| 377 | if( find_option("ipv4",0,0) ) g.fIPv4 = 1; |
| 378 | } |
| 379 | |
| 380 | /* |
| 381 | ** If the "proxy" setting is defined, then change the URL settings |
| 382 | ** (initialized by a prior call to url_parse()) so that the HTTP |
| 383 |
+5
-4
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -467,13 +467,13 @@ | ||
| 467 | 467 | style_submenu_element("Newer", "Newer entries", |
| 468 | 468 | "%s/access_log?o=%d&n=%d&y=%d", g.zTop, skip>=n ? skip-n : 0, |
| 469 | 469 | n, y); |
| 470 | 470 | } |
| 471 | 471 | rc = db_prepare_ignore_error(&q, "%s", blob_sql_text(&sql)); |
| 472 | - @ <center><table border="1" cellpadding="5"> | |
| 473 | - @ <tr><th width="33%%">Date</th><th width="34%%">User</th> | |
| 474 | - @ <th width="33%%">IP Address</th></tr> | |
| 472 | + @ <center><table border="1" cellpadding="5" id='logtable'> | |
| 473 | + @ <thead><tr><th width="33%%">Date</th><th width="34%%">User</th> | |
| 474 | + @ <th width="33%%">IP Address</th></tr></thead><tbody> | |
| 475 | 475 | while( rc==SQLITE_OK && db_step(&q)==SQLITE_ROW ){ |
| 476 | 476 | const char *zName = db_column_text(&q, 0); |
| 477 | 477 | const char *zIP = db_column_text(&q, 1); |
| 478 | 478 | const char *zDate = db_column_text(&q, 2); |
| 479 | 479 | int bSuccess = db_column_int(&q, 3); |
| @@ -492,11 +492,11 @@ | ||
| 492 | 492 | } |
| 493 | 493 | if( skip>0 || cnt>n ){ |
| 494 | 494 | style_submenu_element("All", "All entries", |
| 495 | 495 | "%s/access_log?n=10000000", g.zTop); |
| 496 | 496 | } |
| 497 | - @ </table></center> | |
| 497 | + @ </tbody></table></center> | |
| 498 | 498 | db_finalize(&q); |
| 499 | 499 | @ <hr> |
| 500 | 500 | @ <form method="post" action="%s(g.zTop)/access_log"> |
| 501 | 501 | @ <label><input type="checkbox" name="delold"> |
| 502 | 502 | @ Delete all but the most recent 200 entries</input></label> |
| @@ -515,7 +515,8 @@ | ||
| 515 | 515 | @ <form method="post" action="%s(g.zTop)/access_log"> |
| 516 | 516 | @ <label><input type="checkbox" name="delall"> |
| 517 | 517 | @ Delete all entries</input></label> |
| 518 | 518 | @ <input type="submit" name="delallbtn" value="Delete"></input> |
| 519 | 519 | @ </form> |
| 520 | + output_table_sorting_javascript("logtable", "Ttt", 1); | |
| 520 | 521 | style_footer(); |
| 521 | 522 | } |
| 522 | 523 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -467,13 +467,13 @@ | |
| 467 | style_submenu_element("Newer", "Newer entries", |
| 468 | "%s/access_log?o=%d&n=%d&y=%d", g.zTop, skip>=n ? skip-n : 0, |
| 469 | n, y); |
| 470 | } |
| 471 | rc = db_prepare_ignore_error(&q, "%s", blob_sql_text(&sql)); |
| 472 | @ <center><table border="1" cellpadding="5"> |
| 473 | @ <tr><th width="33%%">Date</th><th width="34%%">User</th> |
| 474 | @ <th width="33%%">IP Address</th></tr> |
| 475 | while( rc==SQLITE_OK && db_step(&q)==SQLITE_ROW ){ |
| 476 | const char *zName = db_column_text(&q, 0); |
| 477 | const char *zIP = db_column_text(&q, 1); |
| 478 | const char *zDate = db_column_text(&q, 2); |
| 479 | int bSuccess = db_column_int(&q, 3); |
| @@ -492,11 +492,11 @@ | |
| 492 | } |
| 493 | if( skip>0 || cnt>n ){ |
| 494 | style_submenu_element("All", "All entries", |
| 495 | "%s/access_log?n=10000000", g.zTop); |
| 496 | } |
| 497 | @ </table></center> |
| 498 | db_finalize(&q); |
| 499 | @ <hr> |
| 500 | @ <form method="post" action="%s(g.zTop)/access_log"> |
| 501 | @ <label><input type="checkbox" name="delold"> |
| 502 | @ Delete all but the most recent 200 entries</input></label> |
| @@ -515,7 +515,8 @@ | |
| 515 | @ <form method="post" action="%s(g.zTop)/access_log"> |
| 516 | @ <label><input type="checkbox" name="delall"> |
| 517 | @ Delete all entries</input></label> |
| 518 | @ <input type="submit" name="delallbtn" value="Delete"></input> |
| 519 | @ </form> |
| 520 | style_footer(); |
| 521 | } |
| 522 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -467,13 +467,13 @@ | |
| 467 | style_submenu_element("Newer", "Newer entries", |
| 468 | "%s/access_log?o=%d&n=%d&y=%d", g.zTop, skip>=n ? skip-n : 0, |
| 469 | n, y); |
| 470 | } |
| 471 | rc = db_prepare_ignore_error(&q, "%s", blob_sql_text(&sql)); |
| 472 | @ <center><table border="1" cellpadding="5" id='logtable'> |
| 473 | @ <thead><tr><th width="33%%">Date</th><th width="34%%">User</th> |
| 474 | @ <th width="33%%">IP Address</th></tr></thead><tbody> |
| 475 | while( rc==SQLITE_OK && db_step(&q)==SQLITE_ROW ){ |
| 476 | const char *zName = db_column_text(&q, 0); |
| 477 | const char *zIP = db_column_text(&q, 1); |
| 478 | const char *zDate = db_column_text(&q, 2); |
| 479 | int bSuccess = db_column_int(&q, 3); |
| @@ -492,11 +492,11 @@ | |
| 492 | } |
| 493 | if( skip>0 || cnt>n ){ |
| 494 | style_submenu_element("All", "All entries", |
| 495 | "%s/access_log?n=10000000", g.zTop); |
| 496 | } |
| 497 | @ </tbody></table></center> |
| 498 | db_finalize(&q); |
| 499 | @ <hr> |
| 500 | @ <form method="post" action="%s(g.zTop)/access_log"> |
| 501 | @ <label><input type="checkbox" name="delold"> |
| 502 | @ Delete all but the most recent 200 entries</input></label> |
| @@ -515,7 +515,8 @@ | |
| 515 | @ <form method="post" action="%s(g.zTop)/access_log"> |
| 516 | @ <label><input type="checkbox" name="delall"> |
| 517 | @ Delete all entries</input></label> |
| 518 | @ <input type="submit" name="delallbtn" value="Delete"></input> |
| 519 | @ </form> |
| 520 | output_table_sorting_javascript("logtable", "Ttt", 1); |
| 521 | style_footer(); |
| 522 | } |
| 523 |
+10
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -331,5 +331,15 @@ | ||
| 331 | 331 | int fossil_is_uuid(const char *zSym){ |
| 332 | 332 | return zSym |
| 333 | 333 | && (UUID_SIZE==strlen(zSym)) |
| 334 | 334 | && validate16(zSym, UUID_SIZE); |
| 335 | 335 | } |
| 336 | + | |
| 337 | +/* | |
| 338 | +** Return true if the input string is NULL or all whitespace. | |
| 339 | +** Return false if the input string contains text. | |
| 340 | +*/ | |
| 341 | +int fossil_all_whitespace(const char *z){ | |
| 342 | + if( z==0 ) return 1; | |
| 343 | + while( fossil_isspace(z[0]) ){ z++; } | |
| 344 | + return z[0]==0; | |
| 345 | +} | |
| 336 | 346 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -331,5 +331,15 @@ | |
| 331 | int fossil_is_uuid(const char *zSym){ |
| 332 | return zSym |
| 333 | && (UUID_SIZE==strlen(zSym)) |
| 334 | && validate16(zSym, UUID_SIZE); |
| 335 | } |
| 336 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -331,5 +331,15 @@ | |
| 331 | int fossil_is_uuid(const char *zSym){ |
| 332 | return zSym |
| 333 | && (UUID_SIZE==strlen(zSym)) |
| 334 | && validate16(zSym, UUID_SIZE); |
| 335 | } |
| 336 | |
| 337 | /* |
| 338 | ** Return true if the input string is NULL or all whitespace. |
| 339 | ** Return false if the input string contains text. |
| 340 | */ |
| 341 | int fossil_all_whitespace(const char *z){ |
| 342 | if( z==0 ) return 1; |
| 343 | while( fossil_isspace(z[0]) ){ z++; } |
| 344 | return z[0]==0; |
| 345 | } |
| 346 |
+2
-2
| --- src/verify.c | ||
| +++ src/verify.c | ||
| @@ -63,11 +63,11 @@ | ||
| 63 | 63 | */ |
| 64 | 64 | static Bag toVerify; |
| 65 | 65 | static int inFinalVerify = 0; |
| 66 | 66 | |
| 67 | 67 | /* |
| 68 | -** This routine is called just prior to each commit operation. | |
| 68 | +** This routine is called just prior to each commit operation. | |
| 69 | 69 | ** |
| 70 | 70 | ** Invoke verify_rid() on every record that has been added or modified |
| 71 | 71 | ** in the repository, in order to make sure that the repository is sane. |
| 72 | 72 | */ |
| 73 | 73 | static int verify_at_commit(void){ |
| @@ -84,11 +84,11 @@ | ||
| 84 | 84 | return 0; |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | 87 | /* |
| 88 | 88 | ** Arrange to verify a particular record prior to committing. |
| 89 | -** | |
| 89 | +** | |
| 90 | 90 | ** If the record rid is less than 1, then just initialize the |
| 91 | 91 | ** verification system but do not record anything as needing |
| 92 | 92 | ** verification. |
| 93 | 93 | */ |
| 94 | 94 | void verify_before_commit(int rid){ |
| 95 | 95 |
| --- src/verify.c | |
| +++ src/verify.c | |
| @@ -63,11 +63,11 @@ | |
| 63 | */ |
| 64 | static Bag toVerify; |
| 65 | static int inFinalVerify = 0; |
| 66 | |
| 67 | /* |
| 68 | ** This routine is called just prior to each commit operation. |
| 69 | ** |
| 70 | ** Invoke verify_rid() on every record that has been added or modified |
| 71 | ** in the repository, in order to make sure that the repository is sane. |
| 72 | */ |
| 73 | static int verify_at_commit(void){ |
| @@ -84,11 +84,11 @@ | |
| 84 | return 0; |
| 85 | } |
| 86 | |
| 87 | /* |
| 88 | ** Arrange to verify a particular record prior to committing. |
| 89 | ** |
| 90 | ** If the record rid is less than 1, then just initialize the |
| 91 | ** verification system but do not record anything as needing |
| 92 | ** verification. |
| 93 | */ |
| 94 | void verify_before_commit(int rid){ |
| 95 |
| --- src/verify.c | |
| +++ src/verify.c | |
| @@ -63,11 +63,11 @@ | |
| 63 | */ |
| 64 | static Bag toVerify; |
| 65 | static int inFinalVerify = 0; |
| 66 | |
| 67 | /* |
| 68 | ** This routine is called just prior to each commit operation. |
| 69 | ** |
| 70 | ** Invoke verify_rid() on every record that has been added or modified |
| 71 | ** in the repository, in order to make sure that the repository is sane. |
| 72 | */ |
| 73 | static int verify_at_commit(void){ |
| @@ -84,11 +84,11 @@ | |
| 84 | return 0; |
| 85 | } |
| 86 | |
| 87 | /* |
| 88 | ** Arrange to verify a particular record prior to committing. |
| 89 | ** |
| 90 | ** If the record rid is less than 1, then just initialize the |
| 91 | ** verification system but do not record anything as needing |
| 92 | ** verification. |
| 93 | */ |
| 94 | void verify_before_commit(int rid){ |
| 95 |
+32
-8
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -155,10 +155,30 @@ | ||
| 155 | 155 | @ <pre> |
| 156 | 156 | @ %h(blob_str(pWiki)) |
| 157 | 157 | @ </pre> |
| 158 | 158 | } |
| 159 | 159 | } |
| 160 | + | |
| 161 | +/* | |
| 162 | +** WEBPAGE: md_rules | |
| 163 | +** | |
| 164 | +** Show a summary of the Markdown wiki formatting rules. | |
| 165 | +*/ | |
| 166 | +void markdown_rules_page(void){ | |
| 167 | + Blob x; | |
| 168 | + int fTxt = P("txt")!=0; | |
| 169 | + style_header("Markdown Formatting Rules"); | |
| 170 | + if( fTxt ){ | |
| 171 | + style_submenu_element("Formatted", "Formatted", "%R/md_rules"); | |
| 172 | + }else{ | |
| 173 | + style_submenu_element("Plain-Text", "Plain-Text", "%R/md_rules?txt=1"); | |
| 174 | + } | |
| 175 | + blob_init(&x, builtin_text("markdown.md"), -1); | |
| 176 | + wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown"); | |
| 177 | + blob_reset(&x); | |
| 178 | + style_footer(); | |
| 179 | +} | |
| 160 | 180 | |
| 161 | 181 | /* |
| 162 | 182 | ** Returns non-zero if moderation is required for wiki changes and wiki |
| 163 | 183 | ** attachments. |
| 164 | 184 | */ |
| @@ -213,11 +233,12 @@ | ||
| 213 | 233 | @ <li> %z(href("%R/wiki?name=%t",zHomePageName)) |
| 214 | 234 | @ %h(zHomePageName)</a> project home page.</li> |
| 215 | 235 | } |
| 216 | 236 | } |
| 217 | 237 | @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li> |
| 218 | - @ <li> %z(href("%R/wiki_rules"))Formatting rules</a> for wiki.</li> | |
| 238 | + @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for | |
| 239 | + @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li> | |
| 219 | 240 | @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a> |
| 220 | 241 | @ to experiment.</li> |
| 221 | 242 | if( g.perm.NewWiki ){ |
| 222 | 243 | @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li> |
| 223 | 244 | if( g.perm.Write ){ |
| @@ -243,17 +264,20 @@ | ||
| 243 | 264 | if( isSandbox ){ |
| 244 | 265 | zBody = db_get("sandbox",zBody); |
| 245 | 266 | zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki"); |
| 246 | 267 | rid = 0; |
| 247 | 268 | }else{ |
| 248 | - zTag = mprintf("wiki-%s", zPageName); | |
| 249 | - rid = db_int(0, | |
| 250 | - "SELECT rid FROM tagxref" | |
| 251 | - " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" | |
| 252 | - " ORDER BY mtime DESC", zTag | |
| 253 | - ); | |
| 254 | - free(zTag); | |
| 269 | + const char *zUuid = P("id"); | |
| 270 | + if( zUuid==0 || (rid = symbolic_name_to_rid(zUuid,"w"))==0 ){ | |
| 271 | + zTag = mprintf("wiki-%s", zPageName); | |
| 272 | + rid = db_int(0, | |
| 273 | + "SELECT rid FROM tagxref" | |
| 274 | + " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" | |
| 275 | + " ORDER BY mtime DESC", zTag | |
| 276 | + ); | |
| 277 | + free(zTag); | |
| 278 | + } | |
| 255 | 279 | pWiki = manifest_get(rid, CFTYPE_WIKI, 0); |
| 256 | 280 | if( pWiki ){ |
| 257 | 281 | zBody = pWiki->zWiki; |
| 258 | 282 | zMimetype = pWiki->zMimetype; |
| 259 | 283 | } |
| 260 | 284 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -155,10 +155,30 @@ | |
| 155 | @ <pre> |
| 156 | @ %h(blob_str(pWiki)) |
| 157 | @ </pre> |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | ** Returns non-zero if moderation is required for wiki changes and wiki |
| 163 | ** attachments. |
| 164 | */ |
| @@ -213,11 +233,12 @@ | |
| 213 | @ <li> %z(href("%R/wiki?name=%t",zHomePageName)) |
| 214 | @ %h(zHomePageName)</a> project home page.</li> |
| 215 | } |
| 216 | } |
| 217 | @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li> |
| 218 | @ <li> %z(href("%R/wiki_rules"))Formatting rules</a> for wiki.</li> |
| 219 | @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a> |
| 220 | @ to experiment.</li> |
| 221 | if( g.perm.NewWiki ){ |
| 222 | @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li> |
| 223 | if( g.perm.Write ){ |
| @@ -243,17 +264,20 @@ | |
| 243 | if( isSandbox ){ |
| 244 | zBody = db_get("sandbox",zBody); |
| 245 | zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki"); |
| 246 | rid = 0; |
| 247 | }else{ |
| 248 | zTag = mprintf("wiki-%s", zPageName); |
| 249 | rid = db_int(0, |
| 250 | "SELECT rid FROM tagxref" |
| 251 | " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" |
| 252 | " ORDER BY mtime DESC", zTag |
| 253 | ); |
| 254 | free(zTag); |
| 255 | pWiki = manifest_get(rid, CFTYPE_WIKI, 0); |
| 256 | if( pWiki ){ |
| 257 | zBody = pWiki->zWiki; |
| 258 | zMimetype = pWiki->zMimetype; |
| 259 | } |
| 260 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -155,10 +155,30 @@ | |
| 155 | @ <pre> |
| 156 | @ %h(blob_str(pWiki)) |
| 157 | @ </pre> |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | ** WEBPAGE: md_rules |
| 163 | ** |
| 164 | ** Show a summary of the Markdown wiki formatting rules. |
| 165 | */ |
| 166 | void markdown_rules_page(void){ |
| 167 | Blob x; |
| 168 | int fTxt = P("txt")!=0; |
| 169 | style_header("Markdown Formatting Rules"); |
| 170 | if( fTxt ){ |
| 171 | style_submenu_element("Formatted", "Formatted", "%R/md_rules"); |
| 172 | }else{ |
| 173 | style_submenu_element("Plain-Text", "Plain-Text", "%R/md_rules?txt=1"); |
| 174 | } |
| 175 | blob_init(&x, builtin_text("markdown.md"), -1); |
| 176 | wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown"); |
| 177 | blob_reset(&x); |
| 178 | style_footer(); |
| 179 | } |
| 180 | |
| 181 | /* |
| 182 | ** Returns non-zero if moderation is required for wiki changes and wiki |
| 183 | ** attachments. |
| 184 | */ |
| @@ -213,11 +233,12 @@ | |
| 233 | @ <li> %z(href("%R/wiki?name=%t",zHomePageName)) |
| 234 | @ %h(zHomePageName)</a> project home page.</li> |
| 235 | } |
| 236 | } |
| 237 | @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li> |
| 238 | @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for |
| 239 | @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li> |
| 240 | @ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a> |
| 241 | @ to experiment.</li> |
| 242 | if( g.perm.NewWiki ){ |
| 243 | @ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li> |
| 244 | if( g.perm.Write ){ |
| @@ -243,17 +264,20 @@ | |
| 264 | if( isSandbox ){ |
| 265 | zBody = db_get("sandbox",zBody); |
| 266 | zMimetype = db_get("sandbox-mimetype","text/x-fossil-wiki"); |
| 267 | rid = 0; |
| 268 | }else{ |
| 269 | const char *zUuid = P("id"); |
| 270 | if( zUuid==0 || (rid = symbolic_name_to_rid(zUuid,"w"))==0 ){ |
| 271 | zTag = mprintf("wiki-%s", zPageName); |
| 272 | rid = db_int(0, |
| 273 | "SELECT rid FROM tagxref" |
| 274 | " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)" |
| 275 | " ORDER BY mtime DESC", zTag |
| 276 | ); |
| 277 | free(zTag); |
| 278 | } |
| 279 | pWiki = manifest_get(rid, CFTYPE_WIKI, 0); |
| 280 | if( pWiki ){ |
| 281 | zBody = pWiki->zWiki; |
| 282 | zMimetype = pWiki->zMimetype; |
| 283 | } |
| 284 |
+72
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -2092,9 +2092,81 @@ | ||
| 2092 | 2092 | for(i=2; i<g.argc; i++){ |
| 2093 | 2093 | blob_read_from_file(&in, g.argv[i]); |
| 2094 | 2094 | blob_zero(&out); |
| 2095 | 2095 | htmlTidy(blob_str(&in), &out); |
| 2096 | 2096 | blob_reset(&in); |
| 2097 | + fossil_puts(blob_str(&out), 0); | |
| 2098 | + blob_reset(&out); | |
| 2099 | + } | |
| 2100 | +} | |
| 2101 | + | |
| 2102 | +/* | |
| 2103 | +** Remove all HTML markup from the input text. The output written into | |
| 2104 | +** pOut is pure text. | |
| 2105 | +*/ | |
| 2106 | +void html_to_plaintext(const char *zIn, Blob *pOut){ | |
| 2107 | + int n; | |
| 2108 | + int i, j; | |
| 2109 | + int nNL = 0; /* Number of \n characters at the end of pOut */ | |
| 2110 | + int nWS = 0; /* True if pOut ends with whitespace */ | |
| 2111 | + while( zIn[0] ){ | |
| 2112 | + n = nextHtmlToken(zIn); | |
| 2113 | + if( zIn[0]=='<' && n>1 ){ | |
| 2114 | + int isCloseTag; | |
| 2115 | + int eTag; | |
| 2116 | + int eType; | |
| 2117 | + char zTag[32]; | |
| 2118 | + isCloseTag = zIn[1]=='/'; | |
| 2119 | + for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){ | |
| 2120 | + zTag[i] = fossil_tolower(zIn[j]); | |
| 2121 | + } | |
| 2122 | + zTag[i] = 0; | |
| 2123 | + eTag = findTag(zTag); | |
| 2124 | + eType = aMarkup[eTag].iType; | |
| 2125 | + if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){ | |
| 2126 | + zIn += n; | |
| 2127 | + while( zIn[0] ){ | |
| 2128 | + n = nextHtmlToken(zIn); | |
| 2129 | + if( fossil_strnicmp(zIn, "</style",7)==0 ) break; | |
| 2130 | + zIn += n; | |
| 2131 | + } | |
| 2132 | + if( zIn[0]=='<' ) zIn += n; | |
| 2133 | + continue; | |
| 2134 | + } | |
| 2135 | + if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){ | |
| 2136 | + if( nNL==0 ){ | |
| 2137 | + blob_append(pOut, "\n", 1); | |
| 2138 | + nNL++; | |
| 2139 | + } | |
| 2140 | + nWS = 1; | |
| 2141 | + } | |
| 2142 | + }else if( fossil_isspace(zIn[0]) ){ | |
| 2143 | + for(i=nNL=0; i<n; i++) if( zIn[i]=='\n' ) nNL++; | |
| 2144 | + if( !nWS ){ | |
| 2145 | + blob_append(pOut, nNL ? "\n" : " ", 1); | |
| 2146 | + nWS = 1; | |
| 2147 | + } | |
| 2148 | + }else{ | |
| 2149 | + blob_append(pOut, zIn, n); | |
| 2150 | + nNL = nWS = 0; | |
| 2151 | + } | |
| 2152 | + zIn += n; | |
| 2153 | + } | |
| 2154 | + if( nNL==0 ) blob_append(pOut, "\n", 1); | |
| 2155 | +} | |
| 2156 | + | |
| 2157 | +/* | |
| 2158 | +** COMMAND: test-html-to-text | |
| 2159 | +*/ | |
| 2160 | +void test_html_to_text(void){ | |
| 2161 | + Blob in, out; | |
| 2162 | + int i; | |
| 2163 | + | |
| 2164 | + for(i=2; i<g.argc; i++){ | |
| 2165 | + blob_read_from_file(&in, g.argv[i]); | |
| 2166 | + blob_zero(&out); | |
| 2167 | + html_to_plaintext(blob_str(&in), &out); | |
| 2168 | + blob_reset(&in); | |
| 2097 | 2169 | fossil_puts(blob_str(&out), 0); |
| 2098 | 2170 | blob_reset(&out); |
| 2099 | 2171 | } |
| 2100 | 2172 | } |
| 2101 | 2173 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -2092,9 +2092,81 @@ | |
| 2092 | for(i=2; i<g.argc; i++){ |
| 2093 | blob_read_from_file(&in, g.argv[i]); |
| 2094 | blob_zero(&out); |
| 2095 | htmlTidy(blob_str(&in), &out); |
| 2096 | blob_reset(&in); |
| 2097 | fossil_puts(blob_str(&out), 0); |
| 2098 | blob_reset(&out); |
| 2099 | } |
| 2100 | } |
| 2101 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -2092,9 +2092,81 @@ | |
| 2092 | for(i=2; i<g.argc; i++){ |
| 2093 | blob_read_from_file(&in, g.argv[i]); |
| 2094 | blob_zero(&out); |
| 2095 | htmlTidy(blob_str(&in), &out); |
| 2096 | blob_reset(&in); |
| 2097 | fossil_puts(blob_str(&out), 0); |
| 2098 | blob_reset(&out); |
| 2099 | } |
| 2100 | } |
| 2101 | |
| 2102 | /* |
| 2103 | ** Remove all HTML markup from the input text. The output written into |
| 2104 | ** pOut is pure text. |
| 2105 | */ |
| 2106 | void html_to_plaintext(const char *zIn, Blob *pOut){ |
| 2107 | int n; |
| 2108 | int i, j; |
| 2109 | int nNL = 0; /* Number of \n characters at the end of pOut */ |
| 2110 | int nWS = 0; /* True if pOut ends with whitespace */ |
| 2111 | while( zIn[0] ){ |
| 2112 | n = nextHtmlToken(zIn); |
| 2113 | if( zIn[0]=='<' && n>1 ){ |
| 2114 | int isCloseTag; |
| 2115 | int eTag; |
| 2116 | int eType; |
| 2117 | char zTag[32]; |
| 2118 | isCloseTag = zIn[1]=='/'; |
| 2119 | for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){ |
| 2120 | zTag[i] = fossil_tolower(zIn[j]); |
| 2121 | } |
| 2122 | zTag[i] = 0; |
| 2123 | eTag = findTag(zTag); |
| 2124 | eType = aMarkup[eTag].iType; |
| 2125 | if( eTag==MARKUP_INVALID && fossil_strnicmp(zIn,"<style",6)==0 ){ |
| 2126 | zIn += n; |
| 2127 | while( zIn[0] ){ |
| 2128 | n = nextHtmlToken(zIn); |
| 2129 | if( fossil_strnicmp(zIn, "</style",7)==0 ) break; |
| 2130 | zIn += n; |
| 2131 | } |
| 2132 | if( zIn[0]=='<' ) zIn += n; |
| 2133 | continue; |
| 2134 | } |
| 2135 | if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){ |
| 2136 | if( nNL==0 ){ |
| 2137 | blob_append(pOut, "\n", 1); |
| 2138 | nNL++; |
| 2139 | } |
| 2140 | nWS = 1; |
| 2141 | } |
| 2142 | }else if( fossil_isspace(zIn[0]) ){ |
| 2143 | for(i=nNL=0; i<n; i++) if( zIn[i]=='\n' ) nNL++; |
| 2144 | if( !nWS ){ |
| 2145 | blob_append(pOut, nNL ? "\n" : " ", 1); |
| 2146 | nWS = 1; |
| 2147 | } |
| 2148 | }else{ |
| 2149 | blob_append(pOut, zIn, n); |
| 2150 | nNL = nWS = 0; |
| 2151 | } |
| 2152 | zIn += n; |
| 2153 | } |
| 2154 | if( nNL==0 ) blob_append(pOut, "\n", 1); |
| 2155 | } |
| 2156 | |
| 2157 | /* |
| 2158 | ** COMMAND: test-html-to-text |
| 2159 | */ |
| 2160 | void test_html_to_text(void){ |
| 2161 | Blob in, out; |
| 2162 | int i; |
| 2163 | |
| 2164 | for(i=2; i<g.argc; i++){ |
| 2165 | blob_read_from_file(&in, g.argv[i]); |
| 2166 | blob_zero(&out); |
| 2167 | html_to_plaintext(blob_str(&in), &out); |
| 2168 | blob_reset(&in); |
| 2169 | fossil_puts(blob_str(&out), 0); |
| 2170 | blob_reset(&out); |
| 2171 | } |
| 2172 | } |
| 2173 |
+2
-2
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -1966,12 +1966,12 @@ | ||
| 1966 | 1966 | g.clockSkewSeen = 1; |
| 1967 | 1967 | } |
| 1968 | 1968 | |
| 1969 | 1969 | fossil_force_newline(); |
| 1970 | 1970 | fossil_print( |
| 1971 | - "%s finished with %lld bytes sent, %lld bytes received\n", | |
| 1972 | - zOpType, nSent, nRcvd); | |
| 1971 | + "%s done, sent: %lld received: %lld ip: %s\n", | |
| 1972 | + zOpType, nSent, nRcvd, g.zIpAddr); | |
| 1973 | 1973 | transport_close(&g.url); |
| 1974 | 1974 | transport_global_shutdown(&g.url); |
| 1975 | 1975 | if( nErr && go==2 ){ |
| 1976 | 1976 | db_multi_exec("DROP TABLE onremote"); |
| 1977 | 1977 | manifest_crosslink_end(MC_PERMIT_HOOKS); |
| 1978 | 1978 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1966,12 +1966,12 @@ | |
| 1966 | g.clockSkewSeen = 1; |
| 1967 | } |
| 1968 | |
| 1969 | fossil_force_newline(); |
| 1970 | fossil_print( |
| 1971 | "%s finished with %lld bytes sent, %lld bytes received\n", |
| 1972 | zOpType, nSent, nRcvd); |
| 1973 | transport_close(&g.url); |
| 1974 | transport_global_shutdown(&g.url); |
| 1975 | if( nErr && go==2 ){ |
| 1976 | db_multi_exec("DROP TABLE onremote"); |
| 1977 | manifest_crosslink_end(MC_PERMIT_HOOKS); |
| 1978 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -1966,12 +1966,12 @@ | |
| 1966 | g.clockSkewSeen = 1; |
| 1967 | } |
| 1968 | |
| 1969 | fossil_force_newline(); |
| 1970 | fossil_print( |
| 1971 | "%s done, sent: %lld received: %lld ip: %s\n", |
| 1972 | zOpType, nSent, nRcvd, g.zIpAddr); |
| 1973 | transport_close(&g.url); |
| 1974 | transport_global_shutdown(&g.url); |
| 1975 | if( nErr && go==2 ){ |
| 1976 | db_multi_exec("DROP TABLE onremote"); |
| 1977 | manifest_crosslink_end(MC_PERMIT_HOOKS); |
| 1978 |
+1
-1
| --- test/diff-test-1.wiki | ||
| +++ test/diff-test-1.wiki | ||
| @@ -17,11 +17,11 @@ | ||
| 17 | 17 | The edit of a line with multibyte characters is the first chunk. |
| 18 | 18 | * <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6" |
| 19 | 19 | target="testwindow">Large diff of sqlite3.c</a>. This diff was very |
| 20 | 20 | slow prior to the performance enhancement change [9e15437e97]. |
| 21 | 21 | * <a href="../../../info/bda00cbada#chunk49" target="testwindow"> |
| 22 | - A difficult indentation change. | |
| 22 | + A difficult indentation change.</a> (show-version-diffs must be enabled) | |
| 23 | 23 | * <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk13" |
| 24 | 24 | target="testwindow">Another tricky indentation.</a> Notice especially |
| 25 | 25 | lines 59398 and 59407 on the left. |
| 26 | 26 | * <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk13" |
| 27 | 27 | target="testwindow">Inverse of the previous.</a> |
| 28 | 28 |
| --- test/diff-test-1.wiki | |
| +++ test/diff-test-1.wiki | |
| @@ -17,11 +17,11 @@ | |
| 17 | The edit of a line with multibyte characters is the first chunk. |
| 18 | * <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6" |
| 19 | target="testwindow">Large diff of sqlite3.c</a>. This diff was very |
| 20 | slow prior to the performance enhancement change [9e15437e97]. |
| 21 | * <a href="../../../info/bda00cbada#chunk49" target="testwindow"> |
| 22 | A difficult indentation change. |
| 23 | * <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk13" |
| 24 | target="testwindow">Another tricky indentation.</a> Notice especially |
| 25 | lines 59398 and 59407 on the left. |
| 26 | * <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk13" |
| 27 | target="testwindow">Inverse of the previous.</a> |
| 28 |
| --- test/diff-test-1.wiki | |
| +++ test/diff-test-1.wiki | |
| @@ -17,11 +17,11 @@ | |
| 17 | The edit of a line with multibyte characters is the first chunk. |
| 18 | * <a href="../../../fdiff?v1=57b0d8183cab0e3d&v2=37b3ef49d73cdfe6" |
| 19 | target="testwindow">Large diff of sqlite3.c</a>. This diff was very |
| 20 | slow prior to the performance enhancement change [9e15437e97]. |
| 21 | * <a href="../../../info/bda00cbada#chunk49" target="testwindow"> |
| 22 | A difficult indentation change.</a> (show-version-diffs must be enabled) |
| 23 | * <a href="../../../fdiff?v1=955cc67ace8fb622&v2=e2e1c87b86664b45#chunk13" |
| 24 | target="testwindow">Another tricky indentation.</a> Notice especially |
| 25 | lines 59398 and 59407 on the left. |
| 26 | * <a href="../../../fdiff?v2=955cc67ace8fb622&v1=e2e1c87b86664b45#chunk13" |
| 27 | target="testwindow">Inverse of the previous.</a> |
| 28 |
+12
-11
| --- test/graph-test-1.wiki | ||
| +++ test/graph-test-1.wiki | ||
| @@ -28,23 +28,21 @@ | ||
| 28 | 28 | multiple branch risers.</a> |
| 29 | 29 | * <a href="../../../timeline?y=ci&a=2010-12-20&n=18" target="testwindow"> |
| 30 | 30 | multiple branch risers, n=18.</a> |
| 31 | 31 | * <a href="../../../timeline?y=ci&a=2010-12-20&n=9" target="testwindow"> |
| 32 | 32 | multiple branch risers, n=9.</a> |
| 33 | - * <a href="../../../timeline?r=experimental" target="testwindow"> | |
| 33 | + * <a href="../../../timeline?r=experimental&n=0" target="testwindow"> | |
| 34 | 34 | Experimental branch with related check-ins.</a> |
| 35 | - * <a href="../../../timeline?r=experimental&mionly" target="testwindow"> | |
| 35 | + * <a href="../../../timeline?r=experimental&mionly&n=0" target="testwindow"> | |
| 36 | 36 | Experimental branch with merge-ins only.</a> |
| 37 | - * <a href="../../../timeline?t=experimental" target="testwindow"> | |
| 37 | + * <a href="../../../timeline?t=experimental&n=0" target="testwindow"> | |
| 38 | 38 | Experimental branch check-ins only.</a> |
| 39 | - * <a href="../../../timeline?r=experimental&n=1000" target="testwindow"> | |
| 40 | - Experimental branch using and related check-ins - 1000 elements.</a> | |
| 41 | - * <a href="../../../timeline?r=release" target="testwindow"> | |
| 39 | + * <a href="../../../timeline?r=release&n=0" target="testwindow"> | |
| 42 | 40 | Check-ins tagged "release" and related check-ins</a> |
| 43 | - * <a href="../../../timeline?r=release&mionly" target="testwindow"> | |
| 41 | + * <a href="../../../timeline?r=release&mionly&n=0" target="testwindow"> | |
| 44 | 42 | Check-ins tagged "release" and merge-ins</a> |
| 45 | - * <a href="../../../timeline?t=release" target="testwindow"> | |
| 43 | + * <a href="../../../timeline?t=release&n=0" target="testwindow"> | |
| 46 | 44 | Only check-ins tagged "release"</a> |
| 47 | 45 | * <a href="../../../finfo?name=Makefile" target="testwindow"> |
| 48 | 46 | History of source file "Makefile".</a> |
| 49 | 47 | * <a href="../../../timeline?a=1970-01-01" target="testwindow"> |
| 50 | 48 | 20 elements after 1970-01-01.</a> |
| @@ -51,22 +49,25 @@ | ||
| 51 | 49 | * <a href="../../../timeline?n=100000000&y=ci" target="testwindow"> |
| 52 | 50 | All check-ins - a huge graph.</a> |
| 53 | 51 | * <a href="../../../timeline?f=8dfed953f7530442" target="testwindow"> |
| 54 | 52 | This malformed commit has a |
| 55 | 53 | merge parent which is not a valid checkin.</a> |
| 56 | - * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9" | |
| 54 | + * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9&shortest" | |
| 57 | 55 | target="testwindow"> |
| 58 | 56 | From e663bac6f7 to a298a0e2f9 by shortest path.</a> |
| 59 | - * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9&nomerge" | |
| 57 | + * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9" | |
| 60 | 58 | target="testwindow"> |
| 61 | 59 | From e663bac6f7 to a298a0e2f9 without merge links.</a> |
| 62 | 60 | * <a href="../../../timeline?me=e663bac6f7&you=a298a0e2f9" |
| 63 | 61 | target="testwindow"> |
| 64 | 62 | Common ancestor path of e663bac6f7 to a298a0e2f9.</a> |
| 65 | - * <a href="../../../timeline?f=65dd90fb95a2af55"> | |
| 63 | + * <a href="../../../timeline?f=65dd90fb95a2af55" target="testwindow"> | |
| 66 | 64 | Merge on the same branch does not result in a leaf. |
| 67 | 65 | </a> |
| 66 | + * <a href="../../../timeline?c=20015206bc" | |
| 67 | + target="testwindow"> | |
| 68 | + This timeline has a hidden commit.</a> Click Unhide to reveal. | |
| 68 | 69 | |
| 69 | 70 | External: |
| 70 | 71 | |
| 71 | 72 | * <a href="http://www.sqlite.org/src/timeline?c=2010-09-29&nd" |
| 72 | 73 | target="testwindow">Timewarp due to a mis-configured system clock.</a> |
| 73 | 74 |
| --- test/graph-test-1.wiki | |
| +++ test/graph-test-1.wiki | |
| @@ -28,23 +28,21 @@ | |
| 28 | multiple branch risers.</a> |
| 29 | * <a href="../../../timeline?y=ci&a=2010-12-20&n=18" target="testwindow"> |
| 30 | multiple branch risers, n=18.</a> |
| 31 | * <a href="../../../timeline?y=ci&a=2010-12-20&n=9" target="testwindow"> |
| 32 | multiple branch risers, n=9.</a> |
| 33 | * <a href="../../../timeline?r=experimental" target="testwindow"> |
| 34 | Experimental branch with related check-ins.</a> |
| 35 | * <a href="../../../timeline?r=experimental&mionly" target="testwindow"> |
| 36 | Experimental branch with merge-ins only.</a> |
| 37 | * <a href="../../../timeline?t=experimental" target="testwindow"> |
| 38 | Experimental branch check-ins only.</a> |
| 39 | * <a href="../../../timeline?r=experimental&n=1000" target="testwindow"> |
| 40 | Experimental branch using and related check-ins - 1000 elements.</a> |
| 41 | * <a href="../../../timeline?r=release" target="testwindow"> |
| 42 | Check-ins tagged "release" and related check-ins</a> |
| 43 | * <a href="../../../timeline?r=release&mionly" target="testwindow"> |
| 44 | Check-ins tagged "release" and merge-ins</a> |
| 45 | * <a href="../../../timeline?t=release" target="testwindow"> |
| 46 | Only check-ins tagged "release"</a> |
| 47 | * <a href="../../../finfo?name=Makefile" target="testwindow"> |
| 48 | History of source file "Makefile".</a> |
| 49 | * <a href="../../../timeline?a=1970-01-01" target="testwindow"> |
| 50 | 20 elements after 1970-01-01.</a> |
| @@ -51,22 +49,25 @@ | |
| 51 | * <a href="../../../timeline?n=100000000&y=ci" target="testwindow"> |
| 52 | All check-ins - a huge graph.</a> |
| 53 | * <a href="../../../timeline?f=8dfed953f7530442" target="testwindow"> |
| 54 | This malformed commit has a |
| 55 | merge parent which is not a valid checkin.</a> |
| 56 | * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9" |
| 57 | target="testwindow"> |
| 58 | From e663bac6f7 to a298a0e2f9 by shortest path.</a> |
| 59 | * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9&nomerge" |
| 60 | target="testwindow"> |
| 61 | From e663bac6f7 to a298a0e2f9 without merge links.</a> |
| 62 | * <a href="../../../timeline?me=e663bac6f7&you=a298a0e2f9" |
| 63 | target="testwindow"> |
| 64 | Common ancestor path of e663bac6f7 to a298a0e2f9.</a> |
| 65 | * <a href="../../../timeline?f=65dd90fb95a2af55"> |
| 66 | Merge on the same branch does not result in a leaf. |
| 67 | </a> |
| 68 | |
| 69 | External: |
| 70 | |
| 71 | * <a href="http://www.sqlite.org/src/timeline?c=2010-09-29&nd" |
| 72 | target="testwindow">Timewarp due to a mis-configured system clock.</a> |
| 73 |
| --- test/graph-test-1.wiki | |
| +++ test/graph-test-1.wiki | |
| @@ -28,23 +28,21 @@ | |
| 28 | multiple branch risers.</a> |
| 29 | * <a href="../../../timeline?y=ci&a=2010-12-20&n=18" target="testwindow"> |
| 30 | multiple branch risers, n=18.</a> |
| 31 | * <a href="../../../timeline?y=ci&a=2010-12-20&n=9" target="testwindow"> |
| 32 | multiple branch risers, n=9.</a> |
| 33 | * <a href="../../../timeline?r=experimental&n=0" target="testwindow"> |
| 34 | Experimental branch with related check-ins.</a> |
| 35 | * <a href="../../../timeline?r=experimental&mionly&n=0" target="testwindow"> |
| 36 | Experimental branch with merge-ins only.</a> |
| 37 | * <a href="../../../timeline?t=experimental&n=0" target="testwindow"> |
| 38 | Experimental branch check-ins only.</a> |
| 39 | * <a href="../../../timeline?r=release&n=0" target="testwindow"> |
| 40 | Check-ins tagged "release" and related check-ins</a> |
| 41 | * <a href="../../../timeline?r=release&mionly&n=0" target="testwindow"> |
| 42 | Check-ins tagged "release" and merge-ins</a> |
| 43 | * <a href="../../../timeline?t=release&n=0" target="testwindow"> |
| 44 | Only check-ins tagged "release"</a> |
| 45 | * <a href="../../../finfo?name=Makefile" target="testwindow"> |
| 46 | History of source file "Makefile".</a> |
| 47 | * <a href="../../../timeline?a=1970-01-01" target="testwindow"> |
| 48 | 20 elements after 1970-01-01.</a> |
| @@ -51,22 +49,25 @@ | |
| 49 | * <a href="../../../timeline?n=100000000&y=ci" target="testwindow"> |
| 50 | All check-ins - a huge graph.</a> |
| 51 | * <a href="../../../timeline?f=8dfed953f7530442" target="testwindow"> |
| 52 | This malformed commit has a |
| 53 | merge parent which is not a valid checkin.</a> |
| 54 | * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9&shortest" |
| 55 | target="testwindow"> |
| 56 | From e663bac6f7 to a298a0e2f9 by shortest path.</a> |
| 57 | * <a href="../../../timeline?from=e663bac6f7&to=a298a0e2f9" |
| 58 | target="testwindow"> |
| 59 | From e663bac6f7 to a298a0e2f9 without merge links.</a> |
| 60 | * <a href="../../../timeline?me=e663bac6f7&you=a298a0e2f9" |
| 61 | target="testwindow"> |
| 62 | Common ancestor path of e663bac6f7 to a298a0e2f9.</a> |
| 63 | * <a href="../../../timeline?f=65dd90fb95a2af55" target="testwindow"> |
| 64 | Merge on the same branch does not result in a leaf. |
| 65 | </a> |
| 66 | * <a href="../../../timeline?c=20015206bc" |
| 67 | target="testwindow"> |
| 68 | This timeline has a hidden commit.</a> Click Unhide to reveal. |
| 69 | |
| 70 | External: |
| 71 | |
| 72 | * <a href="http://www.sqlite.org/src/timeline?c=2010-09-29&nd" |
| 73 | target="testwindow">Timewarp due to a mis-configured system clock.</a> |
| 74 |
+1
-1
| --- win/Makefile.PellesCGMake | ||
| +++ win/Makefile.PellesCGMake | ||
| @@ -141,11 +141,11 @@ | ||
| 141 | 141 | # generate the index source, containing all web references,.. |
| 142 | 142 | page_index.h: $(TRANSLATEDSRC) mkindex.exe |
| 143 | 143 | mkindex.exe $(TRANSLATEDSRC) >$@ |
| 144 | 144 | |
| 145 | 145 | builtin_data.h: $(EXTRA_FILES) mkbuiltin.exe |
| 146 | - mkbuiltin.exe $(EXTRA_FILES) >$@ | |
| 146 | + mkbuiltin.exe --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ | |
| 147 | 147 | |
| 148 | 148 | # extracting version info from manifest |
| 149 | 149 | VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION |
| 150 | 150 | version.exe ..\manifest.uuid ..\manifest ..\VERSION >$@ |
| 151 | 151 | |
| 152 | 152 |
| --- win/Makefile.PellesCGMake | |
| +++ win/Makefile.PellesCGMake | |
| @@ -141,11 +141,11 @@ | |
| 141 | # generate the index source, containing all web references,.. |
| 142 | page_index.h: $(TRANSLATEDSRC) mkindex.exe |
| 143 | mkindex.exe $(TRANSLATEDSRC) >$@ |
| 144 | |
| 145 | builtin_data.h: $(EXTRA_FILES) mkbuiltin.exe |
| 146 | mkbuiltin.exe $(EXTRA_FILES) >$@ |
| 147 | |
| 148 | # extracting version info from manifest |
| 149 | VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION |
| 150 | version.exe ..\manifest.uuid ..\manifest ..\VERSION >$@ |
| 151 | |
| 152 |
| --- win/Makefile.PellesCGMake | |
| +++ win/Makefile.PellesCGMake | |
| @@ -141,11 +141,11 @@ | |
| 141 | # generate the index source, containing all web references,.. |
| 142 | page_index.h: $(TRANSLATEDSRC) mkindex.exe |
| 143 | mkindex.exe $(TRANSLATEDSRC) >$@ |
| 144 | |
| 145 | builtin_data.h: $(EXTRA_FILES) mkbuiltin.exe |
| 146 | mkbuiltin.exe --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ |
| 147 | |
| 148 | # extracting version info from manifest |
| 149 | VERSION.h: version.exe ..\manifest.uuid ..\manifest ..\VERSION |
| 150 | version.exe ..\manifest.uuid ..\manifest ..\VERSION >$@ |
| 151 | |
| 152 |
+11
-5
| --- win/Makefile.dmc | ||
| +++ win/Makefile.dmc | ||
| @@ -28,13 +28,13 @@ | ||
| 28 | 28 | |
| 29 | 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS |
| 30 | 30 | |
| 31 | 31 | SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | 32 | |
| 33 | -SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.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 md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c | |
| 33 | +SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.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 md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c | |
| 34 | 34 | |
| 35 | -OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$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)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$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)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$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)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$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)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 35 | +OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$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)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$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)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$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)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$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)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$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)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O | |
| 36 | 36 | |
| 37 | 37 | |
| 38 | 38 | RC=$(DMDIR)\bin\rcc |
| 39 | 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | 40 | |
| @@ -49,11 +49,11 @@ | ||
| 49 | 49 | |
| 50 | 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | 52 | |
| 53 | 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | - +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo foci fusefs glob graph gzip http http_socket http_ssl http_transport import info 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 md5 merge merge3 moderate name path pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ | |
| 54 | + +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo foci fusefs glob graph gzip http http_socket http_ssl http_transport import info 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 md5 merge merge3 moderate name path pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ | |
| 55 | 55 | +echo fossil >> $@ |
| 56 | 56 | +echo fossil >> $@ |
| 57 | 57 | +echo $(LIBS) >> $@ |
| 58 | 58 | +echo. >> $@ |
| 59 | 59 | +echo fossil >> $@ |
| @@ -96,11 +96,11 @@ | ||
| 96 | 96 | |
| 97 | 97 | page_index.h: mkindex$E $(SRC) |
| 98 | 98 | +$** > $@ |
| 99 | 99 | |
| 100 | 100 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 101 | - +$** > $@ | |
| 101 | + mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ | |
| 102 | 102 | |
| 103 | 103 | clean: |
| 104 | 104 | -del $(OBJDIR)\*.obj |
| 105 | 105 | -del *.obj *_.c *.h *.map |
| 106 | 106 | |
| @@ -674,10 +674,16 @@ | ||
| 674 | 674 | $(OBJDIR)\stat$O : stat_.c stat.h |
| 675 | 675 | $(TCC) -o$@ -c stat_.c |
| 676 | 676 | |
| 677 | 677 | stat_.c : $(SRCDIR)\stat.c |
| 678 | 678 | +translate$E $** > $@ |
| 679 | + | |
| 680 | +$(OBJDIR)\statrep$O : statrep_.c statrep.h | |
| 681 | + $(TCC) -o$@ -c statrep_.c | |
| 682 | + | |
| 683 | +statrep_.c : $(SRCDIR)\statrep.c | |
| 684 | + +translate$E $** > $@ | |
| 679 | 685 | |
| 680 | 686 | $(OBJDIR)\style$O : style_.c style.h |
| 681 | 687 | $(TCC) -o$@ -c style_.c |
| 682 | 688 | |
| 683 | 689 | style_.c : $(SRCDIR)\style.c |
| @@ -826,7 +832,7 @@ | ||
| 826 | 832 | |
| 827 | 833 | zip_.c : $(SRCDIR)\zip.c |
| 828 | 834 | +translate$E $** > $@ |
| 829 | 835 | |
| 830 | 836 | headers: makeheaders$E page_index.h builtin_data.h VERSION.h |
| 831 | - +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.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 captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.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 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 md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.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 report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.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 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 wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h | |
| 837 | + +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.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 captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.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 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 md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.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 report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.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 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 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 wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h | |
| 832 | 838 | @copy /Y nul: headers |
| 833 | 839 |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -28,13 +28,13 @@ | |
| 28 | |
| 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS |
| 30 | |
| 31 | SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | |
| 33 | SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.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 md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c |
| 34 | |
| 35 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$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)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$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)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$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)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$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)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 36 | |
| 37 | |
| 38 | RC=$(DMDIR)\bin\rcc |
| 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | |
| 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo foci fusefs glob graph gzip http http_socket http_ssl http_transport import info 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 md5 merge merge3 moderate name path pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ |
| 55 | +echo fossil >> $@ |
| 56 | +echo fossil >> $@ |
| 57 | +echo $(LIBS) >> $@ |
| 58 | +echo. >> $@ |
| 59 | +echo fossil >> $@ |
| @@ -96,11 +96,11 @@ | |
| 96 | |
| 97 | page_index.h: mkindex$E $(SRC) |
| 98 | +$** > $@ |
| 99 | |
| 100 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 101 | +$** > $@ |
| 102 | |
| 103 | clean: |
| 104 | -del $(OBJDIR)\*.obj |
| 105 | -del *.obj *_.c *.h *.map |
| 106 | |
| @@ -674,10 +674,16 @@ | |
| 674 | $(OBJDIR)\stat$O : stat_.c stat.h |
| 675 | $(TCC) -o$@ -c stat_.c |
| 676 | |
| 677 | stat_.c : $(SRCDIR)\stat.c |
| 678 | +translate$E $** > $@ |
| 679 | |
| 680 | $(OBJDIR)\style$O : style_.c style.h |
| 681 | $(TCC) -o$@ -c style_.c |
| 682 | |
| 683 | style_.c : $(SRCDIR)\style.c |
| @@ -826,7 +832,7 @@ | |
| 826 | |
| 827 | zip_.c : $(SRCDIR)\zip.c |
| 828 | +translate$E $** > $@ |
| 829 | |
| 830 | headers: makeheaders$E page_index.h builtin_data.h VERSION.h |
| 831 | +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.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 captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.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 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 md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.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 report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.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 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 wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h |
| 832 | @copy /Y nul: headers |
| 833 |
| --- win/Makefile.dmc | |
| +++ win/Makefile.dmc | |
| @@ -28,13 +28,13 @@ | |
| 28 | |
| 29 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_OMIT_DEPRECATED -DSQLITE_ENABLE_EXPLAIN_COMMENTS |
| 30 | |
| 31 | SHELL_OPTIONS = -Dmain=sqlite3_shell -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=fossil_open -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen |
| 32 | |
| 33 | SRC = add_.c allrepo_.c attach_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c export_.c file_.c finfo_.c foci_.c fusefs_.c glob_.c graph_.c gzip_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.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 md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c sitemap_.c skins_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c |
| 34 | |
| 35 | OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$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)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\fusefs$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$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)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$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)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$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)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$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)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O |
| 36 | |
| 37 | |
| 38 | RC=$(DMDIR)\bin\rcc |
| 39 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 40 | |
| @@ -49,11 +49,11 @@ | |
| 49 | |
| 50 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 51 | $(RC) $(RCFLAGS) -o$@ $** |
| 52 | |
| 53 | $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res |
| 54 | +echo add allrepo attach bag bisect blob branch browse builtin bundle cache captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event export file finfo foci fusefs glob graph gzip http http_socket http_ssl http_transport import info 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 md5 merge merge3 moderate name path pivot popen pqueue printf publish purge rebuild regexp report rss schema search setup sha1 shun sitemap skins sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode update url user utf8 util verify vfile wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ |
| 55 | +echo fossil >> $@ |
| 56 | +echo fossil >> $@ |
| 57 | +echo $(LIBS) >> $@ |
| 58 | +echo. >> $@ |
| 59 | +echo fossil >> $@ |
| @@ -96,11 +96,11 @@ | |
| 96 | |
| 97 | page_index.h: mkindex$E $(SRC) |
| 98 | +$** > $@ |
| 99 | |
| 100 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 101 | mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ |
| 102 | |
| 103 | clean: |
| 104 | -del $(OBJDIR)\*.obj |
| 105 | -del *.obj *_.c *.h *.map |
| 106 | |
| @@ -674,10 +674,16 @@ | |
| 674 | $(OBJDIR)\stat$O : stat_.c stat.h |
| 675 | $(TCC) -o$@ -c stat_.c |
| 676 | |
| 677 | stat_.c : $(SRCDIR)\stat.c |
| 678 | +translate$E $** > $@ |
| 679 | |
| 680 | $(OBJDIR)\statrep$O : statrep_.c statrep.h |
| 681 | $(TCC) -o$@ -c statrep_.c |
| 682 | |
| 683 | statrep_.c : $(SRCDIR)\statrep.c |
| 684 | +translate$E $** > $@ |
| 685 | |
| 686 | $(OBJDIR)\style$O : style_.c style.h |
| 687 | $(TCC) -o$@ -c style_.c |
| 688 | |
| 689 | style_.c : $(SRCDIR)\style.c |
| @@ -826,7 +832,7 @@ | |
| 832 | |
| 833 | zip_.c : $(SRCDIR)\zip.c |
| 834 | +translate$E $** > $@ |
| 835 | |
| 836 | headers: makeheaders$E page_index.h builtin_data.h VERSION.h |
| 837 | +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.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 captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h export_.c:export.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h fusefs_.c:fusefs.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.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 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 md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.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 report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.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 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 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 wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h |
| 838 | @copy /Y nul: headers |
| 839 |
+42
-4
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -146,12 +146,13 @@ | ||
| 146 | 146 | #### The directories where the OpenSSL include and library files are located. |
| 147 | 147 | # The recommended usage here is to use the Sysinternals junction tool |
| 148 | 148 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 149 | 149 | # Fossil source code directory and the target OpenSSL source directory. |
| 150 | 150 | # |
| 151 | -OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1j/include | |
| 152 | -OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1j | |
| 151 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2 | |
| 152 | +OPENSSLINCDIR = $(OPENSSLDIR)/include | |
| 153 | +OPENSSLLIBDIR = $(OPENSSLDIR) | |
| 153 | 154 | |
| 154 | 155 | #### Either the directory where the Tcl library is installed or the Tcl |
| 155 | 156 | # source code directory resides (depending on the value of the macro |
| 156 | 157 | # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, |
| 157 | 158 | # this directory must have "include" and "lib" sub-directories. If |
| @@ -456,10 +457,11 @@ | ||
| 456 | 457 | $(SRCDIR)/sitemap.c \ |
| 457 | 458 | $(SRCDIR)/skins.c \ |
| 458 | 459 | $(SRCDIR)/sqlcmd.c \ |
| 459 | 460 | $(SRCDIR)/stash.c \ |
| 460 | 461 | $(SRCDIR)/stat.c \ |
| 462 | + $(SRCDIR)/statrep.c \ | |
| 461 | 463 | $(SRCDIR)/style.c \ |
| 462 | 464 | $(SRCDIR)/sync.c \ |
| 463 | 465 | $(SRCDIR)/tag.c \ |
| 464 | 466 | $(SRCDIR)/tar.c \ |
| 465 | 467 | $(SRCDIR)/th_main.c \ |
| @@ -483,11 +485,36 @@ | ||
| 483 | 485 | $(SRCDIR)/xfer.c \ |
| 484 | 486 | $(SRCDIR)/xfersetup.c \ |
| 485 | 487 | $(SRCDIR)/zip.c |
| 486 | 488 | |
| 487 | 489 | EXTRA_FILES = \ |
| 488 | - $(SRCDIR)/diff.tcl | |
| 490 | + $(SRCDIR)/../skins/black_and_white/css.txt \ | |
| 491 | + $(SRCDIR)/../skins/black_and_white/footer.txt \ | |
| 492 | + $(SRCDIR)/../skins/black_and_white/header.txt \ | |
| 493 | + $(SRCDIR)/../skins/default/css.txt \ | |
| 494 | + $(SRCDIR)/../skins/default/footer.txt \ | |
| 495 | + $(SRCDIR)/../skins/default/header.txt \ | |
| 496 | + $(SRCDIR)/../skins/eagle/css.txt \ | |
| 497 | + $(SRCDIR)/../skins/eagle/footer.txt \ | |
| 498 | + $(SRCDIR)/../skins/eagle/header.txt \ | |
| 499 | + $(SRCDIR)/../skins/enhanced1/css.txt \ | |
| 500 | + $(SRCDIR)/../skins/enhanced1/footer.txt \ | |
| 501 | + $(SRCDIR)/../skins/enhanced1/header.txt \ | |
| 502 | + $(SRCDIR)/../skins/etienne1/css.txt \ | |
| 503 | + $(SRCDIR)/../skins/etienne1/footer.txt \ | |
| 504 | + $(SRCDIR)/../skins/etienne1/header.txt \ | |
| 505 | + $(SRCDIR)/../skins/khaki/css.txt \ | |
| 506 | + $(SRCDIR)/../skins/khaki/footer.txt \ | |
| 507 | + $(SRCDIR)/../skins/khaki/header.txt \ | |
| 508 | + $(SRCDIR)/../skins/plain_gray/css.txt \ | |
| 509 | + $(SRCDIR)/../skins/plain_gray/footer.txt \ | |
| 510 | + $(SRCDIR)/../skins/plain_gray/header.txt \ | |
| 511 | + $(SRCDIR)/../skins/rounded1/css.txt \ | |
| 512 | + $(SRCDIR)/../skins/rounded1/footer.txt \ | |
| 513 | + $(SRCDIR)/../skins/rounded1/header.txt \ | |
| 514 | + $(SRCDIR)/diff.tcl \ | |
| 515 | + $(SRCDIR)/markdown.md | |
| 489 | 516 | |
| 490 | 517 | TRANS_SRC = \ |
| 491 | 518 | $(OBJDIR)/add_.c \ |
| 492 | 519 | $(OBJDIR)/allrepo_.c \ |
| 493 | 520 | $(OBJDIR)/attach_.c \ |
| @@ -578,10 +605,11 @@ | ||
| 578 | 605 | $(OBJDIR)/sitemap_.c \ |
| 579 | 606 | $(OBJDIR)/skins_.c \ |
| 580 | 607 | $(OBJDIR)/sqlcmd_.c \ |
| 581 | 608 | $(OBJDIR)/stash_.c \ |
| 582 | 609 | $(OBJDIR)/stat_.c \ |
| 610 | + $(OBJDIR)/statrep_.c \ | |
| 583 | 611 | $(OBJDIR)/style_.c \ |
| 584 | 612 | $(OBJDIR)/sync_.c \ |
| 585 | 613 | $(OBJDIR)/tag_.c \ |
| 586 | 614 | $(OBJDIR)/tar_.c \ |
| 587 | 615 | $(OBJDIR)/th_main_.c \ |
| @@ -697,10 +725,11 @@ | ||
| 697 | 725 | $(OBJDIR)/sitemap.o \ |
| 698 | 726 | $(OBJDIR)/skins.o \ |
| 699 | 727 | $(OBJDIR)/sqlcmd.o \ |
| 700 | 728 | $(OBJDIR)/stash.o \ |
| 701 | 729 | $(OBJDIR)/stat.o \ |
| 730 | + $(OBJDIR)/statrep.o \ | |
| 702 | 731 | $(OBJDIR)/style.o \ |
| 703 | 732 | $(OBJDIR)/sync.o \ |
| 704 | 733 | $(OBJDIR)/tag.o \ |
| 705 | 734 | $(OBJDIR)/tar.o \ |
| 706 | 735 | $(OBJDIR)/th_main.o \ |
| @@ -914,11 +943,11 @@ | ||
| 914 | 943 | |
| 915 | 944 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(MKINDEX) |
| 916 | 945 | $(MKINDEX) $(TRANS_SRC) >$@ |
| 917 | 946 | |
| 918 | 947 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) |
| 919 | - $(MKBUILTIN) $(EXTRA_FILES) >$@ | |
| 948 | + $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ | |
| 920 | 949 | |
| 921 | 950 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 922 | 951 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 923 | 952 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 924 | 953 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| @@ -1009,10 +1038,11 @@ | ||
| 1009 | 1038 | $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ |
| 1010 | 1039 | $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ |
| 1011 | 1040 | $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ |
| 1012 | 1041 | $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ |
| 1013 | 1042 | $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ |
| 1043 | + $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ | |
| 1014 | 1044 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1015 | 1045 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1016 | 1046 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1017 | 1047 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1018 | 1048 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| @@ -1778,10 +1808,18 @@ | ||
| 1778 | 1808 | |
| 1779 | 1809 | $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h |
| 1780 | 1810 | $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c |
| 1781 | 1811 | |
| 1782 | 1812 | $(OBJDIR)/stat.h: $(OBJDIR)/headers |
| 1813 | + | |
| 1814 | +$(OBJDIR)/statrep_.c: $(SRCDIR)/statrep.c $(TRANSLATE) | |
| 1815 | + $(TRANSLATE) $(SRCDIR)/statrep.c >$@ | |
| 1816 | + | |
| 1817 | +$(OBJDIR)/statrep.o: $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h $(SRCDIR)/config.h | |
| 1818 | + $(XTCC) -o $(OBJDIR)/statrep.o -c $(OBJDIR)/statrep_.c | |
| 1819 | + | |
| 1820 | +$(OBJDIR)/statrep.h: $(OBJDIR)/headers | |
| 1783 | 1821 | |
| 1784 | 1822 | $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(TRANSLATE) |
| 1785 | 1823 | $(TRANSLATE) $(SRCDIR)/style.c >$@ |
| 1786 | 1824 | |
| 1787 | 1825 | $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h |
| 1788 | 1826 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -146,12 +146,13 @@ | |
| 146 | #### The directories where the OpenSSL include and library files are located. |
| 147 | # The recommended usage here is to use the Sysinternals junction tool |
| 148 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 149 | # Fossil source code directory and the target OpenSSL source directory. |
| 150 | # |
| 151 | OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1j/include |
| 152 | OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1j |
| 153 | |
| 154 | #### Either the directory where the Tcl library is installed or the Tcl |
| 155 | # source code directory resides (depending on the value of the macro |
| 156 | # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, |
| 157 | # this directory must have "include" and "lib" sub-directories. If |
| @@ -456,10 +457,11 @@ | |
| 456 | $(SRCDIR)/sitemap.c \ |
| 457 | $(SRCDIR)/skins.c \ |
| 458 | $(SRCDIR)/sqlcmd.c \ |
| 459 | $(SRCDIR)/stash.c \ |
| 460 | $(SRCDIR)/stat.c \ |
| 461 | $(SRCDIR)/style.c \ |
| 462 | $(SRCDIR)/sync.c \ |
| 463 | $(SRCDIR)/tag.c \ |
| 464 | $(SRCDIR)/tar.c \ |
| 465 | $(SRCDIR)/th_main.c \ |
| @@ -483,11 +485,36 @@ | |
| 483 | $(SRCDIR)/xfer.c \ |
| 484 | $(SRCDIR)/xfersetup.c \ |
| 485 | $(SRCDIR)/zip.c |
| 486 | |
| 487 | EXTRA_FILES = \ |
| 488 | $(SRCDIR)/diff.tcl |
| 489 | |
| 490 | TRANS_SRC = \ |
| 491 | $(OBJDIR)/add_.c \ |
| 492 | $(OBJDIR)/allrepo_.c \ |
| 493 | $(OBJDIR)/attach_.c \ |
| @@ -578,10 +605,11 @@ | |
| 578 | $(OBJDIR)/sitemap_.c \ |
| 579 | $(OBJDIR)/skins_.c \ |
| 580 | $(OBJDIR)/sqlcmd_.c \ |
| 581 | $(OBJDIR)/stash_.c \ |
| 582 | $(OBJDIR)/stat_.c \ |
| 583 | $(OBJDIR)/style_.c \ |
| 584 | $(OBJDIR)/sync_.c \ |
| 585 | $(OBJDIR)/tag_.c \ |
| 586 | $(OBJDIR)/tar_.c \ |
| 587 | $(OBJDIR)/th_main_.c \ |
| @@ -697,10 +725,11 @@ | |
| 697 | $(OBJDIR)/sitemap.o \ |
| 698 | $(OBJDIR)/skins.o \ |
| 699 | $(OBJDIR)/sqlcmd.o \ |
| 700 | $(OBJDIR)/stash.o \ |
| 701 | $(OBJDIR)/stat.o \ |
| 702 | $(OBJDIR)/style.o \ |
| 703 | $(OBJDIR)/sync.o \ |
| 704 | $(OBJDIR)/tag.o \ |
| 705 | $(OBJDIR)/tar.o \ |
| 706 | $(OBJDIR)/th_main.o \ |
| @@ -914,11 +943,11 @@ | |
| 914 | |
| 915 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(MKINDEX) |
| 916 | $(MKINDEX) $(TRANS_SRC) >$@ |
| 917 | |
| 918 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) |
| 919 | $(MKBUILTIN) $(EXTRA_FILES) >$@ |
| 920 | |
| 921 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 922 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 923 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 924 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| @@ -1009,10 +1038,11 @@ | |
| 1009 | $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ |
| 1010 | $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ |
| 1011 | $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ |
| 1012 | $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ |
| 1013 | $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ |
| 1014 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1015 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1016 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1017 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1018 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| @@ -1778,10 +1808,18 @@ | |
| 1778 | |
| 1779 | $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h |
| 1780 | $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c |
| 1781 | |
| 1782 | $(OBJDIR)/stat.h: $(OBJDIR)/headers |
| 1783 | |
| 1784 | $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(TRANSLATE) |
| 1785 | $(TRANSLATE) $(SRCDIR)/style.c >$@ |
| 1786 | |
| 1787 | $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h |
| 1788 |
| --- win/Makefile.mingw | |
| +++ win/Makefile.mingw | |
| @@ -146,12 +146,13 @@ | |
| 146 | #### The directories where the OpenSSL include and library files are located. |
| 147 | # The recommended usage here is to use the Sysinternals junction tool |
| 148 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 149 | # Fossil source code directory and the target OpenSSL source directory. |
| 150 | # |
| 151 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2 |
| 152 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 153 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 154 | |
| 155 | #### Either the directory where the Tcl library is installed or the Tcl |
| 156 | # source code directory resides (depending on the value of the macro |
| 157 | # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, |
| 158 | # this directory must have "include" and "lib" sub-directories. If |
| @@ -456,10 +457,11 @@ | |
| 457 | $(SRCDIR)/sitemap.c \ |
| 458 | $(SRCDIR)/skins.c \ |
| 459 | $(SRCDIR)/sqlcmd.c \ |
| 460 | $(SRCDIR)/stash.c \ |
| 461 | $(SRCDIR)/stat.c \ |
| 462 | $(SRCDIR)/statrep.c \ |
| 463 | $(SRCDIR)/style.c \ |
| 464 | $(SRCDIR)/sync.c \ |
| 465 | $(SRCDIR)/tag.c \ |
| 466 | $(SRCDIR)/tar.c \ |
| 467 | $(SRCDIR)/th_main.c \ |
| @@ -483,11 +485,36 @@ | |
| 485 | $(SRCDIR)/xfer.c \ |
| 486 | $(SRCDIR)/xfersetup.c \ |
| 487 | $(SRCDIR)/zip.c |
| 488 | |
| 489 | EXTRA_FILES = \ |
| 490 | $(SRCDIR)/../skins/black_and_white/css.txt \ |
| 491 | $(SRCDIR)/../skins/black_and_white/footer.txt \ |
| 492 | $(SRCDIR)/../skins/black_and_white/header.txt \ |
| 493 | $(SRCDIR)/../skins/default/css.txt \ |
| 494 | $(SRCDIR)/../skins/default/footer.txt \ |
| 495 | $(SRCDIR)/../skins/default/header.txt \ |
| 496 | $(SRCDIR)/../skins/eagle/css.txt \ |
| 497 | $(SRCDIR)/../skins/eagle/footer.txt \ |
| 498 | $(SRCDIR)/../skins/eagle/header.txt \ |
| 499 | $(SRCDIR)/../skins/enhanced1/css.txt \ |
| 500 | $(SRCDIR)/../skins/enhanced1/footer.txt \ |
| 501 | $(SRCDIR)/../skins/enhanced1/header.txt \ |
| 502 | $(SRCDIR)/../skins/etienne1/css.txt \ |
| 503 | $(SRCDIR)/../skins/etienne1/footer.txt \ |
| 504 | $(SRCDIR)/../skins/etienne1/header.txt \ |
| 505 | $(SRCDIR)/../skins/khaki/css.txt \ |
| 506 | $(SRCDIR)/../skins/khaki/footer.txt \ |
| 507 | $(SRCDIR)/../skins/khaki/header.txt \ |
| 508 | $(SRCDIR)/../skins/plain_gray/css.txt \ |
| 509 | $(SRCDIR)/../skins/plain_gray/footer.txt \ |
| 510 | $(SRCDIR)/../skins/plain_gray/header.txt \ |
| 511 | $(SRCDIR)/../skins/rounded1/css.txt \ |
| 512 | $(SRCDIR)/../skins/rounded1/footer.txt \ |
| 513 | $(SRCDIR)/../skins/rounded1/header.txt \ |
| 514 | $(SRCDIR)/diff.tcl \ |
| 515 | $(SRCDIR)/markdown.md |
| 516 | |
| 517 | TRANS_SRC = \ |
| 518 | $(OBJDIR)/add_.c \ |
| 519 | $(OBJDIR)/allrepo_.c \ |
| 520 | $(OBJDIR)/attach_.c \ |
| @@ -578,10 +605,11 @@ | |
| 605 | $(OBJDIR)/sitemap_.c \ |
| 606 | $(OBJDIR)/skins_.c \ |
| 607 | $(OBJDIR)/sqlcmd_.c \ |
| 608 | $(OBJDIR)/stash_.c \ |
| 609 | $(OBJDIR)/stat_.c \ |
| 610 | $(OBJDIR)/statrep_.c \ |
| 611 | $(OBJDIR)/style_.c \ |
| 612 | $(OBJDIR)/sync_.c \ |
| 613 | $(OBJDIR)/tag_.c \ |
| 614 | $(OBJDIR)/tar_.c \ |
| 615 | $(OBJDIR)/th_main_.c \ |
| @@ -697,10 +725,11 @@ | |
| 725 | $(OBJDIR)/sitemap.o \ |
| 726 | $(OBJDIR)/skins.o \ |
| 727 | $(OBJDIR)/sqlcmd.o \ |
| 728 | $(OBJDIR)/stash.o \ |
| 729 | $(OBJDIR)/stat.o \ |
| 730 | $(OBJDIR)/statrep.o \ |
| 731 | $(OBJDIR)/style.o \ |
| 732 | $(OBJDIR)/sync.o \ |
| 733 | $(OBJDIR)/tag.o \ |
| 734 | $(OBJDIR)/tar.o \ |
| 735 | $(OBJDIR)/th_main.o \ |
| @@ -914,11 +943,11 @@ | |
| 943 | |
| 944 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(MKINDEX) |
| 945 | $(MKINDEX) $(TRANS_SRC) >$@ |
| 946 | |
| 947 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) |
| 948 | $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ |
| 949 | |
| 950 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 951 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 952 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 953 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| @@ -1009,10 +1038,11 @@ | |
| 1038 | $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ |
| 1039 | $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ |
| 1040 | $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ |
| 1041 | $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ |
| 1042 | $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ |
| 1043 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 1044 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1045 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1046 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1047 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1048 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| @@ -1778,10 +1808,18 @@ | |
| 1808 | |
| 1809 | $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h |
| 1810 | $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c |
| 1811 | |
| 1812 | $(OBJDIR)/stat.h: $(OBJDIR)/headers |
| 1813 | |
| 1814 | $(OBJDIR)/statrep_.c: $(SRCDIR)/statrep.c $(TRANSLATE) |
| 1815 | $(TRANSLATE) $(SRCDIR)/statrep.c >$@ |
| 1816 | |
| 1817 | $(OBJDIR)/statrep.o: $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h $(SRCDIR)/config.h |
| 1818 | $(XTCC) -o $(OBJDIR)/statrep.o -c $(OBJDIR)/statrep_.c |
| 1819 | |
| 1820 | $(OBJDIR)/statrep.h: $(OBJDIR)/headers |
| 1821 | |
| 1822 | $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(TRANSLATE) |
| 1823 | $(TRANSLATE) $(SRCDIR)/style.c >$@ |
| 1824 | |
| 1825 | $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h |
| 1826 |
+42
-4
| --- win/Makefile.mingw.mistachkin | ||
| +++ win/Makefile.mingw.mistachkin | ||
| @@ -146,12 +146,13 @@ | ||
| 146 | 146 | #### The directories where the OpenSSL include and library files are located. |
| 147 | 147 | # The recommended usage here is to use the Sysinternals junction tool |
| 148 | 148 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 149 | 149 | # Fossil source code directory and the target OpenSSL source directory. |
| 150 | 150 | # |
| 151 | -OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1j/include | |
| 152 | -OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1j | |
| 151 | +OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2 | |
| 152 | +OPENSSLINCDIR = $(OPENSSLDIR)/include | |
| 153 | +OPENSSLLIBDIR = $(OPENSSLDIR) | |
| 153 | 154 | |
| 154 | 155 | #### Either the directory where the Tcl library is installed or the Tcl |
| 155 | 156 | # source code directory resides (depending on the value of the macro |
| 156 | 157 | # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, |
| 157 | 158 | # this directory must have "include" and "lib" sub-directories. If |
| @@ -456,10 +457,11 @@ | ||
| 456 | 457 | $(SRCDIR)/sitemap.c \ |
| 457 | 458 | $(SRCDIR)/skins.c \ |
| 458 | 459 | $(SRCDIR)/sqlcmd.c \ |
| 459 | 460 | $(SRCDIR)/stash.c \ |
| 460 | 461 | $(SRCDIR)/stat.c \ |
| 462 | + $(SRCDIR)/statrep.c \ | |
| 461 | 463 | $(SRCDIR)/style.c \ |
| 462 | 464 | $(SRCDIR)/sync.c \ |
| 463 | 465 | $(SRCDIR)/tag.c \ |
| 464 | 466 | $(SRCDIR)/tar.c \ |
| 465 | 467 | $(SRCDIR)/th_main.c \ |
| @@ -483,11 +485,36 @@ | ||
| 483 | 485 | $(SRCDIR)/xfer.c \ |
| 484 | 486 | $(SRCDIR)/xfersetup.c \ |
| 485 | 487 | $(SRCDIR)/zip.c |
| 486 | 488 | |
| 487 | 489 | EXTRA_FILES = \ |
| 488 | - $(SRCDIR)/diff.tcl | |
| 490 | + $(SRCDIR)/../skins/black_and_white/css.txt \ | |
| 491 | + $(SRCDIR)/../skins/black_and_white/footer.txt \ | |
| 492 | + $(SRCDIR)/../skins/black_and_white/header.txt \ | |
| 493 | + $(SRCDIR)/../skins/default/css.txt \ | |
| 494 | + $(SRCDIR)/../skins/default/footer.txt \ | |
| 495 | + $(SRCDIR)/../skins/default/header.txt \ | |
| 496 | + $(SRCDIR)/../skins/eagle/css.txt \ | |
| 497 | + $(SRCDIR)/../skins/eagle/footer.txt \ | |
| 498 | + $(SRCDIR)/../skins/eagle/header.txt \ | |
| 499 | + $(SRCDIR)/../skins/enhanced1/css.txt \ | |
| 500 | + $(SRCDIR)/../skins/enhanced1/footer.txt \ | |
| 501 | + $(SRCDIR)/../skins/enhanced1/header.txt \ | |
| 502 | + $(SRCDIR)/../skins/etienne1/css.txt \ | |
| 503 | + $(SRCDIR)/../skins/etienne1/footer.txt \ | |
| 504 | + $(SRCDIR)/../skins/etienne1/header.txt \ | |
| 505 | + $(SRCDIR)/../skins/khaki/css.txt \ | |
| 506 | + $(SRCDIR)/../skins/khaki/footer.txt \ | |
| 507 | + $(SRCDIR)/../skins/khaki/header.txt \ | |
| 508 | + $(SRCDIR)/../skins/plain_gray/css.txt \ | |
| 509 | + $(SRCDIR)/../skins/plain_gray/footer.txt \ | |
| 510 | + $(SRCDIR)/../skins/plain_gray/header.txt \ | |
| 511 | + $(SRCDIR)/../skins/rounded1/css.txt \ | |
| 512 | + $(SRCDIR)/../skins/rounded1/footer.txt \ | |
| 513 | + $(SRCDIR)/../skins/rounded1/header.txt \ | |
| 514 | + $(SRCDIR)/diff.tcl \ | |
| 515 | + $(SRCDIR)/markdown.md | |
| 489 | 516 | |
| 490 | 517 | TRANS_SRC = \ |
| 491 | 518 | $(OBJDIR)/add_.c \ |
| 492 | 519 | $(OBJDIR)/allrepo_.c \ |
| 493 | 520 | $(OBJDIR)/attach_.c \ |
| @@ -578,10 +605,11 @@ | ||
| 578 | 605 | $(OBJDIR)/sitemap_.c \ |
| 579 | 606 | $(OBJDIR)/skins_.c \ |
| 580 | 607 | $(OBJDIR)/sqlcmd_.c \ |
| 581 | 608 | $(OBJDIR)/stash_.c \ |
| 582 | 609 | $(OBJDIR)/stat_.c \ |
| 610 | + $(OBJDIR)/statrep_.c \ | |
| 583 | 611 | $(OBJDIR)/style_.c \ |
| 584 | 612 | $(OBJDIR)/sync_.c \ |
| 585 | 613 | $(OBJDIR)/tag_.c \ |
| 586 | 614 | $(OBJDIR)/tar_.c \ |
| 587 | 615 | $(OBJDIR)/th_main_.c \ |
| @@ -697,10 +725,11 @@ | ||
| 697 | 725 | $(OBJDIR)/sitemap.o \ |
| 698 | 726 | $(OBJDIR)/skins.o \ |
| 699 | 727 | $(OBJDIR)/sqlcmd.o \ |
| 700 | 728 | $(OBJDIR)/stash.o \ |
| 701 | 729 | $(OBJDIR)/stat.o \ |
| 730 | + $(OBJDIR)/statrep.o \ | |
| 702 | 731 | $(OBJDIR)/style.o \ |
| 703 | 732 | $(OBJDIR)/sync.o \ |
| 704 | 733 | $(OBJDIR)/tag.o \ |
| 705 | 734 | $(OBJDIR)/tar.o \ |
| 706 | 735 | $(OBJDIR)/th_main.o \ |
| @@ -914,11 +943,11 @@ | ||
| 914 | 943 | |
| 915 | 944 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(MKINDEX) |
| 916 | 945 | $(MKINDEX) $(TRANS_SRC) >$@ |
| 917 | 946 | |
| 918 | 947 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) |
| 919 | - $(MKBUILTIN) $(EXTRA_FILES) >$@ | |
| 948 | + $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ | |
| 920 | 949 | |
| 921 | 950 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 922 | 951 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 923 | 952 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 924 | 953 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| @@ -1009,10 +1038,11 @@ | ||
| 1009 | 1038 | $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ |
| 1010 | 1039 | $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ |
| 1011 | 1040 | $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ |
| 1012 | 1041 | $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ |
| 1013 | 1042 | $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ |
| 1043 | + $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ | |
| 1014 | 1044 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1015 | 1045 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1016 | 1046 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1017 | 1047 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1018 | 1048 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| @@ -1778,10 +1808,18 @@ | ||
| 1778 | 1808 | |
| 1779 | 1809 | $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h |
| 1780 | 1810 | $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c |
| 1781 | 1811 | |
| 1782 | 1812 | $(OBJDIR)/stat.h: $(OBJDIR)/headers |
| 1813 | + | |
| 1814 | +$(OBJDIR)/statrep_.c: $(SRCDIR)/statrep.c $(TRANSLATE) | |
| 1815 | + $(TRANSLATE) $(SRCDIR)/statrep.c >$@ | |
| 1816 | + | |
| 1817 | +$(OBJDIR)/statrep.o: $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h $(SRCDIR)/config.h | |
| 1818 | + $(XTCC) -o $(OBJDIR)/statrep.o -c $(OBJDIR)/statrep_.c | |
| 1819 | + | |
| 1820 | +$(OBJDIR)/statrep.h: $(OBJDIR)/headers | |
| 1783 | 1821 | |
| 1784 | 1822 | $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(TRANSLATE) |
| 1785 | 1823 | $(TRANSLATE) $(SRCDIR)/style.c >$@ |
| 1786 | 1824 | |
| 1787 | 1825 | $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h |
| 1788 | 1826 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -146,12 +146,13 @@ | |
| 146 | #### The directories where the OpenSSL include and library files are located. |
| 147 | # The recommended usage here is to use the Sysinternals junction tool |
| 148 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 149 | # Fossil source code directory and the target OpenSSL source directory. |
| 150 | # |
| 151 | OPENSSLINCDIR = $(SRCDIR)/../compat/openssl-1.0.1j/include |
| 152 | OPENSSLLIBDIR = $(SRCDIR)/../compat/openssl-1.0.1j |
| 153 | |
| 154 | #### Either the directory where the Tcl library is installed or the Tcl |
| 155 | # source code directory resides (depending on the value of the macro |
| 156 | # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, |
| 157 | # this directory must have "include" and "lib" sub-directories. If |
| @@ -456,10 +457,11 @@ | |
| 456 | $(SRCDIR)/sitemap.c \ |
| 457 | $(SRCDIR)/skins.c \ |
| 458 | $(SRCDIR)/sqlcmd.c \ |
| 459 | $(SRCDIR)/stash.c \ |
| 460 | $(SRCDIR)/stat.c \ |
| 461 | $(SRCDIR)/style.c \ |
| 462 | $(SRCDIR)/sync.c \ |
| 463 | $(SRCDIR)/tag.c \ |
| 464 | $(SRCDIR)/tar.c \ |
| 465 | $(SRCDIR)/th_main.c \ |
| @@ -483,11 +485,36 @@ | |
| 483 | $(SRCDIR)/xfer.c \ |
| 484 | $(SRCDIR)/xfersetup.c \ |
| 485 | $(SRCDIR)/zip.c |
| 486 | |
| 487 | EXTRA_FILES = \ |
| 488 | $(SRCDIR)/diff.tcl |
| 489 | |
| 490 | TRANS_SRC = \ |
| 491 | $(OBJDIR)/add_.c \ |
| 492 | $(OBJDIR)/allrepo_.c \ |
| 493 | $(OBJDIR)/attach_.c \ |
| @@ -578,10 +605,11 @@ | |
| 578 | $(OBJDIR)/sitemap_.c \ |
| 579 | $(OBJDIR)/skins_.c \ |
| 580 | $(OBJDIR)/sqlcmd_.c \ |
| 581 | $(OBJDIR)/stash_.c \ |
| 582 | $(OBJDIR)/stat_.c \ |
| 583 | $(OBJDIR)/style_.c \ |
| 584 | $(OBJDIR)/sync_.c \ |
| 585 | $(OBJDIR)/tag_.c \ |
| 586 | $(OBJDIR)/tar_.c \ |
| 587 | $(OBJDIR)/th_main_.c \ |
| @@ -697,10 +725,11 @@ | |
| 697 | $(OBJDIR)/sitemap.o \ |
| 698 | $(OBJDIR)/skins.o \ |
| 699 | $(OBJDIR)/sqlcmd.o \ |
| 700 | $(OBJDIR)/stash.o \ |
| 701 | $(OBJDIR)/stat.o \ |
| 702 | $(OBJDIR)/style.o \ |
| 703 | $(OBJDIR)/sync.o \ |
| 704 | $(OBJDIR)/tag.o \ |
| 705 | $(OBJDIR)/tar.o \ |
| 706 | $(OBJDIR)/th_main.o \ |
| @@ -914,11 +943,11 @@ | |
| 914 | |
| 915 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(MKINDEX) |
| 916 | $(MKINDEX) $(TRANS_SRC) >$@ |
| 917 | |
| 918 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) |
| 919 | $(MKBUILTIN) $(EXTRA_FILES) >$@ |
| 920 | |
| 921 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 922 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 923 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 924 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| @@ -1009,10 +1038,11 @@ | |
| 1009 | $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ |
| 1010 | $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ |
| 1011 | $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ |
| 1012 | $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ |
| 1013 | $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ |
| 1014 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1015 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1016 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1017 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1018 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| @@ -1778,10 +1808,18 @@ | |
| 1778 | |
| 1779 | $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h |
| 1780 | $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c |
| 1781 | |
| 1782 | $(OBJDIR)/stat.h: $(OBJDIR)/headers |
| 1783 | |
| 1784 | $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(TRANSLATE) |
| 1785 | $(TRANSLATE) $(SRCDIR)/style.c >$@ |
| 1786 | |
| 1787 | $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h |
| 1788 |
| --- win/Makefile.mingw.mistachkin | |
| +++ win/Makefile.mingw.mistachkin | |
| @@ -146,12 +146,13 @@ | |
| 146 | #### The directories where the OpenSSL include and library files are located. |
| 147 | # The recommended usage here is to use the Sysinternals junction tool |
| 148 | # to create a hard link between an "openssl-1.x" sub-directory of the |
| 149 | # Fossil source code directory and the target OpenSSL source directory. |
| 150 | # |
| 151 | OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2 |
| 152 | OPENSSLINCDIR = $(OPENSSLDIR)/include |
| 153 | OPENSSLLIBDIR = $(OPENSSLDIR) |
| 154 | |
| 155 | #### Either the directory where the Tcl library is installed or the Tcl |
| 156 | # source code directory resides (depending on the value of the macro |
| 157 | # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, |
| 158 | # this directory must have "include" and "lib" sub-directories. If |
| @@ -456,10 +457,11 @@ | |
| 457 | $(SRCDIR)/sitemap.c \ |
| 458 | $(SRCDIR)/skins.c \ |
| 459 | $(SRCDIR)/sqlcmd.c \ |
| 460 | $(SRCDIR)/stash.c \ |
| 461 | $(SRCDIR)/stat.c \ |
| 462 | $(SRCDIR)/statrep.c \ |
| 463 | $(SRCDIR)/style.c \ |
| 464 | $(SRCDIR)/sync.c \ |
| 465 | $(SRCDIR)/tag.c \ |
| 466 | $(SRCDIR)/tar.c \ |
| 467 | $(SRCDIR)/th_main.c \ |
| @@ -483,11 +485,36 @@ | |
| 485 | $(SRCDIR)/xfer.c \ |
| 486 | $(SRCDIR)/xfersetup.c \ |
| 487 | $(SRCDIR)/zip.c |
| 488 | |
| 489 | EXTRA_FILES = \ |
| 490 | $(SRCDIR)/../skins/black_and_white/css.txt \ |
| 491 | $(SRCDIR)/../skins/black_and_white/footer.txt \ |
| 492 | $(SRCDIR)/../skins/black_and_white/header.txt \ |
| 493 | $(SRCDIR)/../skins/default/css.txt \ |
| 494 | $(SRCDIR)/../skins/default/footer.txt \ |
| 495 | $(SRCDIR)/../skins/default/header.txt \ |
| 496 | $(SRCDIR)/../skins/eagle/css.txt \ |
| 497 | $(SRCDIR)/../skins/eagle/footer.txt \ |
| 498 | $(SRCDIR)/../skins/eagle/header.txt \ |
| 499 | $(SRCDIR)/../skins/enhanced1/css.txt \ |
| 500 | $(SRCDIR)/../skins/enhanced1/footer.txt \ |
| 501 | $(SRCDIR)/../skins/enhanced1/header.txt \ |
| 502 | $(SRCDIR)/../skins/etienne1/css.txt \ |
| 503 | $(SRCDIR)/../skins/etienne1/footer.txt \ |
| 504 | $(SRCDIR)/../skins/etienne1/header.txt \ |
| 505 | $(SRCDIR)/../skins/khaki/css.txt \ |
| 506 | $(SRCDIR)/../skins/khaki/footer.txt \ |
| 507 | $(SRCDIR)/../skins/khaki/header.txt \ |
| 508 | $(SRCDIR)/../skins/plain_gray/css.txt \ |
| 509 | $(SRCDIR)/../skins/plain_gray/footer.txt \ |
| 510 | $(SRCDIR)/../skins/plain_gray/header.txt \ |
| 511 | $(SRCDIR)/../skins/rounded1/css.txt \ |
| 512 | $(SRCDIR)/../skins/rounded1/footer.txt \ |
| 513 | $(SRCDIR)/../skins/rounded1/header.txt \ |
| 514 | $(SRCDIR)/diff.tcl \ |
| 515 | $(SRCDIR)/markdown.md |
| 516 | |
| 517 | TRANS_SRC = \ |
| 518 | $(OBJDIR)/add_.c \ |
| 519 | $(OBJDIR)/allrepo_.c \ |
| 520 | $(OBJDIR)/attach_.c \ |
| @@ -578,10 +605,11 @@ | |
| 605 | $(OBJDIR)/sitemap_.c \ |
| 606 | $(OBJDIR)/skins_.c \ |
| 607 | $(OBJDIR)/sqlcmd_.c \ |
| 608 | $(OBJDIR)/stash_.c \ |
| 609 | $(OBJDIR)/stat_.c \ |
| 610 | $(OBJDIR)/statrep_.c \ |
| 611 | $(OBJDIR)/style_.c \ |
| 612 | $(OBJDIR)/sync_.c \ |
| 613 | $(OBJDIR)/tag_.c \ |
| 614 | $(OBJDIR)/tar_.c \ |
| 615 | $(OBJDIR)/th_main_.c \ |
| @@ -697,10 +725,11 @@ | |
| 725 | $(OBJDIR)/sitemap.o \ |
| 726 | $(OBJDIR)/skins.o \ |
| 727 | $(OBJDIR)/sqlcmd.o \ |
| 728 | $(OBJDIR)/stash.o \ |
| 729 | $(OBJDIR)/stat.o \ |
| 730 | $(OBJDIR)/statrep.o \ |
| 731 | $(OBJDIR)/style.o \ |
| 732 | $(OBJDIR)/sync.o \ |
| 733 | $(OBJDIR)/tag.o \ |
| 734 | $(OBJDIR)/tar.o \ |
| 735 | $(OBJDIR)/th_main.o \ |
| @@ -914,11 +943,11 @@ | |
| 943 | |
| 944 | $(OBJDIR)/page_index.h: $(TRANS_SRC) $(MKINDEX) |
| 945 | $(MKINDEX) $(TRANS_SRC) >$@ |
| 946 | |
| 947 | $(OBJDIR)/builtin_data.h: $(MKBUILTIN) $(EXTRA_FILES) |
| 948 | $(MKBUILTIN) --prefix $(SRCDIR)/ $(EXTRA_FILES) >$@ |
| 949 | |
| 950 | $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h |
| 951 | $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \ |
| 952 | $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \ |
| 953 | $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \ |
| @@ -1009,10 +1038,11 @@ | |
| 1038 | $(OBJDIR)/sitemap_.c:$(OBJDIR)/sitemap.h \ |
| 1039 | $(OBJDIR)/skins_.c:$(OBJDIR)/skins.h \ |
| 1040 | $(OBJDIR)/sqlcmd_.c:$(OBJDIR)/sqlcmd.h \ |
| 1041 | $(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \ |
| 1042 | $(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \ |
| 1043 | $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \ |
| 1044 | $(OBJDIR)/style_.c:$(OBJDIR)/style.h \ |
| 1045 | $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \ |
| 1046 | $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \ |
| 1047 | $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \ |
| 1048 | $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \ |
| @@ -1778,10 +1808,18 @@ | |
| 1808 | |
| 1809 | $(OBJDIR)/stat.o: $(OBJDIR)/stat_.c $(OBJDIR)/stat.h $(SRCDIR)/config.h |
| 1810 | $(XTCC) -o $(OBJDIR)/stat.o -c $(OBJDIR)/stat_.c |
| 1811 | |
| 1812 | $(OBJDIR)/stat.h: $(OBJDIR)/headers |
| 1813 | |
| 1814 | $(OBJDIR)/statrep_.c: $(SRCDIR)/statrep.c $(TRANSLATE) |
| 1815 | $(TRANSLATE) $(SRCDIR)/statrep.c >$@ |
| 1816 | |
| 1817 | $(OBJDIR)/statrep.o: $(OBJDIR)/statrep_.c $(OBJDIR)/statrep.h $(SRCDIR)/config.h |
| 1818 | $(XTCC) -o $(OBJDIR)/statrep.o -c $(OBJDIR)/statrep_.c |
| 1819 | |
| 1820 | $(OBJDIR)/statrep.h: $(OBJDIR)/headers |
| 1821 | |
| 1822 | $(OBJDIR)/style_.c: $(SRCDIR)/style.c $(TRANSLATE) |
| 1823 | $(TRANSLATE) $(SRCDIR)/style.c >$@ |
| 1824 | |
| 1825 | $(OBJDIR)/style.o: $(OBJDIR)/style_.c $(OBJDIR)/style.h $(SRCDIR)/config.h |
| 1826 |
+38
-3
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -55,11 +55,11 @@ | ||
| 55 | 55 | |
| 56 | 56 | # Uncomment to enable Tcl support |
| 57 | 57 | # FOSSIL_ENABLE_TCL = 1 |
| 58 | 58 | |
| 59 | 59 | !ifdef FOSSIL_ENABLE_SSL |
| 60 | -SSLDIR = $(B)\compat\openssl-1.0.1j | |
| 60 | +SSLDIR = $(B)\compat\openssl-1.0.2 | |
| 61 | 61 | SSLINCDIR = $(SSLDIR)\inc32 |
| 62 | 62 | SSLLIBDIR = $(SSLDIR)\out32 |
| 63 | 63 | SSLLFLAGS = /nologo /opt:ref /debug |
| 64 | 64 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 65 | 65 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| @@ -296,10 +296,11 @@ | ||
| 296 | 296 | sitemap_.c \ |
| 297 | 297 | skins_.c \ |
| 298 | 298 | sqlcmd_.c \ |
| 299 | 299 | stash_.c \ |
| 300 | 300 | stat_.c \ |
| 301 | + statrep_.c \ | |
| 301 | 302 | style_.c \ |
| 302 | 303 | sync_.c \ |
| 303 | 304 | tag_.c \ |
| 304 | 305 | tar_.c \ |
| 305 | 306 | th_main_.c \ |
| @@ -322,11 +323,36 @@ | ||
| 322 | 323 | wysiwyg_.c \ |
| 323 | 324 | xfer_.c \ |
| 324 | 325 | xfersetup_.c \ |
| 325 | 326 | zip_.c |
| 326 | 327 | |
| 327 | -EXTRA_FILES = $(SRCDIR)\diff.tcl | |
| 328 | +EXTRA_FILES = $(SRCDIR)\../skins/black_and_white/css.txt \ | |
| 329 | + $(SRCDIR)\../skins/black_and_white/footer.txt \ | |
| 330 | + $(SRCDIR)\../skins/black_and_white/header.txt \ | |
| 331 | + $(SRCDIR)\../skins/default/css.txt \ | |
| 332 | + $(SRCDIR)\../skins/default/footer.txt \ | |
| 333 | + $(SRCDIR)\../skins/default/header.txt \ | |
| 334 | + $(SRCDIR)\../skins/eagle/css.txt \ | |
| 335 | + $(SRCDIR)\../skins/eagle/footer.txt \ | |
| 336 | + $(SRCDIR)\../skins/eagle/header.txt \ | |
| 337 | + $(SRCDIR)\../skins/enhanced1/css.txt \ | |
| 338 | + $(SRCDIR)\../skins/enhanced1/footer.txt \ | |
| 339 | + $(SRCDIR)\../skins/enhanced1/header.txt \ | |
| 340 | + $(SRCDIR)\../skins/etienne1/css.txt \ | |
| 341 | + $(SRCDIR)\../skins/etienne1/footer.txt \ | |
| 342 | + $(SRCDIR)\../skins/etienne1/header.txt \ | |
| 343 | + $(SRCDIR)\../skins/khaki/css.txt \ | |
| 344 | + $(SRCDIR)\../skins/khaki/footer.txt \ | |
| 345 | + $(SRCDIR)\../skins/khaki/header.txt \ | |
| 346 | + $(SRCDIR)\../skins/plain_gray/css.txt \ | |
| 347 | + $(SRCDIR)\../skins/plain_gray/footer.txt \ | |
| 348 | + $(SRCDIR)\../skins/plain_gray/header.txt \ | |
| 349 | + $(SRCDIR)\../skins/rounded1/css.txt \ | |
| 350 | + $(SRCDIR)\../skins/rounded1/footer.txt \ | |
| 351 | + $(SRCDIR)\../skins/rounded1/header.txt \ | |
| 352 | + $(SRCDIR)\diff.tcl \ | |
| 353 | + $(SRCDIR)\markdown.md | |
| 328 | 354 | |
| 329 | 355 | OBJ = $(OX)\add$O \ |
| 330 | 356 | $(OX)\allrepo$O \ |
| 331 | 357 | $(OX)\attach$O \ |
| 332 | 358 | $(OX)\bag$O \ |
| @@ -419,10 +445,11 @@ | ||
| 419 | 445 | $(OX)\skins$O \ |
| 420 | 446 | $(OX)\sqlcmd$O \ |
| 421 | 447 | $(OX)\sqlite3$O \ |
| 422 | 448 | $(OX)\stash$O \ |
| 423 | 449 | $(OX)\stat$O \ |
| 450 | + $(OX)\statrep$O \ | |
| 424 | 451 | $(OX)\style$O \ |
| 425 | 452 | $(OX)\sync$O \ |
| 426 | 453 | $(OX)\tag$O \ |
| 427 | 454 | $(OX)\tar$O \ |
| 428 | 455 | $(OX)\th$O \ |
| @@ -593,10 +620,11 @@ | ||
| 593 | 620 | echo $(OX)\skins.obj >> $@ |
| 594 | 621 | echo $(OX)\sqlcmd.obj >> $@ |
| 595 | 622 | echo $(OX)\sqlite3.obj >> $@ |
| 596 | 623 | echo $(OX)\stash.obj >> $@ |
| 597 | 624 | echo $(OX)\stat.obj >> $@ |
| 625 | + echo $(OX)\statrep.obj >> $@ | |
| 598 | 626 | echo $(OX)\style.obj >> $@ |
| 599 | 627 | echo $(OX)\sync.obj >> $@ |
| 600 | 628 | echo $(OX)\tag.obj >> $@ |
| 601 | 629 | echo $(OX)\tar.obj >> $@ |
| 602 | 630 | echo $(OX)\th.obj >> $@ |
| @@ -674,11 +702,11 @@ | ||
| 674 | 702 | |
| 675 | 703 | page_index.h: mkindex$E $(SRC) |
| 676 | 704 | $** > $@ |
| 677 | 705 | |
| 678 | 706 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 679 | - $** > $@ | |
| 707 | + mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ | |
| 680 | 708 | |
| 681 | 709 | clean: |
| 682 | 710 | -del $(OX)\*.obj |
| 683 | 711 | -del *.obj |
| 684 | 712 | -del *_.c |
| @@ -1271,10 +1299,16 @@ | ||
| 1271 | 1299 | $(OX)\stat$O : stat_.c stat.h |
| 1272 | 1300 | $(TCC) /Fo$@ -c stat_.c |
| 1273 | 1301 | |
| 1274 | 1302 | stat_.c : $(SRCDIR)\stat.c |
| 1275 | 1303 | translate$E $** > $@ |
| 1304 | + | |
| 1305 | +$(OX)\statrep$O : statrep_.c statrep.h | |
| 1306 | + $(TCC) /Fo$@ -c statrep_.c | |
| 1307 | + | |
| 1308 | +statrep_.c : $(SRCDIR)\statrep.c | |
| 1309 | + translate$E $** > $@ | |
| 1276 | 1310 | |
| 1277 | 1311 | $(OX)\style$O : style_.c style.h |
| 1278 | 1312 | $(TCC) /Fo$@ -c style_.c |
| 1279 | 1313 | |
| 1280 | 1314 | style_.c : $(SRCDIR)\style.c |
| @@ -1518,10 +1552,11 @@ | ||
| 1518 | 1552 | sitemap_.c:sitemap.h \ |
| 1519 | 1553 | skins_.c:skins.h \ |
| 1520 | 1554 | sqlcmd_.c:sqlcmd.h \ |
| 1521 | 1555 | stash_.c:stash.h \ |
| 1522 | 1556 | stat_.c:stat.h \ |
| 1557 | + statrep_.c:statrep.h \ | |
| 1523 | 1558 | style_.c:style.h \ |
| 1524 | 1559 | sync_.c:sync.h \ |
| 1525 | 1560 | tag_.c:tag.h \ |
| 1526 | 1561 | tar_.c:tar.h \ |
| 1527 | 1562 | th_main_.c:th_main.h \ |
| 1528 | 1563 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -55,11 +55,11 @@ | |
| 55 | |
| 56 | # Uncomment to enable Tcl support |
| 57 | # FOSSIL_ENABLE_TCL = 1 |
| 58 | |
| 59 | !ifdef FOSSIL_ENABLE_SSL |
| 60 | SSLDIR = $(B)\compat\openssl-1.0.1j |
| 61 | SSLINCDIR = $(SSLDIR)\inc32 |
| 62 | SSLLIBDIR = $(SSLDIR)\out32 |
| 63 | SSLLFLAGS = /nologo /opt:ref /debug |
| 64 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 65 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| @@ -296,10 +296,11 @@ | |
| 296 | sitemap_.c \ |
| 297 | skins_.c \ |
| 298 | sqlcmd_.c \ |
| 299 | stash_.c \ |
| 300 | stat_.c \ |
| 301 | style_.c \ |
| 302 | sync_.c \ |
| 303 | tag_.c \ |
| 304 | tar_.c \ |
| 305 | th_main_.c \ |
| @@ -322,11 +323,36 @@ | |
| 322 | wysiwyg_.c \ |
| 323 | xfer_.c \ |
| 324 | xfersetup_.c \ |
| 325 | zip_.c |
| 326 | |
| 327 | EXTRA_FILES = $(SRCDIR)\diff.tcl |
| 328 | |
| 329 | OBJ = $(OX)\add$O \ |
| 330 | $(OX)\allrepo$O \ |
| 331 | $(OX)\attach$O \ |
| 332 | $(OX)\bag$O \ |
| @@ -419,10 +445,11 @@ | |
| 419 | $(OX)\skins$O \ |
| 420 | $(OX)\sqlcmd$O \ |
| 421 | $(OX)\sqlite3$O \ |
| 422 | $(OX)\stash$O \ |
| 423 | $(OX)\stat$O \ |
| 424 | $(OX)\style$O \ |
| 425 | $(OX)\sync$O \ |
| 426 | $(OX)\tag$O \ |
| 427 | $(OX)\tar$O \ |
| 428 | $(OX)\th$O \ |
| @@ -593,10 +620,11 @@ | |
| 593 | echo $(OX)\skins.obj >> $@ |
| 594 | echo $(OX)\sqlcmd.obj >> $@ |
| 595 | echo $(OX)\sqlite3.obj >> $@ |
| 596 | echo $(OX)\stash.obj >> $@ |
| 597 | echo $(OX)\stat.obj >> $@ |
| 598 | echo $(OX)\style.obj >> $@ |
| 599 | echo $(OX)\sync.obj >> $@ |
| 600 | echo $(OX)\tag.obj >> $@ |
| 601 | echo $(OX)\tar.obj >> $@ |
| 602 | echo $(OX)\th.obj >> $@ |
| @@ -674,11 +702,11 @@ | |
| 674 | |
| 675 | page_index.h: mkindex$E $(SRC) |
| 676 | $** > $@ |
| 677 | |
| 678 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 679 | $** > $@ |
| 680 | |
| 681 | clean: |
| 682 | -del $(OX)\*.obj |
| 683 | -del *.obj |
| 684 | -del *_.c |
| @@ -1271,10 +1299,16 @@ | |
| 1271 | $(OX)\stat$O : stat_.c stat.h |
| 1272 | $(TCC) /Fo$@ -c stat_.c |
| 1273 | |
| 1274 | stat_.c : $(SRCDIR)\stat.c |
| 1275 | translate$E $** > $@ |
| 1276 | |
| 1277 | $(OX)\style$O : style_.c style.h |
| 1278 | $(TCC) /Fo$@ -c style_.c |
| 1279 | |
| 1280 | style_.c : $(SRCDIR)\style.c |
| @@ -1518,10 +1552,11 @@ | |
| 1518 | sitemap_.c:sitemap.h \ |
| 1519 | skins_.c:skins.h \ |
| 1520 | sqlcmd_.c:sqlcmd.h \ |
| 1521 | stash_.c:stash.h \ |
| 1522 | stat_.c:stat.h \ |
| 1523 | style_.c:style.h \ |
| 1524 | sync_.c:sync.h \ |
| 1525 | tag_.c:tag.h \ |
| 1526 | tar_.c:tar.h \ |
| 1527 | th_main_.c:th_main.h \ |
| 1528 |
| --- win/Makefile.msc | |
| +++ win/Makefile.msc | |
| @@ -55,11 +55,11 @@ | |
| 55 | |
| 56 | # Uncomment to enable Tcl support |
| 57 | # FOSSIL_ENABLE_TCL = 1 |
| 58 | |
| 59 | !ifdef FOSSIL_ENABLE_SSL |
| 60 | SSLDIR = $(B)\compat\openssl-1.0.2 |
| 61 | SSLINCDIR = $(SSLDIR)\inc32 |
| 62 | SSLLIBDIR = $(SSLDIR)\out32 |
| 63 | SSLLFLAGS = /nologo /opt:ref /debug |
| 64 | SSLLIB = ssleay32.lib libeay32.lib user32.lib gdi32.lib |
| 65 | !if "$(PLATFORM)"=="amd64" || "$(PLATFORM)"=="x64" |
| @@ -296,10 +296,11 @@ | |
| 296 | sitemap_.c \ |
| 297 | skins_.c \ |
| 298 | sqlcmd_.c \ |
| 299 | stash_.c \ |
| 300 | stat_.c \ |
| 301 | statrep_.c \ |
| 302 | style_.c \ |
| 303 | sync_.c \ |
| 304 | tag_.c \ |
| 305 | tar_.c \ |
| 306 | th_main_.c \ |
| @@ -322,11 +323,36 @@ | |
| 323 | wysiwyg_.c \ |
| 324 | xfer_.c \ |
| 325 | xfersetup_.c \ |
| 326 | zip_.c |
| 327 | |
| 328 | EXTRA_FILES = $(SRCDIR)\../skins/black_and_white/css.txt \ |
| 329 | $(SRCDIR)\../skins/black_and_white/footer.txt \ |
| 330 | $(SRCDIR)\../skins/black_and_white/header.txt \ |
| 331 | $(SRCDIR)\../skins/default/css.txt \ |
| 332 | $(SRCDIR)\../skins/default/footer.txt \ |
| 333 | $(SRCDIR)\../skins/default/header.txt \ |
| 334 | $(SRCDIR)\../skins/eagle/css.txt \ |
| 335 | $(SRCDIR)\../skins/eagle/footer.txt \ |
| 336 | $(SRCDIR)\../skins/eagle/header.txt \ |
| 337 | $(SRCDIR)\../skins/enhanced1/css.txt \ |
| 338 | $(SRCDIR)\../skins/enhanced1/footer.txt \ |
| 339 | $(SRCDIR)\../skins/enhanced1/header.txt \ |
| 340 | $(SRCDIR)\../skins/etienne1/css.txt \ |
| 341 | $(SRCDIR)\../skins/etienne1/footer.txt \ |
| 342 | $(SRCDIR)\../skins/etienne1/header.txt \ |
| 343 | $(SRCDIR)\../skins/khaki/css.txt \ |
| 344 | $(SRCDIR)\../skins/khaki/footer.txt \ |
| 345 | $(SRCDIR)\../skins/khaki/header.txt \ |
| 346 | $(SRCDIR)\../skins/plain_gray/css.txt \ |
| 347 | $(SRCDIR)\../skins/plain_gray/footer.txt \ |
| 348 | $(SRCDIR)\../skins/plain_gray/header.txt \ |
| 349 | $(SRCDIR)\../skins/rounded1/css.txt \ |
| 350 | $(SRCDIR)\../skins/rounded1/footer.txt \ |
| 351 | $(SRCDIR)\../skins/rounded1/header.txt \ |
| 352 | $(SRCDIR)\diff.tcl \ |
| 353 | $(SRCDIR)\markdown.md |
| 354 | |
| 355 | OBJ = $(OX)\add$O \ |
| 356 | $(OX)\allrepo$O \ |
| 357 | $(OX)\attach$O \ |
| 358 | $(OX)\bag$O \ |
| @@ -419,10 +445,11 @@ | |
| 445 | $(OX)\skins$O \ |
| 446 | $(OX)\sqlcmd$O \ |
| 447 | $(OX)\sqlite3$O \ |
| 448 | $(OX)\stash$O \ |
| 449 | $(OX)\stat$O \ |
| 450 | $(OX)\statrep$O \ |
| 451 | $(OX)\style$O \ |
| 452 | $(OX)\sync$O \ |
| 453 | $(OX)\tag$O \ |
| 454 | $(OX)\tar$O \ |
| 455 | $(OX)\th$O \ |
| @@ -593,10 +620,11 @@ | |
| 620 | echo $(OX)\skins.obj >> $@ |
| 621 | echo $(OX)\sqlcmd.obj >> $@ |
| 622 | echo $(OX)\sqlite3.obj >> $@ |
| 623 | echo $(OX)\stash.obj >> $@ |
| 624 | echo $(OX)\stat.obj >> $@ |
| 625 | echo $(OX)\statrep.obj >> $@ |
| 626 | echo $(OX)\style.obj >> $@ |
| 627 | echo $(OX)\sync.obj >> $@ |
| 628 | echo $(OX)\tag.obj >> $@ |
| 629 | echo $(OX)\tar.obj >> $@ |
| 630 | echo $(OX)\th.obj >> $@ |
| @@ -674,11 +702,11 @@ | |
| 702 | |
| 703 | page_index.h: mkindex$E $(SRC) |
| 704 | $** > $@ |
| 705 | |
| 706 | builtin_data.h: mkbuiltin$E $(EXTRA_FILES) |
| 707 | mkbuiltin$E --prefix $(SRCDIR)/ $(EXTRA_FILES) > $@ |
| 708 | |
| 709 | clean: |
| 710 | -del $(OX)\*.obj |
| 711 | -del *.obj |
| 712 | -del *_.c |
| @@ -1271,10 +1299,16 @@ | |
| 1299 | $(OX)\stat$O : stat_.c stat.h |
| 1300 | $(TCC) /Fo$@ -c stat_.c |
| 1301 | |
| 1302 | stat_.c : $(SRCDIR)\stat.c |
| 1303 | translate$E $** > $@ |
| 1304 | |
| 1305 | $(OX)\statrep$O : statrep_.c statrep.h |
| 1306 | $(TCC) /Fo$@ -c statrep_.c |
| 1307 | |
| 1308 | statrep_.c : $(SRCDIR)\statrep.c |
| 1309 | translate$E $** > $@ |
| 1310 | |
| 1311 | $(OX)\style$O : style_.c style.h |
| 1312 | $(TCC) /Fo$@ -c style_.c |
| 1313 | |
| 1314 | style_.c : $(SRCDIR)\style.c |
| @@ -1518,10 +1552,11 @@ | |
| 1552 | sitemap_.c:sitemap.h \ |
| 1553 | skins_.c:skins.h \ |
| 1554 | sqlcmd_.c:sqlcmd.h \ |
| 1555 | stash_.c:stash.h \ |
| 1556 | stat_.c:stat.h \ |
| 1557 | statrep_.c:statrep.h \ |
| 1558 | style_.c:style.h \ |
| 1559 | sync_.c:sync.h \ |
| 1560 | tag_.c:tag.h \ |
| 1561 | tar_.c:tar.h \ |
| 1562 | th_main_.c:th_main.h \ |
| 1563 |
+2
-2
| --- win/include/unistd.h | ||
| +++ win/include/unistd.h | ||
| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | #ifndef _UNISTD_H |
| 2 | 2 | #define _UNISTD_H 1 |
| 3 | 3 | |
| 4 | -/* This file intended to serve as a drop-in replacement for | |
| 4 | +/* This file intended to serve as a drop-in replacement for | |
| 5 | 5 | * unistd.h on Windows |
| 6 | - * Please add functionality as neeeded | |
| 6 | + * Please add functionality as neeeded | |
| 7 | 7 | */ |
| 8 | 8 | |
| 9 | 9 | #include <stdlib.h> |
| 10 | 10 | #include <io.h> |
| 11 | 11 | #define srandom srand |
| 12 | 12 |
| --- win/include/unistd.h | |
| +++ win/include/unistd.h | |
| @@ -1,11 +1,11 @@ | |
| 1 | #ifndef _UNISTD_H |
| 2 | #define _UNISTD_H 1 |
| 3 | |
| 4 | /* This file intended to serve as a drop-in replacement for |
| 5 | * unistd.h on Windows |
| 6 | * Please add functionality as neeeded |
| 7 | */ |
| 8 | |
| 9 | #include <stdlib.h> |
| 10 | #include <io.h> |
| 11 | #define srandom srand |
| 12 |
| --- win/include/unistd.h | |
| +++ win/include/unistd.h | |
| @@ -1,11 +1,11 @@ | |
| 1 | #ifndef _UNISTD_H |
| 2 | #define _UNISTD_H 1 |
| 3 | |
| 4 | /* This file intended to serve as a drop-in replacement for |
| 5 | * unistd.h on Windows |
| 6 | * Please add functionality as neeeded |
| 7 | */ |
| 8 | |
| 9 | #include <stdlib.h> |
| 10 | #include <io.h> |
| 11 | #define srandom srand |
| 12 |
+1
-1
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -122,11 +122,11 @@ | ||
| 122 | 122 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 123 | 123 | first <a href="https://www.openssl.org/source/">download the official |
| 124 | 124 | source code for OpenSSL</a> and extract it to an appropriately named |
| 125 | 125 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 126 | 126 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 127 | -"<b>compat/openssl-1.0.1j</b>"), then make sure that some recent | |
| 127 | +"<b>compat/openssl-1.0.2</b>"), then make sure that some recent | |
| 128 | 128 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 129 | 129 | and finally run one of the following commands: |
| 130 | 130 | <blockquote><pre> |
| 131 | 131 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 132 | 132 | </pre></blockquote> |
| 133 | 133 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -122,11 +122,11 @@ | |
| 122 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 123 | first <a href="https://www.openssl.org/source/">download the official |
| 124 | source code for OpenSSL</a> and extract it to an appropriately named |
| 125 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 126 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 127 | "<b>compat/openssl-1.0.1j</b>"), then make sure that some recent |
| 128 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 129 | and finally run one of the following commands: |
| 130 | <blockquote><pre> |
| 131 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 132 | </pre></blockquote> |
| 133 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -122,11 +122,11 @@ | |
| 122 | the optional <a href="https://www.openssl.org/">OpenSSL</a> support, |
| 123 | first <a href="https://www.openssl.org/source/">download the official |
| 124 | source code for OpenSSL</a> and extract it to an appropriately named |
| 125 | "<b>openssl-X.Y.ZA</b>" subdirectory within the local |
| 126 | [/tree?ci=trunk&name=compat | compat] directory (e.g. |
| 127 | "<b>compat/openssl-1.0.2</b>"), then make sure that some recent |
| 128 | <a href="http://www.perl.org/">Perl</a> binaries are installed locally, |
| 129 | and finally run one of the following commands: |
| 130 | <blockquote><pre> |
| 131 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| 132 | </pre></blockquote> |
| 133 |
+75
-13
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,34 +1,96 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | -<h2>Changes For Version 1.30 (as yet unreleased)</h2> | |
| 4 | - * Add setting to control the number of times autosync will be tried before | |
| 5 | - returning an error. | |
| 3 | +<h2>Changes For Version 1.31 (2015-??-??)</h2> | |
| 4 | + * Add direct Subversion import support to | |
| 5 | + [/help?cmd=import|fossil import]. (??) | |
| 6 | + * Add IPv6 support to [/help?cmd=sync|fossil sync] and | |
| 7 | + [/help?cmd=clone|fossil clone] | |
| 8 | + * Add more skins such as "San Francisco Modern" and "Eagle". | |
| 9 | + * Update SQLite to version 3.8.9. (??) | |
| 10 | + * Improve the display of the history of changes to a single | |
| 11 | + file. A [/help?cmd=rebuild|fossil rebuild] is needed for that. | |
| 12 | + * Enable ZIP and Tarball downloads for user "nobody" by default. | |
| 13 | + * Make the now() SQL function available in the | |
| 14 | + [/help?cmd=sqlite|fossil sqlite] command. | |
| 15 | + * Improve table sorting in various pages. | |
| 16 | + * Add support for setting environment variables from within CGI script | |
| 17 | + files. | |
| 18 | + * Enhance the Ad-Unit processing to allow a choice of two different | |
| 19 | + ad-units. | |
| 20 | + * During shutdown, check to see if the check-out database (".fslckout") | |
| 21 | + contains a lot of free space, and if it does, VACUUM it. | |
| 22 | + | |
| 23 | +<h2>Changes For Version 1.30 (2015-01-19)</h2> | |
| 24 | + * Added the [/help?cmd=bundle|fossil bundle] command. | |
| 25 | + * Added the [/help?cmd=purge|fossil purge] command. | |
| 26 | + * Added the [/help?cmd=publish|fossil publish] command. | |
| 27 | + * Added the [/help?cmd=unpublished|fossil unpublished] command. | |
| 28 | + * Enhance the [/tree] webpage to show the age of each file with the option | |
| 29 | + to sort by age. | |
| 30 | + * Enhance the [/brlist] webpage to show additional information about each branch | |
| 31 | + and to be sortable by clicking on column headers. | |
| 32 | + * Add support for Docker. Just install docker and type | |
| 33 | + "sudo docker run -d -p 8080:8080 nijtmans/fossil" to get it running. | |
| 6 | 34 | * Add the [/help/fusefs|fossil fusefs DIRECTORY] command that mounts a |
| 7 | 35 | Fuse Filesystem at the given DIRECTORY and populates it with read-only |
| 8 | 36 | copies of all historical check-ins. This only works on systems that |
| 9 | 37 | support FuseFS. |
| 38 | + * Add the administrative log that records all configuration. | |
| 39 | + * Added the [/sitemap] webpage. | |
| 40 | + * Added the [/bloblist] web page. | |
| 41 | + * Let [/help?cmd=new|fossil new] no longer create an initial empty commit | |
| 42 | + by default. The first commit after checking out an empty repository will | |
| 43 | + become the initial commit. | |
| 44 | + * Added the [/help?cmd=all|fossil all dbstat] and | |
| 45 | + [/help?cmd=all|fossil all info] commands. | |
| 46 | + * Update SQLite to version 3.8.8. | |
| 47 | + * Added the --verily option to the [/help?cmd=clean|fossil clean] command. | |
| 48 | + * Add the "autosync-tries" setting to control the number of autosync attempts | |
| 49 | + before returning an error. | |
| 10 | 50 | * Added a compile-time option (--with-miniz) to build using miniz instead |
| 11 | 51 | of zlib. Disabled by default. |
| 12 | - * Several fixes to the TH1 expression parser. | |
| 13 | 52 | * Support customization of commands and webpages, including the ability to |
| 14 | 53 | add new ones, via the "TH1 hooks" feature. Disabled by default. Enabled |
| 15 | 54 | via a compile-time option. |
| 16 | 55 | * Add the <nowiki>[checkout], [render], [styleHeader], [styleFooter], |
| 17 | 56 | [trace], [getParameter], [setParameter], [artifact], and |
| 18 | 57 | [globalState]</nowiki> commands to TH1, primarily for use by TH1 hooks. |
| 19 | - * Bring in the latest version of autosetup from upstream. | |
| 20 | - * When committing a (non-binary) file which contains bytes forming an | |
| 21 | - invalid UTF-8 stream, fossil now adds the possibility to convert it | |
| 22 | - to a valid UTF-8 stream ('c') if you like. | |
| 23 | - * Let [/help?cmd=new|fossil new] no longer create an initial empty commit | |
| 24 | - by default. The first commit after checking out an empty repository will | |
| 25 | - become the initial commit. (NOT PLANNED FOR 1.30, BUT DONE TO EXPOSE | |
| 26 | - [/help?cmd=new|fossil new --empty] TO MORE FIELD TESTING BEFORE | |
| 27 | - MAKING ANY DECISION ON IT!) | |
| 58 | + * Automatically adjust the width of command-line timeline output according to the | |
| 59 | + detected width of the terminal. | |
| 60 | + * Prompt the user to optionally fix invalid UTF-8 at check-in. | |
| 28 | 61 | * Added a line-number toggle option to the [/help?cmd=/info|/info] |
| 29 | 62 | and [/help?cmd=/artifact|/artifact] pages. |
| 63 | + * Most commands now issue errors rather than silently ignoring unrecognized | |
| 64 | + command-line options. | |
| 65 | + * Use full 40-character SHA1 hashes (instead of abbreviations) in most | |
| 66 | + internal URLs. | |
| 67 | + * The "ssh:" sync method on windows now uses "plink.exe" instead of "ssh" as | |
| 68 | + the secure-shell client program. | |
| 69 | + * Prevent a partial clone when the connection is lost. | |
| 70 | + * Make the distinction between 301 and 302 redirects. | |
| 71 | + * Allow commits against a closed check-in as long as the commit goes onto | |
| 72 | + a different branch. | |
| 73 | + * Improved cache control in the web interface reduces unnecessary requests | |
| 74 | + for common resources like the page logo and CSS. | |
| 75 | + * Fix a rare and long-standing sync protocol bug | |
| 76 | + that would silently prevent the sync from running to completion. Before | |
| 77 | + this bug-fix it was sometimes necessary to do "fossil sync --verily" to | |
| 78 | + get two repositories in sync. | |
| 79 | + * Add the [/finfo?name=src/foci.c|files_of_checkin] virtual table - useful | |
| 80 | + for ad hoc queries in the [/help?cmd=sqlite3|fossil sql] interface, | |
| 81 | + and also used internally. | |
| 82 | + * Added the "$secureurl" TH1 variable for use in headers and footers. | |
| 83 | + * (Internal:) Add the ability to include resources as separate files in the | |
| 84 | + source tree that are converted into constant byte arrays in the compiled | |
| 85 | + binary. Use this feature to store the Tk script that implements the --tk | |
| 86 | + diff option in a separate file for easier editing. | |
| 87 | + * (Internal:) Implement a system of compile-time checks to help ensure | |
| 88 | + the correctness of printf-style formatting strings. | |
| 89 | + * Fix CVE-2014-3566, also known as the POODLE SSL 3.0 vulnerability. | |
| 90 | + * Numerous documentation fixes and improvements. | |
| 91 | + * Other obscure and minor bug fixes - see the timeline for details. | |
| 30 | 92 | |
| 31 | 93 | <h2>Changes For Version 1.29 (2014-06-12)</h2> |
| 32 | 94 | * Add the ability to display content, diffs and annotations for UTF16 |
| 33 | 95 | text files in the web interface. |
| 34 | 96 | * Add the "SaveAs..." and "Invert" buttons |
| 35 | 97 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,34 +1,96 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2>Changes For Version 1.30 (as yet unreleased)</h2> |
| 4 | * Add setting to control the number of times autosync will be tried before |
| 5 | returning an error. |
| 6 | * Add the [/help/fusefs|fossil fusefs DIRECTORY] command that mounts a |
| 7 | Fuse Filesystem at the given DIRECTORY and populates it with read-only |
| 8 | copies of all historical check-ins. This only works on systems that |
| 9 | support FuseFS. |
| 10 | * Added a compile-time option (--with-miniz) to build using miniz instead |
| 11 | of zlib. Disabled by default. |
| 12 | * Several fixes to the TH1 expression parser. |
| 13 | * Support customization of commands and webpages, including the ability to |
| 14 | add new ones, via the "TH1 hooks" feature. Disabled by default. Enabled |
| 15 | via a compile-time option. |
| 16 | * Add the <nowiki>[checkout], [render], [styleHeader], [styleFooter], |
| 17 | [trace], [getParameter], [setParameter], [artifact], and |
| 18 | [globalState]</nowiki> commands to TH1, primarily for use by TH1 hooks. |
| 19 | * Bring in the latest version of autosetup from upstream. |
| 20 | * When committing a (non-binary) file which contains bytes forming an |
| 21 | invalid UTF-8 stream, fossil now adds the possibility to convert it |
| 22 | to a valid UTF-8 stream ('c') if you like. |
| 23 | * Let [/help?cmd=new|fossil new] no longer create an initial empty commit |
| 24 | by default. The first commit after checking out an empty repository will |
| 25 | become the initial commit. (NOT PLANNED FOR 1.30, BUT DONE TO EXPOSE |
| 26 | [/help?cmd=new|fossil new --empty] TO MORE FIELD TESTING BEFORE |
| 27 | MAKING ANY DECISION ON IT!) |
| 28 | * Added a line-number toggle option to the [/help?cmd=/info|/info] |
| 29 | and [/help?cmd=/artifact|/artifact] pages. |
| 30 | |
| 31 | <h2>Changes For Version 1.29 (2014-06-12)</h2> |
| 32 | * Add the ability to display content, diffs and annotations for UTF16 |
| 33 | text files in the web interface. |
| 34 | * Add the "SaveAs..." and "Invert" buttons |
| 35 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,34 +1,96 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2>Changes For Version 1.31 (2015-??-??)</h2> |
| 4 | * Add direct Subversion import support to |
| 5 | [/help?cmd=import|fossil import]. (??) |
| 6 | * Add IPv6 support to [/help?cmd=sync|fossil sync] and |
| 7 | [/help?cmd=clone|fossil clone] |
| 8 | * Add more skins such as "San Francisco Modern" and "Eagle". |
| 9 | * Update SQLite to version 3.8.9. (??) |
| 10 | * Improve the display of the history of changes to a single |
| 11 | file. A [/help?cmd=rebuild|fossil rebuild] is needed for that. |
| 12 | * Enable ZIP and Tarball downloads for user "nobody" by default. |
| 13 | * Make the now() SQL function available in the |
| 14 | [/help?cmd=sqlite|fossil sqlite] command. |
| 15 | * Improve table sorting in various pages. |
| 16 | * Add support for setting environment variables from within CGI script |
| 17 | files. |
| 18 | * Enhance the Ad-Unit processing to allow a choice of two different |
| 19 | ad-units. |
| 20 | * During shutdown, check to see if the check-out database (".fslckout") |
| 21 | contains a lot of free space, and if it does, VACUUM it. |
| 22 | |
| 23 | <h2>Changes For Version 1.30 (2015-01-19)</h2> |
| 24 | * Added the [/help?cmd=bundle|fossil bundle] command. |
| 25 | * Added the [/help?cmd=purge|fossil purge] command. |
| 26 | * Added the [/help?cmd=publish|fossil publish] command. |
| 27 | * Added the [/help?cmd=unpublished|fossil unpublished] command. |
| 28 | * Enhance the [/tree] webpage to show the age of each file with the option |
| 29 | to sort by age. |
| 30 | * Enhance the [/brlist] webpage to show additional information about each branch |
| 31 | and to be sortable by clicking on column headers. |
| 32 | * Add support for Docker. Just install docker and type |
| 33 | "sudo docker run -d -p 8080:8080 nijtmans/fossil" to get it running. |
| 34 | * Add the [/help/fusefs|fossil fusefs DIRECTORY] command that mounts a |
| 35 | Fuse Filesystem at the given DIRECTORY and populates it with read-only |
| 36 | copies of all historical check-ins. This only works on systems that |
| 37 | support FuseFS. |
| 38 | * Add the administrative log that records all configuration. |
| 39 | * Added the [/sitemap] webpage. |
| 40 | * Added the [/bloblist] web page. |
| 41 | * Let [/help?cmd=new|fossil new] no longer create an initial empty commit |
| 42 | by default. The first commit after checking out an empty repository will |
| 43 | become the initial commit. |
| 44 | * Added the [/help?cmd=all|fossil all dbstat] and |
| 45 | [/help?cmd=all|fossil all info] commands. |
| 46 | * Update SQLite to version 3.8.8. |
| 47 | * Added the --verily option to the [/help?cmd=clean|fossil clean] command. |
| 48 | * Add the "autosync-tries" setting to control the number of autosync attempts |
| 49 | before returning an error. |
| 50 | * Added a compile-time option (--with-miniz) to build using miniz instead |
| 51 | of zlib. Disabled by default. |
| 52 | * Support customization of commands and webpages, including the ability to |
| 53 | add new ones, via the "TH1 hooks" feature. Disabled by default. Enabled |
| 54 | via a compile-time option. |
| 55 | * Add the <nowiki>[checkout], [render], [styleHeader], [styleFooter], |
| 56 | [trace], [getParameter], [setParameter], [artifact], and |
| 57 | [globalState]</nowiki> commands to TH1, primarily for use by TH1 hooks. |
| 58 | * Automatically adjust the width of command-line timeline output according to the |
| 59 | detected width of the terminal. |
| 60 | * Prompt the user to optionally fix invalid UTF-8 at check-in. |
| 61 | * Added a line-number toggle option to the [/help?cmd=/info|/info] |
| 62 | and [/help?cmd=/artifact|/artifact] pages. |
| 63 | * Most commands now issue errors rather than silently ignoring unrecognized |
| 64 | command-line options. |
| 65 | * Use full 40-character SHA1 hashes (instead of abbreviations) in most |
| 66 | internal URLs. |
| 67 | * The "ssh:" sync method on windows now uses "plink.exe" instead of "ssh" as |
| 68 | the secure-shell client program. |
| 69 | * Prevent a partial clone when the connection is lost. |
| 70 | * Make the distinction between 301 and 302 redirects. |
| 71 | * Allow commits against a closed check-in as long as the commit goes onto |
| 72 | a different branch. |
| 73 | * Improved cache control in the web interface reduces unnecessary requests |
| 74 | for common resources like the page logo and CSS. |
| 75 | * Fix a rare and long-standing sync protocol bug |
| 76 | that would silently prevent the sync from running to completion. Before |
| 77 | this bug-fix it was sometimes necessary to do "fossil sync --verily" to |
| 78 | get two repositories in sync. |
| 79 | * Add the [/finfo?name=src/foci.c|files_of_checkin] virtual table - useful |
| 80 | for ad hoc queries in the [/help?cmd=sqlite3|fossil sql] interface, |
| 81 | and also used internally. |
| 82 | * Added the "$secureurl" TH1 variable for use in headers and footers. |
| 83 | * (Internal:) Add the ability to include resources as separate files in the |
| 84 | source tree that are converted into constant byte arrays in the compiled |
| 85 | binary. Use this feature to store the Tk script that implements the --tk |
| 86 | diff option in a separate file for easier editing. |
| 87 | * (Internal:) Implement a system of compile-time checks to help ensure |
| 88 | the correctness of printf-style formatting strings. |
| 89 | * Fix CVE-2014-3566, also known as the POODLE SSL 3.0 vulnerability. |
| 90 | * Numerous documentation fixes and improvements. |
| 91 | * Other obscure and minor bug fixes - see the timeline for details. |
| 92 | |
| 93 | <h2>Changes For Version 1.29 (2014-06-12)</h2> |
| 94 | * Add the ability to display content, diffs and annotations for UTF16 |
| 95 | text files in the web interface. |
| 96 | * Add the "SaveAs..." and "Invert" buttons |
| 97 |
+2
-2
| --- www/delta_format.wiki | ||
| +++ www/delta_format.wiki | ||
| @@ -66,11 +66,11 @@ | ||
| 66 | 66 | <a name="slist"></a><h3>1.3 Segment-List</h3> |
| 67 | 67 | <img src="delta2.gif" align="left" hspace="10"> |
| 68 | 68 | |
| 69 | 69 | <p>The segment-list of a delta describes how to create the target from |
| 70 | 70 | the original by a combination of inserting literal byte-sequences and |
| 71 | -copying ranges of bytes from the original. This is there the | |
| 71 | +copying ranges of bytes from the original. This is where the | |
| 72 | 72 | compression takes place, by encoding the large common parts of |
| 73 | 73 | original and target in small copy instructions.</p> |
| 74 | 74 | |
| 75 | 75 | <p>The target is constructed from beginning to end, with the data |
| 76 | 76 | generated by each instruction appended after the data of all previous |
| @@ -213,11 +213,11 @@ | ||
| 213 | 213 | <ul> |
| 214 | 214 | <li>Pure text files generate a pure text delta. |
| 215 | 215 | </li> |
| 216 | 216 | <li>Binary files generate a delta that may contain some binary data. |
| 217 | 217 | </li> |
| 218 | -<li>The delta encoding does not attempt to compress the content | |
| 218 | +<li>The delta encoding does not attempt to compress the content. | |
| 219 | 219 | It was considered to be much |
| 220 | 220 | more sensible to do compression using a separate general-purpose |
| 221 | 221 | compression library, like <a href="http://www.zlib.net">zlib</a>. |
| 222 | 222 | </li> |
| 223 | 223 | </ul> |
| 224 | 224 |
| --- www/delta_format.wiki | |
| +++ www/delta_format.wiki | |
| @@ -66,11 +66,11 @@ | |
| 66 | <a name="slist"></a><h3>1.3 Segment-List</h3> |
| 67 | <img src="delta2.gif" align="left" hspace="10"> |
| 68 | |
| 69 | <p>The segment-list of a delta describes how to create the target from |
| 70 | the original by a combination of inserting literal byte-sequences and |
| 71 | copying ranges of bytes from the original. This is there the |
| 72 | compression takes place, by encoding the large common parts of |
| 73 | original and target in small copy instructions.</p> |
| 74 | |
| 75 | <p>The target is constructed from beginning to end, with the data |
| 76 | generated by each instruction appended after the data of all previous |
| @@ -213,11 +213,11 @@ | |
| 213 | <ul> |
| 214 | <li>Pure text files generate a pure text delta. |
| 215 | </li> |
| 216 | <li>Binary files generate a delta that may contain some binary data. |
| 217 | </li> |
| 218 | <li>The delta encoding does not attempt to compress the content |
| 219 | It was considered to be much |
| 220 | more sensible to do compression using a separate general-purpose |
| 221 | compression library, like <a href="http://www.zlib.net">zlib</a>. |
| 222 | </li> |
| 223 | </ul> |
| 224 |
| --- www/delta_format.wiki | |
| +++ www/delta_format.wiki | |
| @@ -66,11 +66,11 @@ | |
| 66 | <a name="slist"></a><h3>1.3 Segment-List</h3> |
| 67 | <img src="delta2.gif" align="left" hspace="10"> |
| 68 | |
| 69 | <p>The segment-list of a delta describes how to create the target from |
| 70 | the original by a combination of inserting literal byte-sequences and |
| 71 | copying ranges of bytes from the original. This is where the |
| 72 | compression takes place, by encoding the large common parts of |
| 73 | original and target in small copy instructions.</p> |
| 74 | |
| 75 | <p>The target is constructed from beginning to end, with the data |
| 76 | generated by each instruction appended after the data of all previous |
| @@ -213,11 +213,11 @@ | |
| 213 | <ul> |
| 214 | <li>Pure text files generate a pure text delta. |
| 215 | </li> |
| 216 | <li>Binary files generate a delta that may contain some binary data. |
| 217 | </li> |
| 218 | <li>The delta encoding does not attempt to compress the content. |
| 219 | It was considered to be much |
| 220 | more sensible to do compression using a separate general-purpose |
| 221 | compression library, like <a href="http://www.zlib.net">zlib</a>. |
| 222 | </li> |
| 223 | </ul> |
| 224 |
+5
-5
| --- www/fileformat.wiki | ||
| +++ www/fileformat.wiki | ||
| @@ -49,11 +49,11 @@ | ||
| 49 | 49 | </ul> |
| 50 | 50 | |
| 51 | 51 | These seven artifact types are described in the following sections. |
| 52 | 52 | |
| 53 | 53 | In the current implementation (as of 2009-01-25) the artifacts that |
| 54 | -make up a fossil repository are stored in in as delta- and zlib-compressed | |
| 54 | +make up a fossil repository are stored as delta- and zlib-compressed | |
| 55 | 55 | blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This |
| 56 | 56 | is an implementation detail and might change in a future release. For |
| 57 | 57 | the purpose of this article "file format" means the format of the artifacts, |
| 58 | 58 | not how the artifacts are stored on disk. It is the artifact format that |
| 59 | 59 | is intended to be enduring. The specifics of how artifacts are stored on |
| @@ -184,11 +184,11 @@ | ||
| 184 | 184 | ancestor, the Q-card is used to identify a single check-in or a small |
| 185 | 185 | range of check-ins which were cherry-picked for inclusion in or |
| 186 | 186 | exclusion from the current manifest. The first argument of |
| 187 | 187 | the Q-card is the artifact ID of another manifest (the "target") |
| 188 | 188 | which has had its changes included or excluded in the current manifest. |
| 189 | -The target is preceeded by "+" or "-" to show inclusion or | |
| 189 | +The target is preceded by "+" or "-" to show inclusion or | |
| 190 | 190 | exclusion, respectively. The optional second argument to the |
| 191 | 191 | Q-card is another manifest artifact ID which is the "baseline" |
| 192 | 192 | for the cherry-pick. If omitted, the baseline is the primary |
| 193 | 193 | parent of the target. The |
| 194 | 194 | changes included or excluded consist of all changes moving from |
| @@ -315,11 +315,11 @@ | ||
| 315 | 315 | is either "+", "-", or "*". The "+" means the tag should be added |
| 316 | 316 | to the artifact. The "-" means the tag should be removed. |
| 317 | 317 | The "*" character means the tag should be added to the artifact |
| 318 | 318 | and all direct descendants (but not descendents through a merge) down |
| 319 | 319 | to but not including the first descendant that contains a |
| 320 | -more recent "-" or "+" tag with the same name. | |
| 320 | +more recent "-", "*", or "+" tag with the same name. | |
| 321 | 321 | The optional third argument is the value of the tag. A tag |
| 322 | 322 | without a value is a boolean. |
| 323 | 323 | |
| 324 | 324 | When two or more tags with the same name are applied to the |
| 325 | 325 | same artifact, the tag with the latest (most recent) date is |
| @@ -362,11 +362,11 @@ | ||
| 362 | 362 | gives the name of the wiki page. The optional N card specifies |
| 363 | 363 | the mimetype of the wiki text. If the N card is omitted, the |
| 364 | 364 | mimetype is assumed to be text/x-fossil-wiki. |
| 365 | 365 | The U card specifies the login |
| 366 | 366 | of the user who made this edit to the wiki page. The Z card is |
| 367 | -the usual checksum over the either artifact and is required. | |
| 367 | +the usual checksum over the entire artifact and is required. | |
| 368 | 368 | |
| 369 | 369 | The W card is used to specify the text of the wiki page. The |
| 370 | 370 | argument to the W card is an integer which is the number of bytes |
| 371 | 371 | of text in the wiki page. That text follows the newline character |
| 372 | 372 | that terminates the W card. The wiki text is always followed by one |
| @@ -453,11 +453,11 @@ | ||
| 453 | 453 | |
| 454 | 454 | There may be zero or one N cards. The N card specifies the mimetype of the |
| 455 | 455 | comment text provided in the C card. If the N card is omitted, the C card |
| 456 | 456 | mimetype is taken to be text/plain. |
| 457 | 457 | |
| 458 | -A single U card gives the name of the user to added the attachment. | |
| 458 | +A single U card gives the name of the user who added the attachment. | |
| 459 | 459 | If an attachment is added anonymously, then the U card may be omitted. |
| 460 | 460 | |
| 461 | 461 | The Z card is the usual checksum over the rest of the attachment artifact. |
| 462 | 462 | The Z card is required. |
| 463 | 463 | |
| 464 | 464 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -49,11 +49,11 @@ | |
| 49 | </ul> |
| 50 | |
| 51 | These seven artifact types are described in the following sections. |
| 52 | |
| 53 | In the current implementation (as of 2009-01-25) the artifacts that |
| 54 | make up a fossil repository are stored in in as delta- and zlib-compressed |
| 55 | blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This |
| 56 | is an implementation detail and might change in a future release. For |
| 57 | the purpose of this article "file format" means the format of the artifacts, |
| 58 | not how the artifacts are stored on disk. It is the artifact format that |
| 59 | is intended to be enduring. The specifics of how artifacts are stored on |
| @@ -184,11 +184,11 @@ | |
| 184 | ancestor, the Q-card is used to identify a single check-in or a small |
| 185 | range of check-ins which were cherry-picked for inclusion in or |
| 186 | exclusion from the current manifest. The first argument of |
| 187 | the Q-card is the artifact ID of another manifest (the "target") |
| 188 | which has had its changes included or excluded in the current manifest. |
| 189 | The target is preceeded by "+" or "-" to show inclusion or |
| 190 | exclusion, respectively. The optional second argument to the |
| 191 | Q-card is another manifest artifact ID which is the "baseline" |
| 192 | for the cherry-pick. If omitted, the baseline is the primary |
| 193 | parent of the target. The |
| 194 | changes included or excluded consist of all changes moving from |
| @@ -315,11 +315,11 @@ | |
| 315 | is either "+", "-", or "*". The "+" means the tag should be added |
| 316 | to the artifact. The "-" means the tag should be removed. |
| 317 | The "*" character means the tag should be added to the artifact |
| 318 | and all direct descendants (but not descendents through a merge) down |
| 319 | to but not including the first descendant that contains a |
| 320 | more recent "-" or "+" tag with the same name. |
| 321 | The optional third argument is the value of the tag. A tag |
| 322 | without a value is a boolean. |
| 323 | |
| 324 | When two or more tags with the same name are applied to the |
| 325 | same artifact, the tag with the latest (most recent) date is |
| @@ -362,11 +362,11 @@ | |
| 362 | gives the name of the wiki page. The optional N card specifies |
| 363 | the mimetype of the wiki text. If the N card is omitted, the |
| 364 | mimetype is assumed to be text/x-fossil-wiki. |
| 365 | The U card specifies the login |
| 366 | of the user who made this edit to the wiki page. The Z card is |
| 367 | the usual checksum over the either artifact and is required. |
| 368 | |
| 369 | The W card is used to specify the text of the wiki page. The |
| 370 | argument to the W card is an integer which is the number of bytes |
| 371 | of text in the wiki page. That text follows the newline character |
| 372 | that terminates the W card. The wiki text is always followed by one |
| @@ -453,11 +453,11 @@ | |
| 453 | |
| 454 | There may be zero or one N cards. The N card specifies the mimetype of the |
| 455 | comment text provided in the C card. If the N card is omitted, the C card |
| 456 | mimetype is taken to be text/plain. |
| 457 | |
| 458 | A single U card gives the name of the user to added the attachment. |
| 459 | If an attachment is added anonymously, then the U card may be omitted. |
| 460 | |
| 461 | The Z card is the usual checksum over the rest of the attachment artifact. |
| 462 | The Z card is required. |
| 463 | |
| 464 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -49,11 +49,11 @@ | |
| 49 | </ul> |
| 50 | |
| 51 | These seven artifact types are described in the following sections. |
| 52 | |
| 53 | In the current implementation (as of 2009-01-25) the artifacts that |
| 54 | make up a fossil repository are stored as delta- and zlib-compressed |
| 55 | blobs in an <a href="http://www.sqlite.org/">SQLite</a> database. This |
| 56 | is an implementation detail and might change in a future release. For |
| 57 | the purpose of this article "file format" means the format of the artifacts, |
| 58 | not how the artifacts are stored on disk. It is the artifact format that |
| 59 | is intended to be enduring. The specifics of how artifacts are stored on |
| @@ -184,11 +184,11 @@ | |
| 184 | ancestor, the Q-card is used to identify a single check-in or a small |
| 185 | range of check-ins which were cherry-picked for inclusion in or |
| 186 | exclusion from the current manifest. The first argument of |
| 187 | the Q-card is the artifact ID of another manifest (the "target") |
| 188 | which has had its changes included or excluded in the current manifest. |
| 189 | The target is preceded by "+" or "-" to show inclusion or |
| 190 | exclusion, respectively. The optional second argument to the |
| 191 | Q-card is another manifest artifact ID which is the "baseline" |
| 192 | for the cherry-pick. If omitted, the baseline is the primary |
| 193 | parent of the target. The |
| 194 | changes included or excluded consist of all changes moving from |
| @@ -315,11 +315,11 @@ | |
| 315 | is either "+", "-", or "*". The "+" means the tag should be added |
| 316 | to the artifact. The "-" means the tag should be removed. |
| 317 | The "*" character means the tag should be added to the artifact |
| 318 | and all direct descendants (but not descendents through a merge) down |
| 319 | to but not including the first descendant that contains a |
| 320 | more recent "-", "*", or "+" tag with the same name. |
| 321 | The optional third argument is the value of the tag. A tag |
| 322 | without a value is a boolean. |
| 323 | |
| 324 | When two or more tags with the same name are applied to the |
| 325 | same artifact, the tag with the latest (most recent) date is |
| @@ -362,11 +362,11 @@ | |
| 362 | gives the name of the wiki page. The optional N card specifies |
| 363 | the mimetype of the wiki text. If the N card is omitted, the |
| 364 | mimetype is assumed to be text/x-fossil-wiki. |
| 365 | The U card specifies the login |
| 366 | of the user who made this edit to the wiki page. The Z card is |
| 367 | the usual checksum over the entire artifact and is required. |
| 368 | |
| 369 | The W card is used to specify the text of the wiki page. The |
| 370 | argument to the W card is an integer which is the number of bytes |
| 371 | of text in the wiki page. That text follows the newline character |
| 372 | that terminates the W card. The wiki text is always followed by one |
| @@ -453,11 +453,11 @@ | |
| 453 | |
| 454 | There may be zero or one N cards. The N card specifies the mimetype of the |
| 455 | comment text provided in the C card. If the N card is omitted, the C card |
| 456 | mimetype is taken to be text/plain. |
| 457 | |
| 458 | A single U card gives the name of the user who added the attachment. |
| 459 | If an attachment is added anonymously, then the U card may be omitted. |
| 460 | |
| 461 | The Z card is the usual checksum over the rest of the attachment artifact. |
| 462 | The Z card is required. |
| 463 | |
| 464 |
+2
-1
| --- www/hints.wiki | ||
| +++ www/hints.wiki | ||
| @@ -9,11 +9,12 @@ | ||
| 9 | 9 | window is run as a separate Tcl/Tk process, so you will need to |
| 10 | 10 | have Tcl/Tk installed on your machine for this to work. Visit |
| 11 | 11 | [http://www.activestate.com/activetcl] to for a quick download of |
| 12 | 12 | Tcl/Tk if you do not already have it on your system.) |
| 13 | 13 | |
| 14 | - 3. The "[/help?cmd=clean | fossil clean -f]" command makes a great | |
| 14 | + 3. The "[/help/clean | fossil clean -f]" or | |
| 15 | + "[/help/clean | fossil clean --verily]" command is a great | |
| 15 | 16 | alternative to "make clean". |
| 16 | 17 | |
| 17 | 18 | 4. Use "[/help?cmd=all | fossil all changes]" to look for any uncommitted |
| 18 | 19 | edits in any of your Fossil projects. Use |
| 19 | 20 | "[/help?cmd=all | fossil all pull]" on your laptop |
| 20 | 21 |
| --- www/hints.wiki | |
| +++ www/hints.wiki | |
| @@ -9,11 +9,12 @@ | |
| 9 | window is run as a separate Tcl/Tk process, so you will need to |
| 10 | have Tcl/Tk installed on your machine for this to work. Visit |
| 11 | [http://www.activestate.com/activetcl] to for a quick download of |
| 12 | Tcl/Tk if you do not already have it on your system.) |
| 13 | |
| 14 | 3. The "[/help?cmd=clean | fossil clean -f]" command makes a great |
| 15 | alternative to "make clean". |
| 16 | |
| 17 | 4. Use "[/help?cmd=all | fossil all changes]" to look for any uncommitted |
| 18 | edits in any of your Fossil projects. Use |
| 19 | "[/help?cmd=all | fossil all pull]" on your laptop |
| 20 |
| --- www/hints.wiki | |
| +++ www/hints.wiki | |
| @@ -9,11 +9,12 @@ | |
| 9 | window is run as a separate Tcl/Tk process, so you will need to |
| 10 | have Tcl/Tk installed on your machine for this to work. Visit |
| 11 | [http://www.activestate.com/activetcl] to for a quick download of |
| 12 | Tcl/Tk if you do not already have it on your system.) |
| 13 | |
| 14 | 3. The "[/help/clean | fossil clean -f]" or |
| 15 | "[/help/clean | fossil clean --verily]" command is a great |
| 16 | alternative to "make clean". |
| 17 | |
| 18 | 4. Use "[/help?cmd=all | fossil all changes]" to look for any uncommitted |
| 19 | edits in any of your Fossil projects. Use |
| 20 | "[/help?cmd=all | fossil all pull]" on your laptop |
| 21 |
+3
-1
| --- www/qandc.wiki | ||
| +++ www/qandc.wiki | ||
| @@ -146,11 +146,13 @@ | ||
| 146 | 146 | You do not need any other packages |
| 147 | 147 | (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, |
| 148 | 148 | sqlite, and so forth) |
| 149 | 149 | in order to run fossil. Fossil runs just fine in a chroot jail all |
| 150 | 150 | by itself. And the self-contained fossil |
| 151 | -executable is much less than 1MB in size. | |
| 151 | +executable is much less than 1MB in size. (Update 2015-01-12: Fossil has | |
| 152 | +grown in the years since the previous sentence was written but is still | |
| 153 | +much less than 2MB according to "size" when compiled using -Os on x64 Linux.) | |
| 152 | 154 | Fossil is the very opposite of bloat.</p> |
| 153 | 155 | </blockquote> |
| 154 | 156 | |
| 155 | 157 | |
| 156 | 158 | </nowiki> |
| 157 | 159 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -146,11 +146,13 @@ | |
| 146 | You do not need any other packages |
| 147 | (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, |
| 148 | sqlite, and so forth) |
| 149 | in order to run fossil. Fossil runs just fine in a chroot jail all |
| 150 | by itself. And the self-contained fossil |
| 151 | executable is much less than 1MB in size. |
| 152 | Fossil is the very opposite of bloat.</p> |
| 153 | </blockquote> |
| 154 | |
| 155 | |
| 156 | </nowiki> |
| 157 |
| --- www/qandc.wiki | |
| +++ www/qandc.wiki | |
| @@ -146,11 +146,13 @@ | |
| 146 | You do not need any other packages |
| 147 | (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, |
| 148 | sqlite, and so forth) |
| 149 | in order to run fossil. Fossil runs just fine in a chroot jail all |
| 150 | by itself. And the self-contained fossil |
| 151 | executable is much less than 1MB in size. (Update 2015-01-12: Fossil has |
| 152 | grown in the years since the previous sentence was written but is still |
| 153 | much less than 2MB according to "size" when compiled using -Os on x64 Linux.) |
| 154 | Fossil is the very opposite of bloat.</p> |
| 155 | </blockquote> |
| 156 | |
| 157 | |
| 158 | </nowiki> |
| 159 |